Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/interrupt/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ template <irq_num_t Number, priority_t Priority> struct super_config {
constexpr static auto irq_number = Number;
};

namespace fields {
template <bool V> struct field_t {};
template <bool V> struct op_t {};

template <bool V> constexpr auto read(field_t<V>) -> op_t<V> { return {}; }
template <bool V> constexpr auto clear(field_t<V>) -> op_t<V> { return {}; }
template <bool V> constexpr auto apply(op_t<V>) { return V; }
} // namespace fields

template <typename EnableField, typename StatusField> struct sub_config {
template <bool Enable> constexpr static auto enable() -> void {}
constexpr static auto enable_field = EnableField{};
Expand Down Expand Up @@ -72,6 +81,9 @@ template <typename... Flows> class flow_config {
};
} // namespace detail

template <bool V = true> using enable_t = detail::fields::field_t<V>;
template <bool V = true> using status_t = detail::fields::field_t<V>;

template <base_irq_config... Cfgs>
struct root : detail::parent_config<Cfgs...> {
template <typename T> using dynamic_controller_t = dynamic_controller<T>;
Expand All @@ -95,6 +107,14 @@ struct sub_irq : detail::policy_config<Policies>,
template <typename... Nexi> using built_t = sub_irq_impl<sub_irq, Nexi...>;
};

template <typename EnableField, typename Policies>
struct id_irq : detail::policy_config<Policies>,
detail::parent_config<>,
detail::sub_config<EnableField, status_t<>> {
template <typename...> using built_t = id_irq_impl<id_irq>;
template <typename> constexpr static bool triggers_flow = false;
};

template <irq_num_t Number, priority_t Priority, typename Policies,
sub_irq_config... Cfgs>
struct shared_irq : detail::policy_config<Policies>,
Expand Down
13 changes: 13 additions & 0 deletions include/interrupt/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ struct sub_irq_impl : Config {
}
};

