Skip to content

Commit

Permalink
[ottf,base] implement new spi_device console
Browse files Browse the repository at this point in the history
This implements a new spi_device RX console by using the SPI device
flash mode feature. The flash read buffer is used as a circular buffer.

Reuses framing protocol from #17494 with minor modifications to
distinguish fresh data from stale values in the read buffer.

Signed-off-by: Anthony Chen <[email protected]>
  • Loading branch information
anthonychen1251 committed Aug 16, 2024
1 parent 07b417e commit d0462e1
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 11 deletions.
1 change: 1 addition & 0 deletions sw/device/lib/runtime/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ cc_library(
"//sw/device/lib/base:macros",
"//sw/device/lib/base:memory",
"//sw/device/lib/base:status",
"//sw/device/lib/dif:spi_device",
"//sw/device/lib/dif:uart",
],
)
Expand Down
136 changes: 136 additions & 0 deletions sw/device/lib/runtime/print.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/status.h"
#include "sw/device/lib/dif/dif_spi_device.h"
#include "sw/device/lib/dif/dif_uart.h"

#include "spi_device_regs.h" // Generated.

// This is declared as an enum to force the values to be
// compile-time constants, but the type is otherwise not
// used for anything.
Expand Down Expand Up @@ -68,6 +71,132 @@ void base_set_stdout(buffer_sink_t out) {
base_stdout = out;
}

static const size_t kSpiDeviceReadBufferSize =
SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH * sizeof(uint32_t);
static const size_t kSpiDeviceFrameHeaderSizeBytes = 12;
static const uint32_t kSpiDeviceFrameMagicNumber = 0xa5a5beef;
static uint32_t spi_device_frame_num = 0;

/**
* Sends data out of the SPI device.
*
* Data is packaged into a frame that is described below.
* The host side reads the header first, then decides how many words
* to read from the data section.
*
* -----------------------------------------------
* | Magic Number | 4-bytes | |
* -----------------------------------| |
* | Frame Number | 4-bytes | Header |
* -----------------------------------| |
* | Data Length (bytes) | 4-bytes | |
* -----------------------------------|----------|
* | Data (word aligned) | |
* -----------------------------------| Data |
* | 0xFF Pad Bytes | <4-bytes | |
* -----------------------------------|----------|
*/
static size_t base_dev_spi_device(void *data, const char *buf, size_t len) {
dif_spi_device_handle_t *spi_device = (dif_spi_device_handle_t *)data;
const size_t kDataPacketSizeBytes = ((len + 3u) & ~3u);
const size_t kFrameSizeBytes =
kSpiDeviceFrameHeaderSizeBytes + kDataPacketSizeBytes;
uint8_t frame_bytes[kFrameSizeBytes];

static uint32_t next_write_address = 0;

if (kFrameSizeBytes >=
(kSpiDeviceReadBufferSize - kSpiDeviceFrameHeaderSizeBytes)) {
return 0;
}

// Add the magic bytes.
for (size_t i = 0; i < 4; ++i) {
frame_bytes[i] = (kSpiDeviceFrameMagicNumber >> (i * 8)) & 0xff;
}
// Add the frame number.
for (size_t i = 0; i < 4; ++i) {
frame_bytes[i + 4] = (spi_device_frame_num >> (i * 8)) & 0xff;
}
// Add the data length.
for (size_t i = 0; i < 4; ++i) {
frame_bytes[i + 8] = (len >> (i * 8)) & 0xff;
}

// Construct the frame data packet.
// Add the data and pad bytes.
for (size_t i = 0; i < ((len + 3u) & ~3u); ++i) {
if (i < len) {
frame_bytes[i + 12] = buf[i];
} else {
frame_bytes[i + 12] = 0xff;
}
}

uint32_t available_buffer_size = 0;
do {
uint32_t last_read_address = 0;
if (dif_spi_device_get_last_read_address(spi_device, &last_read_address) !=
kDifOk) {
return 0;
}

// Adjust the last read address. The host continuously reads from the read
// buffer, unaware of whether a new frame has arrived. This could result in
// the reported last_read_address being the header size ahead of the actual
// address of the last valid frame if all the frames in the read buffer has
// been consumed by the host. While it's harmless to adjust the last read
// address even if the reported value is correct, doing so might temporarily
// underestimate the available buffer size by the size of a header.
uint32_t adjusted_last_read_address =
(kSpiDeviceReadBufferSize + last_read_address -
kSpiDeviceFrameHeaderSizeBytes) %
kSpiDeviceReadBufferSize;
uint32_t next_read_address =
((adjusted_last_read_address + 1) & ~3u) % kSpiDeviceReadBufferSize;

if (next_read_address > next_write_address) {
available_buffer_size = next_read_address - next_write_address - 1;
} else {
available_buffer_size = next_read_address +
(kSpiDeviceReadBufferSize - next_write_address) -
1;
}
} while (kFrameSizeBytes > available_buffer_size);

// Write frame (handle wrap-around)
if (next_write_address + kFrameSizeBytes > kSpiDeviceReadBufferSize) {
size_t first_part_size = kSpiDeviceReadBufferSize - next_write_address;
size_t second_part_size = kFrameSizeBytes - first_part_size;
if (dif_spi_device_write_flash_buffer(
spi_device, kDifSpiDeviceFlashBufferTypeEFlash, next_write_address,
first_part_size, frame_bytes) != kDifOk) {
return 0;
}
if (dif_spi_device_write_flash_buffer(
spi_device, kDifSpiDeviceFlashBufferTypeEFlash, 0, second_part_size,
(uint8_t *)(frame_bytes + first_part_size)) != kDifOk) {
return 0;
}
} else {
if (dif_spi_device_write_flash_buffer(
spi_device, kDifSpiDeviceFlashBufferTypeEFlash, next_write_address,
kFrameSizeBytes, frame_bytes) != kDifOk) {
return 0;
}
}

if (dif_spi_device_set_flash_status_registers(spi_device, 0x00) != kDifOk) {
return 0;
}

next_write_address =
(next_write_address + kFrameSizeBytes) % kSpiDeviceReadBufferSize;
spi_device_frame_num++;

return len;
}

