Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cherry-pick] #24345 to earlgrey_1.0.0 branch #24834

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
180 changes: 180 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,176 @@ void base_set_stdout(buffer_sink_t out) {
base_stdout = out;
}

static const size_t kSpiDeviceReadBufferSizeBytes =
SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH * sizeof(uint32_t);
static const size_t kSpiDeviceFrameHeaderSizeBytes = 12;
static const size_t kSpiDeviceMaxFramePayloadSizeBytes =
kSpiDeviceReadBufferSizeBytes - kSpiDeviceFrameHeaderSizeBytes - 4;
static const uint32_t kSpiDeviceFrameMagicNumber = 0xa5a5beef;
static uint32_t spi_device_frame_num = 0;

static status_t spi_device_send_data(dif_spi_device_handle_t *spi_device,
const uint8_t *buf, size_t len,
size_t address) {
if (len == 0) {
return OK_STATUS();
}

size_t space_to_end_of_buffer = kSpiDeviceReadBufferSizeBytes - address;
size_t first_part_size =
space_to_end_of_buffer < len ? space_to_end_of_buffer : len;

TRY(dif_spi_device_write_flash_buffer(spi_device,
kDifSpiDeviceFlashBufferTypeEFlash,
address, first_part_size, buf));

// Handle wrap-around.
if (first_part_size < len) {
size_t second_part_size = len - first_part_size;
TRY(dif_spi_device_write_flash_buffer(
spi_device, kDifSpiDeviceFlashBufferTypeEFlash, 0, second_part_size,
(uint8_t *)(buf + first_part_size)));
}

return OK_STATUS();
}

/**
* 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 spi_device_send_frame(void *data, const char *buf, size_t len) {
dif_spi_device_handle_t *spi_device = (dif_spi_device_handle_t *)data;
const size_t data_packet_size_bytes = ((len + 3u) & ~3u);
const size_t frame_size_bytes =
kSpiDeviceFrameHeaderSizeBytes + data_packet_size_bytes;
uint8_t frame_header_bytes[kSpiDeviceFrameHeaderSizeBytes];

static uint32_t next_write_address = 0;

if (frame_size_bytes >= kSpiDeviceReadBufferSizeBytes) {
return 0;
}

// Add the magic bytes.
for (size_t i = 0; i < 4; ++i) {
frame_header_bytes[i] = (kSpiDeviceFrameMagicNumber >> (i * 8)) & 0xff;
}
// Add the frame number.
for (size_t i = 0; i < 4; ++i) {
frame_header_bytes[i + 4] = (spi_device_frame_num >> (i * 8)) & 0xff;
}
// Add the data length.
for (size_t i = 0; i < 4; ++i) {
frame_header_bytes[i + 8] = (len >> (i * 8)) & 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 =
(kSpiDeviceReadBufferSizeBytes + last_read_address -
kSpiDeviceFrameHeaderSizeBytes) %
kSpiDeviceReadBufferSizeBytes;
uint32_t next_read_address = ((adjusted_last_read_address + 1) & ~3u) %
kSpiDeviceReadBufferSizeBytes;

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

// Send frame header.
if (!status_ok(spi_device_send_data(spi_device, frame_header_bytes,
kSpiDeviceFrameHeaderSizeBytes,
next_write_address))) {
return 0;
}
// Send aligned data.
size_t data_write_address =
(next_write_address + kSpiDeviceFrameHeaderSizeBytes) %
kSpiDeviceReadBufferSizeBytes;
size_t aligned_data_len = len & (~3u);
if (!status_ok(spi_device_send_data(spi_device, (uint8_t *)buf,
aligned_data_len, data_write_address))) {
return 0;
}

// Send unaligned data.
if (len != aligned_data_len) {
uint8_t pad_bytes[4] = {0xff, 0xff, 0xff, 0xff};
size_t pad_write_address =
(data_write_address + aligned_data_len) % kSpiDeviceReadBufferSizeBytes;

for (size_t i = 0; i + aligned_data_len < len; i++) {
pad_bytes[i] = buf[aligned_data_len + i];
}
if (!status_ok(spi_device_send_data(spi_device, pad_bytes, 4,
pad_write_address))) {
return 0;
}
}

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

next_write_address =
(next_write_address + frame_size_bytes) % kSpiDeviceReadBufferSizeBytes;
spi_device_frame_num++;

return len;
}

static size_t base_dev_spi_device(void *data, const char *buf, size_t len) {
size_t write_data_len = 0;

while (write_data_len < len) {
size_t payload_len = len - write_data_len;
if (payload_len > kSpiDeviceMaxFramePayloadSizeBytes) {
payload_len = kSpiDeviceMaxFramePayloadSizeBytes;
}

if (spi_device_send_frame(data, buf + write_data_len, payload_len) ==
payload_len) {
write_data_len += payload_len;
}
}

return write_data_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 +251,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
98 changes: 87 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,91 @@ 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(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 (size_t i = 0; i < ARRAYSIZE(read_commands); ++i) {
uint8_t slot = (uint8_t)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 (size_t i = 0; i < ARRAYSIZE(write_commands); ++i) {
uint8_t slot = (uint8_t)i + 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
Loading
Loading