Software APIs
i2c_host_clock_stretching_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 #include <assert.h>
5 
13 #include "sw/device/lib/runtime/irq.h"
16 #include "sw/device/lib/testing/i2c_testutils.h"
17 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
18 #include "sw/device/lib/testing/rv_plic_testutils.h"
19 #include "sw/device/lib/testing/test_framework/check.h"
21 
23 #include "i2c_regs.h" // Generated.
24 
25 static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
26  "This test assumes the target platform is little endian.");
27 
28 OTTF_DEFINE_TEST_CONFIG();
29 
30 enum {
31 
33 
34  kDeviceAddr = 0x22,
35  // Registers
36  kSwResetReg = 0xDF,
37  kManufacturerIdReg = 0xDE,
38  kProductIdReg = 0xDD,
39  kRxDelayReg = 0xDC,
40  kTxDelayReg = 0xDB,
41  kCache63BitsReg = 0xCF,
42  // Registers values
43  kManufacturerId = 0xA1,
44  kProductId = 0xA2,
45 
46  // Others
47  kDefaultTimeoutMicros = 10000,
48 };
49 
50 /**
51  * Declared volatile because it is referenced in the main program flow as well
52  * as the ISR.
53  */
54 // Hold the irs result.
55 static volatile status_t isr_result;
56 // Used to sync the irs and the main thread.
57 static volatile dif_i2c_irq_t irq_fired;
58 static dif_rv_plic_t plic;
59 static dif_rv_core_ibex_t rv_core_ibex;
60 static dif_pinmux_t pinmux;
61 static dif_i2c_t i2c;
62 
63 /**
64  * Provides external IRQ handling for this test.
65  *
66  * This function overrides the default OTTF external ISR.
67  *
68  * For each IRQ, it performs the following:
69  * 1. Claims the IRQ fired (finds PLIC IRQ index).
70  * 2. Checks that the index belongs to the expected peripheral.
71  * 3. Checks that only the correct / expected IRQ is triggered.
72  * 4. Clears the IRQ at the peripheral.
73  * 5. Completes the IRQ service at PLIC.
74  */
75 static status_t external_isr(void) {
76  dif_rv_plic_irq_id_t plic_irq_id;
77  TRY(dif_rv_plic_irq_claim(&plic, kHart, &plic_irq_id));
78 
81  TRY_CHECK(peripheral == kTopEarlgreyPlicPeripheralI2c2,
82  "IRQ from incorrect peripheral: exp = %d(i2c2), found = %d",
83  kTopEarlgreyPlicPeripheralI2c2, peripheral);
84 
85  irq_fired =
86  (dif_i2c_irq_t)(plic_irq_id - (dif_rv_plic_irq_id_t)
88 
89  LOG_INFO("%s: plic:%d, i2c:%d", __func__, plic_irq_id, irq_fired);
90  TRY(dif_i2c_irq_acknowledge(&i2c, irq_fired));
91 
92  // Complete the IRQ at PLIC.
93  TRY(dif_rv_plic_irq_complete(&plic, kHart, plic_irq_id));
94  return OK_STATUS();
95 }
96 
97 void ottf_external_isr(uint32_t *exc_info) {
98  status_t tmp = external_isr();
99  if (status_ok(isr_result)) {
100  isr_result = tmp;
101  }
102 }
103 
104 static status_t read_manufacture_id(void) {
105  uint8_t reg = kManufacturerIdReg, data = 0;
106  TRY(i2c_testutils_write(&i2c, kDeviceAddr, 1, &reg, true));
107  TRY(i2c_testutils_read(&i2c, kDeviceAddr, 1, &data, kDefaultTimeoutMicros));
108  TRY_CHECK(data == kManufacturerId, "Unexpected value %x", data);
109  return OK_STATUS();
110 }
111 
112 static status_t read_product_id(void) {
113  uint8_t reg = kProductIdReg, data = 0;
114  TRY(i2c_testutils_write(&i2c, kDeviceAddr, 1, &reg, true));
115  TRY(i2c_testutils_read(&i2c, kDeviceAddr, 1, &data, kDefaultTimeoutMicros));
116  TRY_CHECK(data == kProductId, "Unexpected value %x", data);
117  return OK_STATUS();
118 }
119 
120 static status_t rx_stretch_timeout(void) {
121  enum { kTimeoutMillis = 15 };
122  TRY(dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqStretchTimeout,
124 
125  // Set the delay that will make the target to stretch the clock.
126  {
127  uint8_t write_buffer[2] = {kRxDelayReg, kTimeoutMillis};
128  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(write_buffer),
129  write_buffer, true));
130  }
131 
132  uint32_t cycles = (kTimeoutMillis - 1) * 100;
134 
135  uint8_t reg = kProductIdReg;
136  TRY(i2c_testutils_write(&i2c, kDeviceAddr, 1, &reg, true));
137 
138  irq_global_ctrl(false);
139  irq_fired = UINT32_MAX;
140  TRY(i2c_testutils_issue_read(&i2c, kDeviceAddr, 1));
141  ATOMIC_WAIT_FOR_INTERRUPT(irq_fired == kDifI2cIrqStretchTimeout);
142 
143  TRY(dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqStretchTimeout,
145 
146  // Reset the delay to disable clock stretching.
147  {
148  uint8_t write_buffer[2] = {kRxDelayReg, 0};
149  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(write_buffer),
150  write_buffer, true));
151 
152  TRY(dif_i2c_reset_rx_fifo(&i2c));
153  }
154 
155  TRY(dif_i2c_reset_rx_fifo(&i2c));
156 
157  return OK_STATUS();
158 }
159 
160 static status_t tx_stretch(void) {
161  enum { kTimeoutMillis = 1, kTxSize = 63 };
162 
163  // Set the delay that will make the target to stretch the clock.
164  {
165  uint8_t write_buffer[2] = {kTxDelayReg, kTimeoutMillis};
166  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(write_buffer),
167  write_buffer, true));
168  }
169 
170  // Init buffer with random data.
171  uint8_t rnd_data[kTxSize];
172  for (int i = 0; i < sizeof(rnd_data); ++i) {
173  uint32_t rand;
174  TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, 1000, &rand));
175  rnd_data[i] = rand & 0xFF;
176  }
177  uint8_t write_buffer[kTxSize + 1];
178  write_buffer[0] = kCache63BitsReg;
179  memcpy(&write_buffer[1], rnd_data, sizeof(rnd_data));
180  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(write_buffer), write_buffer,
181  false));
182  // The transmission may take a long time due to the clock stretching.
183  const size_t timeout = (kTimeoutMillis * kTxSize) * 1000;
184  busy_spin_micros(timeout);
185 
186  uint8_t reg = kCache63BitsReg;
187  uint8_t read_data[kTxSize] = {0};
188  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(reg), &reg, true));
189  TRY(i2c_testutils_read(&i2c, kDeviceAddr, sizeof(read_data), read_data,
190  timeout));
191  TRY_CHECK_ARRAYS_EQ(read_data, rnd_data, sizeof(read_data));
192 
193  // Reset the delay to disable clock stretching.
194  {
195  uint8_t write_buffer[2] = {kTxDelayReg, 0};
196  TRY(i2c_testutils_write(&i2c, kDeviceAddr, sizeof(write_buffer),
197  write_buffer, true));
198  }
199 
200  return OK_STATUS();
201 }
202 
203 static status_t test_init(void) {
204  mmio_region_t base_addr =
206 
207  TRY(dif_rv_core_ibex_init(base_addr, &rv_core_ibex));
208 
210  TRY(dif_i2c_init(base_addr, &i2c));
211 
213  TRY(dif_pinmux_init(base_addr, &pinmux));
214  TRY(i2c_testutils_select_pinmux(&pinmux, 2, I2cPinmuxPlatformIdCw310Pmod));
216 
218  TRY(dif_rv_plic_init(base_addr, &plic));
219 
220  rv_plic_testutils_irq_range_enable(&plic, kHart,
223 
224  // Enable the external IRQ at Ibex.
225  irq_global_ctrl(true);
226  irq_external_ctrl(true);
227 
228  return OK_STATUS();
229 }
230 
231 bool test_main(void) {
232  CHECK_STATUS_OK(test_init());
233 
236 
237  status_t test_result = OK_STATUS();
238  for (size_t i = 0; i < ARRAYSIZE(speeds); ++i) {
239  CHECK_STATUS_OK(i2c_testutils_set_speed(&i2c, speeds[i]));
240  EXECUTE_TEST(test_result, read_manufacture_id);
241  EXECUTE_TEST(test_result, read_product_id);
242  EXECUTE_TEST(test_result, rx_stretch_timeout);
243  EXECUTE_TEST(test_result, tx_stretch);
244  }
245 
246  return status_ok(test_result) && status_ok(isr_result);
247 }