Software APIs
uart_tx_rx_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 
14 #include "sw/device/lib/runtime/irq.h"
16 #include "sw/device/lib/testing/clkmgr_testutils.h"
17 #include "sw/device/lib/testing/test_framework/check.h"
18 #include "sw/device/lib/testing/test_framework/ottf_console.h"
20 #include "sw/device/lib/testing/test_framework/ottf_utils.h"
21 #include "sw/device/lib/testing/test_framework/status.h"
22 #include "sw/device/lib/testing/uart_testutils.h"
23 
25 
26 #define UART_DATASET_SIZE 64
27 
28 static dif_clkmgr_t clkmgr;
29 static dif_pinmux_t pinmux;
30 static dif_rv_plic_t plic;
31 static dif_uart_t uart;
32 
33 /**
34  * UART TX RX test
35  *
36  * This test sends and receives a known dataset over UART. The size of the
37  * dataset is indicated with UART_DATASET_SIZE. The dataset is agreed upon by
38  * the device (a.k.a. the OpenTitan chip) and the host (a.k.a. the simulation
39  * device, such as DV testbench) communicating with it. Data transmitted over
40  * TX is checked for correctness at the host, and likewise, data sent by the
41  * host is checked for correctness at the device (in this SW test). The data
42  * transmitted over TX and RX ports may occur simultaneously. The test ensures
43  * that the TX watermark, RX watermark and TX empty interrupts are seen.
44  * At the end, the host transmits another set of random data (greater than the
45  * RX fifo size) which the device drops, to generate the RX overflow condition.
46  * The test passes when the datasets at both ends match the expected and all
47  * required interrupts are seen.
48  */
49 
50 /**
51  * UART test data transfer direction
52  *
53  * Enumeration indicating the direction of transfer of test data.
54  */
55 typedef enum uart_direction {
56  kUartSend = 0,
57  kUartReceive,
58 } uart_direction_t;
59 
60 /**
61  * Indicates the UART instance under test.
62  *
63  * When running in `dv_sim`, the external DV testbench finds this symbol's
64  * address and modifies it via backdoor, to test a different UART instance with
65  * the same test SW image. Hence, we add the `volatile` keyword to prevent the
66  * compiler from optimizing it out.
67  *
68  * The `const` is needed to put it in the .rodata section, otherwise it gets
69  * placed in .data section in the main SRAM. We cannot backdoor write anything
70  * in SRAM at the start of the test because the CRT init code wipes it to 0s.
71  */
72 OTTF_BACKDOOR_VAR_DV uint8_t kUartIdxDv = 0xff;
73 /**
74  * Outside of DV simulation environments, the `kUartIdx` symbol needs to be
75  * _non_ `const` so that we can modify it via OTTF commands. `kUartIdx` is used
76  * as the source of truth in the test but we copy the value from `kUartIdxDv`
77  * to here if it has been set.
78  */
79 static volatile uint8_t kUartIdx = 0xff;
80 
81 /**
82  * Indicates the baud rate of the UART under test; this is set within the
83  * `dv_sim` environment but the override is not used on physical hardware.
84  */
85 OTTF_BACKDOOR_VAR_DV uint64_t kUartBaudrateDV = 0u;
86 /**
87  * Indicates the clock frequency of the UART under test; this is set within
88  * the `dv_sim` environment but the override is not used on physical hardware.
89  */
90 OTTF_BACKDOOR_VAR_DV uint64_t kUartClockFreqHzDV = 0u;
91 
92 /**
93  * Indicates if ext_clk is used and what speed.
94  *
95  * Similar to `kUartIdx`, this may be overridden in DV testbench
96  */
97 static volatile const bool kUseExtClk = false;
98 static volatile const bool kUseLowSpeedSel = false;
99 
100 // A set of bytes to be send out of TX.
101 //
102 // The first byte must be FF so we can differentiate this blob from ASCII sent
103 // by the ROM when it starts. FF is not UTF-8 / ASCII.
104 static volatile const uint8_t kUartTxData[UART_DATASET_SIZE] = {
105  0xff, 0x50, 0xc6, 0xb4, 0xbe, 0x16, 0xed, 0x55, 0x16, 0x1d, 0xe6,
106  0x1c, 0xde, 0x9f, 0xfd, 0x24, 0x89, 0x81, 0x4d, 0x0d, 0x1a, 0x12,
107  0x4f, 0x57, 0xea, 0xd6, 0x6f, 0xc0, 0x7d, 0x46, 0xe7, 0x37, 0x81,
108  0xd3, 0x8e, 0x16, 0xad, 0x7b, 0xd0, 0xe2, 0x4f, 0xff, 0x39, 0xe6,
109  0x71, 0x3c, 0x82, 0x04, 0xec, 0x3a, 0x27, 0xcc, 0x3d, 0x58, 0x0e,
110  0x56, 0xd2, 0xd2, 0xb9, 0xa3, 0xb5, 0x3d, 0xc0, 0x40,
111 };
112 
113 // The set of bytes expected to be received over RX.
114 static volatile const uint8_t kExpUartRxData[UART_DATASET_SIZE] = {
115  0x1b, 0x95, 0xc5, 0xb5, 0x8a, 0xa4, 0xa8, 0x9f, 0x6a, 0x7d, 0x6b,
116  0x0c, 0xcd, 0xd5, 0xa6, 0x8f, 0x07, 0x3a, 0x9e, 0x82, 0xe6, 0xa2,
117  0x2b, 0xe0, 0x0c, 0x30, 0xe8, 0x5a, 0x05, 0x14, 0x79, 0x8a, 0xFf,
118  0x88, 0x29, 0xda, 0xc8, 0xdd, 0x82, 0xd5, 0x68, 0xa5, 0x9d, 0x5a,
119  0x48, 0x02, 0x7f, 0x24, 0x32, 0xaf, 0x9d, 0xca, 0xa7, 0x06, 0x0c,
120  0x96, 0x65, 0x18, 0xe4, 0x7f, 0x26, 0x44, 0xf3, 0x14,
121 };
122 
123 // There are multiple uart instances in the chip. These variables will be
124 // updated according to the uart we select.
125 static volatile uart_cfg_params_t uart_cfg;
126 
127 /**
128  * Set our expectation & event indications of the interrupts we intend to
129  * exercise in this test. These are declared volatile since they are used by the
130  * ISR.
131  */
132 static volatile bool exp_uart_irq_tx_watermark;
133 static volatile bool uart_irq_tx_watermark_fired;
134 static volatile bool exp_uart_irq_tx_empty;
135 static volatile bool uart_irq_tx_empty_fired;
136 static volatile bool exp_uart_irq_rx_watermark;
137 static volatile bool uart_irq_rx_watermark_fired;
138 static volatile bool exp_uart_irq_tx_done;
139 static volatile bool uart_irq_tx_done_fired;
140 static volatile bool exp_uart_irq_rx_overflow;
141 static volatile bool uart_irq_rx_overflow_fired;
142 
143 enum {
144  kCommandTimeout = 5000000, // microseconds
145 };
146 
147 /**
148  * Provides external irq handling for this test.
149  *
150  * This function overrides the default OTTF external ISR.
151  */
152 void ottf_external_isr(uint32_t *exc_info) {
153  // Find which interrupt fired at PLIC by claiming it.
154  dif_rv_plic_irq_id_t plic_irq_id;
155  CHECK_DIF_OK(
156  dif_rv_plic_irq_claim(&plic, kTopEarlgreyPlicTargetIbex0, &plic_irq_id));
157 
158  // Check if it is the right peripheral.
161  CHECK(peripheral == uart_cfg.peripheral_id,
162  "Interrupt from unexpected peripheral: %d", peripheral);
163 
164  // Correlate the interrupt fired at PLIC with UART.
165  dif_uart_irq_t uart_irq;
166  if (plic_irq_id == uart_cfg.irq_tx_watermark_id) {
167  CHECK_DIF_OK(dif_uart_irq_set_enabled(&uart, kDifUartIrqTxWatermark,
169  CHECK(exp_uart_irq_tx_watermark, "Unexpected TX watermark interrupt");
170  uart_irq_tx_watermark_fired = true;
171  uart_irq = kDifUartIrqTxWatermark;
172  } else if (plic_irq_id == uart_cfg.irq_tx_empty_id) {
173  CHECK_DIF_OK(dif_uart_irq_set_enabled(&uart, kDifUartIrqTxEmpty,
175  CHECK(exp_uart_irq_tx_empty, "Unexpected TX empty interrupt");
176  uart_irq_tx_empty_fired = true;
177  uart_irq = kDifUartIrqTxEmpty;
178  } else if (plic_irq_id == uart_cfg.irq_rx_watermark_id) {
179  CHECK_DIF_OK(dif_uart_irq_set_enabled(&uart, kDifUartIrqRxWatermark,
181  CHECK(exp_uart_irq_rx_watermark, "Unexpected RX watermark interrupt");
182  uart_irq_rx_watermark_fired = true;
183  uart_irq = kDifUartIrqRxWatermark;
184  } else if (plic_irq_id == uart_cfg.irq_tx_done_id) {
185  CHECK(exp_uart_irq_tx_done, "Unexpected TX done interrupt");
186  uart_irq_tx_done_fired = true;
187  uart_irq = kDifUartIrqTxDone;
188  } else if (plic_irq_id == uart_cfg.irq_rx_overflow_id) {
189  CHECK(exp_uart_irq_rx_overflow, "Unexpected RX overflow interrupt");
190  uart_irq_rx_overflow_fired = true;
191  uart_irq = kDifUartIrqRxOverflow;
192  } else {
193  LOG_ERROR("Unexpected interrupt (at PLIC): %d", plic_irq_id);
194  test_status_set(kTestStatusFailed);
195  // The `abort()` call below is redundant. It is added to prevent the
196  // compilation error due to not initializing the `uart_irq` enum variable
197  // above. See issue #2157 for moe details.
198  abort();
199  }
200 
201  // Check if the same interrupt fired at UART as well.
202  bool is_pending;
203  CHECK_DIF_OK(dif_uart_irq_is_pending(&uart, uart_irq, &is_pending));
204  CHECK(is_pending, "UART interrupt fired at PLIC did not fire at UART");
205 
206  // Clear the interrupt at UART.
207  CHECK_DIF_OK(dif_uart_irq_acknowledge(&uart, uart_irq));
208 
209  // Complete the IRQ at PLIC.
211  plic_irq_id));
212 }
213 
214 /**
215  * Initializes UART and enables the relevant interrupts.
216  */
217 static void uart_init_with_irqs(mmio_region_t base_addr, dif_uart_t *uart,
218  uint32_t uartBaudrate, uint32_t uartFreqHz) {
219  LOG_INFO("Initializing the UART.");
220 
221  CHECK_DIF_OK(dif_uart_init(base_addr, uart));
222  CHECK_DIF_OK(dif_uart_configure(uart, (dif_uart_config_t){
223  .baudrate = (uint32_t)uartBaudrate,
224  .clk_freq_hz = (uint32_t)uartFreqHz,
225  .parity_enable = kDifToggleDisabled,
226  .parity = kDifUartParityEven,
227  .tx_enable = kDifToggleEnabled,
228  .rx_enable = kDifToggleEnabled,
229  }));
230 
231  // Set the TX and RX watermark to 16 bytes.
234 
235  // Enable these UART interrupts - RX watermark, TX empty and RX overflow.
236  // TX watermark is enabled once the TX buffer has been written (otherwise it
237  // will fire immediately).
238  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
240  CHECK_DIF_OK(
241  dif_uart_irq_set_enabled(uart, kDifUartIrqTxDone, kDifToggleEnabled));
242  CHECK_DIF_OK(
243  dif_uart_irq_set_enabled(uart, kDifUartIrqRxOverflow, kDifToggleEnabled));
244 }
245 
246 /**
247  * Initializes PLIC and enables the relevant UART interrupts.
248  */
249 static void plic_init_with_irqs(mmio_region_t base_addr, dif_rv_plic_t *plic) {
250  LOG_INFO("Initializing the PLIC. %08x", uart_cfg.irq_tx_watermark_id);
251 
252  CHECK_DIF_OK(dif_rv_plic_init(base_addr, plic));
253 
254  // Set the priority of UART interrupts at PLIC to be >=1 (so ensure the target
255  // does get interrupted).
256  CHECK_DIF_OK(
257  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_tx_watermark_id, 0x1));
258  CHECK_DIF_OK(
259  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_tx_empty_id, 0x1));
260  CHECK_DIF_OK(
261  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_watermark_id, 0x2));
262  CHECK_DIF_OK(
263  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_tx_done_id, 0x3));
264  CHECK_DIF_OK(
265  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_overflow_id, 0x1));
266  CHECK_DIF_OK(
267  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_frame_err_id, 0x2));
268  CHECK_DIF_OK(
269  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_break_err_id, 0x3));
270  CHECK_DIF_OK(
271  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_timeout_id, 0x1));
272  CHECK_DIF_OK(
273  dif_rv_plic_irq_set_priority(plic, uart_cfg.irq_rx_parity_err_id, 0x2));
274 
275  // Set the threshold for the Ibex to 0.
276  CHECK_DIF_OK(
278 
279  // Enable all UART interrupts at the PLIC.
280  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_tx_watermark_id,
283 
284  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_tx_empty_id,
287 
288  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_watermark_id,
291 
292  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_tx_done_id,
295 
296  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_overflow_id,
299 
300  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_frame_err_id,
303 
304  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_break_err_id,
307 
308  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_timeout_id,
311 
312  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, uart_cfg.irq_rx_parity_err_id,
315 }
316 
317 /**
318  * Continue ongoing transmission of bytes.
319  *
320  * This is a wrapper around `dif_uart_bytes_send|receive()` functions. It picks
321  * up an ongoing transfer of data starting at `dataset_index` location until
322  * the UART can no longer accept any more data to be sent / return any more
323  * data received, depending on the direction of the data transfer indicated with
324  * the `uart_direction` argument. It uses the `bytes_written` / `bytes_read`
325  * value to advance the `dataset_index` for the next round. It updates the
326  * `transfer_done` arg to indicate if the ongoing transfer has completed.
327  */
328 static bool uart_transfer_ongoing_bytes(const dif_uart_t *uart,
329  uart_direction_t uart_direction,
330  uint8_t *data, size_t dataset_size,
331  size_t *dataset_index,
332  size_t max_xfer_size,
333  bool *transfer_done) {
334  size_t bytes_remaining = dataset_size - *dataset_index;
335  size_t bytes_to_xfer =
336  max_xfer_size < bytes_remaining ? max_xfer_size : bytes_remaining;
337  size_t bytes_transferred = 0;
338  bool result = false;
339  switch (uart_direction) {
340  case kUartSend:
341  result = dif_uart_bytes_send(uart, &data[*dataset_index], bytes_to_xfer,
342  &bytes_transferred) == kDifOk;
343 
344  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqTxWatermark,
346  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqTxEmpty,
348  break;
349  case kUartReceive:
350  result =
351  dif_uart_bytes_receive(uart, bytes_to_xfer, &data[*dataset_index],
352  &bytes_transferred) == kDifOk;
353 
354  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
356  break;
357  default:
358  LOG_FATAL("Invalid UART data transfer direction!");
359  }
360  *dataset_index += bytes_transferred;
361  *transfer_done = *dataset_index == dataset_size;
362  return result;
363 }
364 
365 static void execute_test(const dif_uart_t *uart) {
366  bool uart_tx_done = false;
367  size_t uart_tx_bytes_written = 0;
368  exp_uart_irq_tx_watermark = true;
369  exp_uart_irq_tx_empty = true;
370  // Set the flag below to true to allow TX data to be sent the first time in
371  // the if condition below. Subsequently, TX watermark interrupt will trigger
372  // more data to be sent.
373  uart_irq_tx_watermark_fired = true;
374  exp_uart_irq_tx_done = false;
375  uart_irq_tx_done_fired = false;
376 
377  bool uart_rx_done = false;
378  size_t uart_rx_bytes_read = 0;
379  exp_uart_irq_rx_watermark = true;
380  // Set the flag below to true to allow RX data to be received the first time
381  // in the if condition below. Subsequently, RX watermark interrupt will
382  // trigger more data to be received.
383  uart_irq_rx_watermark_fired = true;
384  exp_uart_irq_rx_overflow = false;
385  uart_irq_rx_overflow_fired = false;
386 
387  // A set of bytes actually received over RX.
388  uint8_t uart_rx_data[UART_DATASET_SIZE];
389 
390  LOG_INFO("Executing the test.");
391  while (!uart_tx_done || !uart_rx_done || !uart_irq_tx_done_fired ||
392  !uart_irq_rx_overflow_fired) {
393  if (!uart_tx_done && uart_irq_tx_watermark_fired) {
394  uart_irq_tx_watermark_fired = false;
395 
396  // Send the remaining kUartTxData as and when the TX watermark fires.
397  // Intentionally limit the transfer size to 32 bytes at a time. This means
398  // we see multiple TX watermark interrupts in the test.
399  CHECK(uart_transfer_ongoing_bytes(
400  uart, kUartSend, (uint8_t *)kUartTxData, UART_DATASET_SIZE,
401  &uart_tx_bytes_written, 32, &uart_tx_done));
402 
403  if (uart_tx_done) {
404  // At this point, we have sent the required number of bytes.
405  // Expect the TX empty interrupt to fire at some point.
406  exp_uart_irq_tx_done = true;
407  }
408  }
409 
410  if (!uart_rx_done && uart_irq_rx_watermark_fired) {
411  uart_irq_rx_watermark_fired = false;
412 
413  // When RX watermark fires, read the data, but if remaining items are less
414  // than 16, RX watermark won't fire. In that case, keep reading until all
415  // item are received.
416  do {
417  CHECK(uart_transfer_ongoing_bytes(
418  uart, kUartReceive, uart_rx_data, UART_DATASET_SIZE,
419  &uart_rx_bytes_read, UART_DATASET_SIZE, &uart_rx_done));
420  } while (!uart_rx_done && (UART_DATASET_SIZE - uart_rx_bytes_read < 16));
421 
422  if (uart_rx_done) {
423  exp_uart_irq_rx_watermark = false;
424  // At this point we have received the required number of bytes.
425  // We disable the RX watermark interrupt and let the fifo
426  // overflow by dropping all future incoming data.
427  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
429  // Expect the RX overflow interrupt to fire at some point.
430  exp_uart_irq_rx_overflow = true;
431  }
432  }
433 
434  if (uart_irq_tx_done_fired) {
435  exp_uart_irq_tx_watermark = false;
436  exp_uart_irq_tx_empty = false;
437  exp_uart_irq_tx_done = false;
438  // To reach this point the TX empty interrupt must have fired
439  CHECK(uart_irq_tx_empty_fired == true, "TX empty interrupt did not fire");
440  }
441 
442  if (uart_irq_rx_overflow_fired) {
443  exp_uart_irq_rx_overflow = false;
444  }
445 
446  // Wait for the next interrupt to arrive.
447  // This check here is necessary as rx interrupts may sometimes occur ahead
448  // of tx interrupts. When this happens, the tx handling code above is not
449  // triggered and as a result an unexpected tx_done interrupt is fired
450  // later.
451  if (!uart_irq_rx_watermark_fired && !uart_irq_tx_watermark_fired &&
452  !uart_irq_rx_overflow_fired) {
454  }
455  }
456 
457  // Check data consistency.
458  LOG_INFO("Checking the received UART RX data for consistency.");
459  for (int i = 0; i < UART_DATASET_SIZE; ++i) {
460  CHECK(uart_rx_data[i] == kExpUartRxData[i],
461  "UART RX data[%d] mismatched: {act: %x, exp: %x}", i, uart_rx_data[i],
462  kExpUartRxData[i]);
463  }
464 }
465 
466 void config_external_clock(const dif_clkmgr_t *clkmgr) {
467  dif_lc_ctrl_t lc;
468  mmio_region_t lc_ctrl_base_addr =
470  CHECK_DIF_OK(dif_lc_ctrl_init(lc_ctrl_base_addr, &lc));
471 
472  LOG_INFO("Read and check LC state.");
473  dif_lc_ctrl_state_t curr_state;
474  CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
475  CHECK(curr_state == kDifLcCtrlStateRma || curr_state == kDifLcCtrlStateDev,
476  "LC State isn't in {kDifLcCtrlStateRma, kDifLcCtrlStateDev}!");
477 
478  CHECK_STATUS_OK(
479  clkmgr_testutils_enable_external_clock_blocking(clkmgr, kUseLowSpeedSel));
480 }
481 
482 OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);
483 
484 bool test_main(void) {
485  mmio_region_t base_addr;
486 
488  CHECK_DIF_OK(dif_clkmgr_init(base_addr, &clkmgr));
489 
491  CHECK_DIF_OK(dif_pinmux_init(base_addr, &pinmux));
492 
493  if (kUartIdxDv != 0xff) {
494  kUartIdx = kUartIdxDv;
495  } else {
496  OTTF_WAIT_FOR(kUartIdx != 0xff, kCommandTimeout);
497  }
498 
499  // Use the default UART baud rate and clock frequency for the target device.
500  uint64_t uartBaudrate = kUartBaudrate;
501  uint64_t uartFreqHz = kClockFreqPeripheralHz;
502 
503  // Collect the target baud rate and the clock frequency of the UART
504  // peripheral, since the `sim_dv` environment may have modified these for
505  // more extensive testing.
506  if (kUartBaudrateDV) {
507  uartBaudrate = kUartBaudrateDV;
508  }
509  if (kUartClockFreqHzDV) {
510  uartFreqHz = kUartClockFreqHzDV;
511  }
512  CHECK(uartBaudrate <= UINT32_MAX, "uartBaudrate must fit in uint32_t");
513  CHECK(uartFreqHz <= UINT32_MAX, "uartFreqHz must fit in uint32_t");
514 
515  // If we're testing UART0 we need to move the console to UART1.
516  if (kUartIdx == 0 && kDeviceType != kDeviceSimDV) {
517  CHECK_STATUS_OK(
518  uart_testutils_select_pinmux(&pinmux, 1, kUartPinmuxChannelConsole));
519  ottf_console_configure_uart(TOP_EARLGREY_UART1_BASE_ADDR);
520  }
521 
522  CHECK_STATUS_OK(
523  uart_testutils_cfg_params(kUartIdx, (uart_cfg_params_t *)&uart_cfg));
524 
525  LOG_INFO("Test UART%d with base_addr: %08x", kUartIdx, uart_cfg.base_addr);
526 
527  // Attach the UART under test.
528  CHECK_STATUS_OK(
529  uart_testutils_select_pinmux(&pinmux, kUartIdx, kUartPinmuxChannelDut));
530 
531  if (kUseExtClk) {
532  config_external_clock(&clkmgr);
533  }
534  CHECK_STATUS_OK(clkmgr_testutils_enable_clock_counts_with_expected_thresholds(
535  &clkmgr, /*jitter_enabled=*/false, kUseExtClk, kUseLowSpeedSel));
536 
537  // Initialize the UART.
538  mmio_region_t chosen_uart_region = mmio_region_from_addr(uart_cfg.base_addr);
539  uart_init_with_irqs(chosen_uart_region, &uart, (uint32_t)uartBaudrate,
540  (uint32_t)uartFreqHz);
541 
542  // Initialize the PLIC.
543  mmio_region_t plic_base_addr =
545  plic_init_with_irqs(plic_base_addr, &plic);
546 
547  // Enable the external IRQ at Ibex.
548  irq_global_ctrl(true);
549  irq_external_ctrl(true);
550 
551  // Execute the test.
552  execute_test(&uart);
553  CHECK_STATUS_OK(clkmgr_testutils_check_measurement_counts(&clkmgr));
554 
555  return true;
556 }