[top/dv] Add spi_tpm test
- The test issues writes from the host to spi device.
- Spi device then loops back data back to the host
on a read command.
Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/dv/sv/spi_agent/seq_lib/spi_host_tpm_seq.sv b/hw/dv/sv/spi_agent/seq_lib/spi_host_tpm_seq.sv
index fa3a6c0..3096a8b 100644
--- a/hw/dv/sv/spi_agent/seq_lib/spi_host_tpm_seq.sv
+++ b/hw/dv/sv/spi_agent/seq_lib/spi_host_tpm_seq.sv
@@ -48,7 +48,7 @@
foreach (address_q[i]) {
address_q[i] == local::address_q[i];
}
- payload_q.size() == 0; // no used for tpm
+ payload_q.size() == 0; // not used for tpm
data.size == local::data_q.size();
foreach (data_q[i]) {
data[i] == local::data_q[i];
diff --git a/hw/ip/spi_device/doc/_index.md b/hw/ip/spi_device/doc/_index.md
index 31c4e82..f0ae59b 100644
--- a/hw/ip/spi_device/doc/_index.md
+++ b/hw/ip/spi_device/doc/_index.md
@@ -48,7 +48,7 @@
- Shared SPI with other SPI Device functionalities. Unique CS# for the TPM
- Flash or Passthrough mode can be active with TPM mode.
Generic and TPM modes are mutually exclusive.
-- HW processed registers for the read requests
+- HW processed registers for read requests during FIFO mode
- TPM_ACCESS_x, TPM_STS_x, TPM_INTF_CAPABILITY, TPM_INT_ENABLE, TPM_INT_STATUS, TPM_INT_VECTOR, TPM_DID_VID, TPM_RID
- TPM_HASH_START returns FFh
- 5 Locality (compile-time parameter)
@@ -866,8 +866,12 @@
### TPM mode: FIFO and CRB
-The HW returns the return-by-HW registers for the read request in the TPM FIFO mode.
-In the TPM CRB mode (TPM_CFG.tpm_mode is 1), the logic always upload the command and address to the SW and waits for the read FIFO data even the received address falls into the managed address.
+The TPM protocol supports two protocol interfaces: FIFO and CRB (Command Response Buffer).
+In terms of hardware design, these two interfaces differ in how return-by-HW registers are handled.
+
+In FIFO mode, when {{< regref "TPM_CFG.tpm_mode" >}} is set to 0, HW registers reads must be returned after a maximum of 1 wait state.
+In CRB mode, when {{< regref "TPM_CFG.tpm_mode" >}} is set to 1, there are no such restrictions.
+The logic always uploads both the command and address to the SW and waits for the return data in CRB mode.
### Return-by-HW register update
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index d6e1cdb..e54849a 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -213,6 +213,19 @@
stage: V2
tests: []
}
+ {
+ name: chip_sw_spi_device_tpm
+ desc: '''Verify the basic operation of the spi tpm mode..
+
+ - The testbench sends a known payload over the chip's SPI device tpm input port.
+ - The testbench sends a read command.
+ - The software test should playback the data received in the write command as the read
+ response.
+ - The testbench should check if the written and read data match.
+ '''
+ stage: V2
+ tests: ["chip_sw_spi_device_tpm_test"]
+ }
// SPI_HOST (pre-verified IP) integration tests:
{
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index eaaa3de..2afb1c8 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -496,6 +496,12 @@
en_run_modes: ["sw_test_mode_test_rom"]
}
{
+ name: chip_sw_spi_device_tpm
+ uvm_test_seq: chip_sw_spi_device_tpm_vseq
+ sw_images: ["//sw/device/tests/sim_dv:spi_device_tpm_tx_rx_test:1"]
+ en_run_modes: ["sw_test_mode_test_rom"]
+ }
+ {
name: chip_sw_spi_host_tx_rx
uvm_test_seq: chip_sw_spi_host_tx_rx_vseq
sw_images: ["//sw/device/tests/sim_dv:spi_host_tx_rx_test:1"]
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index aaf089c..61a9911 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -105,6 +105,7 @@
- seq_lib/chip_sw_csrng_lc_hw_debug_en_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_usb_ast_clk_calib_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_i2c_host_tx_rx_vseq.sv: {is_include_file: true}
+ - seq_lib/chip_sw_spi_device_tpm_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_patt_ios_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_rv_core_ibex_lockstep_glitch_vseq.sv: {is_include_file: true}
- autogen/chip_env_pkg__params.sv: {is_include_file: true}
diff --git a/hw/top_earlgrey/dv/env/chip_if.sv b/hw/top_earlgrey/dv/env/chip_if.sv
index fc648bb..661cf98 100644
--- a/hw/top_earlgrey/dv/env/chip_if.sv
+++ b/hw/top_earlgrey/dv/env/chip_if.sv
@@ -173,14 +173,16 @@
// Functional (dedicated) interface: SPI host interface (drives traffic into the chip).
bit enable_spi_host = 1;
+ bit enable_spi_tpm = 0;
spi_if spi_host_if(.rst_n(`SPI_DEVICE_HIER.rst_ni),
.sio({ios[SpiDevD3], ios[SpiDevD2], ios[SpiDevD1], ios[SpiDevD0]}));
- assign ios[SpiDevClk] = enable_spi_host ? spi_host_if.sck : 1'bz;
- assign ios[SpiDevCsL] = enable_spi_host ? spi_host_if.csb : 1'bz;
+ assign ios[SpiDevClk] = enable_spi_host | enable_spi_tpm ? spi_host_if.sck : 1'bz;
+ assign ios[SpiDevCsL] = enable_spi_host ? spi_host_if.csb[0] : 1'bz;
+ assign ios[IoA7] = enable_spi_tpm ? spi_host_if.csb[1] : 1'bz;
initial begin
do begin
- spi_host_if.disconnect(!enable_spi_host);
- @(enable_spi_host);
+ spi_host_if.disconnect(!enable_spi_host & !enable_spi_tpm);
+ @(enable_spi_host | enable_spi_tpm);
end while (1);
end
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_device_tpm_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_device_tpm_vseq.sv
new file mode 100644
index 0000000..d237dc0
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_device_tpm_vseq.sv
@@ -0,0 +1,79 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class chip_sw_spi_device_tpm_vseq extends chip_sw_base_vseq;
+ `uvm_object_utils(chip_sw_spi_device_tpm_vseq)
+
+ `uvm_object_new
+
+ rand bit [23:0] addr;
+ rand bit [7:0] data_q[$];
+ constraint size_c { data_q.size() <= 64; }
+
+ virtual task tpm_txn (bit wr, bit [23:0] addr, bit [7:0] data_q[$] = {0},
+ int len, output logic [7:0] rdata_q[]);
+ spi_host_tpm_seq m_host_tpm_seq;
+ `uvm_create_on(m_host_tpm_seq, p_sequencer.spi_sequencer_h)
+
+ // Common attribute assignments
+ m_host_tpm_seq.write_command = wr;
+ m_host_tpm_seq.addr = addr;
+
+ // This is a write transaction
+ if (wr) begin
+ m_host_tpm_seq.data_q = data_q;
+ end else begin
+ m_host_tpm_seq.read_size = len;
+ end
+
+ `uvm_send(m_host_tpm_seq)
+ `uvm_info(`gfn, $sformatf("TPM transaction sent"), UVM_MEDIUM)
+
+ // This is a read trasnaction
+ if (!wr) begin
+ rdata_q = m_host_tpm_seq.rsp.data;
+ end
+ endtask
+
+ virtual task body();
+ logic [7:0] rdata_q[];
+ super.body();
+
+ // Enable desired modes
+ cfg.chip_vif.enable_spi_host = 0;
+ cfg.chip_vif.enable_spi_tpm = 1;
+
+ // Directly set the expected cs_id
+ cfg.m_spi_agent_cfg.csb_sel_in_cfg = 1;
+ cfg.m_spi_agent_cfg.csid = 1;
+
+ // enable spi agent interface to begin
+ `DV_WAIT(cfg.sw_logger_vif.printed_log == "Begin TPM Test",
+ "Timedout waiting for spi host c configuration.")
+
+ for (int i = 0; i < 10; i++) begin
+ `uvm_info(`gfn, $sformatf("Begin transaction %d", i), UVM_MEDIUM)
+
+ // Write transaction
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(data_q)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(addr)
+ foreach (data_q[i]) begin
+ `uvm_info(`gfn, $sformatf("Expected data: 0x%x", data_q[i]), UVM_MEDIUM)
+ end
+ tpm_txn (.wr(1), .addr(addr), .data_q(data_q), .len(data_q.size()), .rdata_q(rdata_q));
+
+ // Read transaction
+ tpm_txn (.wr(0), .addr(addr), .len(data_q.size()), .rdata_q(rdata_q));
+ foreach (rdata_q[i]) begin
+ `uvm_info(`gfn, $sformatf("Read data: 0x%x", data_q[i]), UVM_MEDIUM)
+ end
+
+ // Confirm that data is looped back
+ `DV_CHECK_Q_EQ(data_q, rdata_q);
+ `uvm_info(`gfn, $sformatf("End transaction %d", i), UVM_MEDIUM)
+ end
+ endtask
+
+
+endclass : chip_sw_spi_device_tpm_vseq
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
index 4db054b..957c638 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
@@ -68,3 +68,4 @@
`include "chip_sw_inject_scramble_seed_vseq.sv"
`include "chip_sw_exit_test_unlocked_bootstrap_vseq.sv"
`include "chip_sw_patt_ios_vseq.sv"
+`include "chip_sw_spi_device_tpm_vseq.sv"
diff --git a/sw/device/lib/dif/dif_spi_device.c b/sw/device/lib/dif/dif_spi_device.c
index e88fe4f..3b56839 100644
--- a/sw/device/lib/dif/dif_spi_device.c
+++ b/sw/device/lib/dif/dif_spi_device.c
@@ -10,7 +10,7 @@
#include "spi_device_regs.h" // Generated.
-#define DIF_SPI_DEVICE_TPM_FIFO_DEPTH 4
+#define DIF_SPI_DEVICE_TPM_FIFO_DEPTH 16
const uint16_t kDifSpiDeviceBufferLen = SPI_DEVICE_BUFFER_SIZE_BYTES;
@@ -1550,7 +1550,9 @@
if (result != kDifOk) {
return result;
}
- if (DIF_SPI_DEVICE_TPM_FIFO_DEPTH - status.read_fifo_occupancy < length) {
+ if ((DIF_SPI_DEVICE_TPM_FIFO_DEPTH - status.read_fifo_occupancy) *
+ sizeof(uint32_t) <
+ length) {
return kDifOutOfRange;
}
for (int i = 0; i < length; i += 4) {
diff --git a/sw/device/lib/dif/dif_spi_device_unittest.cc b/sw/device/lib/dif/dif_spi_device_unittest.cc
index 3b99437..bc0c54d 100644
--- a/sw/device/lib/dif/dif_spi_device_unittest.cc
+++ b/sw/device/lib/dif/dif_spi_device_unittest.cc
@@ -1792,17 +1792,17 @@
{
{SPI_DEVICE_TPM_STATUS_CMDADDR_NOTEMPTY_BIT, 0},
{SPI_DEVICE_TPM_STATUS_RDFIFO_NOTEMPTY_BIT, 1},
- {SPI_DEVICE_TPM_STATUS_RDFIFO_DEPTH_OFFSET, 3},
+ {SPI_DEVICE_TPM_STATUS_RDFIFO_DEPTH_OFFSET, 14},
{SPI_DEVICE_TPM_STATUS_WRFIFO_DEPTH_OFFSET, 4},
});
- EXPECT_EQ(dif_spi_device_tpm_write_data(&spi_, /*length=*/2, data),
+ EXPECT_EQ(dif_spi_device_tpm_write_data(&spi_, /*length=*/9, data),
kDifOutOfRange);
EXPECT_READ32(SPI_DEVICE_TPM_STATUS_REG_OFFSET,
{
{SPI_DEVICE_TPM_STATUS_CMDADDR_NOTEMPTY_BIT, 0},
- {SPI_DEVICE_TPM_STATUS_RDFIFO_NOTEMPTY_BIT, 0},
- {SPI_DEVICE_TPM_STATUS_RDFIFO_DEPTH_OFFSET, 0},
+ {SPI_DEVICE_TPM_STATUS_RDFIFO_NOTEMPTY_BIT, 1},
+ {SPI_DEVICE_TPM_STATUS_RDFIFO_DEPTH_OFFSET, 15},
{SPI_DEVICE_TPM_STATUS_WRFIFO_DEPTH_OFFSET, 4},
});
EXPECT_EQ(dif_spi_device_tpm_write_data(&spi_, /*length=*/5, data),
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD
index 8d488c2..6709378 100644
--- a/sw/device/tests/sim_dv/BUILD
+++ b/sw/device/tests/sim_dv/BUILD
@@ -724,6 +724,7 @@
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/arch:device",
"//sw/device/lib/base:mmio",
+ "//sw/device/lib/dif:base",
"//sw/device/lib/dif:rv_plic",
"//sw/device/lib/dif:spi_device",
"//sw/device/lib/runtime:hart",
@@ -735,6 +736,26 @@
)
opentitan_functest(
+ name = "spi_device_tpm_tx_rx_test",
+ srcs = ["spi_device_tpm_tx_rx_test.c"],
+ targets = ["dv"],
+ deps = [
+ "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+ "//sw/device/lib/arch:device",
+ "//sw/device/lib/base:mmio",
+ "//sw/device/lib/dif:pinmux",
+ "//sw/device/lib/dif:rv_plic",
+ "//sw/device/lib/dif:spi_device",
+ "//sw/device/lib/runtime:hart",
+ "//sw/device/lib/runtime:irq",
+ "//sw/device/lib/runtime:log",
+ "//sw/device/lib/testing:isr_testutils",
+ "//sw/device/lib/testing/test_framework:ottf_main",
+ "//sw/device/lib/testing/test_framework:status",
+ ],
+)
+
+opentitan_functest(
name = "spi_host_tx_rx_test",
srcs = ["spi_host_tx_rx_test.c"],
targets = ["dv"],
diff --git a/sw/device/tests/sim_dv/spi_device_tpm_tx_rx_test.c b/sw/device/tests/sim_dv/spi_device_tpm_tx_rx_test.c
new file mode 100644
index 0000000..0d93a42
--- /dev/null
+++ b/sw/device/tests/sim_dv/spi_device_tpm_tx_rx_test.c
@@ -0,0 +1,224 @@
+// 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/arch/device.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_base.h"
+#include "sw/device/lib/dif/dif_pinmux.h"
+#include "sw/device/lib/dif/dif_rv_plic.h"
+#include "sw/device/lib/dif/dif_spi_device.h"
+#include "sw/device/lib/runtime/hart.h"
+#include "sw/device/lib/runtime/irq.h"
+#include "sw/device/lib/runtime/log.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"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "sw/device/lib/testing/autogen/isr_testutils.h"
+
+OTTF_DEFINE_TEST_CONFIG();
+
+static dif_spi_device_handle_t spi_device;
+static dif_pinmux_t pinmux;
+static dif_rv_plic_t plic;
+
+// Enum for TPM command
+typedef enum {
+ kTpmWriteCommand = 0x0,
+ kTpmReadCommand = 0x80,
+} tpm_cmd_t;
+
+const static uint8_t kIterations = 10;
+const static uint8_t kTpmCommandRwMask = 0x80;
+const static uint8_t kTpmCommandSizeMask = 0x3f;
+
+const static dif_spi_device_tpm_config_t tpm_config = {
+ .interface = kDifSpiDeviceTpmInterfaceCrb,
+ .disable_return_by_hardware = false,
+ .disable_address_prefix_check = false,
+ .disable_locality_check = false};
+
+static volatile bool header_interrupt_received = false;
+
+static void en_plic_irqs(dif_rv_plic_t *plic) {
+ // Enable functional interrupts as well as error interrupts to make sure
+ // everything is behaving as expected.
+ top_earlgrey_plic_irq_id_t plic_irqs[] = {
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericRxFull,
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericRxWatermark,
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericTxWatermark,
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericRxError,
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericRxOverflow,
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericTxUnderflow,
+ kTopEarlgreyPlicIrqIdSpiDeviceUploadCmdfifoNotEmpty,
+ kTopEarlgreyPlicIrqIdSpiDeviceUploadPayloadNotEmpty,
+ kTopEarlgreyPlicIrqIdSpiDeviceUploadPayloadOverflow,
+ kTopEarlgreyPlicIrqIdSpiDeviceReadbufWatermark,
+ kTopEarlgreyPlicIrqIdSpiDeviceReadbufFlip,
+ kTopEarlgreyPlicIrqIdSpiDeviceTpmHeaderNotEmpty};
+
+ for (uint32_t i = 0; i < ARRAYSIZE(plic_irqs); ++i) {
+ CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
+ plic, plic_irqs[i], kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled));
+
+ // Assign a default priority
+ CHECK_DIF_OK(dif_rv_plic_irq_set_priority(plic, plic_irqs[i],
+ kDifRvPlicMaxPriority));
+ }
+
+ // Enable the external IRQ at Ibex.
+ irq_global_ctrl(true);
+ irq_external_ctrl(true);
+}
+
+static void en_spi_device_irqs(dif_spi_device_t *spi_device) {
+ dif_spi_device_irq_t spi_device_irqs[] = {
+ kDifSpiDeviceIrqGenericRxFull,
+ kDifSpiDeviceIrqGenericRxWatermark,
+ kDifSpiDeviceIrqGenericTxWatermark,
+ kDifSpiDeviceIrqGenericRxError,
+ kDifSpiDeviceIrqGenericRxOverflow,
+ kDifSpiDeviceIrqGenericTxUnderflow,
+ kDifSpiDeviceIrqUploadCmdfifoNotEmpty,
+ kDifSpiDeviceIrqUploadPayloadNotEmpty,
+ kDifSpiDeviceIrqUploadPayloadOverflow,
+ kDifSpiDeviceIrqReadbufWatermark,
+ kDifSpiDeviceIrqReadbufFlip,
+ kDifSpiDeviceIrqTpmHeaderNotEmpty};
+
+ for (uint32_t i = 0; i <= ARRAYSIZE(spi_device_irqs); ++i) {
+ CHECK_DIF_OK(dif_spi_device_irq_set_enabled(spi_device, spi_device_irqs[i],
+ kDifToggleEnabled));
+ }
+}
+
+void ottf_external_isr(void) {
+ plic_isr_ctx_t plic_ctx = {.rv_plic = &plic,
+ .hart_id = kTopEarlgreyPlicTargetIbex0};
+
+ // We should only be receiving the tpm header interrupt during this test.
+ spi_device_isr_ctx_t spi_device_ctx = {
+ .spi_device = &spi_device.dev,
+ .plic_spi_device_start_irq_id =
+ kTopEarlgreyPlicIrqIdSpiDeviceGenericRxFull,
+ .expected_irq = kDifSpiDeviceIrqTpmHeaderNotEmpty,
+ .is_only_irq = true};
+
+ top_earlgrey_plic_peripheral_t peripheral;
+ dif_spi_device_irq_t spi_device_irq;
+ isr_testutils_spi_device_isr(plic_ctx, spi_device_ctx, &peripheral,
+ &spi_device_irq);
+
+ switch (spi_device_irq) {
+ case kDifSpiDeviceIrqTpmHeaderNotEmpty:
+ header_interrupt_received = true;
+ // Disable interrupt until work is handled.
+ CHECK_DIF_OK(dif_spi_device_irq_set_enabled(
+ &spi_device.dev, kDifSpiDeviceIrqTpmHeaderNotEmpty,
+ kDifToggleDisabled));
+ break;
+ default:
+ LOG_ERROR("Unexpected interrupt: %d", spi_device_irq);
+ break;
+ }
+}
+
+static void ack_spi_tpm_header_irq(dif_spi_device_handle_t *spi_device) {
+ // Clear interrupt state and re-enable interrupt.
+ header_interrupt_received = false;
+ CHECK_DIF_OK(dif_spi_device_irq_acknowledge(
+ &spi_device->dev, kDifSpiDeviceIrqTpmHeaderNotEmpty));
+ CHECK_DIF_OK(dif_spi_device_irq_set_enabled(
+ &spi_device->dev, kDifSpiDeviceIrqTpmHeaderNotEmpty, kDifToggleEnabled));
+}
+
+// This routine is needed to make sure that an interrupt does not sneak in
+// and jump excution away between the boolean check and the actual invocation
+// of wait_for_interrupt.
+static void atomic_wait_for_interrupt() {
+ irq_global_ctrl(false);
+ if (!header_interrupt_received) {
+ wait_for_interrupt();
+ }
+ irq_global_ctrl(true);
+}
+
+bool test_main(void) {
+ CHECK_DIF_OK(dif_pinmux_init(
+ mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
+
+ CHECK_DIF_OK(dif_spi_device_init_handle(
+ mmio_region_from_addr(TOP_EARLGREY_SPI_DEVICE_BASE_ADDR), &spi_device));
+
+ CHECK_DIF_OK(dif_rv_plic_init(
+ mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
+
+ // Set IoA7 for tpm csb.
+ // Longer term this needs to migrate to a top specific, platform specific
+ // setting.
+ CHECK_DIF_OK(dif_pinmux_input_select(
+ &pinmux, kTopEarlgreyPinmuxPeripheralInSpiDeviceTpmCsb,
+ kTopEarlgreyPinmuxInselIoa7));
+
+ CHECK_DIF_OK(
+ dif_spi_device_tpm_configure(&spi_device, kDifToggleEnabled, tpm_config));
+
+ // enable interrupts
+ en_plic_irqs(&plic);
+ en_spi_device_irqs(&spi_device.dev);
+
+ // Sync message with testbench to begin.
+ LOG_INFO("Begin TPM Test");
+
+ for (uint32_t i = 0; i < kIterations; i++) {
+ LOG_INFO("Iteration %d", i);
+
+ // Wait for write interrupt.
+ atomic_wait_for_interrupt();
+
+ // Check what comamnd we have received. Store it as expected variables
+ // and compare when the read command is issued.
+ uint8_t write_command;
+ uint32_t write_addr;
+ CHECK_DIF_OK(dif_spi_device_tpm_get_command(&spi_device, &write_command,
+ &write_addr));
+ CHECK((write_command & kTpmCommandRwMask) == kTpmWriteCommand,
+ "Expected write command, received read");
+
+ // Poll for write data to complete.
+ uint32_t num_bytes = (write_command & kTpmCommandSizeMask) + 1;
+ LOG_INFO("Expecting %d bytes from tpm write", num_bytes);
+
+ uint8_t buf[64];
+ dif_result_t status = kDifOutOfRange;
+ while (status == kDifOutOfRange) {
+ status = dif_spi_device_tpm_read_data(&spi_device, num_bytes, buf);
+ };
+ CHECK_DIF_OK(status);
+
+ // Finished processing the write command
+ ack_spi_tpm_header_irq(&spi_device);
+
+ // Wait for read interrupt.
+ atomic_wait_for_interrupt();
+ // Send the written data right back out for reads.
+ CHECK_DIF_OK(dif_spi_device_tpm_write_data(&spi_device, num_bytes, buf));
+
+ uint8_t read_command;
+ uint32_t read_addr;
+ CHECK_DIF_OK(
+ dif_spi_device_tpm_get_command(&spi_device, &read_command, &read_addr));
+ ack_spi_tpm_header_irq(&spi_device);
+
+ // Make sure the received command matches expectation
+ LOG_INFO("Expected 0x%x, received 0x%x",
+ (kTpmReadCommand | (num_bytes - 1)), read_command);
+ CHECK((kTpmReadCommand | (num_bytes - 1)) == read_command,
+ "Expected read command, received write");
+ CHECK(write_addr == read_addr, "Received address did not match");
+ }
+
+ return true;
+};