diff --git a/include/groov/write.hpp b/include/groov/write.hpp index 45cc893..25ad5db 100644 --- a/include/groov/write.hpp +++ b/include/groov/write.hpp @@ -118,9 +118,9 @@ auto write(Spec const &s) -> async::sender auto { all_fields_per_reg_t, written_fields_per_reg_t>; - using field_masks_t = boost::mp11::mp_transform; + using field_masks_t = + boost::mp11::mp_transform; detail::check_read_only(); detail::check_rmw(value).value_or(0); }); - groov::sync_write(grp0("reg0"_r = 0x76)); + groov::sync_write(grp0("reg0"_r = 0x10325476u)); CHECK(data0 == 0xa5a5'a5a5u); CHECK(my_bus::num_reads == 0); CHECK(my_bus::num_writes == 0); CHECK(write_call_count == 1); CHECK(write_addr == &data0); - CHECK(write_value == 0xbabefa76u); + CHECK(write_value == 0x10325476u); auto v = groov::test::get_value("reg0"_f); REQUIRE(v); CHECK(*v == 0xbabefaceu); diff --git a/test/write.cpp b/test/write.cpp index 7f0858b..2f82bab 100644 --- a/test/write.cpp +++ b/test/write.cpp @@ -15,6 +15,7 @@ #include #include +#include namespace { struct bus { @@ -549,3 +550,74 @@ TEST_CASE("write a field in a register containing WO fields which won't be " CHECK(groov::write(grp_be("reg4.field0"_r = 1)) | async::sync_wait()); CHECK(data3 == 42); } + +namespace { +struct rmw_check_bus { + static inline std::uint32_t expected_mask{}; + + template + static auto write(auto addr, auto value) -> async::sender auto { + CHECK((Mask | IdMask) == expected_mask); + return async::just_result_of([=] { + auto prev = *addr & ~(Mask | IdMask); + *addr = prev | value | IdValue; + }); + } + + template + static auto read(auto addr) -> async::sender auto { + return async::just_result_of([=] { return *addr; }); + } +}; + +std::uint32_t rmw_check_data{}; +using rmw_check_R = groov::reg<"r", std::uint32_t, &rmw_check_data, + groov::w::replace, F0, F1, F2>; + +using rmw_check_G = groov::group<"group", rmw_check_bus, rmw_check_R>; +constexpr auto rmw_check_grp = rmw_check_G{}; +} // namespace + +TEST_CASE("writing a register that is not covered by fields does not RMW", + "[write]") { + using namespace groov::literals; + rmw_check_bus::expected_mask = 0xffff'ffffu; + rmw_check_data = 0; + CHECK(groov::write(rmw_check_grp("r"_r = 0xa5a5u)) | async::sync_wait()); + CHECK(rmw_check_data == 0xa5a5u); +} + +TEST_CASE("writing register fields in a register that is not covered by fields " + "incurs RMW", + "[write]") { + using namespace groov::literals; + rmw_check_bus::expected_mask = 0xffu; + rmw_check_data = 0; + CHECK( + groov::write(rmw_check_grp("r.field0"_f = 0b1u, "r.field1"_f = 0b1010u, + "r.field2"_f = 0b110u)) | + async::sync_wait()); + CHECK(rmw_check_data == 0b110'1010'1u); +} + +namespace { +using F_overlap = groov::field<"f_overlap", std::uint8_t, 3, 0>; + +std::uint32_t overlap_data{}; +using R_overlap = groov::reg<"r", std::uint32_t, &overlap_data, + groov::w::replace, F0, F_overlap>; + +using G_overlap = groov::group<"group", bus, R_overlap>; +constexpr auto overlap_grp = G_overlap{}; +} // namespace + +TEST_CASE("overlapping writes: last one takes priority", "[write]") { + using namespace groov::literals; + overlap_data = 0xffff'ffffu; + CHECK(sync_write(overlap_grp("r.f_overlap"_r = 0, "r.field0"_f = 1))); + CHECK(overlap_data == 0xffff'fff1u); + + overlap_data = 0xffff'ffffu; + CHECK(sync_write(overlap_grp("r.field0"_f = 1, "r.f_overlap"_r = 0))); + CHECK(overlap_data == 0xffff'fff0u); +}