add Chapter18

This commit is contained in:
Amar Mahmutbegovic
2025-02-06 00:19:59 +01:00
parent 9cc9cc7d73
commit 8634accda5
1533 changed files with 1092521 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
#pragma once
#include <interrupt/fwd.hpp>
#include <interrupt/policies.hpp>
#include <stdx/tuple.hpp>
#include <stdx/type_traits.hpp>
#include <concepts>
namespace interrupt {
namespace detail {
template <typename T, template <typename...> typename X>
concept specializes = stdx::is_specialization_of_v<std::remove_cvref_t<T>, X>;
}
template <typename T>
concept root_config = requires {
{ T::children } -> detail::specializes<stdx::tuple>;
{ T::descendants } -> detail::specializes<stdx::tuple>;
typename T::template dynamic_controller_t<int>;
};
template <typename T>
concept base_irq_config =
status_policy<typename T::status_policy_t> and
detail::specializes<typename T::resources_t, resource_list> and requires {
{ T::template enable<true>() } -> std::same_as<void>;
{ T::children } -> detail::specializes<stdx::tuple>;
{ T::descendants } -> detail::specializes<stdx::tuple>;
};
template <typename T>
concept irq_config = base_irq_config<T> and requires {
{ T::irq_number } -> std::same_as<irq_num_t const &>;
};
template <typename T>
concept sub_irq_config = base_irq_config<T> and requires {
T::enable_field;
T::status_field;
};
template <typename T>
concept base_irq_interface = requires(T const &t) {
{ t.get_interrupt_enables() } -> detail::specializes<stdx::tuple>;
{ t.run() } -> std::same_as<void>;
};
template <typename T>
concept irq_interface = base_irq_interface<T> and requires(T const &t) {
{ T::irq_number } -> std::same_as<irq_num_t const &>;
{ t.init_mcu_interrupts() } -> std::same_as<void>;
};
template <typename T>
concept sub_irq_interface = base_irq_interface<T>;
template <typename T, typename Flow>
concept nexus_for = requires {
T::template service<Flow>();
{ T::template service<Flow>.active } -> std::same_as<bool const &>;
};
} // namespace interrupt

View File

@@ -0,0 +1,133 @@
#pragma once
#include <interrupt/concepts.hpp>
#include <interrupt/dynamic_controller.hpp>
#include <interrupt/fwd.hpp>
#include <interrupt/hal.hpp>
#include <interrupt/impl.hpp>
#include <interrupt/policies.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
namespace interrupt {
namespace detail {
template <typename Policies> struct policy_config {
using status_policy_t =
typename Policies::template type<status_clear_policy,
clear_status_first>;
using resources_t =
typename Policies::template type<required_resources_policy,
required_resources<>>::resources;
};
template <base_irq_config... Cfgs> struct parent_config {
constexpr static auto children = stdx::tuple<Cfgs...>{};
constexpr static auto descendants =
stdx::tuple_cat(children, children.apply([](auto... child) {
return stdx::tuple_cat(child.descendants...);
}));
template <template <typename...> typename C, typename... Ts>
using build = C<typename Cfgs::template built_t<Ts...>...>;
};
template <irq_num_t Number, priority_t Priority> struct super_config {
template <bool Enable> constexpr static auto enable() -> void {
hal::irq_init<Enable, Number, Priority>();
}
constexpr static auto irq_number = Number;
};
template <typename EnableField, typename StatusField> struct sub_config {
template <bool Enable> constexpr static auto enable() -> void {}
constexpr static auto enable_field = EnableField{};
constexpr static auto status_field = StatusField{};
};
template <typename... Flows> class flow_config {
template <typename Flow, typename... Nexi>
constexpr static auto one_active() {
return (... or Nexi::template service<Flow>.active);
}
template <typename Flow, typename... Nexi>
constexpr static auto one_isr() -> void {
(Nexi::template service<Flow>(), ...);
}
public:
template <typename... Nexi>
constexpr static bool active = (... or one_active<Flows, Nexi...>());
template <typename Nexus>
constexpr static bool has_flows_for = (... and nexus_for<Nexus, Flows>);
template <typename Flow>
constexpr static bool triggers_flow = (... or std::same_as<Flow, Flows>);
template <typename... Nexi> constexpr static auto isr() -> void {
(one_isr<Flows, Nexi...>(), ...);
}
};
} // namespace detail
template <base_irq_config... Cfgs>
struct root : detail::parent_config<Cfgs...> {
template <typename T> using dynamic_controller_t = dynamic_controller<T>;
};
template <irq_num_t Number, priority_t Priority, typename Policies,
typename... Flows>
struct irq : detail::policy_config<Policies>,
detail::parent_config<>,
detail::super_config<Number, Priority>,
detail::flow_config<Flows...> {
template <typename... Nexi> using built_t = irq_impl<irq, Nexi...>;
};
template <typename EnableField, typename StatusField, typename Policies,
typename... Flows>
struct sub_irq : detail::policy_config<Policies>,
detail::parent_config<>,
detail::sub_config<EnableField, StatusField>,
detail::flow_config<Flows...> {
template <typename... Nexi> using built_t = sub_irq_impl<sub_irq, Nexi...>;
};
template <irq_num_t Number, priority_t Priority, typename Policies,
sub_irq_config... Cfgs>
struct shared_irq : detail::policy_config<Policies>,
detail::parent_config<Cfgs...>,
detail::super_config<Number, Priority> {
template <typename... Subs>
using sub_built_t = shared_irq_impl<shared_irq, Subs...>;
template <typename... Nexi>
using built_t =
typename detail::parent_config<Cfgs...>::template build<sub_built_t,
Nexi...>;
template <typename Flow>
constexpr static bool triggers_flow =
(... or Cfgs::template triggers_flow<Flow>);
};
template <typename EnableField, typename StatusField, typename Policies,
sub_irq_config... Cfgs>
struct shared_sub_irq : detail::policy_config<Policies>,
detail::parent_config<Cfgs...>,
detail::sub_config<EnableField, StatusField> {
template <typename... Subs>
using sub_built_t = shared_sub_irq_impl<shared_sub_irq, Subs...>;
template <typename... Nexi>
using built_t =
typename detail::parent_config<Cfgs...>::template build<sub_built_t,
Nexi...>;
template <typename Flow>
constexpr static bool triggers_flow =
(... or Cfgs::template triggers_flow<Flow>);
};
} // namespace interrupt

