Software APIs
i2c_device_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 
13 #include "sw/device/lib/runtime/irq.h"
15 #include "sw/device/lib/testing/i2c_testutils.h"
16 #include "sw/device/lib/testing/rand_testutils.h"
17 #include "sw/device/lib/testing/test_framework/check.h"
19 #include "sw/device/lib/testing/test_framework/status.h"
20 
22 #include "sw/device/lib/testing/autogen/isr_testutils.h"
23 
24 // TODO #14111, remove it once pinout configuration is provided
25 #include "i2c_regs.h"
26 #include "pinmux_regs.h"
27 
28 static dif_i2c_t i2c;
29 static dif_pinmux_t pinmux;
30 static dif_rv_plic_t plic;
31 
32 OTTF_DEFINE_TEST_CONFIG();
33 
34 /**
35  * This symbol is meant to be backdoor loaded by the testbench.
36  * The testbench will inform the test the rough speed of the clock
37  * used by the I2C modules.
38  *
39  * The I2C Device state machine does depend on the I2C timing configuration
40  */
41 static volatile const uint8_t kClockPeriodNanos = 0;
42 static volatile const uint8_t kI2cRiseFallNanos = 0;
43 static volatile const uint32_t kI2cClockPeriodNanos = 0;
44 
45 /**
46  * This symbol is meant to be backdoor loaded by the testbench.
47  * to indicate which I2c is actually under test.
48  */
49 static volatile const uint8_t kI2cIdx = 0;
50 
51 /**
52  * This set of symbols is meant to be backdoor loaded by the testbench.
53  * to indicate the address that will be listened to by the device.
54  */
55 static volatile const uint8_t kI2cDeviceAddress0 = 0x55;
56 static volatile const uint8_t kI2cDeviceMask0 = 0x7f;
57 static volatile const uint8_t kI2cDeviceAddress1 = 0x7f; // disable match on
58  // second address
59 static volatile const uint8_t kI2cDeviceMask1 = 0x7f;
60 
61 /**
62  * This symbol is meant to be backdoor loaded by the testbench.
63  * to indicate the number of bytes that should be sent.
64  *
65  * Because the test doesn't manage the FIFO during transaction, there's a limit
66  * to the number of bytes we can loopback in the test. I2C_PARAM_FIFO_DEPTH - 4
67  */
68 static volatile const uint8_t kI2cByteCount = 0;
69 
70 static volatile bool tx_empty_irq_seen = false;
71 static volatile bool cmd_complete_irq_seen = false;
72 
73 /**
74  * This constant indicates the number of interrupt requests.
75  */
76 enum {
77  kNumI2cIrqs = 5,
78 };
79 
80 typedef struct i2c_conf {
81  const int unsigned base_addr;
82  const uint32_t i2c_irq_fmt_threshold_id;
83  const top_earlgrey_plic_irq_id_t plic_irqs[kNumI2cIrqs];
84 } i2c_conf_t;
85 
86 const i2c_conf_t i2c_configuration[] = {
87  {.base_addr = TOP_EARLGREY_I2C0_BASE_ADDR,
88  .i2c_irq_fmt_threshold_id = kTopEarlgreyPlicIrqIdI2c0FmtThreshold,
94  {.base_addr = TOP_EARLGREY_I2C1_BASE_ADDR,
95  .i2c_irq_fmt_threshold_id = kTopEarlgreyPlicIrqIdI2c1FmtThreshold,
101  {.base_addr = TOP_EARLGREY_I2C2_BASE_ADDR,
102  .i2c_irq_fmt_threshold_id = kTopEarlgreyPlicIrqIdI2c2FmtThreshold,
108 
109 /**
110  * Provides external irq handling for this test.
111  *
112  * This function overrides the default OTTF external ISR.
113  */
114 void ottf_external_isr(uint32_t *exc_info) {
115  plic_isr_ctx_t plic_ctx = {.rv_plic = &plic,
116  .hart_id = kTopEarlgreyPlicTargetIbex0};
117 
118  i2c_isr_ctx_t i2c_ctx = {
119  .i2c = &i2c,
120  .plic_i2c_start_irq_id =
121  i2c_configuration[kI2cIdx].i2c_irq_fmt_threshold_id,
122  .expected_irq = 0,
123  .is_only_irq = false};
124 
126  dif_i2c_irq_t i2c_irq;
127  isr_testutils_i2c_isr(plic_ctx, i2c_ctx, false, &peripheral, &i2c_irq);
128 
129  switch (i2c_irq) {
130  case kDifI2cIrqTxStretch:
131  tx_empty_irq_seen = true;
132  i2c_irq = kDifI2cIrqTxStretch;
133  break;
134  case kDifI2cIrqCmdComplete:
135  cmd_complete_irq_seen = true;
136  i2c_irq = kDifI2cIrqCmdComplete;
137  break;
138  default:
139  LOG_ERROR("Unexpected interrupt (at I2C): %d", i2c_irq);
140  break;
141  }
142 }
143 
144 void check_addr(uint8_t addr, dif_i2c_id_t id0, dif_i2c_id_t id1) {
145  CHECK(((addr & id0.mask) == id0.address) ||
146  ((addr & id1.mask) == id1.address));
147 }
148 
149 bool test_main(void) {
150  LOG_INFO("Testing I2C index %d", kI2cIdx);
151 
152  if (kI2cByteCount > I2C_PARAM_FIFO_DEPTH - 4) {
153  LOG_ERROR(
154  "Test cannot fit %d bytes, 2 START records, and 2 STOP records in "
155  "buffers of depth %d",
156  kI2cByteCount, I2C_PARAM_FIFO_DEPTH);
157  }
158 
159  CHECK_DIF_OK(dif_i2c_init(
160  mmio_region_from_addr(i2c_configuration[kI2cIdx].base_addr), &i2c));
161 
162  CHECK_DIF_OK(dif_pinmux_init(
164 
165  CHECK_DIF_OK(dif_rv_plic_init(
167 
168  CHECK_STATUS_OK(
169  i2c_testutils_select_pinmux(&pinmux, kI2cIdx, I2cPinmuxPlatformIdDvsim));
170 
171  // Enable functional interrupts as well as error interrupts to make sure
172  // everything is behaving as expected.
173  for (uint32_t i = 0; i < kNumI2cIrqs; ++i) {
174  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
175  &plic, i2c_configuration[kI2cIdx].plic_irqs[i],
177 
178  // Assign a default priority
179  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
180  &plic, i2c_configuration[kI2cIdx].plic_irqs[i], kDifRvPlicMaxPriority));
181  }
182 
183  // Enable the external IRQ at Ibex.
184  irq_global_ctrl(true);
185  irq_external_ctrl(true);
186 
187  // I2C speed parameters.
188  dif_i2c_timing_config_t timing_config = {
190  .clock_period_nanos = kClockPeriodNanos,
191  .sda_rise_nanos = kI2cRiseFallNanos,
192  .sda_fall_nanos = kI2cRiseFallNanos,
193  .scl_period_nanos = kI2cClockPeriodNanos};
194 
195  dif_i2c_config_t config;
196  CHECK_DIF_OK(dif_i2c_compute_timing(timing_config, &config));
197  CHECK_DIF_OK(dif_i2c_configure(&i2c, config));
198  dif_i2c_id_t id0 = {.mask = kI2cDeviceMask0, .address = kI2cDeviceAddress0},
199  id1 = {.mask = kI2cDeviceMask1, .address = kI2cDeviceAddress1};
200  CHECK_DIF_OK(dif_i2c_set_device_id(&i2c, &id0, &id1));
201  CHECK_DIF_OK(dif_i2c_device_set_enabled(&i2c, kDifToggleEnabled));
202 
203  // TODO #15081, transaction complete may not be set by i2c device.
204  CHECK(!cmd_complete_irq_seen);
205 
206  CHECK_DIF_OK(
207  dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqTxStretch, kDifToggleEnabled));
208  CHECK_DIF_OK(
209  dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqCmdComplete, kDifToggleEnabled));
210 
211  // Randomize variables.
212  uint8_t expected_data[kI2cByteCount];
213  LOG_INFO("Loopback %d bytes with addresses %0h, %0h", kI2cByteCount,
214  kI2cDeviceAddress0, kI2cDeviceAddress1);
215 
216  // Controlling the randomization from C side is a bit slow, but might be
217  // easier for portability to a different setup later.
218  for (uint32_t i = 0; i < kI2cByteCount; ++i) {
219  expected_data[i] = (uint8_t)rand_testutils_gen32_range(0, 0xff);
220  };
221 
222  dif_i2c_level_t tx_fifo_lvl;
223  CHECK_DIF_OK(dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, NULL));
224  IBEX_SPIN_FOR(!(tx_fifo_lvl > 0 && tx_empty_irq_seen == false), 100);
225  CHECK_STATUS_OK(
226  i2c_testutils_target_read(&i2c, kI2cByteCount, expected_data));
227  tx_empty_irq_seen = false;
228 
229  LOG_INFO("Data written to fifo");
230 
231  dif_i2c_level_t acq_fifo_lvl;
232  do {
233  CHECK_DIF_OK(
234  dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl));
235  } while (acq_fifo_lvl < 2);
236 
237  CHECK(tx_fifo_lvl == 0);
238 
239  uint8_t addr;
240  CHECK_STATUS_OK(i2c_testutils_target_check_read(&i2c, &addr, NULL));
241  check_addr(addr, id0, id1);
242 
243  // Read data from i2c device.
244  CHECK_STATUS_OK(i2c_testutils_target_write(&i2c, kI2cByteCount));
245  do {
246  CHECK_DIF_OK(
247  dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl));
248  } while (acq_fifo_lvl < kI2cByteCount + 2); // acquired message, address and
249  // junk
250 
251  uint8_t received_data[kI2cByteCount];
252  CHECK_STATUS_OK(i2c_testutils_target_check_write(&i2c, kI2cByteCount, &addr,
253  received_data, NULL));
254  check_addr(addr, id0, id1);
255 
256  for (uint8_t i = 0; i < kI2cByteCount; ++i) {
257  CHECK(expected_data[i] == received_data[i]);
258  }
259 
260  CHECK(cmd_complete_irq_seen);
261 
262  return true;
263 }