Software APIs
uart_parity_break_test.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 
12 #include "sw/device/lib/runtime/irq.h"
14 #include "sw/device/lib/testing/test_framework/check.h"
15 #include "sw/device/lib/testing/test_framework/ottf_console.h"
17 #include "sw/device/lib/testing/test_framework/ottf_utils.h"
18 #include "sw/device/lib/testing/test_framework/status.h"
19 #include "sw/device/lib/testing/uart_testutils.h"
20 
22 
23 static dif_pinmux_t pinmux;
24 static dif_rv_plic_t rv_plic;
25 static dif_uart_t uart;
26 
27 // Parameters of the test to be set at the beginning.
28 // Parity settings are:
29 // - 0 => Odd
30 // - 1 => Even
31 // - 2 => Disabled
32 static volatile uint8_t parity = UINT8_MAX;
33 static volatile uint8_t uart_idx = UINT8_MAX;
34 
35 // Parameters for the particular UART instance under test.
36 static volatile uart_cfg_params_t uart_cfg;
37 
38 // Whether we expect and have received the RX parity error interrupt.
39 static volatile bool uart_irq_rx_parity_err_expected = false;
40 static volatile bool uart_irq_rx_parity_err_fired = false;
41 // Whether we expect and have received the RX line break error interrupt.
42 static volatile bool uart_irq_rx_break_err_expected = false;
43 static volatile bool uart_irq_rx_break_err_fired = false;
44 
45 enum {
46  // Timeout for ujson commands.
47  kCommandTimeoutMicros = 5 * 1000 * 1000,
48  // Timeout for a parity error to be received.
49  kParityErrTimeoutMicros = 1 * 1000 * 1000,
50 };
51 
52 typedef enum test_phase {
53  kTestPhaseCfg,
54  kTestPhaseSend,
55  kTestPhaseRecv,
56  kTestPhaseRecvErr,
57  kTestPhaseBreakErr,
58  kTestPhaseBreakErrDone,
59 } test_phase_t;
60 static volatile uint8_t test_phase = kTestPhaseCfg;
61 
62 // Some random bytes that will be sent and received to check the parity.
63 static const uint8_t kUartData[32] = {
64  0x3f, 0x39, 0xb0, 0x4e, 0xa6, 0xce, 0xe5, 0xb7, 0x94, 0x48, 0xec,
65  0xb5, 0x48, 0x5c, 0x08, 0x5b, 0xcd, 0x47, 0xae, 0x80, 0xbb, 0x49,
66  0xa1, 0x7c, 0x39, 0x20, 0xd1, 0x6d, 0x2f, 0x4f, 0x94, 0xd8,
67 };
68 
69 // Configure the UART under test selected by `uart_idx` with `parity`.
70 static status_t configure_uart(void) {
71  TRY(uart_testutils_cfg_params(uart_idx, (uart_cfg_params_t *)&uart_cfg));
72 
73  TRY(dif_uart_init(mmio_region_from_addr(uart_cfg.base_addr), &uart));
74 
75  dif_toggle_t parity_enable =
77  TRY(dif_uart_configure(&uart,
79  .baudrate = (uint32_t)kUartBaudrate,
80  .clk_freq_hz = (uint32_t)kClockFreqPeripheralHz,
81  .parity_enable = parity_enable,
82  .parity = parity,
83  .tx_enable = kDifToggleDisabled,
84  .rx_enable = kDifToggleDisabled,
85  }));
86 
87  TRY(uart_testutils_select_pinmux(&pinmux, uart_idx, kUartPinmuxChannelDut));
88 
91 
92  return OK_STATUS();
93 }
94 
95 // Configure the `rx_parity_err` and `rx_break_err` interrupts for the UART
96 // under test.
97 static status_t configure_interrupts(void) {
98  TRY(dif_uart_irq_disable_all(&uart, NULL));
99 
100  TRY(dif_uart_irq_set_enabled(&uart, kDifUartIrqRxParityErr,
102  TRY(dif_uart_irq_set_enabled(&uart, kDifUartIrqRxBreakErr,
104 
105  TRY(dif_rv_plic_irq_set_priority(&rv_plic, uart_cfg.irq_rx_parity_err_id,
106  0x1));
107  TRY(dif_rv_plic_irq_set_priority(&rv_plic, uart_cfg.irq_rx_break_err_id,
108  0x1));
109 
110  TRY(dif_rv_plic_irq_set_enabled(&rv_plic, uart_cfg.irq_rx_parity_err_id,
113  TRY(dif_rv_plic_irq_set_enabled(&rv_plic, uart_cfg.irq_rx_break_err_id,
116 
118  0x0));
119 
120  // Enable the external IRQ at Ibex.
121  irq_global_ctrl(true);
122  irq_external_ctrl(true);
123 
124  return OK_STATUS();
125 }
126 
127 // This function overrides the default OTTF external ISR.
128 //
129 // It handles the `rx_parity_err` and `rx_break_err` interrupts and checks they
130 // came from the correct UART. If the interrupts were expected, we let the main
131 // thread know that they were triggered.
132 void ottf_external_isr(uint32_t *exc_info) {
133  // Claim the interrupt.
134  dif_rv_plic_irq_id_t plic_irq_id;
135  CHECK_DIF_OK(dif_rv_plic_irq_claim(&rv_plic, kTopEarlgreyPlicTargetIbex0,
136  &plic_irq_id));
137 
138  // Check the interrupt fired on the correct UART.
141 
142  // Handle interrupts for the console UART separately.
143  if (peripheral != uart_cfg.peripheral_id) {
144  ottf_console_flow_control_isr(exc_info);
145  }
146 
147  // Check it was the parity error that fired and that we expected it.
148  uint32_t uart_irq_id = 0;
149  if (plic_irq_id == uart_cfg.irq_rx_parity_err_id) {
150  CHECK(uart_irq_rx_parity_err_expected, "Unexpected parity error interrupt");
151  uart_irq_rx_parity_err_fired = true;
152  uart_irq_id = kDifUartIrqRxParityErr;
153  } else if (plic_irq_id == uart_cfg.irq_rx_break_err_id) {
154  CHECK(uart_irq_rx_break_err_expected, "Unexpected break error interrupt");
155  uart_irq_rx_break_err_fired = true;
156  uart_irq_id = kDifUartIrqRxBreakErr;
157  } else {
158  CHECK(false, "Unexpected interrupt from UART: %d", plic_irq_id);
159  }
160 
161  // Check that the same interrupt fired at the UART as well.
162  bool is_pending;
163  CHECK_DIF_OK(dif_uart_irq_is_pending(&uart, uart_irq_id, &is_pending));
164  CHECK(is_pending, "UART interrupt fired at PLIC did not fire at UART");
165 
166  // Acknowledge interrupt.
167  CHECK_DIF_OK(dif_uart_irq_acknowledge(&uart, uart_irq_id));
169  plic_irq_id));
170 }
171 
172 // Body of the test:
173 // 1. Send some data and have the host check its parity.
174 // 2. Receive some data with the correct parity and check it.
175 // 3. Receive some data with the wrong parity and check the interrupt fired.
176 // 4. Wait for the host to trigger a line break error.
177 static status_t execute_test(void) {
178  // Wait for host to sync and then send the expected bytes.
179  OTTF_WAIT_FOR(test_phase == kTestPhaseSend, kCommandTimeoutMicros);
180 
181  size_t bytes_to_send = ARRAYSIZE(kUartData);
182  uint8_t *send_buf = (uint8_t *)kUartData;
183 
184  LOG_INFO("Sending data...");
185  while (bytes_to_send > 0) {
186  size_t bytes_sent = 0;
187  TRY(dif_uart_bytes_send(&uart, send_buf, bytes_to_send, &bytes_sent));
188  bytes_to_send -= bytes_sent;
189  send_buf += bytes_sent;
190  }
191 
192  // Wait for the host to sync and try receive the expected bytes.
193  OTTF_WAIT_FOR(test_phase == kTestPhaseRecv, kCommandTimeoutMicros);
194 
195  size_t bytes_to_recv = ARRAYSIZE(kUartData);
196  uint8_t recv_buf[ARRAYSIZE(kUartData)] = {0};
197  uint8_t *recv_ptr = recv_buf;
198 
199  LOG_INFO("Receiving data with correct parity...");
200  while (bytes_to_recv > 0) {
201  size_t len = 0;
202  TRY(dif_uart_bytes_receive(&uart, bytes_to_recv, recv_ptr, &len));
203  bytes_to_recv -= len;
204  recv_ptr += len;
205  }
206 
207  TRY_CHECK_ARRAYS_EQ(recv_buf, kUartData, ARRAYSIZE(kUartData));
208 
209  // Only expect a parity error if parity was enabled.
210  if (parity < 2) {
211  // Wait for host to sync and then receive data but now expecting the parity
212  // error interrupt to trigger.
213  uart_irq_rx_parity_err_expected = true;
214  OTTF_WAIT_FOR(test_phase == kTestPhaseRecvErr, kCommandTimeoutMicros);
215 
216  LOG_INFO("Receiving data with wrong parity");
217  ATOMIC_WAIT_FOR_INTERRUPT(uart_irq_rx_parity_err_fired);
218  }
219 
220  // We test the RX line break error interrupt at all supported levels.
221  dif_uart_rx_break_level_t break_levels[4] = {
222  kDifUartRxBreakLevel2,
223  kDifUartRxBreakLevel4,
224  kDifUartRxBreakLevel8,
225  kDifUartRxBreakLevel16,
226  };
227 
228  for (int i = 0; i < ARRAYSIZE(break_levels); i++) {
229  TRY(dif_uart_rx_break_level_set(&uart, break_levels[i]));
230 
231  // Wait for host to sync and then expect to receive a line break.
232  uart_irq_rx_break_err_expected = true;
233  OTTF_WAIT_FOR(test_phase == kTestPhaseBreakErr, kCommandTimeoutMicros);
234 
235  LOG_INFO("Waiting for line break");
236  ATOMIC_WAIT_FOR_INTERRUPT(uart_irq_rx_break_err_fired);
237 
238  test_phase = kTestPhaseBreakErrDone;
239  }
240 
241  return OK_STATUS();
242 }
243 
244 OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);
245 
246 bool test_main(void) {
247  mmio_region_t base_addr;
249  CHECK_DIF_OK(dif_pinmux_init(base_addr, &pinmux));
251  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &rv_plic));
252 
253  // Wait for host to tell us which parity and UART to test.
254  OTTF_WAIT_FOR(uart_idx != UINT8_MAX && parity != UINT8_MAX,
255  kCommandTimeoutMicros);
256 
257  // If we're testing UART0 we need to move the console to UART1.
258  if (uart_idx == 0) {
259  CHECK_STATUS_OK(
260  uart_testutils_select_pinmux(&pinmux, 1, kUartPinmuxChannelConsole));
261  ottf_console_configure_uart(TOP_EARLGREY_UART1_BASE_ADDR);
262  }
263 
264  // Configure the UART under test.
265  CHECK_STATUS_OK(configure_uart());
266  CHECK_STATUS_OK(configure_interrupts());
267 
268  LOG_INFO("UART%d configured", uart_idx);
269 
270  status_t result = OK_STATUS();
271  EXECUTE_TEST(result, execute_test);
272  return status_ok(result);
273 }