Software APIs
rv_plic_smoketest.c
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 
9 #include "sw/device/lib/runtime/irq.h"
11 #include "sw/device/lib/testing/test_framework/check.h"
13 #include "sw/device/lib/testing/test_framework/status.h"
14 
15 static_assert(kDtRvPlicCount == 1, "This test expects exactly one rv_plic");
16 
17 static const dt_rv_plic_t kTestRvPlic = (dt_rv_plic_t)0;
18 static const dt_uart_t kTestUart = (dt_uart_t)0;
19 
20 enum {
21  kPlicTarget = 0,
22 };
23 
24 static dif_rv_plic_t plic0;
25 static dif_uart_t uart0;
26 
27 // These flags are used in the test routine to verify that a corresponding
28 // interrupt has elapsed, and has been serviced. These are declared as volatile
29 // since they are referenced in the ISR routine as well as in the main program
30 // flow.
31 static volatile bool uart_rx_overflow_handled;
32 static volatile bool uart_tx_done_handled;
33 
34 /**
35  * UART ISR.
36  *
37  * Services UART interrupts, sets the appropriate flags that are used to
38  * determine success or failure of the test.
39  */
40 static void handle_uart_isr(const dif_rv_plic_irq_id_t plic_irq_id) {
41  dt_uart_irq_t uart_irq = dt_uart_irq_from_plic_id(kTestUart, plic_irq_id);
42 
43  switch (uart_irq) {
44  case kDtUartIrqRxOverflow:
45  CHECK(!uart_rx_overflow_handled,
46  "UART RX overflow IRQ asserted more than once");
47 
48  uart_rx_overflow_handled = true;
49  break;
50  case kDtUartIrqTxDone:
51  CHECK(!uart_tx_done_handled, "UART TX done IRQ asserted more than once");
52 
53  uart_tx_done_handled = true;
54  break;
55  default:
56  LOG_FATAL("ISR is not implemented!");
57  test_status_set(kTestStatusFailed);
58  }
59 
60  CHECK_DIF_OK(dif_uart_irq_acknowledge(&uart0, uart_irq));
61 }
62 
63 /**
64  * External ISR.
65  *
66  * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
67  * line to the CPU, which results in a call to this OTTF ISR. This ISR
68  * overrides the default OTTF implementation.
69  */
70 void ottf_external_isr(uint32_t *exc_info) {
71  // Claim the IRQ by reading the Ibex specific CC register.
72  dif_rv_plic_irq_id_t plic_irq_id;
73  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic0, kPlicTarget, &plic_irq_id));
74 
75  // Check if the interrupted peripheral is UART.
76  dt_instance_id_t inst_id = dt_plic_id_to_instance_id(plic_irq_id);
77  CHECK(inst_id == dt_uart_instance_id(kTestUart),
78  "Interrupt from incorrect peripheral: (exp: %d, obs: %s)",
79  dt_uart_instance_id(kTestUart), inst_id);
80  handle_uart_isr(plic_irq_id);
81 
82  // Complete the IRQ by writing the IRQ source to the Ibex specific CC
83  // register.
84  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic0, kPlicTarget, plic_irq_id));
85 }
86 
87 static void uart_initialise(dt_uart_t uart_id, dif_uart_t *uart) {
88  CHECK_DIF_OK(dif_uart_init_from_dt(uart_id, uart));
89  CHECK(kUartBaudrate <= UINT32_MAX, "kUartBaudrate must fit in uint32_t");
90  CHECK(kClockFreqPeripheralHz <= UINT32_MAX,
91  "kClockFreqPeripheralHz must fit in uint32_t");
92  CHECK_DIF_OK(dif_uart_configure(
93  uart, (dif_uart_config_t){
94  .baudrate = (uint32_t)kUartBaudrate,
95  .clk_freq_hz = (uint32_t)kClockFreqPeripheralHz,
96  .parity_enable = kDifToggleDisabled,
97  .parity = kDifUartParityEven,
98  .tx_enable = kDifToggleEnabled,
99  .rx_enable = kDifToggleEnabled,
100  }));
101 }
102 
103 /**
104  * Configures all the relevant interrupts in UART.
105  */
106 static void uart_configure_irqs(dif_uart_t *uart) {
107  CHECK_DIF_OK(dif_uart_irq_set_enabled(&uart0, kDifUartIrqRxOverflow,
109  CHECK_DIF_OK(
110  dif_uart_irq_set_enabled(&uart0, kDifUartIrqTxDone, kDifToggleEnabled));
111 }
112 
113 /**
114  * Configures all the relevant interrupts in PLIC.
115  */
116 static void plic_configure_irqs(dif_rv_plic_t *plic) {
117  dt_plic_irq_id_t rx_ovf_irq =
118  dt_uart_irq_to_plic_id(kTestUart, kDtUartIrqRxOverflow);
119  dt_plic_irq_id_t tx_done_irq =
120  dt_uart_irq_to_plic_id(kTestUart, kDtUartIrqTxDone);
121 
122  // Set IRQ priorities to MAX
123  CHECK_DIF_OK(
125 
126  CHECK_DIF_OK(
128 
129  // Set Ibex IRQ priority threshold level
130  CHECK_DIF_OK(dif_rv_plic_target_set_threshold(&plic0, kPlicTarget,
132 
133  // Enable IRQs in PLIC
134  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, rx_ovf_irq, kPlicTarget,
136 
137  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, tx_done_irq, kPlicTarget,
139 }
140 
141 static void execute_test(dif_uart_t *uart) {
142  // Force UART RX overflow interrupt.
143  uart_rx_overflow_handled = false;
144  CHECK_DIF_OK(dif_uart_irq_force(uart, kDifUartIrqRxOverflow, true));
145  // Check if the IRQ has occured and has been handled appropriately.
146  if (!uart_rx_overflow_handled) {
147  busy_spin_micros(10);
148  }
149  CHECK(uart_rx_overflow_handled, "RX overflow IRQ has not been handled!");
150 
151  // Force UART TX done interrupt.
152  uart_tx_done_handled = false;
153  CHECK_DIF_OK(dif_uart_irq_force(uart, kDifUartIrqTxDone, true));
154  // Check if the IRQ has occured and has been handled appropriately.
155  if (!uart_tx_done_handled) {
156  busy_spin_micros(10);
157  }
158  CHECK(uart_tx_done_handled, "TX done IRQ has not been handled!");
159 }
160 
161 OTTF_DEFINE_TEST_CONFIG(.console.test_may_clobber = true);
162 
163 bool test_main(void) {
164  // Enable IRQs on Ibex
165  irq_global_ctrl(true);
166  irq_external_ctrl(true);
167 
168  // No debug output in case of UART initialisation failure.
169  uart_initialise(kTestUart, &uart0);
170 
171  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kTestRvPlic, &plic0));
172 
173  uart_configure_irqs(&uart0);
174  plic_configure_irqs(&plic0);
175  execute_test(&uart0);
176 
177  return true;
178 }