View File

@@ -0,0 +1,231 @@
#pragma once
#include <conc/concurrency.hpp>
#include <stdx/compiler.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
#include <stdx/type_traits.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <cstdint>
#include <limits>
#include <type_traits>
namespace interrupt {
enum class resource_status : std::uint8_t { OFF, ON };
template <typename Irq>
concept has_enable_field = requires { Irq::enable_field; };
template <typename Irq>
concept has_resource =
has_enable_field<Irq> and
not boost::mp11::mp_empty<typename Irq::resources_t>::value;
template <typename Root> struct dynamic_controller {
private:
using all_resources_t =
boost::mp11::mp_unique<decltype(Root::descendants.apply(
[]<typename... Irqs>(Irqs const &...) {
return boost::mp11::mp_append<typename Irqs::resources_t...>{};
}))>;
template <typename Register>
CONSTINIT static inline typename Register::DataType allowed_enables =
std::numeric_limits<typename Register::DataType>::max();
template <typename Register>
CONSTINIT static inline typename Register::DataType dynamic_enables{};
template <typename Resource>
CONSTINIT static inline bool is_resource_on = true;
template <typename Resource> struct doesnt_require_resource {
template <typename Irq>
using fn =
std::bool_constant<has_enable_field<Irq> and
not boost::mp11::mp_contains<
typename Irq::resources_t, Resource>::value>;
};
template <typename Register> struct in_register {
template <typename Field>
using fn = std::is_same<Register, typename Field::RegisterType>;
};
/**
* For each ResourceType, keep track of what interrupts can still be enabled
* when that resource goes down.
*
* Each bit in this mask corresponds to an interrupt enable field in
* RegType. If the bit is '1', that means the corresponding interrupt can be
* enabled when the resource is not available. If the bit is '0', that means
* the corresponding interrupt must be disabled when the resource is not
* available.
*
* @tparam ResourceType
* The resource we want to check.
*
* @tparam RegType
* The specific register mask we want to check.
*/
template <typename ResourceType, typename RegType>
constexpr static typename RegType::DataType irqs_allowed = []() {
// get all interrupt enable fields that don't require the given resource
auto const matching_irqs =
stdx::filter<doesnt_require_resource<ResourceType>::template fn>(
Root::descendants);
auto const interrupt_enables_tuple = stdx::transform(
[](auto irq) { return irq.enable_field; }, matching_irqs);
// filter fields that aren't in RegType
auto const fields_in_reg =
stdx::filter<in_register<RegType>::template fn>(
interrupt_enables_tuple);
// set the bits in the mask for interrupts that don't require the
// resource
using DataType = typename RegType::DataType;
return fields_in_reg.fold_left(
DataType{}, [](DataType value, auto field) -> DataType {
return value | field.get_mask();
});
}();
template <typename RegTypeTuple>
static inline void reprogram_interrupt_enables(RegTypeTuple regs) {
stdx::for_each(
[]<typename R>(R reg) {
// make sure we don't enable any interrupts that are not allowed
// according to resource availability
auto const final_enables =
allowed_enables<R> & dynamic_enables<R>;
// update the hardware registers
apply(write(reg.raw(final_enables)));
},
regs);
}
/**
* tuple of every interrupt register affected by a resource
*/
template <typename Irq>
using has_resource_t = std::bool_constant<has_resource<Irq>>;
constexpr static auto all_resource_affected_regs =
stdx::to_unsorted_set(stdx::transform(
[]<typename Irq>(Irq) { return Irq::enable_field.get_register(); },
stdx::filter<has_resource_t>(Root::descendants)));
/**
* Reprogram interrupt enables based on updated resource availability.
*/
static inline auto recalculate_allowed_enables() {
// set allowed_enables mask for each resource affected register
stdx::for_each(
[]<typename R>(R) {
using DataType = typename R::DataType;
allowed_enables<R> = std::numeric_limits<DataType>::max();
},
all_resource_affected_regs);
// for each resource, if it is not on, mask out unavailable interrupts
stdx::template_for_each<all_resources_t>([]<typename Rsrc>() {
if (not is_resource_on<Rsrc>) {
stdx::for_each(
[]<typename R>(R) {
allowed_enables<R> &= irqs_allowed<Rsrc, R>;
},
all_resource_affected_regs);
}
});
return all_resource_affected_regs;
}
/**
* Store the interrupt enable values that FW _wants_ at runtime,
* irrespective of any resource conflicts that would require specific
* interrupts to be disabled.
*
* @tparam RegType
* The croo::Register this value corresponds to.
*/
template <typename... Flows> struct match_flow {
template <typename Irq>
using fn =
std::bool_constant<has_enable_field<Irq> and
(... or Irq::template triggers_flow<Flows>)>;
};
template <bool Enable, typename... Flows>
static inline void enable_by_name() {
// NOTE: critical section is not needed here because shared state is
// only updated by the final call to enable_by_field
// TODO: add support to enable/disable top-level IRQs by name.
// this will require another way to manage them vs. mmio
// registers. once that goes in, then enable_by_field should be
// removed or made private.
auto const matching_irqs =
stdx::filter<match_flow<Flows...>::template fn>(Root::descendants);
auto const interrupt_enables_tuple = stdx::transform(
[](auto irq) { return irq.enable_field; }, matching_irqs);
interrupt_enables_tuple.apply([]<typename... Fields>(Fields...) {
enable_by_field<Enable, Fields...>();
});
}
template <typename ResourceType>
static inline void update_resource(resource_status status) {
conc::call_in_critical_section<dynamic_controller>([&] {
is_resource_on<ResourceType> = (status == resource_status::ON);
recalculate_allowed_enables();
reprogram_interrupt_enables(all_resource_affected_regs);
});
}
public:
template <typename ResourceType> static inline void turn_on_resource() {
update_resource<ResourceType>(resource_status::ON);
}
template <typename ResourceType> static inline void turn_off_resource() {
update_resource<ResourceType>(resource_status::OFF);
}
template <bool Enable, typename... Fields>
static inline void enable_by_field() {
conc::call_in_critical_section<dynamic_controller>([] {
[[maybe_unused]] auto const enable = []<typename F>() -> void {
using R = typename F::RegisterType;
if constexpr (Enable) {
dynamic_enables<R> |= F::get_mask();
} else {
dynamic_enables<R> &= ~F::get_mask();
}
};
(enable.template operator()<Fields>(), ...);
auto const unique_regs = stdx::to_unsorted_set(
stdx::tuple<typename Fields::RegisterType...>{});
reprogram_interrupt_enables(unique_regs);
});
}
template <typename... Flows> static inline void enable() {
enable_by_name<true, Flows...>();
}
template <typename... Flows> static inline void disable() {
enable_by_name<false, Flows...>();
}
};
} // namespace interrupt

