Software APIs
i2c_testutils.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/i2c_testutils.h"
6 
7 #include <assert.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 
17 #include "sw/device/lib/testing/pinmux_testutils.h"
18 #include "sw/device/lib/testing/test_framework/check.h"
19 
21 #include "i2c_regs.h" // Generated.
22 
23 #define MODULE_ID MAKE_MODULE_ID('i', 'i', 't')
24 
25 enum {
26  kI2cWrite = 0,
27  kI2cRead = 1,
28 };
29 
30 // Default flags for i2c operations.
31 static const dif_i2c_fmt_flags_t kDefaultFlags = {.start = false,
32  .stop = false,
33  .read = false,
34  .read_cont = false,
35  .suppress_nak_irq = false};
36 
37 /**
38  * Define an i2c pinmux configuration.
39  */
40 typedef struct i2c_pinmux_pins {
44 
45 /**
46  * Define an i2c pinmux configuration.
47  */
48 typedef struct i2c_platform_pins {
52 
53 /**
54  * This table store the pins of all i2c instances of Earlgrey.
55  * This is used to connect i2c instances to mio pins based on the platform.
56  */
57 static const i2c_pinmux_pins_t kI2cPinmuxPins[] = {
58  // I2C0.
59  {.sda =
60  {
63  },
64  .scl =
65  {
68  }},
69  // I2C1.
70  {.sda =
71  {
74  },
75  .scl =
76  {
79  }},
80  // I2C2.
81  {.sda =
82  {
85  },
86  .scl =
87  {
90  }},
91 };
92 
93 /**
94  * Map the combination of platform and I2C ID to pins for SCL and SDA.
95  */
96 static status_t map_platform_to_pins(i2c_pinmux_platform_id_t platform,
97  uint8_t i2c_id,
98  i2c_platform_pins_t *pins) {
99  TRY_CHECK(pins != NULL);
100  switch (platform) {
101  case I2cPinmuxPlatformIdHyper310: // CW310 HyperDebug
102  *pins =
103  (i2c_platform_pins_t){.sda =
104  {
105  .mio_out = kTopEarlgreyPinmuxMioOutIoa7,
107  },
108  .scl = {
109  .mio_out = kTopEarlgreyPinmuxMioOutIoa8,
111  }};
112  break;
113  case I2cPinmuxPlatformIdDvsim: // DV
114  // In DV, there's one agent for each I2C instance, with a fixed set of
115  // muxed pins.
116  switch (i2c_id) {
117  case 0: // I2C0 uses the same pins as CW310 HyperDebug
118  TRY(map_platform_to_pins(I2cPinmuxPlatformIdHyper310, i2c_id, pins));
119  break;
120  case 1:
121  *pins = (i2c_platform_pins_t){
122  .sda =
123  {
126  },
127  .scl =
128  {
129  .mio_out = kTopEarlgreyPinmuxMioOutIob9,
131  },
132  };
133  break;
134  case 2: // I2C2 uses the same pins as CW310 PMOD
135  TRY(map_platform_to_pins(I2cPinmuxPlatformIdCw310Pmod, i2c_id, pins));
136  break;
137  default:
138  TRY_CHECK(false, "invalid i2c_id: %0d", i2c_id);
139  break;
140  }
141  break;
142  case I2cPinmuxPlatformIdCw310Pmod: // CW310 PMOD
143  *pins = (i2c_platform_pins_t){
144  .sda =
145  {
148  },
149  .scl = {
152  }};
153  break;
154  default:
155  TRY_CHECK(false, "invalid platform: %0d", platform);
156  break;
157  }
158  return OK_STATUS();
159 }
160 
161 status_t i2c_testutils_write(const dif_i2c_t *i2c, uint8_t addr,
162  size_t byte_count, const uint8_t *data,
163  bool skip_stop) {
164  dif_i2c_fmt_flags_t flags = kDefaultFlags;
165  uint8_t data_frame;
166 
167  TRY_CHECK(byte_count > 0);
168 
169  // First write the address.
170  flags.start = true;
171  data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cWrite;
172  TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
173 
174  // Once address phase is through, blast the rest as generic data.
175  flags = kDefaultFlags;
176  if (byte_count > 1) {
177  TRY(dif_i2c_write_bytes_raw(i2c, byte_count - 1, data, flags));
178  }
179  // Issue a stop for the last byte.
180  flags.stop = !skip_stop;
181  TRY(dif_i2c_write_byte_raw(i2c, data[byte_count - 1], flags));
182 
183  // TODO: Check for errors / status.
184  return OK_STATUS();
185 }
186 
187 status_t i2c_testutils_issue_read(const dif_i2c_t *i2c, uint8_t addr,
188  uint8_t byte_count) {
189  dif_i2c_fmt_flags_t flags = kDefaultFlags;
190  uint8_t data_frame;
191  // The current function doesn't check for space in the FIFOs
192 
193  // First, issue a write the address transaction.
194  flags.start = true;
195  data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cRead;
196  TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
197 
198  TRY(i2c_testutils_wait_transaction_finish(i2c));
199  dif_i2c_controller_halt_events_t halt_events = {0};
200  TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
201  if (!halt_events.nack_received) {
202  // We got an ack, schedule the read transaction.
203  flags = kDefaultFlags;
204  flags.read = true;
205  flags.stop = true;
206  // Inform the controller how many bytes to read overall.
207  TRY(dif_i2c_write_byte_raw(i2c, byte_count, flags));
208  } else {
209  // We got a nack, clear the irq and return. The caller may retry later.
210  TRY(dif_i2c_reset_fmt_fifo(i2c));
211  TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
212  // Force Stop to be sent after the unexpected NACK.
215  }
216  return OK_STATUS(halt_events.nack_received);
217 }
218 
219 status_t i2c_testutils_read(const dif_i2c_t *i2c, uint8_t addr,
220  size_t byte_count, uint8_t *data, size_t timeout) {
221  uint32_t nak_count = 0;
222  ibex_timeout_t timer = ibex_timeout_init(timeout);
223 
224  // Make sure to start from a clean state.
225  dif_i2c_controller_halt_events_t halt_events = {0};
226  TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
227  TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
228  TRY(dif_i2c_reset_rx_fifo(i2c));
229  do {
230  // Per chunk, as many bytes as fit into the RX FIFO could be transferred.
231  // However, as the number of bytes has to be communicated to the target in
232  // one byte, UINT8_MAX is another upper limit.
233  size_t max_chunk =
234  I2C_PARAM_FIFO_DEPTH < UINT8_MAX ? I2C_PARAM_FIFO_DEPTH : UINT8_MAX;
235  uint8_t chunk = (uint8_t)(byte_count < max_chunk ? byte_count : max_chunk);
236 
237  // Loop until we get an ACK from the device or a timeout.
238  while (TRY(i2c_testutils_issue_read(i2c, addr, chunk))) {
239  nak_count++;
240  if (ibex_timeout_check(&timer)) {
241  return DEADLINE_EXCEEDED();
242  }
243  }
244 
245  if (chunk > 0) {
246  TRY(dif_i2c_read_bytes(i2c, chunk, data));
247  data += chunk;
248  byte_count -= chunk;
249  }
250  } while (byte_count > 0);
251 
252  TRY(i2c_testutils_wait_transaction_finish(i2c));
253  return OK_STATUS((int32_t)nak_count);
254 }
255 
256 status_t i2c_testutils_target_check_start(const dif_i2c_t *i2c, uint8_t *addr) {
257  dif_i2c_level_t acq_fifo_lvl;
258  TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
259  TRY_CHECK(acq_fifo_lvl > 1);
260 
261  dif_i2c_signal_t signal;
262  uint8_t byte;
263  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
264  // Check acq_fifo is as expected and write addr and continue
265  TRY_CHECK(signal == kDifI2cSignalStart);
266  *addr = byte >> 1;
267 
268  return OK_STATUS(byte & kI2cRead);
269 }
270 
271 status_t i2c_testutils_target_check_end(const dif_i2c_t *i2c,
272  uint8_t *cont_byte) {
273  dif_i2c_level_t acq_fifo_lvl;
274  TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
275  TRY_CHECK(acq_fifo_lvl >= 1);
276 
277  dif_i2c_signal_t signal;
278  uint8_t byte;
279  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
280  // Check transaction is terminated with a stop or a continue that the caller
281  // is prepared to handle
282  if (signal == kDifI2cSignalStop) {
283  return OK_STATUS(false);
284  }
285  TRY_CHECK(cont_byte != NULL);
286  *cont_byte = byte;
287  TRY_CHECK(signal == kDifI2cSignalRepeat);
288 
289  return OK_STATUS(true);
290 }
291 
292 status_t i2c_testutils_target_read(const dif_i2c_t *i2c, uint16_t byte_count,
293  const uint8_t *data) {
294  dif_i2c_level_t tx_fifo_lvl, acq_fifo_lvl;
295  TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl));
296  // Check there's space in tx_fifo and acq_fifo
297  TRY_CHECK(tx_fifo_lvl + byte_count <= I2C_PARAM_FIFO_DEPTH);
298  TRY_CHECK(acq_fifo_lvl + 2 <= I2C_PARAM_FIFO_DEPTH);
299 
300  for (uint16_t i = 0; i < byte_count; ++i) {
301  TRY(dif_i2c_transmit_byte(i2c, data[i]));
302  }
303  // TODO: Check for errors / status.
304  return OK_STATUS();
305 }
306 
307 status_t i2c_testutils_target_check_read(const dif_i2c_t *i2c, uint8_t *addr,
308  uint8_t *cont_byte) {
309  int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
310  TRY_CHECK(dir == kI2cRead);
311  // TODO: Check for errors / status.
312  return i2c_testutils_target_check_end(i2c, cont_byte);
313 }
314 
315 status_t i2c_testutils_target_write(const dif_i2c_t *i2c, uint16_t byte_count) {
316  dif_i2c_level_t acq_fifo_lvl;
317  TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
318  TRY_CHECK(acq_fifo_lvl + 2 + byte_count <= I2C_PARAM_FIFO_DEPTH);
319 
320  // TODO: Check for errors / status.
321  return OK_STATUS();
322 }
323 
324 status_t i2c_testutils_target_check_write(const dif_i2c_t *i2c,
325  uint16_t byte_count, uint8_t *addr,
326  uint8_t *bytes, uint8_t *cont_byte) {
327  dif_i2c_level_t acq_fifo_lvl;
328  TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
329  TRY_CHECK(acq_fifo_lvl >= 2 + byte_count);
330 
331  int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
332  TRY_CHECK(dir == kI2cWrite);
333 
334  for (uint16_t i = 0; i < byte_count; ++i) {
335  dif_i2c_signal_t signal;
336  TRY(dif_i2c_acquire_byte(i2c, bytes + i, &signal));
337  TRY_CHECK(signal == kDifI2cSignalNone);
338  }
339 
340  // TODO: Check for errors / status.
341 
342  return i2c_testutils_target_check_end(i2c, cont_byte);
343 }
344 
345 status_t i2c_testutils_select_pinmux(const dif_pinmux_t *pinmux, uint8_t i2c_id,
346  i2c_pinmux_platform_id_t platform) {
347  TRY_CHECK(
348  platform < I2cPinmuxPlatformIdCount && i2c_id < ARRAYSIZE(kI2cPinmuxPins),
349  "Index out of bounds");
350  i2c_platform_pins_t platform_pins;
351  TRY(map_platform_to_pins(platform, i2c_id, &platform_pins));
352  // Configure sda pin.
353  TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].sda.peripheral_in,
354  platform_pins.sda.insel));
355  TRY(dif_pinmux_output_select(pinmux, platform_pins.sda.mio_out,
356  kI2cPinmuxPins[i2c_id].sda.outsel));
357 
358  // Configure scl pin.
359  TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].scl.peripheral_in,
360  platform_pins.scl.insel));
361  TRY(dif_pinmux_output_select(pinmux, platform_pins.scl.mio_out,
362  kI2cPinmuxPins[i2c_id].scl.outsel));
363  return OK_STATUS();
364 }
365 
366 status_t i2c_testutils_detach_pinmux(const dif_pinmux_t *pinmux,
367  uint8_t i2c_id) {
368  // Configure sda pin.
369  TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].sda.peripheral_in,
371 
372  // Configure scl pin.
373  TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].scl.peripheral_in,
375  return OK_STATUS();
376 }
377 
378 status_t i2c_testutils_set_speed(const dif_i2c_t *i2c, dif_i2c_speed_t speed) {
379  uint32_t speed_khz = 0;
380  switch (speed) {
382  LOG_INFO("Setting i2c to %s mode.", "Standard (100kHz)");
383  speed_khz = 100;
384  break;
385  case kDifI2cSpeedFast:
386  LOG_INFO("Setting i2c to %s mode.", "Fast (400kHz)");
387  speed_khz = 400;
388  break;
390  LOG_INFO("Setting i2c to %s mode.", "FastPlus (1000kHz)");
391  speed_khz = 1000;
392  break;
393  }
394  // I2C speed parameters.
395  dif_i2c_timing_config_t timing_config = {
397  .clock_period_nanos =
398  (uint32_t)udiv64_slow(1000000000, kClockFreqPeripheralHz, NULL),
399  .sda_rise_nanos = 400,
400  .sda_fall_nanos = 110,
401  .scl_period_nanos = 1000000 / speed_khz};
402 
404  TRY(dif_i2c_get_status(i2c, &status));
405  if (status.enable_host) {
407  }
408  dif_i2c_config_t config;
409  TRY(dif_i2c_compute_timing(timing_config, &config));
410 
411  LOG_INFO("period:%d nanos, cycles={fall=%d, rise=%d, hi=%d, lo=%d}",
412  timing_config.clock_period_nanos, config.fall_cycles,
413  config.rise_cycles, config.scl_time_high_cycles,
414  config.scl_time_low_cycles);
415 
416  TRY(dif_i2c_configure(i2c, config));
417  // Reenable if it was enabled before.
418  if (status.enable_host) {
420  }
421 
422  return OK_STATUS();
423 }
424 
425 status_t i2c_testutils_wait_host_idle(const dif_i2c_t *i2c) {
427  do {
428  TRY(dif_i2c_get_status(i2c, &status));
429  } while (!status.host_idle);
430  return OK_STATUS();
431 }
432 
433 status_t i2c_testutils_wait_transaction_finish(const dif_i2c_t *i2c) {
435  bool controller_halted = false;
436  do {
437  TRY(dif_i2c_irq_is_pending(i2c, kDifI2cIrqControllerHalt,
438  &controller_halted));
439  if (controller_halted) {
440  return OK_STATUS(1);
441  }
442  TRY(dif_i2c_get_status(i2c, &status));
443  } while (!status.fmt_fifo_empty);
444  return OK_STATUS(0);
445 }