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
55 changes: 38 additions & 17 deletions include/interrupt/dynamic_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,12 @@ struct dynamic_controller {

using irqs_t = detail::collect_t<Root, stdx::type_list>;

// cached values for each enable register
// shadow values for each enable register
template <typename Register>
using register_t = typename Hal::template register_datatype_t<Register>;

template <typename Register>
constinit static inline register_t<Register> cached_enable{};
constinit static inline register_t<Register> shadow_enable{};

// which IRQs are potentially affected by a change?
template <typename PropagatePolicy, typename... Ts>
Expand Down Expand Up @@ -339,20 +339,20 @@ struct dynamic_controller {
}
}

constexpr static auto force_write = [](auto reg, auto &cached_value,
constexpr static auto force_write = [](auto reg, auto &shadow_value,
auto new_value) {
cached_value = new_value;
Hal::write(reg, cached_value);
shadow_value = new_value;
Hal::write(reg, shadow_value);
};

constexpr static auto write_if_changed = [](auto reg, auto &cached_value,
constexpr static auto write_if_changed = [](auto reg, auto &shadow_value,
auto new_value) {
if (new_value != std::exchange(cached_value, new_value)) {
Hal::write(reg, cached_value);
if (new_value != std::exchange(shadow_value, new_value)) {
Hal::write(reg, shadow_value);
}
};

// update cached values and write the changed registers
// update shadow values and write the changed registers
template <auto WriteFn = write_if_changed,
template <typename...> typename L, typename... Irqs>
static auto update(L<Irqs...>) -> void {
Expand All @@ -374,9 +374,9 @@ struct dynamic_controller {
(compute_register<reg_t, I>(value), ...,
compute_register<reg_t, Is>(value));

auto &cached_value = cached_enable<reg_t>;
auto new_value = (cached_value & ~mask) | value;
WriteFn(r, cached_value, new_value);
auto &shadow_value = shadow_enable<reg_t>;
auto new_value = (shadow_value & ~mask) | value;
WriteFn(r, shadow_value, new_value);
}
},
by_registers);
Expand All @@ -391,7 +391,7 @@ struct dynamic_controller {
}

// reset: mark resources, flows and all named irqs enabled
// and clear all cached state
// and clear all shadow state
template <typename ActiveResources, typename ActiveFlows, typename IrqList>
static auto reset_internal_state() -> void {
resource_enables = resources_t{ActiveResources{}};
Expand All @@ -407,7 +407,26 @@ struct dynamic_controller {
decltype(detail::register_for<
typename I::enable_field_t>::template fn<Hal>());
if constexpr (not is_no_register_v<reg_t>) {
cached_enable<reg_t> = {};
shadow_enable<reg_t> = {};
}
},
by_registers);
}

// set: mark shadow state for irqs which are already enabled
template <typename EnabledIrqs> static auto set_internal_state() -> void {
constexpr auto by_registers =
stdx::gather_by<detail::get_register_q<Hal>::template from_irq>(
EnabledIrqs{});
stdx::for_each(
[]<typename I, typename... Is>(stdx::tuple<I, Is...>) -> void {
using reg_t =
decltype(detail::register_for<
typename I::enable_field_t>::template fn<Hal>());
if constexpr (not is_no_register_v<reg_t>) {
auto &value = shadow_enable<reg_t>;
((value |= mask_for<reg_t, I>()), ...,
(value |= mask_for<reg_t, Is>()));
}
},
by_registers);
Expand All @@ -434,9 +453,10 @@ struct dynamic_controller {
using potential_enable_irqs_t =
boost::mp11::mp_copy_if<detail::descendants_t<Root>,
detail::has_enable_field>;
using enable_irqs_t = boost::mp11::mp_set_difference<
potential_enable_irqs_t,
stdx::tuple<typename AlreadyEnabled::config_t...>>;
using active_irqs_t = stdx::tuple<typename AlreadyEnabled::config_t...>;
using enable_irqs_t =
boost::mp11::mp_set_difference<potential_enable_irqs_t,
active_irqs_t>;

using active_resources_t =
Policy::template active_resources_t<resources_t>;
Expand All @@ -445,6 +465,7 @@ struct dynamic_controller {
conc::call_in_critical_section<mutex_t>([] {
reset_internal_state<active_resources_t, active_flows_t,
potential_enable_irqs_t>();
set_internal_state<active_irqs_t>();
update(enable_irqs_t{});
});
}
Expand Down
4 changes: 3 additions & 1 deletion include/interrupt/policies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ using no_irqs_policy = irqs_policy<>;

struct default_policy : all_resources_policy,
all_flows_policy,
all_irqs_policy {};
all_irqs_policy {
constexpr static auto dynamic_enable_top_level = false;
};
} // namespace dynamic_init
} // namespace interrupt
25 changes: 25 additions & 0 deletions test/interrupt/dynamic_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,31 @@ TEST_CASE("dynamic init enables all fields", "[dynamic controller]") {
CHECK(*v == EN_TOP::mask<std::uint32_t>);
}

TEST_CASE("dynamic init correctly handles top-level fields after manager init",
"[dynamic controller]") {
using namespace groov::literals;
reset_dynamic_state();

// manager init statically enables top level IRQs
using M = interrupt::manager<config_t, test_hal<G>, test_nexus>;
groov::test::set_value<G>("enable_top"_r, EN_TOP::mask<std::uint32_t>);
M::init<interrupt::dynamic_init::default_policy>();

auto v = groov::test::get_value<G>("enable_top"_r);
REQUIRE(v);
CHECK(*v == EN_TOP::mask<std::uint32_t>);

M::dynamic_t::disable<"shared">();
v = groov::test::get_value<G>("enable_top"_r);
REQUIRE(v);
CHECK(*v == 0);

M::dynamic_t::enable<"shared">();
v = groov::test::get_value<G>("enable_top"_r);
REQUIRE(v);
CHECK(*v == EN_TOP::mask<std::uint32_t>);
}

namespace {
using noflows_config_t = interrupt::root<interrupt::shared_irq<
"shared", 0_irq, 0, stdx::cts_t<"enable_top.top"_cts>,
Expand Down