View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdx/compiler.hpp>
#include <cstddef>
#include <cstdint>
namespace interrupt {
enum struct irq_num_t : std::uint32_t {};
using priority_t = std::size_t;
inline namespace literals {
// NOLINTNEXTLINE(google-runtime-int)
CONSTEVAL auto operator""_irq(unsigned long long int v) -> irq_num_t {
return static_cast<irq_num_t>(v);
}
} // namespace literals
} // namespace interrupt

View File

@@ -0,0 +1,67 @@
#pragma once
#include <interrupt/fwd.hpp>
#include <interrupt/policies.hpp>
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/type_traits.hpp>
namespace interrupt {
template <typename T>
concept hal_interface = requires(T const &t, void (*isr)()) {
{ t.init() } -> stdx::same_as<void>;
{
t.template irq_init<true, irq_num_t{}, std::size_t{}>()
} -> stdx::same_as<void>;
{
t.template run<dont_clear_status>(irq_num_t{}, isr)
} -> stdx::same_as<void>;
};
template <typename...> struct null_hal {
static auto init() -> void { undefined(); }
template <bool Enable, irq_num_t IrqNumber, std::size_t PriorityLevel>
static auto irq_init() -> void {
undefined();
}
template <status_policy>
static auto run(irq_num_t, stdx::invocable auto const &) -> void {
undefined();
}
private:
static auto undefined() -> void {
static_assert(stdx::always_false_v<null_hal>,
"No interrupt HAL defined: inject one");
}
};
static_assert(hal_interface<null_hal<>>);
template <typename...> inline auto injected_hal = null_hal{};
struct hal {
template <typename... Ts>
requires(sizeof...(Ts) == 0)
ALWAYS_INLINE static auto init() -> void {
injected_hal<Ts...>.init();
}
template <bool Enable, irq_num_t IrqNumber, int Priority, typename... Ts>
requires(sizeof...(Ts) == 0)
ALWAYS_INLINE static auto irq_init() -> void {
injected_hal<Ts...>.template irq_init<Enable, IrqNumber, Priority>();
}
template <status_policy P, typename... Ts>
requires(sizeof...(Ts) == 0)
ALWAYS_INLINE static auto run(irq_num_t irq,
stdx::invocable auto const &isr) -> void {
injected_hal<Ts...>.template run<P>(irq, isr);
}
};
static_assert(hal_interface<hal>);
} // namespace interrupt

