[spi_passthru] Test SFDP 1. Program a known pattern into the spi_device SFDP RAM. Read back and verify the pattern. 2. Enhance the opentitantool `spi sdfp` command to include reading at non-zero offsets. 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 c34ecb3..fae8627 100644 --- a/sw/device/lib/testing/json/command.h +++ b/sw/device/lib/testing/json/command.h
@@ -18,6 +18,7 @@ value(_, SpiConfigureJedecId) \ value(_, SpiReadStatus) \ value(_, SpiWriteStatus) \ + value(_, SpiWriteSfdp) \ value(_, SwStrapRead) UJSON_SERDE_ENUM(TestCommand, test_command_t, ENUM_TEST_COMMAND);
diff --git a/sw/device/lib/testing/json/spi_passthru.h b/sw/device/lib/testing/json/spi_passthru.h index 9ebe145..d2d5582 100644 --- a/sw/device/lib/testing/json/spi_passthru.h +++ b/sw/device/lib/testing/json/spi_passthru.h
@@ -22,6 +22,10 @@ field(addr_4b, bool) UJSON_SERDE_STRUCT(StatusRegister, status_register_t, STRUCT_STATUS_REGISTER); +#define STRUCT_SFDP_DATA(field, string) \ + field(data, uint8_t, 256) +UJSON_SERDE_STRUCT(SfdpData, sfdp_data_t, STRUCT_SFDP_DATA); + // clang-format on #ifdef __cplusplus }
diff --git a/sw/device/tests/spi_passthru_test.c b/sw/device/tests/spi_passthru_test.c index 0d4d068..3ab0ebe 100644 --- a/sw/device/tests/spi_passthru_test.c +++ b/sw/device/tests/spi_passthru_test.c
@@ -56,6 +56,14 @@ return OK_STATUS(); } +static status_t write_sfdp_data(ujson_t *uj, dif_spi_device_handle_t *spid) { + sfdp_data_t sfdp; + TRY(ujson_deserialize_sfdp_data_t(uj, &sfdp)); + TRY(dif_spi_device_write_flash_buffer(spid, kDifSpiDeviceFlashBufferTypeSfdp, + 0, sizeof(sfdp.data), sfdp.data)); + return RESP_OK_STATUS(uj); +} + status_t command_processor(ujson_t *uj) { while (true) { test_command_t command; @@ -70,6 +78,10 @@ case kTestCommandSpiWriteStatus: RESP_ERR(uj, write_status_register(uj, &spid)); break; + case kTestCommandSpiWriteSfdp: + RESP_ERR(uj, write_sfdp_data(uj, &spid)); + break; + default: LOG_ERROR("Unrecognized command: %d", command); RESP_ERR(uj, INVALID_ARGUMENT());
diff --git a/sw/host/opentitanlib/src/test_utils/spi_passthru.rs b/sw/host/opentitanlib/src/test_utils/spi_passthru.rs index f64af10..d30ce2c 100644 --- a/sw/host/opentitanlib/src/test_utils/spi_passthru.rs +++ b/sw/host/opentitanlib/src/test_utils/spi_passthru.rs
@@ -35,3 +35,12 @@ Ok(()) } } + +impl SfdpData { + pub fn write(&self, uart: &dyn Uart) -> Result<()> { + TestCommand::SpiWriteSfdp.send(&*uart)?; + self.send(uart)?; + Status::recv(uart, Duration::from_secs(300), false)?; + Ok(()) + } +}
diff --git a/sw/host/opentitantool/src/command/spi.rs b/sw/host/opentitantool/src/command/spi.rs index 16ee979..97d8734 100644 --- a/sw/host/opentitantool/src/command/spi.rs +++ b/sw/host/opentitantool/src/command/spi.rs
@@ -27,6 +27,13 @@ help = "Display raw SFDP bytes rather than the parsed struct." )] raw: Option<usize>, + + #[structopt( + short, + long, + help = "Start reading SFDP at offset. Only valid with --raw." + )] + offset: Option<u32>, } // Print a hexdump of a buffer to `writer`. @@ -70,9 +77,16 @@ let spi = context.params.create(transport, "BOOTSTRAP")?; if let Some(length) = self.raw { + let offset = self.offset.unwrap_or(0); let mut buffer = vec![0u8; length]; spi.run_transaction(&mut [ - Transfer::Write(&[SpiFlash::READ_SFDP, 0, 0, 0, 0]), + Transfer::Write(&[ + SpiFlash::READ_SFDP, + (offset >> 16) as u8, + (offset >> 8) as u8, + (offset >> 0) as u8, + 0, // Dummy byte. + ]), Transfer::Read(&mut buffer), ])?; hexdump(io::stdout(), &buffer)?;
diff --git a/sw/host/tests/chip/spi_passthru/src/main.rs b/sw/host/tests/chip/spi_passthru/src/main.rs index bfa2408..186d3c6 100644 --- a/sw/host/tests/chip/spi_passthru/src/main.rs +++ b/sw/host/tests/chip/spi_passthru/src/main.rs
@@ -8,10 +8,10 @@ use opentitanlib::app::TransportWrapper; use opentitanlib::execute_test; -use opentitanlib::io::spi::Transfer; +use opentitanlib::io::spi::{Target, Transfer}; use opentitanlib::spiflash::SpiFlash; use opentitanlib::test_utils::init::InitializeTest; -use opentitanlib::test_utils::spi_passthru::{ConfigJedecId, StatusRegister}; +use opentitanlib::test_utils::spi_passthru::{ConfigJedecId, SfdpData, StatusRegister}; use opentitanlib::uart::console::UartConsole; //const FLASH_STATUS_WIP: u32 = 0x01; @@ -134,6 +134,47 @@ Ok(()) } +fn read_sfdp(spi: &dyn Target, offset: u32) -> Result<Vec<u8>> { + let mut buf = vec![0u8; 256]; + spi.run_transaction(&mut [ + // READ_SFDP always takes a 3-byte address followed by a dummy + // byte regardless of address mode. + Transfer::Write(&[ + SpiFlash::READ_SFDP, + (offset >> 16) as u8, + (offset >> 8) as u8, + (offset >> 0) as u8, + 0, // Dummy byte. + ]), + Transfer::Read(&mut buf), + ])?; + Ok(buf) +} + +fn test_read_sfdp(opts: &Opts, transport: &TransportWrapper) -> Result<()> { + let uart = transport.uart("console")?; + let spi = transport.spi(&opts.spi)?; + + let sfdp = SfdpData { + data: (0..256).map(|x| x as u8).collect(), + }; + sfdp.write(&*uart)?; + + // Read and compare the whole SFDP buffer. + let buf = read_sfdp(&*spi, 0)?; + assert_eq!(buf, sfdp.data.as_slice()); + + // Test a read that would go beyond the length of the SFDP data. + // The observed behavior should be that the buffer recieved from + // the device should wrap around. + let buf = read_sfdp(&*spi, 0x30)?; + let data = sfdp.data.as_slice(); + assert_eq!(buf[0x00..0xd0], data[0x30..0x100]); + assert_eq!(buf[0xd0..0x100], data[0x00..0x30]); + + Ok(()) +} + fn main() -> Result<()> { let opts = Opts::from_args(); opts.init.init_logging(); @@ -148,5 +189,6 @@ execute_test!(test_enter_exit_4b_mode, &opts, &transport); execute_test!(test_write_enable_disable, &opts, &transport); execute_test!(test_read_status_extended, &opts, &transport); + execute_test!(test_read_sfdp, &opts, &transport); Ok(()) }