Software APIs
spi_host_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 
12 #include "sw/device/lib/testing/pinmux_testutils.h"
13 #include "sw/device/lib/testing/rand_testutils.h"
14 #include "sw/device/lib/testing/spi_host_testutils.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
17 #include "sw/device/lib/testing/test_framework/status.h"
18 
20 
21 OTTF_DEFINE_TEST_CONFIG();
22 
23 /**
24  * Indicates the spi_host instance under test.
25  *
26  * This constant is backdoor_overwritten by the vseq.
27  * (See comment in uart_tx_rx_test.c for details on 'static volatile const'
28  * qualifiers)
29  */
30 static volatile const uint8_t kSPIHostIdx = 0x0;
31 
32 #define DATA_SET_SIZE 16
33 
34 static dif_spi_host_t spi_host;
35 static dif_pinmux_t pinmux;
36 
37 // pinmap defined in chip_if.sv (spi_device1_if)
38 static const top_earlgrey_muxed_pads_t spi_host1_muxed_pads[6] = {
39  kTopEarlgreyMuxedPadsIob0, // sck
40  kTopEarlgreyMuxedPadsIob1, // csb
41  kTopEarlgreyMuxedPadsIob6, // sio[3]
42  kTopEarlgreyMuxedPadsIob5, // sio[2]
43  kTopEarlgreyMuxedPadsIob4, // sio[1]
44  kTopEarlgreyMuxedPadsIob3, // sio[0]
45 };
46 
47 // For spi_host1
48 // sck output
49 // csb output
50 // sio[0:3] bidir (input+output)
51 
52 /** To setup the pinmux using the enum's in top_earlgrey.h ...
53  *
54  * - Choose corresponding pad/periph
55  * from...
56  *
57  * dif_result_t dif_pinmux_output_select(...,
58  * dif_pinmux_index_t mio_pad_output, | top_earlgrey_pinmux_mio_out_t
59  * dif_pinmux_index_t outsel) | top_earlgrey_pinmux_outsel_t
60  * dif_result_t dif_pinmux_input_select(...,
61  * dif_pinmux_index_t peripheral_input, |
62  * top_earlgrey_pinmux_peripheral_in_t dif_pinmux_index_t insel) |
63  * top_earlgrey_pinmux_insel_t
64  *
65  */
66 
67 typedef struct pinmux_select {
69  dif_pinmux_index_t peripheral;
71 
72 static const pinmux_select_t pinmux_out_config[] = {
73  // spi_host1
74  {
76  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Sck, // SCK
77  },
78  {
80  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Csb, // CSB
81  },
82  {
84  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Sd0, // sio[0]
85  },
86  {
88  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Sd1, // sio[1]
89  },
90  {
92  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Sd2, // sio[2]
93  },
94  {
96  .peripheral = kTopEarlgreyPinmuxOutselSpiHost1Sd3, // sio[3]
97  },
98 };
99 
100 static const pinmux_select_t pinmux_in_config[] = {
101  // spi_host1
102  {
104  .peripheral = kTopEarlgreyPinmuxPeripheralInSpiHost1Sd0, // sio[0]
105  },
106  {
108  .peripheral = kTopEarlgreyPinmuxPeripheralInSpiHost1Sd1, // sio[1]
109  },
110  {
112  .peripheral = kTopEarlgreyPinmuxPeripheralInSpiHost1Sd2, // sio[2]
113  },
114  {
116  .peripheral = kTopEarlgreyPinmuxPeripheralInSpiHost1Sd3, // sio[3]
117  },
118 };
119 
120 /**
121  * Initialize the provided SPI host.
122  */
123 void init_spi_host(dif_spi_host_t *spi_host,
124  uint32_t peripheral_clock_freq_hz) {
125  dif_spi_host_config_t config = {
126  .spi_clock = peripheral_clock_freq_hz / 2,
127  .peripheral_clock_freq_hz = peripheral_clock_freq_hz,
128  .chip_select = {.idle = 2, .trail = 2, .lead = 2},
129  .full_cycle = true,
130  .cpha = true,
131  .cpol = true,
132  };
133  CHECK_DIF_OK(dif_spi_host_configure(spi_host, config));
134  CHECK_DIF_OK(dif_spi_host_output_set_enabled(spi_host, /*enabled=*/true));
135 }
136 
137 /**
138  * Setup pads and pinmux for spi_host0
139  *
140  * This peripheral is 'muxed', so configure the pinmux as well as pads.
141  */
142 void setup_pinmux_pads_spi_host1(void) {
143  // Set weak pull-ups, fast slew rate and strong drive strengh for the pads
144  dif_pinmux_pad_attr_t out_attr;
145  dif_pinmux_pad_attr_t in_attr = {
146  .slew_rate = 1,
147  .drive_strength = 3,
148  // set weak pull-ups for all the pads
149  .flags = kDifPinmuxPadAttrPullResistorEnable |
150  kDifPinmuxPadAttrPullResistorUp};
151  dif_result_t res;
152  for (uint32_t i = 0; i < ARRAYSIZE(spi_host1_muxed_pads); ++i) {
153  res = dif_pinmux_pad_write_attrs(&pinmux, spi_host1_muxed_pads[i],
154  kDifPinmuxPadKindMio, in_attr, &out_attr);
155  if (res == kDifError) {
156  // Some target platforms may not support the specified value for slew rate
157  // and drive strength. If that's the case, use the values actually
158  // supported.
159  if (out_attr.slew_rate != in_attr.slew_rate) {
160  LOG_INFO(
161  "Specified slew rate not supported, trying supported slew rate");
162  in_attr.slew_rate = out_attr.slew_rate;
163  }
164  if (out_attr.drive_strength != in_attr.drive_strength) {
165  LOG_INFO(
166  "Specified drive strength not supported, trying supported drive "
167  "strength");
168  in_attr.drive_strength = out_attr.drive_strength;
169  }
170  CHECK_DIF_OK(dif_pinmux_pad_write_attrs(&pinmux, spi_host1_muxed_pads[i],
171  kDifPinmuxPadKindMio, in_attr,
172  &out_attr));
173  // Note: fallthrough with the modified `in_attr` so that the same
174  // attributes are used for all pads.
175  }
176  }
177 
178  // Setup Inputs
179  for (int i = 0; i < ARRAYSIZE(pinmux_in_config); ++i) {
180  pinmux_select_t setting = pinmux_in_config[i];
181  CHECK_DIF_OK(
182  dif_pinmux_input_select(&pinmux, setting.peripheral, setting.pad));
183  }
184  // Setup Outputs
185  for (int i = 0; i < ARRAYSIZE(pinmux_out_config); ++i) {
186  pinmux_select_t setting = pinmux_out_config[i];
187  CHECK_DIF_OK(
188  dif_pinmux_output_select(&pinmux, setting.pad, setting.peripheral));
189  }
190 }
191 
192 bool test_main(void) {
193  // Initialize the pinmux.
194  CHECK_DIF_OK(dif_pinmux_init(
196  pinmux_testutils_init(&pinmux);
197 
198  // Setup pinmux if required, enable weak pull-up on relevant pads, set slew
199  // rate and drive strength.
200  CHECK_STATUS_OK(
201  spi_host_testutils_configure_host0_pad_attrs(&pinmux)); // direct
202  setup_pinmux_pads_spi_host1(); // muxed
203 
204  // Setup spi host configuration
205  LOG_INFO("Testing spi_host%0d", kSPIHostIdx);
206  uintptr_t base_addr;
207  uint64_t clkHz;
208  switch (kSPIHostIdx) {
209  case 0: {
212  break;
213  }
214  case 1: {
216  clkHz = kClockFreqPeripheralHz;
217  break;
218  }
219  default:
220  LOG_FATAL("Invalid kSPIHostIdx: %d", kSPIHostIdx);
221  return false;
222  }
223  CHECK_DIF_OK(dif_spi_host_init(mmio_region_from_addr(base_addr), &spi_host));
224  init_spi_host(&spi_host, (uint32_t)clkHz);
225 
226  // DV sync message
227  LOG_INFO("spi host configuration complete");
228 
229  uint32_t expected_data[DATA_SET_SIZE];
230  uint32_t received_data[DATA_SET_SIZE];
231  for (uint32_t i = 0; i < ARRAYSIZE(expected_data); ++i) {
232  expected_data[i] = rand_testutils_gen32();
233  }
234 
235  // Define the segments:
236  // 1st segment, TX only, host sends out the first word.
237  // 2nd segment, Bidirectional. The external device begins sending back data
238  // that it received. So it always lags the TX by 1 word.
239  // 3rd segment, RX only, final word readback.
240  dif_spi_host_segment_t host_operations[3] = {
242  .tx = {.width = kDifSpiHostWidthStandard,
243  .buf = &expected_data[0],
244  .length = 4}},
245 
247  .bidir = {.width = kDifSpiHostWidthStandard,
248  .txbuf = &expected_data[1],
249  .rxbuf = received_data,
250  .length = (DATA_SET_SIZE - 1) * sizeof(uint32_t)}},
251 
252  {.type = kDifSpiHostSegmentTypeRx,
253  .tx = {.width = kDifSpiHostWidthStandard,
254  .buf = &received_data[DATA_SET_SIZE - 1],
255  .length = 4}},
256  };
257 
258  // Issue the transaction
259  CHECK_DIF_OK(dif_spi_host_transaction(&spi_host, /*csid=*/0, host_operations,
260  ARRAYSIZE(host_operations)));
261 
262  CHECK_ARRAYS_EQ(received_data, expected_data, ARRAYSIZE(expected_data));
263 
264  return true;
265 }