View File

@@ -0,0 +1,107 @@
#pragma once
#include <interrupt/concepts.hpp>
#include <interrupt/hal.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
namespace interrupt {
template <typename Nexus, typename Config>
concept nexus_for_cfg = Config::template has_flows_for<Nexus>;
template <typename Config, nexus_for_cfg<Config>... Nexi>
struct irq_impl : Config {
constexpr static bool active = Config::template active<Nexi...>;
static auto init_mcu_interrupts() -> void {
Config::template enable<active>();
}
[[nodiscard]] static auto get_interrupt_enables() -> stdx::tuple<> {
return {};
}
static auto run() -> void {
if constexpr (active) {
using status_policy_t = typename Config::status_policy_t;
hal::run<status_policy_t>(Config::irq_number,
[] { Config::template isr<Nexi...>(); });
}
}
};
template <typename Config, nexus_for_cfg<Config>... Nexi>
struct sub_irq_impl : Config {
constexpr static bool active = Config::template active<Nexi...>;
using Config::enable_field;
using Config::status_field;
[[nodiscard]] static auto get_interrupt_enables() {
if constexpr (active) {
return stdx::make_tuple(enable_field);
} else {
return stdx::tuple{};
}
}
static auto run() -> void {
if constexpr (active) {
using status_policy_t = typename Config::status_policy_t;
if (apply(read(enable_field)) && apply(read(status_field))) {
status_policy_t::run([&] { apply(clear(status_field)); },
[&] { Config::template isr<Nexi...>(); });
}
}
}
};
template <typename Config, sub_irq_interface... Subs>
struct shared_irq_impl : Config {
constexpr static bool active = (Subs::active or ...);
static auto init_mcu_interrupts() -> void {
Config::template enable<active>();
}
[[nodiscard]] static auto get_interrupt_enables() {
return stdx::tuple_cat(Subs::get_interrupt_enables()...);
}
static auto run() -> void {
if constexpr (active) {
using status_policy_t = typename Config::status_policy_t;
hal::run<status_policy_t>(Config::irq_number,
[] { (Subs::run(), ...); });
}
}
};
template <typename Config, sub_irq_interface... Subs>
struct shared_sub_irq_impl : Config {
constexpr static bool active = (Subs::active or ...);
using Config::enable_field;
using Config::status_field;
[[nodiscard]] static auto get_interrupt_enables() {
if constexpr (active) {
return stdx::tuple_cat(stdx::make_tuple(enable_field),
Subs::get_interrupt_enables()...);
} else {
return stdx::tuple{};
}
}
static auto run() -> void {
if constexpr (active) {
using status_policy_t = typename Config::status_policy_t;
if (apply(read(enable_field)) && apply(read(status_field))) {
status_policy_t::run([&] { apply(clear(status_field)); },
[&] { (Subs::run(), ...); });
}
}
}
};
} // namespace interrupt

