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;
+}