[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(()) }