View File

@@ -0,0 +1,57 @@
#pragma once
#include <interrupt/concepts.hpp>
#include <interrupt/dynamic_controller.hpp>
#include <interrupt/hal.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <algorithm>
#include <type_traits>
namespace interrupt {
namespace detail {
template <typename Dynamic, irq_interface... Impls> struct manager {
void init() const {
// TODO: log exact interrupt manager configuration
// (should be a single compile-time string with no arguments)
hal::init();
init_mcu_interrupts();
init_sub_interrupts();
}
void init_mcu_interrupts() const { (Impls::init_mcu_interrupts(), ...); }
void init_sub_interrupts() const {
auto enables = stdx::tuple_cat(Impls::get_interrupt_enables()...);
enables.apply([]<typename... Enables>(Enables...) {
Dynamic::template enable_by_field<true, Enables...>();
});
}
template <irq_num_t Number> inline void run() const {
using M = stdx::type_map<stdx::vt_pair<Impls::irq_number, Impls>...>;
using irq_t = stdx::value_lookup_t<M, Number>;
if constexpr (not std::is_void_v<irq_t>) {
irq_t::run();
}
}
[[nodiscard]] constexpr auto max_irq() const -> irq_num_t {
return static_cast<irq_num_t>(
std::max({stdx::to_underlying(Impls::irq_number)...}));
}
};
template <typename Config> struct build_manager {
using dynamic_t = typename Config::template dynamic_controller_t<Config>;
template <typename... Built> using impl = manager<dynamic_t, Built...>;
};
} // namespace detail
template <interrupt::root_config Config, typename... Nexi>
using manager = typename Config::template build<
detail::build_manager<Config>::template impl, Nexi...>;
} // namespace interrupt

View File

@@ -0,0 +1,80 @@
#pragma once
#include <stdx/concepts.hpp>
#include <stdx/tuple.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <utility>
namespace interrupt {
template <typename T>
concept policy = requires { typename T::policy_type; };
template <typename T>
concept status_policy = policy<T> and requires(void (*f)()) {
{ T::run(f, f) } -> stdx::same_as<void>;
};
struct status_clear_policy;
struct clear_status_first {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &clear_status,
stdx::invocable auto const &run) {
clear_status();
run();
}
static_assert(status_policy<clear_status_first>);
};
struct clear_status_last {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &clear_status,
stdx::invocable auto const &run) {
run();
clear_status();
}
static_assert(status_policy<clear_status_last>);
};
struct dont_clear_status {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &,
stdx::invocable auto const &run) {
run();
}
static_assert(status_policy<dont_clear_status>);
};
struct required_resources_policy;
template <typename... Resources> struct resource_list {};
template <typename T>
concept resources_policy =
policy<T> and
stdx::is_specialization_of_v<typename T::resources, resource_list>;
template <typename... Resources> struct required_resources {
using policy_type = required_resources_policy;
using resources = resource_list<Resources...>;
};
template <typename... Policies> struct policies {
template <typename PolicyType, typename Default>
constexpr static auto get() {
using M = stdx::type_map<
stdx::tt_pair<typename Policies::policy_type, Policies>...>;
return stdx::type_lookup_t<M, PolicyType, Default>{};
}
template <typename PolicyType, typename Default>
using type = decltype(get<PolicyType, Default>());
};
} // namespace interrupt