From 3450a3e906b4e621ef4cbbb9fb9d61baf72d40cd Mon Sep 17 00:00:00 2001 From: Alice Ziuziakowska Date: Fri, 20 Feb 2026 14:11:21 +0000 Subject: [PATCH 1/2] sw, mmio.h: add type-safe volatile read/write macros Signed-off-by: Alice Ziuziakowska --- sw/device/lib/hal/mmio.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sw/device/lib/hal/mmio.h b/sw/device/lib/hal/mmio.h index a03c2e6c0..1eba26ca7 100644 --- a/sw/device/lib/hal/mmio.h +++ b/sw/device/lib/hal/mmio.h @@ -17,3 +17,6 @@ #define DEV_READ16(addr) (*((volatile uint16_t *)(addr))) #define DEV_WRITE64(addr, val) (*((volatile uint64_t *)(addr)) = (val)) #define DEV_READ64(addr) (*((volatile uint64_t *)(addr))) + +#define VOLATILE_READ(reg) (*((volatile __typeof((reg)) *)&(reg))) +#define VOLATILE_WRITE(reg, val) (*((volatile __typeof((reg)) *)&(reg)) = (__typeof((reg)))(val)) From 1755b42ea1179337e063bacf9c9b944757aeaafd Mon Sep 17 00:00:00 2001 From: Alice Ziuziakowska Date: Wed, 25 Feb 2026 15:22:43 +0000 Subject: [PATCH 2/2] sw: update SPI Device HAL to use new autogen interface Signed-off-by: Alice Ziuziakowska --- sw/device/bootrom/CMakeLists.txt | 2 +- sw/device/bootrom/bootrom.c | 63 +- sw/device/examples/spi.c | 40 +- sw/device/lib/hal/mocha.c | 3 +- sw/device/lib/hal/reg_field.h | 18 - sw/device/lib/hal/spi_device.c | 1083 ++++++++++++----------- sw/device/lib/hal/spi_device.h | 365 ++++---- sw/device/tests/spi_device/flash_mode.c | 46 +- sw/device/tests/spi_device/smoketest.c | 130 +-- 9 files changed, 901 insertions(+), 849 deletions(-) delete mode 100644 sw/device/lib/hal/reg_field.h diff --git a/sw/device/bootrom/CMakeLists.txt b/sw/device/bootrom/CMakeLists.txt index 33e406b6f..d7dc92eb3 100644 --- a/sw/device/bootrom/CMakeLists.txt +++ b/sw/device/bootrom/CMakeLists.txt @@ -6,7 +6,7 @@ set(NAME bootrom) add_executable(${NAME} bootrom.c) target_link_libraries(${NAME} hal_vanilla runtime_vanilla startup_vanilla) target_compile_options(${NAME} PUBLIC ${VANILLA_FLAGS}) -target_link_options(${NAME} PUBLIC +target_link_options(${NAME} PUBLIC "-nodefaultlibs" "-Wl,--defsym,BOOT_ROM_OFFSET=0x00" "-T${LDS}" diff --git a/sw/device/bootrom/bootrom.c b/sw/device/bootrom/bootrom.c index 6a07286c9..2d8fc321b 100644 --- a/sw/device/bootrom/bootrom.c +++ b/sw/device/bootrom/bootrom.c @@ -4,6 +4,7 @@ #include "boot/trap.h" #include "hal/gpio.h" +#include "hal/mmio.h" #include "hal/mocha.h" #include "hal/spi_device.h" #include "hal/uart.h" @@ -32,7 +33,7 @@ int main(void) spi_boot_strap(console); enum { BootAddress = 0x10004080 }; - uprintf(console, "\nJumping to: 0x%0x\n", BootAddress); + uprintf(console, "\nJumping to: 0x%x\n", BootAddress); boot(BootAddress); uprintf(console, "\nFailed to boot?\n"); @@ -53,9 +54,9 @@ bool spi_boot_strap(uart_t console) spi_device_t spid = mocha_system_spi_device(); spi_device_init(spid); - spi_device_enable_set(spid, true); - spi_device_flash_status_set(spid, 0); - + spi_device_flash_mode_set(spid, spi_device_flash_mode_flash); + const spi_device_flash_status clear_status = { 0 }; + spi_device_flash_status_set(spid, clear_status); uint32_t received_resets = 0; size_t count = 0; @@ -66,45 +67,47 @@ bool spi_boot_strap(uart_t console) count = 0; } - spi_device_cmd_t cmd = spi_device_cmd_get_non_blocking(spid); - if (cmd.status != spi_device_status_ready) { - if (cmd.status == spi_device_status_overflow) { + spi_device_software_command command; + enum spi_device_status command_status = + spi_device_software_command_get_non_blocking(spid, &command); + if (command_status != spi_device_status_ready) { + if (command_status == spi_device_status_overflow) { uprintf(console, "SPI payload overflow\n"); - spi_device_flash_status_set(spid, 0); + spi_device_flash_status_busy_set(spid, false); + continue; } - continue; } - switch (cmd.opcode) { - case SPI_DEVICE_OPCODE_SECTOR_ERASE: + switch (command.opcode) { + case spi_device_opcode_sector_erase: // No need to erase SRAM. break; - case SPI_DEVICE_OPCODE_PAGE_PROGRAM: - if (cmd.payload_byte_count > 0) { - page_program(console, spid, cmd.address, cmd.payload_byte_count); + case spi_device_opcode_page_program: + if (command.has_address && command.payload_byte_count > 0) { + page_program(console, spid, command.address, command.payload_byte_count); } break; - case SPI_DEVICE_OPCODE_RESET: + case spi_device_opcode_reset: // This is a workaround to openFPGALoader that starts with a reset. if (received_resets++ > 0) { // Exit boot strap - spi_device_enable_set(spid, false); + spi_device_flash_mode_set(spid, spi_device_flash_mode_disabled); return true; } - uprintf(console, "\nFirst reset"); + uprintf(console, "\nFirst reset\n"); break; default: - uprintf(console, "\nUnsupported command: 0x%0x", cmd.opcode); + uprintf(console, "\nUnsupported command: 0x%x", command.opcode); break; } // Finished processing the write, clear the busy bit. - spi_device_flash_status_set(spid, 0); + spi_device_flash_status_busy_set(spid, false); } return true; } -static inline bool is_overriding_me(uintptr_t addr) +static inline bool is_overwriting_me(uintptr_t addr) { return addr >= (uintptr_t)_program_start && addr < (uintptr_t)_program_end; } @@ -114,24 +117,28 @@ void page_program(uart_t console, spi_device_t spid, uint32_t offset, uint32_t b // TODO: Enable the spi flash 4 bytes addressing. enum { SramOffset = 0x10000000 }; uintptr_t ptr = SramOffset + offset; - uint32_t payload_offset = 0; + uprintf(console, "page program: addr: 0x%x len: 0x%x bytes\n", (uint32_t)ptr, bytes); + size_t num_words = bytes / 4; + if (bytes % 4 != 0) { + num_words++; + } - if (bytes > SPI_DEVICE_PAYLOAD_AREA_NUM_BYTES) { + if (bytes > spi_device_ingress_buffer_size_payload_fifo * sizeof(uint32_t)) { uprintf(console, "\npage program size out of bounds"); return; } // TODO: Now only SRAM is supported, but when 4 bytes addressing is enabled and the HW supports // DRAM and ROM, then we need to check that the offset is valid within a memory address space. - if (is_overriding_me(ptr) || is_overriding_me(ptr + bytes)) { - uprintf(console, "\nPlease don't override the bootROM."); + if (is_overwriting_me(ptr) || is_overwriting_me(ptr + bytes)) { + uprintf(console, "\nPlease don't overwrite the bootROM."); return; } - while (payload_offset < bytes) { - *((volatile uint64_t *)ptr) = spi_device_flash_payload_buffer_read64(spid, payload_offset); - ptr += sizeof(uint64_t); - payload_offset += sizeof(uint64_t); + for (size_t i = 0; i < num_words; i++) { + uint32_t word = + VOLATILE_READ(spid->ingress_buffer[spi_device_ingress_buffer_offset_payload_fifo + i]); + ((uint32_t *)ptr)[i] = word; } } diff --git a/sw/device/examples/spi.c b/sw/device/examples/spi.c index 3262d3bed..26ab72970 100644 --- a/sw/device/examples/spi.c +++ b/sw/device/examples/spi.c @@ -15,31 +15,36 @@ int main(void) spi_device_t spi_device = mocha_system_spi_device(); uart_init(uart); spi_device_init(spi_device); + spi_device_flash_mode_set(spi_device, spi_device_flash_mode_flash); + const spi_device_flash_status clear_status = { 0 }; + spi_device_flash_status_set(spi_device, clear_status); uprintf(uart, "Hello SPI in Mocha!\n"); // Poll and process SPI command - spi_device_cmd_t cmd; - while (1) { + spi_device_software_command cmd; + while (true) { // Now process SPI command (if any) - cmd = spi_device_cmd_get(spi_device); - if (cmd.status != 0) { - uprintf(uart, "SPI payload overflow\n"); - spi_device_flash_status_set(spi_device, 0); - continue; + enum spi_device_status status = spi_device_software_command_get(spi_device, &cmd); + if (status != spi_device_status_ready) { + if (status == spi_device_status_overflow) { + uprintf(uart, "SPI payload overflow\n"); + spi_device_flash_status_set(spi_device, clear_status); + continue; + } } switch (cmd.opcode) { - case SPI_DEVICE_OPCODE_CHIP_ERASE: + case spi_device_opcode_chip_erase: uprintf(uart, "SPI CHIP ERASE"); break; - case SPI_DEVICE_OPCODE_SECTOR_ERASE: + case spi_device_opcode_sector_erase: uprintf(uart, "SPI SECTOR ERASE"); break; - case SPI_DEVICE_OPCODE_PAGE_PROGRAM: + case spi_device_opcode_page_program: uprintf(uart, "SPI PAGE PROGRAM"); break; - case SPI_DEVICE_OPCODE_RESET: + case spi_device_opcode_reset: uprintf(uart, "SPI RESET"); break; default: @@ -47,7 +52,7 @@ int main(void) break; } - if (cmd.address != UINT32_MAX) { + if (cmd.has_address) { uprintf(uart, " addr: 0x%x\n", cmd.address); } @@ -55,23 +60,22 @@ int main(void) uprintf(uart, "payload bytes: 0x%x\n", cmd.payload_byte_count); uint32_t payload_word_count = ((uint32_t)cmd.payload_byte_count) / sizeof(uint32_t); if ((cmd.payload_byte_count % sizeof(uint32_t)) != 0) { - ++payload_word_count; + payload_word_count++; } uprintf(uart, "payload data:\n"); uint32_t word; for (uint32_t i = 0; i < payload_word_count; ++i) { - word = spi_device_flash_payload_buffer_read(spi_device, i * sizeof(uint32_t)); - spi_device_flash_read_buffer_write(spi_device, cmd.address + i * sizeof(uint32_t), - word); - uprintf(uart, "0x%x\n", word); + if (spi_device_flash_payload_buffer_read_word(spi_device, i, &word)) { + uprintf(uart, "0x%x\n", word); + } } } uprintf(uart, "\n"); - spi_device_flash_status_set(spi_device, 0); + spi_device_flash_status_set(spi_device, clear_status); } return 0; diff --git a/sw/device/lib/hal/mocha.c b/sw/device/lib/hal/mocha.c index 7839bab4d..82af52150 100644 --- a/sw/device/lib/hal/mocha.c +++ b/sw/device/lib/hal/mocha.c @@ -99,7 +99,8 @@ i2c_t mocha_system_i2c(void) spi_device_t mocha_system_spi_device(void) { #if defined(__riscv_zcherihybrid) - return (spi_device_t)create_mmio_capability(spi_device_base, 0x1FC0u); + return (spi_device_t)create_mmio_capability(spi_device_base, + sizeof(struct spi_device_memory_layout)); #else /* !defined(__riscv_zcherihybrid) */ return (spi_device_t)spi_device_base; #endif /* defined(__riscv_zcherihybrid) */ diff --git a/sw/device/lib/hal/reg_field.h b/sw/device/lib/hal/reg_field.h deleted file mode 100644 index 2fcf63e64..000000000 --- a/sw/device/lib/hal/reg_field.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright lowRISC contributors (COSMIC project). -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -// Register field macros. - -#pragma once - -#include - -static inline uint32_t reg32_field(uint8_t upper, uint8_t lower, uint32_t value) -{ - if (32 <= upper || upper < lower) { - return 0; - } - const uint32_t mask = (1 << (upper - lower + 1)) - 1; - return (mask & value) << lower; -} diff --git a/sw/device/lib/hal/spi_device.c b/sw/device/lib/hal/spi_device.c index 9e3979ac8..330ba5e57 100644 --- a/sw/device/lib/hal/spi_device.c +++ b/sw/device/lib/hal/spi_device.c @@ -4,645 +4,716 @@ #include "hal/spi_device.h" #include "hal/mmio.h" -#include "hal/reg_field.h" #include #include -bool spi_device_interrupt_is_pending(spi_device_t spi_device, uint8_t intr_id) +#define array_len(arr) (sizeof(arr) / sizeof((arr)[0])) + +enum : size_t { + spi_device_sfdp_size_sfdp_header = 2u, + spi_device_sfdp_size_bfpt_header = 2u, + spi_device_sfdp_size_bfpt = 23u, +}; + +enum : size_t { + spi_device_sfdp_offset_sfdp_header = 0u, + spi_device_sfdp_offset_bfpt_header = + spi_device_sfdp_offset_sfdp_header + spi_device_sfdp_size_sfdp_header, + spi_device_sfdp_offset_bfpt = + spi_device_sfdp_offset_bfpt_header + spi_device_sfdp_size_bfpt_header, +}; + +/* SFDP signature */ +enum : uint32_t { + sfdp_signature = 0x50444653u, +}; + +enum : uint8_t { + sfdp_major = 0x01u, + sfdp_minor = 0x0au, +}; + +/* access protocol for SFDP: 3-byte addressing, 8 wait states (dummy cycles) (JESD216F 6.2.3) */ +enum : uint8_t { + spi_device_sfdp_access_protocol = 0xffu, +}; + +/* BFPT header */ +enum : uint8_t { + bfpt_major = 0x01u, + bfpt_minor = 0x07u, +}; + +enum : uint32_t { + spi_device_log2_density_bytes = 20u, + spi_device_log2_density_bits = spi_device_log2_density_bytes + 3u, +}; + +static const uint32_t spi_device_lowrisc_flash_sfdp[] = { + /* SFDP signature that indicates the presence of a SFDP table (JESD216F 6.2.1) */ + [spi_device_sfdp_offset_sfdp_header] = sfdp_signature, + + /* [ 7: 0]: SFDP minor revision number (JESD216F 6.2.2) + * [15: 8]: SFDP major revision number (JESD216F 6.2.2) + * [23:16]: Number of parameter headers, zero-based (JESD216F 6.2.2) = 0x0 + * [31:24]: SFDP access protocol (JESD216F 6.2.3) */ + [spi_device_sfdp_offset_sfdp_header + 1u] = + (((uint32_t)spi_device_sfdp_access_protocol) << 24) | (0x00 << 16) | + (((uint32_t)sfdp_major) << 8) | ((uint32_t)(sfdp_minor)), + + /* Basic Flash Parameters Table (BFPT) parameter header */ + /* BFPT parameter header word 1: + * [ 7: 0]: LSB of the parameter ID that indicates parameter table ownership and type + * (JESD216F 6.3.1, 6.3.3) = 0x7 + * [15: 8]: Parameter table minor revision number (JESD216F 6.3.1) + * [23:16]: Parameter table major revision number (JESD216F 6.3.1) + * [31:24]: Length of the parameter table in words, one-based (JESD216F 6.3.1) = 23 */ + [spi_device_sfdp_offset_bfpt_header] = + (spi_device_sfdp_size_bfpt << 24) | (((uint32_t)bfpt_major) << 16) | + (((uint32_t)bfpt_minor) << 8), + + /* BFPT parameter header word 2: + * [23: 0]: Word-aligned byte offset of the corresponding parameter table from the start of + * the SFDP table (JESD216F 6.3.2) + * [31:24]: MSB of the parameter ID that indicates parameter table ownership and type + * (JESD216F 6.3.2, 6.3.3) */ + [spi_device_sfdp_offset_bfpt_header + 1u] = (0xffu << 24) | (0x04u), + + /* Note: Words below are numbered starting from 1 to match JESD216F. Some fields + * that are not supported by OpenTitan are merged for the sake of conciseness. + * Reserved fields are set to all 1s, unless otherwise specified. */ + + /* BFPT word 1: + * [31:23]: Unused (all 1s) + * [22:19]: (1S-1S-4S) (1S-4S-4S) (1S-2S-2S) DTR Clock = 0x0 (not supported) + * [18:17]: Address bytes = 0x0 (3-byte only addressing) + * [16:16]: (1S-1S-2S) = 0x0 (not supported) + * [15: 8]: 4 KiB erase instruction opcode = 0x20 + * [ 7: 5]: Unused (all 1s) + * [ 4: 4]: Write enable instruction opcode = 0x1 (use 0x06 for WREN) + * [ 3: 3]: Volatile block protect bits = 0x1 (solely volatile) + * [ 2: 2]: Write granularity = 0x1 (buffer >= 64 B) + * [ 1: 0]: Block/sector erase sizes = 0x1 (uniform 4 KiB erase) */ + [spi_device_sfdp_offset_bfpt] = + (0x1ffu << 23) | (((uint32_t)spi_device_opcode_sector_erase) << 8) | (0b11111101u), + + /* BFPT word 2: + * [31:31]: Density greater than 2 Gib = 0x0 (false) + * [30: 0]: Flash memory density in bits, zero-based (0x7fffff) */ + [spi_device_sfdp_offset_bfpt + 1u] = (1u << spi_device_log2_density_bits) - 1u, + + /* BFPT word 3: + * [31: 0]: Fast read (1S-4S-4S) (1S-1S-4S) = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 2u] = 0u, + + /* BFPT word 4: + * [31: 0]: Fast read (1S-1S-2S) (1S-2S-2S) = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 3u] = 0u, + + /* BFPT word 5: + * [31: 5]: Reserved + * [ 4: 4]: Fast read (4S-4S-4S) support = 0x0 (not supported) + * [ 3: 1]: Reserved + * [ 0: 0]: Fast read (2S-2S-2S) support = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 4u] = UINT32_MAX & ~(0b1u << 4) & ~(0b1u), + + /* BFPT word 6: + * [31:16]: Fast read (2S-2S-2S) (not supported, 0x0) + * [15: 0]: Reserved */ + [spi_device_sfdp_offset_bfpt + 5u] = 0x0000ffffu, + + /* BFPT word 7: + * [31:16]: Fast read (4S-4S-4S) = 0x0 (not supported) + * [15: 0]: Reserved */ + [spi_device_sfdp_offset_bfpt + 6u] = 0x0000ffffu, + + /* BFPT word 8: + * [31:16]: Erase type 2 instruction and size = 0x0 (not supported) + * [15: 8]: Erase type 1 instruction opcode = 0x20 + * [ 7: 0]: log2 of Erase type 1 size = 0x0c (4 KiB) */ + [spi_device_sfdp_offset_bfpt + 7u] = + (((uint32_t)spi_device_opcode_sector_erase) << 8) | (0x0cu), + + /* BFPT word 9: + * [31: 0]: Erase type 4 and 3 = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 8u] = 0u, + + /* BFPT word 10: + * [31:11]: Erase 4,3,2 typical time = 0x0 (not supported) + * [10: 9]: Erase type 1 time unit = 0x1 (16 ms) + * [ 8: 4]: Erase type 1 time count, zero-based = 0x8 + * formula: (count + 1) * unit + * (8 + 1) * 16 ms = 144 ms + * [ 3: 0]: Max erase time multiplier, zero-based = 0x6 + * formula: 2 * (multiplier + 1) * erase_time */ + [spi_device_sfdp_offset_bfpt + 9u] = (0x1u << 9) | (0x8u << 4) | (0x6u), + + /* BFPT word 11: + * [31:31]: Reserved + * [30:29]: Chip erase time units = 0x0 (16 ms) + * [28:24]: Chip erase time count, zero-based = 0xb + * formula: (count + 1) * unit + * (11 + 1) * 16 ms = 192 ms + * [23:23]: Additional byte program time units = 0x1 (8 us) + * [22:19]: Additional byte program time count, zero-based = 0x5 + * formula: (count + 1) * unit + * (5 + 1) * 8 us = 48 us + * [18:18]: First byte program time unit = 0x1 (8 us) + * [17:14]: First byte program time count, zero-based = 0x5 + * formula: (count + 1) * unit + * (5 + 1) * 8 us = 48 us + * [13:13]: Page program time unit = 0x1 (64 us) + * [12: 8]: Page program time count, zero-based = 0x0b + * formula: (count + 1) * unit + * (11 + 1) * 64 us = 768 us + * [ 7: 4]: log2 of page size = 0x8 (512-byte pages) + * [ 3: 0]: Max program time multiplier, zero-based = 0x0 + * formula: 2 * (multiplier + 1) * program_time */ + [spi_device_sfdp_offset_bfpt + 10u] = + (0b1u << 31) | (0xbu << 24) | (0b1u << 23) | (0x5u << 19) | (0b1u << 18) | (0x5u << 14) | + (0b1u << 13) | (0xbu << 8) | (0x8u << 4), + + /* BFPT word 12: + * [31:31]: Suspend/Resume supported = 0x1 (not supported) + * [30: 9]: Suspend/Resume latencies for erase & program = 0x1 (not supported) + * [ 8: 8]: Reserved + * [ 7: 0]: Prohibited ops during suspend = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 11u] = (0b1u << 31) | (0b1u << 8), + + /* BFPT word 13: + * [31: 0]: Erase/program suspend/resume instructions = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 12u] = 0u, + + /* BFPT word 14: + * [31:31]: Deep powerdown support = 0x1 (not supported) + * [30: 8]: Deep powerdown instructions and delay = 0x0 (not supported) + * [ 7: 2]: Busy polling = 0x1 (poll on bit 0 using 0x05 opcode) + * [ 1: 0]: Reserved */ + [spi_device_sfdp_offset_bfpt + 13u] = (0b1u << 31) | (0x1u << 2) | (0b11), + + /* BFPT word 15: + * [31:24]: Reserved + * [23: 0]: Hold, QE, (4S-4S-4S), 0-4-4 = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 14u] = (0xffu << 24), + + /* BFPT word 16: + * [31:14]: 4-Byte addressing = 0x0 (not supported) + * [13: 8]: Soft-reset = 0x10 (0x66/0x99 sequence) + * [ 7: 7]: Reserved + * [ 6: 0]: Status register = 0x0 (read-only) */ + [spi_device_sfdp_offset_bfpt + 15u] = (0x10u << 8) | (0b1u << 7), + + /* BFPT word 17: + * [31: 0]: Fast read (1S-8S-8S) (1S-1S-8S) = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 16u] = 0u, + + /* BFPT word 18: + * Reserved fields of this word should be 0 (JESD216F 6.4.21) + * [31, 0]: Data strobe, SPI protocol reset, etc. = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 17u] = 0u, + + /* BFPT word 19: + * Reserved fields of this word should be 0 (JESD216F 6.4.22) + * [31, 0]: Octable enable, (8D-8D-8D), 0-8-8 mode = 0x0 (not suported) */ + [spi_device_sfdp_offset_bfpt + 18u] = 0u, + + /* BFPT word 20: + * [31, 0]: Max (8S-8S-8S) (4D-4D-4D) (4S-4S-4S) speed = all 1s (not supported) */ + [spi_device_sfdp_offset_bfpt + 19u] = UINT32_MAX, + + /* BFPT word 21: + * Reserved fields of this word should be 0 (JESD216F 6.4.24) + * [31, 0]: Fast read support for various modes = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 20u] = 0u, + + /* BFPT word 22: + * [31, 0]: Fast read (1S-1D-1D) (1S-2D-2D) = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 21u] = 0u, + + /* BFPT word 23: + * [31, 0]: Fast read (1S-4D-4D) (4S-2D-2D) = 0x0 (not supported) */ + [spi_device_sfdp_offset_bfpt + 22u] = 0u, +}; + +_Static_assert(array_len(spi_device_lowrisc_flash_sfdp) == + spi_device_sfdp_size_sfdp_header + spi_device_sfdp_size_bfpt_header + + spi_device_sfdp_size_bfpt, + "SFDP data unexpected size"); + +_Static_assert(array_len(spi_device_lowrisc_flash_sfdp) <= spi_device_egress_buffer_size_sfdp, + "SFDP data too big to fit in buffer"); + +static const spi_device_jedec_cc lowrisc_jedec_cc = { + /* lowRISC's JEDEC ID consists of 12 0x7F continuation codes ... */ + .cc = 0x7fu, + /* Workaround for openFPGAloader only reading one byte for JEDEC manufacturer ID */ + .num_cc = 0u, +}; + +static const spi_device_jedec_id lowrisc_jedec_id = { + /* ... followed by 0xEF */ + .mf = 0xefu, + /* JEDEC device ID: + * [15: 8]: log2 of flash size in bytes + * [ 7: 4]: Chip generation = 0x0 + * [ 3: 3]: Bootstrap bit = 0x1 + * [ 2: 0]: Chip revision = 0x0 */ + .id = (((uint16_t)spi_device_log2_density_bytes) << 8u) | (0b1u << 3), +}; + +void spi_device_init(spi_device_t spi_device) { - if (intr_id > SPI_DEVICE_MAX_INTR) { - return false; + spi_device_jedec_cc_set(spi_device, lowrisc_jedec_cc); + spi_device_jedec_id_set(spi_device, lowrisc_jedec_id); + + /* fill SFDP with all 1s, then write the flash SFDP data into it */ + for (size_t i = 0; i < spi_device_egress_buffer_size_sfdp; i++) { + VOLATILE_WRITE(spi_device->egress_buffer[spi_device_egress_buffer_offset_sfdp + i], + UINT32_MAX); + } + for (size_t i = 0; i < array_len(spi_device_lowrisc_flash_sfdp); i++) { + VOLATILE_WRITE(spi_device->egress_buffer[spi_device_egress_buffer_offset_sfdp + i], + spi_device_lowrisc_flash_sfdp[i]); } - return ((DEV_READ(spi_device + SPI_DEVICE_INTR_STATE_REG) & (1 << intr_id)) != 0); -} -void spi_device_interrupt_clear(spi_device_t spi_device, uint8_t intr_id) + spi_device_flash_status status = { 0u }; + spi_device_flash_status_set(spi_device, status); + + /* Configure command slots */ + /* Slot 0: read status */ + spi_device_cmd_info command_slot_0 = { + .opcode = spi_device_opcode_read_status, + .addr_mode = spi_device_addr_mode_disabled, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_0, 0); + + /* Slot 3: read JEDEC */ + spi_device_cmd_info command_slot_3 = { + .opcode = spi_device_opcode_read_jedec, + .addr_mode = spi_device_addr_mode_disabled, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_3, 3); + + /* Slot 4: read SFDP */ + spi_device_cmd_info command_slot_4 = { + .opcode = spi_device_opcode_read_sfdp, + .addr_mode = spi_device_addr_mode_3b, + .dummy_en = true, + .dummy_size = 7u, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_4, 4); + + /* Slot 5: read */ + spi_device_cmd_info command_slot_5 = { + .opcode = spi_device_opcode_read, + .addr_mode = spi_device_addr_mode_cfg, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_5, 5); + + /* Slot 11: chip erase */ + spi_device_cmd_info command_slot_11 = { + .opcode = spi_device_opcode_chip_erase, + .addr_mode = spi_device_addr_mode_disabled, + .upload = true, + .busy = true, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_11, 11); + + /* Slot 12: sector erase */ + spi_device_cmd_info command_slot_12 = { + .opcode = spi_device_opcode_sector_erase, + .addr_mode = spi_device_addr_mode_cfg, + .upload = true, + .busy = true, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_slot_12, 12); + + /* Slot 13: page program */ + spi_device_cmd_info command_info_13 = { + .opcode = spi_device_opcode_page_program, + .addr_mode = spi_device_addr_mode_cfg, + .upload = true, + .busy = true, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_info_13, 13); + + /* Slot 14: reset */ + spi_device_cmd_info command_info_14 = { + .opcode = spi_device_opcode_reset, + .addr_mode = spi_device_addr_mode_disabled, + .upload = true, + .busy = true, + .valid = true, + }; + spi_device_cmd_info_set(spi_device, command_info_14, 14); + + /* enable write enable and write disable commands */ + spi_device_cmd_info_wren_set(spi_device, spi_device_opcode_wren, true); + spi_device_cmd_info_wrdi_set(spi_device, spi_device_opcode_wrdi, true); + + /* disable 4b address mode */ + spi_device_4b_addr_mode_enable_set_unchecked(spi_device, false); +} + +void spi_device_flash_mode_set(spi_device_t spi_device, enum spi_device_flash_mode mode) +{ + spi_device_control control = VOLATILE_READ(spi_device->control); + control.mode = mode; + VOLATILE_WRITE(spi_device->control, control); +} + +spi_device_intr spi_device_interrupt_enable_get(spi_device_t spi_device) +{ + return VOLATILE_READ(spi_device->intr_enable); +} + +void spi_device_interrupt_enable_set(spi_device_t spi_device, spi_device_intr intrs) +{ + VOLATILE_WRITE(spi_device->intr_enable, intrs); +} + +void spi_device_interrupt_enable(spi_device_t spi_device, spi_device_intr intrs) +{ + spi_device_intr enable = VOLATILE_READ(spi_device->intr_enable); + VOLATILE_WRITE(spi_device->intr_enable, enable | intrs); +} + +void spi_device_interrupt_disable(spi_device_t spi_device, spi_device_intr intrs) { - if (intr_id <= SPI_DEVICE_MAX_INTR) { - DEV_WRITE(spi_device + SPI_DEVICE_INTR_STATE_REG, 1 << intr_id); - } + spi_device_intr enable = VOLATILE_READ(spi_device->intr_enable); + VOLATILE_WRITE(spi_device->intr_enable, enable & ~intrs); } void spi_device_interrupt_disable_all(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_INTR_ENABLE_REG, 0); + VOLATILE_WRITE(spi_device->intr_enable, 0u); } -void spi_device_interrupt_enable(spi_device_t spi_device, uint8_t intr_id) +void spi_device_interrupt_force(spi_device_t spi_device, spi_device_intr intrs) { - if (intr_id <= SPI_DEVICE_MAX_INTR) { - DEV_WRITE(spi_device + SPI_DEVICE_INTR_ENABLE_REG, - DEV_READ(spi_device + SPI_DEVICE_INTR_ENABLE_REG) | (1 << intr_id)); - } + VOLATILE_WRITE(spi_device->intr_test, intrs); } -void spi_device_interrupt_disable(spi_device_t spi_device, uint8_t intr_id) +void spi_device_interrupt_clear(spi_device_t spi_device, spi_device_intr intrs) { - if (intr_id <= SPI_DEVICE_MAX_INTR) { - DEV_WRITE(spi_device + SPI_DEVICE_INTR_ENABLE_REG, - DEV_READ(spi_device + SPI_DEVICE_INTR_ENABLE_REG) & ~(1 << intr_id)); - } + VOLATILE_WRITE(spi_device->intr_state, intrs); } -void spi_device_interrupt_trigger(spi_device_t spi_device, uint8_t intr_id) +bool spi_device_interrupt_all_pending(spi_device_t spi_device, uint32_t intrs) { - if (intr_id <= SPI_DEVICE_MAX_INTR) { - DEV_WRITE(spi_device + SPI_DEVICE_INTR_TEST_REG, 1 << intr_id); - } + return (VOLATILE_READ(spi_device->intr_state) & intrs) == intrs; } -void spi_device_enable_set(spi_device_t spi_device, bool enable) +bool spi_device_interrupt_any_pending(spi_device_t spi_device, uint32_t intrs) { - DEV_WRITE(spi_device + SPI_DEVICE_CTRL_REG, enable << SPI_DEVICE_CTRL_MODE_OFFSET); + return (VOLATILE_READ(spi_device->intr_state) & intrs) != 0u; } -void spi_device_4b_addr_mode_enable_set(spi_device_t spi_device, bool enable) +spi_device_flash_status spi_device_flash_status_get(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_ADDR_MODE_REG, (uint32_t)enable); - - // Wait for change to take effect - while (DEV_READ(spi_device + SPI_DEVICE_ADDR_MODE_REG) & SPI_DEVICE_ADDR_MODE_PENDING_MASK) { - } + return VOLATILE_READ(spi_device->flash_status); } -bool spi_device_4b_addr_mode_enable_get(spi_device_t spi_device) +void spi_device_flash_status_set(spi_device_t spi_device, spi_device_flash_status status) { - return ((DEV_READ(spi_device + SPI_DEVICE_ADDR_MODE_REG) & SPI_DEVICE_ADDR_MODE_4B_EN_MASK) == - 1); + VOLATILE_WRITE(spi_device->flash_status, status); } -void spi_device_flash_status_set(spi_device_t spi_device, uint32_t flash_status) +void spi_device_flash_status_busy_set(spi_device_t spi_device, bool busy) { - DEV_WRITE(spi_device + SPI_DEVICE_FLASH_STATUS_REG, flash_status); + spi_device_flash_status status = VOLATILE_READ(spi_device->flash_status); + status.busy = busy; + VOLATILE_WRITE(spi_device->flash_status, status); } -uint32_t spi_device_flash_status_get(spi_device_t spi_device) +void spi_device_flash_status_wel_set(spi_device_t spi_device, bool wel) { - return DEV_READ(spi_device + SPI_DEVICE_FLASH_STATUS_REG); + spi_device_flash_status status = VOLATILE_READ(spi_device->flash_status); + status.wel = wel; + VOLATILE_WRITE(spi_device->flash_status, status); } -void spi_device_jedec_cc_set(spi_device_t spi_device, uint8_t cc, uint8_t num_cc) +bool spi_device_4b_addr_mode_enable_get(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_JEDEC_CC_REG, - ((uint32_t)cc << SPI_DEVICE_JEDEC_CC) | - ((uint32_t)num_cc << SPI_DEVICE_JEDEC_NUM_CC)); + spi_device_addr_mode mode = VOLATILE_READ(spi_device->addr_mode); + return mode.addr_4b_en; } -uint16_t spi_device_jedec_cc_get(spi_device_t spi_device) +void spi_device_4b_addr_mode_enable_set(spi_device_t spi_device, bool enable) { - return (uint16_t)DEV_READ(spi_device + SPI_DEVICE_JEDEC_CC_REG); + spi_device_4b_addr_mode_enable_set_unchecked(spi_device, enable); + /* poll the pending bit until the update is committed */ + spi_device_addr_mode mode; + do { + mode = VOLATILE_READ(spi_device->addr_mode); + } while (mode.pending == true); } -void spi_device_jedec_id_set_raw(spi_device_t spi_device, uint32_t data) +void spi_device_4b_addr_mode_enable_set_unchecked(spi_device_t spi_device, bool enable) { - DEV_WRITE(spi_device + SPI_DEVICE_JEDEC_ID_REG, data); + spi_device_addr_mode mode = { .addr_4b_en = enable }; + VOLATILE_WRITE(spi_device->addr_mode, mode); } -void spi_device_jedec_id_set(spi_device_t spi_device, bool rom_bootstrap, uint8_t chip_rev, - uint8_t chip_gen, uint8_t density, uint8_t manufacturer_id) +spi_device_cmd_info spi_device_cmd_info_get(spi_device_t spi_device, size_t index) { - uint32_t reg = ((uint32_t)rom_bootstrap) << SPI_DEVICE_ROM_BOOTSTRAP; - reg = reg | (((uint32_t)chip_rev & SPI_DEVICE_CHIP_REV_MASK) << SPI_DEVICE_CHIP_REV); - reg = reg | (((uint32_t)chip_gen & SPI_DEVICE_CHIP_GEN_MASK) << SPI_DEVICE_CHIP_GEN); - reg = reg | (((uint32_t)density) << SPI_DEVICE_DENSITY); - reg = reg | (((uint32_t)manufacturer_id) << SPI_DEVICE_MANUFACTURER_ID); - spi_device_jedec_id_set_raw(spi_device, reg); + if (index >= array_len(spi_device->cmd_info)) { + spi_device_cmd_info invalid = { 0u }; + return invalid; + } + return VOLATILE_READ(spi_device->cmd_info[index]); } -uint32_t spi_device_jedec_id_get(spi_device_t spi_device) +void spi_device_cmd_info_set(spi_device_t spi_device, spi_device_cmd_info cmd_info, size_t index) { - return DEV_READ(spi_device + SPI_DEVICE_JEDEC_ID_REG); + if (index >= array_len(spi_device->cmd_info)) { + return; + } + VOLATILE_WRITE(spi_device->cmd_info[index], cmd_info); } -void spi_device_mailbox_addr_set(spi_device_t spi_device, uint32_t addr) +spi_device_cmd_info_wren spi_device_cmd_info_wren_get(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_MAILBOX_ADDR_REG, addr); + return VOLATILE_READ(spi_device->cmd_info_wren); } -uint32_t spi_device_mailbox_addr_get(spi_device_t spi_device) +void spi_device_cmd_info_wren_set(spi_device_t spi_device, uint8_t opcode, bool valid) { - return DEV_READ(spi_device + SPI_DEVICE_MAILBOX_ADDR_REG); + spi_device_cmd_info_wren wren = { + .opcode = opcode, + .valid = valid, + }; + VOLATILE_WRITE(spi_device->cmd_info_wren, wren); } -uint32_t spi_device_upload_status_get(spi_device_t spi_device) +spi_device_cmd_info_wrdi spi_device_cmd_info_wrdi_get(spi_device_t spi_device) { - return DEV_READ(spi_device + SPI_DEVICE_UPLOAD_STATUS_REG); + return VOLATILE_READ(spi_device->cmd_info_wrdi); } -uint32_t spi_device_upload_status2_get(spi_device_t spi_device) +void spi_device_cmd_info_wrdi_set(spi_device_t spi_device, uint8_t opcode, bool valid) { - return DEV_READ(spi_device + SPI_DEVICE_UPLOAD_STATUS2_REG); + spi_device_cmd_info_wrdi wrdi = { + .opcode = opcode, + .valid = valid, + }; + VOLATILE_WRITE(spi_device->cmd_info_wrdi, wrdi); } -uint32_t spi_device_upload_cmdfifo_read(spi_device_t spi_device) +uint32_t spi_device_cmd_filter_get(spi_device_t spi_device, size_t index) { - return DEV_READ(spi_device + SPI_DEVICE_UPLOAD_CMDFIFO_REG); + if (index >= array_len(spi_device->cmd_filter)) { + return 0; + } + return VOLATILE_READ(spi_device->cmd_filter[index]); } -uint32_t spi_device_upload_addrfifo_read(spi_device_t spi_device) +void spi_device_cmd_filter_enable_all(spi_device_t spi_device) { - return DEV_READ(spi_device + SPI_DEVICE_UPLOAD_ADDRFIFO_REG); + for (size_t index = 0; index < array_len(spi_device->cmd_filter); index++) { + VOLATILE_WRITE(spi_device->cmd_filter[index], UINT32_MAX); + } } -void spi_device_cmd_filter_set(spi_device_t spi_device, uint32_t offset, uint32_t data) +void spi_device_cmd_filter_enable_set(spi_device_t spi_device, size_t index, uint32_t filter) { - if (offset < SPI_DEVICE_CMD_FILTER_0_REG || offset > SPI_DEVICE_CMD_FILTER_7_REG) { + if (index >= array_len(spi_device->cmd_filter)) { return; } - - DEV_WRITE(spi_device + offset, data); + VOLATILE_WRITE(spi_device->cmd_filter[index], filter); } -uint32_t spi_device_cmd_filter_get(spi_device_t spi_device, uint32_t offset) +void spi_device_cmd_filter_enable(spi_device_t spi_device, size_t index, uint32_t filter) { - if (offset < SPI_DEVICE_CMD_FILTER_0_REG || offset > SPI_DEVICE_CMD_FILTER_7_REG) { - return 0; + if (index >= array_len(spi_device->cmd_filter)) { + return; } - - return DEV_READ(spi_device + offset); + uint32_t cmd_filter = VOLATILE_READ(spi_device->cmd_filter[index]); + VOLATILE_WRITE(spi_device->cmd_filter[index], cmd_filter | filter); } -void spi_device_cmd_info_set_raw(spi_device_t spi_device, uint32_t offset, uint32_t data) +void spi_device_cmd_filter_disable(spi_device_t spi_device, size_t index, uint32_t filter) { - if (offset < SPI_DEVICE_CMD_INFO_0_REG || offset > SPI_DEVICE_CMD_INFO_23_REG) { + if (index >= array_len(spi_device->cmd_filter)) { return; } - - DEV_WRITE(spi_device + offset, data); + uint32_t cmd_filter = VOLATILE_READ(spi_device->cmd_filter[index]); + VOLATILE_WRITE(spi_device->cmd_filter[index], cmd_filter & ~filter); } -void spi_device_cmd_info_set(spi_device_t spi_device, uint32_t offset, uint8_t opcode, bool address, - uint8_t dummy_cycles, bool handled_in_sw) +void spi_device_cmd_filter_disable_all(spi_device_t spi_device) { - if (offset < SPI_DEVICE_CMD_INFO_0_REG || offset > SPI_DEVICE_CMD_INFO_23_REG) { - return; - } - - uint32_t reg = 0; - reg = reg | (opcode << SPI_DEVICE_CMD_OPCODE); - - if (address) { - reg = reg | ((SPI_DEVICE_CMD_ADDR_MODE_ADDR_3B & SPI_DEVICE_CMD_ADDR_MODE_MASK) - << SPI_DEVICE_CMD_ADDR_MODE); - } else { - reg = reg | ((SPI_DEVICE_CMD_ADDR_MODE_ADDR_DISABLED & SPI_DEVICE_CMD_ADDR_MODE_MASK) - << SPI_DEVICE_CMD_ADDR_MODE); - } - - if (dummy_cycles > 0) { - reg = reg | - (((dummy_cycles - 1) & SPI_DEVICE_CMD_DUMMY_SIZE_MASK) << SPI_DEVICE_CMD_DUMMY_SIZE); - reg = reg | (1 << SPI_DEVICE_CMD_DUMMY_EN); + for (size_t index = 0; index < array_len(spi_device->cmd_filter); index++) { + VOLATILE_WRITE(spi_device->cmd_filter[index], 0u); } - - if (handled_in_sw) { - reg = reg | (1 << SPI_DEVICE_CMD_UPLOAD); - reg = reg | (1 << SPI_DEVICE_CMD_BUSY); - } - - reg = reg | (1 << SPI_DEVICE_CMD_VALID); - - spi_device_cmd_info_set_raw(spi_device, offset, reg); } -uint32_t spi_device_cmd_info_get(spi_device_t spi_device, uint32_t offset) +spi_device_jedec_cc spi_device_jedec_cc_get(spi_device_t spi_device) { - if (offset < SPI_DEVICE_CMD_INFO_0_REG || offset > SPI_DEVICE_CMD_INFO_23_REG) { - return 0; - } + return VOLATILE_READ(spi_device->jedec_cc); +} - return DEV_READ(spi_device + offset); +void spi_device_jedec_cc_set(spi_device_t spi_device, spi_device_jedec_cc jedec_cc) +{ + VOLATILE_WRITE(spi_device->jedec_cc, jedec_cc); } -void spi_device_cmd_info_write_enable_set_raw(spi_device_t spi_device, uint32_t data) +spi_device_jedec_id spi_device_jedec_id_get(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_CMD_INFO_WREN_REG, data); + return VOLATILE_READ(spi_device->jedec_id); } -void spi_device_cmd_info_write_enable_set(spi_device_t spi_device, uint8_t opcode) +void spi_device_jedec_id_set(spi_device_t spi_device, spi_device_jedec_id jedec_id) { - uint32_t reg = 0; - reg = reg | (opcode << SPI_DEVICE_CMD_OPCODE); - reg = reg | (1 << SPI_DEVICE_CMD_VALID); + VOLATILE_WRITE(spi_device->jedec_id, jedec_id); +} - spi_device_cmd_info_write_enable_set_raw(spi_device, reg); +spi_device_upload_status spi_device_upload_status_get(spi_device_t spi_device) +{ + return VOLATILE_READ(spi_device->upload_status); } -uint32_t spi_device_cmd_info_write_enable_get(spi_device_t spi_device) +spi_device_upload_status2 spi_device_upload_status2_get(spi_device_t spi_device) { - return DEV_READ(spi_device + SPI_DEVICE_CMD_INFO_WREN_REG); + return VOLATILE_READ(spi_device->upload_status2); } -void spi_device_cmd_info_write_disable_set_raw(spi_device_t spi_device, uint32_t data) +spi_device_upload_cmdfifo spi_device_upload_cmdfifo_get(spi_device_t spi_device) { - DEV_WRITE(spi_device + SPI_DEVICE_CMD_INFO_WRDI_REG, data); + return VOLATILE_READ(spi_device->upload_cmdfifo); } -void spi_device_cmd_info_write_disable_set(spi_device_t spi_device, uint8_t opcode) +uint32_t spi_device_upload_addrfifo_get(spi_device_t spi_device) { - uint32_t reg = 0; - reg = reg | (opcode << SPI_DEVICE_CMD_OPCODE); - reg = reg | (1 << SPI_DEVICE_CMD_VALID); + return VOLATILE_READ(spi_device->upload_addrfifo); +} - spi_device_cmd_info_write_disable_set_raw(spi_device, reg); +uint32_t spi_device_mailbox_addr_get(spi_device_t spi_device) +{ + return VOLATILE_READ(spi_device->mailbox_addr); } -uint32_t spi_device_cmd_info_write_disable_get(spi_device_t spi_device) +void spi_device_mailbox_addr_set(spi_device_t spi_device, uint32_t mailbox_addr) { - return DEV_READ(spi_device + SPI_DEVICE_CMD_INFO_WRDI_REG); + VOLATILE_WRITE(spi_device->mailbox_addr, mailbox_addr); } -bool spi_device_flash_read_buffer_write(spi_device_t spi_device, uint32_t offset, uint32_t data) +enum spi_device_status spi_device_software_command_get_non_blocking( + spi_device_t spi_device, spi_device_software_command *command) { - // Ignore unaligned writes - if ((offset % sizeof(uint32_t)) != 0) { - return false; + /* busy poll for a software-handled command */ + if (!spi_device_interrupt_all_pending(spi_device, spi_device_intr_upload_cmdfifo_not_empty)) { + return spi_device_status_empty; } - // Ignore writes outside read buffer - if (offset >= SPI_DEVICE_READ_BUFFER_NUM_BYTES) { - return false; + + /* clear the interrupt */ + spi_device_interrupt_clear(spi_device, spi_device_intr_upload_cmdfifo_not_empty); + + /* check for payload overflow */ + if (spi_device_interrupt_all_pending(spi_device, spi_device_intr_upload_payload_overflow)) { + spi_device_interrupt_clear(spi_device, spi_device_intr_upload_payload_overflow); + return spi_device_status_overflow; } - DEV_WRITE(spi_device + SPI_DEVICE_EGRESS_BUFFER_OFFSET + SPI_DEVICE_READ_BUFFER_OFFSET + offset, - data); - return true; + + /* get the command opcode, address, and payload size */ + command->opcode = spi_device_upload_cmdfifo_get(spi_device).data; + + if (spi_device_upload_status_get(spi_device).addrfifo_notempty) { + command->has_address = true; + command->address = spi_device_upload_addrfifo_get(spi_device); + } else { + /* no address */ + command->has_address = false; + command->address = 0u; + } + command->payload_byte_count = spi_device_upload_status2_get(spi_device).payload_depth; + + return spi_device_status_ready; } -uint32_t spi_device_flash_payload_buffer_read(spi_device_t spi_device, uint32_t offset) +enum spi_device_status +spi_device_software_command_get(spi_device_t spi_device, spi_device_software_command *command) { - // Ignore unaligned reads - if ((offset % sizeof(uint32_t)) != 0) { - return 0; - } - // Ignore reads outside payload buffer - if (offset >= SPI_DEVICE_PAYLOAD_AREA_NUM_BYTES) { - return 0; - } - return DEV_READ( - spi_device + SPI_DEVICE_INGRESS_BUFFER_OFFSET + SPI_DEVICE_PAYLOAD_AREA_OFFSET + offset); -} - -void spi_device_sfdp_table_init(spi_device_t spi_device) -{ - // Prepare pointer to SFDP area in egress buffer - void *buf_ptr = spi_device + SPI_DEVICE_EGRESS_BUFFER_OFFSET + SPI_DEVICE_SFDP_AREA_OFFSET; - - // clang-format off - - // Write SFDP header 1st word - // [31: 0]: SFDP signature that indicates the presence of a SFDP table (JESD216F 6.2.1) - DEV_WRITE(buf_ptr, SPI_DEVICE_SFDP_SIGNATURE); - buf_ptr += sizeof(uint32_t); - - // Write SFDP header 2nd word - // [ 7: 0]: SFDP minor revision number (JESD216F 6.2.2) - // [15: 8]: SFDP major revision number (JESD216F 6.2.2) - // [23:16]: Number of parameter headers, zero-based (JESD216F 6.2.2) - // [31:24]: SFDP access protocol (JESD216F 6.2.3) - DEV_WRITE(buf_ptr, - reg32_field( 7, 0, SPI_DEVICE_SFDP_MINOR_REVISION) | - reg32_field(15, 8, SPI_DEVICE_SFDP_MAJOR_REVISION) | - reg32_field(23, 16, SPI_DEVICE_SFDP_PARAM_COUNT) | - reg32_field(31, 24, SPI_DEVICE_SFDP_ACCESS_PROTOCOL)); - buf_ptr += sizeof(uint32_t); - - // Write Basic Flash Parameters Table (BFPT) parameter header 1st word - // [ 7: 0]: LSB of the parameter ID that indicates parameter table ownership and type (JESD216F 6.3.1, 6.3.3) - // [15: 8]: Parameter table minor revision number (JESD216F 6.3.1) - // [23:16]: Parameter table major revision number (JESD216F 6.3.1) - // [31:24]: Length of the parameter table in words, one-based (JESD216F 6.3.1) - DEV_WRITE(buf_ptr, - reg32_field( 7, 0, SPI_DEVICE_BFPT_PARAM_ID_LSB) | - reg32_field(15, 8, SPI_DEVICE_BFPT_MINOR_REVISION) | - reg32_field(23, 16, SPI_DEVICE_BFPT_MAJOR_REVISION) | - reg32_field(31, 24, SPI_DEVICE_BFPT_NUM_WORDS)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT parameter header 2nd word - // [23: 0]: Word-aligned byte offset of the corresponding parameter table from the start of the SFDP table (JESD216F 6.3.2) - // [31:24]: MSB of the parameter ID that indicates parameter table ownership and type (JESD216F 6.3.2, 6.3.3) - DEV_WRITE(buf_ptr, - reg32_field(23, 0, 4) | - reg32_field(31, 24, SPI_DEVICE_BFPT_PARAM_ID_MSB)); - buf_ptr += sizeof(uint32_t); - - // Note: Words below are numbered starting from 1 to match JESD216F. Some fields - // that are not supported by OpenTitan are merged for the sake of conciseness. - - // Write BFPT 1st word - // [31:23]: Unused (all 1s) - // [22:19]: (1S-1S-4S) (1S-4S-4S) (1S-2S-2S) DTR Clock (not supported: 0x0) - // [18:17]: Address bytes (3-byte only addressing: 0x0) - // [16:16]: (1S-1S-2S) (not supported: 0x0) - // [15: 8]: 4 KiB erase instruction (0x20) - // [ 7: 5]: Unused (all 1s) - // [ 4: 4]: Write enable instruction (use 0x06 for WREN: 0x1) - // [ 3: 3]: Volatile block protect bits (solely volatile: 0x1) - // [ 2: 2]: Write granularity (buffer >= 64 B: 0x1) - // [ 1: 0]: Block/sector erase sizes (uniform 4 KiB erase: 0x1) - DEV_WRITE(buf_ptr, - reg32_field(31, 23, UINT32_MAX) | - reg32_field(22, 19, 0) | - reg32_field(18, 17, 0x0) | - reg32_field(16, 16, 0) | - reg32_field(15, 8, SPI_DEVICE_OPCODE_SECTOR_ERASE) | - reg32_field( 7, 5, UINT32_MAX) | - reg32_field( 4, 4, 0x1) | - reg32_field( 3, 3, 0x1) | - reg32_field( 2, 2, 0x1) | - reg32_field( 1, 0, 0x1)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 2nd Word - // [31:31]: Density greater than 2 Gib (0x0) - // [30: 0]: Flash memory density in bits, zero-based (0x7fffff) - DEV_WRITE(buf_ptr, - reg32_field(31, 31, 0x0) | - reg32_field(30, 0, MOCHA_SPI_DEVICE_DENSITY_BITS - 1)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 3rd Word - // [31: 0]: Fast read (1S-4S-4S) (1S-1S-4S) (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 4th Word - // [31: 0]: Fast read (1S-1S-2S) (1S-2S-2S) (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 5th Word - // [31: 5]: Reserved (all 1s) - // [ 4: 4]: Fast read (4S-4S-4S) support (not supported, 0x0) - // [ 3: 1]: Reserved (all 1s) - // [ 0: 0]: Fast read (2S-2S-2S) support (not supported, 0x0) - DEV_WRITE(buf_ptr, - reg32_field(31, 5, UINT32_MAX) | - reg32_field( 4, 4, 0x0) | - reg32_field( 3, 1, UINT32_MAX) | - reg32_field( 0, 0, 0x0)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 6th Word - // [31:16]: Fast read (2S-2S-2S) (not supported, 0x0) - // [15: 0]: Reserved (all 1s) - DEV_WRITE(buf_ptr, - reg32_field(31, 16, 0x0) | - reg32_field(15, 0, UINT32_MAX)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 7th Word - // [31:16]: Fast read (4S-4S-4S) (not supported, 0x0) - // [15: 0]: Reserved (all 1s) - DEV_WRITE(buf_ptr, - reg32_field(31, 16, 0x0) | - reg32_field(15, 0, UINT32_MAX)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 8th Word - // [31:16]: Erase type 2 instruction and size (not supported, 0x0) - // [15: 8]: Erase type 1 instruction (0x20) - // [ 7: 0]: Erase type 1 size (4 KiB, 2^N bytes, N = 0x0c) - DEV_WRITE(buf_ptr, - reg32_field(31, 16, 0x0) | - reg32_field(15, 8, SPI_DEVICE_OPCODE_SECTOR_ERASE) | - reg32_field( 7, 0, 0x0C)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 9th Word - // [31: 0]: Erase type 4 and 3 (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 10th Word - // [31:11]: Erase 4,3,2 typical time (not supported, 0x0) - // [10: 9]: Erase type 1 time unit (16 ms, 0x1) - // [ 8: 4]: Erase type 1 time count, zero-based (0x8) - // formula: (count + 1) * unit - // (8 + 1) * 16 ms = 144 ms - // [ 3: 0]: Max erase time multiplier, zero-based (0x6) - // formula: 2 * (multiplier + 1) * erase_time - DEV_WRITE(buf_ptr, - reg32_field(31, 11, 0x0) | - reg32_field(10, 9, 0x1) | - reg32_field( 8, 4, 0x8) | - reg32_field( 3, 0, 0x6)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 11th Word - // [31:31]: Reserved (all 1s) - // [30:29]: Chip erase time units (16 ms, 0x0) - // [28:24]: Chip erase time count, zero-based (0xb) - // formula: (count + 1) * unit - // (11 + 1) * 16 ms = 192 ms - // [23:23]: Additional byte program time units (8 us, 0x1) - // [22:19]: Additional byte program time count, zero-based (0x5) - // formula: (count + 1) * unit - // (5 + 1) * 8 us = 48 us - // [18:18]: First byte program time unit (8 us, 0x1) - // [17:14]: First byte program time count, zero-based (0x5) - // formula: (count + 1) * unit - // (5 + 1) * 8 us = 48 us - // [13:13]: Page program time unit (64 us, 0x1) - // [12: 8]: Page program time count, zero-based (0xb) - // formula: (count + 1) * unit - // (11 + 1) * 64 us = 768 us - // [ 7: 4]: Page size, 2^N (0x8) - // [ 3: 0]: Max program time multiplier, zero-based (0x0) - // formula: 2 * (multiplier + 1) * program_time - DEV_WRITE(buf_ptr, - reg32_field(31, 31, UINT32_MAX) | - reg32_field(30, 29, 0x0) | - reg32_field(28, 24, 0xB) | - reg32_field(23, 23, 0x1) | - reg32_field(22, 19, 0x5) | - reg32_field(18, 18, 0x1) | - reg32_field(17, 14, 0x5) | - reg32_field(13, 13, 0x1) | - reg32_field(12, 8, 0xB) | - reg32_field( 7, 4, 0x8) | - reg32_field( 3, 0, 0x0)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 12th Word - // [31:31]: Suspend/Resume supported (not supported, 0x1) - // [30: 9]: Suspend/Resume latencies for erase & program (not supported, 0x0) - // [ 8: 8]: Reserved (all 1s) - // [ 7: 0]: Prohibited ops during suspend (not supported, 0x0) - DEV_WRITE(buf_ptr, - reg32_field(31, 31, 0x1) | - reg32_field(30, 9, 0x0) | - reg32_field( 8, 8, UINT32_MAX) | - reg32_field( 7, 0, 0x0)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 13th Word - // [31: 0]: Erase/program suspend/resume instructions (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 14th Word - // [31:31]: Deep powerdown support (not supported, 0x1) - // [30: 8]: Deep powerdown instructions and delay (not supported, 0x0) - // [ 7: 2]: Busy polling (bit 0 using 0x05 instruction, 0x1) - // [ 1: 0]: Reserved (all 1s) - DEV_WRITE(buf_ptr, - reg32_field(31, 31, 0x1) | - reg32_field(30, 8, 0x0) | - reg32_field( 7, 2, 0x1) | - reg32_field( 1, 0, UINT32_MAX)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 15th Word - // [31:24]: Reserved (all 1s) - // [23: 0]: Hold, QE, (4S-4S-4S), 0-4-4 (not supported, 0x0) - DEV_WRITE(buf_ptr, - reg32_field(31, 24, UINT32_MAX) | - reg32_field(23, 0, 0x0)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 16th Word - // [31:14]: 4-Byte addressing (not supported, 0x0) - // [13: 8]: Soft-reset (0x66/0x99 sequence, 0x10) - // [ 7: 7]: Reserved - // [ 6: 0]: Status register (read-only, 0x0) - DEV_WRITE(buf_ptr, - reg32_field(31, 14, 0x0) | - reg32_field(13, 8, 0x10) | - reg32_field( 7, 7, UINT32_MAX) | - reg32_field( 6, 0, 0x0)); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 17th Word - // [31: 0]: Fast read (1S-8S-8S) (1S-1S-8S) (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 18th Word - // [31, 0]: Data strobe, SPI protocol reset, etc. (not supported, 0x0) - // - // Note: Reserved fields of this word should be 0 (JESD216F 6.4.21). - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 19th Word - // [31, 0]: Octable enable, (8D-8D-8D), 0-8-8 mode (not suported, 0x0) - // - // Note: Reserved fields of this word should be 0 (JESD216F 6.4.22). - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 20th Word - // [31, 0]: Max (8S-8S-8S) (4D-4D-4D) (4S-4S-4S) speed - // (not supported, 0xffffffff) - DEV_WRITE(buf_ptr, UINT32_MAX); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 21st Word - // [31, 0]: Fast read support for various modes (not supported, 0x0) - // - // Note: Reserved fields of this word should be 0 (JESD216F 6.4.24). - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 22nd Word - // [31, 0]: Fast read (1S-1D-1D) (1S-2D-2D) (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // Write BFPT 23rd Word - // [31, 0]: Fast read (1S-4D-4D) (4S-2D-2D) (not supported, 0x0) - DEV_WRITE(buf_ptr, 0); - buf_ptr += sizeof(uint32_t); - - // clang-format on - - // Fill the remaining space with `0xff`s. - while (buf_ptr < spi_device + SPI_DEVICE_EGRESS_BUFFER_OFFSET + SPI_DEVICE_SFDP_AREA_OFFSET + - SPI_DEVICE_SFDP_AREA_NUM_BYTES) { - DEV_WRITE(buf_ptr, UINT32_MAX); - buf_ptr += sizeof(uint32_t); + /* busy poll for a software-handled command */ + while ( + !spi_device_interrupt_all_pending(spi_device, spi_device_intr_upload_cmdfifo_not_empty)) { } + + return spi_device_software_command_get_non_blocking(spi_device, command); } -void spi_device_init(spi_device_t spi_device) +void spi_device_flash_payload_buffer_copy_bytes(spi_device_t spi_device, size_t num_bytes, + uint32_t *dest) { - spi_device_4b_addr_mode_enable_set(spi_device, false); - spi_device_jedec_cc_set(spi_device, MOCHA_SPI_DEVICE_JEDEC_CC, MOCHA_SPI_DEVICE_JEDEC_CC_COUNT); - spi_device_jedec_id_set(spi_device, MOCHA_SPI_DEVICE_ROM_BOOTSTRAP, MOCHA_SPI_DEVICE_CHIP_REV, - MOCHA_SPI_DEVICE_CHIP_GEN, MOCHA_SPI_DEVICE_DENSITY_BYTES_LOG2, - MOCHA_SPI_DEVICE_MANUFACTURER_ID); - spi_device_sfdp_table_init(spi_device); - spi_device_flash_status_set(spi_device, 0); - - // Configure commands - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_0_REG, SPI_DEVICE_OPCODE_READ_STATUS, - false, 0, false); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_3_REG, SPI_DEVICE_OPCODE_READ_JEDEC_ID, - false, 0, false); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_4_REG, SPI_DEVICE_OPCODE_READ_SFDP, - true, 8, false); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_5_REG, SPI_DEVICE_OPCODE_READ_DATA, - true, 0, false); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_11_REG, SPI_DEVICE_OPCODE_CHIP_ERASE, - false, 0, true); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_12_REG, SPI_DEVICE_OPCODE_SECTOR_ERASE, - true, 0, true); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_13_REG, SPI_DEVICE_OPCODE_PAGE_PROGRAM, - true, 0, true); - spi_device_cmd_info_set(spi_device, SPI_DEVICE_CMD_INFO_14_REG, SPI_DEVICE_OPCODE_RESET, false, - 0, true); - // Configure WRITE_ENABLE and WRITE_DISABLE commands - spi_device_cmd_info_write_enable_set(spi_device, SPI_DEVICE_OPCODE_WRITE_ENABLE); - spi_device_cmd_info_write_disable_set(spi_device, SPI_DEVICE_OPCODE_WRITE_DISABLE); -} - -spi_device_cmd_t spi_device_cmd_get_non_blocking(spi_device_t spi_device) -{ - // Set return defaults - spi_device_cmd_t cmd = { .status = spi_device_status_ready, - .opcode = 0x0, - .address = UINT32_MAX, - .payload_byte_count = 0 }; - - // Check for software-handled command - if (!spi_device_interrupt_is_pending(spi_device, SPI_DEVICE_INTR_UPLOAD_CMDFIFO_NOT_EMPTY)) { - cmd.status = spi_device_status_empty; - return cmd; + size_t round_up_bytes = (num_bytes + 0b11u) & (~0b11u); + size_t num_words = round_up_bytes / sizeof(uint32_t); + /* limit to length of payload fifo */ + num_words = num_words > spi_device_ingress_buffer_size_payload_fifo ? + spi_device_ingress_buffer_size_payload_fifo : + num_words; + for (size_t i = 0; i < num_words; i++) { + dest[i] = VOLATILE_READ( + spi_device->ingress_buffer[spi_device_ingress_buffer_offset_payload_fifo + i]); } +} - // Clear interrupt - spi_device_interrupt_clear(spi_device, SPI_DEVICE_INTR_UPLOAD_CMDFIFO_NOT_EMPTY); +bool spi_device_flash_payload_buffer_read_word(spi_device_t spi_device, size_t word_index, + uint32_t *word) +{ + if (word_index >= spi_device_ingress_buffer_size_payload_fifo) { + return false; + } + *word = VOLATILE_READ( + spi_device->ingress_buffer[spi_device_ingress_buffer_offset_payload_fifo + word_index]); + return true; +} - // Check for payload overflow - if (spi_device_interrupt_is_pending(spi_device, SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW)) { - cmd.status = spi_device_status_overflow; - spi_device_interrupt_clear(spi_device, SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW); - return cmd; +bool spi_device_flash_payload_buffer_read_byte(spi_device_t spi_device, size_t byte_index, + uint8_t *byte) +{ + size_t word_index = byte_index >> 4u; + uint32_t word; + if (!spi_device_flash_payload_buffer_read_word(spi_device, word_index, &word)) { + return false; } - // Get opcode - cmd.opcode = (uint8_t)spi_device_upload_cmdfifo_read(spi_device); + *byte = (uint8_t)(word >> ((byte_index % 4u) * 8u)); + return true; +} - // Get address - if ((spi_device_upload_status_get(spi_device) & - SPI_DEVICE_UPLOAD_STATUS_ADDRFIFO_NOTEMPTY_MASK) != 0) { - cmd.address = spi_device_upload_addrfifo_read(spi_device); - } else { - // No address - cmd.address = UINT32_MAX; +bool spi_device_flash_read_buffer_write_word(spi_device_t spi_device, size_t word_index, + uint32_t word) +{ + if (word_index >= spi_device_egress_buffer_size_read) { + return false; } - // Get payload size - cmd.payload_byte_count = (uint16_t)(spi_device_upload_status2_get(spi_device) & - SPI_DEVICE_UPLOAD_STATUS2_PAYLOAD_DEPTH_MASK); - return cmd; + VOLATILE_WRITE(spi_device->egress_buffer[spi_device_egress_buffer_offset_read + word_index], + word); + return true; } -spi_device_cmd_t spi_device_cmd_get(spi_device_t spi_device) +bool spi_device_flash_read_buffer_write_byte(spi_device_t spi_device, size_t byte_index, + uint8_t byte) { - // Wait for software-handled command - while (!spi_device_interrupt_is_pending(spi_device, SPI_DEVICE_INTR_UPLOAD_CMDFIFO_NOT_EMPTY)) { + size_t word_index = byte_index >> 4u; + if (word_index >= spi_device_egress_buffer_size_read) { + return false; } - return spi_device_cmd_get_non_blocking(spi_device); + uint32_t word = + VOLATILE_READ(spi_device->egress_buffer[spi_device_egress_buffer_offset_read + word_index]); + size_t shift = (byte_index % 4u) * 8u; + word &= ~(0xffu << shift); + word |= ((uint32_t)byte) << shift; + VOLATILE_WRITE(spi_device->egress_buffer[spi_device_egress_buffer_offset_read + word_index], + word); + return true; } diff --git a/sw/device/lib/hal/spi_device.h b/sw/device/lib/hal/spi_device.h index 64d102a4d..9f870f7d7 100644 --- a/sw/device/lib/hal/spi_device.h +++ b/sw/device/lib/hal/spi_device.h @@ -4,215 +4,184 @@ #pragma once -#include "hal/mmio.h" +#include "autogen/spi_device.h" #include +#include #include -#define SPI_DEVICE_INTR_STATE_REG (0x0) -#define SPI_DEVICE_INTR_ENABLE_REG (0x4) -#define SPI_DEVICE_INTR_TEST_REG (0x8) -#define SPI_DEVICE_INTR_UPLOAD_CMDFIFO_NOT_EMPTY (0) -#define SPI_DEVICE_INTR_UPLOAD_PAYLOAD_NOT_EMPTY (1) -#define SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW (2) -#define SPI_DEVICE_INTR_READBUF_WATERMARK (3) -#define SPI_DEVICE_INTR_READBUF_FLIP (4) -#define SPI_DEVICE_INTR_TPM_HEADER_NOT_EMPTY (5) -#define SPI_DEVICE_INTR_TPM_RDFIFO_CMD_END (6) -#define SPI_DEVICE_INTR_TPM_RDFIFO_DROP (7) -#define SPI_DEVICE_MAX_INTR (7) - -#define SPI_DEVICE_CTRL_REG (0x10) -#define SPI_DEVICE_CTRL_MODE_OFFSET (4) -#define SPI_DEVICE_CTRL_MODE_MASK (0x1 << 4) - -#define SPI_DEVICE_ADDR_MODE_REG (0x20) -#define SPI_DEVICE_ADDR_MODE_4B_EN_MASK (0x1) -#define SPI_DEVICE_ADDR_MODE_PENDING_MASK (0x70000000) - -#define SPI_DEVICE_FLASH_STATUS_REG (0x28) -#define SPI_DEVICE_FLASH_STATUS_BUSY_MASK (0x1) -#define SPI_DEVICE_FLASH_STATUS_WEL_MASK (0x2) - -#define SPI_DEVICE_JEDEC_CC_REG (0x2C) -#define SPI_DEVICE_JEDEC_CC (0) -#define SPI_DEVICE_JEDEC_NUM_CC (8) - -#define SPI_DEVICE_JEDEC_ID_REG (0x30) -#define SPI_DEVICE_CHIP_REV (0) -#define SPI_DEVICE_CHIP_REV_MASK (0x7) -#define SPI_DEVICE_ROM_BOOTSTRAP (3) -#define SPI_DEVICE_CHIP_GEN (4) -#define SPI_DEVICE_CHIP_GEN_MASK (0xF) -#define SPI_DEVICE_DENSITY (8) -#define SPI_DEVICE_MANUFACTURER_ID (16) - -#define SPI_DEVICE_MAILBOX_ADDR_REG (0x38) - -#define SPI_DEVICE_UPLOAD_STATUS_REG (0x3C) -#define SPI_DEVICE_UPLOAD_STATUS_CMDFIFO_NOTEMPTY_MASK (0x80) -#define SPI_DEVICE_UPLOAD_STATUS_ADDRFIFO_NOTEMPTY_MASK (0x8000) - -#define SPI_DEVICE_UPLOAD_STATUS2_REG (0x40) -#define SPI_DEVICE_UPLOAD_STATUS2_PAYLOAD_DEPTH_MASK (0x1FF) - -#define SPI_DEVICE_UPLOAD_CMDFIFO_REG (0x44) -#define SPI_DEVICE_UPLOAD_ADDRFIFO_REG (0x48) - -#define SPI_DEVICE_CMD_FILTER_0_REG (0x4C) -#define SPI_DEVICE_CMD_FILTER_1_REG (0x50) -#define SPI_DEVICE_CMD_FILTER_2_REG (0x54) -#define SPI_DEVICE_CMD_FILTER_3_REG (0x58) -#define SPI_DEVICE_CMD_FILTER_4_REG (0x5C) -#define SPI_DEVICE_CMD_FILTER_5_REG (0x60) -#define SPI_DEVICE_CMD_FILTER_6_REG (0x64) -#define SPI_DEVICE_CMD_FILTER_7_REG (0x68) - -#define SPI_DEVICE_CMD_INFO_0_REG (0x7C) -#define SPI_DEVICE_CMD_INFO_1_REG (0x80) -#define SPI_DEVICE_CMD_INFO_2_REG (0x84) -#define SPI_DEVICE_CMD_INFO_3_REG (0x88) -#define SPI_DEVICE_CMD_INFO_4_REG (0x8C) -#define SPI_DEVICE_CMD_INFO_5_REG (0x90) -#define SPI_DEVICE_CMD_INFO_6_REG (0x94) -#define SPI_DEVICE_CMD_INFO_7_REG (0x98) -#define SPI_DEVICE_CMD_INFO_8_REG (0x9C) -#define SPI_DEVICE_CMD_INFO_9_REG (0xA0) -#define SPI_DEVICE_CMD_INFO_10_REG (0xA4) -#define SPI_DEVICE_CMD_INFO_11_REG (0xA8) -#define SPI_DEVICE_CMD_INFO_12_REG (0xAC) -#define SPI_DEVICE_CMD_INFO_13_REG (0xB0) -#define SPI_DEVICE_CMD_INFO_14_REG (0xB4) -#define SPI_DEVICE_CMD_INFO_15_REG (0xB8) -#define SPI_DEVICE_CMD_INFO_16_REG (0xBC) -#define SPI_DEVICE_CMD_INFO_17_REG (0xC0) -#define SPI_DEVICE_CMD_INFO_18_REG (0xC4) -#define SPI_DEVICE_CMD_INFO_19_REG (0xC8) -#define SPI_DEVICE_CMD_INFO_20_REG (0xCC) -#define SPI_DEVICE_CMD_INFO_21_REG (0xD0) -#define SPI_DEVICE_CMD_INFO_22_REG (0xD4) -#define SPI_DEVICE_CMD_INFO_23_REG (0xD8) -#define SPI_DEVICE_CMD_OPCODE (0) -#define SPI_DEVICE_CMD_ADDR_MODE (8) -#define SPI_DEVICE_CMD_ADDR_MODE_MASK (0x3) -#define SPI_DEVICE_CMD_ADDR_MODE_ADDR_DISABLED (0x0) -#define SPI_DEVICE_CMD_ADDR_MODE_ADDR_CFG (0x1) -#define SPI_DEVICE_CMD_ADDR_MODE_ADDR_3B (0x2) -#define SPI_DEVICE_CMD_ADDR_MODE_ADDR_4B (0x3) -#define SPI_DEVICE_CMD_DUMMY_SIZE (12) -#define SPI_DEVICE_CMD_DUMMY_SIZE_MASK (0x7) -#define SPI_DEVICE_CMD_DUMMY_EN (15) -#define SPI_DEVICE_CMD_UPLOAD (24) -#define SPI_DEVICE_CMD_BUSY (25) -#define SPI_DEVICE_CMD_VALID (31) - -#define SPI_DEVICE_CMD_INFO_WREN_REG (0xE4) -#define SPI_DEVICE_CMD_INFO_WRDI_REG (0xE8) - -#define SPI_DEVICE_EGRESS_BUFFER_OFFSET (0x1000) -#define SPI_DEVICE_READ_BUFFER_OFFSET (0x0) -#define SPI_DEVICE_READ_BUFFER_NUM_BYTES (2048) -#define SPI_DEVICE_SFDP_AREA_OFFSET (0xC00) -#define SPI_DEVICE_SFDP_AREA_NUM_BYTES (256) - -#define SPI_DEVICE_INGRESS_BUFFER_OFFSET (0x1E00) -#define SPI_DEVICE_PAYLOAD_AREA_OFFSET (0x0) -#define SPI_DEVICE_PAYLOAD_AREA_NUM_BYTES (256) - -#define SPI_DEVICE_SFDP_SIGNATURE (0x50444653) -#define SPI_DEVICE_SFDP_MINOR_REVISION (0x0A) -#define SPI_DEVICE_SFDP_MAJOR_REVISION (0x01) -#define SPI_DEVICE_SFDP_PARAM_COUNT (0) -// 3-byte addressing for SFDP_READ command, 8 wait states (JESD216F 6.2.3) -#define SPI_DEVICE_SFDP_ACCESS_PROTOCOL (0xFF) - -#define SPI_DEVICE_BFPT_MINOR_REVISION (0x07) -#define SPI_DEVICE_BFPT_MAJOR_REVISION (0x01) -#define SPI_DEVICE_BFPT_PARAM_ID_LSB (0x00) -#define SPI_DEVICE_BFPT_PARAM_ID_MSB (0xFF) -#define SPI_DEVICE_BFPT_NUM_WORDS (23) - -#define SPI_DEVICE_OPCODE_PAGE_PROGRAM (0x02) -#define SPI_DEVICE_OPCODE_READ_DATA (0x03) -#define SPI_DEVICE_OPCODE_WRITE_DISABLE (0x04) -#define SPI_DEVICE_OPCODE_READ_STATUS (0x05) -#define SPI_DEVICE_OPCODE_WRITE_ENABLE (0x06) -#define SPI_DEVICE_OPCODE_SECTOR_ERASE (0x20) -#define SPI_DEVICE_OPCODE_READ_SFDP (0x5A) -#define SPI_DEVICE_OPCODE_RESET (0x99) -#define SPI_DEVICE_OPCODE_READ_JEDEC_ID (0x9F) -#define SPI_DEVICE_OPCODE_CHIP_ERASE (0xC7) - -#define MOCHA_SPI_DEVICE_JEDEC_CC (0x7F) -#define MOCHA_SPI_DEVICE_JEDEC_CC_COUNT (0) -#define MOCHA_SPI_DEVICE_ROM_BOOTSTRAP (true) -#define MOCHA_SPI_DEVICE_CHIP_REV (0) -#define MOCHA_SPI_DEVICE_CHIP_GEN (0) -#define MOCHA_SPI_DEVICE_DENSITY_BITS ((1 << 20) * 8) -#define MOCHA_SPI_DEVICE_DENSITY_BYTES_LOG2 (20) -#define MOCHA_SPI_DEVICE_MANUFACTURER_ID (0xef) - -typedef void *spi_device_t; - -typedef enum spi_device_status { +enum spi_device_flash_mode : uint32_t { + spi_device_flash_mode_disabled = 0x0u, + spi_device_flash_mode_flash = 0x1u, + spi_device_flash_mode_passthrough = 0x2u, +}; + +enum spi_device_addr_mode : uint32_t { + spi_device_addr_mode_disabled = 0x0u, + spi_device_addr_mode_cfg = 0x1u, + spi_device_addr_mode_3b = 0x2u, + spi_device_addr_mode_4b = 0x3u, +}; + +enum spi_device_opcode : uint8_t { + spi_device_opcode_page_program = 0x02u, + spi_device_opcode_read = 0x03u, + spi_device_opcode_wrdi = 0x04u, + spi_device_opcode_read_status = 0x05u, + spi_device_opcode_wren = 0x06u, + spi_device_opcode_sector_erase = 0x20u, + spi_device_opcode_read_sfdp = 0x5au, + spi_device_opcode_reset = 0x99u, + spi_device_opcode_read_jedec = 0x9fu, + spi_device_opcode_chip_erase = 0xc7u, +}; + +enum spi_device_egress_buffer_size : size_t { + spi_device_egress_buffer_size_read = 512u, + spi_device_egress_buffer_size_mailbox = 256u, + spi_device_egress_buffer_size_sfdp = 64u, + spi_device_egress_buffer_size_tpm_read_fifo = 16u, +}; + +_Static_assert((spi_device_egress_buffer_size_read + spi_device_egress_buffer_size_mailbox + + spi_device_egress_buffer_size_sfdp + spi_device_egress_buffer_size_tpm_read_fifo) == + sizeof(((spi_device_t)0)->egress_buffer) / sizeof(uint32_t), + "incorrect spi_device egress_buffer sizes"); + +enum spi_device_egress_buffer_offset : size_t { + spi_device_egress_buffer_offset_read = 0u, + spi_device_egress_buffer_offset_mailbox = + spi_device_egress_buffer_offset_read + spi_device_egress_buffer_size_read, + spi_device_egress_buffer_offset_sfdp = + spi_device_egress_buffer_offset_mailbox + spi_device_egress_buffer_size_mailbox, + spi_device_egress_buffer_offset_tpm_read_fifo = + spi_device_egress_buffer_offset_sfdp + spi_device_egress_buffer_size_sfdp, +}; + +_Static_assert(spi_device_egress_buffer_offset_read == 0ul, + "incorrect spi_device egress_buffer read offset"); +_Static_assert(spi_device_egress_buffer_offset_mailbox == 512ul, + "incorrect spi_device egress_buffer mailbox offset"); +_Static_assert(spi_device_egress_buffer_offset_sfdp == 768ul, + "incorrect spi_device egress_buffer sfdp offset"); +_Static_assert(spi_device_egress_buffer_offset_tpm_read_fifo == 832ul, + "incorrect spi_device egress_buffer tpm_read_fifo offset"); + +enum spi_device_ingress_buffer_size : size_t { + spi_device_ingress_buffer_size_payload_fifo = 64u, + spi_device_ingress_buffer_size_cmd_fifo = 16u, + spi_device_ingress_buffer_size_addr_fifo = 16u, + spi_device_ingress_buffer_size_tpm_write_fifo = 16u, +}; + +_Static_assert((spi_device_ingress_buffer_size_payload_fifo + + spi_device_ingress_buffer_size_cmd_fifo + spi_device_ingress_buffer_size_addr_fifo + + spi_device_ingress_buffer_size_tpm_write_fifo) == + sizeof(((spi_device_t)0)->ingress_buffer) / sizeof(uint32_t), + "incorrect spi_device ingress_buffer sizes"); + +enum spi_device_ingress_buffer_offset : size_t { + spi_device_ingress_buffer_offset_payload_fifo = 0u, + spi_device_ingress_buffer_offset_cmd_fifo = + spi_device_ingress_buffer_offset_payload_fifo + spi_device_ingress_buffer_size_payload_fifo, + spi_device_ingress_buffer_offset_addr_fifo = + spi_device_ingress_buffer_offset_cmd_fifo + spi_device_ingress_buffer_size_cmd_fifo, + spi_device_ingress_buffer_offset_tpm_write_fifo = + spi_device_ingress_buffer_offset_addr_fifo + spi_device_ingress_buffer_size_addr_fifo, +}; + +_Static_assert(spi_device_ingress_buffer_offset_payload_fifo == 0ul, + "incorrect spi_device ingress_buffer payload_fifo offset"); +_Static_assert(spi_device_ingress_buffer_offset_cmd_fifo == 64ul, + "incorrect spi_device ingress_buffer cmd_fifo offset"); +_Static_assert(spi_device_ingress_buffer_offset_addr_fifo == 80ul, + "incorrect spi_device ingress_buffer addr_fifo offset"); +_Static_assert(spi_device_ingress_buffer_offset_tpm_write_fifo == 96ul, + "incorrect spi_device ingress_buffer tpm_read_fifo offset"); + +enum spi_device_status { spi_device_status_ready = 0, spi_device_status_empty = 1, spi_device_status_overflow = 2, -} spi_device_status_t; +}; -typedef struct spi_device_cmd { - spi_device_status_t status; +typedef struct { + enum spi_device_status status; + bool has_address; uint8_t opcode; uint16_t payload_byte_count; uint32_t address; -} spi_device_cmd_t; +} spi_device_software_command; -bool spi_device_interrupt_is_pending(spi_device_t spi_device, uint8_t intr_id); -void spi_device_interrupt_clear(spi_device_t spi_device, uint8_t intr_id); +/* initialisation */ +void spi_device_init(spi_device_t spi_device); +void spi_device_flash_mode_set(spi_device_t spi_device, enum spi_device_flash_mode mode); + +/* interrupts */ +spi_device_intr spi_device_interrupt_enable_get(spi_device_t spi_device); +void spi_device_interrupt_enable_set(spi_device_t spi_device, spi_device_intr intrs); +void spi_device_interrupt_enable(spi_device_t spi_device, spi_device_intr intrs); +void spi_device_interrupt_disable(spi_device_t spi_device, spi_device_intr intrs); void spi_device_interrupt_disable_all(spi_device_t spi_device); -void spi_device_interrupt_enable(spi_device_t spi_device, uint8_t intr_id); -void spi_device_interrupt_disable(spi_device_t spi_device, uint8_t intr_id); -void spi_device_interrupt_trigger(spi_device_t spi_device, uint8_t intr_id); -void spi_device_enable_set(spi_device_t spi_device, bool enable); -void spi_device_4b_addr_mode_enable_set(spi_device_t spi_device, bool enable); +void spi_device_interrupt_force(spi_device_t spi_device, spi_device_intr intrs); +void spi_device_interrupt_clear(spi_device_t spi_device, spi_device_intr intrs); +bool spi_device_interrupt_all_pending(spi_device_t spi_device, spi_device_intr intrs); +bool spi_device_interrupt_any_pending(spi_device_t spi_device, spi_device_intr intrs); + +/* SPI device flash status */ +spi_device_flash_status spi_device_flash_status_get(spi_device_t spi_device); +void spi_device_flash_status_set(spi_device_t spi_device, spi_device_flash_status status); +void spi_device_flash_status_busy_set(spi_device_t spi_device, bool busy); +void spi_device_flash_status_wel_set(spi_device_t spi_device, bool wel); + +/* SPI device command info and address mode */ bool spi_device_4b_addr_mode_enable_get(spi_device_t spi_device); -void spi_device_flash_status_set(spi_device_t spi_device, uint32_t flash_status); -uint32_t spi_device_flash_status_get(spi_device_t spi_device); -void spi_device_jedec_cc_set(spi_device_t spi_device, uint8_t cc, uint8_t num_cc); -uint16_t spi_device_jedec_cc_get(spi_device_t spi_device); -void spi_device_jedec_id_set_raw(spi_device_t spi_device, uint32_t data); -void spi_device_jedec_id_set(spi_device_t spi_device, bool rom_bootstrap, uint8_t chip_rev, - uint8_t chip_gen, uint8_t density, uint8_t manufacturer_id); -uint32_t spi_device_jedec_id_get(spi_device_t spi_device); -void spi_device_mailbox_addr_set(spi_device_t spi_device, uint32_t addr); +void spi_device_4b_addr_mode_enable_set(spi_device_t spi_device, bool enable); +void spi_device_4b_addr_mode_enable_set_unchecked(spi_device_t spi_device, bool enable); +spi_device_cmd_info spi_device_cmd_info_get(spi_device_t spi_device, size_t index); +void spi_device_cmd_info_set(spi_device_t spi_device, spi_device_cmd_info cmd_info, size_t index); +spi_device_cmd_info_wren spi_device_cmd_info_wren_get(spi_device_t spi_device); +void spi_device_cmd_info_wren_set(spi_device_t spi_device, uint8_t opcode, bool valid); +spi_device_cmd_info_wrdi spi_device_cmd_info_wrdi_get(spi_device_t spi_device); +void spi_device_cmd_info_wrdi_set(spi_device_t spi_device, uint8_t opcode, bool valid); + +/* SPI device command filtering */ +uint32_t spi_device_cmd_filter_get(spi_device_t spi_device, size_t index); +void spi_device_cmd_filter_enable_all(spi_device_t spi_device); +void spi_device_cmd_filter_enable_set(spi_device_t spi_device, size_t index, uint32_t filter); +void spi_device_cmd_filter_enable(spi_device_t spi_device, size_t index, uint32_t filter); +void spi_device_cmd_filter_disable(spi_device_t spi_device, size_t index, uint32_t filter); +void spi_device_cmd_filter_disable_all(spi_device_t spi_device); + +/* SPI device JEDEC configuration */ +spi_device_jedec_cc spi_device_jedec_cc_get(spi_device_t spi_device); +void spi_device_jedec_cc_set(spi_device_t spi_device, spi_device_jedec_cc jedec_cc); +spi_device_jedec_id spi_device_jedec_id_get(spi_device_t spi_device); +void spi_device_jedec_id_set(spi_device_t spi_device, spi_device_jedec_id jedec_id); + +/* SPI device sw-handled command FIFOs and mailbox */ +spi_device_upload_status spi_device_upload_status_get(spi_device_t spi_device); +spi_device_upload_status2 spi_device_upload_status2_get(spi_device_t spi_device); +spi_device_upload_cmdfifo spi_device_upload_cmdfifo_get(spi_device_t spi_device); +uint32_t spi_device_upload_addrfifo_get(spi_device_t spi_device); uint32_t spi_device_mailbox_addr_get(spi_device_t spi_device); -uint32_t spi_device_upload_status_get(spi_device_t spi_device); -uint32_t spi_device_upload_status2_get(spi_device_t spi_device); -uint32_t spi_device_upload_cmdfifo_read(spi_device_t spi_device); -uint32_t spi_device_upload_addrfifo_read(spi_device_t spi_device); -void spi_device_cmd_filter_set(spi_device_t spi_device, uint32_t offset, uint32_t data); -uint32_t spi_device_cmd_filter_get(spi_device_t spi_device, uint32_t offset); -void spi_device_cmd_info_set_raw(spi_device_t spi_device, uint32_t offset, uint32_t data); -void spi_device_cmd_info_set(spi_device_t spi_device, uint32_t offset, uint8_t opcode, bool address, - uint8_t dummy_cycles, bool handled_in_sw); -uint32_t spi_device_cmd_info_get(spi_device_t spi_device, uint32_t offset); -void spi_device_cmd_info_write_enable_set_raw(spi_device_t spi_device, uint32_t data); -void spi_device_cmd_info_write_enable_set(spi_device_t spi_device, uint8_t opcode); -uint32_t spi_device_cmd_info_write_enable_get(spi_device_t spi_device); -void spi_device_cmd_info_write_disable_set_raw(spi_device_t spi_device, uint32_t data); -void spi_device_cmd_info_write_disable_set(spi_device_t spi_device, uint8_t opcode); -uint32_t spi_device_cmd_info_write_disable_get(spi_device_t spi_device); -bool spi_device_flash_read_buffer_write(spi_device_t spi_device, uint32_t offset, uint32_t data); -uint32_t spi_device_flash_payload_buffer_read(spi_device_t spi_device, uint32_t offset); - -static inline uint64_t -spi_device_flash_payload_buffer_read64(spi_device_t spi_device, uint32_t offset) -{ - uintptr_t addr = (uintptr_t)spi_device + SPI_DEVICE_INGRESS_BUFFER_OFFSET + - SPI_DEVICE_PAYLOAD_AREA_OFFSET + offset; - return DEV_READ64(addr); -} - -void spi_device_sfdp_table_init(spi_device_t spi_device); -void spi_device_init(spi_device_t spi_device); -spi_device_cmd_t spi_device_cmd_get(spi_device_t spi_device); -spi_device_cmd_t spi_device_cmd_get_non_blocking(spi_device_t spi_device); +void spi_device_mailbox_addr_set(spi_device_t spi_device, uint32_t mailbox_addr); + +/* SPI device uploaded sw-handled command */ +enum spi_device_status +spi_device_software_command_get(spi_device_t spi_device, spi_device_software_command *command); +enum spi_device_status spi_device_software_command_get_non_blocking( + spi_device_t spi_device, spi_device_software_command *command); + +/* SPI device flash payload buffer */ +void spi_device_flash_payload_buffer_copy_bytes(spi_device_t spi_device, size_t num_bytes, + uint32_t *dest); +bool spi_device_flash_payload_buffer_read_word(spi_device_t spi_device, size_t word_index, + uint32_t *word); +bool spi_device_flash_payload_buffer_read_byte(spi_device_t spi_device, size_t byte_index, + uint8_t *byte); + +/* SPI device flash read buffer */ +bool spi_device_flash_read_buffer_write_word(spi_device_t spi_device, size_t word_index, + uint32_t word); +bool spi_device_flash_read_buffer_write_byte(spi_device_t spi_device, size_t byte_index, + uint8_t byte); diff --git a/sw/device/tests/spi_device/flash_mode.c b/sw/device/tests/spi_device/flash_mode.c index f95cac71a..3a1d33ad6 100644 --- a/sw/device/tests/spi_device/flash_mode.c +++ b/sw/device/tests/spi_device/flash_mode.c @@ -11,26 +11,27 @@ bool spi_cmd_poll_test(spi_device_t spi_device, uart_t uart) { - spi_device_cmd_t cmd; - while (1) { - cmd = spi_device_cmd_get(spi_device); - if (cmd.status != 0) { + spi_device_software_command command; + while (true) { + bool success = spi_device_software_command_get(spi_device, &command); + if (!success) { uart_puts(uart, "SPI payload overflow\n"); - spi_device_flash_status_set(spi_device, 0); + spi_device_flash_status status = { 0 }; + spi_device_flash_status_set(spi_device, status); continue; } - switch (cmd.opcode) { - case SPI_DEVICE_OPCODE_CHIP_ERASE: + switch (command.opcode) { + case spi_device_opcode_chip_erase: uart_puts(uart, "SPI CHIP ERASE"); break; - case SPI_DEVICE_OPCODE_SECTOR_ERASE: + case spi_device_opcode_sector_erase: uart_puts(uart, "SPI SECTOR ERASE"); break; - case SPI_DEVICE_OPCODE_PAGE_PROGRAM: + case spi_device_opcode_page_program: uart_puts(uart, "SPI PAGE PROGRAM"); break; - case SPI_DEVICE_OPCODE_RESET: + case spi_device_opcode_reset: uart_puts(uart, "SPI RESET"); break; default: @@ -38,30 +39,31 @@ bool spi_cmd_poll_test(spi_device_t spi_device, uart_t uart) break; } - if (cmd.address != UINT32_MAX) { - uprintf(uart, " addr: 0x%x\n", cmd.address); + if (command.has_address) { + uprintf(uart, " addr: 0x%x\n", command.address); } - if (cmd.payload_byte_count > 0) { - uprintf(uart, "payload bytes: 0x%x\n", cmd.payload_byte_count); - uint32_t payload_word_count = ((uint32_t)cmd.payload_byte_count) / sizeof(uint32_t); - if ((cmd.payload_byte_count % sizeof(uint32_t)) != 0) { - ++payload_word_count; + if (command.payload_byte_count > 0) { + uprintf(uart, "payload bytes: 0x%x\n", command.payload_byte_count); + uint32_t payload_word_count = ((uint32_t)command.payload_byte_count) / sizeof(uint32_t); + if ((command.payload_byte_count % sizeof(uint32_t)) != 0) { + payload_word_count++; } uart_puts(uart, "payload data:\n"); uint32_t word; for (uint32_t i = 0; i < payload_word_count; ++i) { - word = spi_device_flash_payload_buffer_read(spi_device, i * sizeof(uint32_t)); - spi_device_flash_read_buffer_write(spi_device, cmd.address + i * sizeof(uint32_t), - word); - uprintf(uart, "0x%x\n", word); + if (spi_device_flash_payload_buffer_read_word(spi_device, i, &word)) { + uprintf(uart, "0x%x\n", word); + spi_device_flash_read_buffer_write_word(spi_device, command.address + i, word); + } } } uart_puts(uart, "\n"); - spi_device_flash_status_set(spi_device, 0); + spi_device_flash_status status = { 0 }; + spi_device_flash_status_set(spi_device, status); } return true; diff --git a/sw/device/tests/spi_device/smoketest.c b/sw/device/tests/spi_device/smoketest.c index 82633a156..da25ab6ec 100644 --- a/sw/device/tests/spi_device/smoketest.c +++ b/sw/device/tests/spi_device/smoketest.c @@ -8,6 +8,8 @@ #include #include +#define array_len(arr) (sizeof(arr) / sizeof((arr)[0])) + uint64_t csr_mip_get() { uint64_t mip; @@ -15,96 +17,110 @@ uint64_t csr_mip_get() return mip; } -bool cmd_filter_readback_test(spi_device_t spi_device, uint32_t offset) +bool cmd_filter_readback_test(spi_device_t spi_device, size_t index) { - spi_device_cmd_filter_set(spi_device, offset, 0x55555555); - if (spi_device_cmd_filter_get(spi_device, offset) != 0x55555555) { + spi_device_cmd_filter_enable_set(spi_device, index, 0x55555555u); + if (spi_device_cmd_filter_get(spi_device, index) != 0x55555555u) { return false; } - - spi_device_cmd_filter_set(spi_device, offset, 0xAAAAAAAA); - if (spi_device_cmd_filter_get(spi_device, offset) != 0xAAAAAAAA) { + spi_device_cmd_filter_enable_set(spi_device, index, 0xaaaaaaaau); + if (spi_device_cmd_filter_get(spi_device, index) != 0xaaaaaaaau) { return false; } - return true; } -bool cmd_info_readback_test(spi_device_t spi_device, uint32_t offset) +bool cmd_info_readback_test(spi_device_t spi_device, size_t index) { - const uint32_t CMD_INFO_RESET_MASK = 0x83FFFFFF; - spi_device_cmd_info_set_raw(spi_device, offset, 0xAAAAAAAA & CMD_INFO_RESET_MASK); - if ((spi_device_cmd_info_get(spi_device, offset) & CMD_INFO_RESET_MASK) != - (0xAAAAAAAA & CMD_INFO_RESET_MASK)) { - return false; - } - - spi_device_cmd_info_set_raw(spi_device, offset, 0x55555555 & CMD_INFO_RESET_MASK); - if ((spi_device_cmd_info_get(spi_device, offset) & CMD_INFO_RESET_MASK) != - (0x55555555 & CMD_INFO_RESET_MASK)) { + union { + uint32_t raw; + spi_device_cmd_info cmdinfo; + } cmd_info = { .cmdinfo = { + .opcode = 0xffu, + .addr_mode = 0x3u, + .addr_swap_en = true, + .mbyte_en = true, + .dummy_size = 0x7u, + .dummy_en = true, + .payload_en = 0xfu, + .payload_dir = true, + .payload_swap_en = true, + .read_pipeline_mode = 0x3u, + .upload = true, + .busy = true, + .valid = true, + } }; + + union { + uint32_t raw; + spi_device_cmd_info cmdinfo; + } cmd_info_readback; + + spi_device_cmd_info_set(spi_device, cmd_info.cmdinfo, index); + cmd_info_readback.cmdinfo = spi_device_cmd_info_get(spi_device, index); + + if (cmd_info.raw != cmd_info_readback.raw) { return false; } - return true; } bool reg_test(spi_device_t spi_device) { - spi_device_4b_addr_mode_enable_set(spi_device, true); + spi_device_4b_addr_mode_enable_set_unchecked(spi_device, true); if (!spi_device_4b_addr_mode_enable_get(spi_device)) { + uart_puts(mocha_system_uart(), "here\n"); return false; } - spi_device_4b_addr_mode_enable_set(spi_device, false); + spi_device_4b_addr_mode_enable_set_unchecked(spi_device, false); if (spi_device_4b_addr_mode_enable_get(spi_device)) { + uart_puts(mocha_system_uart(), "here\n"); return false; } - spi_device_jedec_cc_set(spi_device, 0xE1, 0x45); - if ((spi_device_jedec_cc_get(spi_device) & 0xFFFF) != 0x45E1) { - return false; - } + spi_device_jedec_cc jedec_cc = { + .cc = 0xe1, + .num_cc = 0x45, + }; + spi_device_jedec_cc_set(spi_device, jedec_cc); - spi_device_jedec_id_set_raw(spi_device, 0x555555); - if ((spi_device_jedec_id_get(spi_device) & 0xFFFFFF) != 0x555555) { + spi_device_jedec_cc jedec_cc_readback = spi_device_jedec_cc_get(spi_device); + if (jedec_cc_readback.cc != jedec_cc.cc || jedec_cc_readback.num_cc != jedec_cc.num_cc) { return false; } - spi_device_jedec_id_set_raw(spi_device, 0xAAAAAA); - if ((spi_device_jedec_id_get(spi_device) & 0xFFFFFF) != 0xAAAAAA) { + spi_device_jedec_id jedec_id = { + .id = 0x5555, + .mf = 0x55, + }; + spi_device_jedec_id_set(spi_device, jedec_id); + + spi_device_jedec_id jedec_id_readback = spi_device_jedec_id_get(spi_device); + if (jedec_id_readback.id != jedec_id.id || jedec_id_readback.mf != jedec_id.mf) { return false; } - spi_device_mailbox_addr_set(spi_device, 0x5555AAAA); - if (spi_device_mailbox_addr_get(spi_device) != 0x5555AAAA) { + spi_device_mailbox_addr_set(spi_device, 0x5555aaaau); + if (spi_device_mailbox_addr_get(spi_device) != 0x5555aaaau) { return false; } - spi_device_mailbox_addr_set(spi_device, 0xAAAA5555); - if (spi_device_mailbox_addr_get(spi_device) != 0xAAAA5555) { + spi_device_mailbox_addr_set(spi_device, 0xaaaa5555u); + if (spi_device_mailbox_addr_get(spi_device) != 0xaaaa5555u) { return false; } - if (!(cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_0_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_1_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_2_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_3_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_4_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_5_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_6_REG) && - cmd_filter_readback_test(spi_device, SPI_DEVICE_CMD_FILTER_7_REG))) { - return false; + for (size_t i = 0; i < 8; i++) { + if (!cmd_filter_readback_test(spi_device, i)) { + return false; + } } - if (!(cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_0_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_1_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_2_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_3_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_20_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_21_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_22_REG) && - cmd_info_readback_test(spi_device, SPI_DEVICE_CMD_INFO_23_REG))) { - return false; + for (size_t i = 0; i < array_len(spi_device->cmd_info); i++) { + if (!cmd_info_readback_test(spi_device, i)) { + return false; + } } return true; @@ -123,7 +139,7 @@ bool machine_irq_test(spi_device_t spi_device, plic_t plic) plic_machine_priority_threshold_set(plic, 0); spi_device_interrupt_disable_all(spi_device); - spi_device_interrupt_enable(spi_device, SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW); + spi_device_interrupt_enable(spi_device, spi_device_intr_upload_payload_overflow); plic_machine_interrupt_enable(plic, SPI_DEVICE_INTR_ID); @@ -132,7 +148,7 @@ bool machine_irq_test(spi_device_t spi_device, plic_t plic) return false; } - spi_device_interrupt_trigger(spi_device, SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW); + spi_device_interrupt_force(spi_device, spi_device_intr_upload_payload_overflow); // Check that mip MEIP is set // Retry to give time for mip to be updated @@ -144,7 +160,7 @@ bool machine_irq_test(spi_device_t spi_device, plic_t plic) } intr_id = plic_machine_interrupt_claim(plic); - spi_device_interrupt_clear(spi_device, SPI_DEVICE_INTR_UPLOAD_PAYLOAD_OVERFLOW); + spi_device_interrupt_clear(spi_device, spi_device_intr_upload_payload_overflow); plic_machine_interrupt_complete(plic, intr_id); // Check that mip MEIP is clear @@ -168,7 +184,7 @@ bool supervisor_irq_test(spi_device_t spi_device, plic_t plic) plic_supervisor_priority_threshold_set(plic, 0); spi_device_interrupt_disable_all(spi_device); - spi_device_interrupt_enable(spi_device, SPI_DEVICE_INTR_READBUF_FLIP); + spi_device_interrupt_enable(spi_device, spi_device_intr_readbuf_flip); plic_supervisor_interrupt_enable(plic, SPI_DEVICE_INTR_ID); @@ -177,7 +193,7 @@ bool supervisor_irq_test(spi_device_t spi_device, plic_t plic) return false; } - spi_device_interrupt_trigger(spi_device, SPI_DEVICE_INTR_READBUF_FLIP); + spi_device_interrupt_force(spi_device, spi_device_intr_readbuf_flip); // Check that mip SEIP is set // Retry to give time for mip to be updated @@ -189,7 +205,7 @@ bool supervisor_irq_test(spi_device_t spi_device, plic_t plic) } intr_id = plic_supervisor_interrupt_claim(plic); - spi_device_interrupt_clear(spi_device, SPI_DEVICE_INTR_READBUF_FLIP); + spi_device_interrupt_clear(spi_device, spi_device_intr_readbuf_flip); plic_supervisor_interrupt_complete(plic, intr_id); // Check that mip SEIP is clear