template <typename Config> 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 <typename Config, sub_irq_interface... Subs>
struct shared_irq_impl : Config {
constexpr static bool active = (Subs::active or ...);
Expand Down
50 changes: 35 additions & 15 deletions test/interrupt/dynamic_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,20 @@ template <typename F> constexpr auto write(field_value_t<F> 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::shared_irq<
0_irq, 0, interrupt::policies<>,
interrupt::id_irq<
en_field_0_t,
interrupt::policies<interrupt::required_resources<test_resource_0>>>,
interrupt::sub_irq<
en_field_1_t, sts_field_t,
interrupt::policies<interrupt::required_resources<test_resource_1>>,
Expand All @@ -79,59 +84,74 @@ using dynamic_t = interrupt::dynamic_controller<config_t>;

auto reset_dynamic_state() -> void {
register_value = 0;

dynamic_t::disable<test_flow_1_t, test_flow_2_t>();
dynamic_t::turn_on_resource<test_resource_0>();
dynamic_t::turn_on_resource<test_resource_1>();
dynamic_t::turn_on_resource<test_resource_2>();

dynamic_t::enable_by_field<true, en_field_0_t>();
CHECK(register_value == 0b1);
}
} // namespace

TEST_CASE("enable one irq", "[dynamic controller]") {
reset_dynamic_state();
dynamic_t::enable<test_flow_1_t>();
CHECK(register_value == 0b1);
CHECK(register_value == 0b11);
}

TEST_CASE("enable multiple irqs", "[dynamic controller]") {
reset_dynamic_state();
dynamic_t::enable<test_flow_1_t, test_flow_2_t>();
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<test_flow_2_t>();
CHECK(register_value == 0b100);
CHECK(register_value == 0b1001);

dynamic_t::turn_off_resource<test_resource_2>();
CHECK(register_value == 0);
CHECK(register_value == 0b1);
dynamic_t::turn_on_resource<test_resource_2>();
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<test_resource_0>();
CHECK(register_value == 0);
dynamic_t::turn_on_resource<test_resource_0>();
CHECK(register_value == 0b1);
}

TEST_CASE("disabling resource disables only irqs that require it",
"[dynamic controller]") {
reset_dynamic_state();

dynamic_t::enable<test_flow_1_t, test_flow_2_t>();
CHECK(register_value == 0b101);
CHECK(register_value == 0b1011);

dynamic_t::turn_off_resource<test_resource_2>();
CHECK(register_value == 0b1);
CHECK(register_value == 0b11);
dynamic_t::turn_on_resource<test_resource_2>();
CHECK(register_value == 0b101);
CHECK(register_value == 0b1011);
}

TEST_CASE("disable resource that multiple irqs require",
"[dynamic controller]") {
reset_dynamic_state();

dynamic_t::enable<test_flow_1_t, test_flow_2_t>();
CHECK(register_value == 0b101);
CHECK(register_value == 0b1011);

dynamic_t::turn_off_resource<test_resource_1>();
CHECK(register_value == 0);
CHECK(register_value == 0b1);
dynamic_t::turn_on_resource<test_resource_1>();
CHECK(register_value == 0b101);
CHECK(register_value == 0b1011);
}
66 changes: 56 additions & 10 deletions test/interrupt/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<config_a, test_nexus>{};
inited = false;
enabled<17_irq> = false;
Expand All @@ -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<config_a, test_nexus>{};
flow_run<flow_1> = false;

Expand All @@ -39,7 +39,7 @@ TEST_CASE("run single flow", "[flow]") {
CHECK(flow_run<flow_1>);
}

TEST_CASE("run multiple flows", "[flow]") {
TEST_CASE("run multiple flows", "[manager]") {
auto m = interrupt::manager<config_b, test_nexus>{};
flow_run<flow_1> = false;
flow_run<flow_2> = false;
Expand All @@ -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<config_a, test_nexus, alt_nexus>{};
flow_run<flow_1> = false;
flow_run<alt_flow<flow_1>> = false;
Expand Down Expand Up @@ -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<config_shared, test_nexus>{};
inited = false;
enabled<33_irq> = false;
Expand All @@ -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<config_shared, test_nexus>{};
enable_field_t<33'1>::value = false;
enable_field_t<33'2>::value = false;
Expand All @@ -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<config_shared, test_nexus>{};
enable_field_t<33'1>::value = true;
status_field_t<33'1>::value = true;
Expand All @@ -137,7 +137,7 @@ TEST_CASE("run flows if sub_irq is enabled", "[flow]") {
CHECK(not flow_run<flow_33_2>);
}

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::shared_irq<
33_irq, 34, interrupt::policies<>,
interrupt::sub_irq<enable_field_t<33'0>, status_field_t<33'0>,
Expand All @@ -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::shared_irq<
33_irq, 34, interrupt::policies<>,
interrupt::sub_irq<enable_field_t<33'0>, status_field_t<33'0>,
Expand Down Expand Up @@ -191,7 +191,7 @@ using config_shared_sub = root<interrupt::shared_irq<
interrupt::policies<>, 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<config_shared_sub, test_nexus>{};
enable_field_t<33'1>::value = false;

Expand All @@ -213,3 +213,49 @@ TEST_CASE("run flows for shared sub irqs if enabled", "[flow]") {
CHECK(flow_run<flow_33_2_1>);
CHECK(not flow_run<flow_33_2_2>);
}

namespace {
using config_shared_no_enable = root<interrupt::shared_irq<
33_irq, 34, interrupt::policies<>,
interrupt::sub_irq<interrupt::enable_t<>, status_field_t<33'1>,
interrupt::policies<>, flow_33_1>>>;
using config_shared_no_status = root<interrupt::shared_irq<
33_irq, 34, interrupt::policies<>,
interrupt::sub_irq<enable_field_t<33'1>, interrupt::status_t<>,
interrupt::policies<>, flow_33_1>>>;
} // namespace

TEST_CASE("run flows with no enable field", "[manager]") {
auto m = interrupt::manager<config_shared_no_enable, test_nexus>{};
status_field_t<33'1>::value = true;
flow_run<flow_33_1> = false;

m.run<33_irq>();

CHECK(not status_field_t<33'2'1>::value);
CHECK(flow_run<flow_33_1>);
}

TEST_CASE("run flows with no status field", "[manager]") {
auto m = interrupt::manager<config_shared_no_status, test_nexus>{};
enable_field_t<33'1>::value = true;
flow_run<flow_33_1> = false;

m.run<33_irq>();

CHECK(flow_run<flow_33_1>);
}

namespace {
using config_shared_id = root<interrupt::shared_irq<
33_irq, 34, interrupt::policies<>,
interrupt::id_irq<enable_field_t<33'0>, interrupt::policies<>>>>;
} // namespace

TEST_CASE("init enables id interrupts", "[manager]") {
auto m = interrupt::manager<config_shared_id, test_nexus>{};

enable_field_t<33'0>::value = false;
m.init();
CHECK(enable_field_t<33'0>::value);
}
Loading