mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: add SDIO support and related tests
- Introduced a new testbench for the SDIO wrapper () to validate APB interactions. - Updated to include IRQ handling for SDIO0 and SDIO1. - Enhanced to account for performance management of SDIO0 and SDIO1. - Modified memory map tests to include SDIO base addresses and performance wait offsets. - Updated pin mapping tests to include SDIO1 signals. - Created a comprehensive suite of SDIO tests () covering various functionalities including CRC, command engine, DMA, and standalone operations. - Added a new test for verifying SDIO register parity against the RTL definition. - Adjusted SOC topology tests to reflect the addition of SDIO in the IRQ and APB fabric.
This commit is contained in:
83 files changed
+11777
-153
No files matched your search
@@ -269,7 +269,7 @@ endif
|
||||
benchmark-report coremark-report \
|
||||
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
||||
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
|
||||
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-opipsram formal-dma formal-clean formal-doctor \
|
||||
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-opipsram formal-dma formal-sdio formal-clean formal-doctor \
|
||||
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all
|
||||
.NOTPARALLEL: setup
|
||||
|
||||
@@ -298,7 +298,7 @@ help:
|
||||
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
|
||||
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
|
||||
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
|
||||
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-opipsram | formal-dma run peripheral proofs' \
|
||||
'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-opipsram | formal-dma | formal-sdio run peripheral proofs' \
|
||||
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
|
||||
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
|
||||
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
|
||||
|
||||
@@ -88,6 +88,14 @@ channel 1, I2C1 channel 2, and media/benchmark clients serialize on bulk
|
||||
channel 3. See [DMA MVP](../docs/ip/dma.md) before adding a concurrent DMA
|
||||
client.
|
||||
|
||||
The SD/SDIO HAL exposes `rs_sd_memory_*` for SD Memory v2 block devices and
|
||||
`rs_sdio_function_*` for SDIO functions; these APIs must not be conflated.
|
||||
Bring-up can run the controller ID/capability/IRQ self-test without a card.
|
||||
The delivered software is limited to fixed-present 3.3 V SDR, 1/4-bit
|
||||
operation, aligned SG DMA buffers, and the validated 72 MHz 24/36 MHz versus
|
||||
`>=100 MHz` 50 MHz clock boundary. It makes no filesystem, hotplug,
|
||||
compliance, or vendor Wi-Fi claim.
|
||||
|
||||
Self-owned application C/H code follows the project
|
||||
[MISRA C:2012 Amendment 2 policy](../docs/misra-c-2012.md). Managed upstream
|
||||
sources and exclusions listed in `quality/embedded_c_policy.json` are outside
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <retrosoc/core/archinfo.h>
|
||||
#include <retrosoc/core/soc.h>
|
||||
#include <retrosoc/hal/sdio.h>
|
||||
#include <retrosoc/hal/uart.h>
|
||||
#include <retrosoc/service/booter.h>
|
||||
#include <retrosoc/service/test.h>
|
||||
@@ -10,6 +11,9 @@ int main(void) {
|
||||
}
|
||||
rs_app_info();
|
||||
ip_archinfo_test(0, 0);
|
||||
if (rs_sdio_controller_selftest(RS_SDIO_INSTANCE_0) != RS_OK) {
|
||||
return 1;
|
||||
}
|
||||
rs_test_finish(RS_TEST_PASSED, 0U);
|
||||
return 0;
|
||||
}
|
||||
@@ -51,6 +51,18 @@ I2C0, I2C1, and bulk/media clients have deterministic channel assignments; see
|
||||
the [DMA MVP contract](../docs/ip/dma.md). The current SDK intentionally
|
||||
accepts only naturally aligned 32-bit DMA transfers.
|
||||
|
||||
`<retrosoc/hal/sdio.h>` provides separate low-level native SD host,
|
||||
SD Memory v2, and SDIO function APIs for `sdio0` and `sdio1`. The controller
|
||||
scope is fixed-present 3.3 V SDR at 1/4-bit width; it does not claim SD
|
||||
Association compliance or a vendor Wi-Fi driver. The 72 MHz SoC profiles use
|
||||
the validated 24/36 MHz operating range, while a 50 MHz SD clock requires a
|
||||
separately validated `clk_i >= 100 MHz` environment. SG DMA descriptors and
|
||||
their buffers must be 16-byte/32-bit aligned as applicable; callers must use a
|
||||
bounce buffer for an unaligned head. PIO and DMA use little-endian byte lanes:
|
||||
bits `[7:0]`/strobe lane 0 is the first wire byte, and `DATA_START` is the
|
||||
coordinated DMA/card-data launch. The SDK does not provide hotplug,
|
||||
filesystem, combo-card, UHS, DDR, or 1.8 V support.
|
||||
|
||||
`crt/inc/` is legacy or generated compatibility material. It is not an active
|
||||
public include root for the firmware build; new code must not add dependencies
|
||||
on it.
|
||||
|
||||
@@ -9,6 +9,8 @@ typedef struct {
|
||||
uint64_t mgmt_wait;
|
||||
uint64_t user_wait;
|
||||
uint64_t dma_wait;
|
||||
uint64_t sdio0_wait;
|
||||
uint64_t sdio1_wait;
|
||||
uint64_t apb4_periph_wait;
|
||||
uint64_t apb4_system_wait;
|
||||
uint64_t sdram_wait;
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
#ifndef RETROSOC_HAL_SDIO_H
|
||||
#define RETROSOC_HAL_SDIO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <retrosoc/core/status.h>
|
||||
#include <retrosoc/hal/sdio_regs.h>
|
||||
|
||||
#define RS_SD_MEMORY_BLOCK_SIZE UINT32_C(512)
|
||||
#define RS_SDIO_PIO_WORD_SIZE UINT32_C(4)
|
||||
|
||||
/*
|
||||
* SDIO words are little-endian byte lanes: bits [7:0] (lane 0) contain the
|
||||
* first byte placed on the SD wire, and each byte is serialized MSB first.
|
||||
* The same convention is used by PIO, AXI stream data, and tail strobes.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
RS_SDIO_INSTANCE_0 = 0,
|
||||
RS_SDIO_INSTANCE_1 = 1,
|
||||
RS_SDIO_INSTANCE_COUNT = 2
|
||||
} rs_sdio_instance_t;
|
||||
|
||||
typedef enum { RS_SDIO_BUS_WIDTH_1 = 0, RS_SDIO_BUS_WIDTH_4 = 1 } rs_sdio_bus_width_t;
|
||||
|
||||
typedef enum {
|
||||
RS_SDIO_RESPONSE_NONE = RS_SDIO_RESP_NONE,
|
||||
RS_SDIO_RESPONSE_R1 = RS_SDIO_RESP_R1,
|
||||
RS_SDIO_RESPONSE_R1B = RS_SDIO_RESP_R1B,
|
||||
RS_SDIO_RESPONSE_R2 = RS_SDIO_RESP_R2,
|
||||
RS_SDIO_RESPONSE_R3 = RS_SDIO_RESP_R3,
|
||||
RS_SDIO_RESPONSE_R4 = RS_SDIO_RESP_R4,
|
||||
RS_SDIO_RESPONSE_R5 = RS_SDIO_RESP_R5,
|
||||
RS_SDIO_RESPONSE_R6 = RS_SDIO_RESP_R6,
|
||||
RS_SDIO_RESPONSE_R7 = RS_SDIO_RESP_R7
|
||||
} rs_sdio_response_type_t;
|
||||
|
||||
typedef enum { RS_SDIO_DATA_FROM_CARD = 0, RS_SDIO_DATA_TO_CARD = 1 } rs_sdio_data_direction_t;
|
||||
|
||||
typedef enum { RS_SD_MEMORY_SDSC = 0, RS_SD_MEMORY_SDHC = 1 } rs_sd_memory_card_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t words[5];
|
||||
} rs_sdio_response_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t index;
|
||||
uint32_t argument;
|
||||
rs_sdio_response_type_t response;
|
||||
bool crc_check;
|
||||
bool index_check;
|
||||
} rs_sdio_command_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t block_size;
|
||||
uint16_t block_count;
|
||||
rs_sdio_data_direction_t direction;
|
||||
bool dma;
|
||||
bool block_mode;
|
||||
bool fixed_address;
|
||||
} rs_sdio_data_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t half_period;
|
||||
uint32_t requested_hz;
|
||||
uint32_t actual_hz;
|
||||
} rs_sdio_clock_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t source_clock_hz;
|
||||
uint32_t target_clock_hz;
|
||||
rs_sdio_bus_width_t bus_width;
|
||||
uint32_t timeout_cmd;
|
||||
uint32_t timeout_data;
|
||||
uint32_t timeout_busy;
|
||||
bool enable_interrupts;
|
||||
} rs_sdio_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t raw_status;
|
||||
uint32_t command_status;
|
||||
uint32_t data_status;
|
||||
uint32_t fifo_status;
|
||||
uint32_t dma_status;
|
||||
uint32_t error_status;
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_enable;
|
||||
uint32_t clock_actual_hz;
|
||||
uint32_t current_descriptor;
|
||||
uint32_t bytes_done;
|
||||
uint32_t dma_error_address;
|
||||
uint32_t dma_error;
|
||||
uint32_t last_command;
|
||||
bool busy;
|
||||
bool present;
|
||||
} rs_sdio_status_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t raw_status;
|
||||
uint32_t current_descriptor;
|
||||
uint32_t bytes_done;
|
||||
uint32_t error_address;
|
||||
uint32_t error_code;
|
||||
bool busy;
|
||||
bool done;
|
||||
bool error;
|
||||
} rs_sdio_dma_status_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cid[5];
|
||||
uint32_t csd[5];
|
||||
uint32_t ocr;
|
||||
uint16_t rca;
|
||||
uint32_t capacity_blocks;
|
||||
uint32_t block_length;
|
||||
rs_sd_memory_card_type_t card_type;
|
||||
rs_sdio_bus_width_t bus_width;
|
||||
uint32_t actual_clock_hz;
|
||||
bool high_capacity;
|
||||
bool high_speed;
|
||||
bool bus_width_fallback;
|
||||
bool speed_fallback;
|
||||
} rs_sd_memory_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t ocr;
|
||||
uint16_t rca;
|
||||
uint8_t function_count;
|
||||
uint8_t cccr_version;
|
||||
uint8_t io_enable;
|
||||
uint8_t io_ready;
|
||||
uint8_t bus_interface_control;
|
||||
uint8_t function_code[8];
|
||||
uint16_t function_block_size[8];
|
||||
bool high_speed;
|
||||
bool high_speed_fallback;
|
||||
bool dat1_interrupt_enabled;
|
||||
} rs_sdio_function_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t function;
|
||||
uint32_t address;
|
||||
bool write;
|
||||
bool raw;
|
||||
uint8_t data;
|
||||
} rs_sdio_cmd52_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t function;
|
||||
uint32_t address;
|
||||
uint16_t count;
|
||||
bool write;
|
||||
bool block_mode;
|
||||
bool fixed_address;
|
||||
} rs_sdio_cmd53_t;
|
||||
|
||||
static inline void rs_sdio_memory_barrier(void) {
|
||||
#if defined(__riscv)
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
#else
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_clock_calculate(uint32_t source_clock_hz, uint32_t target_clock_hz,
|
||||
rs_sdio_clock_t *clock);
|
||||
rs_status_t rs_sdio_response_field(const rs_sdio_response_t *response, uint32_t high_bit,
|
||||
uint32_t low_bit, uint32_t *value);
|
||||
rs_status_t rs_sd_memory_address(rs_sd_memory_card_type_t card_type, uint32_t block,
|
||||
uint32_t *argument);
|
||||
rs_status_t rs_sd_memory_parse_csd(const rs_sdio_response_t *response, rs_sd_memory_info_t *info);
|
||||
rs_status_t rs_sdio_cmd52_argument(const rs_sdio_cmd52_t *command, uint32_t *argument);
|
||||
rs_status_t rs_sdio_cmd53_argument(const rs_sdio_cmd53_t *command, uint32_t *argument);
|
||||
rs_status_t rs_sdio_validate_dma_buffer(const void *buffer, size_t byte_count);
|
||||
rs_status_t rs_sdio_descriptor_prepare(rs_sdio_descriptor_t *descriptor, uintptr_t buffer,
|
||||
size_t byte_count, uintptr_t next, bool end, bool irq);
|
||||
rs_status_t rs_sdio_descriptor_validate(const rs_sdio_descriptor_t *descriptor);
|
||||
rs_status_t rs_sdio_descriptor_chain_validate(const rs_sdio_descriptor_t *descriptors,
|
||||
uint16_t count, uint32_t total_bytes);
|
||||
rs_status_t rs_sdio_descriptor_publish(rs_sdio_descriptor_t *descriptor);
|
||||
rs_status_t rs_sdio_descriptor_publish_chain(rs_sdio_descriptor_t *descriptors, uint16_t count);
|
||||
|
||||
rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t *version,
|
||||
uint32_t *capability);
|
||||
rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_t *config);
|
||||
rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock_hz,
|
||||
uint32_t target_clock_hz, rs_sdio_clock_t *clock);
|
||||
rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width_t width);
|
||||
rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_timeout,
|
||||
uint32_t data_timeout, uint32_t busy_timeout);
|
||||
rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable);
|
||||
rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *status);
|
||||
|
||||
rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_command_t *command);
|
||||
rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t *response,
|
||||
rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_command_t *command,
|
||||
rs_sdio_response_t *response, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t *response);
|
||||
|
||||
rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance,
|
||||
const rs_sdio_data_config_t *config);
|
||||
rs_status_t rs_sdio_data_start(rs_sdio_instance_t instance);
|
||||
rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, size_t byte_count,
|
||||
rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t byte_count,
|
||||
rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout);
|
||||
|
||||
rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t *descriptors,
|
||||
uint16_t count, uint32_t total_bytes);
|
||||
/* DATA_START is the coordinated DMA and card-data launch operation. */
|
||||
rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance);
|
||||
rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance);
|
||||
rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_status_t *status);
|
||||
rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout);
|
||||
|
||||
rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask);
|
||||
rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask);
|
||||
rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask);
|
||||
rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask);
|
||||
rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask);
|
||||
rs_status_t rs_sdio_controller_selftest(rs_sdio_instance_t instance);
|
||||
|
||||
rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
|
||||
rs_sd_memory_info_t *info, rs_timeout_t timeout);
|
||||
rs_status_t rs_sd_memory_read_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
|
||||
uint32_t block, uint32_t count, void *buffer,
|
||||
rs_timeout_t timeout);
|
||||
rs_status_t rs_sd_memory_write_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
|
||||
uint32_t block, uint32_t count, const void *buffer,
|
||||
rs_timeout_t timeout);
|
||||
|
||||
rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
|
||||
rs_sdio_function_info_t *info, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_function_enable(rs_sdio_instance_t instance, rs_sdio_function_info_t *info,
|
||||
uint8_t function, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t function,
|
||||
uint32_t address, uint8_t *data, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t function,
|
||||
uint32_t address, uint8_t data, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_function_cmd53_transfer(rs_sdio_instance_t instance, uint8_t function,
|
||||
uint32_t address, void *buffer, size_t byte_count,
|
||||
bool write, bool block_mode, bool fixed_address,
|
||||
uint16_t block_size, rs_timeout_t timeout);
|
||||
rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable);
|
||||
rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pending);
|
||||
rs_status_t rs_sdio_function_irq_ack(rs_sdio_instance_t instance);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,314 @@
|
||||
#ifndef RETROSOC_HAL_SDIO_REGS_H
|
||||
#define RETROSOC_HAL_SDIO_REGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* The ABI names below are a direct hand-maintained C mirror of
|
||||
* rtl/ip/storage/sdio_define.svh. Keep the ABI prefix stable so the parity
|
||||
* test can compare the two files without a generated register description.
|
||||
*/
|
||||
#define RS_SDIO_ABI_IP_ID UINT32_C(0x000)
|
||||
#define RS_SDIO_ABI_IP_VERSION UINT32_C(0x004)
|
||||
#define RS_SDIO_ABI_CAPABILITY UINT32_C(0x008)
|
||||
#define RS_SDIO_ABI_HOST_CTRL UINT32_C(0x00C)
|
||||
#define RS_SDIO_ABI_CLOCK_CTRL UINT32_C(0x010)
|
||||
#define RS_SDIO_ABI_CLOCK_ACTUAL UINT32_C(0x014)
|
||||
#define RS_SDIO_ABI_BUS_CTRL UINT32_C(0x018)
|
||||
#define RS_SDIO_ABI_TIMEOUT_CMD UINT32_C(0x01C)
|
||||
#define RS_SDIO_ABI_TIMEOUT_DATA UINT32_C(0x020)
|
||||
#define RS_SDIO_ABI_TIMEOUT_BUSY UINT32_C(0x024)
|
||||
#define RS_SDIO_ABI_STATUS UINT32_C(0x028)
|
||||
#define RS_SDIO_ABI_PRESENT UINT32_C(0x02C)
|
||||
|
||||
#define RS_SDIO_ABI_CMD_ARG UINT32_C(0x040)
|
||||
#define RS_SDIO_ABI_CMD_CFG UINT32_C(0x044)
|
||||
#define RS_SDIO_ABI_CMD_START UINT32_C(0x048)
|
||||
#define RS_SDIO_ABI_CMD_STATUS UINT32_C(0x04C)
|
||||
#define RS_SDIO_ABI_RESP0 UINT32_C(0x050)
|
||||
#define RS_SDIO_ABI_RESP1 UINT32_C(0x054)
|
||||
#define RS_SDIO_ABI_RESP2 UINT32_C(0x058)
|
||||
#define RS_SDIO_ABI_RESP3 UINT32_C(0x05C)
|
||||
#define RS_SDIO_ABI_RESP4 UINT32_C(0x060)
|
||||
|
||||
#define RS_SDIO_ABI_BLOCK_SIZE UINT32_C(0x080)
|
||||
#define RS_SDIO_ABI_BLOCK_COUNT UINT32_C(0x084)
|
||||
#define RS_SDIO_ABI_DATA_CFG UINT32_C(0x088)
|
||||
#define RS_SDIO_ABI_DATA_START UINT32_C(0x08C)
|
||||
#define RS_SDIO_ABI_PIO_DATA UINT32_C(0x090)
|
||||
#define RS_SDIO_ABI_FIFO_STATUS UINT32_C(0x094)
|
||||
#define RS_SDIO_ABI_FIFO_WATERMARK UINT32_C(0x098)
|
||||
|
||||
#define RS_SDIO_ABI_DESC_BASE UINT32_C(0x0C0)
|
||||
#define RS_SDIO_ABI_DESC_COUNT UINT32_C(0x0C4)
|
||||
#define RS_SDIO_ABI_DMA_CTRL UINT32_C(0x0C8)
|
||||
#define RS_SDIO_ABI_DMA_STATUS UINT32_C(0x0CC)
|
||||
#define RS_SDIO_ABI_CURRENT_DESC UINT32_C(0x0D0)
|
||||
#define RS_SDIO_ABI_BYTES_DONE UINT32_C(0x0D4)
|
||||
#define RS_SDIO_ABI_DMA_ERROR_ADDR UINT32_C(0x0D8)
|
||||
#define RS_SDIO_ABI_DMA_ERROR UINT32_C(0x0DC)
|
||||
|
||||
#define RS_SDIO_ABI_IRQ_STATUS UINT32_C(0x100)
|
||||
#define RS_SDIO_ABI_IRQ_ENABLE UINT32_C(0x104)
|
||||
#define RS_SDIO_ABI_IRQ_TEST UINT32_C(0x108)
|
||||
#define RS_SDIO_ABI_ERROR_STATUS UINT32_C(0x10C)
|
||||
#define RS_SDIO_ABI_ERROR_ABORT 7U
|
||||
#define RS_SDIO_ABI_LAST_CMD UINT32_C(0x110)
|
||||
#define RS_SDIO_ABI_CRC_ERROR_COUNT UINT32_C(0x114)
|
||||
#define RS_SDIO_ABI_TIMEOUT_COUNT UINT32_C(0x118)
|
||||
#define RS_SDIO_ABI_AXI_ERROR_COUNT UINT32_C(0x11C)
|
||||
#define RS_SDIO_ABI_STALL_COUNT UINT32_C(0x120)
|
||||
#define RS_SDIO_ABI_DEBUG UINT32_C(0x124)
|
||||
|
||||
#define RS_SDIO_ABI_HOST_CTRL_ENABLE 0U
|
||||
#define RS_SDIO_ABI_HOST_CTRL_ABORT 1U
|
||||
#define RS_SDIO_ABI_HOST_CTRL_IRQ 2U
|
||||
#define RS_SDIO_ABI_CLOCK_CTRL_ENABLE 0U
|
||||
#define RS_SDIO_ABI_CLOCK_CTRL_HALF_PERIOD_LSB 8U
|
||||
#define RS_SDIO_ABI_BUS_CTRL_WIDTH_LSB 0U
|
||||
#define RS_SDIO_ABI_BUS_CTRL_WIDTH_1 0U
|
||||
#define RS_SDIO_ABI_BUS_CTRL_WIDTH_4 1U
|
||||
#define RS_SDIO_ABI_CMD_CFG_INDEX_LSB 0U
|
||||
#define RS_SDIO_ABI_CMD_CFG_RESP_LSB 8U
|
||||
#define RS_SDIO_ABI_CMD_CFG_CRC_CHECK 12U
|
||||
#define RS_SDIO_ABI_CMD_CFG_INDEX_CHECK 13U
|
||||
|
||||
#define RS_SDIO_ABI_RESP_NONE 0U
|
||||
#define RS_SDIO_ABI_RESP_R1 1U
|
||||
#define RS_SDIO_ABI_RESP_R1B 2U
|
||||
#define RS_SDIO_ABI_RESP_R2 3U
|
||||
#define RS_SDIO_ABI_RESP_R3 4U
|
||||
#define RS_SDIO_ABI_RESP_R4 5U
|
||||
#define RS_SDIO_ABI_RESP_R5 6U
|
||||
#define RS_SDIO_ABI_RESP_R6 7U
|
||||
#define RS_SDIO_ABI_RESP_R7 8U
|
||||
|
||||
#define RS_SDIO_ABI_DATA_CFG_DIRECTION 0U
|
||||
#define RS_SDIO_ABI_DATA_CFG_WIDTH_LSB 1U
|
||||
#define RS_SDIO_ABI_DATA_CFG_DMA 4U
|
||||
#define RS_SDIO_ABI_DATA_CFG_BLOCK 5U
|
||||
#define RS_SDIO_ABI_DATA_CFG_FIXED 6U
|
||||
#define RS_SDIO_ABI_DMA_CTRL_START 0U
|
||||
#define RS_SDIO_ABI_DMA_CTRL_ABORT 1U
|
||||
|
||||
#define RS_SDIO_ABI_DESC_OWN 0U
|
||||
#define RS_SDIO_ABI_DESC_CHAIN 1U
|
||||
#define RS_SDIO_ABI_DESC_END 2U
|
||||
#define RS_SDIO_ABI_DESC_IRQ 3U
|
||||
#define RS_SDIO_ABI_DESC_DONE 16U
|
||||
#define RS_SDIO_ABI_DESC_ERROR 17U
|
||||
|
||||
#define RS_SDIO_ABI_IRQ_CMD_DONE 0U
|
||||
#define RS_SDIO_ABI_IRQ_DATA_DONE 1U
|
||||
#define RS_SDIO_ABI_IRQ_DMA_DONE 2U
|
||||
#define RS_SDIO_ABI_IRQ_CMD_ERROR 3U
|
||||
#define RS_SDIO_ABI_IRQ_DATA_ERROR 4U
|
||||
#define RS_SDIO_ABI_IRQ_DMA_ERROR 5U
|
||||
#define RS_SDIO_ABI_IRQ_DAT1 6U
|
||||
#define RS_SDIO_ABI_IRQ_ABORT 7U
|
||||
|
||||
#define RS_SDIO_ABI_CAP_SD_MEMORY_V2 0U
|
||||
#define RS_SDIO_ABI_CAP_SDIO_V2 1U
|
||||
#define RS_SDIO_ABI_CAP_BUS_1BIT 2U
|
||||
#define RS_SDIO_ABI_CAP_BUS_4BIT 3U
|
||||
#define RS_SDIO_ABI_CAP_SDR 4U
|
||||
#define RS_SDIO_ABI_CAP_SG_DMA 5U
|
||||
#define RS_SDIO_ABI_CAP_PIO 6U
|
||||
#define RS_SDIO_ABI_CAP_CRC 7U
|
||||
#define RS_SDIO_ABI_CAP_MAX_BURST_16 8U
|
||||
|
||||
#define RS_SDIO_REG_IP_ID RS_SDIO_ABI_IP_ID
|
||||
#define RS_SDIO_REG_IP_VERSION RS_SDIO_ABI_IP_VERSION
|
||||
#define RS_SDIO_REG_CAPABILITY RS_SDIO_ABI_CAPABILITY
|
||||
#define RS_SDIO_REG_HOST_CTRL RS_SDIO_ABI_HOST_CTRL
|
||||
#define RS_SDIO_REG_CLOCK_CTRL RS_SDIO_ABI_CLOCK_CTRL
|
||||
#define RS_SDIO_REG_CLOCK_ACTUAL RS_SDIO_ABI_CLOCK_ACTUAL
|
||||
#define RS_SDIO_REG_BUS_CTRL RS_SDIO_ABI_BUS_CTRL
|
||||
#define RS_SDIO_REG_TIMEOUT_CMD RS_SDIO_ABI_TIMEOUT_CMD
|
||||
#define RS_SDIO_REG_TIMEOUT_DATA RS_SDIO_ABI_TIMEOUT_DATA
|
||||
#define RS_SDIO_REG_TIMEOUT_BUSY RS_SDIO_ABI_TIMEOUT_BUSY
|
||||
#define RS_SDIO_REG_STATUS RS_SDIO_ABI_STATUS
|
||||
#define RS_SDIO_REG_PRESENT RS_SDIO_ABI_PRESENT
|
||||
#define RS_SDIO_REG_CMD_ARG RS_SDIO_ABI_CMD_ARG
|
||||
#define RS_SDIO_REG_CMD_CFG RS_SDIO_ABI_CMD_CFG
|
||||
#define RS_SDIO_REG_CMD_START RS_SDIO_ABI_CMD_START
|
||||
#define RS_SDIO_REG_CMD_STATUS RS_SDIO_ABI_CMD_STATUS
|
||||
#define RS_SDIO_REG_RESP0 RS_SDIO_ABI_RESP0
|
||||
#define RS_SDIO_REG_RESP1 RS_SDIO_ABI_RESP1
|
||||
#define RS_SDIO_REG_RESP2 RS_SDIO_ABI_RESP2
|
||||
#define RS_SDIO_REG_RESP3 RS_SDIO_ABI_RESP3
|
||||
#define RS_SDIO_REG_RESP4 RS_SDIO_ABI_RESP4
|
||||
#define RS_SDIO_REG_BLOCK_SIZE RS_SDIO_ABI_BLOCK_SIZE
|
||||
#define RS_SDIO_REG_BLOCK_COUNT RS_SDIO_ABI_BLOCK_COUNT
|
||||
#define RS_SDIO_REG_DATA_CFG RS_SDIO_ABI_DATA_CFG
|
||||
#define RS_SDIO_REG_DATA_START RS_SDIO_ABI_DATA_START
|
||||
#define RS_SDIO_REG_PIO_DATA RS_SDIO_ABI_PIO_DATA
|
||||
#define RS_SDIO_REG_FIFO_STATUS RS_SDIO_ABI_FIFO_STATUS
|
||||
#define RS_SDIO_REG_FIFO_WATERMARK RS_SDIO_ABI_FIFO_WATERMARK
|
||||
#define RS_SDIO_REG_DESC_BASE RS_SDIO_ABI_DESC_BASE
|
||||
#define RS_SDIO_REG_DESC_COUNT RS_SDIO_ABI_DESC_COUNT
|
||||
#define RS_SDIO_REG_DMA_CTRL RS_SDIO_ABI_DMA_CTRL
|
||||
#define RS_SDIO_REG_DMA_STATUS RS_SDIO_ABI_DMA_STATUS
|
||||
#define RS_SDIO_REG_CURRENT_DESC RS_SDIO_ABI_CURRENT_DESC
|
||||
#define RS_SDIO_REG_BYTES_DONE RS_SDIO_ABI_BYTES_DONE
|
||||
#define RS_SDIO_REG_DMA_ERROR_ADDR RS_SDIO_ABI_DMA_ERROR_ADDR
|
||||
#define RS_SDIO_REG_DMA_ERROR RS_SDIO_ABI_DMA_ERROR
|
||||
#define RS_SDIO_REG_IRQ_STATUS RS_SDIO_ABI_IRQ_STATUS
|
||||
#define RS_SDIO_REG_IRQ_ENABLE RS_SDIO_ABI_IRQ_ENABLE
|
||||
#define RS_SDIO_REG_IRQ_TEST RS_SDIO_ABI_IRQ_TEST
|
||||
#define RS_SDIO_REG_ERROR_STATUS RS_SDIO_ABI_ERROR_STATUS
|
||||
#define RS_SDIO_REG_LAST_CMD RS_SDIO_ABI_LAST_CMD
|
||||
#define RS_SDIO_REG_CRC_ERROR_COUNT RS_SDIO_ABI_CRC_ERROR_COUNT
|
||||
#define RS_SDIO_REG_TIMEOUT_COUNT RS_SDIO_ABI_TIMEOUT_COUNT
|
||||
#define RS_SDIO_REG_AXI_ERROR_COUNT RS_SDIO_ABI_AXI_ERROR_COUNT
|
||||
#define RS_SDIO_REG_STALL_COUNT RS_SDIO_ABI_STALL_COUNT
|
||||
#define RS_SDIO_REG_DEBUG RS_SDIO_ABI_DEBUG
|
||||
|
||||
#define RS_SDIO_HOST_CTRL_ENABLE_BIT RS_SDIO_ABI_HOST_CTRL_ENABLE
|
||||
#define RS_SDIO_HOST_CTRL_ABORT_BIT RS_SDIO_ABI_HOST_CTRL_ABORT
|
||||
#define RS_SDIO_HOST_CTRL_IRQ_BIT RS_SDIO_ABI_HOST_CTRL_IRQ
|
||||
#define RS_SDIO_CLOCK_CTRL_ENABLE_BIT RS_SDIO_ABI_CLOCK_CTRL_ENABLE
|
||||
#define RS_SDIO_CLOCK_CTRL_HALF_PERIOD_LSB RS_SDIO_ABI_CLOCK_CTRL_HALF_PERIOD_LSB
|
||||
#define RS_SDIO_BUS_CTRL_WIDTH_LSB RS_SDIO_ABI_BUS_CTRL_WIDTH_LSB
|
||||
#define RS_SDIO_BUS_CTRL_WIDTH_1 RS_SDIO_ABI_BUS_CTRL_WIDTH_1
|
||||
#define RS_SDIO_BUS_CTRL_WIDTH_4 RS_SDIO_ABI_BUS_CTRL_WIDTH_4
|
||||
#define RS_SDIO_CMD_CFG_INDEX_LSB RS_SDIO_ABI_CMD_CFG_INDEX_LSB
|
||||
#define RS_SDIO_CMD_CFG_RESP_LSB RS_SDIO_ABI_CMD_CFG_RESP_LSB
|
||||
#define RS_SDIO_CMD_CFG_CRC_CHECK_BIT RS_SDIO_ABI_CMD_CFG_CRC_CHECK
|
||||
#define RS_SDIO_CMD_CFG_INDEX_CHECK_BIT RS_SDIO_ABI_CMD_CFG_INDEX_CHECK
|
||||
|
||||
#define RS_SDIO_RESP_NONE RS_SDIO_ABI_RESP_NONE
|
||||
#define RS_SDIO_RESP_R1 RS_SDIO_ABI_RESP_R1
|
||||
#define RS_SDIO_RESP_R1B RS_SDIO_ABI_RESP_R1B
|
||||
#define RS_SDIO_RESP_R2 RS_SDIO_ABI_RESP_R2
|
||||
#define RS_SDIO_RESP_R3 RS_SDIO_ABI_RESP_R3
|
||||
#define RS_SDIO_RESP_R4 RS_SDIO_ABI_RESP_R4
|
||||
#define RS_SDIO_RESP_R5 RS_SDIO_ABI_RESP_R5
|
||||
#define RS_SDIO_RESP_R6 RS_SDIO_ABI_RESP_R6
|
||||
#define RS_SDIO_RESP_R7 RS_SDIO_ABI_RESP_R7
|
||||
|
||||
#define RS_SDIO_DATA_CFG_DIRECTION_BIT RS_SDIO_ABI_DATA_CFG_DIRECTION
|
||||
#define RS_SDIO_DATA_CFG_WIDTH_LSB RS_SDIO_ABI_DATA_CFG_WIDTH_LSB
|
||||
#define RS_SDIO_DATA_CFG_DMA_BIT RS_SDIO_ABI_DATA_CFG_DMA
|
||||
#define RS_SDIO_DATA_CFG_BLOCK_BIT RS_SDIO_ABI_DATA_CFG_BLOCK
|
||||
#define RS_SDIO_DATA_CFG_FIXED_BIT RS_SDIO_ABI_DATA_CFG_FIXED
|
||||
#define RS_SDIO_DMA_CTRL_START_BIT RS_SDIO_ABI_DMA_CTRL_START
|
||||
#define RS_SDIO_DMA_CTRL_ABORT_BIT RS_SDIO_ABI_DMA_CTRL_ABORT
|
||||
|
||||
#define RS_SDIO_DESC_OWN_BIT RS_SDIO_ABI_DESC_OWN
|
||||
#define RS_SDIO_DESC_CHAIN_BIT RS_SDIO_ABI_DESC_CHAIN
|
||||
#define RS_SDIO_DESC_END_BIT RS_SDIO_ABI_DESC_END
|
||||
#define RS_SDIO_DESC_IRQ_BIT RS_SDIO_ABI_DESC_IRQ
|
||||
#define RS_SDIO_DESC_DONE_BIT RS_SDIO_ABI_DESC_DONE
|
||||
#define RS_SDIO_DESC_ERROR_BIT RS_SDIO_ABI_DESC_ERROR
|
||||
|
||||
#define RS_SDIO_IRQ_CMD_DONE (UINT32_C(1) << RS_SDIO_ABI_IRQ_CMD_DONE)
|
||||
#define RS_SDIO_IRQ_DATA_DONE (UINT32_C(1) << RS_SDIO_ABI_IRQ_DATA_DONE)
|
||||
#define RS_SDIO_IRQ_DMA_DONE (UINT32_C(1) << RS_SDIO_ABI_IRQ_DMA_DONE)
|
||||
#define RS_SDIO_IRQ_CMD_ERROR (UINT32_C(1) << RS_SDIO_ABI_IRQ_CMD_ERROR)
|
||||
#define RS_SDIO_IRQ_DATA_ERROR (UINT32_C(1) << RS_SDIO_ABI_IRQ_DATA_ERROR)
|
||||
#define RS_SDIO_IRQ_DMA_ERROR (UINT32_C(1) << RS_SDIO_ABI_IRQ_DMA_ERROR)
|
||||
#define RS_SDIO_IRQ_DAT1 (UINT32_C(1) << RS_SDIO_ABI_IRQ_DAT1)
|
||||
#define RS_SDIO_IRQ_ABORT (UINT32_C(1) << RS_SDIO_ABI_IRQ_ABORT)
|
||||
#define RS_SDIO_IRQ_ALL UINT32_C(0x000000FF)
|
||||
|
||||
#define RS_SDIO_CAP_SD_MEMORY_V2 (UINT32_C(1) << RS_SDIO_ABI_CAP_SD_MEMORY_V2)
|
||||
#define RS_SDIO_CAP_SDIO_V2 (UINT32_C(1) << RS_SDIO_ABI_CAP_SDIO_V2)
|
||||
#define RS_SDIO_CAP_BUS_1BIT (UINT32_C(1) << RS_SDIO_ABI_CAP_BUS_1BIT)
|
||||
#define RS_SDIO_CAP_BUS_4BIT (UINT32_C(1) << RS_SDIO_ABI_CAP_BUS_4BIT)
|
||||
#define RS_SDIO_CAP_SDR (UINT32_C(1) << RS_SDIO_ABI_CAP_SDR)
|
||||
#define RS_SDIO_CAP_SG_DMA (UINT32_C(1) << RS_SDIO_ABI_CAP_SG_DMA)
|
||||
#define RS_SDIO_CAP_PIO (UINT32_C(1) << RS_SDIO_ABI_CAP_PIO)
|
||||
#define RS_SDIO_CAP_CRC (UINT32_C(1) << RS_SDIO_ABI_CAP_CRC)
|
||||
#define RS_SDIO_CAP_MAX_BURST_16 (UINT32_C(1) << RS_SDIO_ABI_CAP_MAX_BURST_16)
|
||||
|
||||
#define RS_SDIO_IP_ID_VALUE UINT32_C(0x5344494F)
|
||||
#define RS_SDIO_IP_VERSION_VALUE UINT32_C(0x00010000)
|
||||
#define RS_SDIO_CAPABILITY_VALUE UINT32_C(0x000001FF)
|
||||
#define RS_SDIO_PRESENT_CARD UINT32_C(0x00000001)
|
||||
#define RS_SDIO_FIFO_WATERMARK_DEFAULT UINT32_C(0x00000001)
|
||||
#define RS_SDIO_LAST_CMD_MASK UINT32_C(0x0000003F)
|
||||
#define RS_SDIO_RESPONSE_R2_HIGH_MASK UINT32_C(0x000000FF)
|
||||
#define RS_SDIO_DEBUG_PIO_VALID UINT32_C(0x00000001)
|
||||
#define RS_SDIO_DEBUG_PIO_READY UINT32_C(0x00000002)
|
||||
|
||||
#define RS_SDIO_HOST_CTRL_ENABLE (UINT32_C(1) << RS_SDIO_HOST_CTRL_ENABLE_BIT)
|
||||
#define RS_SDIO_HOST_CTRL_ABORT (UINT32_C(1) << RS_SDIO_HOST_CTRL_ABORT_BIT)
|
||||
#define RS_SDIO_HOST_CTRL_IRQ (UINT32_C(1) << RS_SDIO_HOST_CTRL_IRQ_BIT)
|
||||
#define RS_SDIO_CLOCK_CTRL_ENABLE (UINT32_C(1) << RS_SDIO_CLOCK_CTRL_ENABLE_BIT)
|
||||
#define RS_SDIO_CLOCK_CTRL_HALF_PERIOD_MASK UINT32_C(0x00FFFF00)
|
||||
#define RS_SDIO_BUS_CTRL_WIDTH_MASK UINT32_C(0x00000003)
|
||||
#define RS_SDIO_CMD_CFG_INDEX_MASK UINT32_C(0x0000003F)
|
||||
#define RS_SDIO_CMD_CFG_RESP_MASK UINT32_C(0x00000F00)
|
||||
#define RS_SDIO_CMD_CFG_CRC_CHECK (UINT32_C(1) << RS_SDIO_CMD_CFG_CRC_CHECK_BIT)
|
||||
#define RS_SDIO_CMD_CFG_INDEX_CHECK (UINT32_C(1) << RS_SDIO_CMD_CFG_INDEX_CHECK_BIT)
|
||||
#define RS_SDIO_DATA_CFG_DIRECTION (UINT32_C(1) << RS_SDIO_DATA_CFG_DIRECTION_BIT)
|
||||
#define RS_SDIO_DATA_CFG_WIDTH_MASK UINT32_C(0x00000006)
|
||||
#define RS_SDIO_DATA_CFG_DMA (UINT32_C(1) << RS_SDIO_DATA_CFG_DMA_BIT)
|
||||
#define RS_SDIO_DATA_CFG_BLOCK (UINT32_C(1) << RS_SDIO_DATA_CFG_BLOCK_BIT)
|
||||
#define RS_SDIO_DATA_CFG_FIXED (UINT32_C(1) << RS_SDIO_DATA_CFG_FIXED_BIT)
|
||||
#define RS_SDIO_DMA_CTRL_START (UINT32_C(1) << RS_SDIO_DMA_CTRL_START_BIT)
|
||||
#define RS_SDIO_DMA_CTRL_ABORT (UINT32_C(1) << RS_SDIO_DMA_CTRL_ABORT_BIT)
|
||||
|
||||
#define RS_SDIO_STATUS_BUSY UINT32_C(0x00000001)
|
||||
#define RS_SDIO_STATUS_CMD_BUSY UINT32_C(0x00000002)
|
||||
#define RS_SDIO_STATUS_DATA_BUSY UINT32_C(0x00000004)
|
||||
#define RS_SDIO_STATUS_DMA_BUSY UINT32_C(0x00000008)
|
||||
#define RS_SDIO_STATUS_PIO_READY UINT32_C(0x00000010)
|
||||
#define RS_SDIO_STATUS_DATA_FIXED UINT32_C(0x00000020)
|
||||
#define RS_SDIO_STATUS_DATA_BLOCK UINT32_C(0x00000040)
|
||||
#define RS_SDIO_STATUS_DAT1_HIGH UINT32_C(0x00000080)
|
||||
#define RS_SDIO_STATUS_CLOCK_RUNNING UINT32_C(0x00000100)
|
||||
|
||||
#define RS_SDIO_CMD_STATUS_BUSY UINT32_C(0x00000001)
|
||||
#define RS_SDIO_CMD_STATUS_ERROR UINT32_C(0x00000002)
|
||||
#define RS_SDIO_CMD_STATUS_TIMEOUT UINT32_C(0x00000004)
|
||||
#define RS_SDIO_CMD_STATUS_CRC_ERROR UINT32_C(0x00000008)
|
||||
#define RS_SDIO_CMD_STATUS_INDEX_ERROR UINT32_C(0x00000010)
|
||||
|
||||
#define RS_SDIO_DATA_STATUS_BUSY UINT32_C(0x00000001)
|
||||
#define RS_SDIO_DATA_STATUS_ERROR UINT32_C(0x00000002)
|
||||
#define RS_SDIO_DATA_STATUS_TIMEOUT UINT32_C(0x00000004)
|
||||
#define RS_SDIO_DATA_STATUS_CRC_ERROR UINT32_C(0x00000008)
|
||||
#define RS_SDIO_DATA_STATUS_BUSY_TIMEOUT UINT32_C(0x00000010)
|
||||
|
||||
#define RS_SDIO_DMA_STATUS_BUSY UINT32_C(0x00000001)
|
||||
#define RS_SDIO_DMA_STATUS_DONE UINT32_C(0x00000002)
|
||||
#define RS_SDIO_DMA_STATUS_ERROR UINT32_C(0x00000004)
|
||||
#define RS_SDIO_DMA_STATUS_ERROR_CODE_SHIFT 3U
|
||||
#define RS_SDIO_DMA_STATUS_ERROR_CODE_MASK UINT32_C(0x000007F8)
|
||||
|
||||
#define RS_SDIO_ERROR_CMD_CRC UINT32_C(0x00000001)
|
||||
#define RS_SDIO_ERROR_DATA_CRC UINT32_C(0x00000002)
|
||||
#define RS_SDIO_ERROR_CMD_TIMEOUT UINT32_C(0x00000004)
|
||||
#define RS_SDIO_ERROR_DATA_TIMEOUT UINT32_C(0x00000008)
|
||||
#define RS_SDIO_ERROR_CMD UINT32_C(0x00000010)
|
||||
#define RS_SDIO_ERROR_DATA UINT32_C(0x00000020)
|
||||
#define RS_SDIO_ERROR_DMA UINT32_C(0x00000040)
|
||||
#define RS_SDIO_ERROR_ABORT (UINT32_C(1) << RS_SDIO_ABI_ERROR_ABORT)
|
||||
#define RS_SDIO_ERROR_ALL UINT32_C(0x000000FF)
|
||||
|
||||
#define RS_SDIO_DMA_ERROR_INVALID_START UINT32_C(0x01)
|
||||
#define RS_SDIO_DMA_ERROR_DESCRIPTOR UINT32_C(0x02)
|
||||
#define RS_SDIO_DMA_ERROR_AXI UINT32_C(0x04)
|
||||
#define RS_SDIO_DMA_ERROR_ABORT UINT32_C(0x08)
|
||||
#define RS_SDIO_DMA_ERROR_PAYLOAD UINT32_C(0x10)
|
||||
#define RS_SDIO_DMA_ERROR_INTERNAL UINT32_C(0x80)
|
||||
|
||||
#define RS_SDIO_DESCRIPTOR_ALIGNMENT UINT32_C(16)
|
||||
#define RS_SDIO_DESCRIPTOR_SIZE UINT32_C(16)
|
||||
#define RS_SDIO_MAX_DESCRIPTOR_COUNT UINT32_C(16)
|
||||
#define RS_SDIO_MAX_BURST_BEATS UINT32_C(16)
|
||||
#define RS_SDIO_DESCRIPTOR_RESERVED_MASK UINT32_C(0xFFFC0000)
|
||||
#define RS_SDIO_DESCRIPTOR_WRITEBACK_MASK UINT32_C(0x00030000)
|
||||
|
||||
typedef struct {
|
||||
uint32_t buffer_addr;
|
||||
uint32_t byte_count;
|
||||
uint32_t next_addr;
|
||||
uint32_t control_status;
|
||||
} rs_sdio_descriptor_t;
|
||||
|
||||
_Static_assert(sizeof(rs_sdio_descriptor_t) == 16U, "SDIO descriptor ABI size");
|
||||
|
||||
#endif
|
||||
@@ -15,6 +15,8 @@ typedef enum {
|
||||
RS_SYSCTRL_PERF_SDRAM_WAIT = 5,
|
||||
RS_SYSCTRL_PERF_PSRAM_WAIT = 6,
|
||||
RS_SYSCTRL_PERF_FLASH_WAIT = 7,
|
||||
RS_SYSCTRL_PERF_SDIO0_WAIT = 8,
|
||||
RS_SYSCTRL_PERF_SDIO1_WAIT = 9,
|
||||
} rs_sysctrl_perf_counter_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -24,6 +24,10 @@ rs_status_t rs_perf_snapshot(rs_perf_snapshot_t *snapshot) {
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_MGMT_WAIT, &snapshot->mgmt_wait) != RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_USER_WAIT, &snapshot->user_wait) != RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_DMA_WAIT, &snapshot->dma_wait) != RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_SDIO0_WAIT, &snapshot->sdio0_wait) !=
|
||||
RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_SDIO1_WAIT, &snapshot->sdio1_wait) !=
|
||||
RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_APB4_PERIPH_WAIT,
|
||||
&snapshot->apb4_periph_wait) != RS_OK) ||
|
||||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_APB4_SYSTEM_WAIT,
|
||||
|
||||
+1515
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,343 @@
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <retrosoc/hal/sdio.h>
|
||||
|
||||
static bool rs_sdio_response_range_valid(uint32_t high_bit, uint32_t low_bit) {
|
||||
return (high_bit < 136U) && (low_bit <= high_bit) && ((high_bit - low_bit) < 32U);
|
||||
}
|
||||
|
||||
static rs_status_t rs_sdio_response_bit(const rs_sdio_response_t *response, uint32_t bit,
|
||||
uint32_t *value) {
|
||||
uint32_t word;
|
||||
uint32_t shift;
|
||||
|
||||
if ((response == NULL) || (value == NULL) || (bit >= 136U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
word = bit / 32U;
|
||||
shift = bit % 32U;
|
||||
*value = (response->words[word] >> shift) & UINT32_C(1);
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
static bool rs_sdio_address_range_valid(uintptr_t address, size_t byte_count) {
|
||||
uintptr_t last;
|
||||
|
||||
if ((address == (uintptr_t)0U) || (address > (uintptr_t)UINT32_MAX) || (byte_count == 0U) ||
|
||||
(byte_count > (size_t)UINT32_MAX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
last = address + (uintptr_t)(byte_count - 1U);
|
||||
return (last >= address) && (last <= (uintptr_t)UINT32_MAX);
|
||||
}
|
||||
|
||||
static bool rs_sdio_descriptor_address_valid(uintptr_t address) {
|
||||
return (address <= (uintptr_t)UINT32_MAX) &&
|
||||
((address % (uintptr_t)RS_SDIO_DESCRIPTOR_ALIGNMENT) == (uintptr_t)0U) &&
|
||||
((address & UINT32_C(0xFFF)) < UINT32_C(0xFF0));
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_clock_calculate(uint32_t source_clock_hz, uint32_t target_clock_hz,
|
||||
rs_sdio_clock_t *clock) {
|
||||
uint64_t denominator;
|
||||
uint64_t half_period;
|
||||
|
||||
if ((source_clock_hz == 0U) || (target_clock_hz == 0U) || (clock == NULL) ||
|
||||
(target_clock_hz > (source_clock_hz / 2U))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
denominator = (uint64_t)target_clock_hz * UINT64_C(2);
|
||||
half_period = ((uint64_t)source_clock_hz + denominator - UINT64_C(1)) / denominator;
|
||||
if ((half_period == UINT64_C(0)) || (half_period > (uint64_t)UINT16_MAX)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
clock->half_period = (uint16_t)half_period;
|
||||
clock->requested_hz = target_clock_hz;
|
||||
clock->actual_hz = source_clock_hz / (2U * (uint32_t)half_period);
|
||||
if (clock->actual_hz == 0U) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_response_field(const rs_sdio_response_t *response, uint32_t high_bit,
|
||||
uint32_t low_bit, uint32_t *value) {
|
||||
uint32_t result = 0U;
|
||||
uint32_t bit;
|
||||
|
||||
if (!rs_sdio_response_range_valid(high_bit, low_bit) || (response == NULL) || (value == NULL)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
for (bit = low_bit; bit <= high_bit; bit++) {
|
||||
uint32_t bit_value;
|
||||
|
||||
if (rs_sdio_response_bit(response, bit, &bit_value) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
result |= bit_value << (bit - low_bit);
|
||||
if (bit == high_bit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*value = result;
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sd_memory_address(rs_sd_memory_card_type_t card_type, uint32_t block,
|
||||
uint32_t *argument) {
|
||||
if (argument == NULL) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
switch (card_type) {
|
||||
case RS_SD_MEMORY_SDSC:
|
||||
if (block > (UINT32_MAX / RS_SD_MEMORY_BLOCK_SIZE)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
*argument = block * RS_SD_MEMORY_BLOCK_SIZE;
|
||||
break;
|
||||
case RS_SD_MEMORY_SDHC:
|
||||
*argument = block;
|
||||
break;
|
||||
default:
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sd_memory_parse_csd(const rs_sdio_response_t *response, rs_sd_memory_info_t *info) {
|
||||
uint32_t csd_structure;
|
||||
uint32_t read_bl_len;
|
||||
uint32_t c_size;
|
||||
uint32_t c_size_mult;
|
||||
uint32_t block_length;
|
||||
uint64_t capacity_bytes;
|
||||
uint64_t capacity_blocks;
|
||||
|
||||
if ((response == NULL) || (info == NULL) ||
|
||||
(rs_sdio_response_field(response, 127U, 126U, &csd_structure) != RS_OK) ||
|
||||
(rs_sdio_response_field(response, 83U, 80U, &read_bl_len) != RS_OK)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if (csd_structure == 1U) {
|
||||
if (rs_sdio_response_field(response, 69U, 48U, &c_size) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
capacity_blocks = ((uint64_t)c_size + UINT64_C(1)) << 10U;
|
||||
capacity_bytes = capacity_blocks * (uint64_t)RS_SD_MEMORY_BLOCK_SIZE;
|
||||
block_length = RS_SD_MEMORY_BLOCK_SIZE;
|
||||
info->card_type = RS_SD_MEMORY_SDHC;
|
||||
info->high_capacity = true;
|
||||
} else if (csd_structure == 0U) {
|
||||
if ((read_bl_len > 11U) || (rs_sdio_response_field(response, 73U, 62U, &c_size) != RS_OK) ||
|
||||
(rs_sdio_response_field(response, 49U, 47U, &c_size_mult) != RS_OK)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
block_length = UINT32_C(1) << read_bl_len;
|
||||
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT64_C(1) << (c_size_mult + 2U)) *
|
||||
(uint64_t)block_length;
|
||||
capacity_blocks = capacity_bytes / (uint64_t)RS_SD_MEMORY_BLOCK_SIZE;
|
||||
info->card_type = RS_SD_MEMORY_SDSC;
|
||||
info->high_capacity = false;
|
||||
} else {
|
||||
return RS_EFORMAT;
|
||||
}
|
||||
if ((capacity_blocks == UINT64_C(0)) || (capacity_blocks > (uint64_t)UINT32_MAX) ||
|
||||
(capacity_bytes == UINT64_C(0))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
info->capacity_blocks = (uint32_t)capacity_blocks;
|
||||
info->block_length = block_length;
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_cmd52_argument(const rs_sdio_cmd52_t *command, uint32_t *argument) {
|
||||
uint32_t value;
|
||||
|
||||
if ((command == NULL) || (argument == NULL) || (command->function > 7U) ||
|
||||
(command->address > UINT32_C(0x1FFFF))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
value =
|
||||
((uint32_t)command->function << 28U) | (command->address << 9U) | (uint32_t)command->data;
|
||||
if (command->write) {
|
||||
value |= UINT32_C(0x80000000);
|
||||
}
|
||||
if (command->raw) {
|
||||
value |= UINT32_C(0x08000000);
|
||||
}
|
||||
*argument = value;
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_cmd53_argument(const rs_sdio_cmd53_t *command, uint32_t *argument) {
|
||||
uint32_t encoded_count;
|
||||
uint32_t value;
|
||||
|
||||
if ((command == NULL) || (argument == NULL) || (command->function > 7U) ||
|
||||
(command->address > UINT32_C(0x1FFFF)) || (command->count == 0U) ||
|
||||
(command->count > UINT16_C(512))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
encoded_count = (command->count == UINT16_C(512)) ? 0U : (uint32_t)command->count;
|
||||
value = ((uint32_t)command->function << 28U) | (command->address << 9U) | encoded_count;
|
||||
if (command->write) {
|
||||
value |= UINT32_C(0x80000000);
|
||||
}
|
||||
if (command->block_mode) {
|
||||
value |= UINT32_C(0x08000000);
|
||||
}
|
||||
if (!command->fixed_address) {
|
||||
value |= UINT32_C(0x04000000);
|
||||
}
|
||||
*argument = value;
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_validate_dma_buffer(const void *buffer, size_t byte_count) {
|
||||
uintptr_t address;
|
||||
|
||||
if (buffer == NULL) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
address = (uintptr_t)buffer;
|
||||
if (((address % sizeof(uint32_t)) != 0U) || !rs_sdio_address_range_valid(address, byte_count)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_descriptor_prepare(rs_sdio_descriptor_t *descriptor, uintptr_t buffer,
|
||||
size_t byte_count, uintptr_t next, bool end, bool irq) {
|
||||
uint32_t control;
|
||||
uintptr_t descriptor_address;
|
||||
|
||||
if ((descriptor == NULL) || !rs_sdio_address_range_valid(buffer, byte_count) ||
|
||||
((buffer % sizeof(uint32_t)) != 0U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
descriptor_address = (uintptr_t)descriptor;
|
||||
if ((descriptor_address <= (uintptr_t)UINT32_MAX) &&
|
||||
!rs_sdio_descriptor_address_valid(descriptor_address)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if (end) {
|
||||
if (next != (uintptr_t)0U) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
} else if ((next == (uintptr_t)0U) || (next > (uintptr_t)UINT32_MAX) ||
|
||||
((next % RS_SDIO_DESCRIPTOR_ALIGNMENT) != 0U) ||
|
||||
!rs_sdio_descriptor_address_valid(next)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
control = end ? (UINT32_C(1) << RS_SDIO_DESC_END_BIT) : (UINT32_C(1) << RS_SDIO_DESC_CHAIN_BIT);
|
||||
if (irq) {
|
||||
control |= UINT32_C(1) << RS_SDIO_DESC_IRQ_BIT;
|
||||
}
|
||||
descriptor->buffer_addr = (uint32_t)buffer;
|
||||
descriptor->byte_count = (uint32_t)byte_count;
|
||||
descriptor->next_addr = (uint32_t)next;
|
||||
descriptor->control_status = control;
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_descriptor_validate(const rs_sdio_descriptor_t *descriptor) {
|
||||
bool end;
|
||||
bool chain;
|
||||
uintptr_t descriptor_address;
|
||||
|
||||
if ((descriptor == NULL) || ((descriptor->buffer_addr % sizeof(uint32_t)) != 0U) ||
|
||||
(descriptor->byte_count == 0U) ||
|
||||
((descriptor->control_status & RS_SDIO_DESCRIPTOR_RESERVED_MASK) != 0U) ||
|
||||
((descriptor->control_status & RS_SDIO_DESCRIPTOR_WRITEBACK_MASK) != 0U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
descriptor_address = (uintptr_t)descriptor;
|
||||
if ((descriptor_address <= (uintptr_t)UINT32_MAX) &&
|
||||
!rs_sdio_descriptor_address_valid(descriptor_address)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
end = (descriptor->control_status & (UINT32_C(1) << RS_SDIO_DESC_END_BIT)) != 0U;
|
||||
chain = (descriptor->control_status & (UINT32_C(1) << RS_SDIO_DESC_CHAIN_BIT)) != 0U;
|
||||
if (end) {
|
||||
if (chain || (descriptor->next_addr != 0U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
} else if (!chain || (descriptor->next_addr == 0U) ||
|
||||
((descriptor->next_addr % RS_SDIO_DESCRIPTOR_ALIGNMENT) != 0U) ||
|
||||
!rs_sdio_descriptor_address_valid((uintptr_t)descriptor->next_addr)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_descriptor_chain_validate(const rs_sdio_descriptor_t *descriptors,
|
||||
uint16_t count, uint32_t total_bytes) {
|
||||
uint32_t total = 0U;
|
||||
uint16_t index;
|
||||
bool found_end = false;
|
||||
|
||||
if ((descriptors == NULL) || (count == 0U) ||
|
||||
(count > (uint16_t)RS_SDIO_MAX_DESCRIPTOR_COUNT) ||
|
||||
(((uintptr_t)descriptors % RS_SDIO_DESCRIPTOR_ALIGNMENT) != 0U) ||
|
||||
((uintptr_t)descriptors > (uintptr_t)UINT32_MAX) ||
|
||||
!rs_sdio_descriptor_address_valid((uintptr_t)descriptors)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
for (index = 0U; index < count; index++) {
|
||||
const rs_sdio_descriptor_t *descriptor = &descriptors[index];
|
||||
bool end;
|
||||
|
||||
if (rs_sdio_descriptor_validate(descriptor) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if (descriptor->byte_count > (UINT32_MAX - total)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
total += descriptor->byte_count;
|
||||
end = (descriptor->control_status & (UINT32_C(1) << RS_SDIO_DESC_END_BIT)) != 0U;
|
||||
if (end) {
|
||||
if (index + 1U != count) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
found_end = true;
|
||||
} else {
|
||||
uintptr_t next = (uintptr_t)&descriptors[index + 1U];
|
||||
|
||||
if ((next > (uintptr_t)UINT32_MAX) || !rs_sdio_descriptor_address_valid(next) ||
|
||||
(descriptor->next_addr != (uint32_t)next)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found_end || ((total_bytes != 0U) && (total != total_bytes))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_descriptor_publish(rs_sdio_descriptor_t *descriptor) {
|
||||
if (rs_sdio_descriptor_validate(descriptor) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
rs_sdio_memory_barrier();
|
||||
descriptor->control_status |= UINT32_C(1) << RS_SDIO_DESC_OWN_BIT;
|
||||
rs_sdio_memory_barrier();
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_sdio_descriptor_publish_chain(rs_sdio_descriptor_t *descriptors, uint16_t count) {
|
||||
uint16_t index;
|
||||
|
||||
if (rs_sdio_descriptor_chain_validate(descriptors, count, 0U) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
for (index = 0U; index < count; index++) {
|
||||
if (rs_sdio_descriptor_publish(&descriptors[index]) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
@@ -22,6 +22,10 @@
|
||||
#define RS_SYSCTRL_PERF_USER_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_USER_WAIT_HI_OFFSET
|
||||
#define RS_SYSCTRL_PERF_DMA_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_DMA_WAIT_LO_OFFSET
|
||||
#define RS_SYSCTRL_PERF_DMA_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_DMA_WAIT_HI_OFFSET
|
||||
#define RS_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET
|
||||
#define RS_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET
|
||||
#define RS_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET
|
||||
#define RS_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET
|
||||
#define RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET
|
||||
#define RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET
|
||||
#define RS_SYSCTRL_PERF_APB4_SYSTEM_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_APB4_SYSTEM_WAIT_LO_OFFSET
|
||||
@@ -84,6 +88,14 @@ static rs_status_t rs_sysctrl_perf_offsets(rs_sysctrl_perf_counter_t counter, ui
|
||||
*low_offset = RS_SYSCTRL_PERF_DMA_WAIT_LO_OFFSET;
|
||||
*high_offset = RS_SYSCTRL_PERF_DMA_WAIT_HI_OFFSET;
|
||||
break;
|
||||
case RS_SYSCTRL_PERF_SDIO0_WAIT:
|
||||
*low_offset = RS_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET;
|
||||
*high_offset = RS_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET;
|
||||
break;
|
||||
case RS_SYSCTRL_PERF_SDIO1_WAIT:
|
||||
*low_offset = RS_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET;
|
||||
*high_offset = RS_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET;
|
||||
break;
|
||||
case RS_SYSCTRL_PERF_APB4_PERIPH_WAIT:
|
||||
*low_offset = RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET;
|
||||
*high_offset = RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET;
|
||||
|
||||
@@ -54,6 +54,9 @@ the root README and subsystem guides.
|
||||
- [ip/opipsram.md](ip/opipsram.md) defines the boot-selected OPI/xSPI and
|
||||
single-clock HyperBus profiles, shared digital PHY, delay-cell boundary,
|
||||
register ABI, central-DMA use, and commercial signoff limits.
|
||||
- [ip/sdio-verification.md](ip/sdio-verification.md) records the standalone
|
||||
SD/SDIO behavioral models, directed coverage, formal bound, and verification
|
||||
blockers.
|
||||
- [ip/ps2.md](ip/ps2.md) defines the Mini SoC PS/2 APB, GPIO pad,
|
||||
interrupt, SDK, and managed-IP integration contract.
|
||||
- The managed [RTC V2 datasheet](../rtl/managed/clusterip/rtc/doc/datasheet.md)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# SD/SDIO Host Verification
|
||||
|
||||
This document is the verification contract for the standalone
|
||||
`apb4_sdio` foundation. The protocol contract remains in
|
||||
[`sdio.md`](sdio.md); this file records evidence and coverage boundaries
|
||||
without changing the IP ABI.
|
||||
|
||||
## Verification components
|
||||
|
||||
| Component | Purpose |
|
||||
| --- | --- |
|
||||
| `tests/rtl/sdio_native_model.sv` | Native SD memory v2 SDSC/SDHC and SDIO-only function model. |
|
||||
| `tests/rtl/sdio_axi_memory_responder.sv` | Deterministic 32-bit AXI responder with backpressure, split, and response-error injection. |
|
||||
| `tests/rtl/sdio_command_tb.sv` | Exact command-frame launch/sample and terminal-bit checks. |
|
||||
| `tests/rtl/sdio_register_tb.sv` | APB register, PIO consume, empty-read, and W1C checks. |
|
||||
| `tests/rtl/sdio_standalone_tb.sv` | APB-driven SDHC/SDSC 1-bit/4-bit PIO and DMA, 1/2/3/5-byte tails, 512-byte FIFO streaming, response-token errors, aborts, clock, response, busy, and CRC/timeout paths. |
|
||||
| `tests/rtl/sdio_sdio_tb.sv` | CMD5, CCCR CMD52 including high-speed capability/enable, CMD53 byte/block fixed/increment, and DAT1 line behavior. |
|
||||
| `tests/rtl/sdio_dual_tb.sv` | Two independent APB/AXI/pad contexts running concurrently. |
|
||||
| `tests/rtl/sdio_dma_stress_tb.sv` | 16-beat bursts, 4 KiB splitting, backpressure, AXI errors, descriptor-fetch/read/write-response abort drain, and writeback observation. |
|
||||
| `rtl/mini/formal/sdio_formal*.sv` | Reduced APB/clock/AXI/descriptor formal harness and properties. |
|
||||
|
||||
The models are verification-only and may use timing controls, tasks, and
|
||||
backing arrays. They do not copy vendor register
|
||||
tables or proprietary descriptor formats.
|
||||
|
||||
## Traceability
|
||||
|
||||
| Requirement | Evidence |
|
||||
| --- | --- |
|
||||
| 400 kHz command flow | `sdio_standalone_tb` programs half-period 90 at 72 MHz and checks `CLOCK_ACTUAL`. |
|
||||
| SDSC byte vs SDHC block addressing | `sdio_sdsc_tb` and `sdio_standalone_tb` use the same command with distinct model geometry. |
|
||||
| 1-bit and 4-bit SDR read/write | `sdio_onebit_tb` plus the 4-bit standalone path; strict token/CRC checks and PIO writes cover both widths. |
|
||||
| Byte lanes and tails | `sdio_standalone_tb` checks lane-0-first packing and 1/2/3/5-byte tails through PIO and DMA in both directions. |
|
||||
| PIO FIFO and remaining | `sdio_standalone_tb` streams a complete 512-byte write/read with consecutive APB accesses and only completes on the final payload word. |
|
||||
| Command/data terminal edges | `sdio_command_tb` and the strict native model reject a missing command end bit or data token; standalone writes verify the final CRC. |
|
||||
| Consecutive PIO reads | `sdio_register_tb` checks read-consume pulses; standalone tests consume two held words without repetition. |
|
||||
| CMD12/R1b and DAT0 busy | Standalone command sequence and parameterized card busy interval. |
|
||||
| CMD5/CMD52/CMD53 | `sdio_sdio_tb`, including CCCR backing and function memory. |
|
||||
| DAT1 active-low IRQ | `sdio_sdio_tb` checks high-level no-IRQ, low-level sticky IRQ, W1C, stopped SDCLK persistence, and high-to-low re-arm. |
|
||||
| Programmable delay and fault hooks | Card tasks provide response/data delay plus one-shot CRC and timeout injection; base CRC/error tests exercise the protocol engines. |
|
||||
| SG chain/writeback | `sdio_dma_stress_tb` runs a bounded two-descriptor chain and checks DONE/OWN writeback. |
|
||||
| AXI 16 beats/4 KiB split/backpressure/error | `sdio_dma_stress_tb` and the reusable responder. |
|
||||
| Abort | `sdio_standalone_tb` aborts command, data, and descriptor fetch through HOST_CTRL; `sdio_dma_stress_tb` aborts after payload AR acceptance and during a stalled write response, checking drain and ERROR/OWN writeback. |
|
||||
| Write response token | `sdio_standalone_tb` injects accepted, CRC-error, and write-error tokens and checks distinct status. |
|
||||
| W1C/counters/descriptor IRQ | `sdio_register_tb` checks persistent ERROR_STATUS W1C, DEBUG semantic bits, IRQ W1C, and descriptor event behavior; standalone checks timeout event counting. |
|
||||
| 100 MHz to 50 MHz SDCLK | `sdio_50mhz_tb` with `half_period=1`. |
|
||||
| Dual-context isolation | `sdio_dual_tb` checks independent status, data, and DAT1 contexts. |
|
||||
|
||||
## Formal boundary
|
||||
|
||||
`make ... formal-sdio` runs a reduced register/DMA/clock proof through the
|
||||
repository SymbiYosys/Yosys/Bitwuzla flow. It does **not** claim a complete
|
||||
native SD host proof. Command/data serializer, card timing, token semantics,
|
||||
PIO FIFO streaming, and full byte-count termination are simulation-covered
|
||||
contracts; they are not certified by this reduced formal target.
|
||||
|
||||
The checked harness covers:
|
||||
|
||||
- APB held-access response reachability and registered response stability;
|
||||
- AXI address-channel legality, 32-bit alignment, maximum 16 beats, and
|
||||
no-cross-4-KiB arithmetic;
|
||||
- conditional payload stability when a valid write beat remains backpressured;
|
||||
- accepted-read abort drain: RREADY remains asserted until the terminal RLAST
|
||||
handshake, with an abort-after-AR cover;
|
||||
- descriptor-count error reachability;
|
||||
- clock transition phase observation and abort/error event observation;
|
||||
- sticky-IRQ event and terminal-event covers.
|
||||
|
||||
The shallow proof intentionally does not claim a complete native data-block
|
||||
proof. Full command/data serializer coverage, arbitrary descriptor memory
|
||||
contents, and two-context formal composition remain simulation/next-stage
|
||||
targets. The current tractable CI invocation is:
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG SYNTH=YOSYS STA=NONE \
|
||||
FORMAL=YES FORMAL_SDIO_DEPTH=12 FORMAL_SDIO_COVER_DEPTH=12 formal-sdio
|
||||
```
|
||||
|
||||
## Evidence status
|
||||
|
||||
**Simulated:** strict native command framing including the final command end
|
||||
bit, R1/R4/R5 responses including the response terminal bit, 1-bit/4-bit
|
||||
data token/payload/final CRC paths, SDSC/SDHC addressing, lane-0-first
|
||||
1/2/3/5-byte tails, 512-byte PIO FIFO streaming, SDIO function memory,
|
||||
accepted/CRC-error/write-error tokens, DAT0 busy, active-low DAT1 sticky IRQ
|
||||
behavior with W1C and stopped SDCLK, AXI split/backpressure/error responses,
|
||||
both DMA directions, descriptor IRQ/writeback, command/data/descriptor abort
|
||||
draining, timeout/counter events, dual-instance concurrency, and 50 MHz phase
|
||||
generation.
|
||||
|
||||
**Formally proven at the reduced bound:** APB response stability, AXI
|
||||
legality/arithmetic, reachable backpressure payload checks, accepted-read abort
|
||||
RREADY obligation/terminal cover, clock phase transition checks,
|
||||
descriptor-count error reachability, and abort/error event observation.
|
||||
Complete native command/data behavior, token decoding, PIO FIFO accounting,
|
||||
and arbitrary block transfers remain simulation evidence only. The proof does
|
||||
not certify complete block transfers or board/PHY behavior.
|
||||
|
||||
**Planned board/PHY coverage:** 3.3 V pad electrical behavior, package and
|
||||
board delay, signal-integrity margins, real SDSC/SDHC vendor cards, SDIO
|
||||
module interoperability, hot removal, UHS/1.8 V, tuning, and long-duration
|
||||
stress. These require hardware, PHY constraints, or specifications not
|
||||
present in this repository.
|
||||
|
||||
## Production RTL blocker closure
|
||||
|
||||
All four production blockers are closed in the current RTL. They have directed
|
||||
simulation evidence; the reduced formal target covers the applicable APB,
|
||||
clock, AXI, and abort obligations but is not a full-host proof:
|
||||
|
||||
1. **Command/data terminal edges:** command serialization advances after the
|
||||
sampled edge, holds the final command bit through card observation, and
|
||||
data write token/CRC terminal holds are phase-safe in both bus widths. The
|
||||
native model now rejects repaired/shifted command frames and missing tokens.
|
||||
2. **PIO read consume:** `PIO_DATA` reads require a valid word, return a
|
||||
registered stable value, and emit one consume pulse per APB read. The
|
||||
register and standalone tests exercise consecutive words.
|
||||
3. **DMA abort drain:** an abort latch suppresses downstream payload delivery,
|
||||
keeps RREADY asserted after AR acceptance, drains response/RLAST, records
|
||||
response/last errors, and only then performs descriptor error writeback and
|
||||
terminalizes. Simulation and formal cover the abort-after-AR case.
|
||||
4. **DAT1 host IRQ:** the pad is double-synchronized in `clk_i`, qualified
|
||||
only while DAT1 is released and the data path is idle, and active-low
|
||||
assertion is latched into IRQ bit 6 with mask/W1C behavior independent of
|
||||
SDCLK.
|
||||
|
||||
These tests are verification evidence, not waivers or certification claims.
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
# Standalone SD/SDIO Host Foundation
|
||||
|
||||
This document is the contract for the standalone `apb4_sdio` IP and its dual
|
||||
Mini SoC integration. The two management windows are `APB4_SDIO0` at
|
||||
`0x1000F000` and `APB4_SDIO1` at `0x10015000`; both are public to the
|
||||
management core and denied to the user core.
|
||||
|
||||
## Scope and maturity
|
||||
|
||||
The host is a single-clock-domain, single-slot native SD host. `clk_i` is the
|
||||
only logic clock. `sdio.sck_o` is a registered output and is never used as a
|
||||
clock for state. Low/high phase clock-enables update outputs and sample inputs.
|
||||
The wrapper has one APB4 management port, one 32-bit native AXI4 master port,
|
||||
and one `sdio_if.dut` pad interface. Instances are independent when integrated.
|
||||
|
||||
The foundation contains generic command, data, CRC, phase, PIO, and bounded
|
||||
scatter-gather DMA building blocks. Card enumeration and card-type policy
|
||||
remain software driven.
|
||||
|
||||
## Supported behavior
|
||||
|
||||
- Native 1-bit and 4-bit SDR transfers.
|
||||
- Generic six-bit command index and 32-bit argument.
|
||||
- No response and R1, R1b, R2, R3, R4, R5, R6, and R7 response framing.
|
||||
- Command index/end checks and CRC7 checks where the response type defines CRC.
|
||||
R3 and R4 do not force a CRC check.
|
||||
- Data token, payload, CRC16, write response, DAT0 busy, and command/data/busy
|
||||
timeout state are present in the protocol engines.
|
||||
- Write response tokens are checked in full: accepted continues to DAT0 busy,
|
||||
CRC-error and write-error tokens terminate with distinct error semantics,
|
||||
and only the accepted path uses `TIMEOUT_BUSY`.
|
||||
- `TIMEOUT_CMD` covers command launch and response framing, `TIMEOUT_DATA`
|
||||
covers data tokens, payload, CRC, and write-response tokens, and
|
||||
`TIMEOUT_BUSY` covers only R1b/DAT0 busy phases.
|
||||
- The 4-bit serializer keeps independent CRC16 state for each DAT lane; CRC
|
||||
errors remain sticky until the next transaction or reset.
|
||||
- PIO word transfers use a 32-bit word and four byte strobes. Lane 0
|
||||
(`word[7:0]`/`WSTRB[0]`) is the first wire byte; each byte is serialized
|
||||
MSB first. Descriptor DMA uses the same stream contract and supports a
|
||||
byte-granular final word in both directions.
|
||||
- The intended software protocols are SD Memory v2 SDSC/SDHC and SDIO v2
|
||||
CMD5/CMD52/CMD53. SDSC byte addressing and SDHC block addressing are software
|
||||
policy, not hard-coded card enumeration in the RTL.
|
||||
- SDIO high-speed initialization is software sequenced: CCCR speed capability
|
||||
is read, the high-speed enable bit is written and read back, and only a
|
||||
verified enable permits the host clock/info flag to change.
|
||||
- The capability word advertises SD Memory v2, SDIO v2, 1/4-bit SDR, CRC, PIO,
|
||||
SG DMA, and 16-beat maximum bursts.
|
||||
|
||||
The implementation does not claim SD Association compliance or certification.
|
||||
Normative specifications and any required licensing must be obtained and
|
||||
reviewed separately before a compliance claim.
|
||||
|
||||
## Clock and I/O boundary
|
||||
|
||||
`CLOCK_CTRL[0]` enables the host clock and `CLOCK_CTRL[23:8]` supplies the
|
||||
half-period in `clk_i` cycles. A value of zero is treated as one cycle. The
|
||||
actual frequency is approximately:
|
||||
|
||||
```
|
||||
sdclk_hz = clk_i_hz / (2 * half_period)
|
||||
```
|
||||
|
||||
Clock stop completes a high phase and stops only low. Command/data output
|
||||
changes occur on launch phase enables; command/data inputs are consumed on
|
||||
sample phase enables. The current SoC profile is 72 MHz, so its evidence
|
||||
boundary is 400 kHz, 24 MHz, and 36 MHz integer-divider modes. A 50 MHz
|
||||
SDCLK requires a separately validated `clk_i >= 100 MHz` environment. No
|
||||
statement about pad, package, board, voltage, or I/O timing signoff follows
|
||||
from the RTL clock test.
|
||||
|
||||
The MVP is fixed-present and 3.3 V SDR. Card-detect, write-protect,
|
||||
power-enable, hot-remove recovery, UHS, 1.8 V switching, DDR, eMMC, MMC,
|
||||
SPI-mode operation, 8-bit mode, tuning/DLL, CQE, and combo-card policy are
|
||||
unsupported.
|
||||
|
||||
## APB4 register contract
|
||||
|
||||
The window is 4 KiB and uses the offsets in `rtl/ip/storage/sdio_define.svh`.
|
||||
An APB access is accepted in the enable phase and completes with a registered
|
||||
one-cycle `PREADY`. Reads have no side effects. Unknown offsets, out-of-window
|
||||
addresses, malformed start writes, and protected configuration writes while
|
||||
active return `PSLVERR`. Command, data, and DMA starts are write pulses.
|
||||
`IRQ_STATUS` and `ERROR_STATUS` are write-one-to-clear. `IRQ` is the masked
|
||||
status reduction.
|
||||
|
||||
`ERROR_STATUS` is event-latched: command/data CRC and timeout, AXI, protocol,
|
||||
and abort events set bits once and remain set until software writes one to the
|
||||
corresponding bit. Level/status signals are not continuously re-ORed after a
|
||||
clear. `DEBUG[0]` is PIO data valid and `DEBUG[1]` is PIO transmit ready.
|
||||
`STALL_COUNT` counts cycles in which a valid PIO/DMA stream word is held
|
||||
without a ready consumer.
|
||||
|
||||
| Offset | Register | Access |
|
||||
| ---: | --- | --- |
|
||||
| 0x000 | IP_ID | RO |
|
||||
| 0x004 | IP_VERSION | RO |
|
||||
| 0x008 | CAPABILITY | RO |
|
||||
| 0x00c | HOST_CTRL | RW; enable, abort, IRQ enable |
|
||||
| 0x010 | CLOCK_CTRL | RW; enable and half-period |
|
||||
| 0x014 | CLOCK_ACTUAL | RO |
|
||||
| 0x018 | BUS_CTRL | RW; 1-bit/4-bit |
|
||||
| 0x01c-0x024 | TIMEOUT_CMD/DATA/BUSY | RW |
|
||||
| 0x028-0x02c | STATUS/PRESENT | RO |
|
||||
| 0x040-0x060 | CMD_ARG, CMD_CFG, CMD_START, CMD_STATUS, RESP0..RESP4 | mixed |
|
||||
| 0x080-0x098 | block/data/PIO/FIFO controls | mixed |
|
||||
| 0x0c0-0x0dc | descriptor and DMA status | mixed |
|
||||
| 0x100-0x124 | IRQ, error, last-command, and counters | mixed |
|
||||
|
||||
R2 is 136 bits and is exposed in `RESP0..RESP4`; the upper register contains
|
||||
the upper eight response bits in its low byte. Reads are side-effect free
|
||||
except for `PIO_DATA`: a valid `PIO_DATA` read consumes exactly one held word,
|
||||
and an empty `PIO_DATA` read completes with `PSLVERR`.
|
||||
|
||||
## Descriptor ABI
|
||||
|
||||
Each descriptor is 16-byte aligned and contains four little-endian 32-bit
|
||||
words:
|
||||
|
||||
| Word | Name | Meaning |
|
||||
| ---: | --- | --- |
|
||||
| 0 | BUFFER_ADDR | 32-bit aligned memory buffer |
|
||||
| 1 | BYTE_COUNT | positive byte count |
|
||||
| 2 | NEXT_ADDR | 16-byte aligned next descriptor; zero at END |
|
||||
| 3 | CONTROL_STATUS | OWN, CHAIN, END, IRQ; DONE and ERROR writeback |
|
||||
|
||||
`OWN` is bit 0, `CHAIN` bit 1, `END` bit 2, `IRQ` bit 3, `DONE` bit 16, and
|
||||
`ERROR` bit 17. Software publishes the descriptor and buffer, executes its
|
||||
platform memory barrier, and sets OWN last. Hardware clears OWN and writes
|
||||
DONE or ERROR after payload completion or a precise failure. `DESC_COUNT` is
|
||||
an inclusive hardware traversal bound; zero length, unaligned descriptor or
|
||||
buffer, malformed chain, loop, early END, total-length mismatch, and an
|
||||
unowned active descriptor are errors. A chain must terminate with END before
|
||||
the bound.
|
||||
|
||||
The descriptor `IRQ` flag raises the visible `IRQ_STATUS.DMA_DONE` event when
|
||||
that descriptor is written back; the same bit is also raised for terminal DMA
|
||||
completion. Descriptor IRQ events obey the normal mask and W1C rules.
|
||||
|
||||
AXI payload bursts are INCR, 32-bit, at most 16 beats, and never cross 4 KiB.
|
||||
Read response errors, write response errors, bad IDs, and RLAST mismatches are
|
||||
reported. A descriptor fetch address with low 12-bit value `0xFF0` or above
|
||||
is rejected so the 16-byte fetch remains within one 4 KiB region. A final
|
||||
descriptor word may use any non-zero contiguous low-byte WSTRB pattern that
|
||||
exactly matches its remaining byte count. The initial buffer address must be
|
||||
aligned; software must use a bounce buffer for an unaligned head. Abort stops
|
||||
new work, drains an accepted AXI transaction, clears OWN, and completes with
|
||||
ERROR.
|
||||
There are no multiple IDs or outstanding transactions.
|
||||
|
||||
## Software sequencing and integration contract
|
||||
|
||||
1. Reset the host, program clock/width/timeouts, and leave host disabled while
|
||||
changing protected configuration.
|
||||
2. Program command and response policy, issue `CMD_START`, and inspect
|
||||
`CMD_STATUS` and response words.
|
||||
3. Program block/data policy. For PIO, write/read `PIO_DATA`; for DMA, fill a
|
||||
descriptor chain, publish OWN last, program `DESC_BASE/DESC_COUNT`, set
|
||||
`DATA_CFG.DMA`, and issue one `DATA_START` operation. `DATA_START` starts
|
||||
DMA and the card data engine atomically. `DMA_CTRL.START` is retained as a
|
||||
compatibility alias for the same coordinated operation and must not be
|
||||
used as an independent memory-transfer start.
|
||||
4. Enable only the desired IRQ bits and clear sticky status with W1C.
|
||||
5. On timeout, CRC, protocol, or AXI error, issue abort/reset, inspect the
|
||||
first error snapshot, and re-enumerate in software.
|
||||
|
||||
Each wrapper's AXI interface is a native 32-bit master. The SoC assigns
|
||||
`sdio0` and `sdio1` to AXI master IDs 3 and 4, respectively; management,
|
||||
user, and central DMA retain IDs 0, 1, and 2. The per-master wait counters
|
||||
are snapshotted by SystemCtrl at `0x08C/0x090` and `0x094/0x098`.
|
||||
|
||||
`sdio0` remains on GPIO15..20 ALT0. `sdio1` uses dedicated
|
||||
`sdio1_clk_o_pad`, `sdio1_cmd_io_pad`, and
|
||||
`sdio1_dat0_io_pad` through `sdio1_dat3_io_pad` pads.
|
||||
The canonical pin map leaves these pads unbound in the FPGA profile until a
|
||||
board package and 3.3 V bank assignment are approved; the existing LVCMOS18
|
||||
constraints are not evidence for SDIO electrical compliance.
|
||||
|
||||
The standalone IP does not allocate memory, perform cache maintenance,
|
||||
implement an IOMMU/firewall, or provide a hosted/runtime C API. The SoC
|
||||
integration only supplies the two native AXI/APB/IRQ/pad paths; the SDK driver
|
||||
is maintained by a separate workstream.
|
||||
+6
-3
@@ -12,7 +12,8 @@ the implementation is separated into `sysctrl_reg`, `sysctrl_core`, and
|
||||
`sysctrl_if` is the SoC-facing contract. It drives the selected user core,
|
||||
per-core reset mask, user-bus enable, selected user IP, performance
|
||||
enable/clear, and sticky terminal test result. It receives user-bus idle,
|
||||
fault metadata, eight wait counters, and asynchronous RTC wake. `sysctrl_core`
|
||||
fault metadata, ten wait counters (including independent SDIO0 and SDIO1
|
||||
master wait counters), and asynchronous RTC wake. `sysctrl_core`
|
||||
synchronizes RTC wake with the Common `cdc_sync` primitive before storing its
|
||||
sticky state.
|
||||
|
||||
@@ -51,10 +52,12 @@ offset macros.
|
||||
| `0x01C` | `PLL_STATUS` | RO | Active profile, valid, busy, error/error reason, safe clock, lock, and capability. |
|
||||
| `0x020` | `USER_CORE_RESET` | RW | Per-user-core active-high reset mask. All ones stops the user bus; a one-hot deassertion starts the selected core. |
|
||||
| `0x024` | `USER_CORE_STATUS` | RW1C/RO | Selected core, user-bus enabled/idle/draining, and bit 11 configuration-error W1C. |
|
||||
| `0x028` | `FAULT_MASTER` | RO | First fault master. |
|
||||
| `0x028` | `FAULT_MASTER` | RO | First fault master, a three-bit AXI master ID (0..4). |
|
||||
| `0x02C` | `FAULT_DETAIL` | RO | First raw RIB response code. |
|
||||
| `0x040` | `PERF_CTRL` | RW | Bit 0 enable, bit 1 clear pulse, bit 2 snapshot pulse. |
|
||||
| `0x044`-`0x080` | `PERF_*_WAIT_{LO,HI}` | RO | Snapshot of management, user, DMA, APB4, APB, SDRAM, PSRAM, and flash wait counters. |
|
||||
| `0x044`-`0x098` | `PERF_*_WAIT_{LO,HI}` | RO | Snapshot of management, user, central DMA, SDIO0, SDIO1, APB4, SDRAM, PSRAM, and flash wait counters. |
|
||||
| `0x08C`/`0x090` | `PERF_SDIO0_WAIT_LO/HI` | RO | Independent SDIO0 AXI master wait snapshot. |
|
||||
| `0x094`/`0x098` | `PERF_SDIO1_WAIT_LO/HI` | RO | Independent SDIO1 AXI master wait snapshot. |
|
||||
| `0x084` | `TEST_STATUS` | RW-once/RO | Full-word write with bit 31 set records sticky done, bit 0 pass, and bits `[15:8]` result code. |
|
||||
| `0x088` | `RTC_WAKE_STATUS` | RW1C/RO | Bit 0 synchronized live wake; bit 1 sticky wake, cleared by writing one to bit 1. |
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ include. Before invoking an FPGA tool, generate the selected profile with
|
||||
`build/<variant>/generated/pin_map/rtl` to the tool include path. Logical pad
|
||||
names and wrapper bindings are owned by `rtl/mini/pin_map/pin_map.json`;
|
||||
package locations remain in the board `.xdc` file.
|
||||
The dedicated SDIO1 pads are intentionally left unbound by the FPGA profile
|
||||
until a board package location and 3.3 V I/O-bank assignment are approved;
|
||||
the existing LVCMOS18 constraints do not establish SDIO electrical support.
|
||||
|
||||
FPGA constraints are board-specific implementation inputs, not generic ASIC
|
||||
RTL. Validate changes with the relevant FPGA tool flow and hardware target; do
|
||||
|
||||
+2
-1
@@ -88,5 +88,6 @@ verification contract are documented in
|
||||
The Mini SoC APB4 platform block is `apb4_system` in `rtl/mini/top`. It owns
|
||||
archinfo, RTC, watchdog, PWM, PS/2, RNG, CRC, and the user-IP APB window.
|
||||
`apb4_periph` remains the APB4 peripheral container. Topology generation
|
||||
and the `soc_apb4_system_fabric.svh` include are documented in
|
||||
includes both management-only native SDIO hosts (`sdio0` on GPIO15..20 ALT0
|
||||
and `sdio1` on dedicated pads), and the `soc_apb4_system_fabric.svh` include is documented in
|
||||
[Mini SoC Topology](mini/integration/README.md).
|
||||
@@ -46,6 +46,14 @@ module sysctrl_core (
|
||||
localparam sysctrl_offset_t PerfUserWaitHi = sysctrl_offset_t'(`APB4_SYSCTRL__PERF_USER_WAIT_HI);
|
||||
localparam sysctrl_offset_t PerfDmaWaitLo = sysctrl_offset_t'(`APB4_SYSCTRL__PERF_DMA_WAIT_LO);
|
||||
localparam sysctrl_offset_t PerfDmaWaitHi = sysctrl_offset_t'(`APB4_SYSCTRL__PERF_DMA_WAIT_HI);
|
||||
localparam sysctrl_offset_t PerfSdio0WaitLo =
|
||||
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO0_WAIT_LO);
|
||||
localparam sysctrl_offset_t PerfSdio0WaitHi =
|
||||
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO0_WAIT_HI);
|
||||
localparam sysctrl_offset_t PerfSdio1WaitLo =
|
||||
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO1_WAIT_LO);
|
||||
localparam sysctrl_offset_t PerfSdio1WaitHi =
|
||||
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO1_WAIT_HI);
|
||||
localparam sysctrl_offset_t PerfApb4PeriphWaitLo =
|
||||
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_APB4_PERIPH_WAIT_LO);
|
||||
localparam sysctrl_offset_t PerfApb4PeriphWaitHi =
|
||||
@@ -90,11 +98,13 @@ module sysctrl_core (
|
||||
logic [2:0] s_fault_detail_d, s_fault_detail_q;
|
||||
logic [31:0] s_fault_addr_d, s_fault_addr_q;
|
||||
logic [31:0] s_fault_count_d, s_fault_count_q;
|
||||
logic [1:0] s_fault_master_d, s_fault_master_q;
|
||||
logic [2:0] s_fault_master_d, s_fault_master_q;
|
||||
logic s_perf_en_d, s_perf_en_q;
|
||||
logic [63:0] s_perf_mgmt_wait_d, s_perf_mgmt_wait_q;
|
||||
logic [63:0] s_perf_user_wait_d, s_perf_user_wait_q;
|
||||
logic [63:0] s_perf_dma_wait_d, s_perf_dma_wait_q;
|
||||
logic [63:0] s_perf_sdio0_wait_d, s_perf_sdio0_wait_q;
|
||||
logic [63:0] s_perf_sdio1_wait_d, s_perf_sdio1_wait_q;
|
||||
logic [63:0] s_perf_apb4_periph_wait_d, s_perf_apb4_periph_wait_q;
|
||||
logic [63:0] s_perf_apb4_system_wait_d, s_perf_apb4_system_wait_q;
|
||||
logic [63:0] s_perf_sdram_wait_d, s_perf_sdram_wait_q;
|
||||
@@ -298,6 +308,8 @@ module sysctrl_core (
|
||||
s_perf_mgmt_wait_d = s_perf_mgmt_wait_q;
|
||||
s_perf_user_wait_d = s_perf_user_wait_q;
|
||||
s_perf_dma_wait_d = s_perf_dma_wait_q;
|
||||
s_perf_sdio0_wait_d = s_perf_sdio0_wait_q;
|
||||
s_perf_sdio1_wait_d = s_perf_sdio1_wait_q;
|
||||
s_perf_apb4_periph_wait_d = s_perf_apb4_periph_wait_q;
|
||||
s_perf_apb4_system_wait_d = s_perf_apb4_system_wait_q;
|
||||
s_perf_sdram_wait_d = s_perf_sdram_wait_q;
|
||||
@@ -310,6 +322,8 @@ module sysctrl_core (
|
||||
s_perf_mgmt_wait_d = sysctrl.perf_mgmt_wait_i;
|
||||
s_perf_user_wait_d = sysctrl.perf_user_wait_i;
|
||||
s_perf_dma_wait_d = sysctrl.perf_dma_wait_i;
|
||||
s_perf_sdio0_wait_d = sysctrl.perf_sdio0_wait_i;
|
||||
s_perf_sdio1_wait_d = sysctrl.perf_sdio1_wait_i;
|
||||
s_perf_apb4_periph_wait_d = sysctrl.perf_apb4_periph_wait_i;
|
||||
s_perf_apb4_system_wait_d = sysctrl.perf_apb4_system_wait_i;
|
||||
s_perf_sdram_wait_d = sysctrl.perf_sdram_wait_i;
|
||||
@@ -370,6 +384,8 @@ module sysctrl_core (
|
||||
s_perf_mgmt_wait_q <= '0;
|
||||
s_perf_user_wait_q <= '0;
|
||||
s_perf_dma_wait_q <= '0;
|
||||
s_perf_sdio0_wait_q <= '0;
|
||||
s_perf_sdio1_wait_q <= '0;
|
||||
s_perf_apb4_periph_wait_q <= '0;
|
||||
s_perf_apb4_system_wait_q <= '0;
|
||||
s_perf_sdram_wait_q <= '0;
|
||||
@@ -406,6 +422,8 @@ module sysctrl_core (
|
||||
s_perf_mgmt_wait_q <= s_perf_mgmt_wait_d;
|
||||
s_perf_user_wait_q <= s_perf_user_wait_d;
|
||||
s_perf_dma_wait_q <= s_perf_dma_wait_d;
|
||||
s_perf_sdio0_wait_q <= s_perf_sdio0_wait_d;
|
||||
s_perf_sdio1_wait_q <= s_perf_sdio1_wait_d;
|
||||
s_perf_apb4_periph_wait_q <= s_perf_apb4_periph_wait_d;
|
||||
s_perf_apb4_system_wait_q <= s_perf_apb4_system_wait_d;
|
||||
s_perf_sdram_wait_q <= s_perf_sdram_wait_d;
|
||||
@@ -451,7 +469,7 @@ module sysctrl_core (
|
||||
{(8 - `USER_CORESEL_WIDTH) {1'b0}},
|
||||
s_sysctrl_coresel_q
|
||||
};
|
||||
FaultMaster: read_data_o = {30'd0, s_fault_master_q};
|
||||
FaultMaster: read_data_o = {29'd0, s_fault_master_q};
|
||||
FaultDetail: read_data_o = {29'd0, s_fault_detail_q};
|
||||
PerfCtrl: read_data_o = {31'd0, s_perf_en_q};
|
||||
PerfMgmtWaitLo: read_data_o = s_perf_mgmt_wait_q[31:0];
|
||||
@@ -460,6 +478,10 @@ module sysctrl_core (
|
||||
PerfUserWaitHi: read_data_o = s_perf_user_wait_q[63:32];
|
||||
PerfDmaWaitLo: read_data_o = s_perf_dma_wait_q[31:0];
|
||||
PerfDmaWaitHi: read_data_o = s_perf_dma_wait_q[63:32];
|
||||
PerfSdio0WaitLo: read_data_o = s_perf_sdio0_wait_q[31:0];
|
||||
PerfSdio0WaitHi: read_data_o = s_perf_sdio0_wait_q[63:32];
|
||||
PerfSdio1WaitLo: read_data_o = s_perf_sdio1_wait_q[31:0];
|
||||
PerfSdio1WaitHi: read_data_o = s_perf_sdio1_wait_q[63:32];
|
||||
PerfApb4PeriphWaitLo: read_data_o = s_perf_apb4_periph_wait_q[31:0];
|
||||
PerfApb4PeriphWaitHi: read_data_o = s_perf_apb4_periph_wait_q[63:32];
|
||||
PerfApb4SystemWaitLo: read_data_o = s_perf_apb4_system_wait_q[31:0];
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
`define APB4_SYSCTRL__PERF_USER_WAIT_HI `SOC_SYSCTRL_PERF_USER_WAIT_HI_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_DMA_WAIT_LO `SOC_SYSCTRL_PERF_DMA_WAIT_LO_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_DMA_WAIT_HI `SOC_SYSCTRL_PERF_DMA_WAIT_HI_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_SDIO0_WAIT_LO `SOC_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_SDIO0_WAIT_HI `SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_SDIO1_WAIT_LO `SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_SDIO1_WAIT_HI `SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_APB4_PERIPH_WAIT_LO `SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_APB4_PERIPH_WAIT_HI `SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET
|
||||
`define APB4_SYSCTRL__PERF_APB4_SYSTEM_WAIT_LO `SOC_SYSCTRL_PERF_APB4_SYSTEM_WAIT_LO_OFFSET
|
||||
|
||||
@@ -9,13 +9,15 @@ interface sysctrl_if ();
|
||||
logic user_bus_enable_o;
|
||||
logic user_bus_idle_i;
|
||||
logic fault_access_i;
|
||||
logic [ 1:0] fault_master_i;
|
||||
logic [ 2:0] fault_master_i;
|
||||
logic [ 2:0] fault_code_i;
|
||||
logic perf_enable_o;
|
||||
logic perf_clear_o;
|
||||
logic [ 63:0] perf_mgmt_wait_i;
|
||||
logic [ 63:0] perf_user_wait_i;
|
||||
logic [ 63:0] perf_dma_wait_i;
|
||||
logic [ 63:0] perf_sdio0_wait_i;
|
||||
logic [ 63:0] perf_sdio1_wait_i;
|
||||
logic [ 63:0] perf_apb4_periph_wait_i;
|
||||
logic [ 63:0] perf_apb4_system_wait_i;
|
||||
logic [ 63:0] perf_sdram_wait_i;
|
||||
@@ -35,6 +37,8 @@ interface sysctrl_if ();
|
||||
input perf_mgmt_wait_i,
|
||||
input perf_user_wait_i,
|
||||
input perf_dma_wait_i,
|
||||
input perf_sdio0_wait_i,
|
||||
input perf_sdio1_wait_i,
|
||||
input perf_apb4_periph_wait_i,
|
||||
input perf_apb4_system_wait_i,
|
||||
input perf_sdram_wait_i,
|
||||
|
||||
+203
-48
@@ -1,62 +1,217 @@
|
||||
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
interface sdio_if ();
|
||||
logic sck_o;
|
||||
logic cmd_oe_o;
|
||||
logic cmd_di_i;
|
||||
logic cmd_do_o;
|
||||
logic [3:0] dat_oe_o;
|
||||
logic [3:0] dat_di_i;
|
||||
logic [3:0] dat_do_o;
|
||||
logic irq_o;
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
modport dut(
|
||||
output sck_o,
|
||||
output cmd_oe_o,
|
||||
input cmd_di_i,
|
||||
output cmd_do_o,
|
||||
output dat_oe_o,
|
||||
input dat_di_i,
|
||||
output dat_do_o,
|
||||
output irq_o
|
||||
);
|
||||
endinterface
|
||||
|
||||
|
||||
module apb4_sdio (
|
||||
// verilog_format: off -- preserve reviewed column alignment
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
apb4_if.slave apb4,
|
||||
sdio_if.dut sdio
|
||||
module apb4_sdio #(
|
||||
parameter int InputClockHz = 72_000_000,
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int DescCount = 16
|
||||
) (
|
||||
// verilog_format: off -- preserve the APB, AXI, and pad column alignment
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
apb4_if.slave apb4,
|
||||
axi4_if.master dma_axi4,
|
||||
sdio_if.dut sdio
|
||||
// verilog_format: on
|
||||
);
|
||||
logic s_busy;
|
||||
logic s_host_enable;
|
||||
logic s_host_irq_enable;
|
||||
logic s_clock_enable;
|
||||
logic [ 15:0] s_half_period;
|
||||
logic [ 1:0] s_bus_width;
|
||||
logic [ 31:0] s_timeout_cmd;
|
||||
logic [ 31:0] s_timeout_data;
|
||||
logic [ 31:0] s_timeout_busy;
|
||||
logic [ 5:0] s_cmd_index;
|
||||
logic [ 31:0] s_cmd_arg;
|
||||
sdio_pkg::sdio_resp_type_e s_cmd_resp_type;
|
||||
logic s_cmd_crc_check;
|
||||
logic s_cmd_index_check;
|
||||
logic s_cmd_start;
|
||||
logic [ 15:0] s_block_size;
|
||||
logic [ 15:0] s_block_count;
|
||||
logic s_data_direction;
|
||||
logic s_data_dma_enable;
|
||||
logic s_data_block_mode;
|
||||
logic s_data_fixed_addr;
|
||||
logic s_data_start;
|
||||
logic s_pio_wvalid;
|
||||
logic [ 31:0] s_pio_wdata;
|
||||
logic [ 3:0] s_pio_wstrb;
|
||||
logic [ 31:0] s_desc_base;
|
||||
logic [ 15:0] s_desc_count;
|
||||
logic s_dma_start;
|
||||
logic s_dma_abort;
|
||||
logic s_host_abort;
|
||||
logic s_irq_dat1_enable;
|
||||
logic s_irq;
|
||||
logic [ 31:0] s_status;
|
||||
logic [ 31:0] s_clock_actual;
|
||||
logic [ 31:0] s_cmd_status;
|
||||
logic [135:0] s_response;
|
||||
logic [ 31:0] s_data_status;
|
||||
logic [ 31:0] s_fifo_status;
|
||||
logic [ 31:0] s_dma_status;
|
||||
logic [ 31:0] s_current_desc;
|
||||
logic [ 31:0] s_bytes_done;
|
||||
logic [ 31:0] s_dma_err_addr;
|
||||
logic [ 31:0] s_dma_err;
|
||||
logic [ 31:0] s_err_stat;
|
||||
logic [ 5:0] s_last_cmd;
|
||||
logic [ 31:0] s_crc_err_count;
|
||||
logic [ 31:0] s_timeout_count;
|
||||
logic [ 31:0] s_axi_err_count;
|
||||
logic [ 31:0] s_stall_count;
|
||||
logic [ 7:0] s_irq_event;
|
||||
logic [ 31:0] s_pio_rdata;
|
||||
logic s_pio_valid;
|
||||
logic s_pio_ready;
|
||||
logic s_pio_read_consume;
|
||||
sdio_if u_sdio_core_if ();
|
||||
|
||||
// This placeholder accepts no APB4 requests (ready remains low) and reports
|
||||
// no errors; all SDIO outputs are deliberately held inactive.
|
||||
logic s_dummy_clk;
|
||||
logic s_dummy_rst_n;
|
||||
assign u_sdio_core_if.cmd_di_i = sdio.cmd_di_i;
|
||||
assign u_sdio_core_if.dat_di_i = sdio.dat_di_i;
|
||||
assign sdio.sck_o = u_sdio_core_if.sck_o;
|
||||
assign sdio.cmd_oe_o = u_sdio_core_if.cmd_oe_o;
|
||||
assign sdio.cmd_do_o = u_sdio_core_if.cmd_do_o;
|
||||
assign sdio.dat_oe_o = u_sdio_core_if.dat_oe_o;
|
||||
assign sdio.dat_do_o = u_sdio_core_if.dat_do_o;
|
||||
|
||||
assign s_dummy_clk = clk_i;
|
||||
assign s_dummy_rst_n = rst_n_i;
|
||||
// apb4
|
||||
assign apb4.prdata = '0;
|
||||
assign apb4.pready = '0;
|
||||
assign apb4.pslverr = 1'b0;
|
||||
// sdio
|
||||
assign sdio.sck_o = '0;
|
||||
assign sdio.cmd_oe_o = '0;
|
||||
assign sdio.cmd_do_o = '0;
|
||||
assign sdio.dat_oe_o = '0;
|
||||
assign sdio.dat_do_o = '0;
|
||||
assign sdio.irq_o = '0;
|
||||
sdio_reg u_sdio_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.busy_i (s_busy),
|
||||
.status_i (s_status),
|
||||
.clock_actual_i (s_clock_actual),
|
||||
.cmd_status_i (s_cmd_status),
|
||||
.response_i (s_response),
|
||||
.data_status_i (s_data_status),
|
||||
.fifo_status_i (s_fifo_status),
|
||||
.dma_status_i (s_dma_status),
|
||||
.current_desc_i (s_current_desc),
|
||||
.bytes_done_i (s_bytes_done),
|
||||
.dma_error_addr_i (s_dma_err_addr),
|
||||
.dma_error_i (s_dma_err),
|
||||
.error_status_i (s_err_stat),
|
||||
.last_cmd_i (s_last_cmd),
|
||||
.crc_error_count_i (s_crc_err_count),
|
||||
.timeout_count_i (s_timeout_count),
|
||||
.axi_error_count_i (s_axi_err_count),
|
||||
.stall_count_i (s_stall_count),
|
||||
.irq_event_i (s_irq_event),
|
||||
.pio_rdata_i (s_pio_rdata),
|
||||
.pio_valid_i (s_pio_valid),
|
||||
.pio_ready_i (s_pio_ready),
|
||||
.pio_read_consume_o(s_pio_read_consume),
|
||||
.host_enable_o (s_host_enable),
|
||||
.host_irq_enable_o (s_host_irq_enable),
|
||||
.clock_enable_o (s_clock_enable),
|
||||
.half_period_o (s_half_period),
|
||||
.bus_width_o (s_bus_width),
|
||||
.timeout_cmd_o (s_timeout_cmd),
|
||||
.timeout_data_o (s_timeout_data),
|
||||
.timeout_busy_o (s_timeout_busy),
|
||||
.cmd_index_o (s_cmd_index),
|
||||
.cmd_arg_o (s_cmd_arg),
|
||||
.cmd_resp_type_o (s_cmd_resp_type),
|
||||
.cmd_crc_check_o (s_cmd_crc_check),
|
||||
.cmd_index_check_o (s_cmd_index_check),
|
||||
.cmd_start_o (s_cmd_start),
|
||||
.block_size_o (s_block_size),
|
||||
.block_count_o (s_block_count),
|
||||
.data_direction_o (s_data_direction),
|
||||
.data_dma_enable_o (s_data_dma_enable),
|
||||
.data_block_mode_o (s_data_block_mode),
|
||||
.data_fixed_addr_o (s_data_fixed_addr),
|
||||
.data_start_o (s_data_start),
|
||||
.pio_wvalid_o (s_pio_wvalid),
|
||||
.pio_wdata_o (s_pio_wdata),
|
||||
.pio_wstrb_o (s_pio_wstrb),
|
||||
.desc_base_o (s_desc_base),
|
||||
.desc_count_o (s_desc_count),
|
||||
.dma_start_o (s_dma_start),
|
||||
.dma_abort_o (s_dma_abort),
|
||||
.host_abort_o (s_host_abort),
|
||||
.irq_dat1_enable_o (s_irq_dat1_enable),
|
||||
.irq_o (s_irq),
|
||||
.apb4 (apb4)
|
||||
);
|
||||
|
||||
sdio_core #(
|
||||
.InputClockHz(InputClockHz),
|
||||
.AddrWidth (AddrWidth),
|
||||
.DataWidth (DataWidth),
|
||||
.DescCount (DescCount)
|
||||
) u_sdio_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.host_enable_i (s_host_enable),
|
||||
.host_irq_enable_i (s_host_irq_enable),
|
||||
.clock_enable_i (s_clock_enable),
|
||||
.half_period_i (s_half_period),
|
||||
.bus_width_i (s_bus_width),
|
||||
.timeout_cmd_i (s_timeout_cmd),
|
||||
.timeout_data_i (s_timeout_data),
|
||||
.timeout_busy_i (s_timeout_busy),
|
||||
.cmd_index_i (s_cmd_index),
|
||||
.cmd_arg_i (s_cmd_arg),
|
||||
.cmd_resp_type_i (s_cmd_resp_type),
|
||||
.cmd_crc_check_i (s_cmd_crc_check),
|
||||
.cmd_index_check_i (s_cmd_index_check),
|
||||
.cmd_start_i (s_cmd_start),
|
||||
.block_size_i (s_block_size),
|
||||
.block_count_i (s_block_count),
|
||||
.data_direction_i (s_data_direction),
|
||||
.data_dma_enable_i (s_data_dma_enable),
|
||||
.data_block_mode_i (s_data_block_mode),
|
||||
.data_fixed_addr_i (s_data_fixed_addr),
|
||||
.data_start_i (s_data_start),
|
||||
.pio_wvalid_i (s_pio_wvalid),
|
||||
.pio_wdata_i (s_pio_wdata),
|
||||
.pio_wstrb_i (s_pio_wstrb),
|
||||
.desc_base_i (s_desc_base),
|
||||
.desc_count_i (s_desc_count),
|
||||
.dma_start_i (s_dma_start),
|
||||
.dma_abort_i (s_dma_abort),
|
||||
.host_abort_i (s_host_abort),
|
||||
.irq_dat1_enable_i (s_irq_dat1_enable),
|
||||
.status_o (s_status),
|
||||
.clock_actual_o (s_clock_actual),
|
||||
.cmd_status_o (s_cmd_status),
|
||||
.response_o (s_response),
|
||||
.data_status_o (s_data_status),
|
||||
.fifo_status_o (s_fifo_status),
|
||||
.dma_status_o (s_dma_status),
|
||||
.current_desc_o (s_current_desc),
|
||||
.bytes_done_o (s_bytes_done),
|
||||
.dma_error_addr_o (s_dma_err_addr),
|
||||
.dma_error_o (s_dma_err),
|
||||
.error_status_o (s_err_stat),
|
||||
.last_cmd_o (s_last_cmd),
|
||||
.crc_error_count_o (s_crc_err_count),
|
||||
.timeout_count_o (s_timeout_count),
|
||||
.axi_error_count_o (s_axi_err_count),
|
||||
.stall_count_o (s_stall_count),
|
||||
.irq_event_o (s_irq_event),
|
||||
.pio_rdata_o (s_pio_rdata),
|
||||
.pio_valid_o (s_pio_valid),
|
||||
.pio_ready_o (s_pio_ready),
|
||||
.pio_read_consume_i(s_pio_read_consume),
|
||||
.busy_o (s_busy),
|
||||
.sdio (u_sdio_core_if),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
assign sdio.irq_o = s_irq;
|
||||
endmodule
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_clock #(
|
||||
parameter int CounterWidth = 16
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic enable_i,
|
||||
input logic [CounterWidth-1:0] half_period_i,
|
||||
output logic sck_o,
|
||||
output logic launch_tick_o,
|
||||
output logic sample_tick_o,
|
||||
output logic running_o
|
||||
);
|
||||
logic [CounterWidth-1:0] s_phase_count_d, s_phase_count_q;
|
||||
logic s_sck_d, s_sck_q;
|
||||
logic [CounterWidth-1:0] s_half_period;
|
||||
logic s_phase_end;
|
||||
|
||||
always_comb begin
|
||||
s_half_period = (half_period_i == '0) ? {{(CounterWidth - 1) {1'b0}}, 1'b1} : half_period_i;
|
||||
s_phase_end = s_phase_count_q + 1'b1 >= s_half_period;
|
||||
s_phase_count_d = s_phase_count_q;
|
||||
s_sck_d = s_sck_q;
|
||||
if (!enable_i && !s_sck_q) begin
|
||||
s_phase_count_d = '0;
|
||||
s_sck_d = 1'b0;
|
||||
end else if (s_phase_end) begin
|
||||
s_phase_count_d = '0;
|
||||
s_sck_d = ~s_sck_q;
|
||||
end else begin
|
||||
s_phase_count_d = s_phase_count_q + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
assign launch_tick_o = s_phase_end && enable_i && !s_sck_q;
|
||||
assign sample_tick_o = s_phase_end && s_sck_q;
|
||||
assign sck_o = s_sck_q;
|
||||
assign running_o = s_sck_q || enable_i;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_phase_count_q <= '0;
|
||||
s_sck_q <= 1'b0;
|
||||
end else begin
|
||||
s_phase_count_q <= s_phase_count_d;
|
||||
s_sck_q <= s_sck_d;
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if (CounterWidth < 1) begin
|
||||
$fatal(1, "sdio_clock: CounterWidth must be positive");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_command #(
|
||||
parameter int TimeoutWidth = 32
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic launch_tick_i,
|
||||
input logic sample_tick_i,
|
||||
input logic start_i,
|
||||
input logic abort_i,
|
||||
input logic [ 5:0] cmd_index_i,
|
||||
input logic [ 31:0] cmd_arg_i,
|
||||
input sdio_pkg::sdio_resp_type_e resp_type_i,
|
||||
input logic crc_check_i,
|
||||
input logic index_check_i,
|
||||
input logic [TimeoutWidth-1:0] timeout_cycles_i,
|
||||
input logic [TimeoutWidth-1:0] busy_timeout_cycles_i,
|
||||
input logic cmd_di_i,
|
||||
input logic dat0_i,
|
||||
output logic cmd_oe_o,
|
||||
output logic cmd_do_o,
|
||||
output logic busy_o,
|
||||
output logic done_o,
|
||||
output logic error_o,
|
||||
output logic timeout_o,
|
||||
output logic crc_error_o,
|
||||
output logic index_error_o,
|
||||
output logic busy_timeout_o,
|
||||
output logic [ 135:0] response_o,
|
||||
output logic [ 5:0] last_cmd_index_o
|
||||
);
|
||||
typedef enum logic [2:0] {
|
||||
Idle,
|
||||
Send,
|
||||
WaitResponse,
|
||||
CaptureResponse,
|
||||
WaitBusy,
|
||||
Complete
|
||||
} command_state_e;
|
||||
|
||||
command_state_e s_state_d, s_state_q;
|
||||
logic [47:0] s_tx_shift_d, s_tx_shift_q;
|
||||
logic [135:0] s_resp_d, s_resp_q;
|
||||
logic [5:0] s_cmd_index_d, s_cmd_index_q;
|
||||
logic [3:0] s_resp_type_bits_d, s_resp_type_bits_q;
|
||||
sdio_pkg::sdio_resp_type_e s_resp_type_q;
|
||||
logic [7:0] s_resp_len_d, s_resp_len_q;
|
||||
logic [7:0] s_bit_count_d, s_bit_count_q;
|
||||
logic [5:0] s_tx_count_d, s_tx_count_q;
|
||||
logic s_tx_started_d, s_tx_started_q;
|
||||
logic [TimeoutWidth-1:0] s_timeout_count_d, s_timeout_count_q;
|
||||
logic s_crc_check_d, s_crc_check_q;
|
||||
logic s_index_check_d, s_index_check_q;
|
||||
logic s_done_d, s_done_q;
|
||||
logic s_err_d, s_err_q;
|
||||
logic s_timeout_d, s_timeout_q;
|
||||
logic s_crc_err_d, s_crc_err_q;
|
||||
logic s_index_err_d, s_index_err_q;
|
||||
logic s_busy_timeout_d, s_busy_timeout_q;
|
||||
logic s_tx_final_hold_d, s_tx_final_hold_q;
|
||||
|
||||
logic [ 135:0] s_resp_shift_next;
|
||||
logic s_timeout_expired;
|
||||
logic s_frame_end_error;
|
||||
logic s_frame_crc_error;
|
||||
logic s_frame_index_error;
|
||||
logic [ 6:0] s_frame_crc;
|
||||
logic [TimeoutWidth-1:0] s_phase_timeout;
|
||||
|
||||
assign s_resp_shift_next = {s_resp_q[134:0], cmd_di_i};
|
||||
assign s_phase_timeout = (s_state_q == WaitBusy) ? busy_timeout_cycles_i : timeout_cycles_i;
|
||||
assign s_timeout_expired = (s_phase_timeout == '0) || (s_timeout_count_q >= s_phase_timeout);
|
||||
assign s_frame_end_error = s_resp_shift_next[0] != 1'b1;
|
||||
assign s_frame_crc = sdio_pkg::sdio_crc7_response(
|
||||
s_resp_shift_next, s_resp_type_q == sdio_pkg::SdioRespR2
|
||||
);
|
||||
assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1];
|
||||
assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q;
|
||||
|
||||
assign cmd_oe_o = (s_state_q == Send) || (s_tx_final_hold_q && !sample_tick_i);
|
||||
assign cmd_do_o = ((s_state_q == Send) || s_tx_final_hold_q) ? s_tx_shift_q[47] : 1'b1;
|
||||
assign busy_o = (s_state_q != Idle) && (s_state_q != Complete);
|
||||
assign done_o = s_done_q;
|
||||
assign error_o = s_err_q;
|
||||
assign timeout_o = s_timeout_q;
|
||||
assign crc_error_o = s_crc_err_q;
|
||||
assign index_error_o = s_index_err_q;
|
||||
assign busy_timeout_o = s_busy_timeout_q;
|
||||
assign response_o = s_resp_q;
|
||||
assign last_cmd_index_o = s_cmd_index_q;
|
||||
|
||||
always_comb begin
|
||||
s_state_d = s_state_q;
|
||||
s_tx_shift_d = s_tx_shift_q;
|
||||
s_resp_d = s_resp_q;
|
||||
s_cmd_index_d = s_cmd_index_q;
|
||||
s_resp_type_bits_d = s_resp_type_q;
|
||||
s_resp_len_d = s_resp_len_q;
|
||||
s_bit_count_d = s_bit_count_q;
|
||||
s_tx_count_d = s_tx_count_q;
|
||||
s_tx_started_d = s_tx_started_q;
|
||||
s_timeout_count_d = s_timeout_count_q;
|
||||
s_crc_check_d = s_crc_check_q;
|
||||
s_index_check_d = s_index_check_q;
|
||||
s_done_d = 1'b0;
|
||||
s_err_d = s_err_q;
|
||||
s_timeout_d = s_timeout_q;
|
||||
s_crc_err_d = s_crc_err_q;
|
||||
s_index_err_d = s_index_err_q;
|
||||
s_busy_timeout_d = s_busy_timeout_q;
|
||||
s_tx_final_hold_d = s_tx_final_hold_q;
|
||||
|
||||
if (s_tx_final_hold_q && sample_tick_i) begin
|
||||
s_tx_final_hold_d = 1'b0;
|
||||
end
|
||||
|
||||
if (s_state_q != Idle && s_state_q != Complete) begin
|
||||
if (s_timeout_expired) begin
|
||||
s_state_d = Complete;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
s_timeout_d = 1'b1;
|
||||
if (s_state_q == WaitBusy) begin
|
||||
s_busy_timeout_d = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_timeout_count_d = s_timeout_count_q + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
if ((s_state_q != Idle) && (s_state_q != Complete) && abort_i) begin
|
||||
s_state_d = Complete;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
s_tx_final_hold_d = 1'b0;
|
||||
end
|
||||
|
||||
if ((s_state_q == Send) && s_tx_started_q && sample_tick_i && (s_tx_count_q != 6'd47)) begin
|
||||
s_tx_shift_d = {s_tx_shift_q[46:0], 1'b0};
|
||||
s_tx_count_d = s_tx_count_q + 1'b1;
|
||||
end
|
||||
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
s_tx_final_hold_d = 1'b0;
|
||||
s_tx_started_d = 1'b0;
|
||||
if (start_i) begin
|
||||
s_state_d = Send;
|
||||
s_tx_shift_d = {
|
||||
1'b0,
|
||||
1'b1,
|
||||
cmd_index_i,
|
||||
cmd_arg_i,
|
||||
sdio_pkg::sdio_crc7_calc({1'b0, 1'b1, cmd_index_i, cmd_arg_i}),
|
||||
1'b1
|
||||
};
|
||||
s_resp_d = '0;
|
||||
s_cmd_index_d = cmd_index_i;
|
||||
s_resp_type_bits_d = resp_type_i;
|
||||
s_resp_len_d = (resp_type_i == sdio_pkg::SdioRespR2) ? 8'd136 : 8'd48;
|
||||
s_bit_count_d = '0;
|
||||
s_tx_count_d = '0;
|
||||
s_tx_started_d = 1'b0;
|
||||
s_timeout_count_d = '0;
|
||||
s_crc_check_d = crc_check_i;
|
||||
s_index_check_d = index_check_i;
|
||||
s_err_d = 1'b0;
|
||||
s_timeout_d = 1'b0;
|
||||
s_crc_err_d = 1'b0;
|
||||
s_index_err_d = 1'b0;
|
||||
s_busy_timeout_d = 1'b0;
|
||||
end
|
||||
end
|
||||
Send: begin
|
||||
if (launch_tick_i) begin
|
||||
s_tx_started_d = 1'b1;
|
||||
if (s_tx_count_q == 6'd47) begin
|
||||
s_tx_final_hold_d = 1'b1;
|
||||
if (s_resp_type_q == sdio_pkg::SdioRespNone) begin
|
||||
s_state_d = Complete;
|
||||
s_done_d = 1'b1;
|
||||
end else begin
|
||||
s_state_d = WaitResponse;
|
||||
s_timeout_count_d = '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
WaitResponse: begin
|
||||
if (sample_tick_i && (cmd_di_i == 1'b0)) begin
|
||||
s_resp_d = '0;
|
||||
s_resp_d[0] = 1'b0;
|
||||
s_bit_count_d = 8'd1;
|
||||
s_timeout_count_d = '0;
|
||||
s_state_d = CaptureResponse;
|
||||
end
|
||||
end
|
||||
CaptureResponse: begin
|
||||
if (sample_tick_i) begin
|
||||
s_resp_d = s_resp_shift_next;
|
||||
if (s_bit_count_q == (s_resp_len_q - 1'b1)) begin
|
||||
if (s_frame_end_error) begin
|
||||
s_err_d = 1'b1;
|
||||
end
|
||||
if (s_crc_check_q && sdio_pkg::sdio_response_has_crc(
|
||||
s_resp_type_q
|
||||
) && s_frame_crc_error) begin
|
||||
s_err_d = 1'b1;
|
||||
s_crc_err_d = 1'b1;
|
||||
end
|
||||
if (s_index_check_q && sdio_pkg::sdio_response_has_index(
|
||||
s_resp_type_q
|
||||
) && s_frame_index_error) begin
|
||||
s_err_d = 1'b1;
|
||||
s_index_err_d = 1'b1;
|
||||
end
|
||||
if ((s_resp_type_q == sdio_pkg::SdioRespR1b) && !s_frame_end_error &&
|
||||
!(s_crc_check_q && s_frame_crc_error)) begin
|
||||
s_state_d = WaitBusy;
|
||||
s_timeout_count_d = '0;
|
||||
end else begin
|
||||
s_state_d = Complete;
|
||||
s_done_d = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_bit_count_d = s_bit_count_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
WaitBusy: begin
|
||||
if (sample_tick_i && dat0_i) begin
|
||||
s_state_d = Complete;
|
||||
s_done_d = 1'b1;
|
||||
end
|
||||
end
|
||||
Complete: begin
|
||||
s_tx_final_hold_d = 1'b0;
|
||||
s_tx_started_d = 1'b0;
|
||||
s_state_d = Idle;
|
||||
end
|
||||
default: begin
|
||||
s_state_d = Idle;
|
||||
s_err_d = 1'b1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_tx_shift_q <= '0;
|
||||
s_resp_q <= '0;
|
||||
s_cmd_index_q <= '0;
|
||||
s_resp_type_bits_q <= '0;
|
||||
s_resp_len_q <= 8'd48;
|
||||
s_bit_count_q <= '0;
|
||||
s_tx_count_q <= '0;
|
||||
s_tx_started_q <= 1'b0;
|
||||
s_timeout_count_q <= '0;
|
||||
s_crc_check_q <= 1'b0;
|
||||
s_index_check_q <= 1'b0;
|
||||
s_done_q <= 1'b0;
|
||||
s_err_q <= 1'b0;
|
||||
s_timeout_q <= 1'b0;
|
||||
s_crc_err_q <= 1'b0;
|
||||
s_index_err_q <= 1'b0;
|
||||
s_busy_timeout_q <= 1'b0;
|
||||
s_tx_final_hold_q <= 1'b0;
|
||||
end else begin
|
||||
s_state_q <= s_state_d;
|
||||
s_tx_shift_q <= s_tx_shift_d;
|
||||
s_resp_q <= s_resp_d;
|
||||
s_cmd_index_q <= s_cmd_index_d;
|
||||
s_resp_type_bits_q <= s_resp_type_bits_d;
|
||||
s_resp_len_q <= s_resp_len_d;
|
||||
s_bit_count_q <= s_bit_count_d;
|
||||
s_tx_count_q <= s_tx_count_d;
|
||||
s_tx_started_q <= s_tx_started_d;
|
||||
s_timeout_count_q <= s_timeout_count_d;
|
||||
s_crc_check_q <= s_crc_check_d;
|
||||
s_index_check_q <= s_index_check_d;
|
||||
s_done_q <= s_done_d;
|
||||
s_err_q <= s_err_d;
|
||||
s_timeout_q <= s_timeout_d;
|
||||
s_crc_err_q <= s_crc_err_d;
|
||||
s_index_err_q <= s_index_err_d;
|
||||
s_busy_timeout_q <= s_busy_timeout_d;
|
||||
s_tx_final_hold_q <= s_tx_final_hold_d;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_resp_type_q = sdio_pkg::sdio_resp_type_e'(s_resp_type_bits_q);
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if (TimeoutWidth < 1) begin
|
||||
$fatal(1, "sdio_command: TimeoutWidth must be positive");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,500 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_core #(
|
||||
parameter int InputClockHz = 72_000_000,
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int DescCount = 16
|
||||
) (
|
||||
// verilog_format: off -- preserve the host, status, and external bus groups
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic host_enable_i,
|
||||
input logic host_irq_enable_i,
|
||||
input logic clock_enable_i,
|
||||
input logic [15:0] half_period_i,
|
||||
input logic [1:0] bus_width_i,
|
||||
input logic [31:0] timeout_cmd_i,
|
||||
input logic [31:0] timeout_data_i,
|
||||
input logic [31:0] timeout_busy_i,
|
||||
input logic [5:0] cmd_index_i,
|
||||
input logic [31:0] cmd_arg_i,
|
||||
input sdio_pkg::sdio_resp_type_e cmd_resp_type_i,
|
||||
input logic cmd_crc_check_i,
|
||||
input logic cmd_index_check_i,
|
||||
input logic cmd_start_i,
|
||||
input logic [15:0] block_size_i,
|
||||
input logic [15:0] block_count_i,
|
||||
input logic data_direction_i,
|
||||
input logic data_dma_enable_i,
|
||||
input logic data_block_mode_i,
|
||||
input logic data_fixed_addr_i,
|
||||
input logic data_start_i,
|
||||
input logic pio_wvalid_i,
|
||||
input logic [31:0] pio_wdata_i,
|
||||
input logic [3:0] pio_wstrb_i,
|
||||
input logic [31:0] desc_base_i,
|
||||
input logic [15:0] desc_count_i,
|
||||
input logic dma_start_i,
|
||||
input logic dma_abort_i,
|
||||
input logic host_abort_i,
|
||||
input logic irq_dat1_enable_i,
|
||||
output logic [31:0] status_o,
|
||||
output logic [31:0] clock_actual_o,
|
||||
output logic [31:0] cmd_status_o,
|
||||
output logic [135:0] response_o,
|
||||
output logic [31:0] data_status_o,
|
||||
output logic [31:0] fifo_status_o,
|
||||
output logic [31:0] dma_status_o,
|
||||
output logic [31:0] current_desc_o,
|
||||
output logic [31:0] bytes_done_o,
|
||||
output logic [31:0] dma_error_addr_o,
|
||||
output logic [31:0] dma_error_o,
|
||||
output logic [31:0] error_status_o,
|
||||
output logic [5:0] last_cmd_o,
|
||||
output logic [31:0] crc_error_count_o,
|
||||
output logic [31:0] timeout_count_o,
|
||||
output logic [31:0] axi_error_count_o,
|
||||
output logic [31:0] stall_count_o,
|
||||
output logic [7:0] irq_event_o,
|
||||
output logic [31:0] pio_rdata_o,
|
||||
output logic pio_valid_o,
|
||||
output logic pio_ready_o,
|
||||
input logic pio_read_consume_i,
|
||||
output logic busy_o,
|
||||
sdio_if.core sdio,
|
||||
axi4_if.master dma_axi4
|
||||
// verilog_format: on
|
||||
);
|
||||
logic s_launch_tick;
|
||||
logic s_sample_tick;
|
||||
logic s_clock_sck;
|
||||
logic s_clock_running;
|
||||
logic s_clock_en;
|
||||
logic s_cmd_oe;
|
||||
logic s_cmd_do;
|
||||
logic s_cmd_busy;
|
||||
logic s_cmd_done;
|
||||
logic s_cmd_err;
|
||||
logic s_cmd_timeout;
|
||||
logic s_cmd_crc_err;
|
||||
logic s_cmd_index_err;
|
||||
logic s_cmd_busy_timeout;
|
||||
logic [135:0] s_cmd_response;
|
||||
logic [ 5:0] s_last_cmd;
|
||||
logic [ 3:0] s_data_oe;
|
||||
logic [ 3:0] s_data_do;
|
||||
logic s_data_tx_valid;
|
||||
logic s_data_tx_ready;
|
||||
logic s_data_tx_wait;
|
||||
logic [ 31:0] s_data_tx_data;
|
||||
logic [ 3:0] s_data_tx_strb;
|
||||
logic s_data_tx_last;
|
||||
logic s_data_rx_valid;
|
||||
logic s_data_rx_ready;
|
||||
logic s_data_rx_stall_q;
|
||||
logic [ 31:0] s_data_rx_data;
|
||||
logic [ 3:0] s_data_rx_strb;
|
||||
logic s_data_rx_last;
|
||||
logic s_data_busy;
|
||||
logic s_data_done;
|
||||
logic s_data_err;
|
||||
logic s_data_timeout;
|
||||
logic s_data_crc_err;
|
||||
logic s_data_busy_timeout;
|
||||
logic s_dma_start;
|
||||
logic s_dma_direction;
|
||||
logic s_dma_data_in_valid;
|
||||
logic s_dma_data_in_ready;
|
||||
logic [ 31:0] s_dma_data_in;
|
||||
logic [ 3:0] s_dma_data_in_strb;
|
||||
logic s_dma_data_in_last;
|
||||
logic s_dma_data_out_valid;
|
||||
logic s_dma_data_out_ready;
|
||||
logic [ 31:0] s_dma_data_out;
|
||||
logic [ 3:0] s_dma_data_out_strb;
|
||||
logic s_dma_data_out_last;
|
||||
logic s_dma_busy;
|
||||
logic s_dma_done;
|
||||
logic s_dma_err;
|
||||
logic [ 7:0] s_dma_err_code;
|
||||
logic [ 31:0] s_dma_current_desc;
|
||||
logic [ 31:0] s_dma_bytes_done;
|
||||
logic [ 31:0] s_dma_err_addr;
|
||||
logic s_dma_descriptor_irq;
|
||||
logic s_dma_abort_done;
|
||||
logic s_dat1_meta_q, s_dat1_sync_q, s_dat1_seen_d, s_dat1_seen_q;
|
||||
logic [31:0] s_pio_rdata_q;
|
||||
logic s_pio_valid_q;
|
||||
logic [31:0] s_pio_tx_data_q[0:3];
|
||||
logic [ 3:0] s_pio_tx_strb_q[0:3];
|
||||
logic [1:0] s_pio_tx_rd_q, s_pio_tx_wr_q;
|
||||
logic [ 2:0] s_pio_tx_count_q;
|
||||
logic [31:0] s_crc_err_count_q;
|
||||
logic [31:0] s_timeout_count_q;
|
||||
logic [31:0] s_axi_err_count_q;
|
||||
logic [31:0] s_stall_count_q;
|
||||
logic [ 7:0] s_irq_event;
|
||||
logic [31:0] s_err_stat;
|
||||
logic [31:0] s_total_bytes;
|
||||
logic s_core_busy;
|
||||
logic [31:0] s_cmd_timeout_cfg;
|
||||
logic s_dat1_qualify;
|
||||
logic s_abort_request;
|
||||
logic s_abort_pending_q;
|
||||
logic s_abort_event;
|
||||
logic s_pio_tx_push, s_pio_tx_pop, s_pio_tx_full;
|
||||
logic [ 2:0] s_pio_tx_head_bytes;
|
||||
logic [31:0] s_pio_tx_bytes_q;
|
||||
logic s_cmd_err_event, s_data_err_event, s_dma_err_event;
|
||||
logic s_cmd_crc_event, s_data_crc_event;
|
||||
logic s_cmd_timeout_event, s_data_timeout_event, s_busy_timeout_event;
|
||||
|
||||
function automatic logic [2:0] count_strobe(input logic [3:0] strb_i);
|
||||
logic [2:0] count;
|
||||
begin
|
||||
count = 3'd0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
if (strb_i[index]) count = count + 1'b1;
|
||||
end
|
||||
return count;
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign s_clock_en = host_enable_i && clock_enable_i &&
|
||||
(s_cmd_busy || s_data_busy || s_dma_busy || cmd_start_i ||
|
||||
data_start_i) &&
|
||||
!s_data_rx_stall_q &&
|
||||
!(s_data_busy && s_data_tx_wait && (s_data_oe == 4'b0000));
|
||||
assign s_total_bytes = {16'd0, block_size_i} * {16'd0, block_count_i};
|
||||
assign s_abort_request = host_abort_i || dma_abort_i ||
|
||||
(s_dma_done && s_dma_err) ||
|
||||
(s_data_done && s_data_err);
|
||||
assign s_dma_start = dma_start_i || (data_start_i && data_dma_enable_i);
|
||||
assign s_dma_direction = data_direction_i;
|
||||
assign s_cmd_timeout_cfg = timeout_cmd_i;
|
||||
assign s_dat1_qualify = host_irq_enable_i && irq_dat1_enable_i && !s_data_busy &&
|
||||
(!host_enable_i || !s_data_oe[1]) && !s_dat1_sync_q;
|
||||
assign s_pio_tx_full = s_pio_tx_count_q == 3'd4;
|
||||
assign s_pio_tx_head_bytes = count_strobe(s_pio_tx_strb_q[s_pio_tx_rd_q]);
|
||||
assign s_pio_tx_push = pio_wvalid_i && pio_ready_o;
|
||||
assign s_data_tx_valid = data_dma_enable_i ? s_dma_data_out_valid : (s_pio_tx_count_q != 3'd0);
|
||||
assign s_data_tx_data = data_dma_enable_i ? s_dma_data_out : s_pio_tx_data_q[s_pio_tx_rd_q];
|
||||
assign s_data_tx_strb = data_dma_enable_i ? s_dma_data_out_strb : s_pio_tx_strb_q[s_pio_tx_rd_q];
|
||||
assign s_data_tx_last = data_dma_enable_i ? s_dma_data_out_last :
|
||||
((s_pio_tx_bytes_q + {29'd0, s_pio_tx_head_bytes}) >=
|
||||
s_total_bytes);
|
||||
assign s_dma_data_out_ready = data_dma_enable_i && s_data_tx_ready;
|
||||
assign s_dma_data_in_valid = data_dma_enable_i && s_data_rx_valid;
|
||||
assign s_dma_data_in = s_data_rx_data;
|
||||
assign s_dma_data_in_strb = s_data_rx_strb;
|
||||
assign s_dma_data_in_last = s_data_rx_last;
|
||||
assign s_data_rx_ready = data_dma_enable_i ? s_dma_data_in_ready : !s_pio_valid_q;
|
||||
assign s_core_busy = s_cmd_busy || s_data_busy || s_dma_busy;
|
||||
assign busy_o = s_core_busy;
|
||||
assign s_pio_tx_pop = !data_dma_enable_i && s_data_tx_valid && s_data_tx_ready;
|
||||
assign s_abort_event = s_abort_pending_q && !s_core_busy;
|
||||
assign s_cmd_err_event = s_cmd_done && s_cmd_err;
|
||||
assign s_data_err_event = s_data_done && s_data_err;
|
||||
assign s_dma_err_event = s_dma_done && s_dma_err;
|
||||
assign s_cmd_crc_event = s_cmd_done && s_cmd_crc_err;
|
||||
assign s_data_crc_event = s_data_done && s_data_crc_err;
|
||||
assign s_cmd_timeout_event = s_cmd_done && (s_cmd_timeout || s_cmd_busy_timeout);
|
||||
assign s_data_timeout_event = s_data_done && s_data_timeout;
|
||||
assign s_busy_timeout_event = s_data_done && s_data_busy_timeout;
|
||||
|
||||
assign sdio.sck_o = s_clock_sck;
|
||||
assign sdio.cmd_oe_o = s_cmd_oe && host_enable_i;
|
||||
assign sdio.cmd_do_o = s_cmd_do;
|
||||
assign sdio.dat_oe_o = host_enable_i ? s_data_oe : 4'b0000;
|
||||
assign sdio.dat_do_o = s_data_do;
|
||||
|
||||
assign pio_rdata_o = s_pio_rdata_q;
|
||||
assign pio_valid_o = s_pio_valid_q;
|
||||
assign pio_ready_o = s_data_busy && !data_dma_enable_i && !s_pio_tx_full &&
|
||||
(data_direction_i == sdio_pkg::SdioDataToCard);
|
||||
assign response_o = s_cmd_response;
|
||||
assign last_cmd_o = s_last_cmd;
|
||||
assign crc_error_count_o = s_crc_err_count_q;
|
||||
assign timeout_count_o = s_timeout_count_q;
|
||||
assign axi_error_count_o = s_axi_err_count_q;
|
||||
assign stall_count_o = s_stall_count_q;
|
||||
assign current_desc_o = s_dma_current_desc;
|
||||
assign bytes_done_o = s_dma_bytes_done;
|
||||
assign dma_error_addr_o = s_dma_err_addr;
|
||||
assign dma_error_o = {24'd0, s_dma_err_code};
|
||||
assign clock_actual_o = (half_period_i == 16'd0) ? 32'd0 : InputClockHz / (2 * half_period_i);
|
||||
assign status_o = {
|
||||
23'd0,
|
||||
s_clock_running,
|
||||
s_dat1_sync_q,
|
||||
data_block_mode_i,
|
||||
data_fixed_addr_i,
|
||||
pio_ready_o,
|
||||
s_dma_busy,
|
||||
s_data_busy,
|
||||
s_cmd_busy,
|
||||
s_core_busy
|
||||
};
|
||||
assign cmd_status_o = {
|
||||
27'd0, s_cmd_index_err, s_cmd_crc_err, s_cmd_timeout, s_cmd_err, s_cmd_busy
|
||||
};
|
||||
assign data_status_o = {
|
||||
27'd0, s_data_busy_timeout, s_data_crc_err, s_data_timeout, s_data_err, s_data_busy
|
||||
};
|
||||
assign fifo_status_o = {28'd0, s_pio_tx_count_q, s_pio_valid_q};
|
||||
assign dma_status_o = {21'd0, s_dma_err_code, s_dma_err, s_dma_done, s_dma_busy};
|
||||
assign error_status_o = s_err_stat;
|
||||
|
||||
assign s_err_stat = {
|
||||
24'd0,
|
||||
s_abort_event || s_dma_abort_done,
|
||||
s_dma_err_event,
|
||||
s_data_err_event,
|
||||
s_cmd_err_event,
|
||||
s_data_timeout_event || s_busy_timeout_event,
|
||||
s_cmd_timeout_event,
|
||||
s_data_crc_event,
|
||||
s_cmd_crc_event
|
||||
};
|
||||
assign s_irq_event = {
|
||||
s_abort_event || s_dma_abort_done,
|
||||
s_dat1_seen_d && !s_dat1_seen_q,
|
||||
s_dma_err_event,
|
||||
s_data_err_event,
|
||||
s_cmd_err_event,
|
||||
s_dma_done || s_dma_descriptor_irq,
|
||||
s_data_done,
|
||||
s_cmd_done
|
||||
};
|
||||
assign irq_event_o = s_irq_event;
|
||||
|
||||
sdio_clock #(
|
||||
.CounterWidth(16)
|
||||
) u_sdio_clock (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (s_clock_en),
|
||||
.half_period_i(half_period_i),
|
||||
.sck_o (s_clock_sck),
|
||||
.launch_tick_o(s_launch_tick),
|
||||
.sample_tick_o(s_sample_tick),
|
||||
.running_o (s_clock_running)
|
||||
);
|
||||
|
||||
sdio_command #(
|
||||
.TimeoutWidth(32)
|
||||
) u_sdio_command (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.launch_tick_i (s_launch_tick),
|
||||
.sample_tick_i (s_sample_tick),
|
||||
.start_i (cmd_start_i),
|
||||
.abort_i (s_abort_request),
|
||||
.cmd_index_i (cmd_index_i),
|
||||
.cmd_arg_i (cmd_arg_i),
|
||||
.resp_type_i (cmd_resp_type_i),
|
||||
.crc_check_i (cmd_crc_check_i),
|
||||
.index_check_i (cmd_index_check_i),
|
||||
.timeout_cycles_i (s_cmd_timeout_cfg),
|
||||
.busy_timeout_cycles_i(timeout_busy_i),
|
||||
.cmd_di_i (sdio.cmd_di_i),
|
||||
.dat0_i (sdio.dat_di_i[0]),
|
||||
.cmd_oe_o (s_cmd_oe),
|
||||
.cmd_do_o (s_cmd_do),
|
||||
.busy_o (s_cmd_busy),
|
||||
.done_o (s_cmd_done),
|
||||
.error_o (s_cmd_err),
|
||||
.timeout_o (s_cmd_timeout),
|
||||
.crc_error_o (s_cmd_crc_err),
|
||||
.index_error_o (s_cmd_index_err),
|
||||
.busy_timeout_o (s_cmd_busy_timeout),
|
||||
.response_o (s_cmd_response),
|
||||
.last_cmd_index_o (s_last_cmd)
|
||||
);
|
||||
|
||||
sdio_data #(
|
||||
.TimeoutWidth(32)
|
||||
) u_sdio_data (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.launch_tick_i (s_launch_tick),
|
||||
.sample_tick_i (s_sample_tick),
|
||||
.start_i (data_start_i),
|
||||
.abort_i (s_abort_request),
|
||||
.direction_i (sdio_pkg::sdio_data_direction_e'(data_direction_i)),
|
||||
.bus_width_i (bus_width_i),
|
||||
.block_size_i (block_size_i),
|
||||
.block_count_i (block_count_i),
|
||||
.timeout_cycles_i (timeout_data_i),
|
||||
.busy_timeout_cycles_i(timeout_busy_i),
|
||||
.dat_di_i (sdio.dat_di_i),
|
||||
.dat0_i (sdio.dat_di_i[0]),
|
||||
.dat_oe_o (s_data_oe),
|
||||
.dat_do_o (s_data_do),
|
||||
.tx_valid_i (s_data_tx_valid),
|
||||
.tx_ready_o (s_data_tx_ready),
|
||||
.tx_wait_o (s_data_tx_wait),
|
||||
.tx_data_i (s_data_tx_data),
|
||||
.tx_strb_i (s_data_tx_strb),
|
||||
.tx_last_i (s_data_tx_last),
|
||||
.rx_valid_o (s_data_rx_valid),
|
||||
.rx_ready_i (s_data_rx_ready),
|
||||
.rx_data_o (s_data_rx_data),
|
||||
.rx_strb_o (s_data_rx_strb),
|
||||
.rx_last_o (s_data_rx_last),
|
||||
.busy_o (s_data_busy),
|
||||
.done_o (s_data_done),
|
||||
.error_o (s_data_err),
|
||||
.timeout_o (s_data_timeout),
|
||||
.crc_error_o (s_data_crc_err),
|
||||
.busy_timeout_o (s_data_busy_timeout)
|
||||
);
|
||||
|
||||
sdio_dma #(
|
||||
.AddrWidth(AddrWidth),
|
||||
.DataWidth(DataWidth),
|
||||
.DescCount(DescCount)
|
||||
) u_sdio_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_dma_start),
|
||||
.abort_i (s_abort_request),
|
||||
.direction_i (s_dma_direction),
|
||||
.desc_base_i (desc_base_i),
|
||||
.desc_count_i (desc_count_i),
|
||||
.total_bytes_i (s_total_bytes),
|
||||
.data_in_valid_i (s_dma_data_in_valid),
|
||||
.data_in_ready_o (s_dma_data_in_ready),
|
||||
.data_in_i (s_dma_data_in),
|
||||
.data_in_strb_i (s_dma_data_in_strb),
|
||||
.data_in_last_i (s_dma_data_in_last),
|
||||
.data_out_valid_o(s_dma_data_out_valid),
|
||||
.data_out_ready_i(s_dma_data_out_ready),
|
||||
.data_out_o (s_dma_data_out),
|
||||
.data_out_strb_o (s_dma_data_out_strb),
|
||||
.data_out_last_o (s_dma_data_out_last),
|
||||
.busy_o (s_dma_busy),
|
||||
.done_o (s_dma_done),
|
||||
.error_o (s_dma_err),
|
||||
.error_code_o (s_dma_err_code),
|
||||
.current_desc_o (s_dma_current_desc),
|
||||
.bytes_done_o (s_dma_bytes_done),
|
||||
.error_addr_o (s_dma_err_addr),
|
||||
.descriptor_irq_o(s_dma_descriptor_irq),
|
||||
.abort_done_o (s_dma_abort_done),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_dat1_meta_q <= 1'b1;
|
||||
s_dat1_sync_q <= 1'b1;
|
||||
s_dat1_seen_q <= 1'b0;
|
||||
s_data_rx_stall_q <= 1'b0;
|
||||
s_pio_rdata_q <= '0;
|
||||
s_pio_valid_q <= 1'b0;
|
||||
s_pio_tx_rd_q <= '0;
|
||||
s_pio_tx_wr_q <= '0;
|
||||
s_pio_tx_count_q <= '0;
|
||||
s_pio_tx_bytes_q <= '0;
|
||||
s_crc_err_count_q <= '0;
|
||||
s_timeout_count_q <= '0;
|
||||
s_axi_err_count_q <= '0;
|
||||
s_stall_count_q <= '0;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
s_pio_tx_data_q[index] <= '0;
|
||||
s_pio_tx_strb_q[index] <= '0;
|
||||
end
|
||||
end else begin
|
||||
s_dat1_meta_q <= sdio.dat_di_i[1];
|
||||
s_dat1_sync_q <= s_dat1_meta_q;
|
||||
s_dat1_seen_q <= s_dat1_seen_d;
|
||||
s_data_rx_stall_q <= s_data_rx_valid && !s_data_rx_ready;
|
||||
|
||||
if (data_start_i) begin
|
||||
s_pio_tx_rd_q <= '0;
|
||||
s_pio_tx_wr_q <= '0;
|
||||
s_pio_tx_count_q <= '0;
|
||||
s_pio_tx_bytes_q <= '0;
|
||||
end else begin
|
||||
if (s_pio_tx_push) begin
|
||||
s_pio_tx_data_q[s_pio_tx_wr_q] <= pio_wdata_i;
|
||||
s_pio_tx_strb_q[s_pio_tx_wr_q] <= pio_wstrb_i;
|
||||
s_pio_tx_wr_q <= s_pio_tx_wr_q + 1'b1;
|
||||
end
|
||||
if (s_pio_tx_pop) begin
|
||||
s_pio_tx_rd_q <= s_pio_tx_rd_q + 1'b1;
|
||||
s_pio_tx_bytes_q <= s_pio_tx_bytes_q + {29'd0, s_pio_tx_head_bytes};
|
||||
end
|
||||
unique case ({
|
||||
s_pio_tx_push, s_pio_tx_pop
|
||||
})
|
||||
2'b10: s_pio_tx_count_q <= s_pio_tx_count_q + 1'b1;
|
||||
2'b01: s_pio_tx_count_q <= s_pio_tx_count_q - 1'b1;
|
||||
default: begin
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
if (s_data_rx_valid && s_data_rx_ready && !data_dma_enable_i) begin
|
||||
s_pio_rdata_q <= s_data_rx_data;
|
||||
s_pio_valid_q <= 1'b1;
|
||||
end
|
||||
if (pio_read_consume_i) begin
|
||||
s_pio_valid_q <= 1'b0;
|
||||
end
|
||||
unique case ({
|
||||
s_cmd_crc_event, s_data_crc_event
|
||||
})
|
||||
2'b10, 2'b01: s_crc_err_count_q <= s_crc_err_count_q + 1'b1;
|
||||
2'b11: s_crc_err_count_q <= s_crc_err_count_q + 32'd2;
|
||||
default: begin
|
||||
end
|
||||
endcase
|
||||
if (s_cmd_timeout_event || s_data_timeout_event || s_busy_timeout_event) begin
|
||||
s_timeout_count_q <= s_timeout_count_q + 1'b1;
|
||||
end
|
||||
if (s_dma_err_event) begin
|
||||
s_axi_err_count_q <= s_axi_err_count_q + 1'b1;
|
||||
end
|
||||
if ((s_data_tx_valid && !s_data_tx_ready) || (s_data_rx_valid && !s_data_rx_ready)) begin
|
||||
s_stall_count_q <= s_stall_count_q + 1'b1;
|
||||
end
|
||||
|
||||
if (s_abort_request && s_core_busy) begin
|
||||
s_abort_pending_q <= 1'b1;
|
||||
end else if (s_abort_pending_q && !s_core_busy) begin
|
||||
s_abort_pending_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_dat1_seen_d = s_dat1_seen_q;
|
||||
if (s_dat1_sync_q) begin
|
||||
s_dat1_seen_d = 1'b0;
|
||||
end else if (s_dat1_qualify) begin
|
||||
s_dat1_seen_d = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if ((AddrWidth < 4) || (DataWidth != 32) || (DescCount < 1)) begin
|
||||
$fatal(1, "sdio_core: unsupported geometry");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_crc16 (
|
||||
input logic [15:0] crc_i,
|
||||
input logic [ 7:0] data_i,
|
||||
output logic [15:0] crc_o
|
||||
);
|
||||
always_comb begin
|
||||
crc_o = sdio_pkg::sdio_crc16_byte(crc_i, data_i);
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_crc7 (
|
||||
input logic [39:0] data_i,
|
||||
output logic [ 6:0] crc_o
|
||||
);
|
||||
always_comb begin
|
||||
crc_o = sdio_pkg::sdio_crc7_calc(data_i);
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,657 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_data #(
|
||||
parameter int TimeoutWidth = 32
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic launch_tick_i,
|
||||
input logic sample_tick_i,
|
||||
input logic start_i,
|
||||
input logic abort_i,
|
||||
input sdio_pkg::sdio_data_direction_e direction_i,
|
||||
input logic [ 1:0] bus_width_i,
|
||||
input logic [ 15:0] block_size_i,
|
||||
input logic [ 15:0] block_count_i,
|
||||
input logic [TimeoutWidth-1:0] timeout_cycles_i,
|
||||
input logic [TimeoutWidth-1:0] busy_timeout_cycles_i,
|
||||
input logic [ 3:0] dat_di_i,
|
||||
input logic dat0_i,
|
||||
output logic [ 3:0] dat_oe_o,
|
||||
output logic [ 3:0] dat_do_o,
|
||||
input logic tx_valid_i,
|
||||
output logic tx_ready_o,
|
||||
output logic tx_wait_o,
|
||||
input logic [ 31:0] tx_data_i,
|
||||
input logic [ 3:0] tx_strb_i,
|
||||
input logic tx_last_i,
|
||||
output logic rx_valid_o,
|
||||
input logic rx_ready_i,
|
||||
output logic [ 31:0] rx_data_o,
|
||||
output logic [ 3:0] rx_strb_o,
|
||||
output logic rx_last_o,
|
||||
output logic busy_o,
|
||||
output logic done_o,
|
||||
output logic error_o,
|
||||
output logic timeout_o,
|
||||
output logic crc_error_o,
|
||||
output logic busy_timeout_o
|
||||
);
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
WriteWaitWord,
|
||||
WriteNextWord,
|
||||
WriteToken,
|
||||
WritePayload,
|
||||
WriteCrc,
|
||||
WriteResponse,
|
||||
WriteBusy,
|
||||
ReadToken,
|
||||
ReadPayload,
|
||||
ReadEmit,
|
||||
ReadCrc
|
||||
} data_state_e;
|
||||
|
||||
data_state_e s_state_d, s_state_q;
|
||||
logic s_width4_d, s_width4_q;
|
||||
logic [31:0] s_target_bytes_d, s_target_bytes_q;
|
||||
logic [31:0] s_tx_total_d, s_tx_total_q;
|
||||
logic [31:0] s_rx_total_d, s_rx_total_q;
|
||||
logic [31:0] s_tx_word_d, s_tx_word_q;
|
||||
logic [3:0] s_tx_strb_d, s_tx_strb_q;
|
||||
logic [2:0] s_tx_active_bytes_d, s_tx_active_bytes_q;
|
||||
logic [2:0] s_tx_byte_d, s_tx_byte_q;
|
||||
logic [3:0] s_tx_bit_d, s_tx_bit_q;
|
||||
logic [15:0] s_tx_crc_d, s_tx_crc_q;
|
||||
logic [63:0] s_tx_crc_lanes_d, s_tx_crc_lanes_q;
|
||||
logic s_tx_token_launched_d, s_tx_token_launched_q;
|
||||
logic [4:0] s_crc_bit_d, s_crc_bit_q;
|
||||
logic s_tx_crc_final_hold_d, s_tx_crc_final_hold_q;
|
||||
logic [31:0] s_rx_word_d, s_rx_word_q;
|
||||
logic [7:0] s_rx_byte_d, s_rx_byte_q;
|
||||
logic [2:0] s_rx_active_bytes_d, s_rx_active_bytes_q;
|
||||
logic [2:0] s_rx_byte_didx_d, s_rx_byte_didx_q;
|
||||
logic [3:0] s_rx_bit_d, s_rx_bit_q;
|
||||
logic [15:0] s_rx_crc_d, s_rx_crc_q;
|
||||
logic [63:0] s_rx_crc_lanes_d, s_rx_crc_lanes_q;
|
||||
logic [15:0] s_rx_crc_received_d, s_rx_crc_received_q;
|
||||
logic [63:0] s_rx_crc_received_lanes_d, s_rx_crc_received_lanes_q;
|
||||
logic [31:0] s_rx_pending_data_d, s_rx_pending_data_q;
|
||||
logic [3:0] s_rx_pending_strb_d, s_rx_pending_strb_q;
|
||||
logic s_rx_pending_last_d, s_rx_pending_last_q;
|
||||
logic [4:0] s_resp_token_d, s_resp_token_q;
|
||||
logic [2:0] s_resp_bit_d, s_resp_bit_q;
|
||||
logic [TimeoutWidth-1:0] s_timeout_count_d, s_timeout_count_q;
|
||||
logic s_done_d, s_done_q;
|
||||
logic s_err_d, s_err_q;
|
||||
logic s_timeout_d, s_timeout_q;
|
||||
logic s_crc_err_d, s_crc_err_q;
|
||||
logic s_busy_timeout_d, s_busy_timeout_q;
|
||||
|
||||
logic [ 31:0] s_target_bytes_calc;
|
||||
logic [ 7:0] s_rx_byte_next;
|
||||
logic [ 31:0] s_rx_remaining;
|
||||
logic [TimeoutWidth-1:0] s_phase_timeout;
|
||||
logic s_timeout_expired;
|
||||
logic s_tx_word_final;
|
||||
|
||||
function automatic logic [2:0] count_strobe(input logic [3:0] strb_i);
|
||||
logic [2:0] count;
|
||||
begin
|
||||
count = 3'd0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
if (strb_i[index]) begin
|
||||
count = count + 1'b1;
|
||||
end
|
||||
end
|
||||
return count;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic strobe_valid(input logic [3:0] strb_i);
|
||||
return (strb_i != 4'd0) && ((strb_i & (strb_i + 4'd1)) == 4'd0);
|
||||
endfunction
|
||||
|
||||
function automatic logic [63:0] update_lane_crc(
|
||||
input logic [63:0] crc_i, input logic [3:0] bits_i, input logic [2:0] lane_count_i);
|
||||
logic [63:0] crc;
|
||||
begin
|
||||
crc = crc_i;
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (lane < lane_count_i) begin
|
||||
crc[lane*16+:16] = sdio_pkg::sdio_crc16_next(crc_i[lane*16+:16], bits_i[lane]);
|
||||
end
|
||||
end
|
||||
return crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [3:0] tx_lane_bits(input logic [31:0] word_i, input logic [3:0] bit_i);
|
||||
logic [3:0] bits;
|
||||
begin
|
||||
bits = 4'b0000;
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
bits[lane] = word_i[lane*8+(int'(7)-int'(bit_i))];
|
||||
end
|
||||
return bits;
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign s_target_bytes_calc = {16'd0, block_size_i} * {16'd0, block_count_i};
|
||||
assign s_rx_remaining = s_target_bytes_q - s_rx_total_q;
|
||||
assign s_rx_byte_next = {s_rx_byte_q[6:0], dat_di_i[0]};
|
||||
assign s_phase_timeout = (s_state_q == WriteBusy) ? busy_timeout_cycles_i : timeout_cycles_i;
|
||||
assign s_timeout_expired = (s_phase_timeout == '0) || (s_timeout_count_q >= s_phase_timeout);
|
||||
|
||||
assign s_tx_word_final = s_width4_q
|
||||
? (s_tx_bit_q == 4'd7)
|
||||
: ((s_tx_bit_q == 4'd7) &&
|
||||
(s_tx_byte_q + 1'b1 >= s_tx_active_bytes_q));
|
||||
assign tx_ready_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
|
||||
((s_state_q == WritePayload) && launch_tick_i && s_tx_word_final &&
|
||||
(s_tx_total_q < s_target_bytes_q));
|
||||
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord);
|
||||
assign rx_valid_o = (s_state_q == ReadEmit);
|
||||
assign rx_data_o = s_rx_pending_data_q;
|
||||
assign rx_strb_o = s_rx_pending_strb_q;
|
||||
assign rx_last_o = s_rx_pending_last_q;
|
||||
assign busy_o = s_state_q != Idle;
|
||||
assign done_o = s_done_q;
|
||||
assign error_o = s_err_q;
|
||||
assign timeout_o = s_timeout_q;
|
||||
assign crc_error_o = s_crc_err_q;
|
||||
assign busy_timeout_o = s_busy_timeout_q;
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (tx_valid_i && tx_ready_o && tx_last_i) begin
|
||||
assert (s_tx_total_q + {29'd0, count_strobe(tx_strb_i)} >= s_target_bytes_q)
|
||||
else $error("sdio_data: tx_last asserted before the target byte count");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
always_comb begin
|
||||
dat_oe_o = 4'b0000;
|
||||
dat_do_o = 4'b1111;
|
||||
if ((s_state_q == WriteToken) || (s_state_q == WritePayload) ||
|
||||
(s_state_q == WriteCrc) || s_tx_crc_final_hold_q) begin
|
||||
if (s_width4_q) begin
|
||||
dat_oe_o = (s_tx_active_bytes_q >= 3'd4)
|
||||
? 4'b1111
|
||||
: ((4'b0001 << s_tx_active_bytes_q) - 1'b1);
|
||||
end else begin
|
||||
dat_oe_o = 4'b0001;
|
||||
end
|
||||
end
|
||||
if (s_state_q == WriteToken) begin
|
||||
dat_do_o = 4'b0000;
|
||||
end else if ((s_state_q == WritePayload) && s_width4_q) begin
|
||||
dat_do_o = 4'b1111;
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (lane < s_tx_active_bytes_q) begin
|
||||
dat_do_o[lane] = s_tx_word_q[lane*8+(int'(7)-int'(s_tx_bit_q))];
|
||||
end
|
||||
end
|
||||
end else if (s_state_q == WritePayload) begin
|
||||
dat_do_o[0] = s_tx_word_q[s_tx_byte_q*8+(int'(7)-int'(s_tx_bit_q))];
|
||||
end else if ((s_state_q == WriteCrc) || s_tx_crc_final_hold_q) begin
|
||||
if (s_width4_q) begin
|
||||
dat_do_o = 4'b1111;
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (lane < s_tx_active_bytes_q) begin
|
||||
dat_do_o[lane] = s_tx_crc_lanes_q[lane*16+(int'(15)-int'(s_crc_bit_q))];
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
dat_do_o[0] = s_tx_crc_q[int'(15)-int'(s_crc_bit_q)];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_state_d = s_state_q;
|
||||
s_width4_d = s_width4_q;
|
||||
s_target_bytes_d = s_target_bytes_q;
|
||||
s_tx_total_d = s_tx_total_q;
|
||||
s_rx_total_d = s_rx_total_q;
|
||||
s_tx_word_d = s_tx_word_q;
|
||||
s_tx_strb_d = s_tx_strb_q;
|
||||
s_tx_active_bytes_d = s_tx_active_bytes_q;
|
||||
s_tx_byte_d = s_tx_byte_q;
|
||||
s_tx_bit_d = s_tx_bit_q;
|
||||
s_tx_crc_d = s_tx_crc_q;
|
||||
s_tx_crc_lanes_d = s_tx_crc_lanes_q;
|
||||
s_tx_token_launched_d = s_tx_token_launched_q;
|
||||
s_crc_bit_d = s_crc_bit_q;
|
||||
s_tx_crc_final_hold_d = s_tx_crc_final_hold_q;
|
||||
s_rx_word_d = s_rx_word_q;
|
||||
s_rx_byte_d = s_rx_byte_q;
|
||||
s_rx_active_bytes_d = s_rx_active_bytes_q;
|
||||
s_rx_byte_didx_d = s_rx_byte_didx_q;
|
||||
s_rx_bit_d = s_rx_bit_q;
|
||||
s_rx_crc_d = s_rx_crc_q;
|
||||
s_rx_crc_lanes_d = s_rx_crc_lanes_q;
|
||||
s_rx_crc_received_d = s_rx_crc_received_q;
|
||||
s_rx_crc_received_lanes_d = s_rx_crc_received_lanes_q;
|
||||
s_rx_pending_data_d = s_rx_pending_data_q;
|
||||
s_rx_pending_strb_d = s_rx_pending_strb_q;
|
||||
s_rx_pending_last_d = s_rx_pending_last_q;
|
||||
s_resp_token_d = s_resp_token_q;
|
||||
s_resp_bit_d = s_resp_bit_q;
|
||||
s_timeout_count_d = s_timeout_count_q;
|
||||
s_done_d = 1'b0;
|
||||
s_err_d = s_err_q;
|
||||
s_timeout_d = s_timeout_q;
|
||||
s_crc_err_d = s_crc_err_q;
|
||||
s_busy_timeout_d = s_busy_timeout_q;
|
||||
|
||||
if (s_tx_crc_final_hold_q && sample_tick_i) begin
|
||||
s_tx_crc_final_hold_d = 1'b0;
|
||||
end
|
||||
|
||||
if ((s_state_q != Idle) && s_timeout_expired) begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
s_timeout_d = 1'b1;
|
||||
s_busy_timeout_d = (s_state_q == WriteBusy);
|
||||
s_tx_crc_final_hold_d = 1'b0;
|
||||
end else if (s_state_q != Idle) begin
|
||||
s_timeout_count_d = s_timeout_count_q + 1'b1;
|
||||
end
|
||||
|
||||
if (abort_i && (s_state_q != Idle)) begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
s_tx_crc_final_hold_d = 1'b0;
|
||||
end else if (s_state_q == Idle) begin
|
||||
if (start_i) begin
|
||||
s_width4_d = bus_width_i == 2'd1;
|
||||
s_target_bytes_d = s_target_bytes_calc;
|
||||
s_tx_total_d = '0;
|
||||
s_rx_total_d = '0;
|
||||
s_tx_word_d = '0;
|
||||
s_tx_strb_d = '0;
|
||||
s_tx_active_bytes_d = '0;
|
||||
s_tx_byte_d = '0;
|
||||
s_tx_bit_d = '0;
|
||||
s_tx_crc_d = '0;
|
||||
s_tx_crc_lanes_d = '0;
|
||||
s_tx_token_launched_d = 1'b0;
|
||||
s_crc_bit_d = '0;
|
||||
s_tx_crc_final_hold_d = 1'b0;
|
||||
s_rx_word_d = '0;
|
||||
s_rx_byte_d = '0;
|
||||
s_rx_active_bytes_d = (s_target_bytes_calc >= 32'd4) ? 3'd4 : s_target_bytes_calc[2:0];
|
||||
s_rx_byte_didx_d = '0;
|
||||
s_rx_bit_d = '0;
|
||||
s_rx_crc_d = '0;
|
||||
s_rx_crc_lanes_d = '0;
|
||||
s_rx_crc_received_d = '0;
|
||||
s_rx_crc_received_lanes_d = '0;
|
||||
s_rx_pending_data_d = '0;
|
||||
s_rx_pending_strb_d = '0;
|
||||
s_rx_pending_last_d = 1'b0;
|
||||
s_resp_token_d = '0;
|
||||
s_resp_bit_d = '0;
|
||||
s_timeout_count_d = '0;
|
||||
s_err_d = s_target_bytes_calc == 32'd0;
|
||||
s_timeout_d = 1'b0;
|
||||
s_crc_err_d = 1'b0;
|
||||
s_busy_timeout_d = 1'b0;
|
||||
if (s_target_bytes_calc == 32'd0) begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
end else if (direction_i == sdio_pkg::SdioDataToCard) begin
|
||||
s_state_d = WriteWaitWord;
|
||||
end else begin
|
||||
s_state_d = ReadToken;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
unique case (s_state_q)
|
||||
WriteWaitWord, WriteNextWord: begin
|
||||
if (tx_valid_i && tx_ready_o) begin
|
||||
s_tx_active_bytes_d = count_strobe(tx_strb_i);
|
||||
s_tx_word_d = tx_data_i;
|
||||
s_tx_strb_d = tx_strb_i;
|
||||
s_tx_byte_d = '0;
|
||||
s_tx_bit_d = '0;
|
||||
if (s_state_q == WriteWaitWord) begin
|
||||
s_tx_crc_d = '0;
|
||||
s_tx_crc_lanes_d = '0;
|
||||
end
|
||||
s_tx_token_launched_d = 1'b0;
|
||||
s_tx_total_d = s_tx_total_q + {29'd0, count_strobe(tx_strb_i)};
|
||||
s_tx_crc_final_hold_d = 1'b0;
|
||||
s_timeout_count_d = '0;
|
||||
if (!strobe_valid(
|
||||
tx_strb_i
|
||||
) || (s_tx_total_q + {29'd0, count_strobe(
|
||||
tx_strb_i
|
||||
)} > s_target_bytes_q)) begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
end else begin
|
||||
s_state_d = (s_state_q == WriteWaitWord) ? WriteToken : WritePayload;
|
||||
end
|
||||
end
|
||||
end
|
||||
WriteToken: begin
|
||||
if (sample_tick_i) begin
|
||||
s_tx_token_launched_d = 1'b1;
|
||||
end
|
||||
if (launch_tick_i && s_tx_token_launched_q) begin
|
||||
s_state_d = WritePayload;
|
||||
s_tx_byte_d = '0;
|
||||
s_tx_bit_d = '0;
|
||||
s_timeout_count_d = '0;
|
||||
s_tx_token_launched_d = 1'b0;
|
||||
end
|
||||
end
|
||||
WritePayload: begin
|
||||
if (launch_tick_i) begin
|
||||
if (s_width4_q) begin
|
||||
s_tx_crc_lanes_d = update_lane_crc(
|
||||
s_tx_crc_lanes_q, tx_lane_bits(s_tx_word_q, s_tx_bit_q), s_tx_active_bytes_q);
|
||||
if (s_tx_bit_q == 4'd7) begin
|
||||
if (s_tx_total_q >= s_target_bytes_q) begin
|
||||
s_state_d = WriteCrc;
|
||||
s_crc_bit_d = '0;
|
||||
end else if (tx_valid_i && tx_ready_o) begin
|
||||
s_tx_word_d = tx_data_i;
|
||||
s_tx_strb_d = tx_strb_i;
|
||||
s_tx_active_bytes_d = count_strobe(tx_strb_i);
|
||||
s_tx_total_d = s_tx_total_q + {29'd0, count_strobe(tx_strb_i)};
|
||||
s_tx_byte_d = '0;
|
||||
s_tx_bit_d = '0;
|
||||
end else begin
|
||||
s_state_d = WriteNextWord;
|
||||
end
|
||||
end else begin
|
||||
s_tx_bit_d = s_tx_bit_q + 1'b1;
|
||||
end
|
||||
end else if (s_tx_bit_q == 4'd7) begin
|
||||
s_tx_crc_d = sdio_pkg::sdio_crc16_byte(s_tx_crc_q, s_tx_word_q[s_tx_byte_q*8+:8]);
|
||||
if (s_tx_byte_q + 1'b1 >= s_tx_active_bytes_q) begin
|
||||
if (s_tx_total_q >= s_target_bytes_q) begin
|
||||
s_state_d = WriteCrc;
|
||||
s_crc_bit_d = '0;
|
||||
end else if (tx_valid_i && tx_ready_o) begin
|
||||
s_tx_word_d = tx_data_i;
|
||||
s_tx_strb_d = tx_strb_i;
|
||||
s_tx_active_bytes_d = count_strobe(tx_strb_i);
|
||||
s_tx_total_d = s_tx_total_q + {29'd0, count_strobe(tx_strb_i)};
|
||||
s_tx_byte_d = '0;
|
||||
s_tx_bit_d = '0;
|
||||
end else begin
|
||||
s_state_d = WriteNextWord;
|
||||
end
|
||||
end else begin
|
||||
s_tx_byte_d = s_tx_byte_q + 1'b1;
|
||||
s_tx_bit_d = '0;
|
||||
end
|
||||
end else begin
|
||||
s_tx_bit_d = s_tx_bit_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
WriteCrc: begin
|
||||
if (launch_tick_i) begin
|
||||
if (s_crc_bit_q == 5'd15) begin
|
||||
s_tx_crc_final_hold_d = 1'b1;
|
||||
s_state_d = WriteResponse;
|
||||
s_resp_token_d = '0;
|
||||
s_resp_bit_d = '0;
|
||||
s_timeout_count_d = '0;
|
||||
end else begin
|
||||
s_crc_bit_d = s_crc_bit_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
WriteResponse: begin
|
||||
if (launch_tick_i && !s_tx_crc_final_hold_q) begin
|
||||
s_resp_token_d = {s_resp_token_q[3:0], dat0_i};
|
||||
if (s_resp_bit_q == 3'd4) begin
|
||||
if ({s_resp_token_q[3:0], dat0_i} == 5'b00101) begin
|
||||
s_state_d = WriteBusy;
|
||||
s_timeout_count_d = '0;
|
||||
end else begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
s_err_d = 1'b1;
|
||||
if ({s_resp_token_q[3:0], dat0_i} == 5'b01011) begin
|
||||
s_crc_err_d = 1'b1;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
s_resp_bit_d = s_resp_bit_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
WriteBusy: begin
|
||||
if (sample_tick_i && dat0_i) begin
|
||||
if (s_tx_total_q >= s_target_bytes_q) begin
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
end else begin
|
||||
s_state_d = WriteWaitWord;
|
||||
s_timeout_count_d = '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
ReadToken: begin
|
||||
if (sample_tick_i && (dat_di_i == 4'b0000)) begin
|
||||
s_state_d = ReadPayload;
|
||||
s_rx_word_d = '0;
|
||||
s_rx_byte_d = '0;
|
||||
s_rx_byte_didx_d = '0;
|
||||
s_rx_bit_d = '0;
|
||||
s_rx_crc_d = '0;
|
||||
s_rx_crc_lanes_d = '0;
|
||||
s_timeout_count_d = '0;
|
||||
end
|
||||
end
|
||||
ReadPayload: begin
|
||||
if (sample_tick_i) begin
|
||||
if (s_width4_q) begin
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (lane < s_rx_active_bytes_q) begin
|
||||
s_rx_word_d[lane*8+(int'(7)-int'(s_rx_bit_q))] = dat_di_i[lane];
|
||||
s_rx_crc_lanes_d[lane*16+:16] =
|
||||
sdio_pkg::sdio_crc16_next(s_rx_crc_lanes_q[lane*16+:16], dat_di_i[lane]);
|
||||
end
|
||||
end
|
||||
if (s_rx_bit_q == 4'd7) begin
|
||||
s_rx_pending_data_d = s_rx_word_d;
|
||||
s_rx_pending_strb_d = (4'b0001 << s_rx_active_bytes_q) - 1'b1;
|
||||
s_rx_pending_last_d =
|
||||
s_rx_total_q + {29'd0, s_rx_active_bytes_q} >= s_target_bytes_q;
|
||||
s_rx_total_d = s_rx_total_q + {29'd0, s_rx_active_bytes_q};
|
||||
s_state_d = ReadEmit;
|
||||
end else begin
|
||||
s_rx_bit_d = s_rx_bit_q + 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_rx_byte_d = s_rx_byte_next;
|
||||
if (s_rx_bit_q == 4'd7) begin
|
||||
s_rx_word_d[s_rx_byte_didx_q*8+:8] = s_rx_byte_next;
|
||||
s_rx_byte_d = '0;
|
||||
if (s_rx_byte_didx_q + 1'b1 >= s_rx_active_bytes_q) begin
|
||||
s_rx_pending_data_d = s_rx_word_d;
|
||||
s_rx_pending_strb_d = (4'b0001 << s_rx_active_bytes_q) - 1'b1;
|
||||
s_rx_pending_last_d =
|
||||
s_rx_total_q + {29'd0, s_rx_active_bytes_q} >= s_target_bytes_q;
|
||||
s_rx_total_d = s_rx_total_q + {29'd0, s_rx_active_bytes_q};
|
||||
s_state_d = ReadEmit;
|
||||
end else begin
|
||||
s_rx_byte_didx_d = s_rx_byte_didx_q + 1'b1;
|
||||
s_rx_bit_d = '0;
|
||||
end
|
||||
s_rx_crc_d = sdio_pkg::sdio_crc16_byte(s_rx_crc_q, s_rx_byte_next);
|
||||
end else begin
|
||||
s_rx_bit_d = s_rx_bit_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ReadEmit: begin
|
||||
if (rx_valid_o && rx_ready_i) begin
|
||||
if (s_rx_pending_last_q) begin
|
||||
s_crc_bit_d = '0;
|
||||
s_state_d = ReadCrc;
|
||||
end else begin
|
||||
s_rx_word_d = '0;
|
||||
s_rx_byte_d = '0;
|
||||
s_rx_active_bytes_d = (s_rx_remaining >= 32'd4) ? 3'd4 : s_rx_remaining[2:0];
|
||||
s_rx_byte_didx_d = '0;
|
||||
s_rx_bit_d = '0;
|
||||
s_state_d = ReadPayload;
|
||||
s_timeout_count_d = '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
ReadCrc: begin
|
||||
if (sample_tick_i) begin
|
||||
if (s_width4_q) begin
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if (lane < s_rx_active_bytes_q) begin
|
||||
s_rx_crc_received_lanes_d[lane*16+:16] = {
|
||||
s_rx_crc_received_lanes_q[lane*16+14-:15], dat_di_i[lane]
|
||||
};
|
||||
end
|
||||
end
|
||||
if (s_crc_bit_q == 5'd15) begin
|
||||
for (int lane = 0; lane < 4; lane++) begin
|
||||
if ((lane < s_rx_active_bytes_q) &&
|
||||
(s_rx_crc_received_lanes_d[lane*16+:16] !=
|
||||
s_rx_crc_lanes_q[lane*16+:16])) begin
|
||||
s_err_d = 1'b1;
|
||||
s_crc_err_d = 1'b1;
|
||||
end
|
||||
end
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
end else begin
|
||||
s_crc_bit_d = s_crc_bit_q + 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_rx_crc_received_d = {s_rx_crc_received_q[14:0], dat_di_i[0]};
|
||||
if (s_crc_bit_q == 5'd15) begin
|
||||
if ({s_rx_crc_received_q[14:0], dat_di_i[0]} != s_rx_crc_q) begin
|
||||
s_err_d = 1'b1;
|
||||
s_crc_err_d = 1'b1;
|
||||
end
|
||||
s_state_d = Idle;
|
||||
s_done_d = 1'b1;
|
||||
end else begin
|
||||
s_crc_bit_d = s_crc_bit_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
s_state_d = Idle;
|
||||
s_err_d = 1'b1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_width4_q <= 1'b0;
|
||||
s_target_bytes_q <= '0;
|
||||
s_tx_total_q <= '0;
|
||||
s_rx_total_q <= '0;
|
||||
s_tx_word_q <= '0;
|
||||
s_tx_strb_q <= '0;
|
||||
s_tx_active_bytes_q <= '0;
|
||||
s_tx_byte_q <= '0;
|
||||
s_tx_bit_q <= '0;
|
||||
s_tx_crc_q <= '0;
|
||||
s_tx_crc_lanes_q <= '0;
|
||||
s_tx_token_launched_q <= 1'b0;
|
||||
s_crc_bit_q <= '0;
|
||||
s_tx_crc_final_hold_q <= 1'b0;
|
||||
s_rx_word_q <= '0;
|
||||
s_rx_byte_q <= '0;
|
||||
s_rx_active_bytes_q <= '0;
|
||||
s_rx_byte_didx_q <= '0;
|
||||
s_rx_bit_q <= '0;
|
||||
s_rx_crc_q <= '0;
|
||||
s_rx_crc_lanes_q <= '0;
|
||||
s_rx_crc_received_q <= '0;
|
||||
s_rx_crc_received_lanes_q <= '0;
|
||||
s_rx_pending_data_q <= '0;
|
||||
s_rx_pending_strb_q <= '0;
|
||||
s_rx_pending_last_q <= 1'b0;
|
||||
s_resp_token_q <= '0;
|
||||
s_resp_bit_q <= '0;
|
||||
s_timeout_count_q <= '0;
|
||||
s_done_q <= 1'b0;
|
||||
s_err_q <= 1'b0;
|
||||
s_timeout_q <= 1'b0;
|
||||
s_crc_err_q <= 1'b0;
|
||||
s_busy_timeout_q <= 1'b0;
|
||||
end else begin
|
||||
s_state_q <= s_state_d;
|
||||
s_width4_q <= s_width4_d;
|
||||
s_target_bytes_q <= s_target_bytes_d;
|
||||
s_tx_total_q <= s_tx_total_d;
|
||||
s_rx_total_q <= s_rx_total_d;
|
||||
s_tx_word_q <= s_tx_word_d;
|
||||
s_tx_strb_q <= s_tx_strb_d;
|
||||
s_tx_active_bytes_q <= s_tx_active_bytes_d;
|
||||
s_tx_byte_q <= s_tx_byte_d;
|
||||
s_tx_bit_q <= s_tx_bit_d;
|
||||
s_tx_crc_q <= s_tx_crc_d;
|
||||
s_tx_crc_lanes_q <= s_tx_crc_lanes_d;
|
||||
s_tx_token_launched_q <= s_tx_token_launched_d;
|
||||
s_crc_bit_q <= s_crc_bit_d;
|
||||
s_tx_crc_final_hold_q <= s_tx_crc_final_hold_d;
|
||||
s_rx_word_q <= s_rx_word_d;
|
||||
s_rx_byte_q <= s_rx_byte_d;
|
||||
s_rx_active_bytes_q <= s_rx_active_bytes_d;
|
||||
s_rx_byte_didx_q <= s_rx_byte_didx_d;
|
||||
s_rx_bit_q <= s_rx_bit_d;
|
||||
s_rx_crc_q <= s_rx_crc_d;
|
||||
s_rx_crc_lanes_q <= s_rx_crc_lanes_d;
|
||||
s_rx_crc_received_q <= s_rx_crc_received_d;
|
||||
s_rx_crc_received_lanes_q <= s_rx_crc_received_lanes_d;
|
||||
s_rx_pending_data_q <= s_rx_pending_data_d;
|
||||
s_rx_pending_strb_q <= s_rx_pending_strb_d;
|
||||
s_rx_pending_last_q <= s_rx_pending_last_d;
|
||||
s_resp_token_q <= s_resp_token_d;
|
||||
s_resp_bit_q <= s_resp_bit_d;
|
||||
s_timeout_count_q <= s_timeout_count_d;
|
||||
s_done_q <= s_done_d;
|
||||
s_err_q <= s_err_d;
|
||||
s_timeout_q <= s_timeout_d;
|
||||
s_crc_err_q <= s_crc_err_d;
|
||||
s_busy_timeout_q <= s_busy_timeout_d;
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if (TimeoutWidth < 1) begin
|
||||
$fatal(1, "sdio_data: TimeoutWidth must be positive");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`ifndef RETROSOC_SDIO_DEFINE_SVH
|
||||
`define RETROSOC_SDIO_DEFINE_SVH
|
||||
|
||||
// verilog_format: off -- preserve the reviewed APB ABI column alignment
|
||||
`define APB4_SDIO__IP_ID 12'h000
|
||||
`define APB4_SDIO__IP_VERSION 12'h004
|
||||
`define APB4_SDIO__CAPABILITY 12'h008
|
||||
`define APB4_SDIO__HOST_CTRL 12'h00C
|
||||
`define APB4_SDIO__CLOCK_CTRL 12'h010
|
||||
`define APB4_SDIO__CLOCK_ACTUAL 12'h014
|
||||
`define APB4_SDIO__BUS_CTRL 12'h018
|
||||
`define APB4_SDIO__TIMEOUT_CMD 12'h01C
|
||||
`define APB4_SDIO__TIMEOUT_DATA 12'h020
|
||||
`define APB4_SDIO__TIMEOUT_BUSY 12'h024
|
||||
`define APB4_SDIO__STATUS 12'h028
|
||||
`define APB4_SDIO__PRESENT 12'h02C
|
||||
|
||||
`define APB4_SDIO__CMD_ARG 12'h040
|
||||
`define APB4_SDIO__CMD_CFG 12'h044
|
||||
`define APB4_SDIO__CMD_START 12'h048
|
||||
`define APB4_SDIO__CMD_STATUS 12'h04C
|
||||
`define APB4_SDIO__RESP0 12'h050
|
||||
`define APB4_SDIO__RESP1 12'h054
|
||||
`define APB4_SDIO__RESP2 12'h058
|
||||
`define APB4_SDIO__RESP3 12'h05C
|
||||
`define APB4_SDIO__RESP4 12'h060
|
||||
|
||||
`define APB4_SDIO__BLOCK_SIZE 12'h080
|
||||
`define APB4_SDIO__BLOCK_COUNT 12'h084
|
||||
`define APB4_SDIO__DATA_CFG 12'h088
|
||||
`define APB4_SDIO__DATA_START 12'h08C
|
||||
`define APB4_SDIO__PIO_DATA 12'h090
|
||||
`define APB4_SDIO__FIFO_STATUS 12'h094
|
||||
`define APB4_SDIO__FIFO_WATERMARK 12'h098
|
||||
|
||||
`define APB4_SDIO__DESC_BASE 12'h0C0
|
||||
`define APB4_SDIO__DESC_COUNT 12'h0C4
|
||||
`define APB4_SDIO__DMA_CTRL 12'h0C8
|
||||
`define APB4_SDIO__DMA_STATUS 12'h0CC
|
||||
`define APB4_SDIO__CURRENT_DESC 12'h0D0
|
||||
`define APB4_SDIO__BYTES_DONE 12'h0D4
|
||||
`define APB4_SDIO__DMA_ERROR_ADDR 12'h0D8
|
||||
`define APB4_SDIO__DMA_ERROR 12'h0DC
|
||||
|
||||
`define APB4_SDIO__IRQ_STATUS 12'h100
|
||||
`define APB4_SDIO__IRQ_ENABLE 12'h104
|
||||
`define APB4_SDIO__IRQ_TEST 12'h108
|
||||
`define APB4_SDIO__ERROR_STATUS 12'h10C
|
||||
`define APB4_SDIO__ERROR_ABORT 7
|
||||
`define APB4_SDIO__LAST_CMD 12'h110
|
||||
`define APB4_SDIO__CRC_ERROR_COUNT 12'h114
|
||||
`define APB4_SDIO__TIMEOUT_COUNT 12'h118
|
||||
`define APB4_SDIO__AXI_ERROR_COUNT 12'h11C
|
||||
`define APB4_SDIO__STALL_COUNT 12'h120
|
||||
`define APB4_SDIO__DEBUG 12'h124
|
||||
|
||||
`define APB4_SDIO__HOST_CTRL_ENABLE 0
|
||||
`define APB4_SDIO__HOST_CTRL_ABORT 1
|
||||
`define APB4_SDIO__HOST_CTRL_IRQ 2
|
||||
`define APB4_SDIO__CLOCK_CTRL_ENABLE 0
|
||||
`define APB4_SDIO__CLOCK_CTRL_HALF_PERIOD_LSB 8
|
||||
`define APB4_SDIO__BUS_CTRL_WIDTH_LSB 0
|
||||
`define APB4_SDIO__BUS_CTRL_WIDTH_1 0
|
||||
`define APB4_SDIO__BUS_CTRL_WIDTH_4 1
|
||||
`define APB4_SDIO__CMD_CFG_INDEX_LSB 0
|
||||
`define APB4_SDIO__CMD_CFG_RESP_LSB 8
|
||||
`define APB4_SDIO__CMD_CFG_CRC_CHECK 12
|
||||
`define APB4_SDIO__CMD_CFG_INDEX_CHECK 13
|
||||
`define APB4_SDIO__RESP_NONE 0
|
||||
`define APB4_SDIO__RESP_R1 1
|
||||
`define APB4_SDIO__RESP_R1B 2
|
||||
`define APB4_SDIO__RESP_R2 3
|
||||
`define APB4_SDIO__RESP_R3 4
|
||||
`define APB4_SDIO__RESP_R4 5
|
||||
`define APB4_SDIO__RESP_R5 6
|
||||
`define APB4_SDIO__RESP_R6 7
|
||||
`define APB4_SDIO__RESP_R7 8
|
||||
`define APB4_SDIO__DATA_CFG_DIRECTION 0
|
||||
`define APB4_SDIO__DATA_CFG_WIDTH_LSB 1
|
||||
`define APB4_SDIO__DATA_CFG_DMA 4
|
||||
`define APB4_SDIO__DATA_CFG_BLOCK 5
|
||||
`define APB4_SDIO__DATA_CFG_FIXED 6
|
||||
`define APB4_SDIO__DMA_CTRL_START 0
|
||||
`define APB4_SDIO__DMA_CTRL_ABORT 1
|
||||
`define APB4_SDIO__DESC_OWN 0
|
||||
`define APB4_SDIO__DESC_CHAIN 1
|
||||
`define APB4_SDIO__DESC_END 2
|
||||
`define APB4_SDIO__DESC_IRQ 3
|
||||
`define APB4_SDIO__DESC_DONE 16
|
||||
`define APB4_SDIO__DESC_ERROR 17
|
||||
|
||||
`define APB4_SDIO__IRQ_CMD_DONE 0
|
||||
`define APB4_SDIO__IRQ_DATA_DONE 1
|
||||
`define APB4_SDIO__IRQ_DMA_DONE 2
|
||||
`define APB4_SDIO__IRQ_CMD_ERROR 3
|
||||
`define APB4_SDIO__IRQ_DATA_ERROR 4
|
||||
`define APB4_SDIO__IRQ_DMA_ERROR 5
|
||||
`define APB4_SDIO__IRQ_DAT1 6
|
||||
`define APB4_SDIO__IRQ_ABORT 7
|
||||
|
||||
`define APB4_SDIO__CAP_SD_MEMORY_V2 0
|
||||
`define APB4_SDIO__CAP_SDIO_V2 1
|
||||
`define APB4_SDIO__CAP_BUS_1BIT 2
|
||||
`define APB4_SDIO__CAP_BUS_4BIT 3
|
||||
`define APB4_SDIO__CAP_SDR 4
|
||||
`define APB4_SDIO__CAP_SG_DMA 5
|
||||
`define APB4_SDIO__CAP_PIO 6
|
||||
`define APB4_SDIO__CAP_CRC 7
|
||||
`define APB4_SDIO__CAP_MAX_BURST_16 8
|
||||
// verilog_format: on
|
||||
|
||||
`endif
|
||||
@@ -0,0 +1,646 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module sdio_dma #(
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int DescCount = 16
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic start_i,
|
||||
input logic abort_i,
|
||||
input logic direction_i,
|
||||
input logic [AddrWidth-1:0] desc_base_i,
|
||||
input logic [ 15:0] desc_count_i,
|
||||
input logic [ 31:0] total_bytes_i,
|
||||
input logic data_in_valid_i,
|
||||
output logic data_in_ready_o,
|
||||
input logic [ 31:0] data_in_i,
|
||||
input logic [ 3:0] data_in_strb_i,
|
||||
input logic data_in_last_i,
|
||||
output logic data_out_valid_o,
|
||||
input logic data_out_ready_i,
|
||||
output logic [ 31:0] data_out_o,
|
||||
output logic [ 3:0] data_out_strb_o,
|
||||
output logic data_out_last_o,
|
||||
output logic busy_o,
|
||||
output logic done_o,
|
||||
output logic error_o,
|
||||
output logic [ 7:0] error_code_o,
|
||||
output logic [AddrWidth-1:0] current_desc_o,
|
||||
output logic [ 31:0] bytes_done_o,
|
||||
output logic [AddrWidth-1:0] error_addr_o,
|
||||
output logic descriptor_irq_o,
|
||||
output logic abort_done_o,
|
||||
axi4_if.master dma_axi4
|
||||
);
|
||||
localparam logic [15:0] DescCountLimit = 16'(DescCount);
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
FetchStart,
|
||||
FetchWait,
|
||||
Validate,
|
||||
PayloadReadStart,
|
||||
PayloadReadWait,
|
||||
PayloadWriteStart,
|
||||
PayloadWriteWait,
|
||||
WritebackStart,
|
||||
WritebackWait,
|
||||
Terminal
|
||||
} dma_state_e;
|
||||
|
||||
dma_state_e s_state_d, s_state_q;
|
||||
logic [AddrWidth-1:0] s_desc_addr_d, s_desc_addr_q;
|
||||
logic [15:0] s_desc_limit_d, s_desc_limit_q;
|
||||
logic [15:0] s_desc_index_d, s_desc_index_q;
|
||||
logic s_direction_d, s_direction_q;
|
||||
logic [31:0] s_total_bytes_d, s_total_bytes_q;
|
||||
logic [31:0] s_total_seen_d, s_total_seen_q;
|
||||
logic [31:0] s_total_done_d, s_total_done_q;
|
||||
logic [31:0] s_desc_progress_d, s_desc_progress_q;
|
||||
logic [4:0] s_burst_beats_d, s_burst_beats_q;
|
||||
logic [4:0] s_burst_index_d, s_burst_index_q;
|
||||
logic [31:0] s_desc_buffer_d, s_desc_buffer_q;
|
||||
logic [31:0] s_desc_count_d, s_desc_count_q;
|
||||
logic [31:0] s_desc_next_d, s_desc_next_q;
|
||||
logic [31:0] s_desc_control_d, s_desc_control_q;
|
||||
logic [31:0] s_writeback_control_d, s_writeback_control_q;
|
||||
logic [31:0] s_fetch_word_d[0:3];
|
||||
logic [31:0] s_fetch_word_q[0:3];
|
||||
logic s_desc_end_d, s_desc_end_q;
|
||||
logic s_desc_irq_d, s_desc_irq_q;
|
||||
logic s_fault_d, s_fault_q;
|
||||
logic [7:0] s_err_code_d, s_err_code_q;
|
||||
logic [AddrWidth-1:0] s_err_addr_d, s_err_addr_q;
|
||||
logic s_done_d, s_done_q;
|
||||
logic s_err_sticky_d, s_err_sticky_q;
|
||||
logic s_abort_pending_d, s_abort_pending_q;
|
||||
logic s_write_final_sent_d, s_write_final_sent_q;
|
||||
logic [4:0] s_write_beat_count_d, s_write_beat_count_q;
|
||||
logic s_descriptor_irq_d, s_descriptor_irq_q;
|
||||
logic s_abort_done_d, s_abort_done_q;
|
||||
|
||||
logic s_read_start_valid;
|
||||
logic s_read_start_ready;
|
||||
logic [AddrWidth-1:0] s_read_addr;
|
||||
logic [ 4:0] s_read_beats;
|
||||
logic s_read_beat_valid;
|
||||
logic s_read_beat_ready;
|
||||
logic [DataWidth-1:0] s_read_data;
|
||||
logic [ 1:0] s_read_resp;
|
||||
logic s_read_last;
|
||||
logic s_read_expected_last;
|
||||
logic s_read_id_error;
|
||||
logic s_read_done;
|
||||
logic s_write_start_valid;
|
||||
logic s_write_start_ready;
|
||||
logic [AddrWidth-1:0] s_write_addr;
|
||||
logic [ 4:0] s_write_beats;
|
||||
logic s_write_data_valid;
|
||||
logic s_write_data_ready;
|
||||
logic [DataWidth-1:0] s_write_data;
|
||||
logic [ 3:0] s_write_strb;
|
||||
logic s_write_done;
|
||||
logic [ 1:0] s_write_resp;
|
||||
logic s_write_id_error;
|
||||
logic s_read_busy;
|
||||
logic s_write_busy;
|
||||
|
||||
logic [ 31:0] s_payload_addr;
|
||||
logic [ 31:0] s_payload_left;
|
||||
logic [ 4:0] s_payload_beats;
|
||||
logic [ 31:0] s_burst_offset;
|
||||
logic [ 31:0] s_beat_left;
|
||||
logic [ 2:0] s_beat_bytes;
|
||||
logic [ 31:0] s_progress_after_beat;
|
||||
logic [ 2:0] s_input_bytes;
|
||||
logic s_axi_read_fault;
|
||||
logic s_axi_write_fault;
|
||||
|
||||
function automatic logic [4:0] choose_burst(input logic [11:0] addr_low_i,
|
||||
input logic [31:0] bytes_i);
|
||||
logic [31:0] needed;
|
||||
logic [31:0] boundary;
|
||||
logic [31:0] selected;
|
||||
begin
|
||||
needed = (bytes_i + 32'd3) >> 2;
|
||||
boundary = (32'd4096 - {20'd0, addr_low_i}) >> 2;
|
||||
selected = needed;
|
||||
if (selected > 32'd16) selected = 32'd16;
|
||||
if (selected > boundary) selected = boundary;
|
||||
if (selected == 32'd0) selected = 32'd1;
|
||||
return selected[4:0];
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [2:0] beat_bytes(input logic [31:0] bytes_i);
|
||||
return (bytes_i >= 32'd4) ? 3'd4 : bytes_i[2:0];
|
||||
endfunction
|
||||
|
||||
function automatic logic strobe_valid(input logic [3:0] strb_i);
|
||||
return (strb_i != 4'd0) && ((strb_i & (strb_i + 4'd1)) == 4'd0);
|
||||
endfunction
|
||||
|
||||
assign s_payload_addr = s_desc_buffer_q + s_desc_progress_q;
|
||||
assign s_payload_left = s_desc_count_q - s_desc_progress_q;
|
||||
assign s_payload_beats = choose_burst(s_payload_addr[11:0], s_payload_left);
|
||||
assign s_burst_offset = {27'd0, s_burst_index_q} << 2;
|
||||
assign s_beat_left = s_payload_left;
|
||||
assign s_beat_bytes = beat_bytes(s_beat_left);
|
||||
assign s_progress_after_beat = s_desc_progress_q + {29'd0, s_beat_bytes};
|
||||
assign s_input_bytes = count_strobe(data_in_strb_i);
|
||||
assign s_axi_read_fault = (s_read_resp != `AXI4_RESP_OKAY) || s_read_id_error ||
|
||||
(s_read_last != s_read_expected_last);
|
||||
assign s_axi_write_fault = (s_write_resp != `AXI4_RESP_OKAY) || s_write_id_error;
|
||||
|
||||
function automatic logic [2:0] count_strobe(input logic [3:0] strb_i);
|
||||
logic [2:0] count;
|
||||
begin
|
||||
count = 3'd0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
if (strb_i[index]) count = count + 1'b1;
|
||||
end
|
||||
return count;
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign s_read_start_valid = !abort_i &&
|
||||
((s_state_q == FetchStart) || (s_state_q == PayloadReadStart));
|
||||
assign s_read_addr = (s_state_q == FetchStart) ? s_desc_addr_q : s_payload_addr;
|
||||
assign s_read_beats = (s_state_q == FetchStart) ? 5'd4 : s_payload_beats;
|
||||
assign s_read_beat_ready = (s_state_q == FetchWait) ||
|
||||
((s_state_q == PayloadReadWait) &&
|
||||
(abort_i || s_abort_pending_q || data_out_ready_i));
|
||||
|
||||
assign s_write_start_valid = (s_state_q == PayloadWriteStart) || (s_state_q == WritebackStart);
|
||||
assign s_write_addr = (s_state_q == PayloadWriteStart) ? s_payload_addr : s_desc_addr_q + 32'd12;
|
||||
assign s_write_beats = (s_state_q == PayloadWriteStart) ? s_payload_beats : 5'd1;
|
||||
assign s_write_data_valid = (s_state_q == PayloadWriteWait)
|
||||
? (s_abort_pending_q || data_in_valid_i)
|
||||
: (s_state_q == WritebackWait);
|
||||
assign s_write_data = (s_state_q == PayloadWriteWait && s_abort_pending_q)
|
||||
? '0
|
||||
: (s_state_q == WritebackWait ? s_writeback_control_q : data_in_i);
|
||||
assign s_write_strb = (s_state_q == PayloadWriteWait && s_abort_pending_q)
|
||||
? 4'b0000
|
||||
: (s_state_q == WritebackWait ? 4'b1111 : data_in_strb_i);
|
||||
|
||||
assign data_in_ready_o = (s_state_q == PayloadWriteWait) && !s_abort_pending_q &&
|
||||
s_write_data_ready;
|
||||
assign data_out_valid_o = (s_state_q == PayloadReadWait) && s_read_beat_valid &&
|
||||
!s_abort_pending_q && !s_axi_read_fault;
|
||||
assign data_out_o = s_read_data;
|
||||
assign data_out_strb_o = (4'b0001 << s_beat_bytes) - 1'b1;
|
||||
assign data_out_last_o = s_desc_end_q && (s_progress_after_beat >= s_desc_count_q);
|
||||
assign busy_o = ((s_state_q != Idle) && (s_state_q != Terminal)) || s_read_busy || s_write_busy;
|
||||
assign done_o = s_done_q;
|
||||
assign error_o = s_err_sticky_q;
|
||||
assign error_code_o = s_err_code_q;
|
||||
assign current_desc_o = s_desc_addr_q;
|
||||
assign bytes_done_o = s_total_done_q;
|
||||
assign error_addr_o = s_err_addr_q;
|
||||
assign descriptor_irq_o = s_descriptor_irq_q;
|
||||
assign abort_done_o = s_abort_done_q;
|
||||
|
||||
dma_axi4_master #(
|
||||
.AddrWidth (AddrWidth),
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats(16)
|
||||
) u_dma_axi4_master (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o(s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_error),
|
||||
.read_done_o (s_read_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strb),
|
||||
.write_done_o (s_write_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_error),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
always_comb begin
|
||||
s_state_d = s_state_q;
|
||||
s_desc_addr_d = s_desc_addr_q;
|
||||
s_desc_limit_d = s_desc_limit_q;
|
||||
s_desc_index_d = s_desc_index_q;
|
||||
s_direction_d = s_direction_q;
|
||||
s_total_bytes_d = s_total_bytes_q;
|
||||
s_total_seen_d = s_total_seen_q;
|
||||
s_total_done_d = s_total_done_q;
|
||||
s_desc_progress_d = s_desc_progress_q;
|
||||
s_burst_beats_d = s_burst_beats_q;
|
||||
s_burst_index_d = s_burst_index_q;
|
||||
s_desc_buffer_d = s_desc_buffer_q;
|
||||
s_desc_count_d = s_desc_count_q;
|
||||
s_desc_next_d = s_desc_next_q;
|
||||
s_desc_control_d = s_desc_control_q;
|
||||
s_writeback_control_d = s_writeback_control_q;
|
||||
s_desc_end_d = s_desc_end_q;
|
||||
s_desc_irq_d = s_desc_irq_q;
|
||||
s_fault_d = s_fault_q;
|
||||
s_err_code_d = s_err_code_q;
|
||||
s_err_addr_d = s_err_addr_q;
|
||||
s_done_d = 1'b0;
|
||||
s_err_sticky_d = s_err_sticky_q;
|
||||
s_abort_pending_d = s_abort_pending_q;
|
||||
s_write_final_sent_d = s_write_final_sent_q;
|
||||
s_write_beat_count_d = s_write_beat_count_q;
|
||||
s_descriptor_irq_d = 1'b0;
|
||||
s_abort_done_d = 1'b0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
s_fetch_word_d[index] = s_fetch_word_q[index];
|
||||
end
|
||||
|
||||
if (s_state_q == Idle) begin
|
||||
if (start_i) begin
|
||||
s_desc_addr_d = desc_base_i;
|
||||
s_desc_limit_d = desc_count_i;
|
||||
s_desc_index_d = '0;
|
||||
s_direction_d = direction_i;
|
||||
s_total_bytes_d = total_bytes_i;
|
||||
s_total_seen_d = '0;
|
||||
s_total_done_d = '0;
|
||||
s_desc_progress_d = '0;
|
||||
s_fault_d = 1'b0;
|
||||
s_err_code_d = '0;
|
||||
s_err_addr_d = '0;
|
||||
s_err_sticky_d = 1'b0;
|
||||
s_abort_pending_d = 1'b0;
|
||||
if ((desc_base_i[3:0] != 4'b0000) || (desc_base_i[11:0] >= 12'hFF0) ||
|
||||
(desc_count_i == 16'd0) || (desc_count_i > DescCountLimit)) begin
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h02;
|
||||
s_err_addr_d = desc_base_i;
|
||||
end else begin
|
||||
s_state_d = FetchStart;
|
||||
end
|
||||
end
|
||||
end else if (s_state_q == FetchStart) begin
|
||||
if (abort_i) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_desc_addr_q;
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
s_abort_done_d = 1'b1;
|
||||
end else if (s_desc_addr_q[3:0] != 4'b0000 || s_desc_addr_q[11:0] >= 12'hFF0) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h02;
|
||||
s_err_addr_d = s_desc_addr_q;
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
end else if (s_read_start_ready) begin
|
||||
s_burst_index_d = '0;
|
||||
s_state_d = FetchWait;
|
||||
end
|
||||
end else begin
|
||||
unique case (s_state_q)
|
||||
FetchWait: begin
|
||||
if (abort_i) begin
|
||||
s_abort_pending_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_desc_addr_q;
|
||||
end
|
||||
if (s_read_beat_valid && s_read_beat_ready) begin
|
||||
s_fetch_word_d[s_burst_index_q[1:0]] = s_read_data;
|
||||
if (s_axi_read_fault) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h04;
|
||||
s_err_addr_d = s_desc_addr_q;
|
||||
end
|
||||
if (s_read_done) begin
|
||||
s_abort_pending_d = 1'b0;
|
||||
s_state_d = Validate;
|
||||
end else begin
|
||||
s_burst_index_d = s_burst_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
Validate: begin
|
||||
s_desc_buffer_d = s_fetch_word_q[0];
|
||||
s_desc_count_d = s_fetch_word_q[1];
|
||||
s_desc_next_d = s_fetch_word_q[2];
|
||||
s_desc_control_d = s_fetch_word_q[3];
|
||||
s_desc_end_d = s_fetch_word_q[3][sdio_pkg::SDIO_DESC_END];
|
||||
s_desc_irq_d = s_fetch_word_q[3][sdio_pkg::SDIO_DESC_IRQ];
|
||||
s_desc_progress_d = '0;
|
||||
s_total_seen_d = s_total_seen_q + s_fetch_word_q[1];
|
||||
if (!s_fetch_word_q[3][sdio_pkg::SDIO_DESC_OWN] ||
|
||||
(s_fetch_word_q[0][1:0] != 2'b00) ||
|
||||
(s_fetch_word_q[1] == 32'd0) ||
|
||||
(s_fetch_word_q[3][31:18] != 14'd0) ||
|
||||
(s_fetch_word_q[3][sdio_pkg::SDIO_DESC_END] &&
|
||||
((s_fetch_word_q[2] != 32'd0) ||
|
||||
s_fetch_word_q[3][sdio_pkg::SDIO_DESC_CHAIN])) ||
|
||||
(!s_fetch_word_q[3][sdio_pkg::SDIO_DESC_END] &&
|
||||
(!s_fetch_word_q[3][sdio_pkg::SDIO_DESC_CHAIN] ||
|
||||
(s_fetch_word_q[2][3:0] != 4'b0000) ||
|
||||
(s_fetch_word_q[2][11:0] >= 12'hFF0) ||
|
||||
(s_fetch_word_q[2] == 32'd0) ||
|
||||
(s_desc_index_q + 1'b1 >= s_desc_limit_q))) ||
|
||||
((s_total_bytes_q != 32'd0) &&
|
||||
((s_total_seen_q + s_fetch_word_q[1] > s_total_bytes_q) ||
|
||||
(s_fetch_word_q[3][sdio_pkg::SDIO_DESC_END] &&
|
||||
(s_total_seen_q + s_fetch_word_q[1] != s_total_bytes_q)))) ||
|
||||
s_fault_q) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
if (s_err_code_q == 8'd0) s_err_code_d = 8'h02;
|
||||
end
|
||||
s_writeback_control_d = (s_fetch_word_q[3] &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
|
||||
if (!s_fetch_word_q[3][sdio_pkg::SDIO_DESC_OWN]) begin
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
end else if (s_fault_d || s_abort_pending_q) begin
|
||||
s_state_d = WritebackStart;
|
||||
end else if (s_direction_q == sdio_pkg::SdioDataFromCard) begin
|
||||
// Card-to-host consumes the stream and AXI-writes memory.
|
||||
s_state_d = PayloadWriteStart;
|
||||
end else begin
|
||||
// Host-to-card AXI-reads memory and produces the stream.
|
||||
s_state_d = PayloadReadStart;
|
||||
end
|
||||
end
|
||||
PayloadReadStart: begin
|
||||
if (abort_i) begin
|
||||
s_abort_pending_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_payload_addr;
|
||||
s_state_d = WritebackStart;
|
||||
end else if (s_read_start_ready) begin
|
||||
s_burst_beats_d = s_payload_beats;
|
||||
s_burst_index_d = '0;
|
||||
s_abort_pending_d = 1'b0;
|
||||
s_state_d = PayloadReadWait;
|
||||
end
|
||||
end
|
||||
PayloadReadWait: begin
|
||||
if (abort_i) begin
|
||||
s_abort_pending_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_payload_addr;
|
||||
end
|
||||
if (s_read_beat_valid && s_read_beat_ready) begin
|
||||
if (s_axi_read_fault) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h04;
|
||||
s_err_addr_d = s_payload_addr + s_burst_offset;
|
||||
end
|
||||
if (!s_abort_pending_q && !abort_i && !s_axi_read_fault) begin
|
||||
s_desc_progress_d = s_progress_after_beat;
|
||||
s_total_done_d = s_total_done_q + {29'd0, s_beat_bytes};
|
||||
end
|
||||
if (s_read_done) begin
|
||||
s_abort_pending_d = 1'b0;
|
||||
if (s_fault_d || abort_i || s_abort_pending_q) begin
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_ERROR);
|
||||
s_state_d = WritebackStart;
|
||||
end else if (s_progress_after_beat >= s_desc_count_q) begin
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_DONE);
|
||||
s_state_d = WritebackStart;
|
||||
end else begin
|
||||
s_state_d = PayloadReadStart;
|
||||
end
|
||||
end else begin
|
||||
s_burst_index_d = s_burst_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
PayloadWriteStart: begin
|
||||
if (abort_i) begin
|
||||
s_abort_pending_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_payload_addr;
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_ERROR);
|
||||
s_state_d = WritebackStart;
|
||||
end else if (s_write_start_ready) begin
|
||||
s_burst_beats_d = s_payload_beats;
|
||||
s_burst_index_d = '0;
|
||||
s_write_beat_count_d = '0;
|
||||
s_write_final_sent_d = 1'b0;
|
||||
s_state_d = PayloadWriteWait;
|
||||
end
|
||||
end
|
||||
PayloadWriteWait: begin
|
||||
if (abort_i) begin
|
||||
s_abort_pending_d = 1'b1;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h08;
|
||||
s_err_addr_d = s_payload_addr + s_burst_offset;
|
||||
end
|
||||
if (s_write_data_valid && s_write_data_ready) begin
|
||||
if (!s_abort_pending_q && !abort_i) begin
|
||||
if (!strobe_valid(
|
||||
data_in_strb_i
|
||||
) || (s_input_bytes != s_beat_bytes) ||
|
||||
(data_in_last_i !=
|
||||
(s_desc_end_q && (s_progress_after_beat >= s_desc_count_q)))) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h10;
|
||||
end
|
||||
s_desc_progress_d = s_progress_after_beat;
|
||||
s_total_done_d = s_total_done_q + {29'd0, s_beat_bytes};
|
||||
end
|
||||
s_write_beat_count_d = s_write_beat_count_q + 1'b1;
|
||||
s_burst_index_d = s_burst_index_q + 1'b1;
|
||||
if (s_write_beat_count_q + 1'b1 >= s_burst_beats_q) begin
|
||||
s_write_final_sent_d = 1'b1;
|
||||
end
|
||||
end
|
||||
if (s_write_done) begin
|
||||
if (s_axi_write_fault) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h04;
|
||||
s_err_addr_d = s_payload_addr + s_burst_offset;
|
||||
end
|
||||
if (s_write_final_sent_q) begin
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
|
||||
s_state_d = WritebackStart;
|
||||
end
|
||||
end
|
||||
end
|
||||
WritebackStart: begin
|
||||
if (s_write_start_ready) begin
|
||||
s_state_d = WritebackWait;
|
||||
end
|
||||
end
|
||||
WritebackWait: begin
|
||||
if (s_write_done) begin
|
||||
if (s_axi_write_fault) begin
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h04;
|
||||
s_err_addr_d = s_desc_addr_q + 32'd12;
|
||||
end
|
||||
if (s_fault_q || s_fault_d || s_axi_write_fault ||
|
||||
s_writeback_control_q[sdio_pkg::SDIO_DESC_ERROR]) begin
|
||||
s_abort_done_d = s_abort_pending_q ||
|
||||
(s_writeback_control_q[sdio_pkg::SDIO_DESC_ERROR] &&
|
||||
(s_err_code_q == 8'h08));
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
end else if (s_desc_end_q) begin
|
||||
s_descriptor_irq_d = s_desc_irq_q;
|
||||
s_state_d = Terminal;
|
||||
s_done_d = 1'b1;
|
||||
end else begin
|
||||
s_descriptor_irq_d = s_desc_irq_q;
|
||||
s_desc_index_d = s_desc_index_q + 1'b1;
|
||||
s_desc_addr_d = s_desc_next_q;
|
||||
s_state_d = FetchStart;
|
||||
end
|
||||
end
|
||||
end
|
||||
Terminal: begin
|
||||
s_state_d = Idle;
|
||||
end
|
||||
default: begin
|
||||
s_state_d = Idle;
|
||||
s_fault_d = 1'b1;
|
||||
s_err_sticky_d = 1'b1;
|
||||
s_err_code_d = 8'h80;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_desc_addr_q <= '0;
|
||||
s_desc_limit_q <= '0;
|
||||
s_desc_index_q <= '0;
|
||||
s_direction_q <= sdio_pkg::SdioDataFromCard;
|
||||
s_total_bytes_q <= '0;
|
||||
s_total_seen_q <= '0;
|
||||
s_total_done_q <= '0;
|
||||
s_desc_progress_q <= '0;
|
||||
s_burst_beats_q <= '0;
|
||||
s_burst_index_q <= '0;
|
||||
s_desc_buffer_q <= '0;
|
||||
s_desc_count_q <= '0;
|
||||
s_desc_next_q <= '0;
|
||||
s_desc_control_q <= '0;
|
||||
s_writeback_control_q <= '0;
|
||||
s_desc_end_q <= 1'b0;
|
||||
s_desc_irq_q <= 1'b0;
|
||||
s_fault_q <= 1'b0;
|
||||
s_err_code_q <= '0;
|
||||
s_err_addr_q <= '0;
|
||||
s_done_q <= 1'b0;
|
||||
s_err_sticky_q <= 1'b0;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
s_write_final_sent_q <= 1'b0;
|
||||
s_write_beat_count_q <= '0;
|
||||
s_descriptor_irq_q <= 1'b0;
|
||||
s_abort_done_q <= 1'b0;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
s_fetch_word_q[index] <= '0;
|
||||
end
|
||||
end else begin
|
||||
s_state_q <= s_state_d;
|
||||
s_desc_addr_q <= s_desc_addr_d;
|
||||
s_desc_limit_q <= s_desc_limit_d;
|
||||
s_desc_index_q <= s_desc_index_d;
|
||||
s_direction_q <= s_direction_d;
|
||||
s_total_bytes_q <= s_total_bytes_d;
|
||||
s_total_seen_q <= s_total_seen_d;
|
||||
s_total_done_q <= s_total_done_d;
|
||||
s_desc_progress_q <= s_desc_progress_d;
|
||||
s_burst_beats_q <= s_burst_beats_d;
|
||||
s_burst_index_q <= s_burst_index_d;
|
||||
s_desc_buffer_q <= s_desc_buffer_d;
|
||||
s_desc_count_q <= s_desc_count_d;
|
||||
s_desc_next_q <= s_desc_next_d;
|
||||
s_desc_control_q <= s_desc_control_d;
|
||||
s_writeback_control_q <= s_writeback_control_d;
|
||||
s_desc_end_q <= s_desc_end_d;
|
||||
s_desc_irq_q <= s_desc_irq_d;
|
||||
s_fault_q <= s_fault_d;
|
||||
s_err_code_q <= s_err_code_d;
|
||||
s_err_addr_q <= s_err_addr_d;
|
||||
s_done_q <= s_done_d;
|
||||
s_err_sticky_q <= s_err_sticky_d;
|
||||
s_abort_pending_q <= s_abort_pending_d;
|
||||
s_write_final_sent_q <= s_write_final_sent_d;
|
||||
s_write_beat_count_q <= s_write_beat_count_d;
|
||||
s_descriptor_irq_q <= s_descriptor_irq_d;
|
||||
s_abort_done_q <= s_abort_done_d;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
s_fetch_word_q[index] <= s_fetch_word_d[index];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if ((AddrWidth < 4) || (DataWidth != 32) || (DescCount < 1) || (DescCount > 65535)) begin
|
||||
$fatal(1, "sdio_dma: unsupported geometry");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_dma_descriptor #(
|
||||
parameter int DescCount = 16
|
||||
) (
|
||||
input logic [31:0] buffer_addr_i,
|
||||
input logic [31:0] byte_count_i,
|
||||
input logic [31:0] next_addr_i,
|
||||
input logic [31:0] control_status_i,
|
||||
input logic [15:0] desc_index_i,
|
||||
output logic own_o,
|
||||
output logic chain_o,
|
||||
output logic end_o,
|
||||
output logic irq_o,
|
||||
output logic valid_o,
|
||||
output logic error_o,
|
||||
output logic address_error_o,
|
||||
output logic length_error_o,
|
||||
output logic chain_error_o
|
||||
);
|
||||
logic s_last_allowed;
|
||||
logic s_control_valid;
|
||||
|
||||
assign own_o = control_status_i[sdio_pkg::SDIO_DESC_OWN];
|
||||
assign chain_o = control_status_i[sdio_pkg::SDIO_DESC_CHAIN];
|
||||
assign end_o = control_status_i[sdio_pkg::SDIO_DESC_END];
|
||||
assign irq_o = control_status_i[sdio_pkg::SDIO_DESC_IRQ];
|
||||
assign s_last_allowed = desc_index_i + 1'b1 < DescCount;
|
||||
assign s_control_valid = own_o && !control_status_i[31:18];
|
||||
assign address_error_o = (buffer_addr_i[1:0] != 2'b00) ||
|
||||
((!end_o || chain_o) && ((next_addr_i[3:0] != 4'b0000) ||
|
||||
(next_addr_i[11:0] >= 12'hFF0)));
|
||||
assign length_error_o = (byte_count_i == 32'd0);
|
||||
assign chain_error_o = end_o ? (next_addr_i != 32'd0) :
|
||||
(!chain_o || (next_addr_i == 32'd0) || !s_last_allowed);
|
||||
assign error_o = !s_control_valid || address_error_o || length_error_o || chain_error_o;
|
||||
assign valid_o = !error_o;
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if (DescCount < 1) begin
|
||||
$fatal(1, "sdio_dma_descriptor: DescCount must be positive");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
interface sdio_if ();
|
||||
logic sck_o;
|
||||
logic cmd_oe_o;
|
||||
logic cmd_di_i;
|
||||
logic cmd_do_o;
|
||||
logic [3:0] dat_oe_o;
|
||||
logic [3:0] dat_di_i;
|
||||
logic [3:0] dat_do_o;
|
||||
logic irq_o;
|
||||
|
||||
modport dut(
|
||||
output sck_o,
|
||||
output cmd_oe_o,
|
||||
input cmd_di_i,
|
||||
output cmd_do_o,
|
||||
output dat_oe_o,
|
||||
input dat_di_i,
|
||||
output dat_do_o,
|
||||
output irq_o
|
||||
);
|
||||
|
||||
modport core(
|
||||
output sck_o,
|
||||
output cmd_oe_o,
|
||||
input cmd_di_i,
|
||||
output cmd_do_o,
|
||||
output dat_oe_o,
|
||||
input dat_di_i,
|
||||
output dat_do_o
|
||||
);
|
||||
endinterface
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
package sdio_pkg;
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
SdioRespNone = 4'd0,
|
||||
SdioRespR1 = 4'd1,
|
||||
SdioRespR1b = 4'd2,
|
||||
SdioRespR2 = 4'd3,
|
||||
SdioRespR3 = 4'd4,
|
||||
SdioRespR4 = 4'd5,
|
||||
SdioRespR5 = 4'd6,
|
||||
SdioRespR6 = 4'd7,
|
||||
SdioRespR7 = 4'd8
|
||||
} sdio_resp_type_e;
|
||||
|
||||
typedef enum logic {
|
||||
SdioDataFromCard = 1'b0,
|
||||
SdioDataToCard = 1'b1
|
||||
} sdio_data_direction_e;
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
SdioClockStopped = 3'd0,
|
||||
SdioClock400k = 3'd1,
|
||||
SdioClock25m = 3'd2,
|
||||
SdioClock50m = 3'd3
|
||||
} sdio_clock_profile_e;
|
||||
|
||||
localparam logic [4:0] SDIO_DESC_OWN = 5'd0;
|
||||
localparam logic [4:0] SDIO_DESC_CHAIN = 5'd1;
|
||||
localparam logic [4:0] SDIO_DESC_END = 5'd2;
|
||||
localparam logic [4:0] SDIO_DESC_IRQ = 5'd3;
|
||||
localparam logic [4:0] SDIO_DESC_DONE = 5'd16;
|
||||
localparam logic [4:0] SDIO_DESC_ERROR = 5'd17;
|
||||
|
||||
function automatic logic [6:0] sdio_crc7_next(input logic [6:0] crc_i, input logic data_i);
|
||||
logic feedback;
|
||||
begin
|
||||
feedback = data_i ^ crc_i[6];
|
||||
sdio_crc7_next = {crc_i[5:0], 1'b0};
|
||||
if (feedback) begin
|
||||
sdio_crc7_next = sdio_crc7_next ^ 7'h09;
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [6:0] sdio_crc7_calc(input logic [39:0] data_i);
|
||||
logic [6:0] crc;
|
||||
begin
|
||||
crc = 7'd0;
|
||||
for (int index = 39; index >= 0; index--) begin
|
||||
crc = sdio_crc7_next(crc, data_i[index]);
|
||||
end
|
||||
return crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [6:0] sdio_crc7_vector(input logic [135:0] data_i,
|
||||
input int unsigned bit_count_i);
|
||||
logic [6:0] crc;
|
||||
begin
|
||||
crc = 7'd0;
|
||||
if ((bit_count_i >= 1) && (bit_count_i <= 136)) begin
|
||||
for (int index = bit_count_i - 1; index >= 0; index--) begin
|
||||
crc = sdio_crc7_next(crc, data_i[index]);
|
||||
end
|
||||
end
|
||||
return crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [6:0] sdio_crc7_response(input logic [135:0] data_i, input logic r2_i);
|
||||
logic [6:0] crc;
|
||||
begin
|
||||
crc = 7'd0;
|
||||
if (r2_i) begin
|
||||
for (int index = 135; index >= 8; index--) begin
|
||||
crc = sdio_crc7_next(crc, data_i[index]);
|
||||
end
|
||||
end else begin
|
||||
for (int index = 47; index >= 8; index--) begin
|
||||
crc = sdio_crc7_next(crc, data_i[index]);
|
||||
end
|
||||
end
|
||||
return crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [15:0] sdio_crc16_next(input logic [15:0] crc_i, input logic data_i);
|
||||
logic feedback;
|
||||
begin
|
||||
feedback = data_i ^ crc_i[15];
|
||||
sdio_crc16_next = {crc_i[14:0], 1'b0};
|
||||
if (feedback) begin
|
||||
sdio_crc16_next = sdio_crc16_next ^ 16'h1021;
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [15:0] sdio_crc16_byte(input logic [15:0] crc_i,
|
||||
input logic [7:0] data_i);
|
||||
logic [15:0] crc;
|
||||
begin
|
||||
crc = crc_i;
|
||||
for (int index = 7; index >= 0; index--) begin
|
||||
crc = sdio_crc16_next(crc, data_i[index]);
|
||||
end
|
||||
return crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic sdio_response_has_crc(input sdio_resp_type_e response_type_i);
|
||||
return (response_type_i == SdioRespR1) || (response_type_i == SdioRespR1b) ||
|
||||
(response_type_i == SdioRespR2) || (response_type_i == SdioRespR5) ||
|
||||
(response_type_i == SdioRespR6) || (response_type_i == SdioRespR7);
|
||||
endfunction
|
||||
|
||||
function automatic logic sdio_response_has_index(input sdio_resp_type_e response_type_i);
|
||||
return (response_type_i != SdioRespNone) && (response_type_i != SdioRespR2) &&
|
||||
(response_type_i != SdioRespR3) && (response_type_i != SdioRespR4);
|
||||
endfunction
|
||||
|
||||
endpackage
|
||||
@@ -0,0 +1,364 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "sdio_define.svh"
|
||||
|
||||
module sdio_reg (
|
||||
// verilog_format: off -- preserve the APB and protocol port column groups
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic busy_i,
|
||||
input logic [31:0] status_i,
|
||||
input logic [31:0] clock_actual_i,
|
||||
input logic [31:0] cmd_status_i,
|
||||
input logic [135:0] response_i,
|
||||
input logic [31:0] data_status_i,
|
||||
input logic [31:0] fifo_status_i,
|
||||
input logic [31:0] dma_status_i,
|
||||
input logic [31:0] current_desc_i,
|
||||
input logic [31:0] bytes_done_i,
|
||||
input logic [31:0] dma_error_addr_i,
|
||||
input logic [31:0] dma_error_i,
|
||||
input logic [31:0] error_status_i,
|
||||
input logic [5:0] last_cmd_i,
|
||||
input logic [31:0] crc_error_count_i,
|
||||
input logic [31:0] timeout_count_i,
|
||||
input logic [31:0] axi_error_count_i,
|
||||
input logic [31:0] stall_count_i,
|
||||
input logic [7:0] irq_event_i,
|
||||
input logic [31:0] pio_rdata_i,
|
||||
input logic pio_valid_i,
|
||||
input logic pio_ready_i,
|
||||
output logic host_enable_o,
|
||||
output logic host_irq_enable_o,
|
||||
output logic clock_enable_o,
|
||||
output logic [15:0] half_period_o,
|
||||
output logic [1:0] bus_width_o,
|
||||
output logic [31:0] timeout_cmd_o,
|
||||
output logic [31:0] timeout_data_o,
|
||||
output logic [31:0] timeout_busy_o,
|
||||
output logic [5:0] cmd_index_o,
|
||||
output logic [31:0] cmd_arg_o,
|
||||
output sdio_pkg::sdio_resp_type_e cmd_resp_type_o,
|
||||
output logic cmd_crc_check_o,
|
||||
output logic cmd_index_check_o,
|
||||
output logic cmd_start_o,
|
||||
output logic [15:0] block_size_o,
|
||||
output logic [15:0] block_count_o,
|
||||
output logic data_direction_o,
|
||||
output logic data_dma_enable_o,
|
||||
output logic data_block_mode_o,
|
||||
output logic data_fixed_addr_o,
|
||||
output logic data_start_o,
|
||||
output logic pio_wvalid_o,
|
||||
output logic [31:0] pio_wdata_o,
|
||||
output logic [3:0] pio_wstrb_o,
|
||||
output logic pio_read_consume_o,
|
||||
output logic [31:0] desc_base_o,
|
||||
output logic [15:0] desc_count_o,
|
||||
output logic dma_start_o,
|
||||
output logic dma_abort_o,
|
||||
output logic host_abort_o,
|
||||
output logic irq_dat1_enable_o,
|
||||
output logic irq_o,
|
||||
apb4_if.slave apb4
|
||||
// verilog_format: on
|
||||
);
|
||||
import sdio_pkg::*;
|
||||
|
||||
logic s_apb4_ready_d, s_apb4_ready_q;
|
||||
logic [31:0] s_apb4_rdata_d, s_apb4_rdata_q;
|
||||
logic s_apb4_resp_err_d, s_apb4_resp_err_q;
|
||||
logic s_req_accept;
|
||||
logic s_write_access;
|
||||
logic s_write_known;
|
||||
logic s_write_error;
|
||||
logic s_pio_write_wait;
|
||||
logic s_pio_write_direction_error;
|
||||
logic [31:0] s_read_data;
|
||||
logic s_read_error;
|
||||
logic [ 2:0] s_host_ctrl_q;
|
||||
logic s_clock_en_q;
|
||||
logic [15:0] s_half_period_q;
|
||||
logic [ 1:0] s_bus_width_q;
|
||||
logic [31:0] s_timeout_cmd_q;
|
||||
logic [31:0] s_timeout_data_q;
|
||||
logic [31:0] s_timeout_busy_q;
|
||||
logic [ 5:0] s_cmd_index_q;
|
||||
logic [31:0] s_cmd_arg_q;
|
||||
logic [ 5:0] s_cmd_cfg_q;
|
||||
logic [15:0] s_block_size_q;
|
||||
logic [15:0] s_block_count_q;
|
||||
logic [ 6:0] s_data_cfg_q;
|
||||
logic [31:0] s_desc_base_q;
|
||||
logic [15:0] s_desc_count_q;
|
||||
logic [ 7:0] s_irq_stat_q;
|
||||
logic [ 7:0] s_irq_en_q;
|
||||
logic [31:0] s_err_stat_q;
|
||||
|
||||
assign s_pio_write_direction_error = (s_data_cfg_q[0] == 1'b0);
|
||||
assign s_pio_write_wait = apb4.psel && apb4.penable && apb4.pwrite &&
|
||||
(apb4.paddr[11:0] == `APB4_SDIO__PIO_DATA) && !pio_ready_i &&
|
||||
!s_pio_write_direction_error;
|
||||
assign s_req_accept = apb4.psel && apb4.penable && !s_apb4_ready_q && !s_pio_write_wait;
|
||||
assign s_write_access = s_req_accept && apb4.pwrite;
|
||||
assign apb4.pready = s_apb4_ready_q;
|
||||
assign apb4.prdata = s_apb4_rdata_q;
|
||||
assign apb4.pslverr = s_apb4_resp_err_q;
|
||||
|
||||
assign host_enable_o = s_host_ctrl_q[0];
|
||||
assign host_irq_enable_o = s_host_ctrl_q[2];
|
||||
assign clock_enable_o = s_clock_en_q;
|
||||
assign half_period_o = s_half_period_q;
|
||||
assign bus_width_o = s_bus_width_q;
|
||||
assign timeout_cmd_o = s_timeout_cmd_q;
|
||||
assign timeout_data_o = s_timeout_data_q;
|
||||
assign timeout_busy_o = s_timeout_busy_q;
|
||||
assign cmd_index_o = s_cmd_index_q;
|
||||
assign cmd_arg_o = s_cmd_arg_q;
|
||||
assign cmd_resp_type_o = sdio_resp_type_e'(s_cmd_cfg_q[3:0]);
|
||||
assign cmd_crc_check_o = s_cmd_cfg_q[4];
|
||||
assign cmd_index_check_o = s_cmd_cfg_q[5];
|
||||
assign block_size_o = s_block_size_q;
|
||||
assign block_count_o = s_block_count_q;
|
||||
assign data_direction_o = s_data_cfg_q[0];
|
||||
assign data_dma_enable_o = s_data_cfg_q[4];
|
||||
assign data_block_mode_o = s_data_cfg_q[5];
|
||||
assign data_fixed_addr_o = s_data_cfg_q[6];
|
||||
assign desc_base_o = s_desc_base_q;
|
||||
assign desc_count_o = s_desc_count_q;
|
||||
assign irq_dat1_enable_o = s_irq_en_q[6];
|
||||
assign irq_o = (s_irq_stat_q & s_irq_en_q) != 8'd0;
|
||||
assign pio_read_consume_o = s_req_accept && !apb4.pwrite &&
|
||||
(apb4.paddr[11:0] == `APB4_SDIO__PIO_DATA) && pio_valid_i;
|
||||
|
||||
always_comb begin
|
||||
s_read_data = '0;
|
||||
s_read_error = 1'b0;
|
||||
unique case (apb4.paddr[11:0])
|
||||
`APB4_SDIO__IP_ID: s_read_data = 32'h5344_494F;
|
||||
`APB4_SDIO__IP_VERSION: s_read_data = 32'h0001_0000;
|
||||
`APB4_SDIO__CAPABILITY: s_read_data = 32'h0000_01FF;
|
||||
`APB4_SDIO__HOST_CTRL: s_read_data = {29'd0, s_host_ctrl_q};
|
||||
`APB4_SDIO__CLOCK_CTRL: s_read_data = {8'd0, s_half_period_q, 7'd0, s_clock_en_q};
|
||||
`APB4_SDIO__CLOCK_ACTUAL: s_read_data = clock_actual_i;
|
||||
`APB4_SDIO__BUS_CTRL: s_read_data = {30'd0, s_bus_width_q};
|
||||
`APB4_SDIO__TIMEOUT_CMD: s_read_data = s_timeout_cmd_q;
|
||||
`APB4_SDIO__TIMEOUT_DATA: s_read_data = s_timeout_data_q;
|
||||
`APB4_SDIO__TIMEOUT_BUSY: s_read_data = s_timeout_busy_q;
|
||||
`APB4_SDIO__STATUS: s_read_data = status_i;
|
||||
`APB4_SDIO__PRESENT: s_read_data = 32'h0000_0001;
|
||||
`APB4_SDIO__CMD_ARG: s_read_data = s_cmd_arg_q;
|
||||
`APB4_SDIO__CMD_CFG: s_read_data = {26'd0, s_cmd_cfg_q};
|
||||
`APB4_SDIO__CMD_STATUS: s_read_data = cmd_status_i;
|
||||
`APB4_SDIO__RESP0: s_read_data = response_i[31:0];
|
||||
`APB4_SDIO__RESP1: s_read_data = response_i[63:32];
|
||||
`APB4_SDIO__RESP2: s_read_data = response_i[95:64];
|
||||
`APB4_SDIO__RESP3: s_read_data = response_i[127:96];
|
||||
`APB4_SDIO__RESP4: s_read_data = {24'd0, response_i[135:128]};
|
||||
`APB4_SDIO__BLOCK_SIZE: s_read_data = {16'd0, s_block_size_q};
|
||||
`APB4_SDIO__BLOCK_COUNT: s_read_data = {16'd0, s_block_count_q};
|
||||
`APB4_SDIO__DATA_CFG: s_read_data = {25'd0, s_data_cfg_q};
|
||||
`APB4_SDIO__DATA_START: s_read_data = data_status_i;
|
||||
`APB4_SDIO__PIO_DATA: begin
|
||||
s_read_data = pio_rdata_i;
|
||||
s_read_error = !pio_valid_i;
|
||||
end
|
||||
`APB4_SDIO__FIFO_STATUS: s_read_data = fifo_status_i;
|
||||
`APB4_SDIO__FIFO_WATERMARK: s_read_data = 32'd1;
|
||||
`APB4_SDIO__DESC_BASE: s_read_data = s_desc_base_q;
|
||||
`APB4_SDIO__DESC_COUNT: s_read_data = {16'd0, s_desc_count_q};
|
||||
`APB4_SDIO__DMA_CTRL: s_read_data = 32'd0;
|
||||
`APB4_SDIO__DMA_STATUS: s_read_data = dma_status_i;
|
||||
`APB4_SDIO__CURRENT_DESC: s_read_data = current_desc_i;
|
||||
`APB4_SDIO__BYTES_DONE: s_read_data = bytes_done_i;
|
||||
`APB4_SDIO__DMA_ERROR_ADDR: s_read_data = dma_error_addr_i;
|
||||
`APB4_SDIO__DMA_ERROR: s_read_data = dma_error_i;
|
||||
`APB4_SDIO__IRQ_STATUS: s_read_data = {24'd0, s_irq_stat_q};
|
||||
`APB4_SDIO__IRQ_ENABLE: s_read_data = {24'd0, s_irq_en_q};
|
||||
`APB4_SDIO__ERROR_STATUS: s_read_data = s_err_stat_q;
|
||||
`APB4_SDIO__LAST_CMD: s_read_data = {26'd0, last_cmd_i};
|
||||
`APB4_SDIO__CRC_ERROR_COUNT: s_read_data = crc_error_count_i;
|
||||
`APB4_SDIO__TIMEOUT_COUNT: s_read_data = timeout_count_i;
|
||||
`APB4_SDIO__AXI_ERROR_COUNT: s_read_data = axi_error_count_i;
|
||||
`APB4_SDIO__STALL_COUNT: s_read_data = stall_count_i;
|
||||
`APB4_SDIO__DEBUG: s_read_data = {30'd0, pio_ready_i, pio_valid_i};
|
||||
default: s_read_error = 1'b1;
|
||||
endcase
|
||||
if (apb4.paddr[31:12] != 20'd0) begin
|
||||
s_read_error = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_write_known = 1'b1;
|
||||
s_write_error = 1'b0;
|
||||
unique case (apb4.paddr[11:0])
|
||||
`APB4_SDIO__HOST_CTRL,
|
||||
`APB4_SDIO__CLOCK_CTRL,
|
||||
`APB4_SDIO__BUS_CTRL,
|
||||
`APB4_SDIO__TIMEOUT_CMD,
|
||||
`APB4_SDIO__TIMEOUT_DATA,
|
||||
`APB4_SDIO__TIMEOUT_BUSY,
|
||||
`APB4_SDIO__CMD_ARG,
|
||||
`APB4_SDIO__CMD_CFG,
|
||||
`APB4_SDIO__BLOCK_SIZE,
|
||||
`APB4_SDIO__BLOCK_COUNT,
|
||||
`APB4_SDIO__DATA_CFG,
|
||||
`APB4_SDIO__DESC_BASE,
|
||||
`APB4_SDIO__DESC_COUNT,
|
||||
`APB4_SDIO__IRQ_ENABLE: begin
|
||||
if (busy_i && (apb4.paddr[11:0] != `APB4_SDIO__HOST_CTRL)) begin
|
||||
s_write_error = 1'b1;
|
||||
end
|
||||
end
|
||||
`APB4_SDIO__CMD_START: begin
|
||||
s_write_error = (apb4.pstrb[0] == 1'b0) || (apb4.pwdata[31:1] != 31'd0) || busy_i;
|
||||
end
|
||||
`APB4_SDIO__DATA_START: begin
|
||||
s_write_error = (apb4.pstrb[0] == 1'b0) || (apb4.pwdata[31:1] != 31'd0) || busy_i;
|
||||
end
|
||||
`APB4_SDIO__PIO_DATA: begin
|
||||
s_write_error = (busy_i == 1'b0) || s_pio_write_direction_error;
|
||||
end
|
||||
`APB4_SDIO__DMA_CTRL: begin
|
||||
s_write_error = (apb4.pwdata[31:2] != 30'd0) ||
|
||||
(apb4.pwdata[0] && !s_data_cfg_q[4]) ||
|
||||
(busy_i && apb4.pwdata[0]);
|
||||
end
|
||||
`APB4_SDIO__IRQ_STATUS, `APB4_SDIO__IRQ_TEST, `APB4_SDIO__ERROR_STATUS: begin
|
||||
s_write_error = apb4.pstrb[0] == 1'b0;
|
||||
end
|
||||
default: begin
|
||||
s_write_known = 1'b0;
|
||||
s_write_error = 1'b1;
|
||||
end
|
||||
endcase
|
||||
if (apb4.paddr[31:12] != 20'd0) begin
|
||||
s_write_error = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_apb4_ready_d = s_req_accept;
|
||||
s_apb4_rdata_d = s_apb4_rdata_q;
|
||||
s_apb4_resp_err_d = s_apb4_resp_err_q;
|
||||
if (s_req_accept) begin
|
||||
s_apb4_rdata_d = s_read_data;
|
||||
s_apb4_resp_err_d = s_read_error;
|
||||
if (s_write_access) begin
|
||||
s_apb4_resp_err_d = !s_write_known || s_write_error;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_apb4_ready_q <= 1'b0;
|
||||
s_apb4_rdata_q <= '0;
|
||||
s_apb4_resp_err_q <= 1'b0;
|
||||
s_host_ctrl_q <= '0;
|
||||
s_clock_en_q <= 1'b0;
|
||||
s_half_period_q <= 16'd90;
|
||||
s_bus_width_q <= 2'd0;
|
||||
s_timeout_cmd_q <= 32'd100000;
|
||||
s_timeout_data_q <= 32'd1000000;
|
||||
s_timeout_busy_q <= 32'd1000000;
|
||||
s_cmd_index_q <= '0;
|
||||
s_cmd_arg_q <= '0;
|
||||
s_cmd_cfg_q <= 6'd0;
|
||||
s_block_size_q <= 16'd512;
|
||||
s_block_count_q <= 16'd1;
|
||||
s_data_cfg_q <= '0;
|
||||
s_desc_base_q <= '0;
|
||||
s_desc_count_q <= 16'd1;
|
||||
s_irq_stat_q <= '0;
|
||||
s_irq_en_q <= '0;
|
||||
s_err_stat_q <= '0;
|
||||
cmd_start_o <= 1'b0;
|
||||
data_start_o <= 1'b0;
|
||||
pio_wvalid_o <= 1'b0;
|
||||
pio_wdata_o <= '0;
|
||||
pio_wstrb_o <= '0;
|
||||
dma_start_o <= 1'b0;
|
||||
dma_abort_o <= 1'b0;
|
||||
host_abort_o <= 1'b0;
|
||||
end else begin
|
||||
s_apb4_ready_q <= s_apb4_ready_d;
|
||||
s_apb4_rdata_q <= s_apb4_rdata_d;
|
||||
s_apb4_resp_err_q <= s_apb4_resp_err_d;
|
||||
cmd_start_o <= 1'b0;
|
||||
data_start_o <= 1'b0;
|
||||
pio_wvalid_o <= 1'b0;
|
||||
dma_start_o <= 1'b0;
|
||||
dma_abort_o <= 1'b0;
|
||||
host_abort_o <= 1'b0;
|
||||
|
||||
s_irq_stat_q <= (s_irq_stat_q & ~irq_event_i) | irq_event_i;
|
||||
s_err_stat_q <= s_err_stat_q | error_status_i;
|
||||
if (s_req_accept && apb4.pwrite && !s_write_error) begin
|
||||
unique case (apb4.paddr[11:0])
|
||||
`APB4_SDIO__HOST_CTRL: begin
|
||||
if (apb4.pstrb[0]) begin
|
||||
s_host_ctrl_q[0] <= apb4.pwdata[0];
|
||||
s_host_ctrl_q[2] <= apb4.pwdata[2];
|
||||
if (apb4.pwdata[1]) begin
|
||||
host_abort_o <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
`APB4_SDIO__CLOCK_CTRL: begin
|
||||
if (apb4.pstrb[0]) s_clock_en_q <= apb4.pwdata[0];
|
||||
if (apb4.pstrb[1]) s_half_period_q[7:0] <= apb4.pwdata[15:8];
|
||||
if (apb4.pstrb[2]) s_half_period_q[15:8] <= apb4.pwdata[23:16];
|
||||
end
|
||||
`APB4_SDIO__BUS_CTRL: if (apb4.pstrb[0]) s_bus_width_q <= apb4.pwdata[1:0];
|
||||
`APB4_SDIO__TIMEOUT_CMD: s_timeout_cmd_q <= apb4.pwdata;
|
||||
`APB4_SDIO__TIMEOUT_DATA: s_timeout_data_q <= apb4.pwdata;
|
||||
`APB4_SDIO__TIMEOUT_BUSY: s_timeout_busy_q <= apb4.pwdata;
|
||||
`APB4_SDIO__CMD_ARG: s_cmd_arg_q <= apb4.pwdata;
|
||||
`APB4_SDIO__CMD_CFG: begin
|
||||
if (apb4.pstrb[0]) s_cmd_index_q <= apb4.pwdata[5:0];
|
||||
if (apb4.pstrb[1]) s_cmd_cfg_q <= apb4.pwdata[13:8];
|
||||
end
|
||||
`APB4_SDIO__CMD_START: cmd_start_o <= 1'b1;
|
||||
`APB4_SDIO__BLOCK_SIZE: s_block_size_q <= apb4.pwdata[15:0];
|
||||
`APB4_SDIO__BLOCK_COUNT: s_block_count_q <= apb4.pwdata[15:0];
|
||||
`APB4_SDIO__DATA_CFG: if (apb4.pstrb[0]) s_data_cfg_q <= apb4.pwdata[6:0];
|
||||
`APB4_SDIO__DATA_START: data_start_o <= 1'b1;
|
||||
`APB4_SDIO__PIO_DATA: begin
|
||||
pio_wvalid_o <= 1'b1;
|
||||
pio_wdata_o <= apb4.pwdata;
|
||||
pio_wstrb_o <= apb4.pstrb;
|
||||
end
|
||||
`APB4_SDIO__DESC_BASE: s_desc_base_q <= apb4.pwdata;
|
||||
`APB4_SDIO__DESC_COUNT: s_desc_count_q <= apb4.pwdata[15:0];
|
||||
`APB4_SDIO__DMA_CTRL: begin
|
||||
dma_start_o <= apb4.pwdata[0];
|
||||
dma_abort_o <= apb4.pwdata[1];
|
||||
if (apb4.pwdata[0]) begin
|
||||
data_start_o <= 1'b1;
|
||||
end
|
||||
end
|
||||
`APB4_SDIO__IRQ_ENABLE: if (apb4.pstrb[0]) s_irq_en_q <= apb4.pwdata[7:0];
|
||||
`APB4_SDIO__IRQ_STATUS: s_irq_stat_q <= (s_irq_stat_q & ~apb4.pwdata[7:0]) | irq_event_i;
|
||||
`APB4_SDIO__IRQ_TEST:
|
||||
s_irq_stat_q <= ((s_irq_stat_q & ~irq_event_i) | irq_event_i) | apb4.pwdata[7:0];
|
||||
`APB4_SDIO__ERROR_STATUS: s_err_stat_q <= (s_err_stat_q & ~apb4.pwdata) | error_status_i;
|
||||
default: begin
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -125,6 +125,22 @@
|
||||
{
|
||||
"symbol": "RTC_WAKE_STATUS",
|
||||
"offset": "0x88"
|
||||
},
|
||||
{
|
||||
"symbol": "PERF_SDIO0_WAIT_LO",
|
||||
"offset": "0x8C"
|
||||
},
|
||||
{
|
||||
"symbol": "PERF_SDIO0_WAIT_HI",
|
||||
"offset": "0x90"
|
||||
},
|
||||
{
|
||||
"symbol": "PERF_SDIO1_WAIT_LO",
|
||||
"offset": "0x94"
|
||||
},
|
||||
{
|
||||
"symbol": "PERF_SDIO1_WAIT_HI",
|
||||
"offset": "0x98"
|
||||
}
|
||||
],
|
||||
"regions": [
|
||||
@@ -275,12 +291,12 @@
|
||||
"user_access": "rw"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_SDIO",
|
||||
"symbol": "APB4_SDIO0",
|
||||
"base": "0x1000F000",
|
||||
"size": "0x00001000",
|
||||
"route": "reserved",
|
||||
"kind": "reserved",
|
||||
"public": false,
|
||||
"route": "apb4_periph",
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
@@ -328,6 +344,15 @@
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_SDIO1",
|
||||
"base": "0x10015000",
|
||||
"size": "0x00001000",
|
||||
"route": "apb4_periph",
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_ARCHINFO",
|
||||
"base": "0x20000000",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
/* verilator lint_off SYNCASYNCNET */
|
||||
|
||||
module axi4_interconnect_sva #(
|
||||
parameter int NumMasters = 3,
|
||||
parameter int NumTargets = 9,
|
||||
parameter int NumMasters = 5,
|
||||
parameter int NumTargets = 10,
|
||||
parameter int MASTER_WIDTH = $clog2(NumMasters),
|
||||
parameter int TARGET_WIDTH = $clog2(NumTargets)
|
||||
) (
|
||||
|
||||
@@ -58,6 +58,9 @@ module retrosoc_tb;
|
||||
wire s_i2s_sclk;
|
||||
wire s_i2s_lrck;
|
||||
wire s_i2s_adcdat;
|
||||
wire s_sdio1_clk;
|
||||
tri1 s_sdio1_cmd;
|
||||
tri1 [ 3:0] s_sdio1_dat;
|
||||
wire s_sdram_clk;
|
||||
wire s_sdram_cke;
|
||||
wire s_sdram_cs_n;
|
||||
|
||||
@@ -30,7 +30,13 @@ module retrosoc_top (
|
||||
output wire jtag_tdo_o,
|
||||
output wire test_done_o,
|
||||
output wire test_pass_o,
|
||||
output wire [7:0] test_code_o
|
||||
output wire [7:0] test_code_o,
|
||||
output wire sdio1_clk_o,
|
||||
inout wire sdio1_cmd_io,
|
||||
inout wire sdio1_dat0_io,
|
||||
inout wire sdio1_dat1_io,
|
||||
inout wire sdio1_dat2_io,
|
||||
inout wire sdio1_dat3_io
|
||||
);
|
||||
|
||||
wire s_clk;
|
||||
|
||||
+13
-1
@@ -22,7 +22,15 @@
|
||||
/ip/storage/spisd_core.sv
|
||||
/ip/storage/spisd_data.sv
|
||||
/ip/storage/apb4_spisd.sv
|
||||
/ip/storage/apb4_sdio.sv
|
||||
/ip/storage/sdio_if.sv
|
||||
/ip/storage/sdio_pkg.sv
|
||||
/ip/storage/sdio_define.svh
|
||||
/ip/storage/sdio_crc7.sv
|
||||
/ip/storage/sdio_crc16.sv
|
||||
/ip/storage/sdio_clock.sv
|
||||
/ip/storage/sdio_command.sv
|
||||
/ip/storage/sdio_data.sv
|
||||
/ip/storage/sdio_dma_descriptor.sv
|
||||
/ip/serial/i2c_filter.sv
|
||||
/ip/serial/i2c_core.sv
|
||||
/ip/serial/i2c_reg.sv
|
||||
@@ -55,6 +63,10 @@
|
||||
/ip/peripheral/dma_define.svh
|
||||
/ip/peripheral/dma_req_if.sv
|
||||
/ip/peripheral/dma_axi4_master.sv
|
||||
/ip/storage/sdio_dma.sv
|
||||
/ip/storage/sdio_reg.sv
|
||||
/ip/storage/sdio_core.sv
|
||||
/ip/storage/apb4_sdio.sv
|
||||
/ip/peripheral/dma_reg.sv
|
||||
/ip/peripheral/dma_core.sv
|
||||
/ip/peripheral/apb4_dma.sv
|
||||
|
||||
@@ -21,6 +21,7 @@ INTERCONNECT = ROOT / "rtl/ip/interconnect"
|
||||
PERIPHERAL = ROOT / "rtl/ip/peripheral"
|
||||
SERIAL = ROOT / "rtl/ip/serial"
|
||||
MEMORY = ROOT / "rtl/ip/memory"
|
||||
STORAGE = ROOT / "rtl/ip/storage"
|
||||
TOP = ROOT / "rtl/mini/top"
|
||||
|
||||
|
||||
@@ -227,6 +228,19 @@ def source_files(target: str) -> list[Path]:
|
||||
PERIPHERAL / "dma_core.sv",
|
||||
SCRIPT_DIR / "dma_formal.sv",
|
||||
]
|
||||
if target == "sdio":
|
||||
return [
|
||||
COMMON_RTL / "interface/apb4_if.sv",
|
||||
COMMON_RTL / "interface/axi4_if.sv",
|
||||
PERIPHERAL / "dma_axi4_master.sv",
|
||||
STORAGE / "sdio_pkg.sv",
|
||||
STORAGE / "sdio_define.svh",
|
||||
STORAGE / "sdio_clock.sv",
|
||||
STORAGE / "sdio_dma_descriptor.sv",
|
||||
STORAGE / "sdio_dma.sv",
|
||||
STORAGE / "sdio_reg.sv",
|
||||
SCRIPT_DIR / "sdio_formal.sv",
|
||||
]
|
||||
raise ValueError(f"unsupported formal target: {target}")
|
||||
|
||||
|
||||
@@ -279,6 +293,7 @@ def parse_args() -> argparse.Namespace:
|
||||
"psram",
|
||||
"opipsram",
|
||||
"dma",
|
||||
"sdio",
|
||||
),
|
||||
required=True,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module sdio_formal_design (
|
||||
// verilog_format: off -- scalar observations mirror the APB and AXI contracts
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic apb_psel_i,
|
||||
input logic apb_penable_i,
|
||||
input logic apb_pwrite_i,
|
||||
input logic [31:0] apb_paddr_i,
|
||||
input logic [31:0] apb_pwdata_i,
|
||||
input logic [ 3:0] apb_pstrb_i,
|
||||
input logic [ 7:0] irq_event_i,
|
||||
input logic axi_awready_i,
|
||||
input logic axi_wready_i,
|
||||
input logic axi_bvalid_i,
|
||||
input logic axi_bid_i,
|
||||
input logic [ 1:0] axi_bresp_i,
|
||||
input logic axi_arready_i,
|
||||
input logic axi_rvalid_i,
|
||||
input logic axi_rid_i,
|
||||
input logic [31:0] axi_rdata_i,
|
||||
input logic [ 1:0] axi_rresp_i,
|
||||
input logic axi_rlast_i,
|
||||
input logic dma_data_out_ready_i,
|
||||
output logic apb_pready_o,
|
||||
output logic [31:0] apb_prdata_o,
|
||||
output logic apb_pslverr_o,
|
||||
output logic irq_o,
|
||||
output logic sck_o,
|
||||
output logic launch_tick_o,
|
||||
output logic sample_tick_o,
|
||||
output logic clock_running_o,
|
||||
output logic dma_busy_o,
|
||||
output logic dma_done_o,
|
||||
output logic dma_error_o,
|
||||
output logic [ 7:0] dma_error_code_o,
|
||||
output logic [31:0] dma_current_desc_o,
|
||||
output logic [31:0] dma_bytes_done_o,
|
||||
output logic [31:0] dma_error_addr_o,
|
||||
output logic dma_data_out_valid_o,
|
||||
output logic [31:0] dma_data_out_o,
|
||||
output logic [ 3:0] dma_data_out_strb_o,
|
||||
output logic dma_data_out_last_o,
|
||||
output logic axi_awvalid_o,
|
||||
output logic [31:0] axi_awaddr_o,
|
||||
output logic [ 7:0] axi_awlen_o,
|
||||
output logic [ 2:0] axi_awsize_o,
|
||||
output logic [ 1:0] axi_awburst_o,
|
||||
output logic axi_wvalid_o,
|
||||
output logic [31:0] axi_wdata_o,
|
||||
output logic [ 3:0] axi_wstrb_o,
|
||||
output logic axi_wlast_o,
|
||||
output logic axi_bready_o,
|
||||
output logic axi_arvalid_o,
|
||||
output logic [31:0] axi_araddr_o,
|
||||
output logic [ 7:0] axi_arlen_o,
|
||||
output logic [ 2:0] axi_arsize_o,
|
||||
output logic [ 1:0] axi_arburst_o,
|
||||
output logic axi_rready_o,
|
||||
output logic host_enable_o,
|
||||
output logic clock_enable_o,
|
||||
output logic [15:0] half_period_o,
|
||||
output logic [ 1:0] bus_width_o,
|
||||
output logic [15:0] desc_count_o,
|
||||
output logic dma_start_o,
|
||||
output logic dma_abort_o
|
||||
// verilog_format: on
|
||||
);
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
|
||||
logic s_dma_busy;
|
||||
logic s_dma_done;
|
||||
logic s_dma_error;
|
||||
logic [ 7:0] s_dma_error_code;
|
||||
logic [ 31:0] s_dma_current_desc;
|
||||
logic [ 31:0] s_dma_bytes_done;
|
||||
logic [ 31:0] s_dma_error_addr;
|
||||
logic s_data_out_valid;
|
||||
logic [ 31:0] s_data_out;
|
||||
logic [ 3:0] s_data_out_strb;
|
||||
logic s_data_out_last;
|
||||
logic s_data_direction;
|
||||
logic s_data_dma_enable;
|
||||
logic [ 15:0] s_block_size;
|
||||
logic [ 15:0] s_block_count;
|
||||
logic [ 31:0] s_desc_base;
|
||||
logic s_clock_enable;
|
||||
logic [ 15:0] s_half_period;
|
||||
logic s_host_enable;
|
||||
logic s_dma_start;
|
||||
logic s_dma_abort;
|
||||
logic s_host_irq_enable;
|
||||
logic s_irq_dat1_enable;
|
||||
logic [ 31:0] s_status;
|
||||
logic [ 31:0] s_clock_actual;
|
||||
logic [ 31:0] s_cmd_status;
|
||||
logic [135:0] s_response;
|
||||
logic [ 31:0] s_data_status;
|
||||
logic [ 31:0] s_fifo_status;
|
||||
logic [ 31:0] s_dma_status;
|
||||
logic [ 31:0] s_dma_error_addr_reg;
|
||||
logic [ 31:0] s_dma_error_reg;
|
||||
logic [ 31:0] s_error_status;
|
||||
logic [ 5:0] s_last_cmd;
|
||||
logic [ 31:0] s_crc_error_count;
|
||||
logic [ 31:0] s_timeout_count;
|
||||
logic [ 31:0] s_axi_error_count;
|
||||
logic [ 31:0] s_stall_count;
|
||||
logic [ 31:0] s_pio_rdata;
|
||||
logic s_pio_valid;
|
||||
logic s_pio_ready;
|
||||
logic s_data_start;
|
||||
logic s_cmd_start;
|
||||
logic [ 5:0] s_cmd_index;
|
||||
logic [ 31:0] s_cmd_arg;
|
||||
sdio_pkg::sdio_resp_type_e s_cmd_resp_type;
|
||||
logic s_cmd_crc_check;
|
||||
logic s_cmd_index_check;
|
||||
logic [ 15:0] s_desc_count_q;
|
||||
|
||||
assign apb4.psel = apb_psel_i;
|
||||
assign apb4.penable = apb_penable_i;
|
||||
assign apb4.pwrite = apb_pwrite_i;
|
||||
assign apb4.paddr = apb_paddr_i;
|
||||
assign apb4.pwdata = apb_pwdata_i;
|
||||
assign apb4.pstrb = apb_pstrb_i;
|
||||
assign apb_pready_o = apb4.pready;
|
||||
assign apb_prdata_o = apb4.prdata;
|
||||
assign apb_pslverr_o = apb4.pslverr;
|
||||
|
||||
assign dma_axi4.awready = axi_awready_i;
|
||||
assign dma_axi4.wready = axi_wready_i;
|
||||
assign dma_axi4.bvalid = axi_bvalid_i;
|
||||
assign dma_axi4.bid = axi_bid_i;
|
||||
assign dma_axi4.bresp = axi_bresp_i;
|
||||
assign dma_axi4.buser = '0;
|
||||
assign dma_axi4.arready = axi_arready_i;
|
||||
assign dma_axi4.rvalid = axi_rvalid_i;
|
||||
assign dma_axi4.rid = axi_rid_i;
|
||||
assign dma_axi4.rdata = axi_rdata_i;
|
||||
assign dma_axi4.rresp = axi_rresp_i;
|
||||
assign dma_axi4.rlast = axi_rlast_i;
|
||||
assign dma_axi4.ruser = '0;
|
||||
|
||||
assign axi_awvalid_o = dma_axi4.awvalid;
|
||||
assign axi_awaddr_o = dma_axi4.awaddr;
|
||||
assign axi_awlen_o = dma_axi4.awlen;
|
||||
assign axi_awsize_o = dma_axi4.awsize;
|
||||
assign axi_awburst_o = dma_axi4.awburst;
|
||||
assign axi_wvalid_o = dma_axi4.wvalid;
|
||||
assign axi_wdata_o = dma_axi4.wdata;
|
||||
assign axi_wstrb_o = dma_axi4.wstrb;
|
||||
assign axi_wlast_o = dma_axi4.wlast;
|
||||
assign axi_bready_o = dma_axi4.bready;
|
||||
assign axi_arvalid_o = dma_axi4.arvalid;
|
||||
assign axi_araddr_o = dma_axi4.araddr;
|
||||
assign axi_arlen_o = dma_axi4.arlen;
|
||||
assign axi_arsize_o = dma_axi4.arsize;
|
||||
assign axi_arburst_o = dma_axi4.arburst;
|
||||
assign axi_rready_o = dma_axi4.rready;
|
||||
|
||||
sdio_reg u_sdio_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.busy_i (s_dma_busy),
|
||||
.status_i (s_status),
|
||||
.clock_actual_i (s_clock_actual),
|
||||
.cmd_status_i (s_cmd_status),
|
||||
.response_i (s_response),
|
||||
.data_status_i (s_data_status),
|
||||
.fifo_status_i (s_fifo_status),
|
||||
.dma_status_i (s_dma_status),
|
||||
.current_desc_i (s_dma_current_desc),
|
||||
.bytes_done_i (s_dma_bytes_done),
|
||||
.dma_error_addr_i (s_dma_error_addr_reg),
|
||||
.dma_error_i (s_dma_error_reg),
|
||||
.error_status_i (s_error_status),
|
||||
.last_cmd_i (s_last_cmd),
|
||||
.crc_error_count_i(s_crc_error_count),
|
||||
.timeout_count_i (s_timeout_count),
|
||||
.axi_error_count_i(s_axi_error_count),
|
||||
.stall_count_i (s_stall_count),
|
||||
.irq_event_i (irq_event_i),
|
||||
.pio_rdata_i (s_pio_rdata),
|
||||
.pio_valid_i (s_pio_valid),
|
||||
.pio_ready_i (s_pio_ready),
|
||||
.host_enable_o (s_host_enable),
|
||||
.host_irq_enable_o(s_host_irq_enable),
|
||||
.clock_enable_o (s_clock_enable),
|
||||
.half_period_o (s_half_period),
|
||||
.bus_width_o (bus_width_o),
|
||||
.timeout_cmd_o (),
|
||||
.timeout_data_o (),
|
||||
.timeout_busy_o (),
|
||||
.cmd_index_o (s_cmd_index),
|
||||
.cmd_arg_o (s_cmd_arg),
|
||||
.cmd_resp_type_o (s_cmd_resp_type),
|
||||
.cmd_crc_check_o (s_cmd_crc_check),
|
||||
.cmd_index_check_o(s_cmd_index_check),
|
||||
.cmd_start_o (s_cmd_start),
|
||||
.block_size_o (s_block_size),
|
||||
.block_count_o (s_block_count),
|
||||
.data_direction_o (s_data_direction),
|
||||
.data_dma_enable_o(s_data_dma_enable),
|
||||
.data_block_mode_o(),
|
||||
.data_fixed_addr_o(),
|
||||
.data_start_o (s_data_start),
|
||||
.pio_wvalid_o (),
|
||||
.pio_wdata_o (),
|
||||
.pio_wstrb_o (),
|
||||
.desc_base_o (s_desc_base),
|
||||
.desc_count_o (s_desc_count_q),
|
||||
.dma_start_o (s_dma_start),
|
||||
.dma_abort_o (s_dma_abort),
|
||||
.irq_dat1_enable_o(s_irq_dat1_enable),
|
||||
.irq_o (irq_o),
|
||||
.apb4 (apb4)
|
||||
);
|
||||
|
||||
assign host_enable_o = s_host_enable;
|
||||
assign clock_enable_o = s_clock_enable;
|
||||
assign half_period_o = s_half_period;
|
||||
assign desc_count_o = s_desc_count_q;
|
||||
assign dma_start_o = s_dma_start;
|
||||
assign dma_abort_o = s_dma_abort;
|
||||
assign s_clock_actual = 32'd0;
|
||||
assign s_status = 32'd0;
|
||||
assign s_cmd_status = 32'd0;
|
||||
assign s_response = '0;
|
||||
assign s_data_status = 32'd0;
|
||||
assign s_fifo_status = 32'd0;
|
||||
assign s_dma_status = 32'd0;
|
||||
assign s_dma_error_addr_reg = s_dma_error_addr;
|
||||
assign s_dma_error_reg = {24'd0, s_dma_error_code};
|
||||
assign s_error_status = {24'd0, s_dma_error};
|
||||
assign s_last_cmd = 6'd0;
|
||||
assign s_crc_error_count = 32'd0;
|
||||
assign s_timeout_count = 32'd0;
|
||||
assign s_axi_error_count = 32'd0;
|
||||
assign s_stall_count = 32'd0;
|
||||
assign s_pio_rdata = 32'd0;
|
||||
assign s_pio_valid = 1'b0;
|
||||
assign s_pio_ready = 1'b1;
|
||||
|
||||
sdio_clock u_sdio_clock (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (s_host_enable && s_clock_enable),
|
||||
.half_period_i(s_half_period),
|
||||
.sck_o (sck_o),
|
||||
.launch_tick_o(launch_tick_o),
|
||||
.sample_tick_o(sample_tick_o),
|
||||
.running_o (clock_running_o)
|
||||
);
|
||||
|
||||
sdio_dma u_sdio_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_dma_start),
|
||||
.abort_i (s_dma_abort),
|
||||
.direction_i (s_data_direction),
|
||||
.desc_base_i (s_desc_base),
|
||||
.desc_count_i (s_desc_count_q),
|
||||
.total_bytes_i ({16'd0, s_block_size} * {16'd0, s_block_count}),
|
||||
.data_in_valid_i (1'b0),
|
||||
.data_in_ready_o (),
|
||||
.data_in_i ('0),
|
||||
.data_in_strb_i ('0),
|
||||
.data_in_last_i (1'b0),
|
||||
.data_out_valid_o(s_data_out_valid),
|
||||
.data_out_ready_i(dma_data_out_ready_i),
|
||||
.data_out_o (s_data_out),
|
||||
.data_out_strb_o (s_data_out_strb),
|
||||
.data_out_last_o (s_data_out_last),
|
||||
.busy_o (s_dma_busy),
|
||||
.done_o (s_dma_done),
|
||||
.error_o (s_dma_error),
|
||||
.error_code_o (s_dma_error_code),
|
||||
.current_desc_o (s_dma_current_desc),
|
||||
.bytes_done_o (s_dma_bytes_done),
|
||||
.error_addr_o (s_dma_error_addr),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
assign dma_busy_o = s_dma_busy;
|
||||
assign dma_done_o = s_dma_done;
|
||||
assign dma_error_o = s_dma_error;
|
||||
assign dma_error_code_o = s_dma_error_code;
|
||||
assign dma_current_desc_o = s_dma_current_desc;
|
||||
assign dma_bytes_done_o = s_dma_bytes_done;
|
||||
assign dma_error_addr_o = s_dma_error_addr;
|
||||
assign dma_data_out_valid_o = s_data_out_valid;
|
||||
assign dma_data_out_o = s_data_out;
|
||||
assign dma_data_out_strb_o = s_data_out_strb;
|
||||
assign dma_data_out_last_o = s_data_out_last;
|
||||
endmodule
|
||||
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
module sdio_formal;
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
reg rst_n_i;
|
||||
reg f_past_valid;
|
||||
|
||||
(* anyseq *)logic apb_psel_i;
|
||||
(* anyseq *)logic apb_penable_i;
|
||||
(* anyseq *)logic apb_pwrite_i;
|
||||
(* anyseq *)logic [31:0] apb_paddr_i;
|
||||
(* anyseq *)logic [31:0] apb_pwdata_i;
|
||||
(* anyseq *)logic [ 3:0] apb_pstrb_i;
|
||||
(* anyseq *)logic [ 7:0] irq_event_i;
|
||||
(* anyseq *)logic axi_awready_i;
|
||||
(* anyseq *)logic axi_wready_i;
|
||||
(* anyseq *)logic axi_bvalid_i;
|
||||
(* anyseq *)logic axi_bid_i;
|
||||
(* anyseq *)logic [ 1:0] axi_bresp_i;
|
||||
(* anyseq *)logic axi_arready_i;
|
||||
(* anyseq *)logic axi_rvalid_i;
|
||||
(* anyseq *)logic axi_rid_i;
|
||||
(* anyseq *)logic [31:0] axi_rdata_i;
|
||||
(* anyseq *)logic [ 1:0] axi_rresp_i;
|
||||
(* anyseq *)logic axi_rlast_i;
|
||||
(* anyseq *)logic dma_data_out_ready_i;
|
||||
|
||||
wire apb_pready_o;
|
||||
wire [31:0] apb_prdata_o;
|
||||
wire apb_pslverr_o;
|
||||
wire irq_o;
|
||||
wire sck_o;
|
||||
wire launch_tick_o;
|
||||
wire sample_tick_o;
|
||||
wire clock_running_o;
|
||||
wire dma_busy_o;
|
||||
wire dma_done_o;
|
||||
wire dma_error_o;
|
||||
wire [ 7:0] dma_error_code_o;
|
||||
wire [31:0] dma_current_desc_o;
|
||||
wire [31:0] dma_bytes_done_o;
|
||||
wire [31:0] dma_error_addr_o;
|
||||
wire dma_data_out_valid_o;
|
||||
wire [31:0] dma_data_out_o;
|
||||
wire [ 3:0] dma_data_out_strb_o;
|
||||
wire dma_data_out_last_o;
|
||||
wire axi_awvalid_o;
|
||||
wire [31:0] axi_awaddr_o;
|
||||
wire [ 7:0] axi_awlen_o;
|
||||
wire [ 2:0] axi_awsize_o;
|
||||
wire [ 1:0] axi_awburst_o;
|
||||
wire axi_wvalid_o;
|
||||
wire [31:0] axi_wdata_o;
|
||||
wire [ 3:0] axi_wstrb_o;
|
||||
wire axi_wlast_o;
|
||||
wire axi_bready_o;
|
||||
wire axi_arvalid_o;
|
||||
wire [31:0] axi_araddr_o;
|
||||
wire [ 7:0] axi_arlen_o;
|
||||
wire [ 2:0] axi_arsize_o;
|
||||
wire [ 1:0] axi_arburst_o;
|
||||
wire axi_rready_o;
|
||||
wire host_enable_o;
|
||||
wire clock_enable_o;
|
||||
wire [15:0] half_period_o;
|
||||
wire [ 1:0] bus_width_o;
|
||||
wire [15:0] desc_count_o;
|
||||
wire dma_start_o;
|
||||
wire dma_abort_o;
|
||||
|
||||
logic [ 4:0] f_abort_age_q;
|
||||
logic f_abort_pending_q;
|
||||
logic f_irq_seen_q;
|
||||
logic f_read_pending_q;
|
||||
logic f_abort_read_pending_q;
|
||||
|
||||
sdio_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb_psel_i (apb_psel_i),
|
||||
.apb_penable_i (apb_penable_i),
|
||||
.apb_pwrite_i (apb_pwrite_i),
|
||||
.apb_paddr_i (apb_paddr_i),
|
||||
.apb_pwdata_i (apb_pwdata_i),
|
||||
.apb_pstrb_i (apb_pstrb_i),
|
||||
.irq_event_i (irq_event_i),
|
||||
.axi_awready_i (axi_awready_i),
|
||||
.axi_wready_i (axi_wready_i),
|
||||
.axi_bvalid_i (axi_bvalid_i),
|
||||
.axi_bid_i (axi_bid_i),
|
||||
.axi_bresp_i (axi_bresp_i),
|
||||
.axi_arready_i (axi_arready_i),
|
||||
.axi_rvalid_i (axi_rvalid_i),
|
||||
.axi_rid_i (axi_rid_i),
|
||||
.axi_rdata_i (axi_rdata_i),
|
||||
.axi_rresp_i (axi_rresp_i),
|
||||
.axi_rlast_i (axi_rlast_i),
|
||||
.dma_data_out_ready_i(dma_data_out_ready_i),
|
||||
.apb_pready_o (apb_pready_o),
|
||||
.apb_prdata_o (apb_prdata_o),
|
||||
.apb_pslverr_o (apb_pslverr_o),
|
||||
.irq_o (irq_o),
|
||||
.sck_o (sck_o),
|
||||
.launch_tick_o (launch_tick_o),
|
||||
.sample_tick_o (sample_tick_o),
|
||||
.clock_running_o (clock_running_o),
|
||||
.dma_busy_o (dma_busy_o),
|
||||
.dma_done_o (dma_done_o),
|
||||
.dma_error_o (dma_error_o),
|
||||
.dma_error_code_o (dma_error_code_o),
|
||||
.dma_current_desc_o (dma_current_desc_o),
|
||||
.dma_bytes_done_o (dma_bytes_done_o),
|
||||
.dma_error_addr_o (dma_error_addr_o),
|
||||
.dma_data_out_valid_o(dma_data_out_valid_o),
|
||||
.dma_data_out_o (dma_data_out_o),
|
||||
.dma_data_out_strb_o (dma_data_out_strb_o),
|
||||
.dma_data_out_last_o (dma_data_out_last_o),
|
||||
.axi_awvalid_o (axi_awvalid_o),
|
||||
.axi_awaddr_o (axi_awaddr_o),
|
||||
.axi_awlen_o (axi_awlen_o),
|
||||
.axi_awsize_o (axi_awsize_o),
|
||||
.axi_awburst_o (axi_awburst_o),
|
||||
.axi_wvalid_o (axi_wvalid_o),
|
||||
.axi_wdata_o (axi_wdata_o),
|
||||
.axi_wstrb_o (axi_wstrb_o),
|
||||
.axi_wlast_o (axi_wlast_o),
|
||||
.axi_bready_o (axi_bready_o),
|
||||
.axi_arvalid_o (axi_arvalid_o),
|
||||
.axi_araddr_o (axi_araddr_o),
|
||||
.axi_arlen_o (axi_arlen_o),
|
||||
.axi_arsize_o (axi_arsize_o),
|
||||
.axi_arburst_o (axi_arburst_o),
|
||||
.axi_rready_o (axi_rready_o),
|
||||
.host_enable_o (host_enable_o),
|
||||
.clock_enable_o (clock_enable_o),
|
||||
.half_period_o (half_period_o),
|
||||
.bus_width_o (bus_width_o),
|
||||
.desc_count_o (desc_count_o),
|
||||
.dma_start_o (dma_start_o),
|
||||
.dma_abort_o (dma_abort_o)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
f_abort_pending_q = 1'b0;
|
||||
f_abort_age_q = 5'd0;
|
||||
f_irq_seen_q = 1'b0;
|
||||
f_read_pending_q = 1'b0;
|
||||
f_abort_read_pending_q = 1'b0;
|
||||
end
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
if (!rst_n_i) begin
|
||||
assume (!apb_psel_i);
|
||||
assume (!axi_bvalid_i);
|
||||
assume (!axi_rvalid_i);
|
||||
assume (!irq_event_i);
|
||||
end
|
||||
if (f_past_valid && $past(rst_n_i)) begin
|
||||
if ($past(apb_psel_i && !apb_penable_i)) begin
|
||||
assume (apb_psel_i);
|
||||
assume (apb_paddr_i == $past(apb_paddr_i));
|
||||
assume (apb_pwrite_i == $past(apb_pwrite_i));
|
||||
assume (apb_pwdata_i == $past(apb_pwdata_i));
|
||||
assume (apb_pstrb_i == $past(apb_pstrb_i));
|
||||
end
|
||||
if ($past(axi_rvalid_i && !axi_rready_o)) begin
|
||||
assume (axi_rvalid_i);
|
||||
assume (axi_rid_i == $past(axi_rid_i));
|
||||
assume (axi_rdata_i == $past(axi_rdata_i));
|
||||
assume (axi_rresp_i == $past(axi_rresp_i));
|
||||
assume (axi_rlast_i == $past(axi_rlast_i));
|
||||
end
|
||||
if ($past(axi_bvalid_i && !axi_bready_o)) begin
|
||||
assume (axi_bvalid_i);
|
||||
assume (axi_bid_i == $past(axi_bid_i));
|
||||
assume (axi_bresp_i == $past(axi_bresp_i));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (!rst_n_i) begin
|
||||
f_abort_pending_q <= 1'b0;
|
||||
f_abort_age_q <= 5'd0;
|
||||
f_irq_seen_q <= 1'b0;
|
||||
f_read_pending_q <= 1'b0;
|
||||
f_abort_read_pending_q <= 1'b0;
|
||||
end
|
||||
if (f_past_valid && $past(rst_n_i)) begin
|
||||
if ($past(
|
||||
apb_pready_o && apb_psel_i && apb_penable_i
|
||||
) && apb_psel_i && apb_penable_i && (apb_paddr_i == $past(
|
||||
apb_paddr_i
|
||||
)) && (apb_pwrite_i == $past(
|
||||
apb_pwrite_i
|
||||
)) && (apb_pwdata_i == $past(
|
||||
apb_pwdata_i
|
||||
)) && (apb_pstrb_i == $past(
|
||||
apb_pstrb_i
|
||||
))) begin
|
||||
assert (apb_prdata_o == $past(apb_prdata_o));
|
||||
assert (apb_pslverr_o == $past(apb_pslverr_o));
|
||||
end
|
||||
|
||||
if ($past(axi_awvalid_o && !axi_awready_i)) begin
|
||||
assert (axi_awvalid_o);
|
||||
assert (axi_awaddr_o == $past(axi_awaddr_o));
|
||||
assert (axi_awlen_o == $past(axi_awlen_o));
|
||||
assert (axi_awsize_o == $past(axi_awsize_o));
|
||||
assert (axi_awburst_o == $past(axi_awburst_o));
|
||||
end
|
||||
if ($past(axi_wvalid_o && !axi_wready_i)) begin
|
||||
if (axi_wvalid_o) begin
|
||||
assert (axi_wdata_o == $past(axi_wdata_o));
|
||||
assert (axi_wstrb_o == $past(axi_wstrb_o));
|
||||
assert (axi_wlast_o == $past(axi_wlast_o));
|
||||
end
|
||||
end
|
||||
if ($past(axi_arvalid_o && !axi_arready_i)) begin
|
||||
assert (axi_arvalid_o);
|
||||
assert (axi_araddr_o == $past(axi_araddr_o));
|
||||
assert (axi_arlen_o == $past(axi_arlen_o));
|
||||
assert (axi_arsize_o == $past(axi_arsize_o));
|
||||
assert (axi_arburst_o == $past(axi_arburst_o));
|
||||
end
|
||||
|
||||
if (axi_awvalid_o) begin
|
||||
assert (axi_awlen_o <= 8'd15);
|
||||
assert (axi_awsize_o == 3'd2);
|
||||
assert ((axi_awburst_o == 2'b01) || (axi_awburst_o == 2'b00));
|
||||
assert (axi_awaddr_o[1:0] == 2'b00);
|
||||
if (axi_awburst_o == 2'b01) begin
|
||||
assert ({1'b0, axi_awaddr_o[11:0]} + (({5'd0, axi_awlen_o} + 13'd1) << 2) <= 13'd4096);
|
||||
end
|
||||
end
|
||||
if (axi_arvalid_o) begin
|
||||
assert (axi_arlen_o <= 8'd15);
|
||||
assert (axi_arsize_o == 3'd2);
|
||||
assert ((axi_arburst_o == 2'b01) || (axi_arburst_o == 2'b00));
|
||||
assert (axi_araddr_o[1:0] == 2'b00);
|
||||
if (axi_arburst_o == 2'b01) begin
|
||||
assert ({1'b0, axi_araddr_o[11:0]} + (({5'd0, axi_arlen_o} + 13'd1) << 2) <= 13'd4096);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if (axi_arvalid_o && axi_arready_i) begin
|
||||
f_read_pending_q <= 1'b1;
|
||||
end
|
||||
if (f_read_pending_q && dma_abort_o) begin
|
||||
f_abort_read_pending_q <= 1'b1;
|
||||
end
|
||||
if (f_read_pending_q && f_abort_read_pending_q) begin
|
||||
assert (axi_rready_o);
|
||||
cover (axi_rvalid_i && axi_rready_o && axi_rlast_i);
|
||||
end
|
||||
if (f_read_pending_q && axi_rvalid_i && axi_rready_o && axi_rlast_i) begin
|
||||
f_read_pending_q <= 1'b0;
|
||||
f_abort_read_pending_q <= 1'b0;
|
||||
end
|
||||
|
||||
// The reduced DMA wrapper has no separate FIFO instance; stream
|
||||
// ownership and byte accounting are the tractable FIFO-bound proxy.
|
||||
assert (dma_bytes_done_o >= 32'd0);
|
||||
if (dma_start_o && (desc_count_o > 16'd16)) begin
|
||||
cover (dma_error_o);
|
||||
end
|
||||
|
||||
if (sck_o != $past(sck_o)) begin
|
||||
assert ($past(launch_tick_o) || $past(sample_tick_o));
|
||||
end
|
||||
|
||||
if (dma_abort_o) begin
|
||||
f_abort_pending_q <= 1'b1;
|
||||
f_abort_age_q <= 5'd0;
|
||||
end else if (f_abort_pending_q) begin
|
||||
if (dma_done_o || dma_error_o || !dma_busy_o) begin
|
||||
f_abort_pending_q <= 1'b0;
|
||||
end else begin
|
||||
if (f_abort_age_q != 5'd31) begin
|
||||
f_abort_age_q <= f_abort_age_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (irq_o) begin
|
||||
f_irq_seen_q <= 1'b1;
|
||||
end
|
||||
if (apb_pwrite_i && apb_penable_i && apb_paddr_i[11:0] == 12'h100 &&
|
||||
apb_pwdata_i[7:0] != 8'd0) begin
|
||||
f_irq_seen_q <= 1'b0;
|
||||
end
|
||||
|
||||
cover (apb_pready_o && apb_psel_i && apb_penable_i);
|
||||
cover (dma_abort_o);
|
||||
cover (irq_o);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -76,6 +76,8 @@ module sysctrl_formal_design (
|
||||
assign sysctrl.perf_mgmt_wait_i = '0;
|
||||
assign sysctrl.perf_user_wait_i = '0;
|
||||
assign sysctrl.perf_dma_wait_i = '0;
|
||||
assign sysctrl.perf_sdio0_wait_i = '0;
|
||||
assign sysctrl.perf_sdio1_wait_i = '0;
|
||||
assign sysctrl.perf_apb4_periph_wait_i = '0;
|
||||
assign sysctrl.perf_apb4_system_wait_i = '0;
|
||||
assign sysctrl.perf_sdram_wait_i = '0;
|
||||
|
||||
@@ -9,7 +9,10 @@ files for `apb4_periph.sv`, `apb4_system.sv`, `axi42apb4_periph.sv`,
|
||||
`axi42apb4_system.sv`, and `retrosoc.sv`. The two APB4 islands share one
|
||||
decoder template. `apb4_system` is instantiated as `u_apb4_system`; its AXI4
|
||||
configuration port is wired by generated `apb4_system_fabric.svh`.
|
||||
`apb4_periph` remains the self-owned APB4 peripheral container. Generated fabric links are explicit 32-bit AXI4 interfaces.
|
||||
`apb4_periph` remains the self-owned APB4 peripheral container. Generated
|
||||
fabric links are explicit 32-bit AXI4 interfaces; `mgmt`, `user`, `dma`,
|
||||
`sdio0`, and `sdio1` are the five native AXI4 masters, while `cfg` and
|
||||
`system` are target-side links.
|
||||
Interface arrays are confined to the AXI4 bus implementation and flattened by
|
||||
the existing synthesis/export flow before FPGA or netlist simulation.
|
||||
|
||||
@@ -18,6 +21,8 @@ RIBP island at `0x1000_0000`. Every active `apb4_periph` memory-map region
|
||||
must appear exactly once. Disabled targets keep their slot and interface
|
||||
declaration but cannot own an address region. This list stays separate from
|
||||
`apb4_system_targets`, which owns the `0x2000_0000` `apb4_system` island.
|
||||
Slot 15 is the active SDIO0 management window; SDIO1 is appended at slot 18
|
||||
so the existing slots 0..17 remain stable.
|
||||
|
||||
Each `gpio_alt_functions` entry defines both alternate modes for one GPIO pin.
|
||||
`inputs` are driven from the selected GPIO input, and `do` and `oe` define the
|
||||
@@ -37,8 +42,10 @@ exactly once, each core-vector bit must have one source, and sources are limited
|
||||
to scalar signal references or one-bit constants. The generator emits the two
|
||||
wrapper bindings, the core-vector wiring, and simulation-only assertions.
|
||||
Existing allocated core IRQ bits retain their compatibility mapping; additions
|
||||
must use currently unallocated bits and preserve the existing entries. Removing
|
||||
a source leaves its core IRQ bit unallocated and driven low. Firmware does not
|
||||
must use currently unallocated bits and preserve the existing entries. SDIO0
|
||||
and SDIO1 use APB4 group bits 16 and 17 and core bits 10 and 21, and remain
|
||||
management-only in the generated user IRQ mask. Removing a source leaves its
|
||||
core IRQ bit unallocated and driven low. Firmware does not
|
||||
expose a generic external interrupt API until the SoC includes a claim/complete
|
||||
interrupt controller.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from typing import Any
|
||||
SV_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
|
||||
SV_REFERENCE_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*(?:\[[0-9]+\])?$")
|
||||
SV_CONSTANT_RE = re.compile(r"(?:1'[bB][01]|'[01])$")
|
||||
FABRIC_LINK_NAMES = ("mgmt", "user", "dma", "cfg", "system")
|
||||
FABRIC_LINK_NAMES = ("mgmt", "user", "dma", "sdio0", "sdio1", "cfg", "system")
|
||||
FABRIC_PROTOCOLS = {name: "axi4" for name in FABRIC_LINK_NAMES}
|
||||
IRQ_GROUP_NAMES = ("apb4_periph", "apb4_system")
|
||||
IRQ_VECTOR_WIDTH = 32
|
||||
@@ -633,6 +633,8 @@ def render_bus_fabric_connections(links: list[FabricLink]) -> str:
|
||||
"mgmt": "mgmt_axi4",
|
||||
"user": "user_axi4",
|
||||
"dma": "dma_axi4",
|
||||
"sdio0": "sdio0_axi4",
|
||||
"sdio1": "sdio1_axi4",
|
||||
"cfg": "cfg_axi4",
|
||||
"system": "system_axi4",
|
||||
}
|
||||
@@ -807,7 +809,9 @@ def generate(topology_path: Path, memory_map_path: Path, output_dir: Path) -> No
|
||||
atomic_write(
|
||||
rtl_dir / "apb4_periph_fabric.svh",
|
||||
render_fabric_connection(fabric_links, "cfg", "cfg_axi4")
|
||||
+ render_fabric_connection(fabric_links, "dma", "dma_axi4"),
|
||||
+ render_fabric_connection(fabric_links, "dma", "dma_axi4")
|
||||
+ render_fabric_connection(fabric_links, "sdio0", "sdio0_axi4")
|
||||
+ render_fabric_connection(fabric_links, "sdio1", "sdio1_axi4"),
|
||||
)
|
||||
atomic_write(
|
||||
rtl_dir / "apb4_system_fabric.svh",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"irq_groups": [
|
||||
{
|
||||
"name": "apb4_periph",
|
||||
"width": 16,
|
||||
"width": 18,
|
||||
"top_signal": "s_apb4_periph_irq"
|
||||
},
|
||||
{
|
||||
@@ -97,6 +97,15 @@
|
||||
"core_bit": 9,
|
||||
"signal": "xpi.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "sdio0",
|
||||
"description": "SDIO0 host interrupt",
|
||||
"group": "apb4_periph",
|
||||
"group_bit": 16,
|
||||
"core_bit": 10,
|
||||
"signal": "sdio0.irq_o",
|
||||
"user_visible": false
|
||||
},
|
||||
{
|
||||
"name": "pwm",
|
||||
"description": "APB4 PWM interrupt",
|
||||
@@ -186,6 +195,15 @@
|
||||
"group_bit": 15,
|
||||
"core_bit": 22,
|
||||
"signal": "opipsram.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "sdio1",
|
||||
"description": "SDIO1 host interrupt",
|
||||
"group": "apb4_periph",
|
||||
"group_bit": 17,
|
||||
"core_bit": 21,
|
||||
"signal": "sdio1.irq_o",
|
||||
"user_visible": false
|
||||
}
|
||||
],
|
||||
"wake_sources": [
|
||||
@@ -212,6 +230,16 @@
|
||||
"interface": "u_dma_axi4_if",
|
||||
"protocol": "axi4"
|
||||
},
|
||||
{
|
||||
"name": "sdio0",
|
||||
"interface": "u_sdio0_axi4_if",
|
||||
"protocol": "axi4"
|
||||
},
|
||||
{
|
||||
"name": "sdio1",
|
||||
"interface": "u_sdio1_axi4_if",
|
||||
"protocol": "axi4"
|
||||
},
|
||||
{
|
||||
"name": "cfg",
|
||||
"interface": "u_cfg_axi4_if",
|
||||
@@ -339,10 +367,10 @@
|
||||
},
|
||||
{
|
||||
"slot": 15,
|
||||
"name": "sdio",
|
||||
"timed_interface": "u_sdio_apb4_if",
|
||||
"pure_interface": "u_sdio_apb4_pure_if",
|
||||
"disabled": true
|
||||
"name": "sdio0",
|
||||
"timed_interface": "u_sdio0_apb4_if",
|
||||
"pure_interface": "u_sdio0_apb4_pure_if",
|
||||
"region": "APB4_SDIO0"
|
||||
},
|
||||
{
|
||||
"slot": 16,
|
||||
@@ -357,6 +385,13 @@
|
||||
"timed_interface": "u_i2c1_apb4_if",
|
||||
"pure_interface": "u_i2c1_apb4_pure_if",
|
||||
"region": "APB4_I2C1"
|
||||
},
|
||||
{
|
||||
"slot": 18,
|
||||
"name": "sdio1",
|
||||
"timed_interface": "u_sdio1_apb4_if",
|
||||
"pure_interface": "u_sdio1_apb4_pure_if",
|
||||
"region": "APB4_SDIO1"
|
||||
}
|
||||
],
|
||||
"apb4_system_targets": [
|
||||
@@ -649,7 +684,7 @@
|
||||
"pin": 15,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "u_sdio_if.sck_o",
|
||||
"do": "u_sdio0_if.sck_o",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
@@ -664,10 +699,10 @@
|
||||
"pin": 16,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_sdio_if.cmd_di_i"
|
||||
"u_sdio0_if.cmd_di_i"
|
||||
],
|
||||
"do": "u_sdio_if.cmd_do_o",
|
||||
"oe": "u_sdio_if.cmd_oe_o"
|
||||
"do": "u_sdio0_if.cmd_do_o",
|
||||
"oe": "u_sdio0_if.cmd_oe_o"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
@@ -681,10 +716,10 @@
|
||||
"pin": 17,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_sdio_if.dat_di_i[0]"
|
||||
"u_sdio0_if.dat_di_i[0]"
|
||||
],
|
||||
"do": "u_sdio_if.dat_do_o[0]",
|
||||
"oe": "u_sdio_if.dat_oe_o[0]"
|
||||
"do": "u_sdio0_if.dat_do_o[0]",
|
||||
"oe": "u_sdio0_if.dat_oe_o[0]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
@@ -698,10 +733,10 @@
|
||||
"pin": 18,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_sdio_if.dat_di_i[1]"
|
||||
"u_sdio0_if.dat_di_i[1]"
|
||||
],
|
||||
"do": "u_sdio_if.dat_do_o[1]",
|
||||
"oe": "u_sdio_if.dat_oe_o[1]"
|
||||
"do": "u_sdio0_if.dat_do_o[1]",
|
||||
"oe": "u_sdio0_if.dat_oe_o[1]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
@@ -715,10 +750,10 @@
|
||||
"pin": 19,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_sdio_if.dat_di_i[2]"
|
||||
"u_sdio0_if.dat_di_i[2]"
|
||||
],
|
||||
"do": "u_sdio_if.dat_do_o[2]",
|
||||
"oe": "u_sdio_if.dat_oe_o[2]"
|
||||
"do": "u_sdio0_if.dat_do_o[2]",
|
||||
"oe": "u_sdio0_if.dat_oe_o[2]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
@@ -732,10 +767,10 @@
|
||||
"pin": 20,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_sdio_if.dat_di_i[3]"
|
||||
"u_sdio0_if.dat_di_i[3]"
|
||||
],
|
||||
"do": "u_sdio_if.dat_do_o[3]",
|
||||
"oe": "u_sdio_if.dat_oe_o[3]"
|
||||
"do": "u_sdio0_if.dat_do_o[3]",
|
||||
"oe": "u_sdio0_if.dat_oe_o[3]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
|
||||
+13
-6
@@ -13,12 +13,15 @@ FORMAL_OPIPSRAM_DEPTH ?= 40
|
||||
FORMAL_OPIPSRAM_BMC_DEPTH ?= 40
|
||||
FORMAL_DMA_DEPTH ?= 24
|
||||
FORMAL_DMA_COVER_DEPTH ?= 32
|
||||
FORMAL_SDIO_DEPTH ?= 20
|
||||
FORMAL_SDIO_COVER_DEPTH ?= 24
|
||||
FORMAL_TIMEOUT ?= 60
|
||||
FORMAL_WS2812_TIMEOUT ?= 120
|
||||
FORMAL_I2C_TIMEOUT ?= 300
|
||||
FORMAL_OPIPSRAM_TIMEOUT ?= 300
|
||||
FORMAL_DMA_TIMEOUT ?= 120
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram opipsram dma
|
||||
FORMAL_SDIO_TIMEOUT ?= 120
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram opipsram dma sdio
|
||||
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
|
||||
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
||||
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
|
||||
@@ -54,6 +57,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
|
||||
$(RTL_PATH)/formal/opipsram_formal_props.sv \
|
||||
$(RTL_PATH)/formal/dma_formal.sv \
|
||||
$(RTL_PATH)/formal/dma_formal_props.sv \
|
||||
$(RTL_PATH)/formal/sdio_formal.sv \
|
||||
$(RTL_PATH)/formal/sdio_formal_props.sv \
|
||||
$(RTL_PATH)/top/rib_bus.sv \
|
||||
$(RTL_PATH)/top/rib_error_slave.sv \
|
||||
$(RTL_PATH)/top/rib_if.sv \
|
||||
@@ -173,14 +178,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
||||
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
|
||||
--mode $(if $(filter dma,$*),bmc,prove) --depth $(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))) --output $@
|
||||
--mode $(if $(filter dma sdio,$*),bmc,prove) --depth $(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH)))))))) --output $@
|
||||
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||
|
||||
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
||||
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
|
||||
--mode cover --depth $(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))) \
|
||||
--mode cover --depth $(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH)))))))) \
|
||||
$(if $(filter i2c,$*),--no-vcd) --output $@
|
||||
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||
|
||||
@@ -204,7 +209,7 @@ $(FORMAL_DIR)/%/prove.stamp: $(FORMAL_DIR)/%/prove.sby $(FORMAL_SOLVER_WRAPPER)
|
||||
--log $(@D)/prove.log --result $(@D)/result-prove.json \
|
||||
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
|
||||
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
|
||||
-- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))))s $(FORMAL_SBY) \
|
||||
-- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))s $(FORMAL_SBY) \
|
||||
-f -d $(@D)/prove $<
|
||||
@test "$$(awk '{print $$1}' $(@D)/prove/status)" = PASS
|
||||
@touch $@
|
||||
@@ -214,7 +219,7 @@ $(FORMAL_DIR)/%/cover.stamp: $(FORMAL_DIR)/%/cover.sby $(FORMAL_SOLVER_WRAPPER)
|
||||
--log $(@D)/cover.log --result $(@D)/result-cover.json \
|
||||
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
|
||||
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
|
||||
-- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))))s $(FORMAL_SBY) \
|
||||
-- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))s $(FORMAL_SBY) \
|
||||
-f -d $(@D)/cover $<
|
||||
@test "$$(awk '{print $$1}' $(@D)/cover/status)" = PASS
|
||||
@touch $@
|
||||
@@ -275,10 +280,12 @@ formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
|
||||
|
||||
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
|
||||
|
||||
formal-sdio: $(FORMAL_DIR)/sdio/.stamp | manifest
|
||||
|
||||
formal-doctor:
|
||||
$(MAKE) FORMAL=YES SIMU=IVERILOG SYNTH=YOSYS STA=NONE doctor
|
||||
|
||||
formal-clean:
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
|
||||
|
||||
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-opipsram formal-dma formal-doctor formal-clean
|
||||
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-opipsram formal-dma formal-sdio formal-doctor formal-clean
|
||||
@@ -107,6 +107,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
|
||||
$(ROOT_PATH)/crt/src/hal/opipsram.c \
|
||||
$(ROOT_PATH)/crt/src/hal/sdram_math.c \
|
||||
$(ROOT_PATH)/crt/src/hal/sdram.c \
|
||||
$(ROOT_PATH)/crt/src/hal/sdio_math.c \
|
||||
$(ROOT_PATH)/crt/src/hal/sdio.c \
|
||||
$(ROOT_PATH)/crt/src/hal/spisd.c \
|
||||
$(ROOT_PATH)/crt/src/hal/qspi.c \
|
||||
$(ROOT_PATH)/crt/src/hal/user_core.c
|
||||
|
||||
@@ -6,5 +6,9 @@ temporary SystemVerilog include files below the selected build variant; do not
|
||||
edit generated files.
|
||||
|
||||
The map intentionally does not contain FPGA package locations. Board-level
|
||||
locations remain owned by the FPGA constraint file. Validate changes with
|
||||
`make check-pin-map` and the pin-map tests before running a regression.
|
||||
locations remain owned by the FPGA constraint file. Dedicated SDIO1 pads are
|
||||
therefore emitted but intentionally unbound in the FPGA profile until a
|
||||
package pin and 3.3 V I/O-bank contract exists. The generic
|
||||
`peripheral_bidir` kind requires explicit input, output, and output-enable
|
||||
signals and is reusable for any peripheral, not just SDIO. Validate changes
|
||||
with `make check-pin-map` and the pin-map tests before running a regression.
|
||||
@@ -16,6 +16,8 @@ from typing import Any
|
||||
VALID_FEATURES = {"HAVE_PLL"}
|
||||
VALID_KINDS = {
|
||||
"full",
|
||||
"peripheral_output",
|
||||
"peripheral_bidir",
|
||||
"schmitt_input",
|
||||
"sdram_dq",
|
||||
"tri_input",
|
||||
@@ -36,6 +38,9 @@ class Pad:
|
||||
kind: str
|
||||
index: int | None
|
||||
signal: str | None
|
||||
input_signal: str | None
|
||||
output_signal: str | None
|
||||
output_enable_signal: str | None
|
||||
interface: str | None
|
||||
peer: str | None
|
||||
bind: bool
|
||||
@@ -93,12 +98,20 @@ def parse_pad(entry: dict[str, Any], defaults: dict[str, Any], index: int | None
|
||||
raise ValueError(f"{name}.kind is not supported")
|
||||
feature = parse_feature(data.get("feature"), f"{name}.feature")
|
||||
signal = format_template(data.get("signal"), index, f"{name}.signal")
|
||||
input_signal = format_template(data.get("input"), index, f"{name}.input")
|
||||
output_signal = format_template(data.get("output"), index, f"{name}.output")
|
||||
output_enable_signal = format_template(
|
||||
data.get("output_enable"), index, f"{name}.output_enable"
|
||||
)
|
||||
interface = format_template(data.get("interface"), index, f"{name}.interface")
|
||||
peer = format_template(data.get("peer"), index, f"{name}.peer")
|
||||
bind = data.get("bind", True)
|
||||
if not isinstance(bind, bool):
|
||||
raise ValueError(f"{name}.bind must be boolean")
|
||||
if kind in {"tri_input", "schmitt_input", "tri_output"} and signal is None:
|
||||
if (
|
||||
kind in {"tri_input", "schmitt_input", "tri_output", "peripheral_output"}
|
||||
and signal is None
|
||||
):
|
||||
raise ValueError(f"{name}.signal is required for {kind}")
|
||||
if kind == "full" and (interface is None or index is None):
|
||||
raise ValueError(f"{name} requires an interface and index")
|
||||
@@ -106,7 +119,24 @@ def parse_pad(entry: dict[str, Any], defaults: dict[str, Any], index: int | None
|
||||
raise ValueError(f"{name} requires an index")
|
||||
if kind == "xtal" and bind and (signal is None or peer is None):
|
||||
raise ValueError(f"{name} requires signal and peer")
|
||||
return Pad(name, direction, feature, kind, index, signal, interface, peer, bind)
|
||||
if kind == "peripheral_bidir" and (
|
||||
input_signal is None or output_signal is None or output_enable_signal is None
|
||||
):
|
||||
raise ValueError(f"{name} requires input, output, and output_enable")
|
||||
return Pad(
|
||||
name,
|
||||
direction,
|
||||
feature,
|
||||
kind,
|
||||
index,
|
||||
signal,
|
||||
input_signal,
|
||||
output_signal,
|
||||
output_enable_signal,
|
||||
interface,
|
||||
peer,
|
||||
bind,
|
||||
)
|
||||
|
||||
|
||||
def expand_group(group: Any, index: int) -> list[Pad]:
|
||||
@@ -215,6 +245,13 @@ def render_pad_instance(pad: Pad) -> str | None:
|
||||
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p({pad.signal}), .c2p_en(1'b1), "
|
||||
".p2c());"
|
||||
)
|
||||
if pad.kind == "peripheral_output":
|
||||
return f" tc_io_out_pad {instance} (.pad({pad.name}), .c2p({pad.signal}));"
|
||||
if pad.kind == "peripheral_bidir":
|
||||
return (
|
||||
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p({pad.output_signal}), "
|
||||
f".c2p_en({pad.output_enable_signal}), .p2c({pad.input_signal}));"
|
||||
)
|
||||
if pad.kind == "full":
|
||||
return (
|
||||
f" tc_io_tri_full_pad {instance} (.pad({pad.name}), .c2p({pad.interface}.do_o[{pad.index}]), "
|
||||
|
||||
@@ -55,6 +55,49 @@
|
||||
"count": 32,
|
||||
"interface": "u_gpio_if"
|
||||
},
|
||||
{
|
||||
"kind": "peripheral_output",
|
||||
"direction": "output",
|
||||
"ports": [
|
||||
{"name": "sdio1_clk_o_pad", "signal": "u_sdio1_if.sck_o"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "peripheral_bidir",
|
||||
"direction": "inout",
|
||||
"ports": [
|
||||
{
|
||||
"name": "sdio1_cmd_io_pad",
|
||||
"input": "u_sdio1_if.cmd_di_i",
|
||||
"output": "u_sdio1_if.cmd_do_o",
|
||||
"output_enable": "u_sdio1_if.cmd_oe_o"
|
||||
},
|
||||
{
|
||||
"name": "sdio1_dat0_io_pad",
|
||||
"input": "u_sdio1_if.dat_di_i[0]",
|
||||
"output": "u_sdio1_if.dat_do_o[0]",
|
||||
"output_enable": "u_sdio1_if.dat_oe_o[0]"
|
||||
},
|
||||
{
|
||||
"name": "sdio1_dat1_io_pad",
|
||||
"input": "u_sdio1_if.dat_di_i[1]",
|
||||
"output": "u_sdio1_if.dat_do_o[1]",
|
||||
"output_enable": "u_sdio1_if.dat_oe_o[1]"
|
||||
},
|
||||
{
|
||||
"name": "sdio1_dat2_io_pad",
|
||||
"input": "u_sdio1_if.dat_di_i[2]",
|
||||
"output": "u_sdio1_if.dat_do_o[2]",
|
||||
"output_enable": "u_sdio1_if.dat_oe_o[2]"
|
||||
},
|
||||
{
|
||||
"name": "sdio1_dat3_io_pad",
|
||||
"input": "u_sdio1_if.dat_di_i[3]",
|
||||
"output": "u_sdio1_if.dat_do_o[3]",
|
||||
"output_enable": "u_sdio1_if.dat_oe_o[3]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "tri_output",
|
||||
"direction": "output",
|
||||
@@ -169,6 +212,12 @@
|
||||
"gpio_25_io_pad": "psram_dat2_io",
|
||||
"gpio_26_io_pad": "psram_dat3_io",
|
||||
"gpio_30_io_pad": "gpio_io30",
|
||||
"sdio1_clk_o_pad": null,
|
||||
"sdio1_cmd_io_pad": null,
|
||||
"sdio1_dat0_io_pad": null,
|
||||
"sdio1_dat1_io_pad": null,
|
||||
"sdio1_dat2_io_pad": null,
|
||||
"sdio1_dat3_io_pad": null,
|
||||
"uart0_tx_o_pad": "uart0_tx_o",
|
||||
"uart0_rx_i_pad": "uart0_rx_i",
|
||||
"xpi_sck_o_pad": "xpi_sck_o",
|
||||
@@ -236,6 +285,12 @@
|
||||
"gpio_27_io_pad": "s_psram_nss1",
|
||||
"gpio_28_io_pad": "s_psram_nss2",
|
||||
"gpio_29_io_pad": "s_psram_nss3",
|
||||
"sdio1_clk_o_pad": "sdio1_clk_o",
|
||||
"sdio1_cmd_io_pad": "sdio1_cmd_io",
|
||||
"sdio1_dat0_io_pad": "sdio1_dat0_io",
|
||||
"sdio1_dat1_io_pad": "sdio1_dat1_io",
|
||||
"sdio1_dat2_io_pad": "sdio1_dat2_io",
|
||||
"sdio1_dat3_io_pad": "sdio1_dat3_io",
|
||||
"xpi_sck_o_pad": "s_xpi_sck_o",
|
||||
"xpi_nss0_o_pad": "s_xpi_nss0_o",
|
||||
"xpi_dat0_io_pad": "s_xpi_dat0_io",
|
||||
@@ -307,6 +362,12 @@
|
||||
"gpio_27_io_pad": "s_psram_nss1",
|
||||
"gpio_28_io_pad": "s_psram_nss2",
|
||||
"gpio_29_io_pad": "s_psram_nss3",
|
||||
"sdio1_clk_o_pad": "s_sdio1_clk",
|
||||
"sdio1_cmd_io_pad": "s_sdio1_cmd",
|
||||
"sdio1_dat0_io_pad": "s_sdio1_dat[0]",
|
||||
"sdio1_dat1_io_pad": "s_sdio1_dat[1]",
|
||||
"sdio1_dat2_io_pad": "s_sdio1_dat[2]",
|
||||
"sdio1_dat3_io_pad": "s_sdio1_dat[3]",
|
||||
"uart0_tx_o_pad": "s_uart0_tx",
|
||||
"uart0_rx_i_pad": "s_uart0_rx",
|
||||
"xpi_sck_o_pad": "s_xpi_sck_o",
|
||||
|
||||
@@ -34,11 +34,14 @@ module apb4_periph (
|
||||
ws2812_if.dut ws2812,
|
||||
xpi_if.dut xpi,
|
||||
axi4_if.master dma_axi4,
|
||||
axi4_if.master sdio0_axi4,
|
||||
axi4_if.master sdio1_axi4,
|
||||
sysctrl_if.dut sysctrl,
|
||||
pll_ctrl_if.sysctrl pll_ctrl,
|
||||
apb4_if.master sdram_cfg,
|
||||
dvp_if.dut dvp,
|
||||
sdio_if.dut sdio,
|
||||
sdio_if.dut sdio0,
|
||||
sdio_if.dut sdio1,
|
||||
opipsram_if.dut opipsram,
|
||||
i2c_if.dut i2c1,
|
||||
input logic fault_valid_i,
|
||||
@@ -271,11 +274,20 @@ axi4_stream_if #(
|
||||
.irq_o (s_dvp_irq)
|
||||
);
|
||||
|
||||
apb4_sdio u_apb4_sdio (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.apb4 (u_sdio_apb4_if),
|
||||
.sdio (sdio)
|
||||
apb4_sdio u_apb4_sdio0 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb4 (u_sdio0_apb4_if),
|
||||
.dma_axi4(sdio0_axi4),
|
||||
.sdio (sdio0)
|
||||
);
|
||||
|
||||
apb4_sdio u_apb4_sdio1 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb4 (u_sdio1_apb4_if),
|
||||
.dma_axi4(sdio1_axi4),
|
||||
.sdio (sdio1)
|
||||
);
|
||||
|
||||
apb4_opipsram u_apb4_opipsram (
|
||||
|
||||
@@ -15,6 +15,8 @@ module axi4_bus (
|
||||
axi4_if.slave mgmt_axi4,
|
||||
axi4_if.slave user_axi4,
|
||||
axi4_if.slave dma_axi4,
|
||||
axi4_if.slave sdio0_axi4,
|
||||
axi4_if.slave sdio1_axi4,
|
||||
axi4_if.master cfg_axi4,
|
||||
axi4_if.master system_axi4,
|
||||
`ifdef HAVE_SRAM_IF
|
||||
@@ -34,11 +36,13 @@ module axi4_bus (
|
||||
output logic [3:0] fault_wstrb_o,
|
||||
output logic fault_reserved_o,
|
||||
output logic fault_access_o,
|
||||
output logic [1:0] fault_master_o,
|
||||
output logic [2:0] fault_master_o,
|
||||
output logic [2:0] fault_code_o,
|
||||
output logic [63:0] perf_mgmt_wait_o,
|
||||
output logic [63:0] perf_user_wait_o,
|
||||
output logic [63:0] perf_dma_wait_o,
|
||||
output logic [63:0] perf_sdio0_wait_o,
|
||||
output logic [63:0] perf_sdio1_wait_o,
|
||||
output logic [63:0] perf_apb4_periph_wait_o,
|
||||
output logic [63:0] perf_apb4_system_wait_o,
|
||||
output logic [63:0] perf_sdram_wait_o,
|
||||
@@ -47,7 +51,7 @@ module axi4_bus (
|
||||
output logic [63:0] perf_opipsram_wait_o
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam int NumMasters = 3;
|
||||
localparam int NumMasters = 5;
|
||||
localparam int NumTargets = 10;
|
||||
|
||||
axi4_if #(
|
||||
@@ -95,6 +99,16 @@ module axi4_bus (
|
||||
.sink (u_master_axi4_if[2])
|
||||
);
|
||||
|
||||
axi4_connector u_sdio0_connector (
|
||||
.source(sdio0_axi4),
|
||||
.sink (u_master_axi4_if[3])
|
||||
);
|
||||
|
||||
axi4_connector u_sdio1_connector (
|
||||
.source(sdio1_axi4),
|
||||
.sink (u_master_axi4_if[4])
|
||||
);
|
||||
|
||||
axi4_connector u_cfg_connector (
|
||||
.source(u_target_axi4_if[0]),
|
||||
.sink (cfg_axi4)
|
||||
@@ -157,6 +171,8 @@ module axi4_bus (
|
||||
.perf_mgmt_wait_o (perf_mgmt_wait_o),
|
||||
.perf_user_wait_o (perf_user_wait_o),
|
||||
.perf_dma_wait_o (perf_dma_wait_o),
|
||||
.perf_sdio0_wait_o (perf_sdio0_wait_o),
|
||||
.perf_sdio1_wait_o (perf_sdio1_wait_o),
|
||||
.perf_apb4_periph_wait_o(perf_apb4_periph_wait_o),
|
||||
.perf_apb4_system_wait_o(perf_apb4_system_wait_o),
|
||||
.perf_sdram_wait_o (perf_sdram_wait_o),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
`include "rib_defs.svh"
|
||||
|
||||
module axi4_interconnect #(
|
||||
parameter int NumMasters = 3,
|
||||
parameter int NumMasters = 5,
|
||||
parameter int NumTargets = 10
|
||||
) (
|
||||
input logic clk_i,
|
||||
@@ -23,11 +23,13 @@ module axi4_interconnect #(
|
||||
output logic [ 3:0] fault_wstrb_o,
|
||||
output logic fault_reserved_o,
|
||||
output logic fault_access_o,
|
||||
output logic [ 1:0] fault_master_o,
|
||||
output logic [ 2:0] fault_master_o,
|
||||
output logic [ 2:0] fault_code_o,
|
||||
output logic [63:0] perf_mgmt_wait_o,
|
||||
output logic [63:0] perf_user_wait_o,
|
||||
output logic [63:0] perf_dma_wait_o,
|
||||
output logic [63:0] perf_sdio0_wait_o,
|
||||
output logic [63:0] perf_sdio1_wait_o,
|
||||
output logic [63:0] perf_apb4_periph_wait_o,
|
||||
output logic [63:0] perf_apb4_system_wait_o,
|
||||
output logic [63:0] perf_sdram_wait_o,
|
||||
@@ -471,7 +473,7 @@ module axi4_interconnect #(
|
||||
fault_wstrb_o = s_master_first_wstrb[master];
|
||||
fault_reserved_o = `SOC_ADDR_IS_RESERVED(s_master_addr[master]);
|
||||
fault_access_o = s_master_access_err[master];
|
||||
fault_master_o = 2'(master);
|
||||
fault_master_o = 3'(master);
|
||||
fault_code_o = s_master_fault_code[master];
|
||||
end
|
||||
end
|
||||
@@ -575,6 +577,8 @@ module axi4_interconnect #(
|
||||
assign perf_mgmt_wait_o = s_perf_master_wait[0];
|
||||
assign perf_user_wait_o = s_perf_master_wait[1];
|
||||
assign perf_dma_wait_o = s_perf_master_wait[2];
|
||||
assign perf_sdio0_wait_o = s_perf_master_wait[3];
|
||||
assign perf_sdio1_wait_o = s_perf_master_wait[4];
|
||||
assign perf_apb4_periph_wait_o = s_perf_target_wait[TARGET_CFG];
|
||||
assign perf_apb4_system_wait_o = s_perf_target_wait[TARGET_APB4_SYSTEM];
|
||||
assign perf_sdram_wait_o = s_perf_target_wait[TARGET_SDRAM];
|
||||
@@ -588,7 +592,7 @@ module axi4_interconnect #(
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if (NumMasters != 3 || ((NumTargets != 9) && (NumTargets != 10))) begin
|
||||
if (NumMasters != 5 || ((NumTargets != 9) && (NumTargets != 10))) begin
|
||||
$fatal(1, "axi4_interconnect: invalid topology dimensions");
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,6 +29,7 @@ module retrosoc (
|
||||
output logic uart_tx_o,
|
||||
xpi_if.dut xpi,
|
||||
sdram_if.dut sdram,
|
||||
sdio_if.dut sdio1,
|
||||
input logic jtag_tck_i,
|
||||
input logic jtag_tms_i,
|
||||
input logic jtag_tdi_i,
|
||||
@@ -66,7 +67,7 @@ module retrosoc (
|
||||
ws2812_if u_ws2812_if ();
|
||||
sysctrl_if u_sysctrl_if ();
|
||||
dvp_if u_dvp_if ();
|
||||
sdio_if u_sdio_if ();
|
||||
sdio_if u_sdio0_if ();
|
||||
opipsram_if u_opipsram_if ();
|
||||
i2c_if u_i2c1_if ();
|
||||
pwm_if u_pwm_if ();
|
||||
@@ -82,13 +83,15 @@ module retrosoc (
|
||||
logic [ 3:0] s_bus_fault_wstrb;
|
||||
logic s_bus_fault_reserved;
|
||||
logic s_bus_fault_access;
|
||||
logic [ 1:0] s_bus_fault_master;
|
||||
logic [ 2:0] s_bus_fault_master;
|
||||
logic [ 2:0] s_bus_fault_code;
|
||||
logic s_perf_en;
|
||||
logic s_perf_clear;
|
||||
logic [ 63:0] s_perf_mgmt_wait;
|
||||
logic [ 63:0] s_perf_user_wait;
|
||||
logic [ 63:0] s_perf_dma_wait;
|
||||
logic [ 63:0] s_perf_sdio0_wait;
|
||||
logic [ 63:0] s_perf_sdio1_wait;
|
||||
logic [ 63:0] s_perf_apb4_periph_wait;
|
||||
logic [ 63:0] s_perf_apb4_system_wait;
|
||||
logic [ 63:0] s_perf_sdram_wait;
|
||||
@@ -109,6 +112,8 @@ module retrosoc (
|
||||
assign u_sysctrl_if.perf_mgmt_wait_i = s_perf_mgmt_wait;
|
||||
assign u_sysctrl_if.perf_user_wait_i = s_perf_user_wait;
|
||||
assign u_sysctrl_if.perf_dma_wait_i = s_perf_dma_wait;
|
||||
assign u_sysctrl_if.perf_sdio0_wait_i = s_perf_sdio0_wait;
|
||||
assign u_sysctrl_if.perf_sdio1_wait_i = s_perf_sdio1_wait;
|
||||
assign u_sysctrl_if.perf_apb4_periph_wait_i = s_perf_apb4_periph_wait;
|
||||
assign u_sysctrl_if.perf_apb4_system_wait_i = s_perf_apb4_system_wait;
|
||||
assign u_sysctrl_if.perf_sdram_wait_i = s_perf_sdram_wait;
|
||||
@@ -179,6 +184,8 @@ core_wrapper u_core_wrapper (
|
||||
.perf_mgmt_wait_o (s_perf_mgmt_wait),
|
||||
.perf_user_wait_o (s_perf_user_wait),
|
||||
.perf_dma_wait_o (s_perf_dma_wait),
|
||||
.perf_sdio0_wait_o (s_perf_sdio0_wait),
|
||||
.perf_sdio1_wait_o (s_perf_sdio1_wait),
|
||||
.perf_apb4_periph_wait_o(s_perf_apb4_periph_wait),
|
||||
.perf_apb4_system_wait_o(s_perf_apb4_system_wait),
|
||||
.perf_sdram_wait_o (s_perf_sdram_wait),
|
||||
@@ -212,7 +219,8 @@ core_wrapper u_core_wrapper (
|
||||
.pll_ctrl (pll_ctrl),
|
||||
.sdram_cfg (u_sdram_cfg_if),
|
||||
.dvp (u_dvp_if),
|
||||
.sdio (u_sdio_if),
|
||||
.sdio0 (u_sdio0_if),
|
||||
.sdio1 (sdio1),
|
||||
.opipsram (u_opipsram_if),
|
||||
.i2c1 (u_i2c1_if),
|
||||
.fault_valid_i (s_bus_fault_valid),
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
// GPIO29 OPI_PSRAM_IO6 BIDI QSPI_PSRAM_NSS3 OUT
|
||||
// GPIO30 OPI_PSRAM_IO7 BIDI PWM_CAPTURE0 IN
|
||||
// GPIO31 OPI_PSRAM_DQS BIDI PWM_CAPTURE1 IN
|
||||
//
|
||||
// Dedicated SDIO1 pads: SDIO1_CLK, SDIO1_CMD, SDIO1_DAT[3:0].
|
||||
|
||||
module retrosoc_asic (
|
||||
`include "retrosoc_asic_ports.svh"
|
||||
@@ -84,6 +86,7 @@ module retrosoc_asic (
|
||||
gpio_if u_gpio_if ();
|
||||
xpi_if u_xpi_if ();
|
||||
sdram_if u_sdram_if ();
|
||||
sdio_if u_sdio1_if ();
|
||||
pll_ctrl_if u_pll_ctrl_if ();
|
||||
|
||||
`include "retrosoc_asic_pad_bindings.svh"
|
||||
@@ -130,6 +133,7 @@ rcu #(
|
||||
.uart_tx_o (s_uart0_tx),
|
||||
.xpi (u_xpi_if),
|
||||
.sdram (u_sdram_if),
|
||||
.sdio1 (u_sdio1_if),
|
||||
.jtag_tck_i (s_jtag_tck),
|
||||
.jtag_tms_i (s_jtag_tms),
|
||||
.jtag_tdi_i (s_jtag_tdi),
|
||||
|
||||
@@ -202,6 +202,18 @@
|
||||
"apb4_sdio.sv",
|
||||
"apb4_spisd.sv",
|
||||
"apb4_xpi.sv",
|
||||
"sdio_clock.sv",
|
||||
"sdio_command.sv",
|
||||
"sdio_core.sv",
|
||||
"sdio_crc16.sv",
|
||||
"sdio_crc7.sv",
|
||||
"sdio_data.sv",
|
||||
"sdio_define.svh",
|
||||
"sdio_dma.sv",
|
||||
"sdio_dma_descriptor.sv",
|
||||
"sdio_if.sv",
|
||||
"sdio_pkg.sv",
|
||||
"sdio_reg.sv",
|
||||
"spisd_cache.sv",
|
||||
"spisd_core.sv",
|
||||
"spisd_data.sv",
|
||||
|
||||
@@ -27,6 +27,7 @@ TEST_SOURCES = (
|
||||
"crt/src/hal/timer_math.c",
|
||||
"crt/src/hal/psram_math.c",
|
||||
"crt/src/hal/sdram_math.c",
|
||||
"crt/src/hal/sdio_math.c",
|
||||
"crt/src/hal/i2s_math.c",
|
||||
"crt/src/hal/dma_math.c",
|
||||
"crt/src/hal/sysctrl.c",
|
||||
|
||||
+85
-9
@@ -13,6 +13,7 @@
|
||||
#include <retrosoc/hal/i2s.h>
|
||||
#include <retrosoc/hal/psram.h>
|
||||
#include <retrosoc/hal/sdram.h>
|
||||
#include <retrosoc/hal/sdio.h>
|
||||
#include <retrosoc/hal/spisd.h>
|
||||
#include <retrosoc/hal/timer.h>
|
||||
#include <retrosoc/hal/uart.h>
|
||||
@@ -503,6 +504,81 @@ static int test_i2c_helpers(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sdio_helpers(void) {
|
||||
rs_sdio_clock_t clock;
|
||||
rs_sdio_response_t csd = {{0U, 0U, 0U, 0U, 0U}};
|
||||
rs_sdio_cmd52_t cmd52 = {
|
||||
.function = 2U,
|
||||
.address = UINT32_C(0x123),
|
||||
.write = true,
|
||||
.raw = false,
|
||||
.data = UINT8_C(0xA5),
|
||||
};
|
||||
rs_sdio_cmd53_t cmd53 = {
|
||||
.function = 1U,
|
||||
.address = UINT32_C(0x100),
|
||||
.count = UINT16_C(512),
|
||||
.write = true,
|
||||
.block_mode = true,
|
||||
.fixed_address = false,
|
||||
};
|
||||
rs_sdio_descriptor_t descriptor;
|
||||
rs_sd_memory_info_t info = {0};
|
||||
uint32_t value;
|
||||
|
||||
if ((rs_sdio_clock_calculate(72000000U, 400000U, &clock) != RS_OK) ||
|
||||
(clock.half_period != 90U) || (clock.actual_hz != 400000U) ||
|
||||
(rs_sdio_clock_calculate(100000000U, 50000000U, &clock) != RS_OK) ||
|
||||
(clock.half_period != 1U) || (clock.actual_hz != 50000000U)) {
|
||||
return 1;
|
||||
}
|
||||
if ((rs_sdio_clock_calculate(0U, 400000U, &clock) != RS_EINVAL) ||
|
||||
(rs_sdio_clock_calculate(72000000U, 0U, &clock) != RS_EINVAL) ||
|
||||
(rs_sdio_clock_calculate(72000000U, 40000001U, &clock) != RS_EINVAL) ||
|
||||
(rs_sdio_clock_calculate(72000000U, 400000U, NULL) != RS_EINVAL)) {
|
||||
return 2;
|
||||
}
|
||||
if ((rs_sd_memory_address(RS_SD_MEMORY_SDSC, 2U, &value) != RS_OK) || (value != 1024U) ||
|
||||
(rs_sd_memory_address(RS_SD_MEMORY_SDHC, 2U, &value) != RS_OK) || (value != 2U) ||
|
||||
(rs_sdio_cmd52_argument(&cmd52, &value) != RS_OK) || (value != UINT32_C(0xA00246A5)) ||
|
||||
(rs_sdio_cmd53_argument(&cmd53, &value) != RS_OK) || (value != UINT32_C(0x9C020000))) {
|
||||
return 3;
|
||||
}
|
||||
if ((rs_sdio_validate_dma_buffer((const void *)(uintptr_t)UINT32_C(0x40000000), 4U) != RS_OK) ||
|
||||
(rs_sdio_validate_dma_buffer((const void *)(uintptr_t)UINT32_C(0x40000002), 4U) !=
|
||||
RS_EINVAL) ||
|
||||
(rs_sdio_descriptor_prepare(&descriptor, (uintptr_t)UINT32_C(0x40000000), 64U,
|
||||
(uintptr_t)0U, true, true) != RS_OK) ||
|
||||
(rs_sdio_descriptor_validate(&descriptor) != RS_OK) ||
|
||||
(rs_sdio_descriptor_publish(&descriptor) != RS_OK) ||
|
||||
((descriptor.control_status & (UINT32_C(1) << RS_SDIO_DESC_OWN_BIT)) == 0U)) {
|
||||
return 4;
|
||||
}
|
||||
if (rs_sdio_descriptor_prepare(&descriptor, (uintptr_t)UINT32_C(0x40000000), 64U,
|
||||
(uintptr_t)UINT32_C(0x40000FF0), false, false) != RS_EINVAL) {
|
||||
return 5;
|
||||
}
|
||||
descriptor.buffer_addr = UINT32_C(0x40000000);
|
||||
descriptor.byte_count = 64U;
|
||||
descriptor.next_addr = UINT32_C(0x40000FF0);
|
||||
descriptor.control_status = UINT32_C(0x00000003);
|
||||
if (rs_sdio_descriptor_validate(&descriptor) != RS_EINVAL) {
|
||||
return 6;
|
||||
}
|
||||
for (uint32_t bit = 48U; bit <= 69U; bit++) {
|
||||
if (((UINT32_C(0x3FF) >> (bit - 48U)) & 1U) != 0U) {
|
||||
csd.words[bit / 32U] |= UINT32_C(1) << (bit % 32U);
|
||||
}
|
||||
}
|
||||
csd.words[3] |= UINT32_C(1) << (126U - 96U);
|
||||
if ((rs_sd_memory_parse_csd(&csd, &info) != RS_OK) || (info.card_type != RS_SD_MEMORY_SDHC) ||
|
||||
(info.capacity_blocks != UINT32_C(0x100000)) ||
|
||||
(info.block_length != RS_SD_MEMORY_BLOCK_SIZE)) {
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_gpio_helpers(void) {
|
||||
rs_gpio_filter_timing_t timing;
|
||||
|
||||
@@ -634,15 +710,15 @@ static int test_video_parser(void) {
|
||||
|
||||
int main(void) {
|
||||
const int results[] = {
|
||||
test_string_helpers(), test_formatter(),
|
||||
test_compiler_helpers(), test_wait_helper(),
|
||||
test_ws2812_helpers(), test_timer_helpers(),
|
||||
test_psram_helpers(), test_sdram_helpers(),
|
||||
test_uart_helpers(), test_i2s_helpers(),
|
||||
test_i2c_helpers(), test_gpio_helpers(),
|
||||
test_dma_config_validation(), test_opipsram_helpers(),
|
||||
test_ps2_decoders(), test_wav_parser(),
|
||||
test_video_parser(),
|
||||
test_string_helpers(), test_formatter(),
|
||||
test_compiler_helpers(), test_wait_helper(),
|
||||
test_ws2812_helpers(), test_timer_helpers(),
|
||||
test_psram_helpers(), test_sdram_helpers(),
|
||||
test_uart_helpers(), test_i2s_helpers(),
|
||||
test_i2c_helpers(), test_sdio_helpers(),
|
||||
test_gpio_helpers(), test_dma_config_validation(),
|
||||
test_opipsram_helpers(), test_ps2_decoders(),
|
||||
test_wav_parser(), test_video_parser(),
|
||||
};
|
||||
|
||||
for (size_t index = 0U; index < (sizeof(results) / sizeof(results[0])); ++index) {
|
||||
|
||||
@@ -16,7 +16,7 @@ module apb4_topology_tb;
|
||||
begin
|
||||
s_decode_addr = address;
|
||||
#1;
|
||||
if (s_psel_comb !== (18'd1 << slot)) begin
|
||||
if (s_psel_comb !== (19'd1 << slot)) begin
|
||||
$fatal(1, "unexpected APB4 target slot for %h: got %b", address, s_psel_comb);
|
||||
end
|
||||
end
|
||||
@@ -40,7 +40,10 @@ module apb4_topology_tb;
|
||||
expect_select(`SOC_ADDR_APB4_SDRAM_BASE, 13);
|
||||
expect_select(`SOC_ADDR_APB4_OPIPSRAM_BASE, 16);
|
||||
expect_select(`SOC_ADDR_APB4_I2C1_BASE, 17);
|
||||
expect_disabled(`SOC_ADDR_APB4_SDIO_BASE);
|
||||
expect_select(`SOC_ADDR_APB4_SDIO0_BASE, 15);
|
||||
expect_select(`SOC_ADDR_APB4_SDIO1_BASE, 18);
|
||||
expect_disabled(`SOC_ADDR_APB4_GA_BASE);
|
||||
expect_disabled(`SOC_ADDR_APB4_APU_BASE);
|
||||
$display("SoC topology APB4 routing test passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
`include "rib_defs.svh"
|
||||
|
||||
module axi4_interconnect_tb;
|
||||
localparam int NumMasters = 3;
|
||||
localparam int NumMasters = 5;
|
||||
localparam int NumTargets = 9;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
@@ -14,7 +14,7 @@ module axi4_interconnect_tb;
|
||||
logic [ 3:0] fault_wstrb_o;
|
||||
logic fault_reserved_o;
|
||||
logic fault_access_o;
|
||||
logic [ 1:0] fault_master_o;
|
||||
logic [ 2:0] fault_master_o;
|
||||
logic [ 2:0] fault_code_o;
|
||||
logic user_bus_idle_o;
|
||||
|
||||
@@ -62,11 +62,14 @@ module axi4_interconnect_tb;
|
||||
.perf_mgmt_wait_o (),
|
||||
.perf_user_wait_o (),
|
||||
.perf_dma_wait_o (),
|
||||
.perf_sdio0_wait_o (),
|
||||
.perf_sdio1_wait_o (),
|
||||
.perf_apb4_periph_wait_o(),
|
||||
.perf_apb4_system_wait_o(),
|
||||
.perf_sdram_wait_o (),
|
||||
.perf_psram_wait_o (),
|
||||
.perf_flash_wait_o ()
|
||||
.perf_flash_wait_o (),
|
||||
.perf_opipsram_wait_o ()
|
||||
);
|
||||
|
||||
for (genvar target = 0; target < NumTargets; target++) begin : GEN_TARGETS
|
||||
@@ -136,7 +139,7 @@ module axi4_interconnect_tb;
|
||||
end
|
||||
if ((beat == int'(length)) &&
|
||||
(!fault_valid_o || (fault_addr_o != address) || fault_access_o ||
|
||||
(fault_master_o != 2'd0) || (fault_code_o != fault_code))) begin
|
||||
(fault_master_o != 3'd0) || (fault_code_o != fault_code))) begin
|
||||
$fatal(1, "management fault classification mismatch");
|
||||
end
|
||||
beat = beat + 1;
|
||||
@@ -168,7 +171,7 @@ module axi4_interconnect_tb;
|
||||
#1;
|
||||
if ((masters[1].bresp != `AXI4_RESP_SLAVE_ERROR) || !fault_valid_o ||
|
||||
!fault_access_o || (fault_addr_o != address) || (fault_wstrb_o != 4'hF) ||
|
||||
(fault_master_o != 2'd1) || (fault_code_o != `RIB_RESP_PROTERR)) begin
|
||||
(fault_master_o != 3'd1) || (fault_code_o != `RIB_RESP_PROTERR)) begin
|
||||
$fatal(1, "user access fault classification mismatch");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
@@ -176,16 +179,95 @@ module axi4_interconnect_tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_contending_sdio_faults;
|
||||
integer cycles;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
masters[3].araddr = 32'hA000_1000;
|
||||
masters[3].arlen = 8'd0;
|
||||
masters[3].arvalid = 1'b1;
|
||||
masters[4].araddr = 32'hA000_2000;
|
||||
masters[4].arlen = 8'd0;
|
||||
masters[4].arvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!(masters[3].arready && masters[4].arready));
|
||||
@(negedge clk_i);
|
||||
masters[3].arvalid = 1'b0;
|
||||
masters[4].arvalid = 1'b0;
|
||||
cycles = 0;
|
||||
while (!masters[3].rvalid && !masters[4].rvalid && cycles < 100) begin
|
||||
@(negedge clk_i);
|
||||
cycles = cycles + 1;
|
||||
end
|
||||
if (cycles >= 100) $fatal(1, "SDIO master contention did not produce a response");
|
||||
if (masters[3].rvalid) begin
|
||||
masters[3].rready = 1'b1;
|
||||
#1;
|
||||
if ((masters[3].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
|
||||
(fault_master_o != 3'd3) || (fault_addr_o != 32'hA000_1000) ||
|
||||
(fault_code_o != `RIB_RESP_DECERR)) begin
|
||||
$fatal(1, "SDIO0 fault attribution mismatch");
|
||||
end
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
masters[3].rready = 1'b0;
|
||||
end else begin
|
||||
masters[4].rready = 1'b1;
|
||||
#1;
|
||||
if ((masters[4].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
|
||||
(fault_master_o != 3'd4) || (fault_addr_o != 32'hA000_2000) ||
|
||||
(fault_code_o != `RIB_RESP_DECERR)) begin
|
||||
$fatal(1, "SDIO1 fault attribution mismatch");
|
||||
end
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
masters[4].rready = 1'b0;
|
||||
end
|
||||
|
||||
cycles = 0;
|
||||
while (!masters[3].rvalid && !masters[4].rvalid && cycles < 100) begin
|
||||
@(negedge clk_i);
|
||||
cycles = cycles + 1;
|
||||
end
|
||||
if (cycles >= 100) $fatal(1, "second SDIO master response was lost");
|
||||
if (masters[3].rvalid) begin
|
||||
masters[3].rready = 1'b1;
|
||||
#1;
|
||||
if ((masters[3].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
|
||||
(fault_master_o != 3'd3) || (fault_addr_o != 32'hA000_1000) ||
|
||||
(fault_code_o != `RIB_RESP_DECERR)) begin
|
||||
$fatal(1, "second SDIO0 fault attribution mismatch");
|
||||
end
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
masters[3].rready = 1'b0;
|
||||
end else begin
|
||||
masters[4].rready = 1'b1;
|
||||
#1;
|
||||
if ((masters[4].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
|
||||
(fault_master_o != 3'd4) || (fault_addr_o != 32'hA000_2000) ||
|
||||
(fault_code_o != `RIB_RESP_DECERR)) begin
|
||||
$fatal(1, "second SDIO1 fault attribution mismatch");
|
||||
end
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
masters[4].rready = 1'b0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
`INIT_MASTER(0)
|
||||
`INIT_MASTER(1)
|
||||
`INIT_MASTER(2)
|
||||
`INIT_MASTER(3)
|
||||
`INIT_MASTER(4)
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
issue_mgmt_error_read(32'hA000_0000, 8'd0, `AXI4_RESP_DECODE_ERROR, `RIB_RESP_DECERR);
|
||||
issue_mgmt_error_read(32'h3000_0000, 8'd16, `AXI4_RESP_SLAVE_ERROR, `RIB_RESP_BURSTERR);
|
||||
issue_denied_user_write(32'h1000_B000);
|
||||
issue_contending_sdio_faults();
|
||||
|
||||
$display("AXI4 interconnect fault classification test passed");
|
||||
$finish;
|
||||
|
||||
@@ -154,7 +154,7 @@ module bus_fault_tb;
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
expect_fault(32'h1000_F000, 4'hF, 1'b1, `RIB_RESP_RESERVED);
|
||||
expect_fault(32'h1001_2000, 4'hF, 1'b1, `RIB_RESP_RESERVED);
|
||||
expect_fault(32'hA000_0000, 4'h0, 1'b0, `RIB_RESP_DECERR);
|
||||
expect_user_denied(32'h1000_B000, 4'hF);
|
||||
$display("bus fault responder test passed");
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_50mhz_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic enable_i = 1'b0;
|
||||
logic [15:0] half_period_i = 16'd1;
|
||||
logic sck_o;
|
||||
logic launch_tick_o;
|
||||
logic sample_tick_o;
|
||||
logic running_o;
|
||||
integer rising_count;
|
||||
integer falling_count;
|
||||
logic previous_sck;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
sdio_clock u_sdio_clock (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (enable_i),
|
||||
.half_period_i(half_period_i),
|
||||
.sck_o (sck_o),
|
||||
.launch_tick_o(launch_tick_o),
|
||||
.sample_tick_o(sample_tick_o),
|
||||
.running_o (running_o)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_n_i && (sck_o != previous_sck)) begin
|
||||
if (sck_o) begin
|
||||
rising_count = rising_count + 1;
|
||||
end else begin
|
||||
falling_count = falling_count + 1;
|
||||
end
|
||||
end
|
||||
previous_sck = sck_o;
|
||||
end
|
||||
|
||||
initial begin
|
||||
rising_count = 0;
|
||||
falling_count = 0;
|
||||
previous_sck = 1'b0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
enable_i = 1'b1;
|
||||
repeat (12) @(posedge clk_i);
|
||||
if ((rising_count < 5) || (falling_count < 5) ||
|
||||
(rising_count > falling_count + 1) ||
|
||||
(falling_count > rising_count + 1) || !running_o) begin
|
||||
$fatal(1, "50 MHz phase count mismatch rise=%0d fall=%0d running=%b", rising_count,
|
||||
falling_count, running_o);
|
||||
end
|
||||
enable_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
if (sck_o || running_o) begin
|
||||
$fatal(1, "50 MHz clock did not stop low");
|
||||
end
|
||||
$display("SDIO 100 MHz to 50 MHz SDCLK test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
`include "axi4_define.svh"
|
||||
|
||||
// Deterministic AXI4 memory target for SDIO standalone verification.
|
||||
// It models the actual Mini-fabric contract: one transaction per direction,
|
||||
// 32-bit data, INCR/FIXED bursts, and no outstanding IDs.
|
||||
module sdio_axi_memory_responder #(
|
||||
parameter int DepthWords = 16384
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
axi4_if.slave axi4
|
||||
);
|
||||
logic [31:0] s_memory [0:DepthWords-1];
|
||||
|
||||
logic s_read_active_q;
|
||||
logic [31:0] s_read_addr_q;
|
||||
logic [ 7:0] s_read_len_q;
|
||||
logic [ 7:0] s_read_index_q;
|
||||
logic s_read_error_q;
|
||||
logic s_read_bad_id_q;
|
||||
logic s_read_bad_last_q;
|
||||
logic s_write_active_q;
|
||||
logic [31:0] s_write_addr_q;
|
||||
logic [ 7:0] s_write_len_q;
|
||||
logic [ 7:0] s_write_index_q;
|
||||
logic s_write_error_q;
|
||||
logic s_write_bad_id_q;
|
||||
logic s_aw_stall_q;
|
||||
logic s_ar_stall_q;
|
||||
logic s_w_stall_q;
|
||||
logic s_b_stall_q;
|
||||
integer s_read_error_address;
|
||||
integer s_write_error_address;
|
||||
integer s_aw_stall_cycles;
|
||||
integer s_ar_stall_cycles;
|
||||
integer s_w_stall_cycles;
|
||||
integer s_b_stall_cycles;
|
||||
|
||||
integer read_burst_count;
|
||||
integer write_burst_count;
|
||||
integer max_read_beats;
|
||||
integer max_write_beats;
|
||||
|
||||
function automatic integer word_index(input logic [31:0] address_i);
|
||||
integer index;
|
||||
begin
|
||||
index = address_i >> 2;
|
||||
if ((index < 0) || (index >= DepthWords)) begin
|
||||
index = 0;
|
||||
end
|
||||
return index;
|
||||
end
|
||||
endfunction
|
||||
|
||||
task automatic clear_memory;
|
||||
begin
|
||||
for (integer index = 0; index < DepthWords; index++) begin
|
||||
s_memory[index] = 32'd0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic write_word(input logic [31:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
s_memory[word_index(address_i)] = data_i;
|
||||
end
|
||||
endtask
|
||||
|
||||
function automatic logic [31:0] read_word(input logic [31:0] address_i);
|
||||
begin
|
||||
read_word = s_memory[word_index(address_i)];
|
||||
end
|
||||
endfunction
|
||||
|
||||
task automatic set_backpressure(input integer aw_cycles_i, input integer ar_cycles_i,
|
||||
input integer w_cycles_i, input integer b_cycles_i);
|
||||
begin
|
||||
s_aw_stall_cycles = (aw_cycles_i < 0) ? 0 : aw_cycles_i;
|
||||
s_ar_stall_cycles = (ar_cycles_i < 0) ? 0 : ar_cycles_i;
|
||||
s_w_stall_cycles = (w_cycles_i < 0) ? 0 : w_cycles_i;
|
||||
s_b_stall_cycles = (b_cycles_i < 0) ? 0 : b_cycles_i;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_read_error(input integer address_i);
|
||||
begin
|
||||
s_read_error_address = address_i;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_write_error(input integer address_i);
|
||||
begin
|
||||
s_write_error_address = address_i;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_read_bad_id;
|
||||
begin
|
||||
s_read_bad_id_q = 1'b1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_read_bad_last;
|
||||
begin
|
||||
s_read_bad_last_q = 1'b1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_write_bad_id;
|
||||
begin
|
||||
s_write_bad_id_q = 1'b1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic clear_errors;
|
||||
begin
|
||||
s_read_error_address = -1;
|
||||
s_write_error_address = -1;
|
||||
s_read_bad_id_q = 1'b0;
|
||||
s_write_bad_id_q = 1'b0;
|
||||
s_read_bad_last_q = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
assign axi4.arready = !s_read_active_q && !s_ar_stall_q;
|
||||
assign axi4.rid = s_read_bad_id_q ? '1 : '0;
|
||||
assign axi4.rdata = s_memory[word_index(
|
||||
s_read_addr_q+((axi4.arburst==`AXI4_BURST_TYPE_FIXED)?32'd0 : ({24'd0, s_read_index_q}<<2))
|
||||
)];
|
||||
assign axi4.rresp = (s_read_error_q) ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY;
|
||||
assign axi4.rlast = s_read_bad_last_q || ((s_read_index_q + 1'b1) >= (s_read_len_q + 1'b1));
|
||||
assign axi4.ruser = '0;
|
||||
assign axi4.rvalid = s_read_active_q;
|
||||
|
||||
assign axi4.awready = !s_write_active_q && !axi4.bvalid && !s_aw_stall_q;
|
||||
assign axi4.wready = s_write_active_q && !s_w_stall_q;
|
||||
assign axi4.bid = s_write_bad_id_q ? '1 : '0;
|
||||
assign axi4.bresp = s_write_error_q ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY;
|
||||
assign axi4.buser = '0;
|
||||
assign axi4.bvalid = s_write_active_q && !s_b_stall_q &&
|
||||
(s_write_index_q >= s_write_len_q + 1'b1);
|
||||
|
||||
always @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_read_active_q <= 1'b0;
|
||||
s_read_addr_q <= 32'd0;
|
||||
s_read_len_q <= 8'd0;
|
||||
s_read_index_q <= 8'd0;
|
||||
s_read_error_q <= 1'b0;
|
||||
s_read_bad_id_q <= 1'b0;
|
||||
s_read_bad_last_q <= 1'b0;
|
||||
s_write_active_q <= 1'b0;
|
||||
s_write_addr_q <= 32'd0;
|
||||
s_write_len_q <= 8'd0;
|
||||
s_write_index_q <= 8'd0;
|
||||
s_write_error_q <= 1'b0;
|
||||
s_write_bad_id_q <= 1'b0;
|
||||
s_aw_stall_q <= 1'b0;
|
||||
s_ar_stall_q <= 1'b0;
|
||||
s_w_stall_q <= 1'b0;
|
||||
s_b_stall_q <= 1'b0;
|
||||
s_read_error_address = -1;
|
||||
s_write_error_address = -1;
|
||||
s_aw_stall_cycles = 0;
|
||||
s_ar_stall_cycles = 0;
|
||||
s_w_stall_cycles = 0;
|
||||
s_b_stall_cycles = 0;
|
||||
read_burst_count = 0;
|
||||
write_burst_count = 0;
|
||||
max_read_beats = 0;
|
||||
max_write_beats = 0;
|
||||
end else begin
|
||||
if (s_aw_stall_cycles > 0) begin
|
||||
s_aw_stall_cycles = s_aw_stall_cycles - 1;
|
||||
s_aw_stall_q = 1'b1;
|
||||
end else begin
|
||||
s_aw_stall_q = 1'b0;
|
||||
end
|
||||
if (s_ar_stall_cycles > 0) begin
|
||||
s_ar_stall_cycles = s_ar_stall_cycles - 1;
|
||||
s_ar_stall_q = 1'b1;
|
||||
end else begin
|
||||
s_ar_stall_q = 1'b0;
|
||||
end
|
||||
if (s_w_stall_cycles > 0) begin
|
||||
s_w_stall_cycles = s_w_stall_cycles - 1;
|
||||
s_w_stall_q = 1'b1;
|
||||
end else begin
|
||||
s_w_stall_q = 1'b0;
|
||||
end
|
||||
if (s_b_stall_cycles > 0) begin
|
||||
s_b_stall_cycles = s_b_stall_cycles - 1;
|
||||
s_b_stall_q = 1'b1;
|
||||
end else begin
|
||||
s_b_stall_q = 1'b0;
|
||||
end
|
||||
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
s_read_active_q = 1'b1;
|
||||
s_read_addr_q = axi4.araddr;
|
||||
s_read_len_q = axi4.arlen;
|
||||
s_read_index_q = 8'd0;
|
||||
s_read_error_q = (s_read_error_address >= 0) && (axi4.araddr == s_read_error_address);
|
||||
s_read_bad_last_q = 1'b0;
|
||||
read_burst_count = read_burst_count + 1;
|
||||
if (({24'd0, axi4.arlen} + 32'd1) > max_read_beats) begin
|
||||
max_read_beats = {24'd0, axi4.arlen} + 32'd1;
|
||||
end
|
||||
end
|
||||
if (axi4.rvalid && axi4.rready) begin
|
||||
if (axi4.rlast) begin
|
||||
s_read_active_q = 1'b0;
|
||||
if (s_read_error_q) begin
|
||||
s_read_error_address = -1;
|
||||
end
|
||||
end else begin
|
||||
s_read_index_q = s_read_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
if (axi4.awvalid && axi4.awready) begin
|
||||
s_write_active_q = 1'b1;
|
||||
s_write_addr_q = axi4.awaddr;
|
||||
s_write_len_q = axi4.awlen;
|
||||
s_write_index_q = 8'd0;
|
||||
s_write_error_q = (s_write_error_address >= 0) && (axi4.awaddr == s_write_error_address);
|
||||
write_burst_count = write_burst_count + 1;
|
||||
if (({24'd0, axi4.awlen} + 32'd1) > max_write_beats) begin
|
||||
max_write_beats = {24'd0, axi4.awlen} + 32'd1;
|
||||
end
|
||||
end
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (axi4.wstrb[byte_index]) begin
|
||||
s_memory[word_index(s_write_addr_q + ((axi4.awburst == `AXI4_BURST_TYPE_FIXED)
|
||||
? 32'd0
|
||||
: ({24'd0, s_write_index_q} << 2)))][byte_index*8+:8] <=
|
||||
axi4.wdata[byte_index*8+:8];
|
||||
end
|
||||
end
|
||||
s_write_index_q = s_write_index_q + 1'b1;
|
||||
end
|
||||
if (axi4.bvalid && axi4.bready) begin
|
||||
s_write_active_q = 1'b0;
|
||||
if (s_write_error_q) begin
|
||||
s_write_error_address = -1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
clear_memory();
|
||||
clear_errors();
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,52 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_clock_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic enable_i = 1'b0;
|
||||
logic [15:0] half_period_i = 16'd2;
|
||||
logic sck_o;
|
||||
logic launch_tick_o;
|
||||
logic sample_tick_o;
|
||||
logic running_o;
|
||||
integer launch_count;
|
||||
integer sample_count;
|
||||
|
||||
always #1 clk_i = ~clk_i;
|
||||
|
||||
sdio_clock u_sdio_clock (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (enable_i),
|
||||
.half_period_i(half_period_i),
|
||||
.sck_o (sck_o),
|
||||
.launch_tick_o(launch_tick_o),
|
||||
.sample_tick_o(sample_tick_o),
|
||||
.running_o (running_o)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (launch_tick_o) launch_count = launch_count + 1;
|
||||
if (sample_tick_o) sample_count = sample_count + 1;
|
||||
end
|
||||
|
||||
initial begin
|
||||
launch_count = 0;
|
||||
sample_count = 0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
enable_i = 1'b1;
|
||||
repeat (8) @(posedge clk_i);
|
||||
if ((launch_count != 2) || (sample_count != 2) || !running_o) begin
|
||||
$fatal(1, "phase counts mismatch: launch=%0d sample=%0d running=%b", launch_count,
|
||||
sample_count, running_o);
|
||||
end
|
||||
enable_i = 1'b0;
|
||||
repeat (4) @(posedge clk_i);
|
||||
if (sck_o || running_o) begin
|
||||
$fatal(1, "clock did not stop on a complete low phase");
|
||||
end
|
||||
$display("SDIO phase clock test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,121 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_command_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic clock_enable_i = 1'b0;
|
||||
logic [ 15:0] half_period_i = 16'd1;
|
||||
logic launch_tick;
|
||||
logic sample_tick;
|
||||
logic sck;
|
||||
logic running;
|
||||
logic start_i = 1'b0;
|
||||
logic [ 5:0] cmd_index_i = 6'd0;
|
||||
logic [ 31:0] cmd_arg_i = '0;
|
||||
sdio_pkg::sdio_resp_type_e resp_type_i = sdio_pkg::SdioRespNone;
|
||||
logic crc_check_i = 1'b0;
|
||||
logic index_check_i = 1'b0;
|
||||
logic [ 31:0] timeout_cycles_i = 32'd1000;
|
||||
logic cmd_di_i = 1'b1;
|
||||
logic dat0_i = 1'b1;
|
||||
logic cmd_oe_o;
|
||||
logic cmd_do_o;
|
||||
logic busy_o;
|
||||
logic done_o;
|
||||
logic error_o;
|
||||
logic timeout_o;
|
||||
logic crc_error_o;
|
||||
logic index_error_o;
|
||||
logic busy_timeout_o;
|
||||
logic [135:0] response_o;
|
||||
logic [ 5:0] last_cmd_index_o;
|
||||
integer launch_count;
|
||||
integer captured_bits;
|
||||
logic [ 47:0] captured_command;
|
||||
|
||||
always #1 clk_i = ~clk_i;
|
||||
|
||||
sdio_clock u_sdio_clock (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (clock_enable_i),
|
||||
.half_period_i(half_period_i),
|
||||
.sck_o (sck),
|
||||
.launch_tick_o(launch_tick),
|
||||
.sample_tick_o(sample_tick),
|
||||
.running_o (running)
|
||||
);
|
||||
sdio_command u_sdio_command (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.launch_tick_i (launch_tick),
|
||||
.sample_tick_i (sample_tick),
|
||||
.start_i (start_i),
|
||||
.cmd_index_i (cmd_index_i),
|
||||
.cmd_arg_i (cmd_arg_i),
|
||||
.resp_type_i (resp_type_i),
|
||||
.crc_check_i (crc_check_i),
|
||||
.index_check_i (index_check_i),
|
||||
.timeout_cycles_i(timeout_cycles_i),
|
||||
.cmd_di_i (cmd_di_i),
|
||||
.dat0_i (dat0_i),
|
||||
.cmd_oe_o (cmd_oe_o),
|
||||
.cmd_do_o (cmd_do_o),
|
||||
.busy_o (busy_o),
|
||||
.done_o (done_o),
|
||||
.error_o (error_o),
|
||||
.timeout_o (timeout_o),
|
||||
.crc_error_o (crc_error_o),
|
||||
.index_error_o (index_error_o),
|
||||
.busy_timeout_o (busy_timeout_o),
|
||||
.response_o (response_o),
|
||||
.last_cmd_index_o(last_cmd_index_o)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (launch_tick) launch_count = launch_count + 1;
|
||||
end
|
||||
|
||||
always @(posedge sck) begin
|
||||
if (cmd_oe_o) begin
|
||||
captured_command = {captured_command[46:0], cmd_do_o};
|
||||
captured_bits = captured_bits + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
launch_count = 0;
|
||||
captured_bits = 0;
|
||||
captured_command = '0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
clock_enable_i = 1'b1;
|
||||
wait (done_o);
|
||||
if (error_o || (launch_count != 48) || (captured_bits != 48) ||
|
||||
(captured_command[47:46] != 2'b01) || (captured_command[0] != 1'b1) ||
|
||||
(captured_command[7:1] != sdio_pkg::sdio_crc7_calc(
|
||||
captured_command[47:8]
|
||||
)) || (last_cmd_index_o != 6'd0) || cmd_oe_o) begin
|
||||
$fatal(1, "no-response command frame failed: err=%b launches=%0d captured=%0d frame=%h oe=%b",
|
||||
error_o, launch_count, captured_bits, captured_command, cmd_oe_o);
|
||||
end
|
||||
|
||||
@(posedge clk_i);
|
||||
resp_type_i = sdio_pkg::SdioRespR1;
|
||||
timeout_cycles_i = 32'd4;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
wait (done_o);
|
||||
if (!error_o || !timeout_o) begin
|
||||
$fatal(1, "response timeout was not reported");
|
||||
end
|
||||
$display("SDIO command engine test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,66 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_crc_tb;
|
||||
logic [39:0] crc7_data;
|
||||
logic [ 6:0] crc7_value;
|
||||
logic [15:0] crc16_value;
|
||||
logic [15:0] crc16_seed;
|
||||
logic [ 7:0] crc16_data;
|
||||
|
||||
sdio_crc7 u_sdio_crc7 (
|
||||
.data_i(crc7_data),
|
||||
.crc_o (crc7_value)
|
||||
);
|
||||
sdio_crc16 u_sdio_crc16 (
|
||||
.crc_i (crc16_seed),
|
||||
.data_i(crc16_data),
|
||||
.crc_o (crc16_value)
|
||||
);
|
||||
|
||||
initial begin
|
||||
crc7_data = {1'b0, 1'b1, 6'd0, 32'd0};
|
||||
#1;
|
||||
if (crc7_value != 7'h4A) begin
|
||||
$fatal(1, "CMD0 CRC7 mismatch: %h", crc7_value);
|
||||
end
|
||||
|
||||
crc7_data = {1'b0, 1'b1, 6'd8, 32'h0000_01AA};
|
||||
#1;
|
||||
if (crc7_value != 7'h43) begin
|
||||
$fatal(1, "CMD8 CRC7 mismatch: %h", crc7_value);
|
||||
end
|
||||
|
||||
crc16_seed = 16'h0000;
|
||||
crc16_data = "1";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "2";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "3";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "4";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "5";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "6";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "7";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "8";
|
||||
#1;
|
||||
crc16_seed = crc16_value;
|
||||
crc16_data = "9";
|
||||
#1;
|
||||
if (crc16_value != 16'h31C3) begin
|
||||
$fatal(1, "CRC16 mismatch: %h", crc16_value);
|
||||
end
|
||||
$display("SDIO CRC vectors passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,68 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_descriptor_tb;
|
||||
logic [31:0] buffer_addr_i = 32'h4000_0000;
|
||||
logic [31:0] byte_count_i = 32'd16;
|
||||
logic [31:0] next_addr_i = '0;
|
||||
logic [31:0] control_status_i = 32'h0001_0005;
|
||||
logic [15:0] desc_index_i = 16'd0;
|
||||
logic own_o;
|
||||
logic chain_o;
|
||||
logic end_o;
|
||||
logic irq_o;
|
||||
logic valid_o;
|
||||
logic error_o;
|
||||
logic address_error_o;
|
||||
logic length_error_o;
|
||||
logic chain_error_o;
|
||||
|
||||
sdio_dma_descriptor #(
|
||||
.DescCount(4)
|
||||
) u_sdio_dma_descriptor (
|
||||
.buffer_addr_i (buffer_addr_i),
|
||||
.byte_count_i (byte_count_i),
|
||||
.next_addr_i (next_addr_i),
|
||||
.control_status_i(control_status_i),
|
||||
.desc_index_i (desc_index_i),
|
||||
.own_o (own_o),
|
||||
.chain_o (chain_o),
|
||||
.end_o (end_o),
|
||||
.irq_o (irq_o),
|
||||
.valid_o (valid_o),
|
||||
.error_o (error_o),
|
||||
.address_error_o (address_error_o),
|
||||
.length_error_o (length_error_o),
|
||||
.chain_error_o (chain_error_o)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (!valid_o || error_o || !own_o || !end_o) begin
|
||||
$fatal(1, "valid terminal descriptor rejected");
|
||||
end
|
||||
control_status_i = 32'h0000_0003;
|
||||
next_addr_i = 32'h4000_0100;
|
||||
#1;
|
||||
if (!valid_o || error_o || !chain_o || end_o) begin
|
||||
$fatal(1, "valid chained descriptor rejected");
|
||||
end
|
||||
next_addr_i = 32'h4000_0FF0;
|
||||
#1;
|
||||
if (!error_o || !address_error_o) $fatal(1, "crossing descriptor chain not rejected");
|
||||
buffer_addr_i = 32'h4000_0002;
|
||||
#1;
|
||||
if (!error_o || !address_error_o) $fatal(1, "unaligned buffer not rejected");
|
||||
buffer_addr_i = 32'h4000_0000;
|
||||
byte_count_i = 32'd0;
|
||||
#1;
|
||||
if (!error_o || !length_error_o) $fatal(1, "zero length not rejected");
|
||||
byte_count_i = 32'd16;
|
||||
control_status_i = 32'h0000_0001;
|
||||
next_addr_i = 32'h4000_0100;
|
||||
desc_index_i = 16'd3;
|
||||
#1;
|
||||
if (!error_o || !chain_error_o) $fatal(1, "chain bound not rejected");
|
||||
$display("SDIO descriptor validation test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module sdio_dma_stress_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic start_i = 1'b0;
|
||||
logic abort_i = 1'b0;
|
||||
logic direction_i = 1'b1;
|
||||
logic [31:0] desc_base_i = 32'd0;
|
||||
logic [15:0] desc_count_i = 16'd1;
|
||||
logic [31:0] total_bytes_i = 32'd0;
|
||||
logic data_in_valid_i = 1'b0;
|
||||
logic data_in_ready_o;
|
||||
logic [31:0] data_in_i = 32'd0;
|
||||
logic [ 3:0] data_in_strb_i = 4'hF;
|
||||
logic data_in_last_i = 1'b0;
|
||||
logic data_out_valid_o;
|
||||
logic data_out_ready_i = 1'b1;
|
||||
logic [31:0] data_out_o;
|
||||
logic [ 3:0] data_out_strb_o;
|
||||
logic data_out_last_o;
|
||||
logic busy_o;
|
||||
logic done_o;
|
||||
logic error_o;
|
||||
logic [ 7:0] error_code_o;
|
||||
logic [31:0] current_desc_o;
|
||||
logic [31:0] bytes_done_o;
|
||||
logic [31:0] error_addr_o;
|
||||
integer output_count;
|
||||
integer guard;
|
||||
logic done_seen;
|
||||
integer abort_read_bursts;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
sdio_axi_memory_responder #(
|
||||
.DepthWords(16384)
|
||||
) u_memory (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
sdio_dma u_sdio_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (start_i),
|
||||
.abort_i (abort_i),
|
||||
.direction_i (direction_i),
|
||||
.desc_base_i (desc_base_i),
|
||||
.desc_count_i (desc_count_i),
|
||||
.total_bytes_i (total_bytes_i),
|
||||
.data_in_valid_i (data_in_valid_i),
|
||||
.data_in_ready_o (data_in_ready_o),
|
||||
.data_in_i (data_in_i),
|
||||
.data_in_strb_i (data_in_strb_i),
|
||||
.data_in_last_i (data_in_last_i),
|
||||
.data_out_valid_o(data_out_valid_o),
|
||||
.data_out_ready_i(data_out_ready_i),
|
||||
.data_out_o (data_out_o),
|
||||
.data_out_strb_o (data_out_strb_o),
|
||||
.data_out_last_o (data_out_last_o),
|
||||
.busy_o (busy_o),
|
||||
.done_o (done_o),
|
||||
.error_o (error_o),
|
||||
.error_code_o (error_code_o),
|
||||
.current_desc_o (current_desc_o),
|
||||
.bytes_done_o (bytes_done_o),
|
||||
.error_addr_o (error_addr_o),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (data_out_valid_o && data_out_ready_i) begin
|
||||
output_count = output_count + 1;
|
||||
end
|
||||
if (done_o) begin
|
||||
done_seen = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
task automatic pulse_start;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_done;
|
||||
logic seen_busy;
|
||||
begin
|
||||
guard = 0;
|
||||
seen_busy = busy_o;
|
||||
while ((!seen_busy || !done_o) && (guard < 200000)) begin
|
||||
@(posedge clk_i);
|
||||
guard = guard + 1;
|
||||
seen_busy = seen_busy || busy_o;
|
||||
end
|
||||
if (guard >= 200000) begin
|
||||
$fatal(1, "SDIO DMA stress transfer did not complete");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic clear_run_state;
|
||||
begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
start_i = 1'b0;
|
||||
abort_i = 1'b0;
|
||||
direction_i = 1'b1;
|
||||
data_out_ready_i = 1'b1;
|
||||
data_in_valid_i = 1'b0;
|
||||
output_count = 0;
|
||||
done_seen = 1'b0;
|
||||
u_memory.clear_errors();
|
||||
u_memory.set_backpressure(0, 0, 0, 0);
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
u_memory.clear_memory();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0100);
|
||||
u_memory.write_word(32'h0000_0004, 32'd64);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
for (integer index = 0; index < 16; index++) begin
|
||||
u_memory.write_word(32'h0000_0100 + (index * 4), 32'h1000_0000 + index);
|
||||
end
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
done_seen = 1'b0;
|
||||
|
||||
// A full 16-beat payload burst under independent channel backpressure.
|
||||
total_bytes_i = 32'd64;
|
||||
$display("DMA STRESS full burst");
|
||||
u_memory.set_backpressure(2, 3, 2, 2);
|
||||
pulse_start();
|
||||
wait_done();
|
||||
if (error_o || (output_count != 16) || (u_memory.max_read_beats != 16)) begin
|
||||
$fatal(1, "16-beat DMA contract failed err=%b outputs=%0d max=%0d", error_o, output_count,
|
||||
u_memory.max_read_beats);
|
||||
end
|
||||
|
||||
// The same payload geometry split at the 4 KiB boundary.
|
||||
clear_run_state();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0FFC);
|
||||
u_memory.write_word(32'h0000_0004, 32'd8);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
u_memory.write_word(32'h0000_0FFC, 32'hAAAA_0001);
|
||||
u_memory.write_word(32'h0000_1000, 32'hAAAA_0002);
|
||||
total_bytes_i = 32'd8;
|
||||
$display("DMA STRESS split");
|
||||
pulse_start();
|
||||
wait_done();
|
||||
if (error_o || (u_memory.read_burst_count < 3)) begin
|
||||
$fatal(1, "4 KiB split DMA contract failed err=%b bursts=%0d", error_o,
|
||||
u_memory.read_burst_count);
|
||||
end
|
||||
|
||||
// A bounded two-descriptor chain must write DONE/clear OWN on both
|
||||
// descriptors before the terminal event.
|
||||
clear_run_state();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0100);
|
||||
u_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_memory.write_word(32'h0000_0008, 32'h0000_0020);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0003);
|
||||
u_memory.write_word(32'h0000_0020, 32'h0000_0110);
|
||||
u_memory.write_word(32'h0000_0024, 32'd4);
|
||||
u_memory.write_word(32'h0000_0028, 32'd0);
|
||||
u_memory.write_word(32'h0000_002C, 32'h0000_0005);
|
||||
u_memory.write_word(32'h0000_0100, 32'h1111_0001);
|
||||
u_memory.write_word(32'h0000_0110, 32'h2222_0002);
|
||||
desc_count_i = 16'd2;
|
||||
total_bytes_i = 32'd8;
|
||||
pulse_start();
|
||||
wait_done();
|
||||
if (error_o || (output_count != 2) || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0001_0002) || (u_memory.read_word(
|
||||
32'h0000_002C
|
||||
) != 32'h0001_0004)) begin
|
||||
$fatal(1, "descriptor chain/writeback contract failed");
|
||||
end
|
||||
|
||||
// A target read error is converted into descriptor ERROR writeback.
|
||||
clear_run_state();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0200);
|
||||
u_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
u_memory.inject_read_error(32'h0000_0200);
|
||||
total_bytes_i = 32'd4;
|
||||
$display("DMA STRESS error");
|
||||
pulse_start();
|
||||
wait_done();
|
||||
if (!error_o || (u_memory.read_word(32'h0000_000C) != 32'h0002_0004)) begin
|
||||
$fatal(1, "AXI read error did not reach descriptor writeback");
|
||||
end
|
||||
|
||||
// Abort with a stalled stream drains the accepted AXI response and
|
||||
// terminates through the same bounded writeback path.
|
||||
clear_run_state();
|
||||
u_memory.clear_memory();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0300);
|
||||
u_memory.write_word(32'h0000_0004, 32'd128);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
total_bytes_i = 32'd128;
|
||||
$display("DMA STRESS abort");
|
||||
data_out_ready_i = 1'b0;
|
||||
pulse_start();
|
||||
wait (u_sdio_dma.s_state_q == 4'd5);
|
||||
wait (u_sdio_dma.s_read_busy);
|
||||
@(posedge clk_i);
|
||||
abort_read_bursts = u_memory.read_burst_count;
|
||||
repeat (5) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
repeat (4) @(posedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
if (!done_seen) begin
|
||||
wait_done();
|
||||
end
|
||||
if (!error_o) begin
|
||||
$fatal(1, "DMA abort did not drain/write back error");
|
||||
end
|
||||
if (busy_o || (u_memory.read_burst_count != abort_read_bursts) || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0002_0004)) begin
|
||||
$fatal(1, "DMA abort left an accepted AXI read active or issued a new request");
|
||||
end
|
||||
|
||||
// Abort during descriptor fetch still drains the accepted four-beat read
|
||||
// and writes ERROR/clears OWN on the fetched active descriptor.
|
||||
clear_run_state();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0400);
|
||||
u_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
total_bytes_i = 32'd4;
|
||||
u_memory.set_backpressure(0, 3, 0, 0);
|
||||
pulse_start();
|
||||
wait (u_sdio_dma.s_state_q == 4'd2);
|
||||
wait (u_sdio_dma.s_read_busy);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
wait_done();
|
||||
if (!error_o || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0002_0004) || u_memory.s_read_active_q) begin
|
||||
$fatal(1, "descriptor-fetch abort did not drain/write back correctly");
|
||||
end
|
||||
|
||||
// Abort while an AXI write response is stalled must wait for B before
|
||||
// terminalizing the descriptor.
|
||||
clear_run_state();
|
||||
direction_i = 1'b0;
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0500);
|
||||
u_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
total_bytes_i = 32'd4;
|
||||
u_memory.set_backpressure(0, 0, 0, 5);
|
||||
pulse_start();
|
||||
wait (u_sdio_dma.s_state_q == 4'd7);
|
||||
data_in_i = 32'hDDCC_BBAA;
|
||||
data_in_strb_i = 4'hF;
|
||||
data_in_last_i = 1'b1;
|
||||
data_in_valid_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
data_in_valid_i = 1'b0;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
wait_done();
|
||||
if (!error_o || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0002_0004) || u_memory.s_write_active_q) begin
|
||||
$fatal(1, "write-response abort did not drain/write back correctly");
|
||||
end
|
||||
|
||||
$display("SDIO AXI 16-beat, 4 KiB split, error, and abort test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,324 @@
|
||||
`timescale 1ns / 1ps
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module sdio_dma_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic start_i = 1'b0;
|
||||
logic abort_i = 1'b0;
|
||||
logic direction_i = 1'b0;
|
||||
logic [31:0] desc_base_i = 32'd0;
|
||||
logic [15:0] desc_count_i = 16'd1;
|
||||
logic [31:0] total_bytes_i = 32'd5;
|
||||
logic data_in_valid_i = 1'b0;
|
||||
logic data_in_ready_o;
|
||||
logic [31:0] data_in_i = '0;
|
||||
logic [ 3:0] data_in_strb_i = '0;
|
||||
logic data_in_last_i = 1'b0;
|
||||
logic data_out_valid_o;
|
||||
logic data_out_ready_i = 1'b1;
|
||||
logic [31:0] data_out_o;
|
||||
logic [ 3:0] data_out_strb_o;
|
||||
logic data_out_last_o;
|
||||
logic busy_o;
|
||||
logic done_o;
|
||||
logic error_o;
|
||||
logic [ 7:0] error_code_o;
|
||||
logic [31:0] current_desc_o;
|
||||
logic [31:0] bytes_done_o;
|
||||
logic [31:0] error_addr_o;
|
||||
logic [31:0] memory [0:2047];
|
||||
logic read_active_q = 1'b0;
|
||||
logic [31:0] read_addr_q = '0;
|
||||
logic [ 4:0] read_beats_q = '0;
|
||||
logic [ 4:0] read_index_q = '0;
|
||||
logic write_active_q = 1'b0;
|
||||
logic [31:0] write_addr_q = '0;
|
||||
logic write_response_q = 1'b0;
|
||||
integer read_bursts;
|
||||
integer max_read_beats;
|
||||
integer output_count;
|
||||
logic descriptor_irq_seen;
|
||||
logic [31:0] output_data [ 0:3];
|
||||
logic [ 3:0] output_strb [ 0:3];
|
||||
logic output_last [ 0:3];
|
||||
logic error_mode = 1'b0;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign dma_axi4.arready = !read_active_q;
|
||||
assign dma_axi4.rid = 1'b0;
|
||||
assign dma_axi4.rdata = memory[(read_addr_q>>2)+read_index_q];
|
||||
assign dma_axi4.rresp =
|
||||
error_mode && (read_addr_q == 32'h0000_0200) ? `AXI4_RESP_SLAVE_ERROR :
|
||||
`AXI4_RESP_OKAY;
|
||||
assign dma_axi4.rlast = (read_index_q + 1'b1) >= read_beats_q;
|
||||
assign dma_axi4.ruser = '0;
|
||||
assign dma_axi4.rvalid = read_active_q;
|
||||
assign dma_axi4.awready = !write_active_q && !write_response_q;
|
||||
assign dma_axi4.wready = write_active_q;
|
||||
assign dma_axi4.bid = 1'b0;
|
||||
assign dma_axi4.bresp = `AXI4_RESP_OKAY;
|
||||
assign dma_axi4.buser = '0;
|
||||
assign dma_axi4.bvalid = write_response_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
read_active_q <= 1'b0;
|
||||
read_addr_q <= '0;
|
||||
read_beats_q <= '0;
|
||||
read_index_q <= '0;
|
||||
write_active_q <= 1'b0;
|
||||
write_addr_q <= '0;
|
||||
write_response_q <= 1'b0;
|
||||
read_bursts <= 0;
|
||||
max_read_beats <= 0;
|
||||
end else begin
|
||||
if (dma_axi4.arvalid && dma_axi4.arready) begin
|
||||
read_active_q <= 1'b1;
|
||||
read_addr_q <= dma_axi4.araddr;
|
||||
read_beats_q <= dma_axi4.arlen + 1'b1;
|
||||
read_index_q <= '0;
|
||||
read_bursts <= read_bursts + 1;
|
||||
if ((dma_axi4.arlen + 1'b1) > max_read_beats) begin
|
||||
max_read_beats <= dma_axi4.arlen + 1'b1;
|
||||
end
|
||||
end
|
||||
if (dma_axi4.rvalid && dma_axi4.rready) begin
|
||||
if (dma_axi4.rlast) begin
|
||||
read_active_q <= 1'b0;
|
||||
end else begin
|
||||
read_index_q <= read_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
if (dma_axi4.awvalid && dma_axi4.awready) begin
|
||||
write_active_q <= 1'b1;
|
||||
write_addr_q <= dma_axi4.awaddr;
|
||||
end
|
||||
if (dma_axi4.wvalid && dma_axi4.wready) begin
|
||||
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (dma_axi4.wstrb[byte_index]) begin
|
||||
memory[(write_addr_q>>2)][byte_index*8+:8] <= dma_axi4.wdata[byte_index*8+:8];
|
||||
end
|
||||
end
|
||||
if (dma_axi4.wlast) begin
|
||||
write_active_q <= 1'b0;
|
||||
write_response_q <= 1'b1;
|
||||
end else begin
|
||||
write_addr_q <= write_addr_q + 4;
|
||||
end
|
||||
end
|
||||
if (dma_axi4.bvalid && dma_axi4.bready) begin
|
||||
write_response_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (data_out_valid_o && data_out_ready_i) begin
|
||||
output_data[output_count] <= data_out_o;
|
||||
output_strb[output_count] <= data_out_strb_o;
|
||||
output_last[output_count] <= data_out_last_o;
|
||||
output_count <= output_count + 1;
|
||||
end
|
||||
if (u_sdio_dma.descriptor_irq_o) begin
|
||||
descriptor_irq_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
sdio_dma #(
|
||||
.AddrWidth(32),
|
||||
.DataWidth(32),
|
||||
.DescCount(16)
|
||||
) u_sdio_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (start_i),
|
||||
.abort_i (abort_i),
|
||||
.direction_i (direction_i),
|
||||
.desc_base_i (desc_base_i),
|
||||
.desc_count_i (desc_count_i),
|
||||
.total_bytes_i (total_bytes_i),
|
||||
.data_in_valid_i (data_in_valid_i),
|
||||
.data_in_ready_o (data_in_ready_o),
|
||||
.data_in_i (data_in_i),
|
||||
.data_in_strb_i (data_in_strb_i),
|
||||
.data_in_last_i (data_in_last_i),
|
||||
.data_out_valid_o(data_out_valid_o),
|
||||
.data_out_ready_i(data_out_ready_i),
|
||||
.data_out_o (data_out_o),
|
||||
.data_out_strb_o (data_out_strb_o),
|
||||
.data_out_last_o (data_out_last_o),
|
||||
.busy_o (busy_o),
|
||||
.done_o (done_o),
|
||||
.error_o (error_o),
|
||||
.error_code_o (error_code_o),
|
||||
.current_desc_o (current_desc_o),
|
||||
.bytes_done_o (bytes_done_o),
|
||||
.error_addr_o (error_addr_o),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
initial begin
|
||||
for (integer index = 0; index < 2048; index++) memory[index] = '0;
|
||||
output_count = 0;
|
||||
descriptor_irq_seen = 1'b0;
|
||||
direction_i = 1'b1;
|
||||
memory[0] = 32'h0000_0FFC;
|
||||
memory[1] = 32'd5;
|
||||
memory[2] = 32'd0;
|
||||
memory[3] = 32'h0000_000D;
|
||||
memory[32'h0FFC>>2] = 32'h4433_2211;
|
||||
memory[32'h1000>>2] = 32'h0000_0055;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
wait (done_o);
|
||||
if (error_o || (output_count != 2) || (bytes_done_o != 32'd5) ||
|
||||
(read_bursts < 3) || (max_read_beats > 16) ||
|
||||
(output_strb[0] != 4'hF) || (output_strb[1] != 4'h1) ||
|
||||
(output_data[0] != 32'h4433_2211) || (output_data[1] != 32'h0000_0055) ||
|
||||
!output_last[1] || (memory[3] != 32'h0001_000C)) begin
|
||||
$fatal(1, "DMA test failed err=%b code=%h outputs=%0d bytes=%0d bursts=%0d max=%0d wb=%h",
|
||||
error_o, error_code_o, output_count, bytes_done_o, read_bursts, max_read_beats,
|
||||
memory[3]);
|
||||
end
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!descriptor_irq_seen) $fatal(1, "descriptor IRQ event was not visible");
|
||||
|
||||
// Card-to-host is the opposite stream direction: input stream bytes are
|
||||
// AXI-written with low lane zero as the first byte.
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
output_count = 0;
|
||||
direction_i = 1'b0;
|
||||
desc_base_i = 32'd0;
|
||||
total_bytes_i = 32'd5;
|
||||
error_mode = 1'b0;
|
||||
memory[0] = 32'h0000_0200;
|
||||
memory[1] = 32'd5;
|
||||
memory[2] = 32'd0;
|
||||
memory[3] = 32'h0000_000D;
|
||||
memory[32'h0200>>2] = '0;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
@(negedge clk_i);
|
||||
data_in_i = 32'h8877_6655;
|
||||
data_in_strb_i = 4'hF;
|
||||
data_in_last_i = 1'b0;
|
||||
data_in_valid_i = 1'b1;
|
||||
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
data_in_i = 32'h0000_0099;
|
||||
data_in_strb_i = 4'h1;
|
||||
data_in_last_i = 1'b1;
|
||||
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
data_in_valid_i = 1'b0;
|
||||
wait (done_o);
|
||||
if (error_o || (bytes_done_o != 32'd5) ||
|
||||
(memory[32'h0200>>2] != 32'h8877_6655) ||
|
||||
(memory[32'h0204>>2][7:0] != 8'h99) ||
|
||||
(memory[3] != 32'h0001_000C) || !descriptor_irq_seen) begin
|
||||
$fatal(1, "card-to-host DMA failed err=%b code=%h bytes=%0d data=%h tail=%h wb=%h", error_o,
|
||||
error_code_o, bytes_done_o, memory[32'h0200>>2], memory[32'h0204>>2], memory[3]);
|
||||
end
|
||||
|
||||
for (integer tail_bytes = 1; tail_bytes <= 3; tail_bytes++) begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
data_in_valid_i = 1'b0;
|
||||
direction_i = 1'b0;
|
||||
desc_base_i = 32'd0;
|
||||
total_bytes_i = tail_bytes;
|
||||
memory[0] = 32'h0000_0300 + (tail_bytes * 32);
|
||||
memory[1] = tail_bytes;
|
||||
memory[2] = 32'd0;
|
||||
memory[3] = 32'h0000_0005;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
|
||||
data_in_i = 32'h0403_0201;
|
||||
data_in_strb_i = (4'b0001 << tail_bytes) - 1'b1;
|
||||
data_in_last_i = 1'b1;
|
||||
data_in_valid_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
data_in_valid_i = 1'b0;
|
||||
wait (done_o);
|
||||
for (integer byte_index = 0; byte_index < tail_bytes; byte_index++) begin
|
||||
if (memory[(32'h0000_0300 + (tail_bytes * 32) >> 2) + (byte_index >> 2)]
|
||||
[(byte_index % 4)*8+:8] != byte_index + 1) begin
|
||||
$fatal(1, "DMA tail readback failed bytes=%0d byte=%0d", tail_bytes, byte_index);
|
||||
end
|
||||
end
|
||||
if (error_o || (bytes_done_o != tail_bytes) || (memory[3] != 32'h0001_0004)) begin
|
||||
$fatal(1, "DMA tail transfer failed bytes=%0d err=%b wb=%h", tail_bytes, error_o,
|
||||
memory[3]);
|
||||
end
|
||||
end
|
||||
|
||||
// A descriptor at ...FF0 is rejected before an AXI fetch.
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
desc_base_i = 32'h0000_0FF0;
|
||||
direction_i = 1'b1;
|
||||
total_bytes_i = 32'd4;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
wait (done_o);
|
||||
if (!error_o || (read_bursts != 0)) begin
|
||||
$fatal(1, "descriptor boundary rejection failed err=%b bursts=%0d", error_o, read_bursts);
|
||||
end
|
||||
|
||||
// Restore a clean responder state for the directed AXI error check.
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
memory[0] = 32'h0000_0200;
|
||||
memory[1] = 32'd4;
|
||||
memory[2] = 32'd0;
|
||||
memory[3] = 32'h0000_0005;
|
||||
memory[32'h0200>>2] = 32'hAABB_CCDD;
|
||||
desc_base_i = 32'd0;
|
||||
total_bytes_i = 32'd4;
|
||||
direction_i = 1'b1;
|
||||
error_mode = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
wait (done_o);
|
||||
if (!error_o || (memory[3] != 32'h0002_0004)) begin
|
||||
$fatal(1, "AXI error was not written back: err=%b code=%h wb=%h", error_o, error_code_o,
|
||||
memory[3]);
|
||||
end
|
||||
$display("SDIO DMA descriptor, tail, and 4 KiB split test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_dual_tb;
|
||||
localparam logic [31:0] HostCtrl = 32'h00C;
|
||||
localparam logic [31:0] ClockCtrl = 32'h010;
|
||||
localparam logic [31:0] BusCtrl = 32'h018;
|
||||
localparam logic [31:0] CmdArg = 32'h040;
|
||||
localparam logic [31:0] CmdCfg = 32'h044;
|
||||
localparam logic [31:0] CmdStart = 32'h048;
|
||||
localparam logic [31:0] CmdStatus = 32'h04C;
|
||||
localparam logic [31:0] Resp0 = 32'h050;
|
||||
localparam logic [31:0] BlockSize = 32'h080;
|
||||
localparam logic [31:0] BlockCount = 32'h084;
|
||||
localparam logic [31:0] DataCfg = 32'h088;
|
||||
localparam logic [31:0] DataStart = 32'h08C;
|
||||
localparam logic [31:0] PioData = 32'h090;
|
||||
localparam logic [31:0] Status = 32'h028;
|
||||
localparam logic [31:0] ErrorStatus = 32'h10C;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst0_n_i = 1'b0;
|
||||
logic rst1_n_i = 1'b0;
|
||||
apb4_if apb0 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst0_n_i)
|
||||
);
|
||||
apb4_if apb1 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst1_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) axi0 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst0_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) axi1 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst1_n_i)
|
||||
);
|
||||
sdio_if sdio0 ();
|
||||
sdio_if sdio1 ();
|
||||
logic card0_cmd_do;
|
||||
logic [ 3:0] card0_dat_do;
|
||||
logic card0_irq;
|
||||
logic card1_cmd_do;
|
||||
logic [ 3:0] card1_dat_do;
|
||||
logic card1_irq;
|
||||
logic [31:0] value0;
|
||||
logic [31:0] value1;
|
||||
integer guard0;
|
||||
integer guard1;
|
||||
|
||||
assign sdio0.cmd_di_i = sdio0.cmd_oe_o ? sdio0.cmd_do_o : card0_cmd_do;
|
||||
assign sdio0.dat_di_i = (sdio0.dat_oe_o & sdio0.dat_do_o) | (~sdio0.dat_oe_o & card0_dat_do);
|
||||
assign sdio1.cmd_di_i = sdio1.cmd_oe_o ? sdio1.cmd_do_o : card1_cmd_do;
|
||||
assign sdio1.dat_di_i = (sdio1.dat_oe_o & sdio1.dat_do_o) | (~sdio1.dat_oe_o & card1_dat_do);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apb4_sdio u_sdio0 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst0_n_i),
|
||||
.apb4 (apb0),
|
||||
.dma_axi4(axi0),
|
||||
.sdio (sdio0)
|
||||
);
|
||||
apb4_sdio u_sdio1 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst1_n_i),
|
||||
.apb4 (apb1),
|
||||
.dma_axi4(axi1),
|
||||
.sdio (sdio1)
|
||||
);
|
||||
|
||||
sdio_sd_memory_model #(
|
||||
.HighCapacity(1'b1),
|
||||
.BusyCycles (8)
|
||||
) u_card0 (
|
||||
.rst_n_i (rst0_n_i),
|
||||
.sck_i (sdio0.sck_o),
|
||||
.cmd_oe_i(sdio0.cmd_oe_o),
|
||||
.cmd_do_i(sdio0.cmd_do_o),
|
||||
.cmd_do_o(card0_cmd_do),
|
||||
.dat_oe_i(sdio0.dat_oe_o),
|
||||
.dat_do_i(sdio0.dat_do_o),
|
||||
.dat_do_o(card0_dat_do),
|
||||
.irq_o (card0_irq)
|
||||
);
|
||||
sdio_sdio_model #(
|
||||
.FunctionCount(2),
|
||||
.FunctionBytes(4096),
|
||||
.BusyCycles (8)
|
||||
) u_card1 (
|
||||
.rst_n_i (rst1_n_i),
|
||||
.sck_i (sdio1.sck_o),
|
||||
.cmd_oe_i(sdio1.cmd_oe_o),
|
||||
.cmd_do_i(sdio1.cmd_do_o),
|
||||
.cmd_do_o(card1_cmd_do),
|
||||
.dat_oe_i(sdio1.dat_oe_o),
|
||||
.dat_do_i(sdio1.dat_do_o),
|
||||
.dat_do_o(card1_dat_do),
|
||||
.irq_o (card1_irq)
|
||||
);
|
||||
sdio_axi_memory_responder u_mem0 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst0_n_i),
|
||||
.axi4 (axi0)
|
||||
);
|
||||
sdio_axi_memory_responder u_mem1 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst1_n_i),
|
||||
.axi4 (axi1)
|
||||
);
|
||||
|
||||
task automatic apb0_idle;
|
||||
begin
|
||||
apb0.psel = 1'b0;
|
||||
apb0.penable = 1'b0;
|
||||
apb0.pwrite = 1'b0;
|
||||
apb0.paddr = '0;
|
||||
apb0.pwdata = '0;
|
||||
apb0.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
task automatic apb1_idle;
|
||||
begin
|
||||
apb1.psel = 1'b0;
|
||||
apb1.penable = 1'b0;
|
||||
apb1.pwrite = 1'b0;
|
||||
apb1.paddr = '0;
|
||||
apb1.pwdata = '0;
|
||||
apb1.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb0_write(input logic [31:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb0.paddr = address_i;
|
||||
apb0.pwdata = data_i;
|
||||
apb0.pstrb = 4'hF;
|
||||
apb0.pwrite = 1'b1;
|
||||
apb0.psel = 1'b1;
|
||||
apb0.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb0.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb0.pready || apb0.pslverr) $fatal(1, "dual APB0 write failed %h", address_i);
|
||||
apb0_idle();
|
||||
end
|
||||
endtask
|
||||
task automatic apb1_write(input logic [31:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb1.paddr = address_i;
|
||||
apb1.pwdata = data_i;
|
||||
apb1.pstrb = 4'hF;
|
||||
apb1.pwrite = 1'b1;
|
||||
apb1.psel = 1'b1;
|
||||
apb1.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb1.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb1.pready || apb1.pslverr) $fatal(1, "dual APB1 write failed %h", address_i);
|
||||
apb1_idle();
|
||||
end
|
||||
endtask
|
||||
task automatic apb0_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb0.paddr = address_i;
|
||||
apb0.pwrite = 1'b0;
|
||||
apb0.psel = 1'b1;
|
||||
apb0.penable = 1'b0;
|
||||
apb0.pstrb = '0;
|
||||
@(negedge clk_i);
|
||||
apb0.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb0.pready || apb0.pslverr) $fatal(1, "dual APB0 read failed %h", address_i);
|
||||
data_o = apb0.prdata;
|
||||
apb0_idle();
|
||||
end
|
||||
endtask
|
||||
task automatic apb1_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb1.paddr = address_i;
|
||||
apb1.pwrite = 1'b0;
|
||||
apb1.psel = 1'b1;
|
||||
apb1.penable = 1'b0;
|
||||
apb1.pstrb = '0;
|
||||
@(negedge clk_i);
|
||||
apb1.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb1.pready || apb1.pslverr) $fatal(1, "dual APB1 read failed %h", address_i);
|
||||
data_o = apb1.prdata;
|
||||
apb1_idle();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait0_command;
|
||||
logic seen_busy;
|
||||
begin
|
||||
guard0 = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (guard0 < 20000) begin
|
||||
@(posedge clk_i);
|
||||
guard0 = guard0 + 1;
|
||||
if (u_sdio0.u_sdio_core.s_cmd_busy) seen_busy = 1'b1;
|
||||
if (seen_busy && (u_sdio0.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (guard0 >= 20000) $fatal(1, "dual SDIO0 command did not drain");
|
||||
end
|
||||
endtask
|
||||
task automatic wait1_command;
|
||||
logic seen_busy;
|
||||
begin
|
||||
guard1 = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (guard1 < 20000) begin
|
||||
@(posedge clk_i);
|
||||
guard1 = guard1 + 1;
|
||||
if (u_sdio1.u_sdio_core.s_cmd_busy) seen_busy = 1'b1;
|
||||
if (seen_busy && (u_sdio1.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (guard1 >= 20000) $fatal(1, "dual SDIO1 command did not drain");
|
||||
end
|
||||
endtask
|
||||
task automatic wait0_data;
|
||||
logic seen_busy;
|
||||
begin
|
||||
guard0 = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (guard0 < 200000) begin
|
||||
@(posedge clk_i);
|
||||
guard0 = guard0 + 1;
|
||||
if (u_sdio0.u_sdio_core.s_data_busy || u_sdio0.u_sdio_core.s_dma_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && !u_sdio0.u_sdio_core.s_data_busy && !u_sdio0.u_sdio_core.s_dma_busy) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (guard0 >= 200000) $fatal(1, "dual SDIO0 data did not drain");
|
||||
end
|
||||
endtask
|
||||
task automatic wait1_data;
|
||||
logic seen_busy;
|
||||
begin
|
||||
guard1 = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (guard1 < 200000) begin
|
||||
@(posedge clk_i);
|
||||
guard1 = guard1 + 1;
|
||||
if (u_sdio1.u_sdio_core.s_data_busy || u_sdio1.u_sdio_core.s_dma_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && !u_sdio1.u_sdio_core.s_data_busy && !u_sdio1.u_sdio_core.s_dma_busy) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (guard1 >= 200000) $fatal(1, "dual SDIO1 data did not drain");
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic run_memory_context;
|
||||
begin
|
||||
apb0_write(ClockCtrl, 32'h0000_0101);
|
||||
apb0_write(HostCtrl, 32'h0000_0005);
|
||||
apb0_write(CmdArg, 32'd2);
|
||||
apb0_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd1, 2'd0, 6'd17});
|
||||
apb0_write(CmdStart, 32'd1);
|
||||
wait0_command();
|
||||
apb0_read(CmdStatus, value0);
|
||||
if ((value0 & 32'h1E) != 0) $fatal(1, "dual SDIO0 CMD17 failed");
|
||||
apb0_write(BusCtrl, 32'd0);
|
||||
apb0_write(BlockSize, 32'd4);
|
||||
apb0_write(BlockCount, 32'd1);
|
||||
apb0_write(DataCfg, 32'd0);
|
||||
u_card0.arm_read(1024, 4, 1'b0, 1'b0);
|
||||
apb0_write(DataStart, 32'd1);
|
||||
wait0_data();
|
||||
apb0_read(PioData, value0);
|
||||
if (value0 != 32'h0000_0000) $fatal(1, "dual SDIO0 data mismatch");
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic run_sdio_context;
|
||||
begin
|
||||
apb1_write(ClockCtrl, 32'h0000_0101);
|
||||
apb1_write(HostCtrl, 32'h0000_0005);
|
||||
apb1_write(CmdArg, 32'd0);
|
||||
apb1_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd5, 2'd0, 6'd5});
|
||||
apb1_write(CmdStart, 32'd1);
|
||||
wait1_command();
|
||||
apb1_write(CmdArg, {1'b0, 3'd0, 1'b0, 1'b0, 17'h00000, 1'b0, 8'h00});
|
||||
apb1_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd6, 2'd0, 6'd52});
|
||||
apb1_write(CmdStart, 32'd1);
|
||||
wait1_command();
|
||||
apb1_write(CmdArg, {1'b0, 3'd1, 1'b0, 1'b1, 17'h00020, 9'd4});
|
||||
apb1_write(CmdCfg, {18'd0, 1'b0, 1'b0, 4'd0, 2'd0, 6'd53});
|
||||
apb1_write(CmdStart, 32'd1);
|
||||
wait1_command();
|
||||
apb1_write(BusCtrl, 32'd1);
|
||||
apb1_write(BlockSize, 32'd4);
|
||||
apb1_write(BlockCount, 32'd1);
|
||||
apb1_write(DataCfg, 32'h0000_0002);
|
||||
u_card1.arm_read(4096 + 32, 4, 1'b1, 1'b0);
|
||||
apb1_write(DataStart, 32'd1);
|
||||
wait1_data();
|
||||
apb1_read(PioData, value1);
|
||||
if (value1 != 32'd0) $fatal(1, "dual SDIO1 data mismatch");
|
||||
u_card1.set_irq();
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
apb0_idle();
|
||||
apb1_idle();
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst0_n_i = 1'b1;
|
||||
rst1_n_i = 1'b1;
|
||||
fork
|
||||
run_memory_context();
|
||||
run_sdio_context();
|
||||
join
|
||||
apb0_read(Status, value0);
|
||||
apb1_read(Status, value1);
|
||||
if (value0 == value1) $fatal(1, "dual status contexts were aliased");
|
||||
if (sdio1.dat_di_i[1] != 1'b0) begin
|
||||
$fatal(1, "dual SDIO1 DAT1 context is not isolated");
|
||||
end
|
||||
apb0_read(ErrorStatus, value0);
|
||||
if (value0 != 0) $fatal(1, "SDIO0 inherited SDIO1 error state: %h", value0);
|
||||
$display("SDIO dual-instance isolation and concurrent traffic test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,294 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_register_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic busy_i = 1'b0;
|
||||
logic [ 31:0] status_i = 32'h0000_0011;
|
||||
logic [ 31:0] clock_actual_i = 32'd400000;
|
||||
logic [ 31:0] cmd_status_i = '0;
|
||||
logic [135:0] response_i = '0;
|
||||
logic [ 31:0] data_status_i = '0;
|
||||
logic [ 31:0] fifo_status_i = '0;
|
||||
logic [ 31:0] dma_status_i = '0;
|
||||
logic [ 31:0] current_desc_i = '0;
|
||||
logic [ 31:0] bytes_done_i = '0;
|
||||
logic [ 31:0] dma_error_addr_i = '0;
|
||||
logic [ 31:0] dma_error_i = '0;
|
||||
logic [ 31:0] error_status_i = '0;
|
||||
logic [ 5:0] last_cmd_i = '0;
|
||||
logic [ 31:0] crc_error_count_i = '0;
|
||||
logic [ 31:0] timeout_count_i = '0;
|
||||
logic [ 31:0] axi_error_count_i = '0;
|
||||
logic [ 31:0] stall_count_i = '0;
|
||||
logic [ 7:0] irq_event_i = '0;
|
||||
logic [ 31:0] pio_rdata_i = '0;
|
||||
logic pio_valid_i = 1'b0;
|
||||
logic pio_ready_i = 1'b0;
|
||||
logic pio_read_consume_o;
|
||||
logic host_enable_o;
|
||||
logic host_irq_enable_o;
|
||||
logic clock_enable_o;
|
||||
logic [ 15:0] half_period_o;
|
||||
logic [ 1:0] bus_width_o;
|
||||
logic [ 31:0] timeout_cmd_o;
|
||||
logic [ 31:0] timeout_data_o;
|
||||
logic [ 31:0] timeout_busy_o;
|
||||
logic [ 5:0] cmd_index_o;
|
||||
logic [ 31:0] cmd_arg_o;
|
||||
sdio_pkg::sdio_resp_type_e cmd_resp_type_o;
|
||||
logic cmd_crc_check_o;
|
||||
logic cmd_index_check_o;
|
||||
logic cmd_start_o;
|
||||
logic [ 15:0] block_size_o;
|
||||
logic [ 15:0] block_count_o;
|
||||
logic data_direction_o;
|
||||
logic data_dma_enable_o;
|
||||
logic data_block_mode_o;
|
||||
logic data_fixed_addr_o;
|
||||
logic data_start_o;
|
||||
logic pio_wvalid_o;
|
||||
logic [ 31:0] pio_wdata_o;
|
||||
logic [ 3:0] pio_wstrb_o;
|
||||
logic [ 31:0] desc_base_o;
|
||||
logic [ 15:0] desc_count_o;
|
||||
logic dma_start_o;
|
||||
logic dma_abort_o;
|
||||
logic irq_dat1_enable_o;
|
||||
logic irq_o;
|
||||
integer pio_consume_count;
|
||||
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (pio_read_consume_o) begin
|
||||
pio_consume_count = pio_consume_count + 1;
|
||||
end
|
||||
end
|
||||
|
||||
sdio_reg u_sdio_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.busy_i (busy_i),
|
||||
.status_i (status_i),
|
||||
.clock_actual_i (clock_actual_i),
|
||||
.cmd_status_i (cmd_status_i),
|
||||
.response_i (response_i),
|
||||
.data_status_i (data_status_i),
|
||||
.fifo_status_i (fifo_status_i),
|
||||
.dma_status_i (dma_status_i),
|
||||
.current_desc_i (current_desc_i),
|
||||
.bytes_done_i (bytes_done_i),
|
||||
.dma_error_addr_i (dma_error_addr_i),
|
||||
.dma_error_i (dma_error_i),
|
||||
.error_status_i (error_status_i),
|
||||
.last_cmd_i (last_cmd_i),
|
||||
.crc_error_count_i (crc_error_count_i),
|
||||
.timeout_count_i (timeout_count_i),
|
||||
.axi_error_count_i (axi_error_count_i),
|
||||
.stall_count_i (stall_count_i),
|
||||
.irq_event_i (irq_event_i),
|
||||
.pio_rdata_i (pio_rdata_i),
|
||||
.pio_valid_i (pio_valid_i),
|
||||
.pio_ready_i (pio_ready_i),
|
||||
.pio_read_consume_o(pio_read_consume_o),
|
||||
.host_enable_o (host_enable_o),
|
||||
.host_irq_enable_o (host_irq_enable_o),
|
||||
.clock_enable_o (clock_enable_o),
|
||||
.half_period_o (half_period_o),
|
||||
.bus_width_o (bus_width_o),
|
||||
.timeout_cmd_o (timeout_cmd_o),
|
||||
.timeout_data_o (timeout_data_o),
|
||||
.timeout_busy_o (timeout_busy_o),
|
||||
.cmd_index_o (cmd_index_o),
|
||||
.cmd_arg_o (cmd_arg_o),
|
||||
.cmd_resp_type_o (cmd_resp_type_o),
|
||||
.cmd_crc_check_o (cmd_crc_check_o),
|
||||
.cmd_index_check_o (cmd_index_check_o),
|
||||
.cmd_start_o (cmd_start_o),
|
||||
.block_size_o (block_size_o),
|
||||
.block_count_o (block_count_o),
|
||||
.data_direction_o (data_direction_o),
|
||||
.data_dma_enable_o (data_dma_enable_o),
|
||||
.data_block_mode_o (data_block_mode_o),
|
||||
.data_fixed_addr_o (data_fixed_addr_o),
|
||||
.data_start_o (data_start_o),
|
||||
.pio_wvalid_o (pio_wvalid_o),
|
||||
.pio_wdata_o (pio_wdata_o),
|
||||
.pio_wstrb_o (pio_wstrb_o),
|
||||
.desc_base_o (desc_base_o),
|
||||
.desc_count_o (desc_count_o),
|
||||
.dma_start_o (dma_start_o),
|
||||
.dma_abort_o (dma_abort_o),
|
||||
.irq_dat1_enable_o (irq_dat1_enable_o),
|
||||
.irq_o (irq_o),
|
||||
.apb4 (apb4)
|
||||
);
|
||||
|
||||
task automatic apb_write(input logic [31:0] address, input logic [31:0] data,
|
||||
input logic [3:0] strobe, input logic expected_error);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address;
|
||||
apb4.pwdata = data;
|
||||
apb4.pstrb = strobe;
|
||||
apb4.pwrite = 1'b1;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || (apb4.pslverr != expected_error)) begin
|
||||
$fatal(1, "APB write %h ready=%b err=%b expected=%b", address, apb4.pready, apb4.pslverr,
|
||||
expected_error);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read_error(input logic [31:0] address);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || !apb4.pslverr) begin
|
||||
$fatal(1, "APB read %h did not report empty PIO error", address);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [31:0] address, output logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || apb4.pslverr) begin
|
||||
$fatal(1, "APB read %h ready=%b err=%b", address, apb4.pready, apb4.pslverr);
|
||||
end
|
||||
data = apb4.prdata;
|
||||
@(negedge clk_i);
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
logic [31:0] value;
|
||||
apb4.paddr = '0;
|
||||
apb4.pprot = '0;
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
apb_read(32'h000, value);
|
||||
if (value != 32'h5344_494F) $fatal(1, "IP ID mismatch");
|
||||
apb_read(32'h008, value);
|
||||
if (value != 32'h0000_01FF) $fatal(1, "capability mismatch");
|
||||
apb_write(32'h010, 32'h0000_0501, 4'hF, 1'b0);
|
||||
apb_read(32'h010, value);
|
||||
if (value != 32'h0000_0501) $fatal(1, "clock control mismatch: %h", value);
|
||||
apb_write(32'h040, 32'hCAFE_BABE, 4'hF, 1'b0);
|
||||
apb_write(32'h044, 32'h0000_3108, 4'hF, 1'b0);
|
||||
apb_write(32'h048, 32'h0000_0001, 4'h1, 1'b0);
|
||||
#1;
|
||||
if (!cmd_start_o || (cmd_index_o != 6'd8) || (cmd_arg_o != 32'hCAFE_BABE)) begin
|
||||
$fatal(1, "command pulse/configuration mismatch");
|
||||
end
|
||||
apb_write(32'h3FC, 32'h0, 4'hF, 1'b1);
|
||||
busy_i = 1'b1;
|
||||
apb_write(32'h040, 32'h0, 4'hF, 1'b1);
|
||||
busy_i = 1'b0;
|
||||
apb_write(32'h104, 32'h0000_0001, 4'h1, 1'b0);
|
||||
apb_write(32'h108, 32'h0000_0001, 4'h1, 1'b0);
|
||||
if (!irq_o) $fatal(1, "IRQ test did not assert IRQ");
|
||||
apb_read(32'h100, value);
|
||||
if ((value & 32'h1) == 0) $fatal(1, "IRQ status did not latch");
|
||||
apb_write(32'h100, 32'h0000_0001, 4'h1, 1'b0);
|
||||
if (irq_o) $fatal(1, "IRQ W1C did not clear IRQ");
|
||||
|
||||
apb_write(32'h104, 32'h0000_0004, 4'h1, 1'b0);
|
||||
irq_event_i = 8'h04;
|
||||
@(posedge clk_i);
|
||||
irq_event_i = 8'h00;
|
||||
apb_read(32'h100, value);
|
||||
if ((value & 32'h04) == 0 || !irq_o) $fatal(1, "descriptor IRQ event did not latch");
|
||||
apb_write(32'h100, 32'h0000_0004, 4'h1, 1'b0);
|
||||
apb_read(32'h100, value);
|
||||
if ((value & 32'h04) != 0 || irq_o) $fatal(1, "descriptor IRQ W1C failed");
|
||||
|
||||
error_status_i = 32'h0000_0001;
|
||||
@(posedge clk_i);
|
||||
error_status_i = 32'h0000_0000;
|
||||
apb_read(32'h10C, value);
|
||||
if ((value & 32'h01) == 0) $fatal(1, "error event did not latch");
|
||||
apb_write(32'h10C, 32'h0000_0001, 4'h1, 1'b0);
|
||||
apb_read(32'h10C, value);
|
||||
if ((value & 32'h01) != 0) $fatal(1, "ERROR_STATUS W1C did not clear");
|
||||
apb_read(32'h10C, value);
|
||||
if ((value & 32'h01) != 0) $fatal(1, "ERROR_STATUS clear was not persistent");
|
||||
error_status_i = 32'h0000_0001;
|
||||
@(posedge clk_i);
|
||||
error_status_i = 32'h0000_0000;
|
||||
apb_read(32'h10C, value);
|
||||
if ((value & 32'h01) == 0) $fatal(1, "new error event did not re-latch");
|
||||
|
||||
pio_rdata_i = 32'h1122_3344;
|
||||
pio_valid_i = 1'b1;
|
||||
pio_ready_i = 1'b0;
|
||||
apb_read(32'h124, value);
|
||||
if (value != 32'h0000_0001) $fatal(1, "DEBUG PIO valid bit mismatch: %h", value);
|
||||
pio_valid_i = 1'b0;
|
||||
pio_ready_i = 1'b1;
|
||||
apb_read(32'h124, value);
|
||||
if (value != 32'h0000_0002) $fatal(1, "DEBUG PIO ready bit mismatch: %h", value);
|
||||
pio_rdata_i = 32'h1122_3344;
|
||||
pio_valid_i = 1'b1;
|
||||
pio_consume_count = 0;
|
||||
apb_read(32'h090, value);
|
||||
if ((value != 32'h1122_3344) || (pio_consume_count != 1)) begin
|
||||
$fatal(1, "PIO read did not return and consume one word: %h count=%0d", value,
|
||||
pio_consume_count);
|
||||
end
|
||||
pio_valid_i = 1'b0;
|
||||
apb_read_error(32'h090);
|
||||
pio_rdata_i = 32'h5566_7788;
|
||||
pio_valid_i = 1'b1;
|
||||
apb_read(32'h090, value);
|
||||
if ((value != 32'h5566_7788) || (pio_consume_count != 2)) begin
|
||||
$fatal(1, "consecutive PIO read contract failed: %h count=%0d", value, pio_consume_count);
|
||||
end
|
||||
$display("SDIO APB register test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_sdio_tb;
|
||||
localparam logic [31:0] HostCtrl = 32'h00C;
|
||||
localparam logic [31:0] ClockCtrl = 32'h010;
|
||||
localparam logic [31:0] BusCtrl = 32'h018;
|
||||
localparam logic [31:0] CmdArg = 32'h040;
|
||||
localparam logic [31:0] CmdCfg = 32'h044;
|
||||
localparam logic [31:0] CmdStart = 32'h048;
|
||||
localparam logic [31:0] CmdStatus = 32'h04C;
|
||||
localparam logic [31:0] Resp0 = 32'h050;
|
||||
localparam logic [31:0] BlockSize = 32'h080;
|
||||
localparam logic [31:0] BlockCount = 32'h084;
|
||||
localparam logic [31:0] DataCfg = 32'h088;
|
||||
localparam logic [31:0] DataStart = 32'h08C;
|
||||
localparam logic [31:0] PioData = 32'h090;
|
||||
localparam logic [31:0] IrqStatus = 32'h100;
|
||||
localparam logic [31:0] IrqEnable = 32'h104;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
sdio_if sdio ();
|
||||
logic card_cmd_do;
|
||||
logic [ 3:0] card_dat_do;
|
||||
logic card_irq;
|
||||
logic [31:0] apb_value;
|
||||
integer timeout_guard;
|
||||
|
||||
assign sdio.cmd_di_i = sdio.cmd_oe_o ? sdio.cmd_do_o : card_cmd_do;
|
||||
assign sdio.dat_di_i = (sdio.dat_oe_o & sdio.dat_do_o) | (~sdio.dat_oe_o & card_dat_do);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apb4_sdio #(
|
||||
.InputClockHz(72_000_000),
|
||||
.AddrWidth (32),
|
||||
.DataWidth (32),
|
||||
.DescCount (16)
|
||||
) u_apb4_sdio (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb4 (apb4),
|
||||
.dma_axi4(dma_axi4),
|
||||
.sdio (sdio)
|
||||
);
|
||||
|
||||
sdio_sdio_model #(
|
||||
.FunctionCount(2),
|
||||
.FunctionBytes(4096),
|
||||
.BusyCycles (8)
|
||||
) u_card (
|
||||
.rst_n_i (rst_n_i),
|
||||
.sck_i (sdio.sck_o),
|
||||
.cmd_oe_i(sdio.cmd_oe_o),
|
||||
.cmd_do_i(sdio.cmd_do_o),
|
||||
.cmd_do_o(card_cmd_do),
|
||||
.dat_oe_i(sdio.dat_oe_o),
|
||||
.dat_do_i(sdio.dat_do_o),
|
||||
.dat_do_o(card_dat_do),
|
||||
.irq_o (card_irq)
|
||||
);
|
||||
|
||||
sdio_axi_memory_responder u_axi_memory (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
task automatic apb_idle;
|
||||
begin
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.paddr = '0;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_command_unchecked(input logic [5:0] index_i, input logic [31:0] argument_i,
|
||||
input logic [3:0] response_i);
|
||||
begin
|
||||
apb_write(CmdArg, argument_i, 4'hF);
|
||||
apb_write(CmdCfg, {18'd0, 1'b0, 1'b0, response_i, 2'd0, index_i}, 4'hF);
|
||||
apb_write(CmdStart, 32'd1, 4'h1);
|
||||
wait_command();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_write(input logic [31:0] address_i, input logic [31:0] data_i,
|
||||
input logic [3:0] strobe_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address_i;
|
||||
apb4.pwdata = data_i;
|
||||
apb4.pstrb = strobe_i;
|
||||
apb4.pwrite = 1'b1;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || apb4.pslverr) begin
|
||||
$fatal(1, "SDIO APB write failed addr=%h data=%h", address_i, data_i);
|
||||
end
|
||||
apb_idle();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address_i;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || apb4.pslverr) begin
|
||||
$fatal(1, "SDIO APB read failed addr=%h", address_i);
|
||||
end
|
||||
data_o = apb4.prdata;
|
||||
apb_idle();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_command;
|
||||
logic seen_busy;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (timeout_guard < 20000) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
if (u_apb4_sdio.u_sdio_core.s_cmd_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && (u_apb4_sdio.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (timeout_guard >= 20000) begin
|
||||
$fatal(1, "SDIO command did not drain");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_data;
|
||||
logic seen_busy;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (timeout_guard < 200000) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
if (u_apb4_sdio.u_sdio_core.s_data_busy || u_apb4_sdio.u_sdio_core.s_dma_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && !u_apb4_sdio.u_sdio_core.s_data_busy &&
|
||||
!u_apb4_sdio.u_sdio_core.s_dma_busy) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (timeout_guard >= 200000) begin
|
||||
$fatal(1, "SDIO data path did not drain");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_write_ready;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
while ((u_apb4_sdio.u_sdio_core.u_sdio_data.s_state_q != 4'd1) &&
|
||||
(timeout_guard < 1000)) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
end
|
||||
if (timeout_guard >= 1000) begin
|
||||
$fatal(1, "SDIO write engine did not become ready");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_command(input logic [5:0] index_i, input logic [31:0] argument_i,
|
||||
input logic [3:0] response_i);
|
||||
logic [31:0] status_value;
|
||||
begin
|
||||
apb_write(CmdArg, argument_i, 4'hF);
|
||||
apb_write(CmdCfg, {18'd0, 1'b1, 1'b1, response_i, 2'd0, index_i}, 4'hF);
|
||||
apb_write(CmdStart, 32'd1, 4'h1);
|
||||
wait_command();
|
||||
apb_read(CmdStatus, status_value);
|
||||
if ((status_value & 32'h0000_001E) != 0) begin
|
||||
$fatal(1, "SDIO command %0d failed status=%h", index_i, status_value);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic configure_data(input logic width4_i, input logic direction_i,
|
||||
input logic block_mode_i, input logic fixed_address_i,
|
||||
input logic [15:0] bytes_i, input logic [15:0] count_i);
|
||||
begin
|
||||
apb_write(BusCtrl, width4_i ? 32'd1 : 32'd0, 4'h1);
|
||||
apb_write(BlockSize, {16'd0, bytes_i}, 4'hF);
|
||||
apb_write(BlockCount, {16'd0, count_i}, 4'hF);
|
||||
apb_write(DataCfg, {
|
||||
25'd0, fixed_address_i, block_mode_i, 1'b0, 1'b0, 1'b0, width4_i, direction_i},
|
||||
4'h1);
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
apb_idle();
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
apb_write(ClockCtrl, 32'h0000_0101, 4'hF);
|
||||
apb_write(HostCtrl, 32'h0000_0005, 4'h1);
|
||||
|
||||
// SDIO enumeration and CCCR/FBR/CIS-backed direct accesses.
|
||||
issue_command(6'd5, 32'h0000_0000, 4'd5);
|
||||
repeat (8) @(posedge clk_i);
|
||||
issue_command(6'd52, 32'h0000_0000, 4'd6);
|
||||
apb_read(Resp0, apb_value);
|
||||
if (apb_value[15:8] != 8'h03) begin
|
||||
$fatal(1, "CCCR version mismatch: %h", apb_value);
|
||||
end
|
||||
issue_command(6'd52, {1'b0, 3'd0, 1'b0, 1'b0, 17'h00013, 1'b0, 8'h00}, 4'd6);
|
||||
apb_read(Resp0, apb_value);
|
||||
if (apb_value[15:8] != 8'h01) $fatal(1, "CCCR high-speed capability missing");
|
||||
issue_command(6'd52, {1'b1, 3'd0, 1'b0, 1'b0, 17'h00013, 1'b0, 8'h03}, 4'd6);
|
||||
issue_command(6'd52, {1'b0, 3'd0, 1'b0, 1'b0, 17'h00013, 1'b0, 8'h00}, 4'd6);
|
||||
apb_read(Resp0, apb_value);
|
||||
if (apb_value[15:8] != 8'h03) $fatal(1, "CCCR high-speed enable did not stick");
|
||||
issue_command(6'd52, {1'b1, 3'd0, 1'b0, 1'b0, 17'h00004, 1'b0, 8'h5A}, 4'd6);
|
||||
apb_read(Resp0, apb_value);
|
||||
if (apb_value[15:8] != 8'h5A) begin
|
||||
$fatal(1, "CCCR write response mismatch: %h", apb_value);
|
||||
end
|
||||
|
||||
// CMD53 byte mode, incrementing address, 4-bit SDR.
|
||||
issue_command_unchecked(6'd53, {1'b0, 3'd1, 1'b0, 1'b1, 17'h00020, 9'd4}, 4'd0);
|
||||
configure_data(1'b1, 1'b0, 1'b0, 1'b0, 16'd4, 16'd1);
|
||||
u_card.arm_read(4096 + 32, 4, 1'b1, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
apb_read(PioData, apb_value);
|
||||
if (apb_value != 32'h0000_0000) begin
|
||||
$fatal(1, "unexpected initial function memory: %h", apb_value);
|
||||
end
|
||||
|
||||
// CMD53 block mode, fixed address, 1-bit SDR write.
|
||||
issue_command_unchecked(6'd53, {1'b1, 3'd1, 1'b1, 1'b0, 17'h00030, 9'd1}, 4'd0);
|
||||
configure_data(1'b0, 1'b1, 1'b1, 1'b1, 16'd4, 16'd1);
|
||||
u_card.arm_write(4096 + 48, 4, 1'b0, 1'b1);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_write_ready();
|
||||
apb_write(PioData, 32'h1122_3344, 4'hF);
|
||||
wait_data();
|
||||
if (u_card.read_backing(4096 + 48) != 8'h11) begin
|
||||
$fatal(1, "fixed-address CMD53 write did not land: %h", u_card.read_backing(4096 + 48));
|
||||
end
|
||||
|
||||
apb_write(IrqEnable, 32'h0000_0040, 4'h1);
|
||||
apb_write(IrqStatus, 32'h0000_00FF, 4'h1);
|
||||
u_card.clear_irq();
|
||||
repeat (4) @(posedge clk_i);
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0040) != 0 || sdio.irq_o) begin
|
||||
$fatal(1, "DAT1 high level caused a false host IRQ");
|
||||
end
|
||||
|
||||
// DAT1 is an active-low function interrupt. It is synchronized in clk_i,
|
||||
// qualified only while the host releases the data path, and remains sticky
|
||||
// while SDCLK is stopped.
|
||||
u_card.set_irq();
|
||||
repeat (4) @(posedge clk_i);
|
||||
if (sdio.dat_di_i[1] != 1'b0 || !card_irq) begin
|
||||
$fatal(1, "SDIO DAT1 interrupt was not asserted low");
|
||||
end
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0040) == 0 || !sdio.irq_o) begin
|
||||
$fatal(1, "active-low DAT1 did not latch host IRQ");
|
||||
end
|
||||
apb_write(IrqStatus, 32'h0000_0040, 4'h1);
|
||||
repeat (8) @(posedge clk_i);
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0040) != 0 || sdio.irq_o) begin
|
||||
$fatal(1, "DAT1 W1C did not clear sticky host IRQ");
|
||||
end
|
||||
if (sdio.sck_o != 1'b0) begin
|
||||
$fatal(1, "SDCLK was not stopped for DAT1 persistence check");
|
||||
end
|
||||
repeat (8) @(posedge clk_i);
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0040) != 0) begin
|
||||
$fatal(1, "cleared DAT1 IRQ reasserted while line remained low");
|
||||
end
|
||||
|
||||
u_card.clear_irq();
|
||||
repeat (4) @(posedge clk_i);
|
||||
if (sdio.dat_di_i[1] != 1'b1) begin
|
||||
$fatal(1, "DAT1 interrupt did not release");
|
||||
end
|
||||
u_card.set_irq();
|
||||
repeat (4) @(posedge clk_i);
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0040) == 0) begin
|
||||
$fatal(1, "DAT1 did not re-arm after returning high");
|
||||
end
|
||||
|
||||
$display("SDIO-only CMD5/CMD52/CMD53/DAT1 test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,677 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of the Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module sdio_standalone_tb #(
|
||||
parameter bit CardHighCapacity = 1'b1,
|
||||
parameter bit RunFourBit = 1'b1
|
||||
);
|
||||
localparam logic [31:0] HostCtrl = 32'h00C;
|
||||
localparam logic [31:0] ClockCtrl = 32'h010;
|
||||
localparam logic [31:0] BusCtrl = 32'h018;
|
||||
localparam logic [31:0] CmdArg = 32'h040;
|
||||
localparam logic [31:0] CmdCfg = 32'h044;
|
||||
localparam logic [31:0] CmdStart = 32'h048;
|
||||
localparam logic [31:0] CmdStatus = 32'h04C;
|
||||
localparam logic [31:0] Resp0 = 32'h050;
|
||||
localparam logic [31:0] BlockSize = 32'h080;
|
||||
localparam logic [31:0] BlockCount = 32'h084;
|
||||
localparam logic [31:0] DataCfg = 32'h088;
|
||||
localparam logic [31:0] DataStart = 32'h08C;
|
||||
localparam logic [31:0] DataStatus = 32'h08C;
|
||||
localparam logic [31:0] IrqStatus = 32'h100;
|
||||
localparam logic [31:0] IrqEnable = 32'h104;
|
||||
localparam logic [31:0] TimeoutData = 32'h020;
|
||||
localparam logic [31:0] TimeoutBusy = 32'h024;
|
||||
localparam logic [31:0] TimeoutCount = 32'h118;
|
||||
localparam logic [31:0] CrcCount = 32'h114;
|
||||
localparam logic [31:0] AxiCount = 32'h11C;
|
||||
localparam logic [31:0] PioData = 32'h090;
|
||||
localparam logic [31:0] DescBase = 32'h0C0;
|
||||
localparam logic [31:0] DescCount = 32'h0C4;
|
||||
localparam logic [31:0] Status = 32'h028;
|
||||
localparam logic [31:0] ClockActual = 32'h014;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
sdio_if sdio ();
|
||||
|
||||
logic card_cmd_do;
|
||||
logic [ 3:0] card_dat_do;
|
||||
logic card_irq;
|
||||
logic [31:0] apb_value;
|
||||
integer timeout_guard;
|
||||
integer timeout_count_before;
|
||||
integer crc_count_before;
|
||||
|
||||
assign sdio.cmd_di_i = sdio.cmd_oe_o ? sdio.cmd_do_o : card_cmd_do;
|
||||
assign sdio.dat_di_i = (sdio.dat_oe_o & sdio.dat_do_o) | (~sdio.dat_oe_o & card_dat_do);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apb4_sdio #(
|
||||
.InputClockHz(72_000_000),
|
||||
.AddrWidth (32),
|
||||
.DataWidth (32),
|
||||
.DescCount (16)
|
||||
) u_apb4_sdio (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb4 (apb4),
|
||||
.dma_axi4(dma_axi4),
|
||||
.sdio (sdio)
|
||||
);
|
||||
|
||||
sdio_sd_memory_model #(
|
||||
.HighCapacity(CardHighCapacity),
|
||||
.BlockBytes (512),
|
||||
.BlockCount (128),
|
||||
.BusyCycles (8)
|
||||
) u_card (
|
||||
.rst_n_i (rst_n_i),
|
||||
.sck_i (sdio.sck_o),
|
||||
.cmd_oe_i(sdio.cmd_oe_o),
|
||||
.cmd_do_i(sdio.cmd_do_o),
|
||||
.cmd_do_o(card_cmd_do),
|
||||
.dat_oe_i(sdio.dat_oe_o),
|
||||
.dat_do_i(sdio.dat_do_o),
|
||||
.dat_do_o(card_dat_do),
|
||||
.irq_o (card_irq)
|
||||
);
|
||||
|
||||
sdio_axi_memory_responder #(
|
||||
.DepthWords(16384)
|
||||
) u_axi_memory (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
task automatic apb_idle;
|
||||
begin
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.paddr = '0;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_write(input logic [31:0] address_i, input logic [31:0] data_i,
|
||||
input logic [3:0] strobe_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address_i;
|
||||
apb4.pwdata = data_i;
|
||||
apb4.pstrb = strobe_i;
|
||||
apb4.pwrite = 1'b1;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
for (integer wait_count = 0; wait_count < 100000; wait_count++) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (apb4.pready) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (!apb4.pready || apb4.pslverr) begin
|
||||
$fatal(1, "SDIO APB write failed addr=%h data=%h err=%b", address_i, data_i, apb4.pslverr);
|
||||
end
|
||||
apb_idle();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address_i;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || apb4.pslverr) begin
|
||||
$fatal(1, "SDIO APB read failed addr=%h err=%b", address_i, apb4.pslverr);
|
||||
end
|
||||
data_o = apb4.prdata;
|
||||
apb_idle();
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_command;
|
||||
logic seen_busy;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (timeout_guard < 20000) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
if (u_apb4_sdio.u_sdio_core.s_cmd_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && (u_apb4_sdio.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (timeout_guard >= 20000) begin
|
||||
$fatal(1, "command did not drain");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_data;
|
||||
logic seen_busy;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
seen_busy = 1'b0;
|
||||
while (timeout_guard < 200000) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
if (u_apb4_sdio.u_sdio_core.s_data_busy || u_apb4_sdio.u_sdio_core.s_dma_busy) begin
|
||||
seen_busy = 1'b1;
|
||||
end
|
||||
if (seen_busy && !u_apb4_sdio.u_sdio_core.s_data_busy &&
|
||||
!u_apb4_sdio.u_sdio_core.s_dma_busy) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (timeout_guard >= 200000) begin
|
||||
$fatal(1, "data path did not drain");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_command(input logic [5:0] index_i, input logic [31:0] argument_i,
|
||||
input logic [3:0] response_i, input logic crc_check_i,
|
||||
input logic index_check_i);
|
||||
logic [31:0] status_value;
|
||||
begin
|
||||
apb_write(CmdArg, argument_i, 4'hF);
|
||||
apb_write(CmdCfg, {18'd0, index_check_i, crc_check_i, response_i, 2'd0, index_i}, 4'hF);
|
||||
apb_write(CmdStart, 32'd1, 4'h1);
|
||||
wait_command();
|
||||
apb_read(CmdStatus, status_value);
|
||||
if ((status_value & 32'h0000_001E) != 0) begin
|
||||
$fatal(1, "command %0d failed status=%h", index_i, status_value);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic configure_data(input logic width4_i, input logic direction_i,
|
||||
input logic [15:0] bytes_i, input logic [15:0] count_i);
|
||||
begin
|
||||
apb_write(BusCtrl, width4_i ? 32'd1 : 32'd0, 4'h1);
|
||||
apb_write(BlockSize, {16'd0, bytes_i}, 4'hF);
|
||||
apb_write(BlockCount, {16'd0, count_i}, 4'hF);
|
||||
apb_write(DataCfg, {25'd0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, width4_i, direction_i}, 4'h1);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic configure_dma_data(input logic width4_i, input logic direction_i,
|
||||
input logic [15:0] bytes_i);
|
||||
begin
|
||||
apb_write(BusCtrl, width4_i ? 32'd1 : 32'd0, 4'h1);
|
||||
apb_write(BlockSize, {16'd0, bytes_i}, 4'hF);
|
||||
apb_write(BlockCount, 32'd1, 4'hF);
|
||||
apb_write(DataCfg, {25'd0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0, width4_i, direction_i}, 4'h1);
|
||||
apb_write(DescBase, 32'd0, 4'hF);
|
||||
apb_write(DescCount, 32'd1, 4'hF);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_pio(input integer address_i, input logic width4_i,
|
||||
output logic [31:0] data_o);
|
||||
begin
|
||||
u_card.arm_read(address_i, 4, width4_i, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
apb_read(PioData, data_o);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_pio_valid;
|
||||
begin
|
||||
timeout_guard = 0;
|
||||
while (!u_apb4_sdio.u_sdio_core.s_pio_valid_q && (timeout_guard < 200000)) begin
|
||||
@(posedge clk_i);
|
||||
timeout_guard = timeout_guard + 1;
|
||||
end
|
||||
if (timeout_guard >= 200000) begin
|
||||
$fatal(1, "PIO read word did not become valid");
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic write_pio(input integer address_i, input logic width4_i,
|
||||
input logic [31:0] data_i);
|
||||
begin
|
||||
u_card.arm_write(address_i, 4, width4_i, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
apb_write(PioData, data_i, 4'hF);
|
||||
wait_data();
|
||||
if (u_card.read_backing(
|
||||
address_i
|
||||
) != data_i[7:0] || u_card.read_backing(
|
||||
address_i + 1
|
||||
) != data_i[15:8] || u_card.read_backing(
|
||||
address_i + 2
|
||||
) != data_i[23:16] || u_card.read_backing(
|
||||
address_i + 3
|
||||
) != data_i[31:24]) begin
|
||||
$fatal(1, "PIO write mismatch at %0d", address_i);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic tail_read_test(input integer address_i, input logic width4_i,
|
||||
input integer byte_count_i);
|
||||
logic [31:0] word0;
|
||||
logic [31:0] word1;
|
||||
integer word_count;
|
||||
integer expected_words;
|
||||
begin
|
||||
configure_data(width4_i, 1'b0, byte_count_i, 16'd1);
|
||||
u_card.arm_read(address_i, byte_count_i, width4_i, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
expected_words = (byte_count_i + 3) / 4;
|
||||
word_count = 0;
|
||||
while (word_count < expected_words) begin
|
||||
wait_pio_valid();
|
||||
if (word_count == 0) begin
|
||||
apb_read(PioData, word0);
|
||||
end else begin
|
||||
apb_read(PioData, word1);
|
||||
end
|
||||
word_count = word_count + 1;
|
||||
end
|
||||
wait_data();
|
||||
for (integer byte_index = 0; byte_index < byte_count_i; byte_index++) begin
|
||||
if ((byte_index < 4 ? word0[(byte_index % 4)*8+:8] :
|
||||
word1[(byte_index % 4)*8+:8]) !=
|
||||
u_card.read_backing(
|
||||
address_i + byte_index
|
||||
)) begin
|
||||
$fatal(1, "tail read mismatch width4=%b bytes=%0d byte=%0d", width4_i, byte_count_i,
|
||||
byte_index);
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic tail_write_test(input integer address_i, input logic width4_i,
|
||||
input integer byte_count_i);
|
||||
integer expected_words;
|
||||
logic [31:0] word;
|
||||
logic [ 3:0] strb;
|
||||
integer remaining;
|
||||
begin
|
||||
configure_data(width4_i, 1'b1, byte_count_i, 16'd1);
|
||||
u_card.arm_write(address_i, byte_count_i, width4_i, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
expected_words = (byte_count_i + 3) / 4;
|
||||
for (integer word_index = 0; word_index < expected_words; word_index++) begin
|
||||
word = '0;
|
||||
remaining = byte_count_i - (word_index * 4);
|
||||
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (byte_index < remaining) begin
|
||||
word[byte_index*8+:8] = 8'hA0 + (word_index * 4) + byte_index;
|
||||
end
|
||||
end
|
||||
strb = (remaining >= 4) ? 4'hF : ((4'b0001 << remaining) - 1'b1);
|
||||
apb_write(PioData, word, strb);
|
||||
end
|
||||
wait_data();
|
||||
for (integer byte_index = 0; byte_index < byte_count_i; byte_index++) begin
|
||||
if (u_card.read_backing(address_i + byte_index) != (8'hA0 + byte_index)) begin
|
||||
$fatal(1, "tail write mismatch width4=%b bytes=%0d byte=%0d data=%h", width4_i,
|
||||
byte_count_i, byte_index, u_card.read_backing(address_i + byte_index));
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
apb_idle();
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
u_card.fill_memory(8'h20);
|
||||
u_card.set_response_delay(1);
|
||||
u_card.set_data_delay(1);
|
||||
|
||||
// 400 kHz command flow at the documented 72 MHz integration clock.
|
||||
apb_write(ClockCtrl, 32'h0000_5A01, 4'hF);
|
||||
apb_write(HostCtrl, 32'h0000_0005, 4'h1);
|
||||
apb_read(ClockActual, apb_value);
|
||||
if (apb_value != 32'd400000) begin
|
||||
$fatal(1, "400 kHz clock calculation mismatch: %0d", apb_value);
|
||||
end
|
||||
issue_command(6'd8, 32'h0000_01AA, 4'd8, 1'b1, 1'b1);
|
||||
apb_read(Resp0, apb_value);
|
||||
if (apb_value[31:8] != 24'h0001AA) begin
|
||||
$fatal(1, "CMD8 response echo mismatch: %h", apb_value);
|
||||
end
|
||||
|
||||
apb_write(TimeoutBusy, 32'd4, 4'hF);
|
||||
apb_write(CmdArg, 32'd0, 4'hF);
|
||||
apb_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd2, 2'd0, 6'd12}, 4'hF);
|
||||
apb_write(CmdStart, 32'd1, 4'h1);
|
||||
wait_command();
|
||||
apb_read(CmdStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0004) == 0) begin
|
||||
$fatal(1, "R1b command did not use TIMEOUT_BUSY");
|
||||
end
|
||||
apb_write(TimeoutBusy, 32'd1000000, 4'hF);
|
||||
|
||||
apb_write(IrqEnable, 32'h0000_0080, 4'h1);
|
||||
u_card.set_response_delay(1000);
|
||||
apb_write(CmdArg, 32'd0, 4'hF);
|
||||
apb_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd1, 2'd0, 6'd17}, 4'hF);
|
||||
apb_write(CmdStart, 32'd1, 4'h1);
|
||||
repeat (8) @(posedge clk_i);
|
||||
apb_write(HostCtrl, 32'h0000_0007, 4'h1);
|
||||
wait_command();
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0080) == 0) $fatal(1, "command abort IRQ missing");
|
||||
apb_write(IrqStatus, 32'h0000_0080, 4'h1);
|
||||
u_card.set_response_delay(1);
|
||||
|
||||
configure_data(1'b0, 1'b0, 16'd4, 16'd1);
|
||||
u_card.set_data_delay(1000);
|
||||
u_card.arm_read(3000, 4, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
repeat (8) @(posedge clk_i);
|
||||
apb_write(HostCtrl, 32'h0000_0007, 4'h1);
|
||||
wait_data();
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0080) == 0) $fatal(1, "data abort IRQ missing");
|
||||
apb_write(IrqStatus, 32'h0000_0080, 4'h1);
|
||||
u_card.set_data_delay(1);
|
||||
|
||||
u_axi_memory.clear_memory();
|
||||
u_axi_memory.write_word(32'h0000_0000, 32'h0000_0200);
|
||||
u_axi_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_axi_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_axi_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
u_axi_memory.set_backpressure(0, 3, 0, 0);
|
||||
configure_dma_data(1'b0, 1'b0, 16'd4);
|
||||
u_card.arm_read(3004, 4, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait (u_apb4_sdio.u_sdio_core.u_sdio_dma.s_state_q == 4'd2);
|
||||
apb_write(HostCtrl, 32'h0000_0007, 4'h1);
|
||||
wait_data();
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0080) == 0 || u_axi_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0002_0004) begin
|
||||
$fatal(1, "descriptor-fetch host abort did not drain/write back");
|
||||
end
|
||||
apb_write(IrqStatus, 32'h0000_0080, 4'h1);
|
||||
u_axi_memory.set_backpressure(0, 0, 0, 0);
|
||||
|
||||
apb_write(TimeoutData, 32'd4, 4'hF);
|
||||
apb_read(TimeoutCount, apb_value);
|
||||
timeout_count_before = apb_value;
|
||||
u_card.set_data_delay(1000);
|
||||
u_card.arm_read(3200, 4, 1'b0, 1'b0);
|
||||
configure_data(1'b0, 1'b0, 16'd4, 16'd1);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
apb_read(DataStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0004) == 0) $fatal(1, "data timeout phase not reported");
|
||||
apb_read(TimeoutCount, apb_value);
|
||||
if (apb_value != timeout_count_before + 1) begin
|
||||
$fatal(1, "timeout counter incremented incorrectly: before=%0d after=%0d",
|
||||
timeout_count_before, apb_value);
|
||||
end
|
||||
apb_write(TimeoutData, 32'd1000000, 4'hF);
|
||||
u_card.set_data_delay(1);
|
||||
|
||||
// SDSC and SDHC use the same native command path but different address
|
||||
// interpretation. The parameterized wrapper is run once for each card
|
||||
// geometry by the pytest entry points.
|
||||
issue_command(
|
||||
6'd17, CardHighCapacity ? (RunFourBit ? 32'd4 : 32'd2) : (RunFourBit ? 32'd2048 : 32'd1024),
|
||||
4'd1, 1'b1, 1'b1);
|
||||
issue_command(6'd12, 32'd0, 4'd2, 1'b1, 1'b1);
|
||||
configure_data(RunFourBit, 1'b0, 16'd4, 16'd1);
|
||||
read_pio(RunFourBit ? 2048 : 1024, RunFourBit, apb_value);
|
||||
if (apb_value != 32'h2322_2120) begin
|
||||
$fatal(1, "4-bit read mismatch: %h", apb_value);
|
||||
end
|
||||
|
||||
// Each APB PIO read consumes one held word. The second word must not
|
||||
// repeat the first word or require a reset between reads.
|
||||
configure_data(RunFourBit, 1'b0, 16'd8, 16'd1);
|
||||
u_card.arm_read(RunFourBit ? 2052 : 1028, 8, RunFourBit, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_pio_valid();
|
||||
apb_read(PioData, apb_value);
|
||||
if (apb_value != 32'h2726_2524) begin
|
||||
$fatal(1, "first back-to-back PIO word mismatch: %h", apb_value);
|
||||
end
|
||||
wait_pio_valid();
|
||||
apb_read(PioData, apb_value);
|
||||
if (apb_value != 32'h2B2A_2928) begin
|
||||
$fatal(1, "second back-to-back PIO word mismatch: %h", apb_value);
|
||||
end
|
||||
wait_data();
|
||||
|
||||
configure_data(1'b0, 1'b1, 16'd4, 16'd1);
|
||||
write_pio(3072, 1'b0, 32'hA1B2_C3D4);
|
||||
configure_data(1'b1, 1'b1, 16'd4, 16'd1);
|
||||
write_pio(4096, 1'b1, 32'h1020_3040);
|
||||
|
||||
// A complete PIO block uses a real ready/consume FIFO. The APB writes
|
||||
// below are intentionally consecutive; PREADY supplies the required
|
||||
// wait-states while the serializer drains the queue.
|
||||
apb_write(ClockCtrl, 32'h0000_0101, 4'hF);
|
||||
|
||||
// End-to-end DMA direction checks use the same card model and AXI
|
||||
// responder as the PIO path.
|
||||
u_axi_memory.clear_memory();
|
||||
u_axi_memory.write_word(32'h0000_0000, 32'h0000_0200);
|
||||
u_axi_memory.write_word(32'h0000_0004, 32'd5);
|
||||
u_axi_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_axi_memory.write_word(32'h0000_000C, 32'h0000_000D);
|
||||
configure_dma_data(1'b1, 1'b0, 16'd5);
|
||||
u_card.arm_read(20000, 5, 1'b1, 1'b0);
|
||||
for (integer byte_index = 0; byte_index < 5; byte_index++) begin
|
||||
u_card.write_backing(20000 + byte_index, 8'h60 + byte_index);
|
||||
end
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
apb_write(IrqEnable, 32'h0000_0084, 4'h1);
|
||||
repeat (2) @(posedge clk_i);
|
||||
apb_read(IrqStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0004) == 0) begin
|
||||
$fatal(1, "descriptor IRQ was not visible through the core");
|
||||
end
|
||||
apb_write(IrqStatus, 32'h0000_0004, 4'h1);
|
||||
if (u_axi_memory.read_word(
|
||||
32'h0000_0200
|
||||
) != 32'h6362_6160 || (u_axi_memory.read_word(
|
||||
32'h0000_0204
|
||||
) & 32'hFF) != 32'h64 || u_axi_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0001_000C) begin
|
||||
$fatal(1, "card-to-memory DMA end-to-end direction failed");
|
||||
end
|
||||
|
||||
u_axi_memory.clear_memory();
|
||||
u_axi_memory.write_word(32'h0000_0000, 32'h0000_0300);
|
||||
u_axi_memory.write_word(32'h0000_0004, 32'd5);
|
||||
u_axi_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_axi_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
u_axi_memory.write_word(32'h0000_0300, 32'hD4C3_B2A1);
|
||||
u_axi_memory.write_word(32'h0000_0304, 32'h0000_00E5);
|
||||
configure_dma_data(1'b0, 1'b1, 16'd5);
|
||||
u_card.arm_write(21000, 5, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
if (u_card.read_backing(
|
||||
21000
|
||||
) != 8'hA1 || u_card.read_backing(
|
||||
21001
|
||||
) != 8'hB2 || u_card.read_backing(
|
||||
21002
|
||||
) != 8'hC3 || u_card.read_backing(
|
||||
21003
|
||||
) != 8'hD4 || u_card.read_backing(
|
||||
21004
|
||||
) != 8'hE5 || u_axi_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0001_0004) begin
|
||||
$fatal(1, "memory-to-card DMA end-to-end direction failed");
|
||||
end
|
||||
|
||||
apb_read(AxiCount, apb_value);
|
||||
timeout_count_before = apb_value;
|
||||
u_axi_memory.clear_memory();
|
||||
u_axi_memory.write_word(32'h0000_0000, 32'h0000_0400);
|
||||
u_axi_memory.write_word(32'h0000_0004, 32'd4);
|
||||
u_axi_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_axi_memory.write_word(32'h0000_000C, 32'h0000_0005);
|
||||
u_axi_memory.write_word(32'h0000_0400, 32'h1122_3344);
|
||||
u_axi_memory.inject_read_error(32'h0000_0400);
|
||||
configure_dma_data(1'b0, 1'b1, 16'd4);
|
||||
u_card.arm_write(22000, 4, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
wait_data();
|
||||
apb_read(AxiCount, apb_value);
|
||||
if (apb_value != timeout_count_before + 1 || u_axi_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0002_0004) begin
|
||||
$fatal(1, "AXI error counter/writeback contract failed: before=%0d after=%0d",
|
||||
timeout_count_before, apb_value);
|
||||
end
|
||||
u_axi_memory.clear_errors();
|
||||
|
||||
apb_write(ClockCtrl, 32'h0000_0100, 4'hF);
|
||||
repeat (4) @(posedge clk_i);
|
||||
apb_write(ClockCtrl, 32'h0000_0101, 4'hF);
|
||||
configure_data(1'b0, 1'b1, 16'd512, 16'd1);
|
||||
u_card.arm_write(8192, 512, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
for (integer word_index = 0; word_index < 128; word_index++) begin
|
||||
logic [31:0] word;
|
||||
word = '0;
|
||||
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
word[byte_index*8+:8] = (word_index * 4) + byte_index;
|
||||
end
|
||||
apb_write(PioData, word, 4'hF);
|
||||
end
|
||||
wait_data();
|
||||
for (integer byte_index = 0; byte_index < 512; byte_index++) begin
|
||||
if (u_card.read_backing(8192 + byte_index) != byte_index[7:0]) begin
|
||||
$fatal(1, "512-byte PIO write mismatch at %0d: %h", byte_index, u_card.read_backing(
|
||||
8192 + byte_index));
|
||||
end
|
||||
end
|
||||
|
||||
configure_data(1'b0, 1'b0, 16'd512, 16'd1);
|
||||
u_card.arm_read(8192, 512, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
for (integer word_index = 0; word_index < 128; word_index++) begin
|
||||
wait_pio_valid();
|
||||
apb_read(PioData, apb_value);
|
||||
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (apb_value[byte_index*8+:8] != ((word_index * 4) + byte_index) % 256) begin
|
||||
$fatal(1, "512-byte PIO read mismatch word=%0d byte=%0d: %h", word_index, byte_index,
|
||||
apb_value);
|
||||
end
|
||||
end
|
||||
end
|
||||
wait_data();
|
||||
|
||||
for (integer tail_bytes = 1; tail_bytes <= 5; tail_bytes++) begin
|
||||
if (tail_bytes != 4) begin
|
||||
tail_read_test(12000 + (tail_bytes * 16), 1'b0, tail_bytes);
|
||||
tail_write_test(13000 + (tail_bytes * 16), 1'b0, tail_bytes);
|
||||
tail_read_test(14000 + (tail_bytes * 16), 1'b1, tail_bytes);
|
||||
tail_write_test(15000 + (tail_bytes * 16), 1'b1, tail_bytes);
|
||||
end
|
||||
end
|
||||
|
||||
apb_write(TimeoutBusy, 32'd4, 4'hF);
|
||||
configure_data(1'b0, 1'b1, 16'd4, 16'd1);
|
||||
u_card.arm_write(15996, 4, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
apb_write(PioData, 32'h4433_2211, 4'hF);
|
||||
wait_data();
|
||||
apb_read(DataStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0010) == 0) begin
|
||||
$fatal(1, "DAT0 busy timeout did not use TIMEOUT_BUSY");
|
||||
end
|
||||
apb_write(TimeoutBusy, 32'd1000000, 4'hF);
|
||||
|
||||
configure_data(1'b0, 1'b1, 16'd4, 16'd1);
|
||||
u_card.inject_next_crc_error();
|
||||
u_card.arm_write(16000, 4, 1'b0, 1'b0);
|
||||
apb_read(CrcCount, apb_value);
|
||||
crc_count_before = apb_value;
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
apb_write(PioData, 32'h4433_2211, 4'hF);
|
||||
wait_data();
|
||||
apb_read(DataStatus, apb_value);
|
||||
if ((apb_value & 32'h0000_0008) == 0) begin
|
||||
$fatal(1, "CRC-error write response token was not reported: status=%h dataerr=%b crc=%b",
|
||||
apb_value, u_apb4_sdio.u_sdio_core.s_data_err, u_apb4_sdio.u_sdio_core.s_data_crc_err);
|
||||
end
|
||||
apb_read(CrcCount, apb_value);
|
||||
if (apb_value != crc_count_before + 1) begin
|
||||
$fatal(1, "CRC counter incremented incorrectly: before=%0d after=%0d", crc_count_before,
|
||||
apb_value);
|
||||
end
|
||||
|
||||
configure_data(1'b0, 1'b1, 16'd4, 16'd1);
|
||||
u_card.inject_next_write_error();
|
||||
u_card.arm_write(16004, 4, 1'b0, 1'b0);
|
||||
apb_write(DataStart, 32'd1, 4'h1);
|
||||
apb_write(PioData, 32'h8877_6655, 4'hF);
|
||||
wait_data();
|
||||
apb_read(DataStatus, apb_value);
|
||||
if (((apb_value & 32'h0000_0002) == 0) || ((apb_value & 32'h0000_0008) != 0)) begin
|
||||
$fatal(1, "write-error response token was not distinguished: %h", apb_value);
|
||||
end
|
||||
|
||||
$display("SDIO standalone native SD model test passed (%s)",
|
||||
CardHighCapacity ? "SDHC" : "SDSC");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sdio_sdsc_tb;
|
||||
sdio_standalone_tb #(.CardHighCapacity(1'b0)) u_tb ();
|
||||
endmodule
|
||||
|
||||
module sdio_onebit_tb;
|
||||
sdio_standalone_tb #(.RunFourBit(1'b0)) u_tb ();
|
||||
endmodule
|
||||
@@ -0,0 +1,74 @@
|
||||
`timescale 1ns / 1ps
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module sdio_wrapper_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
sdio_if sdio ();
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign dma_axi4.awready = 1'b1;
|
||||
assign dma_axi4.wready = 1'b1;
|
||||
assign dma_axi4.bid = '0;
|
||||
assign dma_axi4.bresp = `AXI4_RESP_OKAY;
|
||||
assign dma_axi4.buser = '0;
|
||||
assign dma_axi4.bvalid = 1'b0;
|
||||
assign dma_axi4.arready = 1'b1;
|
||||
assign dma_axi4.rid = '0;
|
||||
assign dma_axi4.rdata = '0;
|
||||
assign dma_axi4.rresp = `AXI4_RESP_OKAY;
|
||||
assign dma_axi4.rlast = 1'b1;
|
||||
assign dma_axi4.ruser = '0;
|
||||
assign dma_axi4.rvalid = 1'b0;
|
||||
|
||||
apb4_sdio #(
|
||||
.InputClockHz(72_000_000),
|
||||
.AddrWidth (32),
|
||||
.DataWidth (32),
|
||||
.DescCount (16)
|
||||
) u_apb4_sdio (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.apb4 (apb4),
|
||||
.dma_axi4(dma_axi4),
|
||||
.sdio (sdio)
|
||||
);
|
||||
|
||||
initial begin
|
||||
apb4.paddr = '0;
|
||||
apb4.pprot = '0;
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = 32'h0000_0000;
|
||||
apb4.psel = 1'b1;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apb4.pready || apb4.pslverr || (apb4.prdata != 32'h5344_494F)) begin
|
||||
$fatal(1, "wrapper APB identification failed");
|
||||
end
|
||||
$display("SDIO wrapper elaboration and APB smoke test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -36,6 +36,8 @@ module soc_irq_topology_tb;
|
||||
case (bit_index)
|
||||
13: expect_irq(32'd1 << 15);
|
||||
14: expect_irq(32'd1 << 20);
|
||||
16: expect_irq(32'd1 << 10);
|
||||
17: expect_irq(32'd1 << 21);
|
||||
default: expect_irq(32'd1 << (bit_index + 7));
|
||||
endcase
|
||||
end
|
||||
@@ -56,8 +58,9 @@ module soc_irq_topology_tb;
|
||||
|
||||
s_apb4_system_irq = '0;
|
||||
expect_irq('0);
|
||||
if (s_irq[10] !== 1'b0) begin
|
||||
$fatal(1, "removed core IRQ bit is not low: IRQ10=%b", s_irq[10]);
|
||||
if ((s_irq[10] !== 1'b0) || (s_irq[21] !== 1'b0)) begin
|
||||
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
|
||||
s_irq[10], s_irq[21]);
|
||||
end
|
||||
if ((s_irq[31:21] !== '0) || (s_irq[19:18] !== '0)) begin
|
||||
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
|
||||
|
||||
+21
-11
@@ -84,6 +84,8 @@ module sysctrl_tb;
|
||||
sysctrl.perf_mgmt_wait_i = 64'd11;
|
||||
sysctrl.perf_user_wait_i = 64'd12;
|
||||
sysctrl.perf_dma_wait_i = 64'd13;
|
||||
sysctrl.perf_sdio0_wait_i = 64'd131;
|
||||
sysctrl.perf_sdio1_wait_i = 64'd141;
|
||||
sysctrl.perf_apb4_periph_wait_i = 64'd14;
|
||||
sysctrl.perf_apb4_system_wait_i = 64'd15;
|
||||
sysctrl.perf_sdram_wait_i = 64'd16;
|
||||
@@ -102,32 +104,36 @@ module sysctrl_tb;
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
@(negedge clk_i);
|
||||
fault_valid_i = 1'b1;
|
||||
fault_addr_i = 32'h1000_F000;
|
||||
fault_wstrb_i = 4'hF;
|
||||
fault_reserved_i = 1'b1;
|
||||
sysctrl.fault_master_i = 3'd3;
|
||||
fault_valid_i = 1'b1;
|
||||
fault_addr_i = 32'h1001_2000;
|
||||
fault_wstrb_i = 4'hF;
|
||||
fault_reserved_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fault_valid_i = 1'b0;
|
||||
|
||||
read_register(32'h1000_B010, read_data);
|
||||
if (read_data !== 32'h0000_000B) $fatal(1, "fault status was not recorded");
|
||||
read_register(32'h1000_B014, read_data);
|
||||
if (read_data !== 32'h1000_F000) $fatal(1, "fault address was not recorded");
|
||||
if (read_data !== 32'h1001_2000) $fatal(1, "fault address was not recorded");
|
||||
read_register(32'h1000_B018, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "fault count was not incremented");
|
||||
read_register(32'h1000_B028, read_data);
|
||||
if (read_data !== 32'h0000_0003) $fatal(1, "three-bit fault master was not recorded");
|
||||
read_register(32'h1000_B02C, read_data);
|
||||
if (read_data !== `RIB_RESP_RESERVED) $fatal(1, "fault detail was not recorded");
|
||||
|
||||
@(negedge clk_i);
|
||||
sysctrl.fault_code_i = `RIB_RESP_DECERR;
|
||||
fault_valid_i = 1'b1;
|
||||
fault_addr_i = 32'hA000_0000;
|
||||
fault_wstrb_i = 4'h0;
|
||||
fault_reserved_i = 1'b0;
|
||||
sysctrl.fault_master_i = 3'd4;
|
||||
sysctrl.fault_code_i = `RIB_RESP_DECERR;
|
||||
fault_valid_i = 1'b1;
|
||||
fault_addr_i = 32'hA000_0000;
|
||||
fault_wstrb_i = 4'h0;
|
||||
fault_reserved_i = 1'b0;
|
||||
@(negedge clk_i);
|
||||
fault_valid_i = 1'b0;
|
||||
read_register(32'h1000_B014, read_data);
|
||||
if (read_data !== 32'h1000_F000) $fatal(1, "later fault overwrote first fault address");
|
||||
if (read_data !== 32'h1001_2000) $fatal(1, "later fault overwrote first fault address");
|
||||
read_register(32'h1000_B018, read_data);
|
||||
if (read_data !== 32'h0000_0002) $fatal(1, "later fault did not increment count");
|
||||
read_register(32'h1000_B02C, read_data);
|
||||
@@ -140,6 +146,10 @@ module sysctrl_tb;
|
||||
write_register(32'h1000_B040, 32'h0000_0005);
|
||||
read_register(32'h1000_B044, read_data);
|
||||
if (read_data !== 32'd11) $fatal(1, "performance snapshot was not recorded");
|
||||
read_register(32'h1000_B08C, read_data);
|
||||
if (read_data !== 32'd131) $fatal(1, "SDIO0 performance snapshot was not recorded");
|
||||
read_register(32'h1000_B094, read_data);
|
||||
if (read_data !== 32'd141) $fatal(1, "SDIO1 performance snapshot was not recorded");
|
||||
|
||||
read_register(32'h1000_B020, read_data);
|
||||
if (read_data !== 32'h0000_003F) $fatal(1, "user cores were not held in reset");
|
||||
|
||||
@@ -63,6 +63,8 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
|
||||
assert "#ifdef __ASSEMBLER__" in header
|
||||
assert "#define UINT32_C(value) value" in header
|
||||
assert "RS_SOC_APB4_SDIO_BASE" not in header
|
||||
assert "RS_SOC_APB4_SDIO0_BASE UINT32_C(0x1000F000)" in header
|
||||
assert "RS_SOC_APB4_SDIO1_BASE UINT32_C(0x10015000)" in header
|
||||
assert "RS_SOC_NMI_" not in header
|
||||
assert "RS_SOC_OPIPSRAM_BASE UINT32_C(0x48000000)" in header
|
||||
assert "RS_SOC_HAS_SRAM 1U" in header
|
||||
@@ -70,6 +72,10 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
|
||||
assert "SOC_SYSCTRL_PLL_STATUS_OFFSET 32'h0000001C" in rtl
|
||||
assert "SOC_SYSCTRL_TEST_STATUS_OFFSET 32'h00000084" in rtl
|
||||
assert "SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET 32'h00000088" in rtl
|
||||
assert "SOC_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET 32'h0000008C" in rtl
|
||||
assert "SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET 32'h00000090" in rtl
|
||||
assert "SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET 32'h00000094" in rtl
|
||||
assert "SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET 32'h00000098" in rtl
|
||||
assert "SOC_ADDR_IS_APB4_PERIPH(addr)" in rtl
|
||||
assert "SOC_ADDR_IS_NMI" not in rtl
|
||||
user_policy = rtl.split("`define SOC_USER_ADDR_READABLE", 1)[1].split(
|
||||
@@ -82,6 +88,10 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
|
||||
assert "RS_SOC_SYSCTRL_PLL_STATUS_OFFSET UINT32_C(0x0000001C)" in header
|
||||
assert "RS_SOC_SYSCTRL_TEST_STATUS_OFFSET UINT32_C(0x00000084)" in header
|
||||
assert "RS_SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET UINT32_C(0x00000088)" in header
|
||||
assert "RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_LO_OFFSET UINT32_C(0x0000008C)" in header
|
||||
assert "RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET UINT32_C(0x00000090)" in header
|
||||
assert "RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET UINT32_C(0x00000094)" in header
|
||||
assert "RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET UINT32_C(0x00000098)" in header
|
||||
assert "PSRAM (wxa!ri) : ORIGIN = 0x40000000, LENGTH = 0x02000000" in linker
|
||||
assert "OPIPSRAM (wxa!ri) : ORIGIN = 0x48000000, LENGTH = 0x08000000" in linker
|
||||
|
||||
@@ -112,6 +122,8 @@ def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> Non
|
||||
assert "`define SOC_ADDR_APB4_OPIPSRAM_BASE 32'h10010000" in rtl
|
||||
assert "`define SOC_ADDR_IS_OPIPSRAM(addr)" in rtl
|
||||
assert "#define RS_SOC_APB4_OPIPSRAM_BASE UINT32_C(0x10010000)" in header
|
||||
assert "`define SOC_ADDR_APB4_SDIO0_BASE 32'h1000F000" in rtl
|
||||
assert "`define SOC_ADDR_APB4_SDIO1_BASE 32'h10015000" in rtl
|
||||
|
||||
|
||||
def test_bootstrap_assembly_uses_the_generated_gpio_admin_base() -> None:
|
||||
|
||||
@@ -54,10 +54,19 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
||||
assert "inout jtag_tck_i_pad" in ports
|
||||
assert "inout jtag_trst_n_i_pad" in ports
|
||||
assert "output jtag_tdo_o_pad" in ports
|
||||
assert "output sdio1_clk_o_pad," in ports
|
||||
assert "inout sdio1_cmd_io_pad," in ports
|
||||
assert "inout sdio1_dat3_io_pad," in ports
|
||||
assert "HAVE_DEBUG" not in ports
|
||||
assert "HAVE_DEBUG" not in pads
|
||||
assert "u_gpio_31_io_pad" in pads
|
||||
assert "u_sdram_dq15_io_pad" in pads
|
||||
assert "u_sdio1_clk_o_pad" in pads
|
||||
assert "tc_io_out_pad u_sdio1_clk_o_pad" in pads
|
||||
assert "u_sdio1_cmd_io_pad" in pads
|
||||
assert ".c2p(u_sdio1_if.cmd_do_o)" in pads
|
||||
assert ".c2p_en(u_sdio1_if.cmd_oe_o)" in pads
|
||||
assert ".p2c(u_sdio1_if.cmd_di_i)" in pads
|
||||
assert ".gpio_30_io_pad(gpio_io30)" in fpga
|
||||
assert ".gpio_15_io_pad()" in fpga
|
||||
assert ".gpio_24_io_pad(s_psram_dat1)" in testbench
|
||||
@@ -66,6 +75,14 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
||||
assert ".sdram_dq15_io_pad(s_sdram_dq[15])" in verilator
|
||||
assert ".jtag_tck_i_pad(s_jtag_tck)" in verilator
|
||||
assert ".jtag_tdo_o_pad(s_jtag_tdo)" in verilator
|
||||
assert ".sdio1_clk_o_pad(sdio1_clk_o)" in verilator
|
||||
assert ".sdio1_cmd_io_pad(sdio1_cmd_io)" in verilator
|
||||
assert ".sdio1_dat3_io_pad(sdio1_dat3_io)" in verilator
|
||||
assert ".sdio1_clk_o_pad(s_sdio1_clk)" in testbench
|
||||
assert ".sdio1_cmd_io_pad(s_sdio1_cmd)" in testbench
|
||||
assert ".sdio1_dat0_io_pad(s_sdio1_dat[0])" in testbench
|
||||
assert ".sdio1_clk_o_pad()" in fpga
|
||||
assert ".sdio1_cmd_io_pad()" in fpga
|
||||
assert ".jtag_tck_i_pad(s_jtag_tck)" in testbench
|
||||
assert "user_gpio_" not in ports
|
||||
assert "user_gpio_" not in pads
|
||||
@@ -108,6 +125,18 @@ def test_pin_map_rejects_unknown_profile_pad(tmp_path: Path) -> None:
|
||||
assert "unknown pad" in result.stderr
|
||||
|
||||
|
||||
def test_pin_map_requires_explicit_bidirectional_peripheral_signals(tmp_path: Path) -> None:
|
||||
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
|
||||
document["pads"][8]["ports"][0].pop("output_enable")
|
||||
invalid_map = tmp_path / "missing-bidir-signal.json"
|
||||
invalid_map.write_text(json.dumps(document), encoding="utf-8")
|
||||
|
||||
result = validate(invalid_map)
|
||||
|
||||
assert result.returncode != 0
|
||||
assert "requires input, output, and output_enable" in result.stderr
|
||||
|
||||
|
||||
def test_fpga_constraints_reference_declared_top_ports() -> None:
|
||||
top_ports = set(
|
||||
re.findall(
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
"""Focused standalone SDIO RTL regressions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CONVERTER = ROOT / "rtl/mini/script/convt_sv2v.py"
|
||||
BUILD = ROOT / "build/test-sdio"
|
||||
|
||||
SDIO_FULL_SOURCES = [
|
||||
"rtl/managed/clusterip/common/rtl/interface/apb4_if.sv",
|
||||
"rtl/managed/clusterip/common/rtl/interface/axi4_if.sv",
|
||||
"rtl/ip/storage/sdio_if.sv",
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_define.svh",
|
||||
"rtl/ip/storage/sdio_crc7.sv",
|
||||
"rtl/ip/storage/sdio_crc16.sv",
|
||||
"rtl/ip/storage/sdio_clock.sv",
|
||||
"rtl/ip/storage/sdio_command.sv",
|
||||
"rtl/ip/storage/sdio_data.sv",
|
||||
"rtl/ip/storage/sdio_dma_descriptor.sv",
|
||||
"rtl/ip/peripheral/dma_axi4_master.sv",
|
||||
"rtl/ip/storage/sdio_dma.sv",
|
||||
"rtl/ip/storage/sdio_reg.sv",
|
||||
"rtl/ip/storage/sdio_core.sv",
|
||||
"rtl/ip/storage/apb4_sdio.sv",
|
||||
"tests/rtl/sdio_native_model.sv",
|
||||
"tests/rtl/sdio_axi_memory_responder.sv",
|
||||
]
|
||||
|
||||
SDIO_FULL_INCDIRS = [
|
||||
"rtl/managed/clusterip/common/rtl/interface",
|
||||
"rtl/ip/peripheral",
|
||||
"rtl/ip/storage",
|
||||
]
|
||||
|
||||
|
||||
def _run_tb(name: str, top: str, sources: list[str], incdirs: list[str], marker: str) -> None:
|
||||
iverilog = shutil.which("iverilog")
|
||||
vvp = shutil.which("vvp")
|
||||
if iverilog is None or vvp is None:
|
||||
return
|
||||
BUILD.mkdir(parents=True, exist_ok=True)
|
||||
filelist = BUILD / f"{name}.fl"
|
||||
filelist.write_text(
|
||||
"\n".join([*(f"+incdir+{ROOT / path}" for path in incdirs), *(str(ROOT / path) for path in sources)])
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
converted = BUILD / f"{name}.v"
|
||||
simulation = BUILD / f"{name}.vvp"
|
||||
subprocess.run(
|
||||
["python3", str(CONVERTER), "-f", str(filelist), "--output", str(converted)],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run([iverilog, "-g2012", "-s", top, "-o", str(simulation), str(converted)], check=True)
|
||||
result = subprocess.run([vvp, str(simulation)], check=True, text=True, capture_output=True)
|
||||
assert marker in result.stdout
|
||||
|
||||
|
||||
def _run_verilator_tb(
|
||||
name: str, top: str, sources: list[str], incdirs: list[str], marker: str
|
||||
) -> None:
|
||||
verilator = shutil.which("verilator")
|
||||
if verilator is None:
|
||||
return
|
||||
build_dir = BUILD / f"{name}-verilator"
|
||||
obj_dir = build_dir / "obj"
|
||||
obj_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_paths = [str(ROOT / path) for path in sources]
|
||||
include_args = [f"-I{ROOT / path}" for path in incdirs]
|
||||
subprocess.run(
|
||||
[
|
||||
verilator,
|
||||
"--binary",
|
||||
"--timing",
|
||||
"--top-module",
|
||||
top,
|
||||
"-Mdir",
|
||||
str(obj_dir),
|
||||
"-o",
|
||||
"simv",
|
||||
*include_args,
|
||||
*source_paths,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
result = subprocess.run(
|
||||
[str(obj_dir / "simv")], check=True, text=True, capture_output=True
|
||||
)
|
||||
assert marker in result.stdout
|
||||
|
||||
|
||||
def test_sdio_crc_vectors() -> None:
|
||||
_run_tb(
|
||||
"crc",
|
||||
"sdio_crc_tb",
|
||||
[
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_crc7.sv",
|
||||
"rtl/ip/storage/sdio_crc16.sv",
|
||||
"tests/rtl/sdio_crc_tb.sv",
|
||||
],
|
||||
["rtl/ip/storage"],
|
||||
"SDIO CRC vectors passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_phase_clock() -> None:
|
||||
_run_tb(
|
||||
"clock",
|
||||
"sdio_clock_tb",
|
||||
["rtl/ip/storage/sdio_clock.sv", "tests/rtl/sdio_clock_tb.sv"],
|
||||
[],
|
||||
"SDIO phase clock test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_command_engine() -> None:
|
||||
_run_tb(
|
||||
"command",
|
||||
"sdio_command_tb",
|
||||
[
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_clock.sv",
|
||||
"rtl/ip/storage/sdio_command.sv",
|
||||
"tests/rtl/sdio_command_tb.sv",
|
||||
],
|
||||
["rtl/ip/storage"],
|
||||
"SDIO command engine test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_registers() -> None:
|
||||
_run_tb(
|
||||
"register",
|
||||
"sdio_register_tb",
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface/apb4_if.sv",
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_define.svh",
|
||||
"rtl/ip/storage/sdio_reg.sv",
|
||||
"tests/rtl/sdio_register_tb.sv",
|
||||
],
|
||||
["rtl/managed/clusterip/common/rtl/interface", "rtl/ip/storage"],
|
||||
"SDIO APB register test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_descriptors() -> None:
|
||||
_run_tb(
|
||||
"descriptor",
|
||||
"sdio_descriptor_tb",
|
||||
[
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_dma_descriptor.sv",
|
||||
"tests/rtl/sdio_descriptor_tb.sv",
|
||||
],
|
||||
["rtl/ip/storage"],
|
||||
"SDIO descriptor validation test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_dma() -> None:
|
||||
_run_tb(
|
||||
"dma",
|
||||
"sdio_dma_tb",
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface/axi4_if.sv",
|
||||
"rtl/ip/peripheral/dma_axi4_master.sv",
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_dma.sv",
|
||||
"tests/rtl/sdio_dma_tb.sv",
|
||||
],
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface",
|
||||
"rtl/ip/peripheral",
|
||||
"rtl/ip/storage",
|
||||
],
|
||||
"SDIO DMA descriptor, tail, and 4 KiB split test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_wrapper() -> None:
|
||||
_run_tb(
|
||||
"wrapper",
|
||||
"sdio_wrapper_tb",
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface/apb4_if.sv",
|
||||
"rtl/managed/clusterip/common/rtl/interface/axi4_if.sv",
|
||||
"rtl/ip/storage/sdio_if.sv",
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_define.svh",
|
||||
"rtl/ip/storage/sdio_crc7.sv",
|
||||
"rtl/ip/storage/sdio_crc16.sv",
|
||||
"rtl/ip/storage/sdio_clock.sv",
|
||||
"rtl/ip/storage/sdio_command.sv",
|
||||
"rtl/ip/storage/sdio_data.sv",
|
||||
"rtl/ip/storage/sdio_dma_descriptor.sv",
|
||||
"rtl/ip/peripheral/dma_axi4_master.sv",
|
||||
"rtl/ip/storage/sdio_dma.sv",
|
||||
"rtl/ip/storage/sdio_reg.sv",
|
||||
"rtl/ip/storage/sdio_core.sv",
|
||||
"rtl/ip/storage/apb4_sdio.sv",
|
||||
"tests/rtl/sdio_wrapper_tb.sv",
|
||||
],
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface",
|
||||
"rtl/ip/peripheral",
|
||||
"rtl/ip/storage",
|
||||
],
|
||||
"SDIO wrapper elaboration and APB smoke test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_standalone_sdhc() -> None:
|
||||
_run_tb(
|
||||
"standalone-sdhc",
|
||||
"sdio_standalone_tb",
|
||||
[*SDIO_FULL_SOURCES, "tests/rtl/sdio_standalone_tb.sv"],
|
||||
SDIO_FULL_INCDIRS,
|
||||
"SDIO standalone native SD model test passed (SDHC)",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_standalone_sdsc() -> None:
|
||||
_run_tb(
|
||||
"standalone-sdsc",
|
||||
"sdio_sdsc_tb",
|
||||
[*SDIO_FULL_SOURCES, "tests/rtl/sdio_standalone_tb.sv"],
|
||||
SDIO_FULL_INCDIRS,
|
||||
"SDIO standalone native SD model test passed (SDSC)",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_standalone_onebit() -> None:
|
||||
_run_tb(
|
||||
"standalone-onebit",
|
||||
"sdio_onebit_tb",
|
||||
[*SDIO_FULL_SOURCES, "tests/rtl/sdio_standalone_tb.sv"],
|
||||
SDIO_FULL_INCDIRS,
|
||||
"SDIO standalone native SD model test passed (SDHC)",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_only_device() -> None:
|
||||
_run_tb(
|
||||
"sdio-only",
|
||||
"sdio_sdio_tb",
|
||||
[*SDIO_FULL_SOURCES, "tests/rtl/sdio_sdio_tb.sv"],
|
||||
SDIO_FULL_INCDIRS,
|
||||
"SDIO-only CMD5/CMD52/CMD53/DAT1 test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_dual_instance() -> None:
|
||||
_run_tb(
|
||||
"dual",
|
||||
"sdio_dual_tb",
|
||||
[*SDIO_FULL_SOURCES, "tests/rtl/sdio_dual_tb.sv"],
|
||||
SDIO_FULL_INCDIRS,
|
||||
"SDIO dual-instance isolation and concurrent traffic test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_dma_stress_contract() -> None:
|
||||
_run_tb(
|
||||
"dma-stress",
|
||||
"sdio_dma_stress_tb",
|
||||
[
|
||||
"rtl/managed/clusterip/common/rtl/interface/axi4_if.sv",
|
||||
"rtl/ip/peripheral/dma_axi4_master.sv",
|
||||
"rtl/ip/storage/sdio_pkg.sv",
|
||||
"rtl/ip/storage/sdio_dma.sv",
|
||||
"tests/rtl/sdio_axi_memory_responder.sv",
|
||||
"tests/rtl/sdio_dma_stress_tb.sv",
|
||||
],
|
||||
["rtl/managed/clusterip/common/rtl/interface", "rtl/ip/peripheral", "rtl/ip/storage"],
|
||||
"SDIO AXI 16-beat, 4 KiB split, error, and abort test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_50mhz_clock() -> None:
|
||||
_run_tb(
|
||||
"50mhz",
|
||||
"sdio_50mhz_tb",
|
||||
["rtl/ip/storage/sdio_clock.sv", "tests/rtl/sdio_50mhz_tb.sv"],
|
||||
[],
|
||||
"SDIO 100 MHz to 50 MHz SDCLK test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_50mhz_clock_verilator() -> None:
|
||||
_run_verilator_tb(
|
||||
"50mhz",
|
||||
"sdio_50mhz_tb",
|
||||
["rtl/ip/storage/sdio_clock.sv", "tests/rtl/sdio_50mhz_tb.sv"],
|
||||
[],
|
||||
"SDIO 100 MHz to 50 MHz SDCLK test passed",
|
||||
)
|
||||
|
||||
|
||||
def test_sdio_sdk_high_speed_enable_sequence() -> None:
|
||||
"""Keep the SDK high-speed capability/enable/verify/clock order explicit."""
|
||||
|
||||
source = (ROOT / "crt/src/hal/sdio.c").read_text(encoding="utf-8")
|
||||
start = source.index("rs_sdio_function_initialize")
|
||||
end = source.index("rs_sdio_function_enable", start)
|
||||
initialize = source[start:end]
|
||||
capability_read = initialize.index('rs_sdio_function_cmd52_read(instance, 0U, 0x13U')
|
||||
enable_write = initialize.index("rs_sdio_function_cmd52_write", capability_read)
|
||||
verify_read = initialize.index('rs_sdio_function_cmd52_read(instance, 0U, 0x13U', enable_write)
|
||||
clock_change = initialize.index("rs_sdio_clock_set", verify_read)
|
||||
assert capability_read < enable_write < verify_read < clock_change
|
||||
assert "value | UINT8_C(0x02)" in initialize
|
||||
assert "value & UINT8_C(0x02)" in initialize
|
||||
high_speed_info = initialize.index("info->high_speed = high_speed_enabled")
|
||||
assert clock_change < high_speed_info
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Check the handwritten SDIO ABI against the RTL register contract."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RTL_DEFINE = ROOT / "rtl/ip/storage/sdio_define.svh"
|
||||
C_HEADER = ROOT / "crt/include/retrosoc/hal/sdio_regs.h"
|
||||
|
||||
|
||||
def _rtl_values() -> dict[str, int]:
|
||||
values: dict[str, int] = {}
|
||||
pattern = re.compile(
|
||||
r"^`define\s+(APB4_SDIO__\w+)\s+"
|
||||
r"(?:(\d+)'([hHdDbB])([0-9a-fA-F_xXzZ]+)|(\d+))\s*$"
|
||||
)
|
||||
for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines():
|
||||
match = pattern.match(line)
|
||||
if match is None:
|
||||
continue
|
||||
name, width, base, digits, decimal = match.groups()
|
||||
del width
|
||||
if decimal is not None:
|
||||
values[name] = int(decimal, 10)
|
||||
else:
|
||||
radix = {"h": 16, "d": 10, "b": 2}[base.lower()]
|
||||
values[name] = int(digits.replace("_", ""), radix)
|
||||
return values
|
||||
|
||||
|
||||
def _c_values() -> dict[str, int]:
|
||||
values: dict[str, int] = {}
|
||||
pattern = re.compile(
|
||||
r"^#define\s+(RS_SDIO_ABI_\w+)\s+"
|
||||
r"(?:UINT32_C\((0[xX][0-9a-fA-F]+)\)|(\d+)[uU]?)\s*$"
|
||||
)
|
||||
for line in C_HEADER.read_text(encoding="utf-8").splitlines():
|
||||
match = pattern.match(line)
|
||||
if match is None:
|
||||
continue
|
||||
name, hexadecimal, decimal = match.groups()
|
||||
values[name.removeprefix("RS_SDIO_ABI_")] = int(
|
||||
hexadecimal, 16
|
||||
) if hexadecimal is not None else int(decimal, 10)
|
||||
return values
|
||||
|
||||
|
||||
def test_sdio_register_constants_match_expected_abi() -> None:
|
||||
rtl = _rtl_values()
|
||||
c_header = _c_values()
|
||||
assert c_header == {
|
||||
name.removeprefix("APB4_SDIO__"): value for name, value in rtl.items()
|
||||
}
|
||||
@@ -74,14 +74,15 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
|
||||
irq_sva = (tmp_path / "rtl/soc_irq_sva.svh").read_text(encoding="utf-8")
|
||||
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
|
||||
|
||||
assert interfaces.count("apb4_if u_") == 17
|
||||
assert interfaces.count("apb4_if u_") == 18
|
||||
assert "nmi_if" not in interfaces
|
||||
assert "soc_nmi" not in interfaces
|
||||
assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes
|
||||
assert "assign s_psel_comb[1] = `SOC_ADDR_IS_APB4_GPIO(s_decode_addr) || `SOC_ADDR_IS_APB4_GPIO_ADMIN(s_decode_addr);" in routes
|
||||
assert "SOC_ADDR_FLASH" not in routes
|
||||
assert "assign s_psel_comb[15] = 1'b0;" in routes
|
||||
assert "assign s_psel_comb[15] = `SOC_ADDR_IS_APB4_SDIO0(s_decode_addr);" in routes
|
||||
assert "assign s_psel_comb[16] = `SOC_ADDR_IS_APB4_OPIPSRAM(s_decode_addr);" in routes
|
||||
assert "assign s_psel_comb[18] = `SOC_ADDR_IS_APB4_SDIO1(s_decode_addr);" in routes
|
||||
assert gpio.count("// GPIO") == 64
|
||||
assert "u_uart1_if" not in gpio
|
||||
assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio
|
||||
@@ -89,6 +90,8 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
|
||||
assert "assign u_gpio_if.alt0_oe_i[0] = 1'b0;" in gpio
|
||||
assert "assign u_gpio_if.alt0_do_i[1] = u_uart0_if.rts_n_o;" in gpio
|
||||
assert "assign u_gpio_if.alt0_oe_i[1] = 1'b1;" in gpio
|
||||
assert "assign u_gpio_if.alt0_do_i[15] = u_sdio0_if.sck_o;" in gpio
|
||||
assert "assign u_sdio0_if.cmd_di_i = u_gpio_if.di_i[16];" in gpio
|
||||
assert "assign u_gpio_if.alt1_do_i[0] = u_ps2_if.ps2_clk_o;" in gpio
|
||||
assert "assign u_gpio_if.alt1_oe_i[0] = u_ps2_if.ps2_clk_oe_o;" in gpio
|
||||
assert "assign u_gpio_if.alt1_do_i[1] = u_ps2_if.ps2_dat_o;" in gpio
|
||||
@@ -113,16 +116,18 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
|
||||
assert "assign user_ip.paddr = s_addr_q;" in apb_routes
|
||||
assert "({32{s_psel_q[7]}} & user_ip.prdata)" in apb_response
|
||||
assert "localparam int NSLV = 8;" in apb_declarations
|
||||
assert fabric.count("axi4_if #(") == 5
|
||||
assert fabric.count("axi4_if #(") == 7
|
||||
assert ".mgmt_axi4(u_mgmt_axi4_if)" in bus_fabric
|
||||
assert ".user_axi4(u_user_axi4_if)" in bus_fabric
|
||||
assert ".dma_axi4(u_dma_axi4_if)" in bus_fabric
|
||||
assert ".sdio0_axi4(u_sdio0_axi4_if)" in bus_fabric
|
||||
assert ".sdio1_axi4(u_sdio1_axi4_if)" in bus_fabric
|
||||
assert ".cfg_axi4(u_cfg_axi4_if)" in bus_fabric
|
||||
assert ".system_axi4(u_system_axi4_if)" in bus_fabric
|
||||
assert ".axi4(u_system_axi4_if)" in apb_system_fabric
|
||||
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
|
||||
assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config
|
||||
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 16" in irq_config
|
||||
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 18" in irq_config
|
||||
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 5" in irq_config
|
||||
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
|
||||
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
|
||||
@@ -131,17 +136,21 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
|
||||
assert "assign irq_o[13] = s_dvp_irq;" in rib_irq
|
||||
assert "assign irq_o[14] = s_dma_irq;" in rib_irq
|
||||
assert "assign irq_o[15] = opipsram.irq_o;" in rib_irq
|
||||
assert "assign irq_o[16] = sdio0.irq_o;" in rib_irq
|
||||
assert "assign irq_o[17] = sdio1.irq_o;" in rib_irq
|
||||
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
|
||||
assert "assign irq_o[4] = s_rng_irq;" in apb_irq
|
||||
assert "s_irq[10]" not in irq_wiring
|
||||
assert "s_irq[10] = s_apb4_periph_irq[16];" in irq_wiring
|
||||
assert "s_irq[15] = s_apb4_periph_irq[13];" in irq_wiring
|
||||
assert "s_irq[16] = s_apb4_system_irq[4];" in irq_wiring
|
||||
assert "s_irq[20] = s_apb4_periph_irq[14];" in irq_wiring
|
||||
assert "s_irq[22] = s_apb4_periph_irq[15];" in irq_wiring
|
||||
assert "irq_i[10] == 1'b0" in irq_sva
|
||||
assert "s_irq[21] = s_apb4_periph_irq[17];" in irq_wiring
|
||||
assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva
|
||||
assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva
|
||||
assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva
|
||||
assert "irq_i[22] == apb4_periph_irq_i[15]" in irq_sva
|
||||
assert "irq_i[21] == apb4_periph_irq_i[17]" in irq_sva
|
||||
assert "irq_i[31] == 1'b0" in irq_sva
|
||||
assert "bind retrosoc soc_irq_topology_sva" in irq_sva
|
||||
assert filelist.startswith("+incdir+")
|
||||
@@ -171,6 +180,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
|
||||
("i2c0", "apb4_periph", 7, 7, "i2c0.irq_o"),
|
||||
("i2s", "apb4_periph", 8, 8, "i2s.irq_o"),
|
||||
("xpi", "apb4_periph", 9, 9, "xpi.irq_o"),
|
||||
("sdio0", "apb4_periph", 16, 10, "sdio0.irq_o"),
|
||||
("pwm", "apb4_system", 0, 11, "pwm.irq_o"),
|
||||
("ps2", "apb4_system", 1, 12, "ps2.irq_o"),
|
||||
("rtc", "apb4_system", 2, 13, "u_rtc_if.irq_o"),
|
||||
@@ -182,6 +192,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
|
||||
("dvp", "apb4_periph", 13, 15, "s_dvp_irq"),
|
||||
("dma", "apb4_periph", 14, 20, "s_dma_irq"),
|
||||
("opipsram", "apb4_periph", 15, 22, "opipsram.irq_o"),
|
||||
("sdio1", "apb4_periph", 17, 21, "sdio1.irq_o"),
|
||||
]
|
||||
|
||||
|
||||
@@ -227,7 +238,8 @@ def test_topology_rejects_duplicate_region_and_disabled_owner(tmp_path: Path) ->
|
||||
assert "multiple targets" in result.stderr
|
||||
|
||||
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
|
||||
document["apb4_periph_targets"][15]["regions"] = ["APB4_SDIO"]
|
||||
document["apb4_periph_targets"][15]["disabled"] = True
|
||||
document["apb4_periph_targets"][15]["regions"] = ["APB4_SDIO0"]
|
||||
result = validate(write_invalid_topology(tmp_path, document))
|
||||
assert result.returncode != 0
|
||||
assert "disabled but declares regions" in result.stderr
|
||||
|
||||
Reference in new issue
Block a user