Software APIs
ottf_console.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 
5 #include "sw/device/lib/testing/test_framework/ottf_console.h"
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 
11 #include "sw/device/lib/base/status.h"
16 #include "sw/device/lib/runtime/irq.h"
18 #include "sw/device/lib/testing/spi_device_testutils.h"
19 #include "sw/device/lib/testing/test_framework/check.h"
20 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
21 #include "sw/device/lib/testing/test_framework/ottf_test_config.h"
22 
23 // TODO: make this toplevel agnostic.
24 #include "dt/dt_rv_plic.h"
25 #include "dt/dt_uart.h"
26 
27 #include "spi_device_regs.h" // Generated.
28 
29 #define MODULE_ID MAKE_MODULE_ID('o', 't', 'c')
30 
31 /**
32  * OTTF console configuration parameters.
33  */
34 enum {
35  /**
36  * SPI device console configuration parameters.
37  */
38  kSpiDeviceRxCommitWait = 63, // clock cycles
39  /**
40  * Flow control parameters.
41  */
42  kFlowControlLowWatermark = 4, // bytes
43  kFlowControlHighWatermark = 8, // bytes
44  kFlowControlRxWatermark = kDifUartWatermarkByte8,
45  /**
46  * HART PLIC Target.
47  */
48  kPlicTarget = 0,
49 };
50 
51 // Potential DIF handles for OTTF console communication.
52 static dif_spi_device_handle_t ottf_console_spi_device;
53 static dif_uart_t ottf_console_uart;
54 
55 // Function pointer to the currently active data sink.
56 static sink_func_ptr sink;
57 // Function pointer to a function that retrieves a single character.
58 static status_t (*getc)(void *);
59 
60 // The `flow_control_state` and `flow_control_irqs` variables are shared between
61 // the interrupt service handler and user code.
62 static volatile ottf_console_flow_control_t flow_control_state;
63 static volatile uint32_t flow_control_irqs;
64 
65 void *ottf_console_get(void) {
66  switch (kOttfTestConfig.console.type) {
67  case kOttfConsoleSpiDevice:
68  return &ottf_console_spi_device;
69  default:
70  return &ottf_console_uart;
71  }
72 }
73 
74 static status_t uart_getc(void *io) {
75  const dif_uart_t *uart = (const dif_uart_t *)io;
76  uint8_t byte;
77  TRY(dif_uart_byte_receive_polled(uart, &byte));
78  TRY(ottf_console_flow_control(uart, kOttfConsoleFlowControlAuto));
79  return OK_STATUS(byte);
80 }
81 
82 /*
83  * The user of this function needs to be aware of the following:
84  * 1. The exact amount of data expected to be sent from the host side must be
85  * known in advance.
86  * 2. Characters should be retrieved from the console as soon as they become
87  * available. Failure to do so may result in an SPI transaction timeout.
88  */
89 static status_t spi_device_getc(void *io) {
90  static size_t index = 0;
91  static upload_info_t info = {0};
93  if (index == info.data_len) {
94  memset(&info, 0, sizeof(upload_info_t));
95  CHECK_STATUS_OK(spi_device_testutils_wait_for_upload(spi_device, &info));
96  index = 0;
97  CHECK_DIF_OK(dif_spi_device_set_flash_status_registers(spi_device, 0x00));
98  }
99 
100  return OK_STATUS(info.data[index++]);
101 }
102 
103 static void spi_device_wait_for_sync(dif_spi_device_handle_t *spi_device) {
104  const uint8_t kBootMagicPattern[4] = {0x02, 0xb0, 0xfe, 0xca};
105  const uint8_t kEmptyPattern[4] = {0};
106 
107  for (size_t i = 0; i < SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH; i++) {
110  i * ARRAYSIZE(kBootMagicPattern), ARRAYSIZE(kBootMagicPattern),
111  kBootMagicPattern));
112  }
113 
114  upload_info_t info = {0};
115  CHECK_STATUS_OK(spi_device_testutils_wait_for_upload(spi_device, &info));
116  // Clear the boot magic in the read buffer.
117  for (size_t i = 0; i < SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH; i++) {
120  i * ARRAYSIZE(kEmptyPattern), ARRAYSIZE(kEmptyPattern), kEmptyPattern));
121  }
122  CHECK_DIF_OK(dif_spi_device_set_flash_status_registers(spi_device, 0x00));
123 }
124 
125 void ottf_console_init(void) {
126  // Initialize/Configure the console device.
127  uintptr_t base_addr = kOttfTestConfig.console.base_addr;
128  switch (kOttfTestConfig.console.type) {
129  case kOttfConsoleUart:
130  // Set a default for the console base address if the base address is not
131  // configured. The default is to use UART0.
132  if (base_addr == 0) {
133  CHECK(kOttfTestConfig.console.type == kOttfConsoleUart);
134  base_addr = dt_uart_primary_reg_block(kDtUart0);
135  }
136 
137  ottf_console_configure_uart(base_addr);
138  sink = get_uart_sink();
139  getc = uart_getc;
140  break;
141  case (kOttfConsoleSpiDevice):
142  ottf_console_configure_spi_device(base_addr);
143  sink = get_spi_device_sink();
144  getc = spi_device_getc;
145  break;
146  default:
147  CHECK(false, "unsupported OTTF console interface.");
148  break;
149  }
150 }
151 
152 void ottf_console_configure_uart(uintptr_t base_addr) {
153  CHECK_DIF_OK(
154  dif_uart_init(mmio_region_from_addr(base_addr), &ottf_console_uart));
155  CHECK(kUartBaudrate <= UINT32_MAX, "kUartBaudrate must fit in uint32_t");
156  CHECK(kClockFreqPeripheralHz <= UINT32_MAX,
157  "kClockFreqPeripheralHz must fit in uint32_t");
158  CHECK_DIF_OK(dif_uart_configure(
159  &ottf_console_uart, (dif_uart_config_t){
160  .baudrate = (uint32_t)kUartBaudrate,
161  .clk_freq_hz = (uint32_t)kClockFreqPeripheralHz,
162  .parity_enable = kDifToggleDisabled,
163  .parity = kDifUartParityEven,
164  .tx_enable = kDifToggleEnabled,
165  .rx_enable = kDifToggleEnabled,
166  }));
167  base_uart_stdout(&ottf_console_uart);
168 
169  // Initialize/Configure console flow control (if requested).
170  if (kOttfTestConfig.enable_uart_flow_control) {
171  ottf_console_flow_control_enable();
172  }
173 }
174 
175 void ottf_console_configure_spi_device(uintptr_t base_addr) {
176  CHECK_DIF_OK(dif_spi_device_init_handle(mmio_region_from_addr(base_addr),
177  &ottf_console_spi_device));
178  CHECK_DIF_OK(dif_spi_device_configure(
179  &ottf_console_spi_device,
181  .tx_order = kDifSpiDeviceBitOrderMsbToLsb,
182  .rx_order = kDifSpiDeviceBitOrderMsbToLsb,
183  .device_mode = kDifSpiDeviceModeFlashEmulation,
184  }));
185  dif_spi_device_flash_command_t read_commands[] = {
186  {
187  // Slot 0: ReadStatus1
188  .opcode = kSpiDeviceFlashOpReadStatus1,
189  .address_type = kDifSpiDeviceFlashAddrDisabled,
190  .dummy_cycles = 0,
191  .payload_io_type = kDifSpiDevicePayloadIoSingle,
192  .payload_dir_to_host = true,
193  },
194  {
195  // Slot 1: ReadStatus2
196  .opcode = kSpiDeviceFlashOpReadStatus2,
197  .address_type = kDifSpiDeviceFlashAddrDisabled,
198  .dummy_cycles = 0,
199  .payload_io_type = kDifSpiDevicePayloadIoSingle,
200  .payload_dir_to_host = true,
201  },
202  {
203  // Slot 2: ReadStatus3
204  .opcode = kSpiDeviceFlashOpReadStatus3,
205  .address_type = kDifSpiDeviceFlashAddrDisabled,
206  .dummy_cycles = 0,
207  .payload_io_type = kDifSpiDevicePayloadIoSingle,
208  .payload_dir_to_host = true,
209  },
210  {
211  // Slot 3: ReadJedecID
212  .opcode = kSpiDeviceFlashOpReadJedec,
213  .address_type = kDifSpiDeviceFlashAddrDisabled,
214  .dummy_cycles = 0,
215  .payload_io_type = kDifSpiDevicePayloadIoSingle,
216  .payload_dir_to_host = true,
217  },
218  {
219  // Slot 4: ReadSfdp
220  .opcode = kSpiDeviceFlashOpReadSfdp,
221  .address_type = kDifSpiDeviceFlashAddr3Byte,
222  .dummy_cycles = 8,
223  .payload_io_type = kDifSpiDevicePayloadIoSingle,
224  .payload_dir_to_host = true,
225  },
226  {
227  // Slot 5: ReadNormal
228  .opcode = kSpiDeviceFlashOpReadNormal,
229  .address_type = kDifSpiDeviceFlashAddr3Byte,
230  .dummy_cycles = 0,
231  .payload_io_type = kDifSpiDevicePayloadIoSingle,
232  .payload_dir_to_host = true,
233  },
234  };
235  for (size_t i = 0; i < ARRAYSIZE(read_commands); ++i) {
236  uint8_t slot = (uint8_t)i + kSpiDeviceReadCommandSlotBase;
238  &ottf_console_spi_device, slot, kDifToggleEnabled, read_commands[i]));
239  }
240  dif_spi_device_flash_command_t write_commands[] = {
241  {
242  // Slot 11: PageProgram
243  .opcode = kSpiDeviceFlashOpPageProgram,
244  .address_type = kDifSpiDeviceFlashAddrCfg,
245  .payload_io_type = kDifSpiDevicePayloadIoSingle,
246  .payload_dir_to_host = false,
247  .upload = true,
248  .set_busy_status = true,
249  },
250  };
251  for (size_t i = 0; i < ARRAYSIZE(write_commands); ++i) {
252  uint8_t slot = (uint8_t)i + kSpiDeviceWriteCommandSlotBase;
254  &ottf_console_spi_device, slot, kDifToggleEnabled, write_commands[i]));
255  }
256  spi_device_wait_for_sync(&ottf_console_spi_device);
257  base_spi_device_stdout(&ottf_console_spi_device);
258 }
259 
260 static uint32_t get_flow_control_watermark_plic_id(void) {
261  for (size_t i = 0; i < kDtUartCount; i++) {
262  dt_uart_t uart = (dt_uart_t)i;
263  if (kOttfTestConfig.console.base_addr == dt_uart_primary_reg_block(uart)) {
264  return dt_uart_irq_to_plic_id(uart, kDtUartIrqRxWatermark);
265  }
266  }
267  return dt_uart_irq_to_plic_id(kDtUart0, kDtUartIrqRxWatermark);
268 }
269 
270 void ottf_console_flow_control_enable(void) {
271  dif_uart_t *uart = (dif_uart_t *)ottf_console_get();
272  CHECK_DIF_OK(dif_uart_watermark_rx_set(uart, kFlowControlRxWatermark));
273  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
275 
276  // Set IRQ priorities to MAX
277  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
278  &ottf_plic, get_flow_control_watermark_plic_id(), kDifRvPlicMaxPriority));
279  // Set Ibex IRQ priority threshold level
280  CHECK_DIF_OK(dif_rv_plic_target_set_threshold(&ottf_plic, kPlicTarget,
282  // Enable IRQs in PLIC
283  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&ottf_plic,
284  get_flow_control_watermark_plic_id(),
285  kPlicTarget, kDifToggleEnabled));
286 
287  flow_control_state = kOttfConsoleFlowControlAuto;
288  irq_global_ctrl(true);
289  irq_external_ctrl(true);
290  // Make sure we're in the Resume state and we emit a Resume to the UART.
291  ottf_console_flow_control((dif_uart_t *)ottf_console_get(),
292  kOttfConsoleFlowControlResume);
293 }
294 
295 // This version of the function is safe to call from within the ISR.
296 static status_t manage_flow_control(const dif_uart_t *uart,
297  ottf_console_flow_control_t ctrl) {
298  if (flow_control_state == kOttfConsoleFlowControlNone) {
299  return OK_STATUS((int32_t)flow_control_state);
300  }
301  if (ctrl == kOttfConsoleFlowControlAuto) {
302  uint32_t avail;
303  TRY(dif_uart_rx_bytes_available(uart, &avail));
304  if (avail < kFlowControlLowWatermark &&
305  flow_control_state != kOttfConsoleFlowControlResume) {
306  // Enable RX watermark interrupt when RX FIFO level is below the
307  // watermark.
308  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
310  ctrl = kOttfConsoleFlowControlResume;
311  } else if (avail >= kFlowControlHighWatermark &&
312  flow_control_state != kOttfConsoleFlowControlPause) {
313  ctrl = kOttfConsoleFlowControlPause;
314  // RX watermark interrupt is status type, so disable the interrupt whilst
315  // RX FIFO is above the watermark to avoid an inifite loop of ISRs.
316  CHECK_DIF_OK(dif_uart_irq_set_enabled(uart, kDifUartIrqRxWatermark,
318  } else {
319  return OK_STATUS((int32_t)flow_control_state);
320  }
321  }
322  uint8_t byte = (uint8_t)ctrl;
323  CHECK_DIF_OK(dif_uart_bytes_send(uart, &byte, 1, NULL));
324  flow_control_state = ctrl;
325  return OK_STATUS((int32_t)flow_control_state);
326 }
327 
328 bool ottf_console_flow_control_isr(uint32_t *exc_info) {
329  dif_uart_t *uart = (dif_uart_t *)ottf_console_get();
330  flow_control_irqs += 1;
331  bool rx;
332  CHECK_DIF_OK(dif_uart_irq_is_pending(uart, kDifUartIrqRxWatermark, &rx));
333  if (rx) {
334  manage_flow_control(uart, kOttfConsoleFlowControlAuto);
335  CHECK_DIF_OK(dif_uart_irq_acknowledge(uart, kDifUartIrqRxWatermark));
336  return true;
337  }
338  return false;
339 }
340 
341 // The public API has to save and restore interrupts to avoid an
342 // unexpected write to the global `flow_control_state`.
343 status_t ottf_console_flow_control(const dif_uart_t *uart,
344  ottf_console_flow_control_t ctrl) {
345  dif_uart_irq_enable_snapshot_t snapshot;
346  CHECK_DIF_OK(dif_uart_irq_disable_all(uart, &snapshot));
347  status_t s = manage_flow_control(uart, ctrl);
348  CHECK_DIF_OK(dif_uart_irq_restore_all(uart, &snapshot));
349  return s;
350 }
351 
352 uint32_t ottf_console_get_flow_control_irqs(void) { return flow_control_irqs; }
353 
354 static bool spi_tx_last_data_chunk(upload_info_t *info) {
355  const static size_t kSpiTxTerminateMagicAddress = 0x100;
356  return info->address == kSpiTxTerminateMagicAddress;
357 }
358 
359 size_t ottf_console_spi_device_read(size_t buf_size, uint8_t *const buf) {
360  size_t received_data_len = 0;
361  upload_info_t info;
362  memset(&info, 0, sizeof(upload_info_t));
363  while (!spi_tx_last_data_chunk(&info)) {
364  CHECK_STATUS_OK(
365  spi_device_testutils_wait_for_upload(&ottf_console_spi_device, &info));
366  if (received_data_len < buf_size) {
367  size_t remaining_buf_size = buf_size - received_data_len;
368  size_t bytes_to_copy = remaining_buf_size < info.data_len
369  ? remaining_buf_size
370  : info.data_len;
371  memcpy(buf + received_data_len, info.data, bytes_to_copy);
372  }
373 
374  received_data_len += info.data_len;
376  &ottf_console_spi_device, 0x00));
377  }
378 
379  return received_data_len;
380 }
381 
382 status_t ottf_console_putbuf(void *io, const char *buf, size_t len) {
383  size_t written_len = sink(io, buf, len);
384  if (len != written_len) {
385  return DATA_LOSS((int32_t)(len - written_len));
386  }
387  return OK_STATUS((int32_t)len);
388 }
389 
390 status_t ottf_console_getc(void *io) { return getc(io); }