Add chip_sw_i2c_device_tx_rx to edacloud and fix other problem with i2c tests Change-Id: Ic2b672233727c03b7e900958352e7c05df355760
diff --git a/hw/top_matcha/dv/chip_sim_cfg.hjson b/hw/top_matcha/dv/chip_sim_cfg.hjson index 7d7f041..0e75e2a 100644 --- a/hw/top_matcha/dv/chip_sim_cfg.hjson +++ b/hw/top_matcha/dv/chip_sim_cfg.hjson
@@ -800,7 +800,7 @@ { name: chip_sw_i2c_device_tx_rx uvm_test_seq: chip_sw_i2c_device_tx_rx_vseq - sw_images: ["//sw/device/tests/sim_dv:i2c_device_tx_rx_test:1"] + sw_images: ["//sw/device/tests/sim_dv:i2c_device_tx_rx_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] } { @@ -2059,6 +2059,7 @@ "chip_sw_example_flash", "chip_sw_example_rom", "chip_sw_gpio", + "chip_sw_i2c_device_tx_rx", "chip_sw_i2c_host_tx_rx", "chip_sw_mem_test", "chip_sw_rom_ctrl_integrity_check", @@ -2140,6 +2141,7 @@ "chip_sw_gpio", "chip_sw_hmac_enc", "chip_sw_hmac_smoketest", + "chip_sw_i2c_device_tx_rx", "chip_sw_i2c_host_tx_rx", "chip_sw_mem_test", "chip_sw_keymgr_key_derivation", @@ -2259,6 +2261,7 @@ "chip_sw_gpio", "chip_sw_hmac_enc", "chip_sw_hmac_smoketest", + "chip_sw_i2c_device_tx_rx", "chip_sw_i2c_host_tx_rx", "chip_sw_mem_test", "chip_sw_keymgr_key_derivation",
diff --git a/hw/top_matcha/dv/env/seq_lib/chip_sw_i2c_device_tx_rx_vseq.sv b/hw/top_matcha/dv/env/seq_lib/chip_sw_i2c_device_tx_rx_vseq.sv index 9e9a796..2937ec0 100644 --- a/hw/top_matcha/dv/env/seq_lib/chip_sw_i2c_device_tx_rx_vseq.sv +++ b/hw/top_matcha/dv/env/seq_lib/chip_sw_i2c_device_tx_rx_vseq.sv
@@ -1,3 +1,4 @@ +// Copyright 2023 Google LLC. // Copyright lowRISC contributors. // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 @@ -77,6 +78,24 @@ cfg.m_i2c_agent_cfgs[i2c_idx].if_mode = Host; // Enbale appropriate interface + case(i2c_idx) + 0: begin + cfg.chip_vif.gpios_if.set_pulldown_en_pin(7, 1'b0); + cfg.chip_vif.gpios_if.set_pulldown_en_pin(8, 1'b0); + end + 1: begin + cfg.chip_vif.gpios_if.set_pulldown_en_pin(18, 1'b0); + end + 2: begin + cfg.chip_vif.gpios_if.set_pulldown_en_pin(19, 1'b0); + cfg.chip_vif.gpios_if.set_pulldown_en_pin(20, 1'b0); + end + default: begin + `uvm_error(`gfn, $sformatf("Unexpected i2c index: %0d", i2c_idx)) + end + endcase + + // Enbale appropriate interface cfg.chip_vif.enable_i2c(.inst_num(i2c_idx), .enable(1)); // tClockLow needs to be "slightly" shorter than the actual clock low period
diff --git a/sw/device/lib/testing/BUILD b/sw/device/lib/testing/BUILD index 305c3c3..813a87d 100644 --- a/sw/device/lib/testing/BUILD +++ b/sw/device/lib/testing/BUILD
@@ -81,3 +81,18 @@ "@lowrisc_opentitan//sw/device/lib/testing/test_framework:check", ], ) + +cc_library( + name = "i2c_testutils", + srcs = ["i2c_testutils.c"], + hdrs = ["@lowrisc_opentitan//sw/device/lib/testing:i2c_testutils.h"], + target_compatible_with = [OPENTITAN_CPU], + deps = [ + "//hw/top_matcha/sw/autogen:top_matcha", + "//sw/device/lib/dif:pinmux", + "@lowrisc_opentitan//hw/ip/i2c/data:i2c_regs", + "@lowrisc_opentitan//sw/device/lib/dif:i2c", + "@lowrisc_opentitan//sw/device/lib/runtime:ibex", + "@lowrisc_opentitan//sw/device/lib/testing/test_framework:check", + ], +)
diff --git a/sw/device/lib/testing/i2c_testutils.c b/sw/device/lib/testing/i2c_testutils.c new file mode 100644 index 0000000..a845463 --- /dev/null +++ b/sw/device/lib/testing/i2c_testutils.c
@@ -0,0 +1,217 @@ +// Copyright 2023 Google LLC. +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/testing/i2c_testutils.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#include "hw/top_matcha/sw/autogen/top_matcha.h" +#include "i2c_regs.h" // Generated. +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_i2c.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/test_framework/check.h" + +static const uint8_t kI2cWrite = 0; +static const uint8_t kI2cRead = 1; + +// Default flags for i2c operations. +static const dif_i2c_fmt_flags_t kDefaultFlags = {.start = false, + .stop = false, + .read = false, + .read_cont = false, + .suppress_nak_irq = false}; + +typedef struct i2c_connect_conf { + const top_matcha_pinmux_mio_out_t pins_mio_out[2]; + const top_matcha_pinmux_insel_t pins_insel[2]; + const top_matcha_pinmux_peripheral_in_t pins_peripheral_in[2]; + const top_matcha_pinmux_outsel_t pins_outsel[2]; +} i2c_connect_conf_t; + +const i2c_connect_conf_t i2c_conf[] = { + {.pins_mio_out = {kTopMatchaPinmuxMioOutIoa7, kTopMatchaPinmuxMioOutIoa8}, + .pins_insel = {kTopMatchaPinmuxInselIoa7, kTopMatchaPinmuxInselIoa8}, + .pins_peripheral_in = {kTopMatchaPinmuxPeripheralInI2c0Scl, + kTopMatchaPinmuxPeripheralInI2c0Sda}, + .pins_outsel = {kTopMatchaPinmuxOutselI2c0Scl, + kTopMatchaPinmuxOutselI2c0Sda}}, + {.pins_mio_out = {kTopMatchaPinmuxMioOutIob9, kTopMatchaPinmuxMioOutIob10}, + .pins_insel = {kTopMatchaPinmuxInselIob9, kTopMatchaPinmuxInselIob10}, + .pins_peripheral_in = {kTopMatchaPinmuxPeripheralInI2c1Scl, + kTopMatchaPinmuxPeripheralInI2c1Sda}, + .pins_outsel = {kTopMatchaPinmuxOutselI2c1Scl, + kTopMatchaPinmuxOutselI2c1Sda}}, + {.pins_mio_out = {kTopMatchaPinmuxMioOutIob11, kTopMatchaPinmuxMioOutIob12}, + .pins_insel = {kTopMatchaPinmuxInselIob11, kTopMatchaPinmuxInselIob12}, + .pins_peripheral_in = {kTopMatchaPinmuxPeripheralInI2c2Scl, + kTopMatchaPinmuxPeripheralInI2c2Sda}, + .pins_outsel = {kTopMatchaPinmuxOutselI2c2Scl, + kTopMatchaPinmuxOutselI2c2Sda}}}; + +void i2c_testutils_wr(const dif_i2c_t *i2c, uint8_t addr, uint8_t byte_count, + const uint8_t *data, bool skip_stop) { + dif_i2c_fmt_flags_t flags = kDefaultFlags; + uint8_t data_frame; + + // The current function does not support initializing a write while another + // transaction is in progress + + // TODO: The current function does not support write payloads + // larger than the fifo depth. + CHECK(byte_count <= I2C_PARAM_FIFO_DEPTH); + + // TODO: #15377 The I2C DIF says: "Callers should prefer + // `dif_i2c_write_byte()` instead, since that function provides clearer + // semantics. This function should only really be used for testing or + // troubleshooting a device. + + // First write the address. + flags.start = true; + data_frame = (addr << 1) | kI2cWrite; + CHECK_DIF_OK(dif_i2c_write_byte_raw(i2c, data_frame, flags)); + + // Once address phase is through, blast the rest as generic data. + flags = kDefaultFlags; + for (uint8_t i = 0; i < byte_count; ++i) { + // Issue a stop for the last byte. + flags.stop = ((i == byte_count - 1) && !skip_stop); + CHECK_DIF_OK(dif_i2c_write_byte_raw(i2c, data[i], flags)); + } + + // TODO: Check for errors / status. +} + +void i2c_testutils_rd(const dif_i2c_t *i2c, uint8_t addr, uint8_t byte_count) { + dif_i2c_fmt_flags_t flags = kDefaultFlags; + uint8_t data_frame; + uint8_t fifo_level; + + // The current function doesn't check for space in the FIFOs + + // First write the address. + flags.start = true; + data_frame = (addr << 1) | kI2cRead; + CHECK_DIF_OK(dif_i2c_write_byte_raw(i2c, data_frame, flags)); + + // Schedule the read transaction by writing flags to the fifo. + flags = kDefaultFlags; + flags.read = true; + flags.stop = true; + + // Inform the controller how many bytes to read overall. + CHECK_DIF_OK(dif_i2c_write_byte_raw(i2c, byte_count, flags)); + + // TODO: Check for errors / status. +} + +bool i2c_testutils_target_check_start(const dif_i2c_t *i2c, uint8_t *addr) { + uint8_t acq_fifo_lvl; + CHECK_DIF_OK(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl)); + CHECK(acq_fifo_lvl > 1); + + dif_i2c_signal_t signal; + uint8_t byte; + CHECK_DIF_OK(dif_i2c_acquire_byte(i2c, &byte, &signal)); + // Check acq_fifo is as expected and write addr and continue + CHECK(signal == kDifI2cSignalStart); + *addr = byte >> 1; + return byte & kI2cRead; +} + +bool i2c_testutils_target_check_end(const dif_i2c_t *i2c, uint8_t *cont_byte) { + uint8_t acq_fifo_lvl; + CHECK_DIF_OK(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl)); + CHECK(acq_fifo_lvl >= 1); + + dif_i2c_signal_t signal; + uint8_t byte; + CHECK_DIF_OK(dif_i2c_acquire_byte(i2c, &byte, &signal)); + // Check transaction is terminated with a stop or a continue that the caller + // is prepared to handle + if (signal == kDifI2cSignalStop) { + return false; + } + CHECK(cont_byte != NULL); + *cont_byte = byte; + CHECK(signal == kDifI2cSignalRepeat); + return true; +} + +void i2c_testutils_target_rd(const dif_i2c_t *i2c, uint8_t byte_count, + const uint8_t *data) { + uint8_t tx_fifo_lvl, acq_fifo_lvl; + CHECK_DIF_OK( + dif_i2c_get_fifo_levels(i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl)); + // Check there's space in tx_fifo and acq_fifo + CHECK(tx_fifo_lvl + byte_count <= I2C_PARAM_FIFO_DEPTH); + CHECK(acq_fifo_lvl + 2 <= I2C_PARAM_FIFO_DEPTH); + + for (uint8_t i = 0; i < byte_count; ++i) { + CHECK_DIF_OK(dif_i2c_transmit_byte(i2c, data[i])); + } + // TODO: Check for errors / status. +} + +bool i2c_testutils_target_check_rd(const dif_i2c_t *i2c, uint8_t *addr, + uint8_t *cont_byte) { + CHECK(i2c_testutils_target_check_start(i2c, addr) == kI2cRead); + // TODO: Check for errors / status. + return i2c_testutils_target_check_end(i2c, cont_byte); +} + +void i2c_testutils_target_wr(const dif_i2c_t *i2c, uint8_t byte_count) { + uint8_t acq_fifo_lvl; + CHECK_DIF_OK(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl)); + CHECK(acq_fifo_lvl + 2 + byte_count <= I2C_PARAM_FIFO_DEPTH); + + // TODO: Check for errors / status. +} + +bool i2c_testutils_target_check_wr(const dif_i2c_t *i2c, uint8_t byte_count, + uint8_t *addr, uint8_t *bytes, + uint8_t *cont_byte) { + uint8_t acq_fifo_lvl; + CHECK_DIF_OK(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl)); + CHECK(acq_fifo_lvl >= 2 + byte_count); + + CHECK(i2c_testutils_target_check_start(i2c, addr) == kI2cWrite); + + for (uint8_t i = 0; i < byte_count; ++i) { + dif_i2c_signal_t signal; + CHECK_DIF_OK(dif_i2c_acquire_byte(i2c, bytes + i, &signal)); + CHECK(signal == kDifI2cSignalNone); + } + + // TODO: Check for errors / status. + + return i2c_testutils_target_check_end(i2c, cont_byte); +} + +void i2c_testutils_connect_i2c_to_pinmux_pins(const dif_pinmux_t *pinmux, + uint8_t kI2cIdx) { + top_matcha_pinmux_mio_out_t i2c_pinmux_out1_id, i2c_pinmux_out2_id; + top_matcha_pinmux_insel_t i2c_pinmux_in1_id, i2c_pinmux_in2_id; + top_matcha_pinmux_peripheral_in_t i2c_pinmux_insel_scl_id, + i2c_pinmux_insel_sda_id; + top_matcha_pinmux_outsel_t i2c_pinmux_outsel_scl_id, i2c_pinmux_outsel_sda_id; + + CHECK_DIF_OK(dif_pinmux_input_select(pinmux, + i2c_conf[kI2cIdx].pins_peripheral_in[0], + i2c_conf[kI2cIdx].pins_insel[0])); + CHECK_DIF_OK(dif_pinmux_input_select(pinmux, + i2c_conf[kI2cIdx].pins_peripheral_in[1], + i2c_conf[kI2cIdx].pins_insel[1])); + CHECK_DIF_OK(dif_pinmux_output_select(pinmux, + i2c_conf[kI2cIdx].pins_mio_out[0], + i2c_conf[kI2cIdx].pins_outsel[0])); + CHECK_DIF_OK(dif_pinmux_output_select(pinmux, + i2c_conf[kI2cIdx].pins_mio_out[1], + i2c_conf[kI2cIdx].pins_outsel[1])); +}
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD index ee50442..b50b41d 100644 --- a/sw/device/tests/sim_dv/BUILD +++ b/sw/device/tests/sim_dv/BUILD
@@ -47,10 +47,10 @@ deps = [ "//sw/device/lib/dif:pinmux", "//sw/device/lib/dif:rstmgr", + "//sw/device/lib/testing:i2c_testutils", "//sw/device/lib/testing:isr_testutils", "//sw/device/tests:test_dv_lib", "@lowrisc_opentitan//sw/device/lib/dif:i2c", - "@lowrisc_opentitan//sw/device/lib/testing:i2c_testutils", "@lowrisc_opentitan//sw/device/lib/testing:rand_testutils", ], ) @@ -380,6 +380,22 @@ ], ) +matcha_dv_test( + name = "i2c_device_tx_rx_test", + srcs = ["i2c_device_tx_rx_test.c"], + deps = [ + "//hw/top_matcha/ip/clkmgr/data/autogen:clkmgr_regs", + "//sw/device/lib/dif:pinmux", + "//sw/device/lib/dif:rv_plic_sec", + "//sw/device/lib/testing:i2c_testutils", + "//sw/device/lib/testing:isr_testutils", + "//sw/device/tests:test_dv_lib", + "@lowrisc_opentitan//hw/ip/lc_ctrl/data:lc_ctrl_regs", + "@lowrisc_opentitan//sw/device/lib/dif:i2c", + "@lowrisc_opentitan//sw/device/lib/testing:rand_testutils", + ], +) + ################################################################################ # Place the Opentitan-sourced DV test below. # # Opentitan DV binary build flow does not support centOS7, so we pull the #
diff --git a/sw/device/tests/sim_dv/i2c_device_tx_rx_test.c b/sw/device/tests/sim_dv/i2c_device_tx_rx_test.c new file mode 100644 index 0000000..f21838a --- /dev/null +++ b/sw/device/tests/sim_dv/i2c_device_tx_rx_test.c
@@ -0,0 +1,265 @@ +// Copyright 2023 Google LLC. +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/arch/device.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_base.h" +#include "sw/device/lib/dif/dif_i2c.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/ibex.h" +#include "sw/device/lib/runtime/irq.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/autogen/isr_testutils.h" +#include "sw/device/lib/testing/i2c_testutils.h" +#include "sw/device/lib/testing/rand_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/lib/testing/test_framework/status.h" + +// TODO #14111, remove it once pinout configuration is provided +#include "i2c_regs.h" +#include "pinmux_regs.h" + +static dif_i2c_t i2c; +static dif_pinmux_t pinmux; +static dif_rv_plic_t plic; + +OTTF_DEFINE_TEST_CONFIG(); + +/** + * This symbol is meant to be backdoor loaded by the testbench. + * The testbench will inform the test the rough speed of the clock + * used by the I2C modules. + * + * The I2C Device state machine does depend on the I2C timing configuration + */ +static volatile const uint8_t kClockPeriodNanos = 0; +static volatile const uint8_t kI2cRiseFallNanos = 0; +static volatile const uint32_t kI2cClockPeriodNanos = 0; + +/** + * This symbol is meant to be backdoor loaded by the testbench. + * to indicate which I2c is actually under test. + */ +static volatile const uint8_t kI2cIdx = 0; + +/** + * This set of symbols is meant to be backdoor loaded by the testbench. + * to indicate the address that will be listened to by the device. + */ +static volatile const uint8_t kI2cDeviceAddress0 = 0x55; +static volatile const uint8_t kI2cDeviceMask0 = 0x7f; +static volatile const uint8_t kI2cDeviceAddress1 = 0x7f; // disable match on + // second address +static volatile const uint8_t kI2cDeviceMask1 = 0x7f; + +/** + * This symbol is meant to be backdoor loaded by the testbench. + * to indicate the number of bytes that should be sent. + * + * Because the test doesn't manage the FIFO during transaction, there's a limit + * to the number of bytes we can loopback in the test. I2C_PARAM_FIFO_DEPTH - 4 + */ +static volatile const uint8_t kI2cByteCount = 0; + +static volatile bool tx_empty_irq_seen = false; +static volatile bool cmd_complete_irq_seen = false; + +/** + * This constant indicates the number of interrupt requests. + */ +enum { + kNumI2cIrqs = 6, +}; + +typedef struct i2c_conf { + const int unsigned base_addr; + const uint32_t i2c_irq_fmt_threshold_id; + const top_matcha_plic_irq_id_t plic_irqs[kNumI2cIrqs]; +} i2c_conf_t; + +const i2c_conf_t i2c_configuration[] = { + {.base_addr = TOP_MATCHA_I2C0_BASE_ADDR, + .i2c_irq_fmt_threshold_id = kTopMatchaPlicIrqIdI2c0FmtThreshold, + .plic_irqs = {kTopMatchaPlicIrqIdI2c0CmdComplete, + kTopMatchaPlicIrqIdI2c0TxStretch, + kTopMatchaPlicIrqIdI2c0TxOverflow, + kTopMatchaPlicIrqIdI2c0AcqFull, + kTopMatchaPlicIrqIdI2c0UnexpStop, + kTopMatchaPlicIrqIdI2c0HostTimeout}}, + {.base_addr = TOP_MATCHA_I2C1_BASE_ADDR, + .i2c_irq_fmt_threshold_id = kTopMatchaPlicIrqIdI2c1FmtThreshold, + .plic_irqs = {kTopMatchaPlicIrqIdI2c1CmdComplete, + kTopMatchaPlicIrqIdI2c1TxStretch, + kTopMatchaPlicIrqIdI2c1TxOverflow, + kTopMatchaPlicIrqIdI2c1AcqFull, + kTopMatchaPlicIrqIdI2c1UnexpStop, + kTopMatchaPlicIrqIdI2c1HostTimeout}}, + {.base_addr = TOP_MATCHA_I2C2_BASE_ADDR, + .i2c_irq_fmt_threshold_id = kTopMatchaPlicIrqIdI2c2FmtThreshold, + .plic_irqs = { + kTopMatchaPlicIrqIdI2c2CmdComplete, kTopMatchaPlicIrqIdI2c2TxStretch, + kTopMatchaPlicIrqIdI2c2TxOverflow, kTopMatchaPlicIrqIdI2c2AcqFull, + kTopMatchaPlicIrqIdI2c2UnexpStop, + kTopMatchaPlicIrqIdI2c2HostTimeout}}}; + +/** + * Provides external irq handling for this test. + * + * This function overrides the default OTTF external ISR. + */ +void ottf_external_isr(void) { + uint32_t i2c_irq_fmt_threshold_id; + + plic_isr_ctx_t plic_ctx = {.rv_plic = &plic, + .hart_id = kTopMatchaPlicTargetIbex0}; + + i2c_isr_ctx_t i2c_ctx = { + .i2c = &i2c, + .plic_i2c_start_irq_id = + i2c_configuration[kI2cIdx].i2c_irq_fmt_threshold_id, + .expected_irq = 0, + .is_only_irq = false}; + + top_matcha_plic_peripheral_t peripheral; + dif_i2c_irq_t i2c_irq; + isr_testutils_i2c_isr(plic_ctx, i2c_ctx, &peripheral, &i2c_irq); + + switch (i2c_irq) { + case kDifI2cIrqTxStretch: + tx_empty_irq_seen = true; + i2c_irq = kDifI2cIrqTxStretch; + break; + case kDifI2cIrqCmdComplete: + cmd_complete_irq_seen = true; + i2c_irq = kDifI2cIrqCmdComplete; + break; + default: + LOG_ERROR("Unexpected interrupt (at I2C): %d", i2c_irq); + break; + } +} + +void check_addr(uint8_t addr, dif_i2c_id_t id0, dif_i2c_id_t id1) { + CHECK(((addr & id0.mask) == id0.address) || + ((addr & id1.mask) == id1.address)); +} + +bool test_main(void) { + LOG_INFO("Testing I2C index %d", kI2cIdx); + + if (kI2cByteCount > I2C_PARAM_FIFO_DEPTH - 4) { + LOG_ERROR( + "Test cannot fit %d bytes, 2 START records, and 2 STOP records in " + "buffers of depth %d", + kI2cByteCount, I2C_PARAM_FIFO_DEPTH); + } + + CHECK_DIF_OK(dif_i2c_init( + mmio_region_from_addr(i2c_configuration[kI2cIdx].base_addr), &i2c)); + + CHECK_DIF_OK(dif_pinmux_init( + mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); + + CHECK_DIF_OK(dif_rv_plic_init( + mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); + + i2c_testutils_connect_i2c_to_pinmux_pins(&pinmux, kI2cIdx); + + // Enable functional interrupts as well as error interrupts to make sure + // everything is behaving as expected. + for (uint32_t i = 0; i < kNumI2cIrqs; ++i) { + CHECK_DIF_OK(dif_rv_plic_irq_set_enabled( + &plic, i2c_configuration[kI2cIdx].plic_irqs[i], + kTopMatchaPlicTargetIbex0, kDifToggleEnabled)); + + // Assign a default priority + CHECK_DIF_OK(dif_rv_plic_irq_set_priority( + &plic, i2c_configuration[kI2cIdx].plic_irqs[i], kDifRvPlicMaxPriority)); + } + + // Enable the external IRQ at Ibex. + irq_global_ctrl(true); + irq_external_ctrl(true); + + // I2C speed parameters. + dif_i2c_timing_config_t timing_config = { + .lowest_target_device_speed = kDifI2cSpeedFastPlus, + .clock_period_nanos = kClockPeriodNanos, + .sda_rise_nanos = kI2cRiseFallNanos, + .sda_fall_nanos = kI2cRiseFallNanos, + .scl_period_nanos = kI2cClockPeriodNanos}; + + dif_i2c_config_t config; + CHECK_DIF_OK(dif_i2c_compute_timing(timing_config, &config)); + CHECK_DIF_OK(dif_i2c_configure(&i2c, config)); + dif_i2c_id_t id0 = {.mask = kI2cDeviceMask0, .address = kI2cDeviceAddress0}, + id1 = {.mask = kI2cDeviceMask1, .address = kI2cDeviceAddress1}; + CHECK_DIF_OK(dif_i2c_set_device_id(&i2c, &id0, &id1)); + CHECK_DIF_OK(dif_i2c_device_set_enabled(&i2c, kDifToggleEnabled)); + + // TODO #15081, transaction complete may not be set by i2c device. + CHECK(!cmd_complete_irq_seen); + + CHECK_DIF_OK( + dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqTxStretch, kDifToggleEnabled)); + CHECK_DIF_OK( + dif_i2c_irq_set_enabled(&i2c, kDifI2cIrqCmdComplete, kDifToggleEnabled)); + + // Randomize variables. + uint8_t expected_data[kI2cByteCount]; + LOG_INFO("Loopback %d bytes with addresses %0h, %0h", kI2cByteCount, + kI2cDeviceAddress0, kI2cDeviceAddress1); + + // Controlling the randomization from C side is a bit slow, but might be + // easier for portability to a different setup later. + for (uint32_t i = 0; i < kI2cByteCount; ++i) { + expected_data[i] = rand_testutils_gen32_range(0, 0xff); + }; + + uint8_t tx_fifo_lvl; + CHECK_DIF_OK(dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, NULL)); + IBEX_SPIN_FOR(!(tx_fifo_lvl > 0 && tx_empty_irq_seen == false), 100); + i2c_testutils_target_rd(&i2c, kI2cByteCount, expected_data); + tx_empty_irq_seen = false; + + LOG_INFO("Data written to fifo"); + + uint8_t acq_fifo_lvl; + do { + CHECK_DIF_OK( + dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl)); + } while (acq_fifo_lvl < 2); + + CHECK(tx_fifo_lvl == 0); + + uint8_t addr; + i2c_testutils_target_check_rd(&i2c, &addr, NULL); + check_addr(addr, id0, id1); + + // Read data from i2c device. + i2c_testutils_target_wr(&i2c, kI2cByteCount); + do { + CHECK_DIF_OK( + dif_i2c_get_fifo_levels(&i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl)); + } while (acq_fifo_lvl < kI2cByteCount + 2); // acquired message, address and + // junk + + uint8_t received_data[kI2cByteCount]; + i2c_testutils_target_check_wr(&i2c, kI2cByteCount, &addr, received_data, + NULL); + check_addr(addr, id0, id1); + + for (uint8_t i = 0; i < kI2cByteCount; ++i) { + CHECK(expected_data[i] == received_data[i]); + } + + CHECK(cmd_complete_irq_seen); + + return true; +}