Software APIs
dif_rv_plic_unittest.cc
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 
6 
7 #include <array>
8 
9 #include "gtest/gtest.h"
11 #include "sw/device/lib/base/mock_mmio.h"
13 
14 #include "rv_plic_regs.h" // Generated.
15 
16 namespace dif_rv_plic_unittest {
17 namespace {
20 using testing::Test;
21 
22 // If either of these static assertions fail, then the unit-tests for related
23 // API should be revisited.
24 static_assert(RV_PLIC_PARAM_NUM_SRC == 186,
25  "PLIC instantiation parameters have changed.");
26 static_assert(RV_PLIC_PARAM_NUM_TARGET == 1,
27  "PLIC instantiation parameters have changed.");
28 
29 constexpr uint32_t kTarget0 = 0;
30 constexpr uint32_t kFirstIrq = 1;
31 
32 class PlicTest : public Test, public MmioTest {
33  protected:
34  dif_rv_plic_t plic_ = {.base_addr = dev().region()};
35 };
36 
37 class ResetTest : public PlicTest {
38  protected:
39  void ExpectReset() {
40  // Priority registers.
41  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
42  ptrdiff_t offset = RV_PLIC_PRIO_0_REG_OFFSET + (sizeof(uint32_t) * i);
43  EXPECT_WRITE32(offset, 0);
44  }
45 
46  // Interrupt enable multireg.
47  EXPECT_WRITE32(RV_PLIC_IE0_0_REG_OFFSET, 0);
48  EXPECT_WRITE32(RV_PLIC_IE0_1_REG_OFFSET, 0);
49  EXPECT_WRITE32(RV_PLIC_IE0_2_REG_OFFSET, 0);
50  EXPECT_WRITE32(RV_PLIC_IE0_3_REG_OFFSET, 0);
51  EXPECT_WRITE32(RV_PLIC_IE0_4_REG_OFFSET, 0);
52  EXPECT_WRITE32(RV_PLIC_IE0_5_REG_OFFSET, 0);
53 
54  // Target threshold registers.
55  EXPECT_WRITE32(RV_PLIC_THRESHOLD0_REG_OFFSET, 0);
56 
57  // Software interrupt pending register.
58  EXPECT_WRITE32(RV_PLIC_MSIP0_REG_OFFSET, 0);
59  }
60 };
61 
62 TEST_F(ResetTest, NullArgs) { EXPECT_DIF_BADARG(dif_rv_plic_reset(nullptr)); }
63 
64 TEST_F(ResetTest, Success) {
65  ExpectReset();
66 
68 }
69 
70 class IrqTest : public PlicTest {
71  protected:
72  IrqTest() {
73  // Make sure to change the `last_bit` when `RV_PLIC_PARAM_NUM_SRC` changes.
74  // As `last_bit` represents the bit index in a register, we need to count
75  // all of the last bits of a multireg to get the total number of bits.
76  // The bit count in IE and IP registers is expected to be the same.
77  //
78  // This check has been added to help diagnose the mismatch of test values
79  // with the HW defines. One of the recent PRs ran into this problem, and
80  // the failure message was not descriptive, so some engineering time was
81  // lost to investigation.
82  uint8_t number_of_sources = 0;
83  for (const auto &reg : kEnableRegisters) {
84  number_of_sources += (reg.last_bit + 1);
85  }
86  EXPECT_EQ(RV_PLIC_PARAM_NUM_SRC, number_of_sources)
87  << "make sure to update the IrqTest register arrays!";
88 
89  EXPECT_EQ(RV_PLIC_PARAM_NUM_TARGET, 1);
90  }
91 
92  struct Register {
93  ptrdiff_t offset; // Register offset from the base.
94  uint8_t last_bit; // Last bit index in the register.
95  };
96  static constexpr std::array<Register, RV_PLIC_IE0_MULTIREG_COUNT>
97  kEnableRegisters{{
98  {RV_PLIC_IE0_0_REG_OFFSET, RV_PLIC_IE0_0_E_31_BIT},
99  {RV_PLIC_IE0_1_REG_OFFSET, RV_PLIC_IE0_1_E_63_BIT},
100  {RV_PLIC_IE0_2_REG_OFFSET, RV_PLIC_IE0_2_E_95_BIT},
101  {RV_PLIC_IE0_3_REG_OFFSET, RV_PLIC_IE0_3_E_127_BIT},
102  {RV_PLIC_IE0_4_REG_OFFSET, RV_PLIC_IE0_4_E_159_BIT},
103  {RV_PLIC_IE0_5_REG_OFFSET, RV_PLIC_IE0_5_E_185_BIT},
104  }};
105  static constexpr std::array<Register, RV_PLIC_IP_MULTIREG_COUNT>
106  kPendingRegisters{{
107  {RV_PLIC_IP_0_REG_OFFSET, RV_PLIC_IP_0_P_31_BIT},
108  {RV_PLIC_IP_1_REG_OFFSET, RV_PLIC_IP_1_P_63_BIT},
109  {RV_PLIC_IP_2_REG_OFFSET, RV_PLIC_IP_2_P_95_BIT},
110  {RV_PLIC_IP_3_REG_OFFSET, RV_PLIC_IP_3_P_127_BIT},
111  {RV_PLIC_IP_4_REG_OFFSET, RV_PLIC_IP_4_P_159_BIT},
112  {RV_PLIC_IP_5_REG_OFFSET, RV_PLIC_IP_5_P_185_BIT},
113  }};
114 
115  // Set enable/disable multireg expectations, one bit per call.
116  template <size_t n>
117  void ExpectIrqSetTests(const std::array<Register, n> &regs, bool enabled) {
118  for (const auto &reg : regs) {
119  for (uint32_t i = 0; i <= reg.last_bit; ++i) {
120  EXPECT_MASK32(reg.offset, {{i, 0x1, enabled}});
121  }
122  }
123  }
124 
125  // Set multireg get status expectations, one bit per call.
126  template <size_t n>
127  void ExpectIrqGetTests(const std::array<Register, n> &regs, bool enabled) {
128  for (const auto &reg : regs) {
129  for (int i = 0; i <= reg.last_bit; ++i) {
130  uint32_t value = 0x1 << i;
131  if (!enabled) {
132  value = ~value;
133  }
134 
135  EXPECT_READ32(reg.offset, value);
136  }
137  }
138  }
139 };
140 
141 constexpr std::array<IrqTest::Register, RV_PLIC_IE0_MULTIREG_COUNT>
142  IrqTest::kEnableRegisters;
143 constexpr std::array<IrqTest::Register, RV_PLIC_IP_MULTIREG_COUNT>
144  IrqTest::kPendingRegisters;
145 
146 class IrqEnableSetTest : public IrqTest {};
147 
148 TEST_F(IrqEnableSetTest, NullArgs) {
150  nullptr, (dif_rv_plic_irq_id_t)kFirstIrq, kTarget0, kDifToggleEnabled));
151 }
152 
153 TEST_F(IrqEnableSetTest, Target0Enable) {
154  ExpectIrqSetTests(kEnableRegisters, true);
155 
156  // Enable every IRQ, one at a time.
157  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
159  kTarget0, kDifToggleEnabled));
160  }
161 }
162 
163 TEST_F(IrqEnableSetTest, Target0Disable) {
164  ExpectIrqSetTests(kEnableRegisters, false);
165 
166  // Disable every bit, one at a time.
167  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
169  kTarget0, kDifToggleDisabled));
170  }
171 }
172 
173 class IrqPrioritySetTest : public PlicTest {};
174 
175 TEST_F(IrqPrioritySetTest, NullArgs) {
177  nullptr, (dif_rv_plic_irq_id_t)kFirstIrq, kDifRvPlicMaxPriority));
178 }
179 
180 TEST_F(IrqPrioritySetTest, PriorityInvalid) {
182  nullptr, (dif_rv_plic_irq_id_t)kFirstIrq, kDifRvPlicMaxPriority + 1));
183 }
184 
185 TEST_F(IrqPrioritySetTest, Success) {
186  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
187  // Set expectations for every priority set call.
188  ptrdiff_t offset = RV_PLIC_PRIO_0_REG_OFFSET + (sizeof(uint32_t) * i);
189  EXPECT_WRITE32(offset, kDifRvPlicMaxPriority);
190 
193  }
194 }
195 
197 
198 TEST_F(TargetThresholdSetTest, NullArgs) {
201 }
202 
203 TEST_F(TargetThresholdSetTest, Target0PriorityInvalid) {
205  &plic_, kTarget0, kDifRvPlicMaxPriority + 1));
206 }
207 
208 TEST_F(TargetThresholdSetTest, Target0Success) {
209  EXPECT_WRITE32(RV_PLIC_THRESHOLD0_REG_OFFSET, kDifRvPlicMaxPriority);
210 
213 }
214 
216 
217 TEST_F(IrqPendingStatusGetTest, NullArgs) {
218  bool status;
220  nullptr, (dif_rv_plic_irq_id_t)kFirstIrq, &status);
221  EXPECT_DIF_BADARG(result);
222 
223  result = dif_rv_plic_irq_is_pending(&plic_, (dif_rv_plic_irq_id_t)kFirstIrq,
224  nullptr);
225  EXPECT_DIF_BADARG(result);
226 
227  result = dif_rv_plic_irq_is_pending(nullptr, (dif_rv_plic_irq_id_t)kFirstIrq,
228  nullptr);
229  EXPECT_DIF_BADARG(result);
230 }
231 
232 TEST_F(IrqPendingStatusGetTest, Enabled) {
233  ExpectIrqGetTests(kPendingRegisters, true);
234 
235  // Get status of every IRQ, one at a time.
236  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
237  bool status;
238  dif_result_t result =
240  EXPECT_DIF_OK(result);
241  EXPECT_TRUE(status);
242  }
243 }
244 
245 TEST_F(IrqPendingStatusGetTest, Disabled) {
246  ExpectIrqGetTests(kPendingRegisters, false);
247 
248  // Get status of every IRQ, one at a time.
249  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
250  bool status;
251  dif_result_t result =
253  EXPECT_DIF_OK(result);
254  EXPECT_FALSE(status);
255  }
256 }
257 
258 class IrqClaimTest : public PlicTest {
259  static_assert(RV_PLIC_PARAM_NUM_TARGET == 1, "");
260 };
261 
262 TEST_F(IrqClaimTest, NullArgs) {
264  EXPECT_DIF_BADARG(dif_rv_plic_irq_claim(nullptr, kTarget0, &data));
265 
266  EXPECT_DIF_BADARG(dif_rv_plic_irq_claim(&plic_, kTarget0, nullptr));
267 
268  EXPECT_DIF_BADARG(dif_rv_plic_irq_claim(nullptr, kTarget0, nullptr));
269 }
270 
271 TEST_F(IrqClaimTest, Target0Success) {
272  // Set expectations for every claim call.
273  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
274  EXPECT_READ32(RV_PLIC_CC0_REG_OFFSET, i);
275  }
276 
277  // Claim every IRQ, one per a call.
278  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
280  EXPECT_DIF_OK(dif_rv_plic_irq_claim(&plic_, kTarget0, &data));
281  EXPECT_EQ(data, i);
282  }
283 }
284 
285 class IrqCompleteTest : public PlicTest {
286  static_assert(RV_PLIC_PARAM_NUM_TARGET == 1, "");
287 };
288 
289 TEST_F(IrqCompleteTest, NullArgs) {
291  dif_rv_plic_irq_complete(nullptr, kTarget0, (dif_rv_plic_irq_id_t)0));
292 }
293 
294 TEST_F(IrqCompleteTest, Target0Success) {
295  // Set expectations for every complete call.
296  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
297  EXPECT_WRITE32(RV_PLIC_CC0_REG_OFFSET, i);
298  }
299 
300  // Complete all of the IRQs.
301  for (int i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
303  dif_rv_plic_irq_complete(&plic_, kTarget0, (dif_rv_plic_irq_id_t)i));
304  }
305 }
306 
308  static_assert(RV_PLIC_PARAM_NUM_TARGET == 1, "");
309 };
310 
311 TEST_F(SoftwareIrqForceTest, NullArgs) {
313 }
314 
315 TEST_F(SoftwareIrqForceTest, BadTarget) {
317  dif_rv_plic_software_irq_force(&plic_, RV_PLIC_PARAM_NUM_TARGET));
318 }
319 
320 TEST_F(SoftwareIrqForceTest, Target0Success) {
321  EXPECT_WRITE32(RV_PLIC_MSIP0_REG_OFFSET, 1);
323 }
324 
326  static_assert(RV_PLIC_PARAM_NUM_TARGET == 1, "");
327 };
328 
329 TEST_F(SoftwareIrqAcknowledgeTest, NullArgs) {
331 }
332 
333 TEST_F(SoftwareIrqAcknowledgeTest, BadTarget) {
335  dif_rv_plic_software_irq_acknowledge(&plic_, RV_PLIC_PARAM_NUM_TARGET));
336 }
337 
338 TEST_F(SoftwareIrqAcknowledgeTest, Target0Success) {
339  EXPECT_WRITE32(RV_PLIC_MSIP0_REG_OFFSET, 0);
341 }
342 
344  static_assert(RV_PLIC_PARAM_NUM_TARGET == 1, "");
345 };
346 
347 TEST_F(SoftwareIrqIsPendingTest, NullArgs) {
349  dif_rv_plic_software_irq_is_pending(nullptr, kTarget0, nullptr));
350 
352  dif_rv_plic_software_irq_is_pending(&plic_, kTarget0, nullptr));
353 
354  bool is_pending;
356  dif_rv_plic_software_irq_is_pending(nullptr, kTarget0, &is_pending));
357 }
358 
359 TEST_F(SoftwareIrqIsPendingTest, BadTarget) {
360  bool is_pending;
362  &plic_, RV_PLIC_PARAM_NUM_TARGET, &is_pending));
363 }
364 
365 TEST_F(SoftwareIrqIsPendingTest, Target0Success) {
366  bool is_pending = false;
367  EXPECT_READ32(RV_PLIC_MSIP0_REG_OFFSET, 1);
368 
370  dif_rv_plic_software_irq_is_pending(&plic_, kTarget0, &is_pending));
371  EXPECT_TRUE(is_pending);
372 
373  // Cleared
374  is_pending = true;
375  EXPECT_READ32(RV_PLIC_MSIP0_REG_OFFSET, 0);
376 
378  dif_rv_plic_software_irq_is_pending(&plic_, kTarget0, &is_pending));
379  EXPECT_FALSE(is_pending);
380 }
381 
382 } // namespace
383 } // namespace dif_rv_plic_unittest