[sw/mmio] Add _shadowed variants of write8 and write32 mmio functions Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/sw/device/lib/base/mmio.c b/sw/device/lib/base/mmio.c index 22625b6..c129955 100644 --- a/sw/device/lib/base/mmio.c +++ b/sw/device/lib/base/mmio.c
@@ -128,6 +128,10 @@ uint8_t value); extern void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value); +extern void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset, + uint8_t value); +extern void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset, + uint32_t value); extern uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset, uint32_t mask, uint32_t mask_index); extern bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
diff --git a/sw/device/lib/base/mmio.h b/sw/device/lib/base/mmio.h index cf1aa11..b1ac07d 100644 --- a/sw/device/lib/base/mmio.h +++ b/sw/device/lib/base/mmio.h
@@ -54,7 +54,9 @@ * An mmio_region_t is an opaque handle to an MMIO region; it should only be * modified using the functions provided in this header. */ -typedef struct mmio_region { volatile void *base; } mmio_region_t; +typedef struct mmio_region { + volatile void *base; +} mmio_region_t; /** * Create a new `mmio_region_t` from the given address. @@ -118,6 +120,23 @@ } /** + * Writes an aligned uint8_t to the MMIO region `base` at the given byte + * offset via two subsequent write operations. + * + * This function is guaranteed to commit a write to memory, and will not be + * reordered with respect to other region manipulations. + * + * @param base the region to write to. + * @param offset the offset to write at, in bytes. + * @param value the value to write. + */ +inline void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset, + uint8_t value) { + ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value; + ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value; +} + +/** * Writes an aligned uint32_t to the MMIO region `base` at the given byte * offset. * @@ -132,6 +151,23 @@ uint32_t value) { ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value; } + +/** + * Writes an aligned uint32_t to the MMIO region `base` at the given byte + * offset via two subsequent write operations. + * + * This function is guaranteed to commit a write to memory, and will not be + * reordered with respect to other region manipulations. + * + * @param base the region to write to. + * @param offset the offset to write at, in bytes. + * @param value the value to write. + */ +inline void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset, + uint32_t value) { + ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value; + ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value; +} #else // MOCK_MMIO /** * "Instrumented" mmio_region_t. @@ -141,7 +177,9 @@ * version of `mmio_region_t`, which prevents users from being able to access * the pointer inside. */ -typedef struct mmio_region { void *mock; } mmio_region_t; +typedef struct mmio_region { + void *mock; +} mmio_region_t; /** * Stubbed-out read/write operations for overriding by a testing library. @@ -155,6 +193,10 @@ void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value); void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value); +void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset, + uint8_t value); +void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset, + uint32_t value); #endif // MOCK_MMIO /**
diff --git a/sw/device/lib/base/testing/mock_mmio.cc b/sw/device/lib/base/testing/mock_mmio.cc index 602e1a5..fbf397b 100644 --- a/sw/device/lib/base/testing/mock_mmio.cc +++ b/sw/device/lib/base/testing/mock_mmio.cc
@@ -31,9 +31,23 @@ dev->Write8(offset, value); } +void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset, + uint8_t value) { + auto *dev = static_cast<MockDevice *>(base.mock); + dev->Write8(offset, value); + dev->Write8(offset, value); +} + void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value) { auto *dev = static_cast<MockDevice *>(base.mock); dev->Write32(offset, value); } + +void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset, + uint32_t value) { + auto *dev = static_cast<MockDevice *>(base.mock); + dev->Write32(offset, value); + dev->Write32(offset, value); +} } // extern "C" } // namespace mock_mmio
diff --git a/sw/device/lib/base/testing/mock_mmio.h b/sw/device/lib/base/testing/mock_mmio.h index a9dff55..995f326 100644 --- a/sw/device/lib/base/testing/mock_mmio.h +++ b/sw/device/lib/base/testing/mock_mmio.h
@@ -202,6 +202,22 @@ EXPECT_WRITE8_AT(this->dev(), offset, __VA_ARGS__); /** + * Expect a shadowed write to the given offset with the given 8-bit value. + * + * The value may be given as an integer, a pointer to little-endian data, + * or a `std::initializer_list<BitField>`. + * + * This function is only available in tests using a fixture that derives + * `MmioTest`. + * + * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE` + * calls. + */ +#define EXPECT_WRITE8_SHADOWED(offset, ...) \ + EXPECT_WRITE8(offset, __VA_ARGS__); \ + EXPECT_WRITE8(offset, __VA_ARGS__); + +/** * Expect a write to the given offset with the given 32-bit value. * * The value may be given as an integer, a pointer to little-endian data, @@ -216,6 +232,22 @@ #define EXPECT_WRITE32(offset, ...) \ EXPECT_WRITE32_AT(this->dev(), offset, __VA_ARGS__); +/** + * Expect a shadowed write to the given offset with the given 32-bit value. + * + * The value may be given as an integer, a pointer to little-endian data, + * or a `std::initializer_list<BitField>`. + * + * This function is only available in tests using a fixture that derives + * `MmioTest`. + * + * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE` + * calls. + */ +#define EXPECT_WRITE32_SHADOWED(offset, ...) \ + EXPECT_WRITE32(offset, __VA_ARGS__); \ + EXPECT_WRITE32(offset, __VA_ARGS__); + #define EXPECT_MASK_INTERNAL_(width, dev, off, ...) \ do { \ auto &device = dev; \
diff --git a/sw/device/silicon_creator/lib/base/abs_mmio.c b/sw/device/silicon_creator/lib/base/abs_mmio.c index bb982b0..b092f86 100644 --- a/sw/device/silicon_creator/lib/base/abs_mmio.c +++ b/sw/device/silicon_creator/lib/base/abs_mmio.c
@@ -8,5 +8,7 @@ // header a link location. extern uint8_t abs_mmio_read8(uint32_t addr); extern void abs_mmio_write8(uint32_t addr, uint8_t value); +extern void abs_mmio_write8_shadowed(uint32_t addr, uint8_t value); extern uint32_t abs_mmio_read32(uint32_t addr); extern void abs_mmio_write32(uint32_t addr, uint32_t value); +extern void abs_mmio_write32_shadowed(uint32_t addr, uint32_t value);
diff --git a/sw/device/silicon_creator/lib/base/abs_mmio.h b/sw/device/silicon_creator/lib/base/abs_mmio.h index 542a845..b87490c 100644 --- a/sw/device/silicon_creator/lib/base/abs_mmio.h +++ b/sw/device/silicon_creator/lib/base/abs_mmio.h
@@ -59,6 +59,18 @@ } /** + * Writes uint8_t to the MMIO `addr` via + * two subsequent write operations. + * + * @param addr the address to write to. + * @param value the value to write. + */ +inline void abs_mmio_write8_shadowed(uint32_t addr, uint8_t value) { + *((volatile uint8_t *)addr) = value; + *((volatile uint8_t *)addr) = value; +} + +/** * Reads an aligned uint32_t from MMIO `addr`. * * @param addr the address to read from. @@ -70,7 +82,7 @@ } /** - * Writes an aligned uint32_t to the MMIO region `addr`. + * Writes an aligned uint32_t to the MMIO `addr`. * * @param addr the address to write to. * @param value the value to write. @@ -79,12 +91,26 @@ *((volatile uint32_t *)addr) = value; } +/** + * Writes an aligned uint32_t to the MMIO `addr` via + * two subsequent write operations. + * + * @param addr the address to write to. + * @param value the value to write. + */ +inline void abs_mmio_write32_shadowed(uint32_t addr, uint32_t value) { + *((volatile uint32_t *)addr) = value; + *((volatile uint32_t *)addr) = value; +} + #else // MOCK_ABS_MMIO extern uint8_t abs_mmio_read8(uint32_t addr); extern void abs_mmio_write8(uint32_t addr, uint8_t value); +extern void abs_mmio_write8_shadowed(uint32_t addr, uint8_t value); extern uint32_t abs_mmio_read32(uint32_t addr); extern void abs_mmio_write32(uint32_t addr, uint32_t value); +extern void abs_mmio_write32_shadowed(uint32_t addr, uint32_t value); #endif // MOCK_ABS_MMIO
diff --git a/sw/device/silicon_creator/lib/base/mock_abs_mmio.h b/sw/device/silicon_creator/lib/base/mock_abs_mmio.h index 6cbd800..a470e81 100644 --- a/sw/device/silicon_creator/lib/base/mock_abs_mmio.h +++ b/sw/device/silicon_creator/lib/base/mock_abs_mmio.h
@@ -57,6 +57,22 @@ EXPECT_CALL(mmio, Write8(addr, mock_mmio::ToInt<uint8_t>(__VA_ARGS__))); /** + * Expect a shadowed write to the given offset with the given 8-bit value. + * + * The value may be given as an integer, a pointer to little-endian data, + * or a `std::initializer_list<BitField>`. + * + * This function is only available in tests using a fixture that derives + * `MmioTest`. + * + * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE` + * calls. + */ +#define EXPECT_ABS_WRITE8_SHADOWED(mmio, addr, ...) \ + EXPECT_CALL(mmio, Write8(addr, mock_mmio::ToInt<uint8_t>(__VA_ARGS__))); \ + EXPECT_CALL(mmio, Write8(addr, mock_mmio::ToInt<uint8_t>(__VA_ARGS__))); + +/** * Expect a read to the device `dev` at the given offset, returning the given * 32-bit value. * @@ -85,6 +101,22 @@ #define EXPECT_ABS_WRITE32(mmio, addr, ...) \ EXPECT_CALL(mmio, Write32(addr, mock_mmio::ToInt<uint32_t>(__VA_ARGS__))); +/** + * Expect a shadowed write to the given offset with the given 32-bit value. + * + * The value may be given as an integer, a pointer to little-endian data, + * or a `std::initializer_list<BitField>`. + * + * This function is only available in tests using a fixture that derives + * `MmioTest`. + * + * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE` + * calls. + */ +#define EXPECT_ABS_WRITE32_SHADOWED(mmio, addr, ...) \ + EXPECT_CALL(mmio, Write32(addr, mock_mmio::ToInt<uint32_t>(__VA_ARGS__))); \ + EXPECT_CALL(mmio, Write32(addr, mock_mmio::ToInt<uint32_t>(__VA_ARGS__))); + extern "C" { uint8_t abs_mmio_read8(uint32_t addr) { @@ -95,6 +127,11 @@ return MockAbsMmio::Instance().Write8(addr, value); } +void abs_mmio_write8_shadowed(uint32_t addr, uint8_t value) { + MockAbsMmio::Instance().Write8(addr, value); + return MockAbsMmio::Instance().Write8(addr, value); +} + uint32_t abs_mmio_read32(uint32_t addr) { return MockAbsMmio::Instance().Read32(addr); } @@ -103,6 +140,11 @@ return MockAbsMmio::Instance().Write32(addr, value); } +void abs_mmio_write32_shadowed(uint32_t addr, uint32_t value) { + MockAbsMmio::Instance().Write32(addr, value); + return MockAbsMmio::Instance().Write32(addr, value); +} + } // extern "C" } // namespace mask_rom_test