[spi_passthru] Test flash erase opcodes
1. Add a ujson struct for communication information about uploaded
commands.
2. Test the chip_erase opcode.
Signed-off-by: Chris Frantz <cfrantz@google.com>
diff --git a/sw/device/lib/testing/json/command.h b/sw/device/lib/testing/json/command.h
index fae8627..8980089 100644
--- a/sw/device/lib/testing/json/command.h
+++ b/sw/device/lib/testing/json/command.h
@@ -17,6 +17,7 @@
value(_, PinmuxConfig) \
value(_, SpiConfigureJedecId) \
value(_, SpiReadStatus) \
+ value(_, SpiWaitForUpload) \
value(_, SpiWriteStatus) \
value(_, SpiWriteSfdp) \
value(_, SwStrapRead)
diff --git a/sw/device/lib/testing/json/spi_passthru.h b/sw/device/lib/testing/json/spi_passthru.h
index d2d5582..84159a6 100644
--- a/sw/device/lib/testing/json/spi_passthru.h
+++ b/sw/device/lib/testing/json/spi_passthru.h
@@ -26,6 +26,16 @@
field(data, uint8_t, 256)
UJSON_SERDE_STRUCT(SfdpData, sfdp_data_t, STRUCT_SFDP_DATA);
+#define STRUCT_UPLOAD_INFO(field, string) \
+ field(opcode, uint8_t) \
+ field(has_address, bool) \
+ field(addr_4b, bool) \
+ field(data_len, uint16_t) \
+ field(flash_status, uint32_t) \
+ field(address, uint32_t) \
+ field(data, uint8_t, 256)
+UJSON_SERDE_STRUCT(UploadInfo, upload_info_t, STRUCT_UPLOAD_INFO);
+
// clang-format on
#ifdef __cplusplus
}
diff --git a/sw/device/tests/spi_passthru_test.c b/sw/device/tests/spi_passthru_test.c
index 3ab0ebe..6217498 100644
--- a/sw/device/tests/spi_passthru_test.c
+++ b/sw/device/tests/spi_passthru_test.c
@@ -64,6 +64,62 @@
return RESP_OK_STATUS(uj);
}
+static status_t wait_for_upload(ujson_t *uj, dif_spi_device_handle_t *spid) {
+ // Wait for a SPI transaction cause an upload.
+ bool upload_pending;
+ do {
+ // The UploadCmdfifoNotEmpty interrupt status is updated after the SPI
+ // transaction completes.
+ TRY(dif_spi_device_irq_is_pending(
+ &spid->dev, kDifSpiDeviceIrqUploadCmdfifoNotEmpty, &upload_pending));
+ } while (!upload_pending);
+
+ upload_info_t info = {0};
+ uint8_t occupancy;
+
+ // Get the SPI opcode.
+ TRY(dif_spi_device_get_flash_command_fifo_occupancy(spid, &occupancy));
+ if (occupancy != 1) {
+ // Cannot have an uploaded command without an opcode.
+ return INTERNAL();
+ }
+ TRY(dif_spi_device_pop_flash_command_fifo(spid, &info.opcode));
+ // Get the flash_status register.
+ TRY(dif_spi_device_get_flash_status_registers(spid, &info.flash_status));
+
+ // Get the SPI address (if available).
+ TRY(dif_spi_device_get_flash_address_fifo_occupancy(spid, &occupancy));
+ if (occupancy) {
+ dif_toggle_t addr_4b;
+ TRY(dif_spi_device_get_4b_address_mode(spid, &addr_4b));
+ info.addr_4b = addr_4b;
+ TRY(dif_spi_device_pop_flash_address_fifo(spid, &info.address));
+ info.has_address = true;
+ }
+
+ // Get the SPI data payload (if available).
+ uint32_t start;
+ TRY(dif_spi_device_get_flash_payload_fifo_occupancy(spid, &info.data_len,
+ &start));
+ if (info.data_len) {
+ if (info.data_len > sizeof(info.data)) {
+ // We aren't expecting more than 256 bytes of data.
+ return INVALID_ARGUMENT();
+ }
+ TRY(dif_spi_device_read_flash_buffer(spid,
+ kDifSpiDeviceFlashBufferTypePayload,
+ start, info.data_len, info.data));
+ }
+
+ // Finished: ack the IRQ and clear the busy bit (and all other bits)
+ // in flash_status.
+ TRY(dif_spi_device_irq_acknowledge(&spid->dev,
+ kDifSpiDeviceIrqUploadCmdfifoNotEmpty));
+ TRY(dif_spi_device_set_flash_status_registers(spid, 0));
+ RESP_OK(ujson_serialize_upload_info_t, uj, &info);
+ return OK_STATUS();
+}
+
status_t command_processor(ujson_t *uj) {
while (true) {
test_command_t command;
@@ -81,6 +137,9 @@
case kTestCommandSpiWriteSfdp:
RESP_ERR(uj, write_sfdp_data(uj, &spid));
break;
+ case kTestCommandSpiWaitForUpload:
+ RESP_ERR(uj, wait_for_upload(uj, &spid));
+ break;
default:
LOG_ERROR("Unrecognized command: %d", command);
diff --git a/sw/host/opentitanlib/src/test_utils/spi_passthru.rs b/sw/host/opentitanlib/src/test_utils/spi_passthru.rs
index d30ce2c..a44814e 100644
--- a/sw/host/opentitanlib/src/test_utils/spi_passthru.rs
+++ b/sw/host/opentitanlib/src/test_utils/spi_passthru.rs
@@ -25,7 +25,7 @@
impl StatusRegister {
pub fn read(uart: &dyn Uart) -> Result<Self> {
TestCommand::SpiReadStatus.send(&*uart)?;
- StatusRegister::recv(uart, Duration::from_secs(300), false)
+ Self::recv(uart, Duration::from_secs(300), false)
}
pub fn write(&self, uart: &dyn Uart) -> Result<()> {
@@ -44,3 +44,14 @@
Ok(())
}
}
+
+impl UploadInfo {
+ pub fn execute<F>(uart: &dyn Uart, f: F) -> Result<Self>
+ where
+ F: FnOnce() -> Result<()>,
+ {
+ TestCommand::SpiWaitForUpload.send(&*uart)?;
+ f()?;
+ Self::recv(uart, Duration::from_secs(300), false)
+ }
+}
diff --git a/sw/host/tests/chip/spi_passthru/src/main.rs b/sw/host/tests/chip/spi_passthru/src/main.rs
index 186d3c6..cb552dd 100644
--- a/sw/host/tests/chip/spi_passthru/src/main.rs
+++ b/sw/host/tests/chip/spi_passthru/src/main.rs
@@ -11,11 +11,12 @@
use opentitanlib::io::spi::{Target, Transfer};
use opentitanlib::spiflash::SpiFlash;
use opentitanlib::test_utils::init::InitializeTest;
-use opentitanlib::test_utils::spi_passthru::{ConfigJedecId, SfdpData, StatusRegister};
+use opentitanlib::test_utils::spi_passthru::{ConfigJedecId, SfdpData, StatusRegister, UploadInfo};
use opentitanlib::uart::console::UartConsole;
-//const FLASH_STATUS_WIP: u32 = 0x01;
+const FLASH_STATUS_WIP: u32 = 0x01;
const FLASH_STATUS_WEL: u32 = 0x02;
+const FLASH_STATUS_STD_BITS: u32 = FLASH_STATUS_WEL | FLASH_STATUS_WIP;
#[derive(Debug, StructOpt)]
struct Opts {
@@ -175,6 +176,26 @@
Ok(())
}
+fn test_chip_erase(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
+ let uart = transport.uart("console")?;
+ let spi = transport.spi(&opts.spi)?;
+ let flash = SpiFlash::default();
+
+ let info = UploadInfo::execute(&*uart, || {
+ flash.chip_erase(&*spi)?;
+ Ok(())
+ })?;
+
+ assert_eq!(info.opcode, SpiFlash::CHIP_ERASE);
+ assert_eq!(info.has_address, false);
+ assert_eq!(info.data_len, 0);
+ assert_eq!(
+ info.flash_status & FLASH_STATUS_STD_BITS,
+ FLASH_STATUS_WEL | FLASH_STATUS_WIP
+ );
+ Ok(())
+}
+
fn main() -> Result<()> {
let opts = Opts::from_args();
opts.init.init_logging();
@@ -190,5 +211,6 @@
execute_test!(test_write_enable_disable, &opts, &transport);
execute_test!(test_read_status_extended, &opts, &transport);
execute_test!(test_read_sfdp, &opts, &transport);
+ execute_test!(test_chip_erase, &opts, &transport);
Ok(())
}