Merge "Run software tests from raw otp"
diff --git a/hw/top_matcha/dv/chip_sim_cfg.hjson b/hw/top_matcha/dv/chip_sim_cfg.hjson
index 6fcc122..0a049d6 100644
--- a/hw/top_matcha/dv/chip_sim_cfg.hjson
+++ b/hw/top_matcha/dv/chip_sim_cfg.hjson
@@ -807,7 +807,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"]
}
{
@@ -2066,6 +2066,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",
@@ -2147,6 +2148,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",
@@ -2267,6 +2269,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/chip_common_pkg.sv b/hw/top_matcha/dv/env/chip_common_pkg.sv
index 33c3192..a7b9be1 100644
--- a/hw/top_matcha/dv/env/chip_common_pkg.sv
+++ b/hw/top_matcha/dv/env/chip_common_pkg.sv
@@ -18,11 +18,10 @@
// Buffer is half of SPI_DEVICE Dual Port SRAM
parameter dv_utils_pkg::uint SPI_FRAME_BYTE_SIZE = spi_device_reg_pkg::SPI_DEVICE_BUFFER_SIZE/2;
- // SW constants - use unmapped address space with at least 32 bytes.
- parameter bit [top_pkg::TL_AW-1:0] SW_DV_START_ADDR = tl_main_pkg::ADDR_SPACE_RV_CORE_IBEX_SEC__CFG +
- rv_core_ibex_reg_pkg::RV_CORE_IBEX_DV_SIM_WINDOW_OFFSET;
- parameter bit [top_pkg::TL_AW-1:0] SW_DV_SMC_START_ADDR = tl_smc_pkg::ADDR_SPACE_RV_CORE_IBEX_SMC__CFG +
- rv_core_ibex_reg_pkg::RV_CORE_IBEX_DV_SIM_WINDOW_OFFSET;
+ // SW constants - map Matcha dv addresses to an memory space which could be
+ // accessed by both secure and SMC cores.
+ parameter bit [top_pkg::TL_AW-1:0] SW_DV_START_ADDR = 32'h503f_fff8;
+ parameter bit [top_pkg::TL_AW-1:0] SW_DV_SMC_START_ADDR = 32'h503f_fff0;
parameter bit [top_pkg::TL_AW-1:0] SW_DV_TEST_STATUS_ADDR = SW_DV_START_ADDR + 0;
parameter bit [top_pkg::TL_AW-1:0] SW_DV_LOG_ADDR = SW_DV_START_ADDR + 4;
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/hw/top_matcha/dv/tb/tb.sv b/hw/top_matcha/dv/tb/tb.sv
index 7d61ddb..4403819 100644
--- a/hw/top_matcha/dv/tb/tb.sv
+++ b/hw/top_matcha/dv/tb/tb.sv
@@ -163,7 +163,7 @@
sim_sram u_sim_sram (
.clk_i (sel_sim_sram ? `CPU_HIER.clk_i : 1'b0),
.rst_ni (`CPU_HIER.rst_ni),
- .tl_in_i (tlul_pkg::tl_h2d_t'(`CPU_HIER.u_tlul_req_buf.out_o)),
+ .tl_in_i (tlul_pkg::tl_h2d_t'(`CPU_HIER.fifo_d.tl_h_i)),
.tl_in_o (),
.tl_out_o (),
.tl_out_i ()
@@ -173,7 +173,6 @@
void'($value$plusargs("en_sim_sram=%0b", en_sim_sram));
if (!dut.chip_if.stub_cpu && en_sim_sram) begin
`SIM_SRAM_IF.start_addr = SW_DV_START_ADDR;
- force `CPU_HIER.u_tlul_rsp_buf.in_i = u_sim_sram.tl_in_o;
end
end
@@ -206,7 +205,7 @@
sim_sram u_sim_smc_sram (
.clk_i (sel_sim_smc_sram ? `SMC_CPU_HIER.clk_i : 1'b0),
.rst_ni (`SMC_CPU_HIER.rst_ni),
- .tl_in_i (tlul_pkg::tl_h2d_t'(`SMC_CPU_HIER.u_tlul_req_buf.out_o)),
+ .tl_in_i (tlul_pkg::tl_h2d_t'(`SMC_CPU_HIER.fifo_d.tl_h_i)),
.tl_in_o (),
.tl_out_o (),
.tl_out_i ()
@@ -216,7 +215,6 @@
void'($value$plusargs("en_sim_smc_sram=%0b", en_sim_smc_sram));
if (!stub_smc && en_sim_smc_sram) begin
`SIM_SMC_SRAM_IF.start_addr = SW_DV_SMC_START_ADDR;
- force `SMC_CPU_HIER.u_tlul_rsp_buf.in_i = u_sim_smc_sram.tl_in_o;
end
end
diff --git a/rules/matcha.bzl b/rules/matcha.bzl
index 9d87d2c..ac14bf5 100644
--- a/rules/matcha.bzl
+++ b/rules/matcha.bzl
@@ -39,7 +39,7 @@
}
DV_CORE_TARGETS = {
- "secure_core": "@lowrisc_opentitan//sw/device/lib/arch:sim_dv",
+ "secure_core": "//sw/device/lib/arch:sim_dv",
"smc": "//sw/device/lib/arch:smc_sim_dv",
}
diff --git a/sw/device/lib/arch/BUILD b/sw/device/lib/arch/BUILD
index b0bc154..9717273 100644
--- a/sw/device/lib/arch/BUILD
+++ b/sw/device/lib/arch/BUILD
@@ -11,6 +11,14 @@
)
cc_library(
+ name = "sim_dv",
+ srcs = ["device_sim_dv.c"],
+ deps = [
+ ":device",
+ ],
+)
+
+cc_library(
name = "smc_sim_dv",
srcs = ["device_smc_sim_dv.c"],
deps = [
diff --git a/sw/device/lib/arch/device_sim_dv.c b/sw/device/lib/arch/device_sim_dv.c
new file mode 100644
index 0000000..3ab7c7d
--- /dev/null
+++ b/sw/device/lib/arch/device_sim_dv.c
@@ -0,0 +1,51 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdbool.h>
+
+#include "sw/device/lib/arch/device.h"
+
+/**
+ * Device-specific symbol definitions for the Verilator device.
+ */
+
+const device_type_t kDeviceType = kDeviceSimDV;
+
+// TODO: DV testbench completely randomizes these. Need to add code to
+// retrieve these from a preloaded memory location set by the testbench.
+
+const uint64_t kClockFreqCpuMhz = 100;
+
+const uint64_t kClockFreqCpuHz = kClockFreqCpuMhz * 1000 * 1000;
+
+uint64_t to_cpu_cycles(uint64_t usec) { return usec * kClockFreqCpuMhz; }
+
+const uint64_t kClockFreqHiSpeedPeripheralHz = 96 * 1000 * 1000; // 96MHz
+
+const uint64_t kClockFreqPeripheralHz = 24 * 1000 * 1000; // 24MHz
+
+const uint64_t kClockFreqUsbHz = 48 * 1000 * 1000; // 48MHz
+
+const uint64_t kClockFreqAonHz = 200 * 1000; // 200kHz
+
+const uint64_t kUartBaudrate = 1 * 1000 * 1000; // 1Mbps
+
+const uint32_t kUartNCOValue =
+ CALCULATE_UART_NCO(kUartBaudrate, kClockFreqPeripheralHz);
+
+const uint32_t kUartTxFifoCpuCycles =
+ CALCULATE_UART_TX_FIFO_CPU_CYCLES(kUartBaudrate, kClockFreqCpuHz);
+
+const uint32_t kAstCheckPollCpuCycles =
+ CALCULATE_AST_CHECK_POLL_CPU_CYCLES(kClockFreqCpuHz);
+
+// Defined in `hw/top_earlgrey/dv/env/chip_env_pkg.sv`
+const uintptr_t kDeviceTestStatusAddress = 0x503ffff8;
+
+// Defined in `hw/top_earlgrey/dv/env/chip_env_pkg.sv`
+const uintptr_t kDeviceLogBypassUartAddress = 0x503ffffc;
+
+const bool kJitterEnabled = false;
+
+void device_fpga_version_print(void) {}
diff --git a/sw/device/lib/arch/device_smc_sim_dv.c b/sw/device/lib/arch/device_smc_sim_dv.c
index ef7c4ac..a216caf 100644
--- a/sw/device/lib/arch/device_smc_sim_dv.c
+++ b/sw/device/lib/arch/device_smc_sim_dv.c
@@ -40,9 +40,9 @@
const uint32_t kAstCheckPollCpuCycles =
CALCULATE_AST_CHECK_POLL_CPU_CYCLES(kClockFreqCpuHz);
-const uintptr_t kDeviceTestStatusAddress = 0x54030080;
+const uintptr_t kDeviceTestStatusAddress = 0x503ffff0;
-const uintptr_t kDeviceLogBypassUartAddress = 0x54030084;
+const uintptr_t kDeviceLogBypassUartAddress = 0x503ffff4;
const bool kJitterEnabled = false;
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/lib/testing/test_framework/ottf_smc.ld b/sw/device/lib/testing/test_framework/ottf_smc.ld
index 067f8dd..ed83c13 100644
--- a/sw/device/lib/testing/test_framework/ottf_smc.ld
+++ b/sw/device/lib/testing/test_framework/ottf_smc.ld
@@ -17,10 +17,17 @@
}
/**
+ * Reserving space at the top of the RAM for DV verification.
+ */
+_dv_sim_window_size = 0x10;
+_dv_sim_window_end = ORIGIN(ram_smc) + LENGTH(ram_smc);
+_dv_sim_window_start = _dv_sim_window_end - _dv_sim_window_size;
+
+/**
* Reserving space at the top of the RAM for the stack.
*/
_stack_size = 0x2000;
-_stack_end = ORIGIN(ram_smc) + LENGTH(ram_smc);
+_stack_end = _dv_sim_window_start;
_stack_start = _stack_end - _stack_size;
/**
@@ -231,5 +238,13 @@
__super_virtual_end = .;
} > super_virtual
+ .stack ORIGIN(ram_smc) + LENGTH(ram_smc) - _stack_size - _dv_sim_window_size (NOLOAD) : ALIGN(4) {
+ __stack_start__ = .;
+ . += _stack_size;
+ . = ALIGN(4);
+ __stack_end__ = .;
+ } > ram_smc
+
+
INCLUDE external/lowrisc_opentitan/sw/device/info_sections.ld
}
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;
+}