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
20namespace 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 */
30inline 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 */
48class 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>
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 */
94class 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_