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