Software APIs
mock_mmio.h
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_LIB_BASE_MOCK_MMIO_H_
6 #define OPENTITAN_SW_DEVICE_LIB_BASE_MOCK_MMIO_H_
7 
8 #include <initializer_list>
9 #include <memory>
10 #include <random>
11 #include <stdint.h>
12 #include <string.h>
13 #include <vector>
14 
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
18 #include "sw/device/lib/base/mock_mmio_test_utils.h"
19 
20 namespace mock_mmio {
21 /**
22  * Reads a little-endian integer from `bytes`. This function is lazy, and will
23  * only perform the converion when used with an EXPECT_* macro. For example:
24  * EXPECT_READ32(offset, LeInt("abcd"));
25  *
26  * It is not possible to directly pass in string literals into EXPECT_* macros;
27  * this is a limitation of C++'s implicit conversion rules and overload
28  * resolution order.
29  */
30 inline mock_mmio::LittleEndianBytes LeInt(const char *bytes) { return {bytes}; }
31 
32 /**
33  * A MockDevice represents a mock implementation of an MMIO device.
34  *
35  * MockDevice provides two mockable member functions, representing a read and a
36  * write at a particular offset from the base address. This class can be
37  * converted into a `mmio_region_t` value, which, when used in `mmio.h`
38  * functions like `read32()`, will map to the appropriate mock member function
39  * calls.
40  *
41  * To maintain sequencing, `ReadN()` and `WriteN()` should not be
42  * `EXPECT_CALL`'ed directly; instead, `EXPECT_READN` and `EXPECT_WRITEN` should
43  * be used, instead.
44  *
45  * To use this class, `-DMOCK_MMIO` must be enabled in all translation units
46  * using `mmio.h`.
47  */
48 class MockDevice {
49  public:
50  MockDevice() = default;
51 
52  MockDevice(const MockDevice &) = delete;
53  MockDevice &operator=(const MockDevice &) = delete;
54  MockDevice(MockDevice &&) = delete;
55  MockDevice &operator=(MockDevice &&) = delete;
56 
57  /**
58  * Converts this MockDevice into a mmio_region_t opaque object,
59  * which is compatible with `mmio.h` functions.
60  */
61  mmio_region_t region() { return {this}; }
62 
63  MOCK_METHOD(uint8_t, Read8, (ptrdiff_t offset));
64  MOCK_METHOD(uint32_t, Read32, (ptrdiff_t offset));
65 
66  MOCK_METHOD(void, Write8, (ptrdiff_t offset, uint8_t value));
67  MOCK_METHOD(void, Write32, (ptrdiff_t offset, uint32_t value));
68 
69  /**
70  * Generates "garbage memory" for use in tests. This function should not
71  * be called directly.
72  */
73  template <typename Int>
74  Int GarbageMemory() {
75  return std::uniform_int_distribution<Int>()(gen_);
76  }
77 
78  private:
79  static std::random_device rd;
80  std::mt19937 gen_ = std::mt19937(rd());
81 };
82 
83 /**
84  * Conveninence fixture for creating device tests.
85  *
86  * This class should be derived by a test fixture (along with `testing::Test`)
87  * and used in a `TEST_F` block. Doing so will make the `EXPECT_READN` and
88  * `EXPECT_WRITEN` conveinence macros useable.
89  *
90  * The device being mocked can be accessed in the test body with `this->dev()`.
91  * `this->` is required in this case, since the name `dev` is not immediately
92  * visible.
93  */
94 class MmioTest {
95  protected:
96  MockDevice &dev() { return *dev_; }
97 
98  private:
99  std::unique_ptr<MockDevice> dev_ =
100  std::make_unique<testing::StrictMock<MockDevice>>();
101  testing::InSequence seq_;
102 };
103 } // namespace mock_mmio
104 
105 /**
106  * Expect a read to the device `dev` at the given offset, returning the given
107  * 8-bit value.
108  *
109  * The value may be given as an integer, a pointer to little-endian data,
110  * or a `std::initializer_list<BitField>`.
111  *
112  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
113  * calls.
114  */
115 #define EXPECT_READ8_AT(dev, offset, ...) \
116  EXPECT_CALL(dev, Read8(offset)) \
117  .WillOnce(testing::Return(mock_mmio::ToInt<uint8_t>(__VA_ARGS__)))
118 
119 /**
120  * Expect a read to the device `dev` at the given offset, returning the given
121  * 32-bit value.
122  *
123  * The value may be given as an integer, a pointer to little-endian data,
124  * or a `std::initializer_list<BitField>`.
125  *
126  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
127  * calls.
128  */
129 #define EXPECT_READ32_AT(dev, offset, ...) \
130  EXPECT_CALL(dev, Read32(offset)) \
131  .WillOnce(testing::Return(mock_mmio::ToInt<uint32_t>(__VA_ARGS__)))
132 
133 /**
134  * Expect a write to the device `dev` at the given offset with the given 8-bit
135  * value.
136  *
137  * The value may be given as an integer, a pointer to little-endian data,
138  * or a `std::initializer_list<BitField>`.
139  *
140  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
141  * calls.
142  */
143 #define EXPECT_WRITE8_AT(dev, offset, ...) \
144  EXPECT_CALL(dev, Write8(offset, mock_mmio::ToInt<uint8_t>(__VA_ARGS__)))
145 
146 /**
147  * Expect a write to the device `dev` at the given offset with the given 32-bit
148  * value.
149  *
150  * The value may be given as an integer, a pointer to little-endian data,
151  * or a `std::initializer_list<BitField>`.
152  *
153  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
154  * calls.
155  */
156 #define EXPECT_WRITE32_AT(dev, offset, ...) \
157  EXPECT_CALL(dev, Write32(offset, mock_mmio::ToInt<uint32_t>(__VA_ARGS__)))
158 
159 /**
160  * Expect a read at the given offset, returning the given 8-bit value.
161  *
162  * The value may be given as an integer, a pointer to little-endian data,
163  * or a `std::initializer_list<BitField>`.
164  *
165  * This function is only available in tests using a fixture that derives
166  * `MmioTest`.
167  *
168  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
169  * calls.
170  */
171 #define EXPECT_READ8(offset, ...) \
172  EXPECT_READ8_AT(this->dev(), offset, __VA_ARGS__)
173 
174 /**
175  * Expect a read at the given offset, returning the given 32-bit value.
176  *
177  * The value may be given as an integer, a pointer to little-endian data,
178  * or a `std::initializer_list<BitField>`.
179  *
180  * This function is only available in tests using a fixture that derives
181  * `MmioTest`.
182  *
183  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
184  * calls.
185  */
186 #define EXPECT_READ32(offset, ...) \
187  EXPECT_READ32_AT(this->dev(), offset, __VA_ARGS__)
188 
189 /**
190  * Expect a write to the given offset with the given 8-bit value.
191  *
192  * The value may be given as an integer, a pointer to little-endian data,
193  * or a `std::initializer_list<BitField>`.
194  *
195  * This function is only available in tests using a fixture that derives
196  * `MmioTest`.
197  *
198  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
199  * calls.
200  */
201 #define EXPECT_WRITE8(offset, ...) \
202  EXPECT_WRITE8_AT(this->dev(), offset, __VA_ARGS__);
203 
204 /**
205  * Expect a shadowed write to the given offset with the given 8-bit value.
206  *
207  * The value may be given as an integer, a pointer to little-endian data,
208  * or a `std::initializer_list<BitField>`.
209  *
210  * This function is only available in tests using a fixture that derives
211  * `MmioTest`.
212  *
213  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
214  * calls.
215  */
216 #define EXPECT_WRITE8_SHADOWED(offset, ...) \
217  EXPECT_WRITE8(offset, __VA_ARGS__); \
218  EXPECT_WRITE8(offset, __VA_ARGS__);
219 
220 /**
221  * Expect a write to the given offset with the given 32-bit value.
222  *
223  * The value may be given as an integer, a pointer to little-endian data,
224  * or a `std::initializer_list<BitField>`.
225  *
226  * This function is only available in tests using a fixture that derives
227  * `MmioTest`.
228  *
229  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
230  * calls.
231  */
232 #define EXPECT_WRITE32(offset, ...) \
233  EXPECT_WRITE32_AT(this->dev(), offset, __VA_ARGS__);
234 
235 /**
236  * Expect a shadowed write to the given offset with the given 32-bit value.
237  *
238  * The value may be given as an integer, a pointer to little-endian data,
239  * or a `std::initializer_list<BitField>`.
240  *
241  * This function is only available in tests using a fixture that derives
242  * `MmioTest`.
243  *
244  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
245  * calls.
246  */
247 #define EXPECT_WRITE32_SHADOWED(offset, ...) \
248  EXPECT_WRITE32(offset, __VA_ARGS__); \
249  EXPECT_WRITE32(offset, __VA_ARGS__);
250 
251 #define EXPECT_MASK_INTERNAL_(width, dev, off, ...) \
252  do { \
253  auto &device = dev; \
254  std::initializer_list<mock_mmio::MaskedBitField> fields = __VA_ARGS__; \
255  \
256  using Int = uint##width##_t; \
257  auto val = device.GarbageMemory<Int>(); \
258  EXPECT_READ##width##_AT(device, off, val); \
259  \
260  for (auto field : fields) { \
261  ASSERT_LT(field.offset, sizeof(Int) * 8); \
262  ASSERT_LE(field.mask, std::numeric_limits<Int>::max()); \
263  ASSERT_LE(field.value, field.mask); \
264  \
265  val &= ~static_cast<Int>(field.mask << field.offset); \
266  val |= static_cast<Int>(field.value << field.offset); \
267  } \
268  EXPECT_WRITE##width##_AT(device, off, val); \
269  } while (false)
270 
271 /**
272  * Expect an unspecified 8-bit read at the given offset, followed by a write to
273  * the same location, with the same value but with some bits changed; the
274  * remaining bits must be untouched.
275  *
276  * The changed bits are specified by a `std::initializer_list<MaskedBitField>`.
277  *
278  * This function is only available in tests using a fixture that derives
279  * `MmioTest`.
280  *
281  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
282  * calls.
283  */
284 #define EXPECT_MASK8(offset, ...) \
285  EXPECT_MASK_INTERNAL_(8, this->dev(), offset, __VA_ARGS__)
286 
287 /**
288  * Expect an unspecified 32-bit read at the given offset, followed by a write to
289  * the same location, with the same value but with some bits changed; the
290  * remaining bits must be untouched.
291  *
292  * The changed bits are specified by a `std::initializer_list<MaskedBitField>`.
293  *
294  * This function is only available in tests using a fixture that derives
295  * `MmioTest`.
296  *
297  * This expectation is sequenced with all other `EXPECT_READ` and `EXPECT_WRITE`
298  * calls.
299  */
300 #define EXPECT_MASK32(offset, ...) \
301  EXPECT_MASK_INTERNAL_(32, this->dev(), offset, __VA_ARGS__)
302 
303 #endif // OPENTITAN_SW_DEVICE_LIB_BASE_MOCK_MMIO_H_