From 469bad05c15b3984a5ae52ab50455250eeda8d30 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Mon, 23 Jun 2025 22:28:13 -0600 Subject: [PATCH] :sparkles: Add `id_irq` type Problem: - It is sometimes useful to dynamically enable/disable a top-level interrupt (of a `shared_irq`) by controlling it with a resource. Solution: - Add `id_irq` which can be used inside a `shared_irq` to represent/enable/disable the `shared_irq` itself. Note: - `id_irq` has an enable field and a policy (for required resources). It does not have a status field. --- include/interrupt/config.hpp | 20 ++++++++ include/interrupt/impl.hpp | 13 ++++++ test/interrupt/dynamic_controller.cpp | 50 ++++++++++++++------ test/interrupt/manager.cpp | 66 +++++++++++++++++++++++---- 4 files changed, 124 insertions(+), 25 deletions(-) diff --git a/include/interrupt/config.hpp b/include/interrupt/config.hpp index 2ef04aa8..092992fa 100644 --- a/include/interrupt/config.hpp +++ b/include/interrupt/config.hpp @@ -39,6 +39,15 @@ template struct super_config { constexpr static auto irq_number = Number; }; +namespace fields { +template struct field_t {}; +template struct op_t {}; + +template constexpr auto read(field_t) -> op_t { return {}; } +template constexpr auto clear(field_t) -> op_t { return {}; } +template constexpr auto apply(op_t) { return V; } +} // namespace fields + template struct sub_config { template constexpr static auto enable() -> void {} constexpr static auto enable_field = EnableField{}; @@ -72,6 +81,9 @@ template class flow_config { }; } // namespace detail +template using enable_t = detail::fields::field_t; +template using status_t = detail::fields::field_t; + template struct root : detail::parent_config { template using dynamic_controller_t = dynamic_controller; @@ -95,6 +107,14 @@ struct sub_irq : detail::policy_config, template using built_t = sub_irq_impl; }; +template +struct id_irq : detail::policy_config, + detail::parent_config<>, + detail::sub_config> { + template using built_t = id_irq_impl; + template constexpr static bool triggers_flow = false; +}; + template struct shared_irq : detail::policy_config, diff --git a/include/interrupt/impl.hpp b/include/interrupt/impl.hpp index 86772f54..4d48b772 100644 --- a/include/interrupt/impl.hpp +++ b/include/interrupt/impl.hpp @@ -57,6 +57,19 @@ struct sub_irq_impl : Config { } }; +template struct id_irq_impl : Config { + constexpr static bool active = true; + + using Config::enable_field; + using Config::status_field; + + [[nodiscard]] static auto get_interrupt_enables() { + return stdx::make_tuple(enable_field); + } + + static auto run() -> void {} +}; + template struct shared_irq_impl : Config { constexpr static bool active = (Subs::active or ...); diff --git a/test/interrupt/dynamic_controller.cpp b/test/interrupt/dynamic_controller.cpp index d2f4c7b1..672300a8 100644 --- a/test/interrupt/dynamic_controller.cpp +++ b/test/interrupt/dynamic_controller.cpp @@ -57,15 +57,20 @@ template constexpr auto write(field_value_t v) { struct test_flow_1_t : public flow::service<"1"> {}; struct test_flow_2_t : public flow::service<"2"> {}; -using en_field_1_t = mock_field_t<1, mock_register_t, 0, 0>; -using sts_field_t = mock_field_t<2, mock_register_t, 1, 1>; -using en_field_2_t = mock_field_t<3, mock_register_t, 2, 2>; +using en_field_0_t = mock_field_t<1, mock_register_t, 0, 0>; +using en_field_1_t = mock_field_t<1, mock_register_t, 1, 1>; +using sts_field_t = mock_field_t<2, mock_register_t, 2, 2>; +using en_field_2_t = mock_field_t<3, mock_register_t, 3, 3>; +struct test_resource_0; struct test_resource_1; struct test_resource_2; using config_t = interrupt::root, + interrupt::id_irq< + en_field_0_t, + interrupt::policies>>, interrupt::sub_irq< en_field_1_t, sts_field_t, interrupt::policies>, @@ -79,35 +84,50 @@ using dynamic_t = interrupt::dynamic_controller; auto reset_dynamic_state() -> void { register_value = 0; + dynamic_t::disable(); + dynamic_t::turn_on_resource(); dynamic_t::turn_on_resource(); dynamic_t::turn_on_resource(); + + dynamic_t::enable_by_field(); + CHECK(register_value == 0b1); } } // namespace TEST_CASE("enable one irq", "[dynamic controller]") { reset_dynamic_state(); dynamic_t::enable(); - CHECK(register_value == 0b1); + CHECK(register_value == 0b11); } TEST_CASE("enable multiple irqs", "[dynamic controller]") { reset_dynamic_state(); dynamic_t::enable(); - CHECK(register_value == 0b101); + CHECK(register_value == 0b1011); } -TEST_CASE("disabling resource disables irq that requires it", +TEST_CASE("disabling resource disables sub_irq that requires it", "[dynamic controller]") { reset_dynamic_state(); dynamic_t::enable(); - CHECK(register_value == 0b100); + CHECK(register_value == 0b1001); dynamic_t::turn_off_resource(); - CHECK(register_value == 0); + CHECK(register_value == 0b1); dynamic_t::turn_on_resource(); - CHECK(register_value == 0b100); + CHECK(register_value == 0b1001); +} + +TEST_CASE("disabling resource disables id_irq that requires it", + "[dynamic controller]") { + reset_dynamic_state(); + + dynamic_t::turn_off_resource(); + CHECK(register_value == 0); + dynamic_t::turn_on_resource(); + CHECK(register_value == 0b1); } TEST_CASE("disabling resource disables only irqs that require it", @@ -115,12 +135,12 @@ TEST_CASE("disabling resource disables only irqs that require it", reset_dynamic_state(); dynamic_t::enable(); - CHECK(register_value == 0b101); + CHECK(register_value == 0b1011); dynamic_t::turn_off_resource(); - CHECK(register_value == 0b1); + CHECK(register_value == 0b11); dynamic_t::turn_on_resource(); - CHECK(register_value == 0b101); + CHECK(register_value == 0b1011); } TEST_CASE("disable resource that multiple irqs require", @@ -128,10 +148,10 @@ TEST_CASE("disable resource that multiple irqs require", reset_dynamic_state(); dynamic_t::enable(); - CHECK(register_value == 0b101); + CHECK(register_value == 0b1011); dynamic_t::turn_off_resource(); - CHECK(register_value == 0); + CHECK(register_value == 0b1); dynamic_t::turn_on_resource(); - CHECK(register_value == 0b101); + CHECK(register_value == 0b1011); } diff --git a/test/interrupt/manager.cpp b/test/interrupt/manager.cpp index 6af1406c..b3c07d79 100644 --- a/test/interrupt/manager.cpp +++ b/test/interrupt/manager.cpp @@ -17,7 +17,7 @@ using config_b = interrupt::root< interrupt::irq<17_irq, 42, interrupt::policies<>, flow_1, flow_2>>; } // namespace -TEST_CASE("init enables interrupts", "[flow]") { +TEST_CASE("init enables interrupts", "[manager]") { auto m = interrupt::manager{}; inited = false; enabled<17_irq> = false; @@ -30,7 +30,7 @@ TEST_CASE("init enables interrupts", "[flow]") { CHECK(priority<17_irq> == 42); } -TEST_CASE("run single flow", "[flow]") { +TEST_CASE("run single flow", "[manager]") { auto m = interrupt::manager{}; flow_run = false; @@ -39,7 +39,7 @@ TEST_CASE("run single flow", "[flow]") { CHECK(flow_run); } -TEST_CASE("run multiple flows", "[flow]") { +TEST_CASE("run multiple flows", "[manager]") { auto m = interrupt::manager{}; flow_run = false; flow_run = false; @@ -58,7 +58,7 @@ struct alt_nexus { }; } // namespace -TEST_CASE("run flow across multiple nexi", "[flow]") { +TEST_CASE("run flow across multiple nexi", "[manager]") { auto m = interrupt::manager{}; flow_run = false; flow_run> = false; @@ -93,7 +93,7 @@ using config_shared = interrupt::irq<38_irq, 39, interrupt::policies<>, flow_38>>; } // namespace -TEST_CASE("init enables mcu interrupts", "[flow]") { +TEST_CASE("init enables mcu interrupts", "[manager]") { auto m = interrupt::manager{}; inited = false; enabled<33_irq> = false; @@ -111,7 +111,7 @@ TEST_CASE("init enables mcu interrupts", "[flow]") { CHECK(priority<38_irq> == 39); } -TEST_CASE("init enables dynamic interrupts", "[flow]") { +TEST_CASE("init enables dynamic interrupts", "[manager]") { auto m = interrupt::manager{}; enable_field_t<33'1>::value = false; enable_field_t<33'2>::value = false; @@ -122,7 +122,7 @@ TEST_CASE("init enables dynamic interrupts", "[flow]") { CHECK(enable_field_t<33'2>::value); } -TEST_CASE("run flows if sub_irq is enabled", "[flow]") { +TEST_CASE("run flows if sub_irq is enabled", "[manager]") { auto m = interrupt::manager{}; enable_field_t<33'1>::value = true; status_field_t<33'1>::value = true; @@ -137,7 +137,7 @@ TEST_CASE("run flows if sub_irq is enabled", "[flow]") { CHECK(not flow_run); } -TEST_CASE("init enables mcu interrupt if any flow is active", "[flow]") { +TEST_CASE("init enables mcu interrupt if any flow is active", "[manager]") { using config_t = root, interrupt::sub_irq, status_field_t<33'0>, @@ -158,7 +158,7 @@ TEST_CASE("init enables mcu interrupt if any flow is active", "[flow]") { } TEST_CASE("init does not enable mcu interrupt if all flows are inactive", - "[flow]") { + "[manager]") { using config_t = root, interrupt::sub_irq, status_field_t<33'0>, @@ -191,7 +191,7 @@ using config_shared_sub = root, flow_33_2_2>>>>; } // namespace -TEST_CASE("run flows for shared sub irqs if enabled", "[flow]") { +TEST_CASE("run flows for shared sub irqs if enabled", "[manager]") { auto m = interrupt::manager{}; enable_field_t<33'1>::value = false; @@ -213,3 +213,49 @@ TEST_CASE("run flows for shared sub irqs if enabled", "[flow]") { CHECK(flow_run); CHECK(not flow_run); } + +namespace { +using config_shared_no_enable = root, + interrupt::sub_irq, status_field_t<33'1>, + interrupt::policies<>, flow_33_1>>>; +using config_shared_no_status = root, + interrupt::sub_irq, interrupt::status_t<>, + interrupt::policies<>, flow_33_1>>>; +} // namespace + +TEST_CASE("run flows with no enable field", "[manager]") { + auto m = interrupt::manager{}; + status_field_t<33'1>::value = true; + flow_run = false; + + m.run<33_irq>(); + + CHECK(not status_field_t<33'2'1>::value); + CHECK(flow_run); +} + +TEST_CASE("run flows with no status field", "[manager]") { + auto m = interrupt::manager{}; + enable_field_t<33'1>::value = true; + flow_run = false; + + m.run<33_irq>(); + + CHECK(flow_run); +} + +namespace { +using config_shared_id = root, + interrupt::id_irq, interrupt::policies<>>>>; +} // namespace + +TEST_CASE("init enables id interrupts", "[manager]") { + auto m = interrupt::manager{}; + + enable_field_t<33'0>::value = false; + m.init(); + CHECK(enable_field_t<33'0>::value); +}