add Chapter18
This commit is contained in:
64
Chapter18/cib/libs/interrupt/concepts.hpp
Normal file
64
Chapter18/cib/libs/interrupt/concepts.hpp
Normal 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
|
||||
133
Chapter18/cib/libs/interrupt/config.hpp
Normal file
133
Chapter18/cib/libs/interrupt/config.hpp
Normal 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
|
||||
231
Chapter18/cib/libs/interrupt/dynamic_controller.hpp
Normal file
231
Chapter18/cib/libs/interrupt/dynamic_controller.hpp
Normal 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
|
||||
18
Chapter18/cib/libs/interrupt/fwd.hpp
Normal file
18
Chapter18/cib/libs/interrupt/fwd.hpp
Normal 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
|
||||
67
Chapter18/cib/libs/interrupt/hal.hpp
Normal file
67
Chapter18/cib/libs/interrupt/hal.hpp
Normal 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
|
||||
107
Chapter18/cib/libs/interrupt/impl.hpp
Normal file
107
Chapter18/cib/libs/interrupt/impl.hpp
Normal 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
|
||||
57
Chapter18/cib/libs/interrupt/manager.hpp
Normal file
57
Chapter18/cib/libs/interrupt/manager.hpp
Normal 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
|
||||
80
Chapter18/cib/libs/interrupt/policies.hpp
Normal file
80
Chapter18/cib/libs/interrupt/policies.hpp
Normal 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
|
||||
Reference in New Issue
Block a user