static size_t base_dev_uart(void *data, const char *buf, size_t len) {
const dif_uart_t *uart = (const dif_uart_t *)data;
for (size_t i = 0; i < len; ++i) {
Expand All @@ -78,6 +207,13 @@ static size_t base_dev_uart(void *data, const char *buf, size_t len) {
return len;
}

void base_spi_device_stdout(const dif_spi_device_handle_t *spi_device) {
// Reset the frame counter.
spi_device_frame_num = 0;
base_set_stdout((buffer_sink_t){.data = (void *)spi_device,
.sink = &base_dev_spi_device});
}

void base_uart_stdout(const dif_uart_t *uart) {
base_set_stdout(
(buffer_sink_t){.data = (void *)uart, .sink = &base_dev_uart});
Expand Down
11 changes: 11 additions & 0 deletions sw/device/lib/runtime/print.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stdarg.h>
#include <stddef.h>

#include "sw/device/lib/dif/dif_spi_device.h"
#include "sw/device/lib/dif/dif_uart.h"

/**
Expand Down Expand Up @@ -280,6 +281,16 @@ size_t base_fhexdump_with(buffer_sink_t out, base_hexdump_fmt_t fmt,
*/
void base_set_stdout(buffer_sink_t out);

/**
* Configures SPI device stdout for `base_print.h` to use.
*
* Note that this function will save `spi_device` in a global variable, so the
* pointer must have static storage duration.
*
* @param spi_device The SPI device handle to use for stdout.
*/
void base_spi_device_stdout(const dif_spi_device_handle_t *spi_device);

/**
* Configures UART stdout for `base_print.h` to use.
*
Expand Down
1 change: 1 addition & 0 deletions sw/device/lib/testing/test_framework/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ dual_cc_library(
"//sw/device/lib/dif:rv_plic",
"//sw/device/lib/runtime:ibex",
"//sw/device/lib/runtime:irq",
"//sw/device/lib/testing:spi_device_testutils",
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
],
shared = [
Expand Down
99 changes: 88 additions & 11 deletions sw/device/lib/testing/test_framework/ottf_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/irq.h"
#include "sw/device/lib/runtime/print.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_isrs.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
Expand Down Expand Up @@ -77,17 +78,7 @@ void ottf_console_init(void) {
ottf_console_configure_uart(base_addr);
break;
case (kOttfConsoleSpiDevice):
CHECK_DIF_OK(dif_spi_device_init_handle(
mmio_region_from_addr(kOttfTestConfig.console.base_addr),
&ottf_console_spi_device));
CHECK_DIF_OK(dif_spi_device_configure(
&ottf_console_spi_device,
(dif_spi_device_config_t){
.tx_order = kDifSpiDeviceBitOrderMsbToLsb,
.rx_order = kDifSpiDeviceBitOrderMsbToLsb,
.device_mode = kDifSpiDeviceModeFlashEmulation,
}));
CHECK(false, "spi_device not yet supported as OTTF console.");
ottf_console_configure_spi_device(base_addr);
break;
default:
CHECK(false, "unsupported OTTF console interface.");
Expand Down Expand Up @@ -118,6 +109,92 @@ void ottf_console_configure_uart(uintptr_t base_addr) {
}
}

void ottf_console_configure_spi_device(uintptr_t base_addr) {
CHECK_DIF_OK(dif_spi_device_init_handle(
mmio_region_from_addr(kOttfTestConfig.console.base_addr),
&ottf_console_spi_device));
CHECK_DIF_OK(dif_spi_device_configure(
&ottf_console_spi_device,
(dif_spi_device_config_t){
.tx_order = kDifSpiDeviceBitOrderMsbToLsb,
.rx_order = kDifSpiDeviceBitOrderMsbToLsb,
.device_mode = kDifSpiDeviceModeFlashEmulation,
}));
dif_spi_device_flash_command_t read_commands[] = {
{
// Slot 0: ReadStatus1
.opcode = kSpiDeviceFlashOpReadStatus1,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 1: ReadStatus2
.opcode = kSpiDeviceFlashOpReadStatus2,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 2: ReadStatus3
.opcode = kSpiDeviceFlashOpReadStatus3,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 3: ReadJedecID
.opcode = kSpiDeviceFlashOpReadJedec,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 4: ReadSfdp
.opcode = kSpiDeviceFlashOpReadSfdp,
.address_type = kDifSpiDeviceFlashAddr3Byte,
.dummy_cycles = 8,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 5: ReadNormal
.opcode = kSpiDeviceFlashOpReadNormal,
.address_type = kDifSpiDeviceFlashAddr3Byte,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
};
for (uint8_t i = 0; i < ARRAYSIZE(read_commands); ++i) {
uint8_t slot = i + kSpiDeviceReadCommandSlotBase;
CHECK_DIF_OK(dif_spi_device_set_flash_command_slot(
&ottf_console_spi_device, slot, kDifToggleEnabled, read_commands[i]));
}
dif_spi_device_flash_command_t write_commands[] = {
{
// Slot 11: PageProgram
.opcode = kSpiDeviceFlashOpPageProgram,
.address_type = kDifSpiDeviceFlashAddrCfg,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = false,
.upload = true,
.set_busy_status = true,
},
};
for (uint8_t i = 0; i < ARRAYSIZE(write_commands); ++i) {
uint8_t slot = i + (uint8_t)kSpiDeviceWriteCommandSlotBase;
CHECK_DIF_OK(dif_spi_device_set_flash_command_slot(
&ottf_console_spi_device, slot, kDifToggleEnabled, write_commands[i]));
}

base_spi_device_stdout(&ottf_console_spi_device);
}

static uint32_t get_flow_control_watermark_plic_id(void) {
switch (kOttfTestConfig.console.base_addr) {
#if !OT_IS_ENGLISH_BREAKFAST
Expand Down
7 changes: 7 additions & 0 deletions sw/device/lib/testing/test_framework/ottf_console.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ void ottf_console_init(void);
*/
void ottf_console_configure_uart(uintptr_t base_addr);

/**
* Configures the given SPI device to be used by the OTTF console.
*
* @param base_addr The base address of the SPI device to use.
*/
void ottf_console_configure_spi_device(uintptr_t base_addr);

/**
* Manage flow control by inspecting the OTTF console device's receive FIFO.
*
Expand Down

0 comments on commit d0462e1

Please sign in to comment.