diff --git a/sw/device/lib/testing/test_framework/ottf_console.c b/sw/device/lib/testing/test_framework/ottf_console.c index 013e2446df21b..44c8ad514e135 100644 --- a/sw/device/lib/testing/test_framework/ottf_console.c +++ b/sw/device/lib/testing/test_framework/ottf_console.c @@ -296,3 +296,31 @@ status_t ottf_console_flow_control(const dif_uart_t *uart, } uint32_t ottf_console_get_flow_control_irqs(void) { return flow_control_irqs; } + +static bool spi_tx_last_data_chunk(upload_info_t *info) { + const static size_t kSpiTxTerminateMagicAddress = 0x100; + return info->address == kSpiTxTerminateMagicAddress; +} + +size_t ottf_console_spi_device_read(size_t buf_size, uint8_t *const buf) { + size_t received_data_len = 0; + upload_info_t info; + memset(&info, 0, sizeof(upload_info_t)); + while (!spi_tx_last_data_chunk(&info)) { + CHECK_STATUS_OK( + spi_device_testutils_wait_for_upload(&ottf_console_spi_device, &info)); + if (received_data_len < buf_size) { + size_t remaining_buf_size = buf_size - received_data_len; + size_t bytes_to_copy = remaining_buf_size < info.data_len + ? remaining_buf_size + : info.data_len; + memcpy(buf + received_data_len, info.data, bytes_to_copy); + } + + received_data_len += info.data_len; + CHECK_DIF_OK(dif_spi_device_set_flash_status_registers( + &ottf_console_spi_device, 0x00)); + } + + return received_data_len; +} diff --git a/sw/device/lib/testing/test_framework/ottf_console.h b/sw/device/lib/testing/test_framework/ottf_console.h index 4f785e51fb272..ba42639c75be4 100644 --- a/sw/device/lib/testing/test_framework/ottf_console.h +++ b/sw/device/lib/testing/test_framework/ottf_console.h @@ -94,4 +94,19 @@ bool ottf_console_flow_control_isr(uint32_t *exc_info); */ uint32_t ottf_console_get_flow_control_irqs(void); +/** + * Read data from the host via the OTTF SPI device console into a provided + * buffer. + * + * The function waits for spi upload commands, then transfers data in chunks + * until a transmission complete signal is received. If the size of data sent + * from the host is greater than the provided buffer, then the excess data will + * be discarded. + * + * @param buf_size The size, in bytes, of the `buf`. + * @param[out] buf A pointer to the location where the data should be stored. + * @return The number of bytes actually received from the host. + */ +size_t ottf_console_spi_device_read(size_t buf_size, uint8_t *const buf); + #endif // OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_OTTF_CONSOLE_H_ diff --git a/sw/device/tests/spi_device_ottf_console_test.c b/sw/device/tests/spi_device_ottf_console_test.c index f18bcabd7406a..4b0f857275f94 100644 --- a/sw/device/tests/spi_device_ottf_console_test.c +++ b/sw/device/tests/spi_device_ottf_console_test.c @@ -4,7 +4,9 @@ #include "sw/device/lib/dif/dif_spi_device.h" #include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/spi_device_testutils.h" #include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_console.h" #include "sw/device/lib/testing/test_framework/ottf_main.h" #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" @@ -93,22 +95,53 @@ static const char kTest4KbDataStr[] = "B30518D571FDD6D38E0477F6CB83C7729A45494F5D7805CCC1432C816B7D8CB089CEA56216" "9489E4F80E70FA685F39E1CD0AD7AD703C2E9601D442004F3D4CE043F0E84007FB7438FE82" "DF4D9304C90B48BB25762DD29D"; + +static uint8_t input_buf[5120]; + bool test_main(void) { - LOG_INFO("Sending empty string..."); + LOG_INFO("Sending empty string to Host..."); LOG_INFO(""); - LOG_INFO("Sending test string..."); + LOG_INFO("Sending test string to Host..."); LOG_INFO("%s", kTestStr); - LOG_INFO("Sending 64B data..."); + LOG_INFO("SYNC: Waiting for console data"); + size_t received_data_len = + ottf_console_spi_device_read(sizeof(input_buf), input_buf); + CHECK(received_data_len == sizeof(kTestStr)); + CHECK_ARRAYS_EQ(input_buf, kTestStr, ARRAYSIZE(kTestStr)); + + LOG_INFO("Sending 64B data to Host..."); LOG_INFO("%s", kTest64bDataStr); - LOG_INFO("Sending 256B data..."); + LOG_INFO("SYNC: Waiting for console data"); + received_data_len = + ottf_console_spi_device_read(sizeof(input_buf), input_buf); + CHECK(received_data_len == sizeof(kTest64bDataStr)); + CHECK_ARRAYS_EQ(input_buf, kTest64bDataStr, ARRAYSIZE(kTest64bDataStr)); + + LOG_INFO("Sending 256B data to Host..."); LOG_INFO("%s", kTest256bDataStr); - LOG_INFO("Sending 1KB data..."); + LOG_INFO("SYNC: Waiting for console data"); + received_data_len = + ottf_console_spi_device_read(sizeof(input_buf), input_buf); + CHECK(received_data_len == sizeof(kTest256bDataStr)); + CHECK_ARRAYS_EQ(input_buf, kTest256bDataStr, ARRAYSIZE(kTest256bDataStr)); + + LOG_INFO("Sending 1KB data to Host..."); for (int i = 1; i <= 2; i++) { LOG_INFO("Round: %d", i); LOG_INFO("%s", kTest1KbDataStr); + LOG_INFO("SYNC: Waiting for console data"); + received_data_len = + ottf_console_spi_device_read(sizeof(input_buf), input_buf); + CHECK(received_data_len == sizeof(kTest1KbDataStr)); + CHECK_ARRAYS_EQ(input_buf, kTest1KbDataStr, ARRAYSIZE(kTest1KbDataStr)); } - LOG_INFO("Sending 4KB data..."); - LOG_INFO("%s", kTest4KbDataStr); + LOG_INFO("Sending 4KB data to Host..."); + LOG_INFO("%s", kTest4KbDataStr); + LOG_INFO("SYNC: Waiting for console data"); + received_data_len = + ottf_console_spi_device_read(sizeof(input_buf), input_buf); + CHECK(received_data_len == sizeof(kTest4KbDataStr)); + CHECK_ARRAYS_EQ(input_buf, kTest4KbDataStr, ARRAYSIZE(kTest4KbDataStr)); return true; } diff --git a/sw/host/opentitanlib/src/console/spi.rs b/sw/host/opentitanlib/src/console/spi.rs index e3ef9000d1b5e..93e2c79694ac9 100644 --- a/sw/host/opentitanlib/src/console/spi.rs +++ b/sw/host/opentitanlib/src/console/spi.rs @@ -25,6 +25,8 @@ impl<'a> SpiConsoleDevice<'a> { const SPI_FLASH_READ_BUFFER_SIZE: u32 = 2048; const SPI_MAX_DATA_LENGTH: usize = 2036; const SPI_FRAME_MAGIC_NUMBER: u32 = 0xa5a5beef; + const SPI_FLASH_PAYLOAD_BUFFER_SIZE: usize = 256; + const SPI_TX_LAST_CHUNK_MAGIC_ADDRESS: u32 = 0x100; pub fn new(spi: &'a dyn Target) -> Result { let mut flash = SpiFlash { @@ -112,4 +114,27 @@ impl<'a> ConsoleDevice for SpiConsoleDevice<'a> { Ok(i) } + + fn console_write(&self, buf: &[u8]) -> Result<()> { + let buf_len: usize = buf.len(); + let mut written_data_len: usize = 0; + while written_data_len < buf_len { + let mut write_address = SpiConsoleDevice::SPI_TX_LAST_CHUNK_MAGIC_ADDRESS; + let mut data_len: usize = buf_len - written_data_len; + + if data_len > SpiConsoleDevice::SPI_FLASH_PAYLOAD_BUFFER_SIZE { + data_len = SpiConsoleDevice::SPI_FLASH_PAYLOAD_BUFFER_SIZE; + write_address = 0; + } + + self.flash.program( + self.spi, + write_address, + &buf[written_data_len..written_data_len + data_len], + )?; + written_data_len += data_len; + } + + Ok(()) + } } diff --git a/sw/host/tests/chip/spi_device_ottf_console/src/main.rs b/sw/host/tests/chip/spi_device_ottf_console/src/main.rs index 7a2c446196767..9a7dbb5c543f3 100644 --- a/sw/host/tests/chip/spi_device_ottf_console/src/main.rs +++ b/sw/host/tests/chip/spi_device_ottf_console/src/main.rs @@ -12,6 +12,7 @@ use std::time::Duration; use opentitanlib::app::TransportWrapper; use opentitanlib::console::spi::SpiConsoleDevice; use opentitanlib::execute_test; +use opentitanlib::io::console::ConsoleDevice; use opentitanlib::test_utils; use opentitanlib::test_utils::init::InitializeTest; use opentitanlib::uart::console::{ExitStatus, UartConsole}; @@ -34,6 +35,8 @@ struct Opts { firmware_elf: PathBuf, } +const SYNC_MSG: &str = r"SYNC:.*\r\n"; + fn spi_device_console_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { let mut console = UartConsole { timeout: Some(opts.timeout), @@ -54,20 +57,40 @@ fn spi_device_console_test(opts: &Opts, transport: &TransportWrapper) -> Result< let mut data = test_utils::object::symbol_data(&object, "kTestStr")?; let mut data_str = std::str::from_utf8(&data)?.trim_matches(char::from(0)); _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + log::info!("Sending test string to Device..."); + _ = UartConsole::wait_for(&spi_console_device, SYNC_MSG, opts.timeout)?; + spi_console_device.console_write(&data)?; + data = test_utils::object::symbol_data(&object, "kTest64bDataStr")?; data_str = std::str::from_utf8(&data)?.trim_matches(char::from(0)); _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + log::info!("Sending 64B data to Device..."); + _ = UartConsole::wait_for(&spi_console_device, SYNC_MSG, opts.timeout)?; + spi_console_device.console_write(&data)?; + data = test_utils::object::symbol_data(&object, "kTest256bDataStr")?; data_str = std::str::from_utf8(&data)?.trim_matches(char::from(0)); _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + log::info!("Sending 256 data to Device..."); + _ = UartConsole::wait_for(&spi_console_device, SYNC_MSG, opts.timeout)?; + spi_console_device.console_write(&data)?; + data = test_utils::object::symbol_data(&object, "kTest1KbDataStr")?; data_str = std::str::from_utf8(&data)?.trim_matches(char::from(0)); // 1KB data will be sent twice. - _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; - _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + for _round in 0..2 { + _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + log::info!("Sending 1KB data to Device..."); + _ = UartConsole::wait_for(&spi_console_device, SYNC_MSG, opts.timeout)?; + spi_console_device.console_write(&data)?; + } + data = test_utils::object::symbol_data(&object, "kTest4KbDataStr")?; data_str = std::str::from_utf8(&data)?.trim_matches(char::from(0)); _ = UartConsole::wait_for(&spi_console_device, data_str, opts.timeout)?; + log::info!("Sending 4KB data to Device..."); + _ = UartConsole::wait_for(&spi_console_device, SYNC_MSG, opts.timeout)?; + spi_console_device.console_write(&data)?; let result = console.interact(&spi_console_device, None, Some(&mut stdout))?; match result {