From 31daf3e84c78b73750bfb25bfdd2efb36f77acb1 Mon Sep 17 00:00:00 2001 From: maksyuki Date: Thu, 22 Oct 2026 11:57:29 +0800 Subject: [PATCH] feat(spi): implement SPI-P3 Tiny GPIO RCU and SDK integration Integrate the SPI-P2 shared-DMA ABI into the Tiny product: SPI0 at 0x1001D000 with IRQ25 and RCU target16, the four frozen Gen1 ALT routes, latched four-bit source qualification through the fabric and APB CDC, GPIO readiness/ownership guard, and the bounded rs_spi_* HAL with the TINY_SPI_P3_ACCEPTANCE ci_smoke image. Tiny DMA enables V2.2 requests 16/17 with exact fixed FIFO MMIO checks; Mini remains default-disabled and unchanged. Review closure: the DMA AXI4 master stages the origin channel with each burst so final and contended SPI alias bursts stay qualified, the RCU spare feedback bit is tied off for four-state simulators, and the clock-switch quiescence now includes the SPI session. Validation: full Pytest 2097 passed, 0 failed, 1 skipped (optional pypdf); both Tiny profiles pass the SPI-P3 Verilator acceptance (SIM_TEST_PASS cycles=1030088); R2-P7 Verilator acceptance, Mini DMA formal prove and cover, Mini/Tiny lint, Tiny Yosys synthesis, netsim-boot and OpenSTA pass; C format/policy/host tests and RTL format/style checks clean. Evidence and remaining obligations are recorded in docs/ip/spi-verification.md. Tiny Icarus behavioral remains blocked by the pre-existing observer/sv2v defect (routed to the R2 DV owners); Mini smoke synthesis host timeout is skipped per maintainer decision. Human phase acceptance and SPI-P4/P5 remain outstanding; phase_accepted stays false. --- Makefile | 17 +- app/apps/ci_smoke/tiny.mk | 5 +- app/apps/ci_smoke/tiny_r2_p7.c | 5 +- app/apps/ci_smoke/tiny_spi_p3.c | 73 ++++ crt/include/retrosoc/hal/rcu_regs.h | 1 + crt/include/retrosoc/hal/spi.h | 67 ++++ crt/src/hal/dma_math.c | 17 +- crt/src/hal/spi.c | 206 +++++++++++ crt/src/hal/spi_math.c | 20 ++ docs/ip/spi-p3.md | 25 ++ docs/ip/spi-verification.md | 149 ++++++++ .../smoke/syn/yosys/apb4_spi_block_top.sv | 4 + .../datasheets/tiny/source-contract.json | 10 +- rtl/ip/multimedia/apu_dma.sv | 3 + rtl/ip/multimedia/jpeg_dma.sv | 65 ++-- rtl/ip/peripheral/apb4_dma.sv | 56 +-- rtl/ip/peripheral/apb4_gpio.sv | 15 +- rtl/ip/peripheral/dma_axi4_master.sv | 77 ++-- rtl/ip/peripheral/dma_core.sv | 172 ++++++--- rtl/ip/serial/apb4_spi.sv | 8 +- rtl/ip/storage/sdio_dma.sv | 65 ++-- rtl/ip/usb/usb2_dma.sv | 65 ++-- rtl/mk/software.mk | 6 +- rtl/tiny/address_map/memory_map.json | 9 + rtl/tiny/filelist/ip.fl | 9 + rtl/tiny/integration/clock_reset_domains.json | 8 +- rtl/tiny/integration/soc_topology.json | 35 +- rtl/tiny/top/retrosoc_tiny.sv | 163 ++++++--- rtl/tiny/top/tiny_apb_async_bridge.sv | 42 ++- rtl/tiny/top/tiny_axi42apb4.sv | 24 +- rtl/tiny/top/tiny_axi4_admission.sv | 2 +- rtl/tiny/top/tiny_axi4_fabric.sv | 10 +- rtl/tiny/top/tiny_axi4_target.sv | 54 +-- rtl/tiny/top/tiny_cpu_mem.sv | 2 +- rtl/tiny/top/tiny_peripheral_control.sv | 28 +- rtl/tiny/top/tiny_sysctrl.sv | 60 ++-- scripts/check_clock_reset_domains.py | 3 +- scripts/generate_tiny.py | 31 +- scripts/run_c_tests.py | 1 + scripts/spi_p3.py | 50 +++ tests/c/test_runtime.c | 125 +++++++ tests/rtl/dma_origin_tb.sv | 334 ++++++++++++++++++ tests/rtl/spi_test_body.svh | 3 + tests/rtl/tiny_axi4_fabric_tb.sv | 4 +- tests/rtl/tiny_p7_source_reset_tb.sv | 65 ++-- tests/rtl/tiny_r2_p5_fabric_formal.sv | 4 +- tests/rtl/tiny_r2_p5_fabric_tb.sv | 4 +- tests/rtl/tiny_r2_p5_reset_tb.sv | 73 ++-- tests/test_dma.py | 67 ++++ tests/test_publication_tiny.py | 2 +- tests/test_spi_p2.py | 4 +- tests/test_spi_p3.py | 61 ++++ tests/test_spi_register_parity.py | 11 +- tests/test_tiny.py | 2 +- tests/test_tiny_r2_p2.py | 2 +- tests/test_tiny_r2_p6.py | 5 +- tests/test_tiny_r2_p7.py | 14 +- tests/test_tiny_r2_p9.py | 8 +- 58 files changed, 1978 insertions(+), 472 deletions(-) create mode 100644 app/apps/ci_smoke/tiny_spi_p3.c create mode 100644 crt/include/retrosoc/hal/spi.h create mode 100644 crt/src/hal/spi.c create mode 100644 crt/src/hal/spi_math.c create mode 100644 docs/ip/spi-p3.md create mode 100644 scripts/spi_p3.py create mode 100644 tests/rtl/dma_origin_tb.sv create mode 100644 tests/test_spi_p3.py diff --git a/Makefile b/Makefile index f98b5838..dcc235e2 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,7 @@ TINY_P9_ACCEPTANCE ?= NO TINY_P9_QUICK ?= NO TINY_P9_WORKLOAD ?= QVGA_RGB565 TINY_P10_ACCEPTANCE ?= NO +TINY_SPI_P3_ACCEPTANCE ?= NO COREMARK_MODE ?= quick NPU_P5_ACCEPTANCE ?= NO NPU_P6_ACCEPTANCE ?= NO @@ -151,6 +152,9 @@ endif ifeq ($(TINY_P10_ACCEPTANCE),YES) CONFIG_KEY_VARS += TINY_P10_ACCEPTANCE endif +ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES) +CONFIG_KEY_VARS += TINY_SPI_P3_ACCEPTANCE +endif VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \ --lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \ $(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))) | tail -n 1)) @@ -316,11 +320,17 @@ $(call validate_value,TINY_P8_ACCEPTANCE,$(VALID_BOOL)) $(call validate_value,TINY_P9_ACCEPTANCE,$(VALID_BOOL)) $(call validate_value,TINY_P9_QUICK,$(VALID_BOOL)) $(call validate_value,TINY_P10_ACCEPTANCE,$(VALID_BOOL)) +$(call validate_value,TINY_SPI_P3_ACCEPTANCE,$(VALID_BOOL)) ifeq ($(TINY_P10_ACCEPTANCE),YES) ifneq ($(SOC)/$(APP)/$(HAVE_CSR)/$(TINY_P7_ACCEPTANCE)/$(TINY_P8_ACCEPTANCE)/$(TINY_P9_ACCEPTANCE)/$(TINY_P9_QUICK),TINY/ci_smoke/YES/NO/NO/NO/NO) $(error TINY_P10_ACCEPTANCE requires Tiny ci_smoke with CSR and no P7/P8/P9 acceptance image) endif endif +ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES) +ifneq ($(SOC)/$(APP)/$(HAVE_CSR)/$(TINY_P7_ACCEPTANCE)/$(TINY_P8_ACCEPTANCE)/$(TINY_P9_ACCEPTANCE)/$(TINY_P10_ACCEPTANCE),TINY/ci_smoke/YES/NO/NO/NO/NO) +$(error TINY_SPI_P3_ACCEPTANCE requires Tiny ci_smoke with CSR and no R2 acceptance image) +endif +endif $(call validate_value,TINY_P9_WORKLOAD,QVGA_RGB565 VGA_YUV422 RECOVERY) ifneq ($(TINY_P9_WORKLOAD),QVGA_RGB565) ifneq ($(TINY_P9_ACCEPTANCE)/$(TINY_P9_QUICK),YES/NO) @@ -546,6 +556,11 @@ spi-p2-lint spi-p2-rtl spi-p2-report: manifest $(PYTHON) $(ROOT_PATH)/scripts/spi_p2.py $(patsubst spi-p2-%,%,$@) \ --variant-root $(VARIANT_ROOT) --pdk $(PDK) +.PHONY: spi-p3-lint spi-p3-rtl spi-p3-report +spi-p3-lint spi-p3-rtl spi-p3-report: manifest + $(PYTHON) $(ROOT_PATH)/scripts/spi_p3.py $(patsubst spi-p3-%,%,$@) \ + --variant-root $(VARIANT_ROOT) --pdk $(PDK) + .PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report crypto-p0-constants: manifest $(PYTHON) $(ROOT_PATH)/scripts/crypto_p0.py constants --variant-root $(VARIANT_ROOT) @@ -1301,4 +1316,4 @@ regress-nightly: python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY --pdk $(PDK)) sim-asm: asm - $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim \ No newline at end of file + $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim diff --git a/app/apps/ci_smoke/tiny.mk b/app/apps/ci_smoke/tiny.mk index 0e8403ec..d139c632 100644 --- a/app/apps/ci_smoke/tiny.mk +++ b/app/apps/ci_smoke/tiny.mk @@ -22,4 +22,7 @@ endif endif ifeq ($(TINY_P10_ACCEPTANCE),YES) APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p10.c -endif \ No newline at end of file +endif +ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES) +APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_spi_p3.c +endif diff --git a/app/apps/ci_smoke/tiny_r2_p7.c b/app/apps/ci_smoke/tiny_r2_p7.c index 41244166..4e7e8165 100644 --- a/app/apps/ci_smoke/tiny_r2_p7.c +++ b/app/apps/ci_smoke/tiny_r2_p7.c @@ -11,8 +11,9 @@ #include #include -/* Current Tiny includes DVP (target 14); later expansion targets stay absent. */ -#define RS_P7_EXPECTED_TARGET_CAPABILITY UINT32_C(0x00007FFF) +/* Current Tiny includes DVP (target 14) and SPI (RCU target 16); the other + * expansion targets stay absent. */ +#define RS_P7_EXPECTED_TARGET_CAPABILITY UINT32_C(0x00017FFF) /* Short P7 firmware: execute in real main SRAM and use real SYSCTRL/APB * crossings. The full peripheral firmware remains a separate regression. */ diff --git a/app/apps/ci_smoke/tiny_spi_p3.c b/app/apps/ci_smoke/tiny_spi_p3.c new file mode 100644 index 00000000..1fe8fc7f --- /dev/null +++ b/app/apps/ci_smoke/tiny_spi_p3.c @@ -0,0 +1,73 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void rs_spi_p3_store_fault(uintptr_t mcause, uintptr_t stack_pointer) { + volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer; + uintptr_t mtval; + __asm__ volatile("csrr %0, mtval" : "=r"(mtval)); + printf("SPI_P3_TRAP mcause=%lx mepc=%lx mtval=%lx\n", (unsigned long)mcause, + (unsigned long)frame[11], (unsigned long)mtval); + for (;;) { + } +} + +int main(void) { + rs_spi_config_t config = { + .divider = 1U, + .cs_setup = 1U, + .cs_hold = 1U, + .cs_inactive = 1U, + .miso_enable = true, + }; + rs_spi_status_t status; + uint32_t ip_id = 0U, version = 0U, capability = 0U; + uint8_t code = 0U; + const rs_timeout_t p3_timeout = 10U; + const rs_gpio_config_t alt0 = { + .mode = RS_GPIO_MODE_ALT0, + .pull = RS_GPIO_PULL_NONE, + .trigger = RS_GPIO_TRIGGER_NONE, + .output_high = false, + }; + const rs_gpio_config_t alt1 = { + .mode = RS_GPIO_MODE_ALT1, + .pull = RS_GPIO_PULL_NONE, + .trigger = RS_GPIO_TRIGGER_NONE, + .output_high = false, + }; + + (void)rs_irq_register_exception(7U, rs_spi_p3_store_fault); + + if ((rs_spi_probe(&ip_id, &version, &capability) != RS_OK) || (ip_id != RS_SPI_ABI_ID_VALUE) || + (version != RS_SPI_ABI_VERSION_VALUE) || ((capability & UINT32_C(0x3)) != UINT32_C(0x3)) || + (rs_gpio_configure(27U, &alt0) != RS_OK) || (rs_gpio_configure(28U, &alt1) != RS_OK) || + (rs_gpio_configure(30U, &alt1) != RS_OK) || (rs_gpio_configure(31U, &alt1) != RS_OK) || + (rs_spi_configure(&config) != RS_OK) || (rs_spi_acquire(p3_timeout) != RS_OK) || + (rs_spi_get_status(&status) != RS_OK) || + ((status.status & (UINT32_C(1) << RS_SPI_ABI_STATUS_GPIO_READY)) == 0U) || + (rs_spi_release(p3_timeout) != RS_OK)) { + code = 1U; + } + if ((code == 0U) && rs_tiny_rcu_apply_gate(RS_TINY_RCU_TARGET_SPI0, p3_timeout) != RS_OK) { + code = 2U; + } + if ((code == 0U) && rs_tiny_rcu_apply_gate(0U, p3_timeout) != RS_OK) { + code = 3U; + } + rs_sysctrl_write_test_status(code == 0U, code); + (void)rs_uart_init(24000000U, 921600U); + printf("SPI_P3_RESULT id=%08x version=%08x capability=%08x code=%u\n", ip_id, version, + capability, code); + for (;;) { + } +} diff --git a/crt/include/retrosoc/hal/rcu_regs.h b/crt/include/retrosoc/hal/rcu_regs.h index f2dfc126..f8ac004e 100644 --- a/crt/include/retrosoc/hal/rcu_regs.h +++ b/crt/include/retrosoc/hal/rcu_regs.h @@ -39,6 +39,7 @@ #define RS_TINY_RCU_STATUS_FAULT UINT32_C(1U << 4) #define RS_TINY_RCU_CAP_PLL_PRESENT UINT32_C(1U << 8) #define RS_TINY_RCU_CAP_TARGET_MASK UINT32_C(0x0003FFFF) +#define RS_TINY_RCU_TARGET_SPI0 (UINT32_C(1) << 16U) #define RS_TINY_RCU_FAULT_MASK UINT32_C(0x0000001F) #define RS_TINY_RCU_RESET_CAUSE_MASK UINT32_C(0x0000003F) #define RS_TINY_RCU_CSR_MSLEEP 0xBF0 diff --git a/crt/include/retrosoc/hal/spi.h b/crt/include/retrosoc/hal/spi.h new file mode 100644 index 00000000..e84399f8 --- /dev/null +++ b/crt/include/retrosoc/hal/spi.h @@ -0,0 +1,67 @@ +#ifndef RETROSOC_HAL_SPI_H +#define RETROSOC_HAL_SPI_H + +#include +#include +#include + +#include +#include + +typedef enum { + RS_SPI_DIRECTION_TX_ONLY = RS_SPI_ABI_TX_ONLY, + RS_SPI_DIRECTION_FULL_DUPLEX = RS_SPI_ABI_FULL_DUPLEX, + RS_SPI_DIRECTION_RX_ONLY = RS_SPI_ABI_RX_ONLY, +} rs_spi_direction_t; + +typedef struct { + uint32_t clock_hz; + uint32_t divider; + uint16_t cs_setup; + uint16_t cs_hold; + uint16_t cs_inactive; + bool cpol; + bool cpha; + bool lsb_first; + bool miso_enable; + bool display_dc; +} rs_spi_config_t; + +typedef struct { + uint32_t frames; + rs_spi_direction_t direction; + bool frame16; + bool hold_cs; + uint16_t dummy; +} rs_spi_segment_t; + +typedef struct { + uint32_t status; + uint32_t levels; + uint32_t fault; + uint32_t frames_done; + uint32_t transport_words; + uint32_t tx_words; + uint32_t rx_words; + uint32_t rx_removed; + uint32_t dma_status; + uint32_t pins_ready; + uint32_t payload_bytes; +} rs_spi_status_t; + +rs_status_t rs_spi_probe(uint32_t *ip_id, uint32_t *version, uint32_t *capability); +rs_status_t rs_spi_divider_from_hz(uint32_t pclk_hz, uint32_t requested_sck_hz, uint32_t *divider, + uint32_t *actual_sck_hz); +rs_status_t rs_spi_configure(const rs_spi_config_t *config); +rs_status_t rs_spi_acquire(rs_timeout_t timeout); +rs_status_t rs_spi_release(rs_timeout_t timeout); +rs_status_t rs_spi_arm(const rs_spi_segment_t *segment); +rs_status_t rs_spi_start(rs_timeout_t timeout); +rs_status_t rs_spi_end(rs_timeout_t timeout); +rs_status_t rs_spi_abort(rs_timeout_t timeout); +rs_status_t rs_spi_recover(rs_timeout_t timeout); +rs_status_t rs_spi_get_status(rs_spi_status_t *status); +rs_status_t rs_spi_transfer_cpu(const uint32_t *tx_words, uint32_t *rx_words, uint32_t word_count, + rs_timeout_t timeout); + +#endif diff --git a/crt/src/hal/dma_math.c b/crt/src/hal/dma_math.c index 90a00717..43fe2621 100644 --- a/crt/src/hal/dma_math.c +++ b/crt/src/hal/dma_math.c @@ -3,6 +3,11 @@ #include #include +#include + +#ifndef RS_SOC_APB4_SPI_BASE +#define RS_SOC_APB4_SPI_BASE UINT32_C(0x1001D000) +#endif static bool rs_dma_address_valid(uintptr_t address, uint32_t byte_count, bool increment) { uintptr_t last_address; @@ -30,11 +35,13 @@ static bool rs_dma_request_valid(const rs_dma_config_t *config) { #ifdef RS_SOC_TINY if ((config->request == RS_DMA_REQUEST_SPI_TX) || (config->request == RS_DMA_REQUEST_SPI_RX)) { + const uintptr_t tx_aperture = (uintptr_t)RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_TXDATA; + const uintptr_t rx_aperture = (uintptr_t)RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_RXDATA; return ((config->byte_count % sizeof(uint32_t)) == 0U) && (((config->request == RS_DMA_REQUEST_SPI_TX) && config->source_increment && - !config->destination_increment) || + !config->destination_increment && config->destination == tx_aperture) || ((config->request == RS_DMA_REQUEST_SPI_RX) && !config->source_increment && - config->destination_increment)); + config->destination_increment && config->source == rx_aperture)); } #endif return (config->request == RS_DMA_REQUEST_SOFTWARE) || @@ -141,12 +148,14 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) { #ifdef RS_SOC_TINY if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_TX) && (((tcd->control & RS_DMA_TCD_SRC_INC) == UINT32_C(0)) || - ((tcd->control & RS_DMA_TCD_DST_INC) != UINT32_C(0)))) { + ((tcd->control & RS_DMA_TCD_DST_INC) != UINT32_C(0)) || + (tcd->destination != (uint32_t)(RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_TXDATA)))) { return RS_EINVAL; } if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_RX) && (((tcd->control & RS_DMA_TCD_SRC_INC) != UINT32_C(0)) || - ((tcd->control & RS_DMA_TCD_DST_INC) == UINT32_C(0)))) { + ((tcd->control & RS_DMA_TCD_DST_INC) == UINT32_C(0)) || + (tcd->source != (uint32_t)(RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_RXDATA)))) { return RS_EINVAL; } #endif diff --git a/crt/src/hal/spi.c b/crt/src/hal/spi.c new file mode 100644 index 00000000..4cf1b76a --- /dev/null +++ b/crt/src/hal/spi.c @@ -0,0 +1,206 @@ +#include + +#include +#include +#include + +#define RS_SPI_ROUTE_PIN_SCK UINT32_C(27) +#define RS_SPI_ROUTE_PIN_MOSI UINT32_C(28) +#define RS_SPI_ROUTE_PIN_MISO UINT32_C(30) +#define RS_SPI_ROUTE_PIN_CS_N UINT32_C(31) + +#if defined(RS_SOC_TINY) && defined(RS_SOC_APB4_SPI_BASE) +#define RS_SPI_TARGET_PRESENT 1 +#define RS_SPI_REG(offset) RS_SOC_REG32(RS_SOC_APB4_SPI_BASE, (offset)) + +static rs_status_t rs_spi_wait_status(uint32_t mask, bool set, rs_timeout_t timeout) { + while (timeout-- != 0U) { + const bool value = (RS_SPI_REG(RS_SPI_ABI_STATUS) & mask) != 0U; + if (value == set) { + return RS_OK; + } + } + return RS_ETIMEOUT; +} +#endif + +rs_status_t rs_spi_probe(uint32_t *ip_id, uint32_t *version, uint32_t *capability) { + if ((ip_id == NULL) || (version == NULL) || (capability == NULL)) { + return RS_EINVAL; + } +#if defined(RS_SPI_TARGET_PRESENT) && defined(RS_SOC_HAS_SPI) && (RS_SOC_HAS_SPI != 0U) + *ip_id = RS_SPI_REG(RS_SPI_ABI_IP_ID); + *version = RS_SPI_REG(RS_SPI_ABI_IP_VERSION); + *capability = RS_SPI_REG(RS_SPI_ABI_CAPABILITY); + return (*ip_id == RS_SPI_ABI_ID_VALUE) ? RS_OK : RS_EIO; +#else + *ip_id = 0U; + *version = 0U; + *capability = 0U; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_configure(const rs_spi_config_t *config) { + if ((config == NULL) || (config->divider == 0U) || (config->divider > 65535U) || + (config->cs_setup == 0U) || (config->cs_hold == 0U) || (config->cs_inactive == 0U) || + (config->display_dc && config->miso_enable)) { + return RS_EINVAL; + } +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_SESSION_CFG) = + (config->cpol ? 1U : 0U) | (config->cpha ? 2U : 0U) | (config->lsb_first ? 4U : 0U) | + (config->miso_enable ? 8U : 0U) | (config->display_dc ? 16U : 0U); + RS_SPI_REG(RS_SPI_ABI_DIV) = config->divider; + RS_SPI_REG(RS_SPI_ABI_CS_SETUP) = config->cs_setup; + RS_SPI_REG(RS_SPI_ABI_CS_HOLD) = config->cs_hold; + RS_SPI_REG(RS_SPI_ABI_CS_INACTIVE) = config->cs_inactive; + return RS_OK; +#else + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_acquire(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_ENABLE; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_GPIO_READY, true, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_release(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + rs_status_t result = + rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SEGMENT_CLEAN, true, timeout); + if (result != RS_OK) + return result; + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_DISABLE; + result = rs_spi_wait_status(UINT32_C(0xF), false, timeout); + if (result != RS_OK) + return result; + const rs_gpio_config_t input = { + .mode = RS_GPIO_MODE_INPUT, + .pull = RS_GPIO_PULL_NONE, + .trigger = RS_GPIO_TRIGGER_NONE, + .output_high = false, + }; + if ((rs_gpio_configure(RS_SPI_ROUTE_PIN_SCK, &input) != RS_OK) || + (rs_gpio_configure(RS_SPI_ROUTE_PIN_MOSI, &input) != RS_OK) || + (rs_gpio_configure(RS_SPI_ROUTE_PIN_MISO, &input) != RS_OK) || + (rs_gpio_configure(RS_SPI_ROUTE_PIN_CS_N, &input) != RS_OK)) { + return RS_EIO; + } + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_RELEASE; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SESSION_OWNED, false, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_arm(const rs_spi_segment_t *segment) { +#ifdef RS_SPI_TARGET_PRESENT + uint32_t value; + if ((segment == NULL) || (segment->frames == 0U) || + ((uint32_t)segment->direction > (uint32_t)RS_SPI_DIRECTION_RX_ONLY)) { + return RS_EINVAL; + } + value = (segment->frame16 ? 1U : 0U) | ((uint32_t)segment->direction << 1U) | + (segment->hold_cs ? 8U : 0U); + RS_SPI_REG(RS_SPI_ABI_SEG_CONFIG) = value; + RS_SPI_REG(RS_SPI_ABI_FRAME_COUNT) = segment->frames; + RS_SPI_REG(RS_SPI_ABI_DUMMY) = segment->dummy; + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_ARM; + return RS_OK; +#else + (void)segment; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_start(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_START; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_CS_ACTIVE, true, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_end(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_END; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SEGMENT_CLEAN, true, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_abort(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_ABORT; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_TRANSPORT_IDLE, true, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_recover(rs_timeout_t timeout) { +#ifdef RS_SPI_TARGET_PRESENT + RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_RECOVER; + return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SESSION_OWNED, false, timeout); +#else + (void)timeout; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_get_status(rs_spi_status_t *status) { + if (status == NULL) + return RS_EINVAL; +#ifdef RS_SPI_TARGET_PRESENT + status->status = RS_SPI_REG(RS_SPI_ABI_STATUS); + status->levels = RS_SPI_REG(RS_SPI_ABI_FIFO_LEVELS); + status->fault = RS_SPI_REG(RS_SPI_ABI_FAULT_CODE); + status->frames_done = RS_SPI_REG(RS_SPI_ABI_FRAMES_DONE); + status->transport_words = RS_SPI_REG(RS_SPI_ABI_TRANSPORT_WORDS); + status->tx_words = RS_SPI_REG(RS_SPI_ABI_TX_WORDS_ACCEPTED); + status->rx_words = RS_SPI_REG(RS_SPI_ABI_RX_WORDS_PRODUCED); + status->rx_removed = RS_SPI_REG(RS_SPI_ABI_RX_WORDS_REMOVED); + status->dma_status = RS_SPI_REG(RS_SPI_ABI_DMA_STATUS); + status->pins_ready = RS_SPI_REG(RS_SPI_ABI_PINS_READY); + status->payload_bytes = RS_SPI_REG(RS_SPI_ABI_PAYLOAD_BYTES); + return RS_OK; +#else + (void)status; + return RS_ENOTSUP; +#endif +} + +rs_status_t rs_spi_transfer_cpu(const uint32_t *tx_words, uint32_t *rx_words, uint32_t word_count, + rs_timeout_t timeout) { + if ((word_count == 0U) || (tx_words == NULL && rx_words == NULL)) + return RS_EINVAL; +#ifdef RS_SPI_TARGET_PRESENT + for (uint32_t index = 0U; index < word_count; ++index) { + if (tx_words != NULL) + RS_SPI_REG(RS_SPI_ABI_CPU_TXDATA) = tx_words[index]; + if (rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_RX_WAIT, false, timeout) != RS_OK) + return RS_ETIMEOUT; + if (rx_words != NULL) + rx_words[index] = RS_SPI_REG(RS_SPI_ABI_CPU_RXDATA); + } + return RS_OK; +#else + (void)tx_words; + (void)rx_words; + (void)timeout; + return RS_ENOTSUP; +#endif +} diff --git a/crt/src/hal/spi_math.c b/crt/src/hal/spi_math.c new file mode 100644 index 00000000..98ef85ed --- /dev/null +++ b/crt/src/hal/spi_math.c @@ -0,0 +1,20 @@ +#include + +rs_status_t rs_spi_divider_from_hz(uint32_t pclk_hz, uint32_t requested_sck_hz, uint32_t *divider, + uint32_t *actual_sck_hz) { + uint64_t denominator; + uint32_t value; + + if ((divider == NULL) || (actual_sck_hz == NULL) || (pclk_hz == 0U) || + (requested_sck_hz == 0U)) { + return RS_EINVAL; + } + denominator = (uint64_t)requested_sck_hz * UINT64_C(2); + value = (uint32_t)(((uint64_t)pclk_hz + denominator - UINT64_C(1)) / denominator); + if ((value == 0U) || (value > UINT32_C(65535))) { + return RS_ENOTSUP; + } + *divider = value; + *actual_sck_hz = pclk_hz / (value * UINT32_C(2)); + return RS_OK; +} diff --git a/docs/ip/spi-p3.md b/docs/ip/spi-p3.md new file mode 100644 index 00000000..d32b29a3 --- /dev/null +++ b/docs/ip/spi-p3.md @@ -0,0 +1,25 @@ +# SPI-P3 Tiny GPIO RCU and SDK Integration + +SPI-P3 binds the accepted SPI-P2 shared-DMA ABI to the Tiny product. It adds +the frozen APB, IRQ25, RCU target16 and four Gen1 routes while preserving the +QFN64 package, existing GPIO ownership, DMA channels and legacy XPI LCD path. + +Tiny internal AXI transactions carry a private four-bit source marker. Fabric +admission rewrites the owner marker, the APB/CDC bridges retain it through the +terminal response, and SPI accepts DMA aliases only for a qualified central +DMA channel. CPU and other-owner accesses cannot use DMA aliases. SPI DMA +requests are fixed to `0x1001D054` (TX) and `0x1001D058` (RX). + +GPIO readiness combines USER_SELECT, handoff, ALT/electrical state and the +Tiny lifecycle reset. Raw GPIO30 input feeds MISO directly; the ordinary GPIO +filter path is not used for SPI sampling. GPIO and SPI gate/reset operations +remain blocked while a PIO user claim or latched SPI session is active. + +The freestanding `rs_spi_*` HAL performs capability discovery, bounded +configuration, acquire/release, CPU segments, status and recovery. It does not +add a display adapter; display and snapshot application work remain SPI-P4. + +Focused P3 records belong under `build//meta/spi-p3/`. Tiny acceptance +requires command success, `TEST_STATUS`/`SIM_TEST_PASS` and no forbidden error +marker. `phase_accepted` remains false until human review. Pre-final STA is +observational; SPI-P5 remains the final physical qualification phase. diff --git a/docs/ip/spi-verification.md b/docs/ip/spi-verification.md index daa349ad..2ca6dd5c 100644 --- a/docs/ip/spi-verification.md +++ b/docs/ip/spi-verification.md @@ -112,6 +112,155 @@ SPI request allocation, the full post-fix Pytest campaign and product regressions are unrun. Product instances retain V2.0/default-disabled SPI. +## SPI-P3 implementation checkpoint (2026-10-21) + +The current authorized worktree integrates SPI-P2 into Tiny with APB4 +`0x1001D000..0x1001DFFF`, IRQ25, RCU target16, the four frozen Gen1 ALT +routes and the ICS55 default/Tiny-IHP130 compatibility profiles. Tiny fabric +source qualification is carried through AXI user metadata and APB CDC; CPU, +DMA and other-owner accesses remain distinguishable at the SPI aliases. Tiny +DMA enables V2.2 requests 16/17 and checks the exact fixed FIFO MMIO addresses. + +The GPIO guard uses the existing USER_SELECT/handoff and ALT/electrical state, +with raw GPIO30 input for MISO. GPIO/SPI lifecycle idle vectors include the +latched SPI session, so RCU gate/reset/rate changes cannot strand accepted +SPI work. The bounded `rs_spi_*` HAL is added under the freestanding SDK and +the `TINY_SPI_P3_ACCEPTANCE` image covers discovery, ownership and RCU target +checks. + +Focused P3 source checks and Tiny firmware/lint records are retained below +`build//meta/spi-p3/`. Full Tiny/Icarus product regression, complete +DMA contention coverage, synthesis/netlist/STA and human phase acceptance +remain required. This checkpoint does not authorize SPI-P4 or SPI-P5. + +SPI-P3 Tiny Verilator acceptance (2026-10-21, same worktree): both profiles +pass with the standard `TINY_SIM_TIMEOUT=20000000` budget. Commands: +`make CONFIG=configs/ci/ics55-tiny.mk APP=ci_smoke TINY_SPI_P3_ACCEPTANCE=YES firmware sim` +and `make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke TINY_SPI_P3_ACCEPTANCE=YES firmware sim`. +Each log reports `SIM_TEST_PASS Tiny cycles=1030643` with no forbidden marker; +`result-sim.json` and `result-sim-check.json` both report `passed`. Evidence +roots: `build/ics55-tiny-2026-10-21-16-15-a257f7b03bdb/` (ICS55) and +`build/ihp130-tiny-2026-10-21-16-15-76f15177ba7a/` (IHP130). The cycle count +is dominated by SAFE24 XPI boot copy (~1.015M cycles for the 7212-byte image); +the SPI APB/RCU/TEST_STATUS sequence completes in the final ~15.6k cycles. A +1,000,000-cycle budget times out inside the boot copy loop and is not a valid +acceptance budget for this image. The same change set moves the +`TINY_SPI_P3_ACCEPTANCE` ci_smoke guard to top level in `Makefile` so +`APP=bringup` builds are rejected at parse time. UART output remained +diagnostic only; `phase_accepted` stays false. + +SPI-P3 phase-gate campaign (2026-10-21, same worktree), per gate: + +| Gate | Result / evidence | +| --- | --- | +| Focused stages, both profiles | PASS: `spi-p3-lint/rtl/report` each report `passed` (13 focused tests per stage); records under `build/ics55-tiny-2026-10-21-16-31-a257f7b03bdb/meta/spi-p3/` and `build/ihp130-tiny-2026-10-21-16-31-76f15177ba7a/meta/spi-p3/` | +| Quality gates | PASS: `ruff check .`, `make sw-format-check sw-policy-check sw-host-test`, `git diff --check` | +| Regression dry-runs | PASS: `--suite pr --soc TINY --pdk IHP130` and `--suite nightly --soc TINY` | +| `make regress-pr` | FAIL (first failing step identified): the Mini IHP130 firmware step emits new self-owned C warnings from `crt/src/hal/spi.c:10` (the non-Tiny fallback `RS_SPI_REG` macro dereferences raw offsets, riscv32-gcc `-Warray-bounds=`); `scripts/regress.py:329` fails the suite by policy. Later matrix steps did not run. Awaiting an implementation fix; not a design-behavior defect | +| Icarus behavioral sims, both profiles | NOT_RUN (blocked, pre-existing): Icarus elaboration of committed `rtl/tiny/dv` observers/tb fails against the sv2v-lowered `converted_soc.v` (struct fields such as `s_req_q[].state`, `s_slow_q.owner`, `u_xpi.apb4.psel`); 29 errors on each profile (`build/ics55-tiny-2026-10-21-16-31-a257f7b03bdb/sim/iverilog/`, `build/ihp130-tiny-2026-10-21-16-36-76f15177ba7a/sim/iverilog/`). Last passing Icarus behavioral run predates the committed R2-P7..P10 observer changes (2026-10-13, `build/ihp130-tiny-2026-10-13-17-06-3f1b609b0b86/`); the sv2v lowering of the referenced structs is byte-identical with and without SPI-P3, so this is not caused by this worktree | +| Full Pytest | FAIL: 16 failed, 2080 passed, 1 skipped in 4035.41 s (`/tmp/pytest_full.log` capture; one execution). All 16 failures trace to this worktree's incomplete follow-through, none pre-existing: (a) `apb4_spi` gained `session_owned_o`/`lifecycle_idle_o` but `physical/smoke/syn/yosys/apb4_spi_block_top.sv` was not updated, breaking all five SPI-P1 standalone Verilator fixture compiles; (b) `rtl/tiny/integration/clock_reset_domains.json` reset `leaf_names` omits the `spi` leaf that `soc_topology.json` lists as an APB target; (c) committed contract tests still encode the pre-P3 state and were not updated to the frozen P3 allocation (IRQ25, RCU16, 29 fabric targets, SPI lifecycle leaf, 4-bit AXI origin): `test_tiny.py::test_tiny_sources_and_abi_are_independent`, `test_tiny_protocol_contracts[fabric-*]`, `test_tiny_r2_p2.py::test_reset_inventory...`, `test_tiny_r2_p5.py::test_concurrent_fabric`, `test_target_reset_isolation`, `test_tiny_r2_p6.py` IRQ/pin and reset-policy contracts; (d) `test_publication_tiny.py` source binding still expects `s_r_data[1]` for the read-terminal condition, legitimately shifted to `s_r_data[4]` by the 4-bit origin widening | + +UART output remained diagnostic only; `phase_accepted` stays false. The +`regress-pr` C-warning failure must be fixed before SPI-P3 phase acceptance; +the Icarus observer/sv2v incompatibility belongs to the R2-P7..P10 DV owners +and is routed back as a separate pre-existing defect. + +SPI-P3 repair campaign (2026-10-21/22, same worktree): the five routed fixes +are implemented. (1) `crt/src/hal/spi.c` now guards every register-touching +body behind `RS_SPI_TARGET_PRESENT` (Tiny + `RS_SOC_APB4_SPI_BASE`) and +returns `RS_ENOTSUP` otherwise; the absolute-offset fallback macro is removed, +so Mini and host builds emit no self-owned C warnings. (2) +`apb4_spi_block_top.sv` and `tests/rtl/spi_test_body.svh` connect the new +`session_owned_o`/`lifecycle_idle_o` pins, restoring the SPI-P1 standalone +fixtures. (3) `clock_reset_domains.json` lists the `spi` leaf and peripheral +crossing, and `scripts/check_clock_reset_domains.py` expects them. (4) The +fabric/reset testbenches use `USER_WIDTH(4)` interfaces and matching 40/7-bit +scoreboard registers, and the committed contract tests now encode the frozen +P3 allocation (IRQ25, RCU16, 23 reset leaves, SPI lifecycle leaf, +`32'h0003_39ff` request mask). (5) The Tiny publication source contract tracks +the 4-bit origin widening (`s_r_data[4]`, `s_rin_data[6:5]`), the renamed +`decode_target`-based fabric check, the DMA request mask validation and the +uart1 tie-off. + +Post-repair gates: all 16 previously failing tests pass; full Pytest reports +2096 passed, 1 skipped (known optional `pypdf`), 0 failed in 4035.08 s; +`ruff check .`, `sw-format-check`, `sw-policy-check`, `sw-host-test` and +`git diff --check` pass; Mini (`configs/ci/ihp130.mk`) and both Tiny +acceptance firmware builds are free of self-owned C warnings; the focused +spi-p3 stages pass on both profiles; the Tiny Verilator acceptance passes +again on both profiles at `SIM_TEST_PASS Tiny cycles=1030643` +(`build/ics55-tiny-2026-10-21-21-12-a257f7b03bdb/`, +`build/ihp130-tiny-2026-10-21-21-12-76f15177ba7a/`). `make regress-pr` then +passed the Mini rtl-lint, firmware, Verilator, debug and Icarus assembly steps +but the Mini Yosys smoke synthesis hit the pre-existing 10800 s host timeout +(`build/ihp130-2026-10-21-21-12-c30fb9412aa3/syn/yosys/result-synth.json`, +continuous OPT-pass progress, identical 10800 s failures on 2026-09-23 and +2026-10-02 with older configurations). Per explicit user decision on +2026-10-22, the Mini Yosys smoke synthesis and its dependent Mini +netlist/STA steps are skipped for this campaign; the recorded Mini steps +(rtl-lint, ci_smoke/shell firmware, Verilator sim, debug-sim, Icarus +assembly) all passed post-repair. + +Tiny PR matrix completion (2026-10-22, same worktree): `rtl-lint` and the +R2-P7 Verilator acceptance passed inside the suite +(`SIM_TEST_PASS Tiny cycles=3859487`, +`build/ihp130-tiny-2026-10-22-07-47-3f1b609b0b86/`); the R2-P7 Icarus +behavioral step aborted the suite at the pre-existing observer/sv2v +elaboration defect (29 errors, `build/ihp130-tiny-2026-10-22-07-47-aae2707c2aa5/sim/iverilog/`). +The remaining suite steps were then executed individually with +`BUILD_TIMESTAMP=2026-10-22-07-58` pinned to one variant root: Tiny Yosys +synthesis passed in 2066.0 s with the netlist retained, `netsim-boot` passed +with `SIM_TEST_PASS Tiny cycles=36069`, and OpenSTA completed with per-corner +reports retained (`build/ihp130-tiny-2026-10-22-07-58-8232fa11e7f3/`; timing +remains observational, e.g. timing_metrics `wns min -0.48`, no closure +claimed). `check-rtl-lint` reports failed because no +`quality/warnings/ihp130-tiny/` baseline exists (every signature reads as new); +this pre-dates SPI-P3 and is non-blocking inside the regression runner. +`check-warnings` passed and `check-metrics` ran in observe mode. The Icarus +behavioral defect remains routed to the R2 DV owners; `phase_accepted` stays +false. + +SPI-P3 review-repair round (2026-10-22, same worktree): independent review of +the full worktree delta found three P1 defects, all fixed and re-verified. +(1) `dma_axi4_master` sampled the origin channel from the live arbiter during +the AW/W/AR address phase, after arbitration advanced, so final single-beat +and contended SPI alias bursts were mis-qualified; the origin channel is now +registered at the start handshake alongside addr/beats/fixed. The new focused +`tests/rtl/dma_origin_tb.sv` (via +`tests/test_dma.py::test_dma_origin_qualifier_stays_staged_per_burst`) checks +every AR/AW/W user field per beat for a nonzero channel under idle and +contended arbitration; it fails against a reverted live-origin variant +(ar=49/aw=47 mismatches) and passes on the fix. (2) RCU feedback bit 15 had +no driver; `retrosoc_tiny.sv` now ties off `s_actual_gate[15]`/ +`s_reset_held[15]`/`s_reset_ready[15]`, and the generated bridges tie +non-SPI origin/channel constants, eliminating the entire UNDRIVEN lint class +(zero entries in the post-fix lint log). (3) The clock-switch quiescence slice +`.clients_idle_i` is widened to `s_periph_idle_ref[16:1]` so SPI sessions veto +rate changes as the phase contract claims. P2/P3 follow-ups fixed in the same +round: `tiny_r2_p5_fabric_formal.sv` widened to USER_WIDTH(4), five remaining +`dma_axi4_master` instantiations tie off the origin inputs, `rs_spi_acquire` +waits on GPIO_READY alone, host tests added for the SPI divider math and the +Tiny SPI DMA aperture checks, plus comment/dead-branch/magic-number/provenance +cleanups. + +Post-repair evidence (2026-10-22): full Pytest 2097 passed, 1 skipped +(optional `pypdf`), 0 failed in 4080.06 s; both SPI-P3 Verilator acceptance +runs pass at `SIM_TEST_PASS Tiny cycles=1030088` +(`build/ics55-tiny-2026-10-22-10-27-a257f7b03bdb/`, +`build/ihp130-tiny-2026-10-22-10-27-76f15177ba7a/`); the R2-P7 Verilator +acceptance still passes at `cycles=3859487` +(`build/ihp130-tiny-2026-10-22-10-27-3f1b609b0b86/`); Mini DMA formal prove +and cover both PASS (`build/ihp130-2026-10-22-10-31-c30fb9412aa3/formal/dma/`); +Mini rtl-lint passes; Tiny Yosys synthesis, `netsim-boot` +(`SIM_TEST_PASS Tiny cycles=36069`) and OpenSTA all pass under +`build/ihp130-tiny-2026-10-22-10-31-8232fa11e7f3/`; `rtl-format-check`, +`ruff check .`, `sw-format-check`, `sw-policy-check`, `sw-host-test` and +`git diff --check` all pass. Remaining gaps are unchanged: the pre-existing +Icarus observer/sv2v defect (R2 DV owners), the Mini smoke-synthesis host +timeout (skipped per user decision), the missing Tiny warning baseline +(non-blocking observation), human phase acceptance, and the deferred +SPI-P4/P5 obligations. `phase_accepted` stays false. + ## SPI-P1 implementation checkpoint (2026-10-18, historical) The approved standalone implementation starts from clean diff --git a/physical/smoke/syn/yosys/apb4_spi_block_top.sv b/physical/smoke/syn/yosys/apb4_spi_block_top.sv index c1ec7b2e..128059ef 100644 --- a/physical/smoke/syn/yosys/apb4_spi_block_top.sv +++ b/physical/smoke/syn/yosys/apb4_spi_block_top.sv @@ -25,6 +25,8 @@ module apb4_spi_block_top ( output logic output_enable_o, output logic irq_o, output logic [ 1:0] credit_commit_o, + output logic session_owned_o, + output logic lifecycle_idle_o, input logic [31:0] paddr_i, pwdata_i, input logic [ 3:0] pstrb_i, @@ -75,6 +77,8 @@ module apb4_spi_block_top ( .output_enable_o (output_enable_o), .irq_o (irq_o), .credit_commit_o (credit_commit_o), + .session_owned_o (session_owned_o), + .lifecycle_idle_o (lifecycle_idle_o), .apb4 (s_apb4) ); endmodule diff --git a/publications/datasheets/tiny/source-contract.json b/publications/datasheets/tiny/source-contract.json index ac2214fa..fb750ee7 100644 --- a/publications/datasheets/tiny/source-contract.json +++ b/publications/datasheets/tiny/source-contract.json @@ -125,7 +125,7 @@ { "file": "rtl/tiny/top/tiny_axi4_fabric.sv", "text": [ - " function automatic logic [4:0] checked_target(\n input logic [31:0] addr_i, input logic [7:0] len_i,\n input logic [2:0] size_i, input logic [1:0] burst_i,\n input logic lock_i, input logic instruction_i);\n logic [32:0] last_addr;\n logic [4:0] decoded;\n decoded = decode_target(addr_i);\n last_addr = {1'b0, addr_i} + (({25'd0, len_i} + 33'd1) << size_i) - 33'd1;\n if (lock_i || (size_i > 3'd2) || (len_i > 8'd15) ||\n ((addr_i & ((32'd1 << size_i) - 32'd1)) != 32'd0) ||\n !((burst_i == 2'b01) || ((burst_i == 2'b00) && (len_i == 8'd0))) ||\n last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||\n (decode_target(last_addr[31:0]) != decoded) ||\n ((decoded >= 5'd5) && (decoded <= 5'd20) && (len_i != 8'd0)))\n return 5'd22;\n if (instruction_i && !(`SOC_ADDR_IS_FLASH(addr_i) ||\n ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000))))\n return ((decoded >= 5'd5) && (decoded <= 5'd20)) ? 5'd22 : 5'd21;\n return decoded;\n endfunction", + " function automatic logic [4:0] checked_target(input logic [31:0] addr_i, input logic [7:0] len_i,\n input logic [2:0] size_i, input logic [1:0] burst_i,\n input logic lock_i, input logic instruction_i);\n logic [32:0] last_addr;\n logic [ 4:0] decoded;\n decoded = decode_target(addr_i);\n last_addr = {1'b0, addr_i} + (({25'd0, len_i} + 33'd1) << size_i) - 33'd1;\n if (lock_i || (size_i > 3'd2) || (len_i > 8'd15) ||\n ((addr_i & ((32'd1 << size_i) - 32'd1)) != 32'd0) ||\n !((burst_i == 2'b01) || ((burst_i == 2'b00) && (len_i == 8'd0))) ||\n last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||\n (decode_target(\n last_addr[31:0]\n ) != decoded) || ((decoded >= 5'd5) && (decoded < DecodeErrorTarget) && (len_i != 8'd0)))\n return ProtocolErrorTarget;\n if (instruction_i && !(\n `SOC_ADDR_IS_FLASH(addr_i)\n || ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000)))) begin\n if ((decoded >= 5'd5) && (decoded < DecodeErrorTarget)) return ProtocolErrorTarget;\n return DecodeErrorTarget;\n end\n return decoded;\n endfunction", "assign s_busy_d = (s_busy_q & ~s_done) | s_admit;", "assign owner_idle_o[owner] = !(s_busy_q[2*owner] || s_busy_q[2*owner+1]);", "assign s_eligible[2*owner] = rst_n_i && owner_idle_o[owner] && masters[owner].arvalid && (!masters[owner].awvalid || !s_cpu_prefer_write_q);", @@ -135,7 +135,7 @@ { "file": "crt/src/hal/dma_math.c", "text": [ - "#ifdef RS_SOC_TINY\n if (config->kind != RS_DMA_KIND_MM_TO_MM) {\n return false;\n }\n#endif" + "#ifdef RS_SOC_TINY\n const uint32_t request_mask = UINT32_C(0x000339FF);\n if (((uint32_t)config->request >= 32U) ||\n ((request_mask & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {\n return false;\n }\n#endif" ] }, { @@ -150,7 +150,7 @@ ".RTC_CLOCK_HZ(24_000_000)", ".WDG_CLOCK_HZ(24_000_000)", ".RESET_PULSE_CYCLES(8)", - "assign u_uart1_if.cts_n_i = 1'b0;" + "assign uart1_tx_o = 1'b1;" ] }, { @@ -187,9 +187,9 @@ "file": "rtl/tiny/top/tiny_axi4_target.sv", "text": [ "assign s_accept = (s_req_q.state == Idle) && enable_i && s_valid;", - "assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]));", + "assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (s_r_valid && s_rready[s_req_q.owner] && s_r_data[4]));", "assign fault_valid_o = s_terminal && s_req_q.error_seen;", - "if (!s_req_q.error_seen && (s_rin_data[3:2] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; s_req_d.error_resp = s_rin_data[3:2]; end", + "if (!s_req_q.error_seen && (s_rin_data[6:5] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; s_req_d.error_resp = s_rin_data[6:5]; end", "assign s_abort_write_valid = (s_req_q.state == AbortResponse) && s_req_q.write && s_req_q.write_done && !s_req_q.b_captured;", "assign s_abort_read_valid = (s_req_q.state == AbortResponse) && !s_req_q.write && (s_req_q.read_count <= {1'b0, s_saved_addr.len});", ".flush_i(1'b0)", diff --git a/rtl/ip/multimedia/apu_dma.sv b/rtl/ip/multimedia/apu_dma.sv index ed33d838..43d26236 100644 --- a/rtl/ip/multimedia/apu_dma.sv +++ b/rtl/ip/multimedia/apu_dma.sv @@ -237,6 +237,9 @@ module apu_dma ( .write_done_o(s_write_done), .write_resp_o(s_write_resp), .write_id_error_o(s_write_id_error), + .origin_enable_i(1'b0), + .read_origin_channel_i(3'd0), + .write_origin_channel_i(3'd0), .axi4(axi4) ); diff --git a/rtl/ip/multimedia/jpeg_dma.sv b/rtl/ip/multimedia/jpeg_dma.sv index 381cedc1..bf0c31cb 100644 --- a/rtl/ip/multimedia/jpeg_dma.sv +++ b/rtl/ip/multimedia/jpeg_dma.sv @@ -440,37 +440,40 @@ module jpeg_dma #( .DataWidth (DataWidth), .MaxBurstBeats(MaxBurstBeats) ) u_axi4_master ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clear_i (1'b0), - .read_start_valid_i (s_read_start_valid), - .read_start_ready_o (s_read_start_ready), - .read_addr_i (s_read_addr_q), - .read_beats_i (s_read_beats), - .read_fixed_i (1'b0), - .read_busy_o (s_read_master_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_err), - .read_done_o (s_read_master_done), - .write_start_valid_i (s_write_start_valid), - .write_start_ready_o (s_write_start_ready), - .write_addr_i (s_write_addr_q), - .write_beats_i (s_write_beats), - .write_fixed_i (1'b0), - .write_busy_o (s_write_master_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_strobe), - .write_done_o (s_write_master_done), - .write_resp_o (s_write_resp), - .write_id_error_o (s_write_id_err), - .axi4 (axi4) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clear_i (1'b0), + .read_start_valid_i (s_read_start_valid), + .read_start_ready_o (s_read_start_ready), + .read_addr_i (s_read_addr_q), + .read_beats_i (s_read_beats), + .read_fixed_i (1'b0), + .read_busy_o (s_read_master_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_err), + .read_done_o (s_read_master_done), + .write_start_valid_i (s_write_start_valid), + .write_start_ready_o (s_write_start_ready), + .write_addr_i (s_write_addr_q), + .write_beats_i (s_write_beats), + .write_fixed_i (1'b0), + .write_busy_o (s_write_master_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_strobe), + .write_done_o (s_write_master_done), + .write_resp_o (s_write_resp), + .write_id_error_o (s_write_id_err), + .origin_enable_i (1'b0), + .read_origin_channel_i (3'd0), + .write_origin_channel_i(3'd0), + .axi4 (axi4) ); dffr #( diff --git a/rtl/ip/peripheral/apb4_dma.sv b/rtl/ip/peripheral/apb4_dma.sv index 466ea410..c4cf6ee8 100644 --- a/rtl/ip/peripheral/apb4_dma.sv +++ b/rtl/ip/peripheral/apb4_dma.sv @@ -9,16 +9,19 @@ // See the Mulan PSL v2 for more details. module apb4_dma #( - parameter int AddrWidth = 32, - parameter int DataWidth = 32, - parameter int NumChannels = 4, - parameter int MaxBurstBeats = 16, - parameter int FifoDepth = 16, - parameter logic [31:0] RequestMask = 32'h0000_3fff, - parameter logic [31:0] SupportedRequests = RequestMask, - parameter logic [31:0] IpVersion = 32'h0002_0000, - parameter bit EnableStreams = 1'b1, - parameter bit EnableSpi = 1'b0 + parameter int AddrWidth = 32, + parameter int DataWidth = 32, + parameter int NumChannels = 4, + parameter int MaxBurstBeats = 16, + parameter int FifoDepth = 16, + parameter logic [31:0] RequestMask = 32'h0000_3fff, + parameter logic [31:0] SupportedRequests = RequestMask, + parameter logic [31:0] IpVersion = 32'h0002_0000, + parameter bit EnableStreams = 1'b1, + parameter bit EnableSpi = 1'b0, + parameter bit EnableOriginQualifier = 1'b0, + parameter logic [31:0] SpiTxAddress = 32'd0, + parameter logic [31:0] SpiRxAddress = 32'd0 ) ( // verilog_format: off -- integration ports retain established peripheral endpoint names. output logic lifecycle_idle_o, @@ -33,7 +36,12 @@ module apb4_dma #( axi4_stream_if.sink i2s_rx_axis, axi4_stream_if.sink dvp_rx_axis, axi4_stream_if.source crypto_in_axis, - axi4_stream_if.sink crypto_out_axis + axi4_stream_if.sink crypto_out_axis, + output logic [1:0] spi_credit_valid_o, + output logic [1:0] spi_credit_retire_o, + output logic [1:0] spi_start_pending_o, + output logic [3:0] spi_status_o, + output logic spi_error_o // verilog_format: on ); logic [ NumChannels*32-1:0] s_ch_cfg; @@ -134,14 +142,17 @@ module apb4_dma #( ); dma_core #( - .RequestMask (RequestMask), - .EnableSpi (EnableSpi), - .EnableStreams(EnableStreams), - .AddrWidth (AddrWidth), - .DataWidth (DataWidth), - .NumChannels (NumChannels), - .MaxBurstBeats(MaxBurstBeats), - .FifoDepth (FifoDepth) + .RequestMask (RequestMask), + .EnableSpi (EnableSpi), + .EnableOriginQualifier(EnableOriginQualifier), + .SpiTxAddress (SpiTxAddress), + .SpiRxAddress (SpiRxAddress), + .EnableStreams (EnableStreams), + .AddrWidth (AddrWidth), + .DataWidth (DataWidth), + .NumChannels (NumChannels), + .MaxBurstBeats (MaxBurstBeats), + .FifoDepth (FifoDepth) ) u_dma_core ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -190,7 +201,12 @@ module apb4_dma #( .i2s_rx_axis (i2s_rx_axis), .dvp_rx_axis (dvp_rx_axis), .crypto_in_axis (crypto_in_axis), - .crypto_out_axis (crypto_out_axis) + .crypto_out_axis (crypto_out_axis), + .spi_credit_valid_o (spi_credit_valid_o), + .spi_credit_retire_o (spi_credit_retire_o), + .spi_start_pending_o (spi_start_pending_o), + .spi_status_o (spi_status_o), + .spi_error_o (spi_error_o) ); logic s_bus_busy; assign s_bus_busy = axi4.awvalid || axi4.arvalid || axi4.wvalid; diff --git a/rtl/ip/peripheral/apb4_gpio.sv b/rtl/ip/peripheral/apb4_gpio.sv index 32af0cd4..3fdfbf4c 100644 --- a/rtl/ip/peripheral/apb4_gpio.sv +++ b/rtl/ip/peripheral/apb4_gpio.sv @@ -21,7 +21,13 @@ module apb4_gpio #( input logic rst_n_i, apb4_if.slave apb4, gpio_if.dut gpio, - user_gpio_if.padctrl user_gpio + user_gpio_if.padctrl user_gpio, + output logic [31:0] user_select_o, + output logic [31:0] user_handoff_o, + output logic [31:0] alt_enable_o, + output logic [31:0] alt_select_o, + output logic [31:0] output_enable_o, + output logic [31:0] open_drain_o // verilog_format: on ); @@ -108,4 +114,11 @@ module apb4_gpio #( .user_gpio (user_gpio) ); + assign user_select_o = s_user_sel; + assign user_handoff_o = s_user_handoff; + assign alt_enable_o = s_alt_en; + assign alt_select_o = s_alt_sel; + assign output_enable_o = s_output_en; + assign open_drain_o = s_open_drain; + endmodule diff --git a/rtl/ip/peripheral/dma_axi4_master.sv b/rtl/ip/peripheral/dma_axi4_master.sv index e10aec71..d36d3adf 100644 --- a/rtl/ip/peripheral/dma_axi4_master.sv +++ b/rtl/ip/peripheral/dma_axi4_master.sv @@ -46,6 +46,9 @@ module dma_axi4_master #( output logic write_done_o, output logic [ 1:0] write_resp_o, output logic write_id_error_o, + input logic origin_enable_i, + input logic [ 2:0] read_origin_channel_i, + input logic [ 2:0] write_origin_channel_i, axi4_if.master axi4 // verilog_format: on ); @@ -74,6 +77,8 @@ module dma_axi4_master #( logic [ 4:0] s_write_beat_q; logic s_read_fixed_q; logic s_write_fixed_q; + logic [ 2:0] s_read_origin_q; + logic [ 2:0] s_write_origin_q; `ifndef SYNTHESIS initial begin @@ -112,12 +117,12 @@ module dma_axi4_master #( assign axi4.awprot = `AXI4_PROT_DATA; assign axi4.awqos = `AXI4_QOS_NORMAL; assign axi4.awregion = `AXI4_REGION_NORMAL; - assign axi4.awuser = '0; + assign axi4.awuser = origin_enable_i ? {1'b1, s_write_origin_q} : '0; assign axi4.awvalid = s_write_state_q == WriteAddress; assign axi4.wdata = write_data_i; assign axi4.wstrb = write_strb_i; assign axi4.wlast = (s_write_beat_q + 1'b1) == s_write_beats_q; - assign axi4.wuser = '0; + assign axi4.wuser = origin_enable_i ? {1'b1, s_write_origin_q} : '0; assign axi4.wvalid = (s_write_state_q == WriteData) && write_data_valid_i; assign axi4.bready = s_write_state_q == WriteResponse; @@ -131,32 +136,35 @@ module dma_axi4_master #( assign axi4.arprot = `AXI4_PROT_DATA; assign axi4.arqos = `AXI4_QOS_NORMAL; assign axi4.arregion = `AXI4_REGION_NORMAL; - assign axi4.aruser = '0; + assign axi4.aruser = origin_enable_i ? {1'b1, s_read_origin_q} : '0; assign axi4.arvalid = s_read_state_q == ReadAddress; assign axi4.rready = (s_read_state_q == ReadData) && read_beat_ready_i; always_ff @(posedge clk_i or negedge rst_n_i) begin if (!rst_n_i) begin - s_read_state_q <= ReadIdle; - s_read_addr_q <= '0; - s_read_beats_q <= 5'd1; - s_read_beat_q <= '0; - s_read_fixed_q <= 1'b0; + s_read_state_q <= ReadIdle; + s_read_addr_q <= '0; + s_read_beats_q <= 5'd1; + s_read_beat_q <= '0; + s_read_fixed_q <= 1'b0; + s_read_origin_q <= 3'd0; end else if (clear_i) begin - s_read_state_q <= ReadIdle; - s_read_addr_q <= '0; - s_read_beats_q <= 5'd1; - s_read_beat_q <= '0; - s_read_fixed_q <= 1'b0; + s_read_state_q <= ReadIdle; + s_read_addr_q <= '0; + s_read_beats_q <= 5'd1; + s_read_beat_q <= '0; + s_read_fixed_q <= 1'b0; + s_read_origin_q <= 3'd0; end else begin unique case (s_read_state_q) ReadIdle: begin if (read_start_valid_i && read_start_ready_o) begin - s_read_addr_q <= read_addr_i; - s_read_beats_q <= read_beats_i; - s_read_beat_q <= '0; - s_read_fixed_q <= read_fixed_i; - s_read_state_q <= ReadAddress; + s_read_addr_q <= read_addr_i; + s_read_beats_q <= read_beats_i; + s_read_beat_q <= '0; + s_read_fixed_q <= read_fixed_i; + s_read_origin_q <= read_origin_channel_i; + s_read_state_q <= ReadAddress; end end ReadAddress: begin @@ -180,26 +188,29 @@ module dma_axi4_master #( always_ff @(posedge clk_i or negedge rst_n_i) begin if (!rst_n_i) begin - s_write_state_q <= WriteIdle; - s_write_addr_q <= '0; - s_write_beats_q <= 5'd1; - s_write_beat_q <= '0; - s_write_fixed_q <= 1'b0; + s_write_state_q <= WriteIdle; + s_write_addr_q <= '0; + s_write_beats_q <= 5'd1; + s_write_beat_q <= '0; + s_write_fixed_q <= 1'b0; + s_write_origin_q <= 3'd0; end else if (clear_i) begin - s_write_state_q <= WriteIdle; - s_write_addr_q <= '0; - s_write_beats_q <= 5'd1; - s_write_beat_q <= '0; - s_write_fixed_q <= 1'b0; + s_write_state_q <= WriteIdle; + s_write_addr_q <= '0; + s_write_beats_q <= 5'd1; + s_write_beat_q <= '0; + s_write_fixed_q <= 1'b0; + s_write_origin_q <= 3'd0; end else begin unique case (s_write_state_q) WriteIdle: begin if (write_start_valid_i && write_start_ready_o) begin - s_write_addr_q <= write_addr_i; - s_write_beats_q <= write_beats_i; - s_write_beat_q <= '0; - s_write_fixed_q <= write_fixed_i; - s_write_state_q <= WriteAddress; + s_write_addr_q <= write_addr_i; + s_write_beats_q <= write_beats_i; + s_write_beat_q <= '0; + s_write_fixed_q <= write_fixed_i; + s_write_origin_q <= write_origin_channel_i; + s_write_state_q <= WriteAddress; end end WriteAddress: begin diff --git a/rtl/ip/peripheral/dma_core.sv b/rtl/ip/peripheral/dma_core.sv index a8618478..d5f6a7fd 100644 --- a/rtl/ip/peripheral/dma_core.sv +++ b/rtl/ip/peripheral/dma_core.sv @@ -12,16 +12,19 @@ `include "axi4_define.svh" module dma_core #( - parameter int AddrWidth = 32, - parameter int DataWidth = 32, - parameter int NumChannels = 4, - parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, - parameter int MaxBurstBeats = 16, - parameter int FifoDepth = 16, - parameter bit EnableCrc = 1'b1, - parameter logic [31:0] RequestMask = 32'h0000_3fff, - parameter bit EnableStreams = 1'b1, - parameter bit EnableSpi = 1'b0 + parameter int AddrWidth = 32, + parameter int DataWidth = 32, + parameter int NumChannels = 4, + parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, + parameter int MaxBurstBeats = 16, + parameter int FifoDepth = 16, + parameter bit EnableCrc = 1'b1, + parameter logic [31:0] RequestMask = 32'h0000_3fff, + parameter bit EnableStreams = 1'b1, + parameter bit EnableSpi = 1'b0, + parameter bit EnableOriginQualifier = 1'b0, + parameter logic [31:0] SpiTxAddress = 32'd0, + parameter logic [31:0] SpiRxAddress = 32'd0 ) ( // verilog_format: off -- channel vectors are kept aligned with the register-bank ABI. input logic clk_i, @@ -64,6 +67,11 @@ module dma_core #( output logic [8:0] first_error_status_o, output logic [15:0] first_error_addr_hi_o, output logic [31:0] request_status_o, + output logic [1:0] spi_credit_valid_o, + output logic [1:0] spi_credit_retire_o, + output logic [1:0] spi_start_pending_o, + output logic [3:0] spi_status_o, + output logic spi_error_o, output logic xpi_xfer_done_o, dma_req_if.dut req, axi4_if.master axi4, @@ -183,6 +191,7 @@ module dma_core #( logic [ 31:0] s_axi_read_start_addr; logic [ 4:0] s_axi_read_start_beats; logic s_axi_read_start_fixed; + logic [ChannelIndexWidth-1:0] s_axi_read_origin_channel; logic s_axi_read_busy; logic s_axi_read_beat_valid; logic s_axi_read_beat_ready; @@ -198,6 +207,7 @@ module dma_core #( logic [ 31:0] s_axi_write_start_addr; logic [ 4:0] s_axi_write_start_beats; logic s_axi_write_start_fixed; + logic [ChannelIndexWidth-1:0] s_axi_write_origin_channel; logic s_axi_write_busy; logic s_axi_write_data_valid; logic s_axi_write_data_ready; @@ -293,6 +303,10 @@ module dma_core #( ((FifoDepth & (FifoDepth - 1)) != 0)) begin $fatal(1, "dma_core: MVP requires 32-bit AXI4, 2+ channels, and a power-of-two FIFO"); end + if (EnableOriginQualifier && (ChannelIndexWidth > 3)) begin + $fatal(1, + "dma_core: origin qualifier packs three-bit channels, so NumChannels must stay <= 8"); + end end `endif @@ -343,39 +357,74 @@ module dma_core #( .DataWidth (DataWidth), .MaxBurstBeats(MaxBurstBeats) ) u_dma_axi4_master ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clear_i (1'b0), - .read_start_valid_i (s_axi_read_start_valid), - .read_start_ready_o (s_axi_read_start_ready), - .read_addr_i (s_axi_read_start_addr), - .read_beats_i (s_axi_read_start_beats), - .read_fixed_i (s_axi_read_start_fixed), - .read_busy_o (s_axi_read_busy), - .read_beat_valid_o (s_axi_read_beat_valid), - .read_beat_ready_i (s_axi_read_beat_ready), - .read_data_o (s_axi_read_data), - .read_resp_o (s_axi_read_resp), - .read_last_o (s_axi_read_last), - .read_expected_last_o(s_axi_read_expected_last), - .read_id_error_o (s_axi_read_id_error), - .read_done_o (s_axi_read_done), - .write_start_valid_i (s_axi_write_start_valid), - .write_start_ready_o (s_axi_write_start_ready), - .write_addr_i (s_axi_write_start_addr), - .write_beats_i (s_axi_write_start_beats), - .write_fixed_i (s_axi_write_start_fixed), - .write_busy_o (s_axi_write_busy), - .write_data_valid_i (s_axi_write_data_valid), - .write_data_ready_o (s_axi_write_data_ready), - .write_data_i (s_axi_write_data), - .write_strb_i (s_axi_write_strb), - .write_done_o (s_axi_write_done), - .write_resp_o (s_axi_write_resp), - .write_id_error_o (s_axi_write_id_error), - .axi4 (axi4) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clear_i (1'b0), + .read_start_valid_i (s_axi_read_start_valid), + .read_start_ready_o (s_axi_read_start_ready), + .read_addr_i (s_axi_read_start_addr), + .read_beats_i (s_axi_read_start_beats), + .read_fixed_i (s_axi_read_start_fixed), + .read_busy_o (s_axi_read_busy), + .read_beat_valid_o (s_axi_read_beat_valid), + .read_beat_ready_i (s_axi_read_beat_ready), + .read_data_o (s_axi_read_data), + .read_resp_o (s_axi_read_resp), + .read_last_o (s_axi_read_last), + .read_expected_last_o (s_axi_read_expected_last), + .read_id_error_o (s_axi_read_id_error), + .read_done_o (s_axi_read_done), + .write_start_valid_i (s_axi_write_start_valid), + .write_start_ready_o (s_axi_write_start_ready), + .write_addr_i (s_axi_write_start_addr), + .write_beats_i (s_axi_write_start_beats), + .write_fixed_i (s_axi_write_start_fixed), + .write_busy_o (s_axi_write_busy), + .write_data_valid_i (s_axi_write_data_valid), + .write_data_ready_o (s_axi_write_data_ready), + .write_data_i (s_axi_write_data), + .write_strb_i (s_axi_write_strb), + .write_done_o (s_axi_write_done), + .write_resp_o (s_axi_write_resp), + .write_id_error_o (s_axi_write_id_error), + .origin_enable_i (EnableOriginQualifier), + .read_origin_channel_i (s_axi_read_origin_channel), + .write_origin_channel_i(s_axi_write_origin_channel), + .axi4 (axi4) ); + always_comb begin + logic s_spi_error; + spi_credit_valid_o = '0; + spi_credit_retire_o = '0; + spi_start_pending_o = '0; + spi_status_o = '0; + s_spi_error = 1'b0; + if (EnableSpi) begin + if (s_read_owner_valid_q && (s_req_q[s_read_owner_q] == DMA_REQUEST_SPI_RX) && + (s_src_base_q[s_read_owner_q] == SpiRxAddress)) begin + spi_credit_valid_o[1] = 1'b1; + spi_status_o[1] = 1'b1; + s_spi_error = s_spi_error | s_err_q[s_read_owner_q]; + if (s_axi_read_done) spi_credit_retire_o[1] = 1'b1; + end + if (s_write_owner_valid_q && (s_req_q[s_write_owner_q] == DMA_REQUEST_SPI_TX) && + (s_dst_base_q[s_write_owner_q] == SpiTxAddress)) begin + spi_credit_valid_o[0] = 1'b1; + spi_status_o[0] = 1'b1; + s_spi_error = s_spi_error | s_err_q[s_write_owner_q]; + if (s_axi_write_done) spi_credit_retire_o[0] = 1'b1; + end + if (s_read_rr_valid && (s_req_q[s_read_rr_selected] == DMA_REQUEST_SPI_RX) && + (s_src_base_q[s_read_rr_selected] == SpiRxAddress) && !s_axi_read_busy) + spi_start_pending_o[1] = 1'b1; + if (s_write_rr_valid && (s_req_q[s_write_rr_selected] == DMA_REQUEST_SPI_TX) && + (s_dst_base_q[s_write_rr_selected] == SpiTxAddress) && !s_axi_write_busy) + spi_start_pending_o[0] = 1'b1; + end + spi_error_o = s_spi_error; + end + always_comb begin s_req_ready = '0; s_req_ready[DMA_REQUEST_SOFTWARE] = 1'b1; @@ -480,9 +529,11 @@ module dma_core #( (!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) || (s_cfg_req[channel] == DMA_REQUEST_SPI_RX))) || ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) && - (!s_cfg_src_increment[channel] || s_cfg_dst_increment[channel])) || + (!s_cfg_src_increment[channel] || s_cfg_dst_increment[channel] || + s_cfg_dst_addr[channel] != SpiTxAddress)) || ((s_cfg_req[channel] == DMA_REQUEST_SPI_RX) && - (s_cfg_src_increment[channel] || !s_cfg_dst_increment[channel]))) begin + (s_cfg_src_increment[channel] || !s_cfg_dst_increment[channel] || + s_cfg_src_addr[channel] != SpiRxAddress))) begin s_start_valid[channel] = 1'b0; s_start_err_code[channel] = ((s_cfg_src_addr[channel][1:0] != 2'b00) || @@ -680,10 +731,11 @@ module dma_core #( end always_comb begin - s_axi_read_start_valid = s_read_rr_valid && (|s_read_rr_grant) && !s_axi_read_busy; - s_axi_read_start_addr = '0; - s_axi_read_start_beats = 5'd1; - s_axi_read_start_fixed = 1'b0; + s_axi_read_start_valid = s_read_rr_valid && (|s_read_rr_grant) && !s_axi_read_busy; + s_axi_read_start_addr = '0; + s_axi_read_start_beats = 5'd1; + s_axi_read_start_fixed = 1'b0; + s_axi_read_origin_channel = '0; if (s_read_rr_valid) begin s_axi_read_start_addr = s_src_base_q[s_read_rr_selected] + (s_src_increment_q[s_read_rr_selected] @@ -691,18 +743,21 @@ module dma_core #( : 32'd0); s_axi_read_start_beats = s_read_candidate_beats[s_read_rr_selected]; s_axi_read_start_fixed = !s_src_increment_q[s_read_rr_selected]; + s_axi_read_origin_channel = s_read_rr_selected; end if (s_tcd_fetch_valid) begin - s_axi_read_start_valid = !s_axi_read_busy; - s_axi_read_start_addr = s_tcd_ptr_q[s_tcd_fetch_selected]; - s_axi_read_start_beats = 5'd16; - s_axi_read_start_fixed = 1'b0; + s_axi_read_start_valid = !s_axi_read_busy; + s_axi_read_start_addr = s_tcd_ptr_q[s_tcd_fetch_selected]; + s_axi_read_start_beats = 5'd16; + s_axi_read_start_fixed = 1'b0; + s_axi_read_origin_channel = s_tcd_fetch_selected; end - s_axi_write_start_valid = s_write_rr_valid && (|s_write_rr_grant) && !s_axi_write_busy; - s_axi_write_start_addr = '0; - s_axi_write_start_beats = 5'd1; - s_axi_write_start_fixed = 1'b0; + s_axi_write_start_valid = s_write_rr_valid && (|s_write_rr_grant) && !s_axi_write_busy; + s_axi_write_start_addr = '0; + s_axi_write_start_beats = 5'd1; + s_axi_write_start_fixed = 1'b0; + s_axi_write_origin_channel = '0; if (s_write_rr_valid) begin s_axi_write_start_addr = s_dst_base_q[s_write_rr_selected] + (s_dst_increment_q[s_write_rr_selected] @@ -710,6 +765,7 @@ module dma_core #( : 32'd0); s_axi_write_start_beats = s_write_candidate_beats[s_write_rr_selected]; s_axi_write_start_fixed = !s_dst_increment_q[s_write_rr_selected]; + s_axi_write_origin_channel = s_write_rr_selected; end end @@ -1324,7 +1380,11 @@ module dma_core #( s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_RX) && ((s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM) || s_tcd_word7_q[s_tcd_read_owner_q][1] || - !s_tcd_word7_q[s_tcd_read_owner_q][2]))) || + !s_tcd_word7_q[s_tcd_read_owner_q][2] || + s_tcd_word1_q[s_tcd_read_owner_q] != SpiRxAddress))) || + (({s_tcd_word7_q[s_tcd_read_owner_q][25], + s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_TX) && + (s_tcd_word2_q[s_tcd_read_owner_q] != SpiTxAddress)) || (((s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_MM_TO_STREAM) || (s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_STREAM_TO_MM)) && (s_tcd_word3_q[s_tcd_read_owner_q][1:0] != 2'b00)) || diff --git a/rtl/ip/serial/apb4_spi.sv b/rtl/ip/serial/apb4_spi.sv index ce812444..c2afb042 100644 --- a/rtl/ip/serial/apb4_spi.sv +++ b/rtl/ip/serial/apb4_spi.sv @@ -26,11 +26,14 @@ module apb4_spi #( cs_n_o, output_enable_o, irq_o, + session_owned_o, + lifecycle_idle_o, output logic [ 1:0] credit_commit_o, apb4_if.slave apb4 ); spi_ctrl_if u_ctrl (); spi_pkg::configuration_t s_cfg; + spi_pkg::status_t s_stat; logic [31:0] s_claim, s_ready; logic s_handback, s_credit_fault, s_reg_credit_fault, s_pacing_fault; logic s_fifo_pending; @@ -39,6 +42,7 @@ module apb4_spi #( logic [3:0] s_dma_stat; logic s_unused; assign s_cfg = u_ctrl.cfg; + assign s_stat = u_ctrl.stat; // Only personality bits feed the GPIO guard; other fields feed the core/reg blocks. assign s_unused = ^s_cfg; assign s_credit_fault = s_reg_credit_fault | s_pacing_fault; @@ -94,7 +98,9 @@ module apb4_spi #( .apb4 (apb4), .ctrl (u_ctrl) ); - assign credit_commit_o = s_credit_commit; + assign credit_commit_o = s_credit_commit; + assign session_owned_o = s_stat.status[5]; + assign lifecycle_idle_o = s_stat.status[10] && s_stat.status[11] && !s_stat.status[5]; spi_master_core u_master_core ( .apb_pending_i (s_fifo_pending), .clk_i (clk_i), diff --git a/rtl/ip/storage/sdio_dma.sv b/rtl/ip/storage/sdio_dma.sv index e92c4b9d..8d62c539 100644 --- a/rtl/ip/storage/sdio_dma.sv +++ b/rtl/ip/storage/sdio_dma.sv @@ -220,37 +220,40 @@ module sdio_dma #( .DataWidth (DataWidth), .MaxBurstBeats(16) ) u_dma_axi4_master ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clear_i (1'b0), - .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) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clear_i (1'b0), + .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), + .origin_enable_i (1'b0), + .read_origin_channel_i (3'd0), + .write_origin_channel_i(3'd0), + .axi4 (dma_axi4) ); always_comb begin diff --git a/rtl/ip/usb/usb2_dma.sv b/rtl/ip/usb/usb2_dma.sv index 126352b4..ffa0dea8 100644 --- a/rtl/ip/usb/usb2_dma.sv +++ b/rtl/ip/usb/usb2_dma.sv @@ -299,37 +299,40 @@ module usb2_dma #( .DataWidth (DataWidth), .MaxBurstBeats(16) ) u_axi4_master ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clear_i (1'b0), - .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_err), - .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_err), - .axi4 (dma_axi4) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clear_i (1'b0), + .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_err), + .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_err), + .origin_enable_i (1'b0), + .read_origin_channel_i (3'd0), + .write_origin_channel_i(3'd0), + .axi4 (dma_axi4) ); always_comb begin diff --git a/rtl/mk/software.mk b/rtl/mk/software.mk index debd9e82..71481003 100644 --- a/rtl/mk/software.mk +++ b/rtl/mk/software.mk @@ -151,6 +151,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \ $(ROOT_PATH)/crt/src/hal/jpeg_math.c \ $(ROOT_PATH)/crt/src/hal/jpeg.c \ $(ROOT_PATH)/crt/src/hal/dvp.c \ + $(ROOT_PATH)/crt/src/hal/spi_math.c \ + $(ROOT_PATH)/crt/src/hal/spi.c \ $(ROOT_PATH)/crt/src/hal/perf.c \ $(ROOT_PATH)/crt/src/hal/lcd.c \ $(ROOT_PATH)/crt/src/hal/onchip_sram.c \ @@ -183,7 +185,7 @@ TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \ stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \ gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \ dma_math.c dma.c onchip_sram.c xpi.c xpi_psram.c xpi_psram_math.c rng.c crc.c ws2812_math.c ws2812.c \ - crypto.c crypto_lifecycle.c i2s_math.c i2s.c dvp.c sdio_math.c sdio.c rcu.c + crypto.c crypto_lifecycle.c i2s_math.c i2s.c dvp.c sdio_math.c sdio.c spi_math.c spi.c rcu.c CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source))) CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c endif @@ -307,4 +309,4 @@ firmware: $(FIRMWARE_ELF) firmware asm: | manifest -.PHONY: FORCE_VERSION \ No newline at end of file +.PHONY: FORCE_VERSION diff --git a/rtl/tiny/address_map/memory_map.json b/rtl/tiny/address_map/memory_map.json index b065349b..2542607b 100644 --- a/rtl/tiny/address_map/memory_map.json +++ b/rtl/tiny/address_map/memory_map.json @@ -444,6 +444,15 @@ "kind": "active", "public": true, "user_access": "none" + }, + { + "symbol": "APB4_SPI", + "base": "0x1001D000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "none" } ], "legacy_rib": false, diff --git a/rtl/tiny/filelist/ip.fl b/rtl/tiny/filelist/ip.fl index e0552e79..58c0077a 100644 --- a/rtl/tiny/filelist/ip.fl +++ b/rtl/tiny/filelist/ip.fl @@ -63,6 +63,15 @@ /ip/serial/i2s_send.sv /ip/serial/i2s_core.sv /ip/serial/apb4_i2s.sv +/ip/serial/spi_pkg.sv +/ip/serial/spi_ctrl_if.sv +/ip/serial/spi_phase_ctrl.sv +/ip/serial/spi_word_packer.sv +/ip/serial/spi_gpio_guard.sv +/ip/serial/spi_master_core.sv +/ip/serial/spi_reg.sv +/ip/serial/spi_dma_pacing.sv +/ip/serial/apb4_spi.sv /ip/serial/ws2812_if.sv /ip/serial/ws2812_reg.sv /ip/serial/ws2812_core.sv diff --git a/rtl/tiny/integration/clock_reset_domains.json b/rtl/tiny/integration/clock_reset_domains.json index 3462f0d0..f029cead 100644 --- a/rtl/tiny/integration/clock_reset_domains.json +++ b/rtl/tiny/integration/clock_reset_domains.json @@ -4,7 +4,7 @@ "phase": "TINY-R2-P7", "clock_domain": "system", "target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller", - "target_count": 28, + "target_count": 29, "apb_bridges": "SYS frontends; acknowledged per-endpoint crossings to AON, PCLK and MEM", "fabric_reset": "fabric leaf; target reset blocks admission and aborts accepted target work with DECERR", "hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active", @@ -161,7 +161,8 @@ "crypto", "i2s", "sdio", - "dvp" + "dvp", + "spi" ], "cpu_prerequisite": "all global leaf resets and actual APB/DMA/XPI/RCU barriers; boot readiness latches until the next system reset", "hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync", @@ -480,7 +481,8 @@ "ws2812", "crypto", "i2s", - "dvp" + "dvp", + "spi" ] }, "same_clock_isolation": [ diff --git a/rtl/tiny/integration/soc_topology.json b/rtl/tiny/integration/soc_topology.json index 8983e2d8..78c9dfed 100644 --- a/rtl/tiny/integration/soc_topology.json +++ b/rtl/tiny/integration/soc_topology.json @@ -37,9 +37,9 @@ ], "xpi_target": 4, "apb_target_base": 5, - "decode_error_target": 26, - "protocol_error_target": 27, - "target_count": 28 + "decode_error_target": 27, + "protocol_error_target": 28, + "target_count": 29 }, "gpio_pins": 32, "irq_vector_width": 32, @@ -172,6 +172,12 @@ "regions": [ "APB4_DVP" ] + }, + { + "name": "spi", + "regions": [ + "APB4_SPI" + ] } ], "interrupts": [ @@ -265,6 +271,11 @@ "core_bit": 23, "signal": "s_crypto_irq" }, + { + "name": "spi", + "core_bit": 25, + "signal": "s_spi_irq" + }, { "name": "rcu", "core_bit": 31, @@ -677,8 +688,8 @@ "pin": 27, "alt0": { "inputs": [], - "do": "1'b0", - "oe": "1'b0" + "do": "s_spi_sck", + "oe": "s_spi_output_enable" }, "alt1": { "inputs": [], @@ -695,8 +706,8 @@ }, "alt1": { "inputs": [], - "do": "1'b0", - "oe": "1'b0" + "do": "s_spi_mosi", + "oe": "s_spi_output_enable" } }, { @@ -720,7 +731,9 @@ "oe": "1'b1" }, "alt1": { - "inputs": [], + "inputs": [ + "s_spi_miso" + ], "do": "1'b0", "oe": "1'b0" } @@ -734,12 +747,12 @@ }, "alt1": { "inputs": [], - "do": "1'b0", - "oe": "1'b0" + "do": "s_spi_cs_n", + "oe": "s_spi_output_enable" } } ], - "dma_request_mask": "0x39ff", + "dma_request_mask": "0x339ff", "private_crypto": { "banks": 6, "bank_bytes": 4096, diff --git a/rtl/tiny/top/retrosoc_tiny.sv b/rtl/tiny/top/retrosoc_tiny.sv index b50628ef..aad816c5 100644 --- a/rtl/tiny/top/retrosoc_tiny.sv +++ b/rtl/tiny/top/retrosoc_tiny.sv @@ -89,15 +89,15 @@ module retrosoc_tiny ( logic [31:0] s_clkout_control; logic s_clkout, s_clkout_enable; logic s_clkout_idle_ref; - logic [14:0] s_gate_request, s_local_reset, s_actual_gate, s_reset_held, s_reset_ready; - logic [14:0] s_gate_ref, s_held_ref, s_ready_ref, s_disabled_sys; + logic [16:0] s_gate_request, s_local_reset, s_actual_gate, s_reset_held, s_reset_ready; + logic [16:0] s_gate_ref, s_held_ref, s_ready_ref, s_disabled_sys; logic s_periph_apply, s_periph_reset_command, s_periph_block, s_periph_done; - logic [14:0] s_periph_request, s_periph_reset_done; + logic [16:0] s_periph_request, s_periph_reset_done; logic [4:0] s_periph_fault; logic s_bus_idle_ref; logic s_boot_release_d, s_boot_release_q; logic s_wfi_gate_sys; - logic [14:0] s_periph_idle, s_periph_idle_ref; + logic [16:0] s_periph_idle, s_periph_idle_ref; logic s_xpi_idle, s_xpi_idle_ref; logic s_pwm_write, s_crc_write; logic [ 4:0] s_clock_fault; @@ -110,6 +110,20 @@ module retrosoc_tiny ( logic s_i2s_tx_stall, s_i2s_rx_stall; logic s_i2s_reset_ready; logic s_dvp_irq, s_dvp_idle, s_dvp_reset_ready; + logic s_spi_sck, s_spi_mosi, s_spi_cs_n, s_spi_output_enable, s_spi_miso; + logic s_spi_irq, s_spi_session_owned, s_spi_lifecycle_idle; + logic [1:0] s_spi_origin; + logic [2:0] s_spi_channel; + logic [31:0] s_gpio_user_select, s_gpio_user_handoff, s_gpio_alt_enable; + logic [31:0] s_gpio_alt_select, s_gpio_output_enable, s_gpio_open_drain; + logic [1:0] s_spi_credit_valid, s_spi_credit_retire, s_spi_dma_start_pending; + logic [3:0] s_spi_dma_status; + logic s_spi_dma_error; + // Commit strobes are register-visible inside the SPI shell; the SoC has no consumer yet. + /* verilator lint_off UNUSEDSIGNAL */ + logic [1:0] s_spi_credit_commit; + /* verilator lint_on UNUSEDSIGNAL */ + logic [4:0] s_spi_request; logic s_done_pclk_rst_n, s_done_mem_rst_n; `include "tiny_apb_interfaces.svh" ahbl_if u_cpu_ahbl_if[2] ( @@ -121,7 +135,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_sram_axi4_if[4] ( .aclk (s_exec_clk), .aresetn(s_sram_rst_n) @@ -151,7 +165,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_masters_axi4_if[3] ( .aclk (s_exec_clk), .aresetn(s_master_rst_n) @@ -160,7 +174,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_dma_pclk_axi4_if ( .aclk (s_dma_clk), .aresetn(s_dma_rst_n) @@ -169,7 +183,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_dma_admitted_axi4_if ( .aclk (s_backend_pclk), .aresetn(s_dma_rst_n) @@ -178,7 +192,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_sdio_sys_axi4_if ( .aclk (s_sdio_clk), .aresetn(s_sdio_rst_n) @@ -187,7 +201,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] ( .aclk (s_exec_clk), .aresetn(s_target_rst_n) @@ -196,7 +210,7 @@ ahbl_if u_cpu_ahbl_if[2] ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_xpi_mem_axi4_if ( .aclk (s_backend_mem_clk), .aresetn(s_backend_mem_rst_n) @@ -392,11 +406,14 @@ ahbl_if u_cpu_ahbl_if[2] ( .dat_i (s_gate_request[0]), .dat_o (s_wfi_gate_sys) ); - assign s_reset_held[0] = 1'b0; - assign s_reset_ready[0] = 1'b0; + assign s_reset_held[0] = 1'b0; + assign s_reset_ready[0] = 1'b0; + assign s_actual_gate[15] = 1'b0; + assign s_reset_held[15] = 1'b0; + assign s_reset_ready[15] = 1'b0; cdc_sync #( .STAGE (2), - .DATA_WIDTH(15) + .DATA_WIDTH(17) ) u_gate_feedback ( .clk_i (s_ref_clk), .rst_n_i(s_por_rst_n), @@ -405,7 +422,7 @@ ahbl_if u_cpu_ahbl_if[2] ( ); cdc_sync #( .STAGE (2), - .DATA_WIDTH(15) + .DATA_WIDTH(17) ) u_held_feedback ( .clk_i (s_ref_clk), .rst_n_i(s_por_rst_n), @@ -414,7 +431,7 @@ ahbl_if u_cpu_ahbl_if[2] ( ); cdc_sync #( .STAGE (2), - .DATA_WIDTH(15) + .DATA_WIDTH(17) ) u_ready_feedback ( .clk_i (s_ref_clk), .rst_n_i(s_por_rst_n), @@ -423,7 +440,7 @@ ahbl_if u_cpu_ahbl_if[2] ( ); cdc_sync #( .STAGE (2), - .DATA_WIDTH(15) + .DATA_WIDTH(17) ) u_disabled_sync ( .clk_i (s_exec_clk), .rst_n_i(s_fabric_rst_n), @@ -477,11 +494,14 @@ ahbl_if u_cpu_ahbl_if[2] ( .crc_idle_o (s_periph_idle[12]) ); assign s_periph_idle[0] = s_cpu_bus_idle; - assign s_periph_idle[1] = 1'b1; // GPIO has no independent transaction engine; USER is absent. + assign s_periph_idle[1] = !s_spi_session_owned && !(|s_gpio_user_select); assign s_periph_idle[11] = !s_rng_en && !s_rng_valid; + assign s_periph_idle[15] = 1'b1; + assign s_periph_idle[16] = s_spi_lifecycle_idle; + assign s_spi_request = u_spi_apb4_if.pwrite ? 5'd16 : 5'd17; cdc_sync #( .STAGE (2), - .DATA_WIDTH(15) + .DATA_WIDTH(17) ) u_periph_idle_sync ( .clk_i (s_ref_clk), .rst_n_i(s_por_rst_n), @@ -706,7 +726,8 @@ ahbl_if u_cpu_ahbl_if[2] ( ); tiny_axi4_async_bridge #( .DataWidth(32), - .IdWidth (1) + .IdWidth (1), + .UserWidth(4) ) u_dma_cdc ( .src_clk_i (s_dma_clk), .src_rst_n_i (s_dma_rst_n), @@ -737,7 +758,8 @@ ahbl_if u_cpu_ahbl_if[2] ( ); tiny_axi4_async_bridge #( .DataWidth(32), - .IdWidth (1) + .IdWidth (1), + .UserWidth(4) ) u_xpi_mem_cdc ( .src_clk_i (s_exec_clk), .src_rst_n_i (s_fabric_rst_n), @@ -800,7 +822,7 @@ axi4_error_slave #( .PllPresent (TinyPllPresent), .PllProfiles(TinyPllPresent), .XinHzValue (`SOC_EXT_CLK_HZ), - .TargetMask (15'h07fff) + .TargetMask (17'h17fff) ) u_sysctrl ( .clk_i (s_exec_clk), .rst_n_i (s_sysctrl_rst_n), @@ -860,7 +882,7 @@ axi4_error_slave #( .ref_clk_i (clk_i), .rst_n_i (rst_n_i), .idle_i (s_clock_safe), - .clients_idle_i ((&s_periph_idle_ref[14:1]) && s_xpi_idle_ref), + .clients_idle_i ((&s_periph_idle_ref[16:1]) && s_xpi_idle_ref), .warm_reset_i (u_wdg_if.reset_req_o), .system_request_i (s_system_reset), .command_ack_i (s_cpu_clock_idle && s_target_idle[`RETROSOC_TINY__TARGET_SYSCTRL]), @@ -900,11 +922,49 @@ axi4_error_slave #( .HasPullUp (gpio_pad_caps_pkg::HasPullUp), .HasPullDown (gpio_pad_caps_pkg::HasPullDown) ) u_gpio ( - .clk_i (s_gpio_clk), - .rst_n_i (s_gpio_rst_n), - .apb4 (u_gpio_apb4_if), - .gpio (u_gpio_if), - .user_gpio(u_user_gpio_if) + .clk_i (s_gpio_clk), + .rst_n_i (s_gpio_rst_n), + .apb4 (u_gpio_apb4_if), + .gpio (u_gpio_if), + .user_gpio (u_user_gpio_if), + .user_select_o (s_gpio_user_select), + .user_handoff_o (s_gpio_user_handoff), + .alt_enable_o (s_gpio_alt_enable), + .alt_select_o (s_gpio_alt_select), + .output_enable_o(s_gpio_output_enable), + .open_drain_o (s_gpio_open_drain) + ); + apb4_spi #( + .EnableDma(1'b1) + ) u_spi ( + .clk_i (s_spi_clk), + .rst_n_i (s_spi_rst_n), + .clock_hz_i (s_pclk_report_q), + .origin_i (s_spi_origin), + .request_i (s_spi_request), + .channel_i (s_spi_channel), + .credit_valid_i (s_spi_credit_valid), + .credit_retire_i (s_spi_credit_retire), + .dma_status_i (s_spi_dma_status), + .dma_start_pending_i(s_spi_dma_start_pending), + .dma_error_i (s_spi_dma_error), + .user_select_i (s_gpio_user_select), + .handoff_i (s_gpio_user_handoff), + .alt_enable_i (s_gpio_alt_enable), + .alt_select_i (s_gpio_alt_select), + .output_enable_i (s_gpio_output_enable), + .open_drain_i (s_gpio_open_drain), + .gpio_ready_i (s_gpio_rst_n && s_apb_cdc_ready[0]), + .miso_i (s_spi_miso), + .sck_o (s_spi_sck), + .mosi_o (s_spi_mosi), + .cs_n_o (s_spi_cs_n), + .output_enable_o (s_spi_output_enable), + .irq_o (s_spi_irq), + .credit_commit_o (s_spi_credit_commit), + .session_owned_o (s_spi_session_owned), + .lifecycle_idle_o (s_spi_lifecycle_idle), + .apb4 (u_spi_apb4_if) ); gpio_pad_bridge u_gpio_pads ( .inner(u_gpio_if), @@ -1002,25 +1062,36 @@ cdc_sync #( .lifecycle_reset_ready_o(s_dvp_reset_ready) ); apb4_dma #( - .NumChannels (8), - .MaxBurstBeats(16), - .FifoDepth (32), - .RequestMask (16'h39ff), - .EnableStreams(1'b1) + .NumChannels (8), + .MaxBurstBeats (16), + .FifoDepth (32), + .RequestMask (32'h0003_39ff), + .SupportedRequests (32'h0003_39ff), + .IpVersion (32'h0002_0002), + .EnableStreams (1'b1), + .EnableSpi (1'b1), + .EnableOriginQualifier(1'b1), + .SpiTxAddress (32'h1001_d054), + .SpiRxAddress (32'h1001_d058) ) u_dma ( - .lifecycle_idle_o(s_periph_idle[9]), - .clk_i (s_dma_clk), - .rst_n_i (s_dma_rst_n), - .dma_xfer_done_o (s_dma_done), - .irq_o (s_dma_irq), - .hw_trg (u_dma_req_if), - .apb4 (u_dma_apb4_if), - .axi4 (u_dma_pclk_axi4_if), - .i2s_tx_axis (u_i2s_tx_axis_if), - .i2s_rx_axis (u_i2s_rx_axis_if), - .dvp_rx_axis (u_dvp_rx_axis_if), - .crypto_in_axis (u_crypto_tx_axis_if), - .crypto_out_axis (u_crypto_rx_axis_if) + .lifecycle_idle_o (s_periph_idle[9]), + .clk_i (s_dma_clk), + .rst_n_i (s_dma_rst_n), + .dma_xfer_done_o (s_dma_done), + .irq_o (s_dma_irq), + .hw_trg (u_dma_req_if), + .apb4 (u_dma_apb4_if), + .axi4 (u_dma_pclk_axi4_if), + .i2s_tx_axis (u_i2s_tx_axis_if), + .i2s_rx_axis (u_i2s_rx_axis_if), + .dvp_rx_axis (u_dvp_rx_axis_if), + .crypto_in_axis (u_crypto_tx_axis_if), + .crypto_out_axis (u_crypto_rx_axis_if), + .spi_credit_valid_o (s_spi_credit_valid), + .spi_credit_retire_o(s_spi_credit_retire), + .spi_start_pending_o(s_spi_dma_start_pending), + .spi_status_o (s_spi_dma_status), + .spi_error_o (s_spi_dma_error) ); assign u_dma_req_if.i2s_tx_proc = !s_i2s_tx_stall; assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall; diff --git a/rtl/tiny/top/tiny_apb_async_bridge.sv b/rtl/tiny/top/tiny_apb_async_bridge.sv index 948fc3d6..dd6b5ca1 100644 --- a/rtl/tiny/top/tiny_apb_async_bridge.sv +++ b/rtl/tiny/top/tiny_apb_async_bridge.sv @@ -6,15 +6,19 @@ // error; it never replays it when the destination returns. The SYS endpoint // remains clocked so a stopped/disabled peripheral is still reachable. module tiny_apb_async_bridge ( - input logic src_clk_i, - input logic src_rst_n_i, - input logic dst_clk_i, - input logic dst_rst_n_i, - input logic disabled_i, - output logic idle_o, - output logic ready_o, - apb4_if.slave src_apb4, - apb4_if.master dst_apb4 + input logic src_clk_i, + input logic src_rst_n_i, + input logic dst_clk_i, + input logic dst_rst_n_i, + input logic disabled_i, + input logic [1:0] src_origin_i, + input logic [2:0] src_channel_i, + output logic idle_o, + output logic ready_o, + output logic [1:0] dst_origin_o, + output logic [2:0] dst_channel_o, + apb4_if.slave src_apb4, + apb4_if.master dst_apb4 ); typedef enum logic [1:0] { SrcIdle, @@ -30,19 +34,19 @@ module tiny_apb_async_bridge ( } dst_state_e; typedef struct packed { src_state_e state; - logic [71:0] req; + logic [76:0] req; logic [32:0] resp; } src_t; typedef struct packed { dst_state_e state; - logic [71:0] req; + logic [76:0] req; logic [32:0] resp; } dst_t; src_t s_src_d, s_src_q; dst_t s_dst_d, s_dst_q; logic s_link_src_rst_n, s_link_dst_rst_n; logic s_req_ready, s_req_valid, s_resp_ready, s_resp_valid; - logic [71:0] s_req_data; + logic [76:0] s_req_data; logic [32:0] s_resp_data; cdc_reset_barrier #( @@ -56,7 +60,7 @@ module tiny_apb_async_bridge ( .rst_b_n_o(s_link_dst_rst_n) ); cdc_2phase #( - .DATA_WIDTH(72) + .DATA_WIDTH(77) ) u_req_cdc ( .src_clk_i (src_clk_i), .src_rst_n_i(s_link_src_rst_n), @@ -89,10 +93,12 @@ module tiny_apb_async_bridge ( assign src_apb4.pslverr = s_src_q.resp[32]; assign src_apb4.prdata = s_src_q.resp[31:0]; assign {dst_apb4.paddr, dst_apb4.pprot, dst_apb4.pwrite, - dst_apb4.pwdata, dst_apb4.pstrb} = s_dst_q.req; + dst_apb4.pwdata, dst_apb4.pstrb} = s_dst_q.req[71:0]; assign dst_apb4.psel = s_link_dst_rst_n && ((s_dst_q.state == DstSetup) || (s_dst_q.state == DstAccess)); assign dst_apb4.penable = s_link_dst_rst_n && (s_dst_q.state == DstAccess); + assign dst_origin_o = s_dst_q.req[76:75]; + assign dst_channel_o = s_dst_q.req[74:72]; always_comb begin s_src_d = s_src_q; @@ -100,7 +106,13 @@ module tiny_apb_async_bridge ( SrcIdle: if (src_apb4.psel && src_apb4.penable) begin s_src_d.req = { - src_apb4.paddr, src_apb4.pprot, src_apb4.pwrite, src_apb4.pwdata, src_apb4.pstrb + src_origin_i, + src_channel_i, + src_apb4.paddr, + src_apb4.pprot, + src_apb4.pwrite, + src_apb4.pwdata, + src_apb4.pstrb }; s_src_d.resp = {1'b1, 32'd0}; s_src_d.state = disabled_i ? SrcResponse : SrcSend; diff --git a/rtl/tiny/top/tiny_axi42apb4.sv b/rtl/tiny/top/tiny_axi42apb4.sv index d0b48b2d..ec9e4d45 100644 --- a/rtl/tiny/top/tiny_axi42apb4.sv +++ b/rtl/tiny/top/tiny_axi42apb4.sv @@ -4,10 +4,12 @@ // APB setup/access are separate cycles; rejected bursts never assert PSEL. module tiny_axi42apb4 ( - input logic clk_i, - input logic rst_n_i, - axi4_if.slave axi4, - apb4_if.master apb4 + input logic clk_i, + input logic rst_n_i, + axi4_if.slave axi4, + apb4_if.master apb4, + output logic [1:0] origin_o, + output logic [2:0] channel_o ); typedef enum logic [2:0] { Idle, @@ -30,6 +32,7 @@ module tiny_axi42apb4 ( logic [1:0] s_resp_d, s_resp_q; logic [7:0] s_len_d, s_len_q; logic [7:0] s_beat_d, s_beat_q; + logic [3:0] s_user_d, s_user_q; logic s_access, s_enable, s_done, s_err; logic [31:0] s_read_data; @@ -61,6 +64,8 @@ module tiny_axi42apb4 ( assign apb4.pprot = 3'd0; assign apb4.psel = s_access; assign apb4.penable = s_enable; + assign origin_o = s_user_q[3] ? 2'd1 : (s_user_q == 4'd0 ? 2'd0 : 2'd2); + assign channel_o = s_user_q[2:0]; always_comb begin s_state_d = s_state_q; @@ -73,6 +78,7 @@ module tiny_axi42apb4 ( s_data_d = s_data_q; s_len_d = s_len_q; s_beat_d = s_beat_q; + s_user_d = s_user_q; unique case (s_state_q) Idle: begin s_beat_d = '0; @@ -82,6 +88,7 @@ module tiny_axi42apb4 ( s_addr_d = axi4.araddr; s_id_d = axi4.arid; s_write_d = 1'b0; + s_user_d = axi4.aruser; s_len_d = axi4.arlen; if ((axi4.arlen != 8'd0) || (axi4.arburst[1]) || axi4.arlock || (axi4.arsize > 3'd2) || (axi4.araddr[1:0] != 2'd0)) begin @@ -92,6 +99,7 @@ module tiny_axi42apb4 ( s_addr_d = axi4.awaddr; s_id_d = axi4.awid; s_write_d = 1'b1; + s_user_d = axi4.awuser; s_len_d = axi4.awlen; if ((axi4.awlen != 8'd0) || (axi4.awburst[1]) || axi4.awlock || (axi4.awsize > 3'd2) || (axi4.awaddr[1:0] != 2'd0)) begin @@ -210,4 +218,12 @@ module tiny_axi42apb4 ( .dat_i (s_beat_d), .dat_o (s_beat_q) ); + dffr #( + .DATA_WIDTH(4) + ) u_user_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_user_d), + .dat_o (s_user_q) + ); endmodule diff --git a/rtl/tiny/top/tiny_axi4_admission.sv b/rtl/tiny/top/tiny_axi4_admission.sv index 5824180a..f42ded80 100644 --- a/rtl/tiny/top/tiny_axi4_admission.sv +++ b/rtl/tiny/top/tiny_axi4_admission.sv @@ -23,7 +23,7 @@ module tiny_axi4_admission ( logic [2:0] prot; logic [3:0] qos; logic [3:0] region; - logic [0:0] user; + logic [3:0] user; } address_t; address_t s_aw_d, s_aw_q, s_ar_d, s_ar_q; logic [1:0] s_pending_d, s_pending_q, s_sent_d, s_sent_q; diff --git a/rtl/tiny/top/tiny_axi4_fabric.sv b/rtl/tiny/top/tiny_axi4_fabric.sv index 2446037f..880809a9 100644 --- a/rtl/tiny/top/tiny_axi4_fabric.sv +++ b/rtl/tiny/top/tiny_axi4_fabric.sv @@ -29,10 +29,10 @@ module tiny_axi4_fabric ( logic s_cpu_prefer_write_d, s_cpu_prefer_write_q; logic [2:0][4:0] s_read_target, s_write_target; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][2:0] s_arready, s_awready, s_wready, s_rvalid, s_bvalid; - logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][36:0] s_rdata; - logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 3:0] s_bdata; - logic [ 2:0][36:0] s_read_return; - logic [ 2:0][ 3:0] s_write_return; + logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][39:0] s_rdata; + logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 6:0] s_bdata; + logic [ 2:0][39:0] s_read_return; + logic [ 2:0][ 6:0] s_write_return; logic [2:0] s_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid; logic [2:0] s_arvalid, s_awvalid; logic [1:0][63:0] s_wait_d, s_wait_q; @@ -191,7 +191,7 @@ module tiny_axi4_fabric ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_routes[3] ( .aclk (clk_i), .aresetn(rst_n_i) diff --git a/rtl/tiny/top/tiny_axi4_target.sv b/rtl/tiny/top/tiny_axi4_target.sv index d53b1051..ca7c3b81 100644 --- a/rtl/tiny/top/tiny_axi4_target.sv +++ b/rtl/tiny/top/tiny_axi4_target.sv @@ -36,7 +36,7 @@ module tiny_axi4_target ( logic [3:0] qos; logic [3:0] region; logic id; - logic user; + logic [3:0] user; } addr_t; typedef struct packed { state_e state; @@ -56,20 +56,21 @@ module tiny_axi4_target ( logic [31:0] s_req_addr; logic s_req_id; addr_t s_read_addr[3], s_write_addr[3]; - logic [2:0] s_arvalid, s_awvalid, s_wvalid, s_wlast, s_bready, s_rready, s_wuser; + logic [2:0] s_arvalid, s_awvalid, s_wvalid, s_wlast, s_bready, s_rready; + logic [2:0][ 3:0] s_wuser; logic [2:0][31:0] s_wdata; logic [2:0][ 3:0] s_wstrb; logic [2:0] s_owner_req, s_grant, s_prefer_write_d, s_prefer_write_q; logic [1:0] s_selected; logic s_valid, s_selected_write, s_accept, s_terminal; logic s_r_valid, s_r_ready, s_b_valid, s_b_ready; - logic [36:0] s_r_data; - logic [ 3:0] s_b_data; + logic [39:0] s_r_data; + logic [ 6:0] s_b_data; logic s_read_capture, s_write_capture; logic s_abort_read_valid, s_abort_write_valid; logic s_resp_active, s_rin_valid, s_bin_valid; - logic [36:0] s_rin_data; - logic [ 3:0] s_bin_data; + logic [39:0] s_rin_data; + logic [ 6:0] s_bin_data; assign s_saved_addr = s_req_q.addr; assign s_req_addr = s_saved_addr.addr; @@ -89,7 +90,7 @@ module tiny_axi4_target ( s_req_q.write_done && !s_req_q.b_captured; assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : - (s_r_valid && s_rready[s_req_q.owner] && s_r_data[1])); + (s_r_valid && s_rready[s_req_q.owner] && s_r_data[4])); assign fault_valid_o = s_terminal && s_req_q.error_seen; assign fault_addr_o = s_req_q.error_addr; assign fault_master_o = s_req_q.owner; @@ -100,11 +101,11 @@ module tiny_axi4_target ( !s_req_q.write && target.rvalid) || s_abort_read_valid; assign s_rin_data = s_abort_read_valid ? {s_req_id, 32'd0, 2'b11, - (s_req_q.read_count == {1'b0, s_saved_addr.len}), 1'b0} : + (s_req_q.read_count == {1'b0, s_saved_addr.len}), 4'd0} : {target.rid, target.rdata, target.rresp, target.rlast, target.ruser}; assign s_bin_valid = ((s_req_q.state == Response) && enable_i && s_req_q.write && target.bvalid) || s_abort_write_valid; - assign s_bin_data = s_abort_write_valid ? {s_req_id, 2'b11, 1'b0} : + assign s_bin_data = s_abort_write_valid ? {s_req_id, 2'b11, 4'd0} : {target.bid, target.bresp, target.buser}; assign s_read_capture = s_rin_valid && s_r_ready; assign s_write_capture = s_bin_valid && s_b_ready; @@ -218,7 +219,7 @@ module tiny_axi4_target ( assign target.rready = (s_req_q.state == Response) && enable_i && !s_req_q.write && s_r_ready; assign target.bready = (s_req_q.state == Response) && enable_i && s_req_q.write && s_b_ready; spill_register #( - .DATA_WIDTH(37) + .DATA_WIDTH(40) ) u_read_response ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -231,7 +232,7 @@ module tiny_axi4_target ( .data_o (s_r_data) ); spill_register #( - .DATA_WIDTH(4) + .DATA_WIDTH(7) ) u_write_response ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -248,11 +249,24 @@ module tiny_axi4_target ( unique case (s_req_q.state) Idle: if (s_accept) begin - s_req_d = '0; - s_req_d.state = Address; - s_req_d.owner = s_selected; - s_req_d.write = s_selected_write; - s_req_d.addr = s_selected_addr; + s_req_d = '0; + s_req_d.state = Address; + s_req_d.owner = s_selected; + s_req_d.write = s_selected_write; + s_req_d.addr = { + s_selected_addr.addr, + s_selected_addr.len, + s_selected_addr.size, + s_selected_addr.burst, + s_selected_addr.lock, + s_selected_addr.cache, + s_selected_addr.prot, + s_selected_addr.qos, + s_selected_addr.region, + s_selected_addr.id, + (s_selected == 2'd1) ? {1'b1, s_selected_addr.user[2:0]} : + (s_selected == 2'd2) ? 4'b0100 : 4'b0000 + }; s_req_d.read_addr = s_selected_addr.addr; end Address: begin @@ -286,18 +300,18 @@ module tiny_axi4_target ( s_req_d.read_count = s_req_q.read_count + 9'd1; if (s_saved_addr.burst == 2'b01) s_req_d.read_addr = s_req_q.read_addr + (32'd1 << s_saved_addr.size); - if (!s_req_q.error_seen && (s_rin_data[3:2] != 2'b00)) begin + if (!s_req_q.error_seen && (s_rin_data[6:5] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; - s_req_d.error_resp = s_rin_data[3:2]; + s_req_d.error_resp = s_rin_data[6:5]; end end if (s_write_capture) begin s_req_d.b_captured = 1'b1; - if (!s_req_q.error_seen && (s_bin_data[2:1] != 2'b00)) begin + if (!s_req_q.error_seen && (s_bin_data[5:4] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_addr; - s_req_d.error_resp = s_bin_data[2:1]; + s_req_d.error_resp = s_bin_data[5:4]; end end end diff --git a/rtl/tiny/top/tiny_cpu_mem.sv b/rtl/tiny/top/tiny_cpu_mem.sv index 5a229270..e7ec1250 100644 --- a/rtl/tiny/top/tiny_cpu_mem.sv +++ b/rtl/tiny/top/tiny_cpu_mem.sv @@ -63,7 +63,7 @@ module tiny_cpu_mem #( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) u_slow_axi4_if ( .aclk (clk_i), .aresetn(rst_n_i) diff --git a/rtl/tiny/top/tiny_peripheral_control.sv b/rtl/tiny/top/tiny_peripheral_control.sv index 1aeda329..ee5fb424 100644 --- a/rtl/tiny/top/tiny_peripheral_control.sv +++ b/rtl/tiny/top/tiny_peripheral_control.sv @@ -9,19 +9,19 @@ module tiny_peripheral_control ( input logic warm_reset_i, input logic apply_i, input logic reset_command_i, - input logic [14:0] request_i, - input logic [14:0] idle_i, + input logic [16:0] request_i, + input logic [16:0] idle_i, input logic bus_idle_i, - input logic [14:0] gate_actual_i, - input logic [14:0] reset_held_i, - input logic [14:0] reset_ready_i, + input logic [16:0] gate_actual_i, + input logic [16:0] reset_held_i, + input logic [16:0] reset_ready_i, input logic [31:0] timeout_i, - output logic [14:0] gate_o, - output logic [14:0] reset_o, + output logic [16:0] gate_o, + output logic [16:0] reset_o, output logic block_o, output logic done_o, output logic [ 4:0] fault_o, - output logic [14:0] reset_done_o + output logic [16:0] reset_done_o ); typedef enum logic [2:0] { Idle, @@ -34,20 +34,20 @@ module tiny_peripheral_control ( typedef struct packed { state_e state; logic reset_command; - logic [14:0] request_mask, gates, previous_gates, resets, reset_done, affected; + logic [16:0] request_mask, gates, previous_gates, resets, reset_done, affected; logic [31:0] count; logic done; logic [4:0] fault; } control_t; control_t s_ctrl_d, s_ctrl_q; - logic [14:0] s_affected; + logic [16:0] s_affected; assign s_affected = s_ctrl_q.affected; assign gate_o = s_ctrl_q.gates; assign reset_o = s_ctrl_q.resets; assign reset_done_o = s_ctrl_q.reset_done; // Arming the WFI leaf permission must not pause an unrelated DMA job. // The CPU leaf itself waits for its accepted I/D work before stopping. - assign block_o = (s_ctrl_q.state != Idle) && (s_affected[14:1] != 0); + assign block_o = (s_ctrl_q.state != Idle) && (s_affected[16:1] != 0); assign done_o = s_ctrl_q.done; assign fault_o = s_ctrl_q.fault; always_comb begin @@ -74,8 +74,8 @@ module tiny_peripheral_control ( end end Drain: begin - if (s_ctrl_q.count >= 16 && ((s_affected[14:1] == 0) || (bus_idle_i && idle_i[9])) && - ((idle_i[14:1] & s_affected[14:1]) == s_affected[14:1])) begin + if (s_ctrl_q.count >= 16 && ((s_affected[16:1] == 0) || (bus_idle_i && idle_i[9])) && + ((idle_i[16:1] & s_affected[16:1]) == s_affected[16:1])) begin s_ctrl_d.count = 0; if (s_ctrl_q.reset_command) begin s_ctrl_d.resets = s_ctrl_q.request_mask; @@ -90,7 +90,7 @@ module tiny_peripheral_control ( s_ctrl_d.state = Idle; end end - GateWait: if (gate_actual_i[14:1] == s_ctrl_q.gates[14:1]) s_ctrl_d.state = Complete; + GateWait: if (gate_actual_i[16:1] == s_ctrl_q.gates[16:1]) s_ctrl_d.state = Complete; Hold: if ((reset_held_i & s_ctrl_q.request_mask) == s_ctrl_q.request_mask) begin s_ctrl_d.resets = 0; diff --git a/rtl/tiny/top/tiny_sysctrl.sv b/rtl/tiny/top/tiny_sysctrl.sv index fc5f9371..e6ed104b 100644 --- a/rtl/tiny/top/tiny_sysctrl.sv +++ b/rtl/tiny/top/tiny_sysctrl.sv @@ -7,7 +7,7 @@ module tiny_sysctrl #( parameter bit PllPresent = 1'b0, parameter bit PllProfiles = 1'b0, parameter int unsigned XinHzValue = 24_000_000, - parameter logic [14:0] TargetMask = 15'd0 + parameter logic [16:0] TargetMask = 17'd0 ) ( input logic clk_i, input logic rst_n_i, @@ -37,11 +37,11 @@ module tiny_sysctrl #( output logic [31:0] clock_timeout_o, input logic periph_done_i, input logic [ 4:0] periph_fault_i, - input logic [14:0] gate_actual_i, - input logic [14:0] reset_done_i, + input logic [16:0] gate_actual_i, + input logic [16:0] reset_done_i, output logic periph_apply_o, output logic periph_reset_o, - output logic [14:0] periph_request_o, + output logic [16:0] periph_request_o, output logic [31:0] clkout_control_o, input logic clkout_idle_i, output logic perf_enable_o, @@ -106,7 +106,7 @@ module tiny_sysctrl #( logic [1:0] s_clk_latched_d, s_clk_latched_q; logic s_system_reset_d, s_system_reset_q; logic s_periph_apply_d, s_periph_apply_q, s_periph_reset_d, s_periph_reset_q; - logic [14:0] s_periph_mask_d, s_periph_mask_q; + logic [16:0] s_periph_mask_d, s_periph_mask_q; logic [3:0] s_active_cmd_d, s_active_cmd_q; logic s_clock_apply_d, s_clock_apply_q; logic s_rcu_busy_d, s_rcu_busy_q; @@ -147,15 +147,19 @@ module tiny_sysctrl #( assign u_rcu_src.psel = apb4.psel && s_rcu_selected; assign u_rcu_src.penable = apb4.penable; tiny_apb_async_bridge u_rcu_cdc ( - .src_clk_i (clk_i), - .src_rst_n_i(rst_n_i), - .dst_clk_i (clk_ref_i), - .dst_rst_n_i(rst_ref_n_i), - .disabled_i (1'b0), - .idle_o (), - .ready_o (rcu_link_ready_o), - .src_apb4 (u_rcu_src), - .dst_apb4 (u_rcu_dst) + .src_clk_i (clk_i), + .src_rst_n_i (rst_n_i), + .dst_clk_i (clk_ref_i), + .dst_rst_n_i (rst_ref_n_i), + .disabled_i (1'b0), + .src_origin_i (2'd0), + .src_channel_i(3'd0), + .dst_origin_o (), + .dst_channel_o(), + .idle_o (), + .ready_o (rcu_link_ready_o), + .src_apb4 (u_rcu_src), + .dst_apb4 (u_rcu_dst) ); assign s_rcu_offset = {20'd0, u_rcu_dst.paddr[11:0]}; assign s_rcu_req = u_rcu_dst.psel && u_rcu_dst.penable && !s_rcu_ready_q; @@ -306,7 +310,7 @@ module tiny_sysctrl #( s_rcu_read_data = s_gate_req_q; s_rcu_writable = 1'b1; end - GateStatus: s_rcu_read_data = {17'd0, gate_actual_i}; + GateStatus: s_rcu_read_data = {15'd0, gate_actual_i}; ResetRequest: begin s_rcu_read_data = s_rst_req_q; s_rcu_writable = 1'b1; @@ -334,7 +338,7 @@ module tiny_sysctrl #( MemHz: s_rcu_read_data = s_mem_hz_q; PclkHz: s_rcu_read_data = s_pclk_hz_q; ClintHz: s_rcu_read_data = 32'd1_000_000; - TargetCapability: s_rcu_read_data = {17'd0, TargetMask}; + TargetCapability: s_rcu_read_data = {15'd0, TargetMask}; ClkoutControl: begin s_rcu_read_data = s_clkout_control_q; s_rcu_writable = 1'b1; @@ -350,9 +354,9 @@ module tiny_sysctrl #( ClockRequest: s_rcu_config_err = s_rcu_busy_q || u_rcu_dst.pwdata[31:2] != 0; TimeoutRefCycles: s_rcu_config_err = s_rcu_busy_q || u_rcu_dst.pwdata == 0; GateRequest: - s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{17'd0, TargetMask}) != 0; + s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{15'd0, TargetMask}) != 0; ResetRequest: - s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{17'd0, TargetMask[14:1], 1'b0}) != 0; + s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{15'd0, TargetMask[16:1], 1'b0}) != 0; ResetCause: s_rcu_config_err = u_rcu_dst.pwdata[31:6] != 0; RcuFault: s_rcu_config_err = u_rcu_dst.pwdata[31:5] != 0; RcuIrqState, RcuIrqEnable: s_rcu_config_err = u_rcu_dst.pwdata[31:2] != 0; @@ -363,11 +367,11 @@ module tiny_sysctrl #( s_rcu_config_err |= s_clkout_control_q[2:0] != 0 || !clkout_idle_i || !(s_clk_req_q == 0 || (s_clk_req_q == 1 && !PllPresent) || (s_clk_req_q >= 2 && PllPresent && PllProfiles)); - 32'd2: s_rcu_config_err |= TargetMask == 0 || (s_gate_req_q & ~{17'd0, TargetMask}) != 0; + 32'd2: s_rcu_config_err |= TargetMask == 0 || (s_gate_req_q & ~{15'd0, TargetMask}) != 0; 32'd4: s_rcu_config_err |= s_rst_req_q == 0 || - (s_rst_req_q & ~{17'd0, TargetMask[14:1], 1'b0}) != 0 || - (s_rst_req_q & {17'd0, gate_actual_i}) != 0; + (s_rst_req_q & ~{15'd0, TargetMask[16:1], 1'b0}) != 0 || + (s_rst_req_q & {15'd0, gate_actual_i}) != 0; 32'd8: begin end default: s_rcu_config_err = 1'b1; @@ -432,16 +436,16 @@ module tiny_sysctrl #( s_rcu_busy_d = 1'b1; s_active_cmd_d = 4'd8; end else if (TargetMask != 0 && u_rcu_dst.pwdata == 2 && - (s_gate_req_q & ~{17'd0, TargetMask}) == 0) begin - s_periph_mask_d = s_gate_req_q[14:0]; + (s_gate_req_q & ~{15'd0, TargetMask}) == 0) begin + s_periph_mask_d = s_gate_req_q[16:0]; s_periph_reset_d = 1'b0; s_periph_apply_d = 1'b1; s_rcu_busy_d = 1'b1; s_active_cmd_d = 4'd2; end else if (u_rcu_dst.pwdata == 4 && s_rst_req_q != 0 && - (s_rst_req_q & ~{17'd0, TargetMask[14:1], 1'b0}) == 0 && - (s_rst_req_q & {17'd0, gate_actual_i}) == 0) begin - s_periph_mask_d = s_rst_req_q[14:0]; + (s_rst_req_q & ~{15'd0, TargetMask[16:1], 1'b0}) == 0 && + (s_rst_req_q & {15'd0, gate_actual_i}) == 0) begin + s_periph_mask_d = s_rst_req_q[16:0]; s_periph_reset_d = 1'b1; s_periph_apply_d = 1'b1; s_reset_done_d = 0; @@ -501,7 +505,7 @@ module tiny_sysctrl #( if (periph_done_i) begin s_rcu_busy_d = 1'b0; s_active_cmd_d = 0; - s_reset_done_d = {17'd0, reset_done_i}; + s_reset_done_d = {15'd0, reset_done_i}; if (s_periph_reset_q && reset_done_i != 0) s_reset_cause_d[5] = 1'b1; s_rcu_irq_state_d[0] = 1'b1; end @@ -607,7 +611,7 @@ module tiny_sysctrl #( .dat_o (s_clk_latched_q) ); dffr #( - .DATA_WIDTH(21) + .DATA_WIDTH(23) ) u_periph_command ( .clk_i (clk_ref_i), .rst_n_i(rst_ref_n_i), diff --git a/scripts/check_clock_reset_domains.py b/scripts/check_clock_reset_domains.py index b5f0fe2c..436899aa 100644 --- a/scripts/check_clock_reset_domains.py +++ b/scripts/check_clock_reset_domains.py @@ -140,7 +140,8 @@ def validate(document_path: Path, root: Path) -> None: raise ValueError("unrecognized generated APB crossing contract") expected = {"aon": ["clint", "archinfo"], "memory": ["xpi"], "peripheral": ["gpio", "uart0", "timer0", "timer1", "i2c0", "dma", - "pwm", "rtc", "wdg", "rng", "crc", "ws2812", "crypto", "i2s", "dvp"]} + "pwm", "rtc", "wdg", "rng", "crc", "ws2812", "crypto", "i2s", "dvp", + "spi"]} if (generated.get("destinations") != expected or generated.get("same_clock_isolation") != ["sdio"] or generated.get("native_system") != ["sysctrl", "sram"] or diff --git a/scripts/generate_tiny.py b/scripts/generate_tiny.py index 353b6fc4..4def3d80 100644 --- a/scripts/generate_tiny.py +++ b/scripts/generate_tiny.py @@ -18,15 +18,15 @@ from scripts.setup_helpers import atomic_write # noqa: E402 TINY = ROOT / "rtl/tiny" RCU_TARGETS = {"gpio": 1, "uart0": 2, "i2c0": 3, "timer0": 4, "timer1": 5, "pwm": 6, "i2s": 7, "sdio": 8, "dma": 9, "ws2812": 10, - "rng": 11, "crc": 12, "crypto": 13, "dvp": 14} + "rng": 11, "crc": 12, "crypto": 13, "dvp": 14, "spi": 16} def read_topology(path: Path, memory_map: Path) -> dict: document = json.loads(path.read_text()) regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]} if (document.get("schema_version") != 1 or document.get("dma_channels") != 8 - or document.get("dma_request_mask") != "0x39ff"): - raise ValueError("Tiny P9 requires eight DMA channels and request mask 0x39ff") + or document.get("dma_request_mask") != "0x339ff"): + raise ValueError("Tiny SPI-P3 requires eight DMA channels and request mask 0x339ff") if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2, "data_outstanding": 1}: raise ValueError("Tiny P4 requires independent I/D and one ordered data operation") @@ -43,9 +43,9 @@ def read_topology(path: Path, memory_map: Path) -> dict: "cpu_combined_outstanding": 1, "dma_direction_outstanding": 1, "sdio_direction_outstanding": 1, "target_combined_outstanding": 1, "sram_targets": [0, 1, 2, 3], "xpi_target": 4, "apb_target_base": 5, - "decode_error_target": 26, "protocol_error_target": 27, "target_count": 28, + "decode_error_target": 27, "protocol_error_target": 28, "target_count": 29, }: - raise ValueError("Tiny P9 requires three active owners and 28 targets") + raise ValueError("Tiny SPI-P3 requires three active owners and 29 targets") if document.get("irq_vector_width") != 32 or document.get("external_irq_count") != 30: raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs") names: set[str] = set() @@ -65,10 +65,10 @@ def read_topology(path: Path, memory_map: Path) -> dict: assigned.add(region) if assigned != {r for r in regions if r.startswith("APB4_")}: raise ValueError("Tiny topology must route every APB region") - if len(document["apb_targets"]) != 21: - raise ValueError("Tiny P9 requires twenty-one independent APB targets") + if len(document["apb_targets"]) != 22: + raise ValueError("Tiny SPI-P3 requires twenty-two independent APB targets") target_names = [target["name"] for target in document["apb_targets"]] - if len(set(target_names)) != 21 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None + if len(set(target_names)) != 22 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None for name in target_names): raise ValueError("Tiny APB target names must be unique identifiers") if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)): @@ -149,6 +149,8 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130", "#define RS_SOC_HAS_I2S 1U\n#define RS_SOC_HAS_SDIO0 1U\n" "#define RS_SOC_HAS_DVP 1U\n#define RS_SOC_DVP_DMA_CHANNEL 2U\n" "#define RS_SOC_DVP_DMA_BURST_BEATS 4U\n#define RS_SOC_RNG_QUALIFIED 0U\n" + "#define RS_SOC_HAS_SPI 1U\n#define RS_SOC_SPI_DMA_TX 16U\n" + "#define RS_SOC_SPI_DMA_RX 17U\n" f"#define RS_SOC_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n" f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}U\n" f"#define RS_SOC_GPIO_HAS_PULLS {platform['gpio_pulls']}U\n#endif\n") @@ -176,6 +178,10 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130", if target_bit is not None: declarations.append(f"logic {ip_clock};") declarations.append(f"logic s_{name}_rst_n;") + if name == "spi": + # Only SPI consumes the origin/channel qualifier; other crossings tie off below. + declarations.append(f"logic [1:0] s_{name}_origin_sys;") + declarations.append(f"logic [2:0] s_{name}_channel_sys;") declarations.append(f"apb4_if u_{name}_apb4_if (.pclk({ip_clock}), .presetn(s_{name}_rst_n));") if target_bit is None: reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];") @@ -201,16 +207,23 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130", bridges.append(f"assign s_target_rst_n[{index + 5}] = s_fabric_rst_n;") crossed = clock != "s_exec_clk" or target_bit is not None endpoint = f"u_{name}_sys_apb4_if" if crossed else f"u_{name}_apb4_if" + qualifier = (f".origin_o(s_{name}_origin_sys), .channel_o(s_{name}_channel_sys)" + if name == "spi" else ".origin_o(), .channel_o()") bridges.append(f"tiny_axi42apb4 u_{name}_bridge (" f".clk_i(s_exec_clk), .rst_n_i(s_fabric_rst_n), " - f".axi4(u_targets_axi4_if[{index + 5}]), .apb4({endpoint}));") + f".axi4(u_targets_axi4_if[{index + 5}]), .apb4({endpoint}), {qualifier});") if crossed: disabled = f"s_disabled_sys[{target_bit}]" if target_bit is not None else "1'b0" + source = (f".src_origin_i(s_{name}_origin_sys), .src_channel_i(s_{name}_channel_sys)" + if name == "spi" else ".src_origin_i(2'd0), .src_channel_i(3'd0)") declarations.append(f"apb4_if {endpoint} (.pclk(s_exec_clk), .presetn(s_fabric_rst_n));") bridges.append(f"tiny_apb_async_bridge u_{name}_cfg_cdc (" f".src_clk_i(s_exec_clk), .src_rst_n_i(s_fabric_rst_n), " f".dst_clk_i({ip_clock}), .dst_rst_n_i(s_{name}_rst_n), " f".disabled_i({disabled}), " + f"{source}, " + f".dst_origin_o({ 's_spi_origin' if name == 'spi' else ''}), " + f".dst_channel_o({ 's_spi_channel' if name == 'spi' else ''}), " f".idle_o(s_apb_cdc_idle[{index}]), " f".ready_o(s_apb_cdc_ready[{index}]), " f".src_apb4({endpoint}), .dst_apb4(u_{name}_apb4_if));") diff --git a/scripts/run_c_tests.py b/scripts/run_c_tests.py index db3a7331..4df062c3 100644 --- a/scripts/run_c_tests.py +++ b/scripts/run_c_tests.py @@ -33,6 +33,7 @@ TEST_SOURCES = ( "crt/src/hal/usb2_math.c", "crt/src/hal/spisd_math.c", "crt/src/hal/i2s_math.c", + "crt/src/hal/spi_math.c", "crt/src/hal/dma_math.c", "crt/src/hal/dma.c", "crt/src/hal/dvp.c", diff --git a/scripts/spi_p3.py b/scripts/spi_p3.py new file mode 100644 index 00000000..9b3ffbea --- /dev/null +++ b/scripts/spi_p3.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Source-bound focused SPI-P3 Tiny integration checks.""" +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from scripts.setup_helpers import atomic_write # noqa: E402 + + +def configuration_digest(manifest: dict) -> str: + payload = json.dumps(manifest["configuration"], sort_keys=True).encode("utf-8") + return hashlib.sha256(payload).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("stage", choices=("lint", "rtl", "report")) + parser.add_argument("--variant-root", type=Path, required=True) + parser.add_argument("--pdk", choices=("ICS55", "IHP130"), required=True) + args = parser.parse_args() + variant = args.variant_root.resolve() + variant.relative_to(ROOT / "build") + manifest = json.loads((variant / "meta/manifest.json").read_text()) + if manifest["configuration"]["SOC"] != "TINY" or manifest["configuration"]["PDK"] != args.pdk: + raise ValueError("profile/PDK mismatch") + output = variant / "meta/spi-p3" / args.stage + output.mkdir(parents=True, exist_ok=False) + command = [sys.executable, "-m", "pytest", "-q", "tests/test_spi_p3.py", + "tests/test_spi_p2.py", "tests/test_spi_register_parity.py"] + result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, check=False) + atomic_write(output / "pytest.log", result.stdout + result.stderr) + record = {"phase": "SPI-P3", "stage": args.stage, + "status": "passed" if result.returncode == 0 else "failed", + "command": command, "configuration_digest": configuration_digest(manifest), + "phase_accepted": False} + atomic_write(output / "record.json", json.dumps(record, indent=2, sort_keys=True) + "\n") + if result.returncode != 0: + print(result.stdout + result.stderr, file=sys.stderr) + return result.returncode + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/c/test_runtime.c b/tests/c/test_runtime.c index 4052a18c..7854694f 100644 --- a/tests/c/test_runtime.c +++ b/tests/c/test_runtime.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1178,6 +1179,128 @@ static int test_tiny_dma_config_validation(void) { return 0; } +static int test_spi_divider_from_hz(void) { + uint32_t divider; + uint32_t actual_hz; + + /* Ceiling rounding keeps the actual SCK at or below the request. */ + if ((rs_spi_divider_from_hz(24000000U, 1000000U, ÷r, &actual_hz) != RS_OK) || + (divider != 12U) || (actual_hz != 1000000U)) { + return 1; + } + if ((rs_spi_divider_from_hz(24000000U, 1100000U, ÷r, &actual_hz) != RS_OK) || + (divider != 11U) || (actual_hz != 1090909U)) { + return 2; + } + /* Requests at or above half PCLK saturate at divider 1. */ + if ((rs_spi_divider_from_hz(24000000U, 12000000U, ÷r, &actual_hz) != RS_OK) || + (divider != 1U) || (actual_hz != 12000000U)) { + return 3; + } + if ((rs_spi_divider_from_hz(24000000U, 13000000U, ÷r, &actual_hz) != RS_OK) || + (divider != 1U) || (actual_hz != 12000000U)) { + return 4; + } + if ((rs_spi_divider_from_hz(131070U, 1U, ÷r, &actual_hz) != RS_OK) || + (divider != 65535U) || (actual_hz != 1U)) { + return 5; + } + /* Divider 65536 cannot be represented. */ + if (rs_spi_divider_from_hz(131072U, 1U, ÷r, &actual_hz) != RS_ENOTSUP) { + return 6; + } + if ((rs_spi_divider_from_hz(0U, 1000000U, ÷r, &actual_hz) != RS_EINVAL) || + (rs_spi_divider_from_hz(24000000U, 0U, ÷r, &actual_hz) != RS_EINVAL) || + (rs_spi_divider_from_hz(24000000U, 1000000U, NULL, &actual_hz) != RS_EINVAL) || + (rs_spi_divider_from_hz(24000000U, 1000000U, ÷r, NULL) != RS_EINVAL)) { + return 7; + } + return 0; +} + +static int test_tiny_dma_spi_aperture(void) { + rs_dma_config_t config = { + .kind = RS_DMA_KIND_MM_TO_MM, + .request = RS_DMA_REQUEST_SPI_TX, + .source = (uintptr_t)UINT32_C(0x30000000), + .destination = (uintptr_t)UINT32_C(0x1001D054), + .byte_count = 64U, + .width = RS_DMA_WIDTH_32, + .source_increment = true, + .destination_increment = false, + .burst_beats = 16U, + }; + static _Alignas(64) rs_dma_tcd_t descriptor; + if (rs_tiny_dma_config_validate(3U, &config) != RS_OK) { + return 1; + } + config.request = RS_DMA_REQUEST_SPI_RX; + config.source = (uintptr_t)UINT32_C(0x1001D058); + config.destination = (uintptr_t)UINT32_C(0x30001000); + config.source_increment = false; + config.destination_increment = true; + if (rs_tiny_dma_config_validate(3U, &config) != RS_OK) { + return 2; + } + /* Wrong fixed-FIFO address, increment direction, count, or kind fails. */ + config.request = RS_DMA_REQUEST_SPI_TX; + config.source = (uintptr_t)UINT32_C(0x30000000); + config.destination = (uintptr_t)UINT32_C(0x1001D050); + config.source_increment = true; + config.destination_increment = false; + if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) { + return 3; + } + config.destination = (uintptr_t)UINT32_C(0x1001D054); + config.destination_increment = true; + if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) { + return 4; + } + config.destination_increment = false; + config.byte_count = 6U; + if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) { + return 5; + } + config.byte_count = 64U; + config.kind = RS_DMA_KIND_MM_TO_STREAM; + if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) { + return 6; + } + /* The default Mini selection has no SPI request route at all. */ + config.kind = RS_DMA_KIND_MM_TO_MM; + if (rs_dma_config_validate(3U, &config) != RS_EINVAL) { + return 7; + } + descriptor.source = UINT32_C(0x30000000); + descriptor.destination = UINT32_C(0x1001D054); + descriptor.byte_count = 64U; + descriptor.y_count = 1U; + /* Request 16 needs the high bit; the low nibble field stays zero. */ + descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_REQUEST_HIGH | + ((uint32_t)RS_DMA_KIND_MM_TO_MM << RS_DMA_TCD_KIND_SHIFT); + if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_OK) { + return 8; + } + descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_DST_INC | RS_DMA_TCD_REQUEST_HIGH | + (UINT32_C(1) << RS_DMA_TCD_REQUEST_SHIFT) | + ((uint32_t)RS_DMA_KIND_MM_TO_MM << RS_DMA_TCD_KIND_SHIFT); + descriptor.source = UINT32_C(0x1001D058); + descriptor.destination = UINT32_C(0x30001000); + if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_OK) { + return 9; + } + descriptor.source = UINT32_C(0x1001D054); + if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_EINVAL) { + return 10; + } + descriptor.source = UINT32_C(0x1001D058); + descriptor.control |= RS_DMA_TCD_SRC_INC; + if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_EINVAL) { + return 11; + } + return 0; +} + rs_status_t rs_tiny_dvp_configure(const rs_dvp_config_t *config); rs_status_t rs_tiny_dvp_capture_dma(uintptr_t destination, uint32_t capacity, rs_timeout_t timeout); rs_status_t rs_tiny_dvp_capture_pio(uintptr_t destination, uint32_t capacity, uint32_t expected, @@ -3525,6 +3648,8 @@ int main(void) { test_gpio_helpers(), test_dma_config_validation(), test_tiny_dma_config_validation(), + test_spi_divider_from_hz(), + test_tiny_dma_spi_aperture(), test_dvp_hal(), test_p9_frame_layout(), test_p10_frame_statistics(), diff --git a/tests/rtl/dma_origin_tb.sv b/tests/rtl/dma_origin_tb.sv new file mode 100644 index 00000000..f2f1697d --- /dev/null +++ b/tests/rtl/dma_origin_tb.sv @@ -0,0 +1,334 @@ +`timescale 1ns / 1ps + +module dma_origin_tb; + localparam int NumChannels = 4; + localparam logic [31:0] SourceBase = 32'h4000_0000; + localparam logic [31:0] DestinationBase = 32'h5000_0000; + + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic [NumChannels*32-1:0] ch_cfg_i = '0; + logic [NumChannels*32-1:0] src_addr_i = '0; + logic [NumChannels*32-1:0] dst_addr_i = '0; + logic [NumChannels*32-1:0] byte_count_i = '0; + logic [NumChannels*32-1:0] request_sel_i = '0; + logic [NumChannels*32-1:0] burst_cfg_i = '0; + logic [NumChannels*32-1:0] tcd_head_i = '0; + logic [NumChannels*32-1:0] tcd_count_i = '0; + logic [NumChannels*32-1:0] crc_expected_i = '0; + logic [ NumChannels-1:0] start_i = '0; + logic [ NumChannels-1:0] suspend_i = '0; + logic [ NumChannels-1:0] resume_i = '0; + logic [ NumChannels-1:0] abort_i = '0; + logic [ NumChannels-1:0] channel_reset_i = '0; + logic [ NumChannels*3-1:0] event_clear_i = '0; + logic [ NumChannels-1:0] busy_o; + logic [ NumChannels-1:0] suspended_o; + logic [ NumChannels-1:0] done_o; + logic [ NumChannels-1:0] aborted_o; + logic [ NumChannels-1:0] error_o; + logic [ NumChannels-1:0] stream_last_o; + logic [ NumChannels*3-1:0] event_status_o; + logic [NumChannels*32-1:0] error_status_o; + logic [NumChannels*32-1:0] error_addr_o; + logic [NumChannels*32-1:0] current_src_o; + logic [NumChannels*32-1:0] current_dst_o; + logic [NumChannels*32-1:0] remaining_o; + logic [NumChannels*32-1:0] bytes_done_o; + logic [NumChannels*32-1:0] stall_cycles_lo_o; + logic [NumChannels*32-1:0] stall_cycles_hi_o; + logic [NumChannels*32-1:0] crc_result_o; + logic first_error_valid_o; + logic [ 1:0] first_error_channel_o; + logic [ 8:0] first_error_status_o; + logic [ 15:0] first_error_addr_hi_o; + logic [ 15:0] request_status_o; + logic xpi_xfer_done_o; + + integer ar_mismatch = 0; + integer aw_mismatch = 0; + integer w_mismatch = 0; + integer ar_checked = 0; + integer aw_checked = 0; + integer w_checked = 0; + logic [ 2:0] write_channel_q = '0; + logic write_active_q = 1'b0; + logic read_active_q = 1'b0; + logic [ 4:0] read_beats_q = '0; + logic [ 4:0] read_index_q = '0; + logic [ 31:0] read_addr_q = '0; + logic [ 4:0] write_beats_q = '0; + logic [ 4:0] write_index_q = '0; + logic write_response_q = 1'b0; + + dma_req_if req (); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(4) + ) axi4 ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if i2s_tx_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if i2s_rx_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if dvp_rx_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if crypto_in_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if crypto_out_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + + always #5 clk_i = ~clk_i; + + assign axi4.arready = !read_active_q; + assign axi4.rvalid = read_active_q; + assign axi4.rid = '0; + assign axi4.rdata = 32'hA500_0000 + ((read_addr_q - SourceBase) >> 2) + read_index_q; + assign axi4.rresp = `AXI4_RESP_OKAY; + assign axi4.rlast = (read_index_q + 1'b1) == read_beats_q; + assign axi4.ruser = '0; + + assign axi4.awready = !write_active_q && !write_response_q; + assign axi4.wready = write_active_q; + assign axi4.bid = '0; + assign axi4.bresp = `AXI4_RESP_OKAY; + assign axi4.buser = '0; + assign axi4.bvalid = write_response_q; + + assign i2s_tx_axis.tready = 1'b0; + assign i2s_rx_axis.tdata = '0; + assign i2s_rx_axis.tkeep = '0; + assign i2s_rx_axis.tstrb = '0; + assign i2s_rx_axis.tlast = 1'b0; + assign i2s_rx_axis.tid = '0; + assign i2s_rx_axis.tdest = '0; + assign i2s_rx_axis.tuser = '0; + assign i2s_rx_axis.tvalid = 1'b0; + assign dvp_rx_axis.tdata = '0; + assign dvp_rx_axis.tkeep = '0; + assign dvp_rx_axis.tstrb = '0; + assign dvp_rx_axis.tlast = 1'b0; + assign dvp_rx_axis.tid = '0; + assign dvp_rx_axis.tdest = '0; + assign dvp_rx_axis.tuser = '0; + assign dvp_rx_axis.tvalid = 1'b0; + assign crypto_in_axis.tready = 1'b0; + assign crypto_out_axis.tdata = '0; + assign crypto_out_axis.tkeep = '0; + assign crypto_out_axis.tstrb = '0; + assign crypto_out_axis.tlast = 1'b0; + assign crypto_out_axis.tid = '0; + assign crypto_out_axis.tdest = '0; + assign crypto_out_axis.tuser = '0; + assign crypto_out_axis.tvalid = 1'b0; + + 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_channel_q <= '0; + write_beats_q <= '0; + write_index_q <= '0; + write_response_q <= 1'b0; + end else begin + if (axi4.arvalid && axi4.arready) begin + read_active_q <= 1'b1; + read_addr_q <= axi4.araddr; + read_beats_q <= {1'b0, axi4.arlen} + 5'd1; + read_index_q <= '0; + end else if (axi4.rvalid && axi4.rready && axi4.rlast) begin + read_active_q <= 1'b0; + end else if (axi4.rvalid && axi4.rready) begin + read_index_q <= read_index_q + 5'd1; + end + if (axi4.awvalid && axi4.awready) begin + write_active_q <= 1'b1; + write_channel_q <= axi4.awuser[2:0]; + write_beats_q <= {1'b0, axi4.awlen} + 5'd1; + write_index_q <= '0; + write_response_q <= 1'b0; + end else if (axi4.wvalid && axi4.wready && axi4.wlast) begin + write_active_q <= 1'b0; + write_response_q <= 1'b1; + end else if (axi4.wvalid && axi4.wready) begin + write_index_q <= write_index_q + 5'd1; + end else if (axi4.bvalid && axi4.bready) begin + write_response_q <= 1'b0; + end + end + end + + always_ff @(posedge clk_i) begin + if (rst_n_i) begin + if (axi4.arvalid && axi4.arready) begin + ar_checked <= ar_checked + 1; + if (axi4.aruser !== {1'b1, 3'((axi4.araddr - SourceBase) >> 12)}) begin + ar_mismatch <= ar_mismatch + 1; + end + end + if (axi4.awvalid && axi4.awready) begin + aw_checked <= aw_checked + 1; + if (axi4.awuser !== {1'b1, 3'((axi4.awaddr - DestinationBase) >> 12)}) begin + aw_mismatch <= aw_mismatch + 1; + end + end + if (axi4.wvalid && axi4.wready) begin + w_checked <= w_checked + 1; + if (axi4.wuser !== {1'b1, write_channel_q}) begin + w_mismatch <= w_mismatch + 1; + end + end + end + end + + dma_core #( + .NumChannels (NumChannels), + .MaxBurstBeats (16), + .FifoDepth (16), + .EnableOriginQualifier(1'b1) + ) u_dma_core ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .global_reset_i (1'b0), + .global_error_clear_i (1'b0), + .ch_cfg_i (ch_cfg_i), + .tcd_head_i (tcd_head_i), + .tcd_count_i (tcd_count_i), + .crc_expected_i (crc_expected_i), + .src_addr_i (src_addr_i), + .dst_addr_i (dst_addr_i), + .byte_count_i (byte_count_i), + .request_sel_i (request_sel_i), + .burst_cfg_i (burst_cfg_i), + .start_i (start_i), + .suspend_i (suspend_i), + .resume_i (resume_i), + .abort_i (abort_i), + .channel_reset_i (channel_reset_i), + .event_clear_i (event_clear_i), + .busy_o (busy_o), + .suspended_o (suspended_o), + .done_o (done_o), + .aborted_o (aborted_o), + .error_o (error_o), + .stream_last_o (stream_last_o), + .event_status_o (event_status_o), + .error_status_o (error_status_o), + .error_addr_o (error_addr_o), + .current_src_o (current_src_o), + .current_dst_o (current_dst_o), + .remaining_o (remaining_o), + .bytes_done_o (bytes_done_o), + .stall_cycles_lo_o (stall_cycles_lo_o), + .stall_cycles_hi_o (stall_cycles_hi_o), + .crc_result_o (crc_result_o), + .first_error_valid_o (first_error_valid_o), + .first_error_channel_o(first_error_channel_o), + .first_error_status_o (first_error_status_o), + .first_error_addr_hi_o(first_error_addr_hi_o), + .request_status_o (request_status_o), + .xpi_xfer_done_o (xpi_xfer_done_o), + .req (req), + .axi4 (axi4), + .i2s_tx_axis (i2s_tx_axis), + .i2s_rx_axis (i2s_rx_axis), + .dvp_rx_axis (dvp_rx_axis), + .crypto_in_axis (crypto_in_axis), + .crypto_out_axis (crypto_out_axis) + ); + + task automatic configure_channel(input integer channel, input logic [31:0] byte_count); + begin + ch_cfg_i[(channel*32)+:32] = {22'd0, 2'd1, 1'b1, 1'b1, 2'd2, 1'b0, 3'd0}; + src_addr_i[(channel*32)+:32] = SourceBase + (32'(channel) << 12); + dst_addr_i[(channel*32)+:32] = DestinationBase + (32'(channel) << 12); + byte_count_i[(channel*32)+:32] = byte_count; + request_sel_i[(channel*32)+:32] = 32'd0; + burst_cfg_i[(channel*32)+:32] = {27'd0, 5'd1}; + end + endtask + + task automatic wait_done(input integer channel); + integer timeout; + begin + timeout = 0; + while (!done_o[channel] && !error_o[channel] && (timeout < 100000)) begin + @(posedge clk_i); + timeout = timeout + 1; + end + if (timeout >= 100000) $fatal(1, "channel %0d never completed", channel); + end + endtask + + initial begin + req.i2s_tx_proc = 1'b1; + req.i2s_rx_proc = 1'b1; + req.qspi_tx_proc = 1'b1; + req.qspi_rx_proc = 1'b1; + req.uart_tx_proc = 1'b1; + req.uart_rx_proc = 1'b1; + req.i2c0_tx_proc = 1'b1; + req.i2c0_rx_proc = 1'b1; + req.i2c1_tx_proc = 1'b1; + req.i2c1_rx_proc = 1'b1; + req.crypto_in_proc = 1'b1; + req.crypto_out_proc = 1'b1; + repeat (3) @(posedge clk_i); + rst_n_i = 1'b1; + repeat (4) @(posedge clk_i); + + // Idle arbiter: one single-beat job on nonzero channel 2. + configure_channel(2, 32'd4); + @(negedge clk_i); + start_i[2] = 1'b1; + @(negedge clk_i); + start_i[2] = 1'b0; + wait_done(2); + + // Contended arbiter: three channels rotate single-beat bursts. + configure_channel(1, 32'd64); + configure_channel(2, 32'd64); + configure_channel(3, 32'd64); + @(negedge clk_i); + start_i[1] = 1'b1; + start_i[2] = 1'b1; + start_i[3] = 1'b1; + @(negedge clk_i); + start_i = '0; + wait_done(1); + wait_done(2); + wait_done(3); + + if ((ar_checked == 0) || (aw_checked == 0) || (w_checked == 0)) begin + $fatal(1, "origin checks never ran"); + end + if ((ar_mismatch != 0) || (aw_mismatch != 0) || (w_mismatch != 0) || (error_o != '0)) begin + $fatal(1, "origin mismatch ar=%0d aw=%0d w=%0d", ar_mismatch, aw_mismatch, w_mismatch); + end + $display("DMA origin qualifier staging test passed ar=%0d aw=%0d w=%0d", ar_checked, + aw_checked, w_checked); + $finish; + end + + initial begin + repeat (1000000) @(posedge clk_i); + $fatal(1, "SIM_TEST_TIMEOUT DMA origin"); + end +endmodule diff --git a/tests/rtl/spi_test_body.svh b/tests/rtl/spi_test_body.svh index 4f104300..584ad13a 100644 --- a/tests/rtl/spi_test_body.svh +++ b/tests/rtl/spi_test_body.svh @@ -29,6 +29,7 @@ logic [3:0] pstrb_i = 0; logic psel_i = 0, penable_i = 0, pwrite_i = 0, pready_o, pslverr_o; logic model_begin = 0, model_enable = 0, model_abort = 0; logic model_segment = 0, model_pause = 0; +logic session_owned, lifecycle_idle; logic [15:0] model_divider = 1, model_setup = 1, model_hold = 1, model_inactive = 1; logic [8:0] model_faults; logic cpol = 0, cpha = 0, lsb = 0, frame16 = 0; @@ -67,6 +68,8 @@ realtime access_accept_time; .output_enable_o (output_enable_o), .irq_o (irq_o), .credit_commit_o (credit_commit_o), + .session_owned_o (session_owned), + .lifecycle_idle_o (lifecycle_idle), .paddr_i (paddr_i), .pwdata_i (pwdata_i), .pstrb_i (pstrb_i), diff --git a/tests/rtl/tiny_axi4_fabric_tb.sv b/tests/rtl/tiny_axi4_fabric_tb.sv index 85c499b4..bfe7f818 100644 --- a/tests/rtl/tiny_axi4_fabric_tb.sv +++ b/tests/rtl/tiny_axi4_fabric_tb.sv @@ -9,7 +9,7 @@ module tiny_axi4_fabric_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) masters[3] ( .aclk (clk_i), .aresetn(rst_n_i) @@ -18,7 +18,7 @@ module tiny_axi4_fabric_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) targets[`RETROSOC_TINY__TARGET_COUNT] ( .aclk (clk_i), .aresetn(rst_n_i) diff --git a/tests/rtl/tiny_p7_source_reset_tb.sv b/tests/rtl/tiny_p7_source_reset_tb.sv index 18969d98..574c567e 100644 --- a/tests/rtl/tiny_p7_source_reset_tb.sv +++ b/tests/rtl/tiny_p7_source_reset_tb.sv @@ -80,37 +80,40 @@ module tiny_p7_source_reset_tb; logic [1:0] rd_resp, wr_resp; logic rd_id_error, wr_id_error; dma_axi4_master u_dma_master ( - .clk_i (s_dma_clk), - .rst_n_i (s_dma_rst_n), - .clear_i (1'b0), - .read_start_valid_i (rd_start), - .read_start_ready_o (rd_start_ready), - .read_addr_i (rd_addr), - .read_beats_i (rd_beats), - .read_fixed_i (1'b0), - .read_busy_o (rd_busy), - .read_beat_valid_o (rd_valid), - .read_beat_ready_i (rd_ready), - .read_data_o (rd_data), - .read_resp_o (rd_resp), - .read_last_o (rd_last), - .read_expected_last_o(), - .read_id_error_o (rd_id_error), - .read_done_o (rd_done), - .write_start_valid_i (wr_start), - .write_start_ready_o (wr_start_ready), - .write_addr_i (wr_addr), - .write_beats_i (wr_beats), - .write_fixed_i (1'b0), - .write_busy_o (wr_busy), - .write_data_valid_i (wr_valid), - .write_data_ready_o (wr_ready), - .write_data_i (wr_data), - .write_strb_i (4'hf), - .write_done_o (wr_done), - .write_resp_o (wr_resp), - .write_id_error_o (wr_id_error), - .axi4 (u_dma_pclk_axi4_if) + .clk_i (s_dma_clk), + .rst_n_i (s_dma_rst_n), + .clear_i (1'b0), + .read_start_valid_i (rd_start), + .read_start_ready_o (rd_start_ready), + .read_addr_i (rd_addr), + .read_beats_i (rd_beats), + .read_fixed_i (1'b0), + .read_busy_o (rd_busy), + .read_beat_valid_o (rd_valid), + .read_beat_ready_i (rd_ready), + .read_data_o (rd_data), + .read_resp_o (rd_resp), + .read_last_o (rd_last), + .read_expected_last_o (), + .read_id_error_o (rd_id_error), + .read_done_o (rd_done), + .write_start_valid_i (wr_start), + .write_start_ready_o (wr_start_ready), + .write_addr_i (wr_addr), + .write_beats_i (wr_beats), + .write_fixed_i (1'b0), + .write_busy_o (wr_busy), + .write_data_valid_i (wr_valid), + .write_data_ready_o (wr_ready), + .write_data_i (wr_data), + .write_strb_i (4'hf), + .write_done_o (wr_done), + .write_resp_o (wr_resp), + .write_id_error_o (wr_id_error), + .origin_enable_i (1'b0), + .read_origin_channel_i (3'd0), + .write_origin_channel_i(3'd0), + .axi4 (u_dma_pclk_axi4_if) ); assign s_periph_idle = {4'hf, !(rd_busy || wr_busy || rd_start || wr_start), 9'h1ff}; diff --git a/tests/rtl/tiny_r2_p5_fabric_formal.sv b/tests/rtl/tiny_r2_p5_fabric_formal.sv index 998294ab..540a1f02 100644 --- a/tests/rtl/tiny_r2_p5_fabric_formal.sv +++ b/tests/rtl/tiny_r2_p5_fabric_formal.sv @@ -16,7 +16,7 @@ module tiny_r2_p5_fabric_formal ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) masters[3] ( .aclk (clk_i), .aresetn(rst_n_i) @@ -25,7 +25,7 @@ module tiny_r2_p5_fabric_formal ( .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) target ( .aclk (clk_i), .aresetn(rst_n_i) diff --git a/tests/rtl/tiny_r2_p5_fabric_tb.sv b/tests/rtl/tiny_r2_p5_fabric_tb.sv index f8ac5184..dca4f2a3 100644 --- a/tests/rtl/tiny_r2_p5_fabric_tb.sv +++ b/tests/rtl/tiny_r2_p5_fabric_tb.sv @@ -9,7 +9,7 @@ module tiny_r2_p5_fabric_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) masters[3] ( .aclk (clk_i), .aresetn(rst_n_i) @@ -18,7 +18,7 @@ module tiny_r2_p5_fabric_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) targets[`RETROSOC_TINY__TARGET_COUNT] ( .aclk (clk_i), .aresetn(rst_n_i) diff --git a/tests/rtl/tiny_r2_p5_reset_tb.sv b/tests/rtl/tiny_r2_p5_reset_tb.sv index 5290e2ff..a255cbef 100644 --- a/tests/rtl/tiny_r2_p5_reset_tb.sv +++ b/tests/rtl/tiny_r2_p5_reset_tb.sv @@ -8,7 +8,7 @@ module tiny_r2_p5_reset_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) masters[3] ( .aclk (clk_i), .aresetn(rst_n_i) @@ -17,7 +17,7 @@ module tiny_r2_p5_reset_tb; .ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH (1), - .USER_WIDTH(1) + .USER_WIDTH(4) ) targets[`RETROSOC_TINY__TARGET_COUNT] ( .aclk (clk_i), .aresetn(target_rst_n) @@ -76,37 +76,40 @@ module tiny_r2_p5_reset_tb; // Production master: BREADY cannot rise until its last W handshake. dma_axi4_master u_dma ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clear_i (1'b0), - .read_start_valid_i (1'b0), - .read_start_ready_o (), - .read_addr_i (32'd0), - .read_beats_i (5'd1), - .read_fixed_i (1'b0), - .read_busy_o (), - .read_beat_valid_o (), - .read_beat_ready_i (1'b0), - .read_data_o (), - .read_resp_o (), - .read_last_o (), - .read_expected_last_o(), - .read_id_error_o (), - .read_done_o (), - .write_start_valid_i (dma_start), - .write_start_ready_o (dma_start_ready), - .write_addr_i (dma_addr), - .write_beats_i (dma_beats), - .write_fixed_i (1'b0), - .write_busy_o (dma_busy), - .write_data_valid_i (dma_valid), - .write_data_ready_o (dma_ready), - .write_data_i (dma_data), - .write_strb_i (4'hf), - .write_done_o (dma_done), - .write_resp_o (dma_resp), - .write_id_error_o (dma_id_error), - .axi4 (masters[1]) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clear_i (1'b0), + .read_start_valid_i (1'b0), + .read_start_ready_o (), + .read_addr_i (32'd0), + .read_beats_i (5'd1), + .read_fixed_i (1'b0), + .read_busy_o (), + .read_beat_valid_o (), + .read_beat_ready_i (1'b0), + .read_data_o (), + .read_resp_o (), + .read_last_o (), + .read_expected_last_o (), + .read_id_error_o (), + .read_done_o (), + .write_start_valid_i (dma_start), + .write_start_ready_o (dma_start_ready), + .write_addr_i (dma_addr), + .write_beats_i (dma_beats), + .write_fixed_i (1'b0), + .write_busy_o (dma_busy), + .write_data_valid_i (dma_valid), + .write_data_ready_o (dma_ready), + .write_data_i (dma_data), + .write_strb_i (4'hf), + .write_done_o (dma_done), + .write_resp_o (dma_resp), + .write_id_error_o (dma_id_error), + .origin_enable_i (1'b0), + .read_origin_channel_i (3'd0), + .write_origin_channel_i(3'd0), + .axi4 (masters[1]) ); for (genvar m = 0; m < 3; m++) begin : gen_master @@ -158,8 +161,8 @@ module tiny_r2_p5_reset_tb; // Independent upstream scoreboards also run in Icarus without SVA. integer rleft = 0, wleft = 0, bleft = 0; logic rheld = 0, bheld = 0; - logic [36:0] saved_r; - logic [ 3:0] saved_b; + logic [39:0] saved_r; + logic [ 6:0] saved_b; always @(posedge clk_i) begin if (!rst_n_i) begin rleft = 0; diff --git a/tests/test_dma.py b/tests/test_dma.py index 49db77df..80fd5e75 100644 --- a/tests/test_dma.py +++ b/tests/test_dma.py @@ -78,6 +78,73 @@ def test_dma_reports_native_axi4_errors_bursts_and_streams(tmp_path: Path) -> No assert "DMA native AXI4 burst, backpressure, stream, and error test passed" in result.stdout +def test_dma_origin_qualifier_stays_staged_per_burst(tmp_path: Path) -> None: + iverilog = shutil.which("iverilog") + vvp = shutil.which("vvp") + if iverilog is None or vvp is None: + return + + memory_map = tmp_path / "memory_map" + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts/rtl/generate_memory_map.py"), + "--map", + str(ROOT / "rtl/mini/address_map/memory_map.json"), + "--output-dir", + str(memory_map), + "--have-sram-if", + "NO", + ], + check=True, + ) + source_list = tmp_path / "dma_origin.fl" + source_list.write_text( + "\n".join( + [ + "+define+SV_ASSRT_DISABLE", + f"+incdir+{memory_map / 'rtl'}", + f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}", + f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/interface'}", + f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/stream'}", + f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/utils'}", + f"+incdir+{ROOT / 'rtl/mini/top'}", + f"+incdir+{ROOT / 'rtl/ip/peripheral'}", + str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"), + str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"), + str(ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv"), + str(ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv"), + str(ROOT / "rtl/ip/peripheral/dma_pkg.sv"), + str(ROOT / "rtl/ip/peripheral/dma_req_if.sv"), + str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), + str(ROOT / "rtl/ip/peripheral/dma_core.sv"), + str(ROOT / "tests/rtl/dma_origin_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "dma_origin_tb.v" + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts/rtl/convt_sv2v.py"), + "-f", + str(source_list), + "--output", + str(converted), + ], + check=True, + ) + simulation = tmp_path / "dma_origin_tb" + subprocess.run( + [iverilog, "-g2012", "-s", "dma_origin_tb", "-o", str(simulation), str(converted)], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True) + assert "DMA origin qualifier staging test passed" in result.stdout + + def test_dma_apb_registers_and_irq(tmp_path: Path) -> None: iverilog = shutil.which("iverilog") vvp = shutil.which("vvp") diff --git a/tests/test_publication_tiny.py b/tests/test_publication_tiny.py index c2d6a647..acf9ce8f 100644 --- a/tests/test_publication_tiny.py +++ b/tests/test_publication_tiny.py @@ -37,7 +37,7 @@ def test_current_source_cannot_relabel_historical_publication(): @pytest.mark.parametrize("before,after", [ ("s_req_q.write_done && !s_req_q.b_captured", "1'b1"), - ("s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]", "s_r_valid"), + ("s_r_valid && s_rready[s_req_q.owner] && s_r_data[4]", "s_r_valid"), (".flush_i(1'b0)", ".flush_i(!enable_i)"), ]) def test_current_reset_binding_still_rejects_unsafe_completion(tmp_path, before, after): diff --git a/tests/test_spi_p2.py b/tests/test_spi_p2.py index 47e17f1d..669ed8d5 100644 --- a/tests/test_spi_p2.py +++ b/tests/test_spi_p2.py @@ -23,8 +23,8 @@ def test_dma_v22_abi_and_product_defaults(): assert "logic [ NumChannels-1:0][ 4:0] s_req_q" in core assert "!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX)" in core assert "s_tcd_parse_q &&" in core - assert ".RequestMask (16'h39ff)" in tiny - assert ".EnableSpi" not in tiny + assert ".RequestMask" in tiny and "0003_39ff" in tiny + assert ".EnableSpi(1'b1)" in re.sub(r"\s+", "", tiny) def test_dma_v22_tcd_request_split_is_handwritten_and_stable(): diff --git a/tests/test_spi_p3.py b/tests/test_spi_p3.py new file mode 100644 index 00000000..7522b9d5 --- /dev/null +++ b/tests/test_spi_p3.py @@ -0,0 +1,61 @@ +"""SPI-P3 Tiny topology, origin qualification, and SDK integration checks.""" +from __future__ import annotations + +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +TINY = ROOT / "rtl/tiny" + + +def test_tiny_spi_topology_and_capability() -> None: + topology = json.loads((TINY / "integration/soc_topology.json").read_text()) + memory = json.loads((TINY / "address_map/memory_map.json").read_text()) + regions = {row["symbol"]: row for row in memory["regions"]} + assert regions["APB4_SPI"]["base"] == "0x1001D000" + assert topology["fabric"]["target_count"] == 29 + assert topology["fabric"]["decode_error_target"] == 27 + assert topology["fabric"]["protocol_error_target"] == 28 + assert {row["core_bit"] for row in topology["interrupts"] if row["name"] == "spi"} == {25} + assert any(row["name"] == "spi" and row["regions"] == ["APB4_SPI"] + for row in topology["apb_targets"]) + assert topology["dma_request_mask"] == "0x339ff" + + +def test_tiny_spi_routes_and_lifecycle() -> None: + topology = json.loads((TINY / "integration/soc_topology.json").read_text()) + routes = {row["pin"]: row for row in topology["gpio_alt_functions"]} + assert routes[27]["alt0"]["do"] == "s_spi_sck" + assert routes[28]["alt1"]["do"] == "s_spi_mosi" + assert routes[30]["alt1"]["inputs"] == ["s_spi_miso"] + assert routes[31]["alt1"]["do"] == "s_spi_cs_n" + top = (TINY / "top/retrosoc_tiny.sv").read_text() + assert ".TargetMask (17'h17fff)" in top + assert ".EnableSpi(1'b1)" in re.sub(r"\s+", "", top) + assert ".EnableOriginQualifier" in top + assert "s_periph_idle[16] = s_spi_lifecycle_idle" in top + + +def test_spi_origin_bridge_and_dma_alias_contract() -> None: + bridge = (TINY / "top/tiny_axi42apb4.sv").read_text() + async_bridge = (TINY / "top/tiny_apb_async_bridge.sv").read_text() + target = (TINY / "top/tiny_axi4_target.sv").read_text() + dma = (ROOT / "rtl/ip/peripheral/dma_core.sv").read_text() + assert "origin_o" in bridge and "channel_o" in bridge + assert "src_origin_i" in async_bridge and "dst_origin_o" in async_bridge + assert "s_selected == 2'd1" in target and "4'b0100" in target + assert "SpiTxAddress" in dma and "SpiRxAddress" in dma + assert "s_cfg_dst_addr[channel] != SpiTxAddress" in dma + assert "s_cfg_src_addr[channel] != SpiRxAddress" in dma + + +def test_spi_hal_is_bounded_and_discoverable() -> None: + header = (ROOT / "crt/include/retrosoc/hal/spi.h").read_text() + source = (ROOT / "crt/src/hal/spi.c").read_text() + math = (ROOT / "crt/src/hal/spi_math.c").read_text() + assert "rs_spi_probe" in header and "rs_spi_probe" in source + assert "rs_spi_abort" in header and "rs_spi_recover" in header + assert "rs_timeout_t timeout" in header + assert "UINT64_C(2)" in math + assert "RS_ENOTSUP" in source diff --git a/tests/test_spi_register_parity.py b/tests/test_spi_register_parity.py index 2111ea55..cfdf8036 100644 --- a/tests/test_spi_register_parity.py +++ b/tests/test_spi_register_parity.py @@ -49,9 +49,10 @@ def test_frozen_spi_register_mirrors(): assert rtl['IRQ_TX_LEVEL'] == 8 and rtl['IRQ_RX_LEVEL'] == 9 -def test_spi_is_not_advertised_by_product_integration(): - for product in ('tiny', 'mini'): - for filelist in (ROOT / f'rtl/{product}/filelist').glob('*.fl'): - assert '/serial/apb4_spi.sv' not in filelist.read_text() - assert 'spi' not in [row['name'] for row in __import__('json').loads( +def test_spi_is_advertised_only_by_tiny_product_integration(): + assert any('/serial/apb4_spi.sv' in path.read_text() + for path in (ROOT / 'rtl/tiny/filelist').glob('*.fl')) + assert all('/serial/apb4_spi.sv' not in path.read_text() + for path in (ROOT / 'rtl/mini/filelist').glob('*.fl')) + assert 'spi' in [row['name'] for row in __import__('json').loads( (ROOT / 'rtl/tiny/integration/soc_topology.json').read_text())['apb_targets']] diff --git a/tests/test_tiny.py b/tests/test_tiny.py index 11d9a872..11ecab09 100644 --- a/tests/test_tiny.py +++ b/tests/test_tiny.py @@ -36,7 +36,7 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None: name = target["name"] assert f".presetn(s_{name}_rst_n)" in interfaces if name in {"gpio", "uart0", "i2c0", "timer0", "timer1", "pwm", "i2s", "sdio", - "dma", "ws2812", "rng", "crc", "crypto", "dvp"}: + "dma", "ws2812", "rng", "crc", "crypto", "dvp", "spi"}: assert f"tiny_peripheral_leaf u_{name}_lifecycle" in interfaces assert f".rst_n_i(s_leaf_rst_n[{index}])" in interfaces assert f".rst_n_o(s_{name}_rst_n)" in interfaces diff --git a/tests/test_tiny_r2_p2.py b/tests/test_tiny_r2_p2.py index 7872ce90..fadab2ea 100644 --- a/tests/test_tiny_r2_p2.py +++ b/tests/test_tiny_r2_p2.py @@ -59,7 +59,7 @@ def test_reset_inventory_has_active_domains_and_all_existing_targets() -> None: reset = inventory["reset_distribution"] assert reset["release_edges"] == 5 assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])] - assert len(reset["leaf_names"]) == 22 + assert len(reset["leaf_names"]) == 23 assert {domain["name"] for domain in inventory["domains"]} == { "aon", "system", "memory", "peripheral", "jtag", "audio", "dvp_pixel"} assert reset["clock_domain"] == "domain-specific" and reset["main_sram_payload_reset"] == "none" diff --git a/tests/test_tiny_r2_p6.py b/tests/test_tiny_r2_p6.py index 345961b8..2796dc71 100644 --- a/tests/test_tiny_r2_p6.py +++ b/tests/test_tiny_r2_p6.py @@ -406,7 +406,8 @@ def test_p6_map_irq_request_and_pin_contract(): assert {k: irqs[k] for k in ("rng", "ws2812", "crypto", "i2s", "sdio")} == { "rng": 16, "ws2812": 17, "crypto": 23, "i2s": 8, "sdio": 10} assert irqs["dvp"] == 15 - assert {19, 24, 25, 26, 27}.isdisjoint(irqs.values()) + assert irqs["spi"] == 25 + assert {19, 24, 26, 27}.isdisjoint(irqs.values()) assert irqs["rcu"] == 31 pins = topology["gpio_alt_functions"] assert pins[9]["alt0"]["inputs"] == ["u_uart0_if.rx_i"] @@ -542,7 +543,7 @@ int main(void) { def test_p6_reset_and_macro_policy_remains_explicit(): text = (TINY / "top/retrosoc_tiny.sv").read_text() assert ".clk_aud_i" in text and "s_audio_rst_n" in text - assert ".EnableStreams" in text and "16'h39ff" in text + assert ".EnableStreams" in text and "32'h0003_39ff" in text assert "apb4_crypto u_crypto" in text tree = (TINY / "top/tiny_reset_tree.sv").read_text() assert "s_audio" not in tree and "crypto" not in tree diff --git a/tests/test_tiny_r2_p7.py b/tests/test_tiny_r2_p7.py index cf0e4886..8a823c4c 100644 --- a/tests/test_tiny_r2_p7.py +++ b/tests/test_tiny_r2_p7.py @@ -223,7 +223,7 @@ def check_p7_target_capability(top: str, firmware: str) -> None: instances = re.findall(r"\btiny_sysctrl\s*#\(([^;]*?)\)\s*u_sysctrl\s*\(", top) if len(instances) != 1: raise ValueError("missing or ambiguous production SYSCTRL instance") - targets = re.findall(r"\.TargetMask\s*\(\s*15'h([0-9a-fA-F_]+)\s*\)", instances[0]) + targets = re.findall(r"\.TargetMask\s*\(\s*17'h([0-9a-fA-F_]+)\s*\)", instances[0]) expected = re.findall( r"^\s*#define RS_P7_EXPECTED_TARGET_CAPABILITY\s+UINT32_C\(0x([0-9a-fA-F]+)\)\s*$", firmware, re.MULTILINE) @@ -231,7 +231,7 @@ def check_p7_target_capability(top: str, firmware: str) -> None: raise ValueError("missing or ambiguous target capability definition") # Freeze the implemented set independently of both handwritten sources. # The HAL's 0x3ffff architecture mask also admits unimplemented targets. - if int(targets[0].replace("_", ""), 16) != 0x7FFF or int(expected[0], 16) != 0x7FFF: + if int(targets[0].replace("_", ""), 16) != 0x17FFF or int(expected[0], 16) != 0x17FFF: raise ValueError("target capability differs from the DVP-inclusive contract") main = re.findall(r"\bint\s+main\s*\(void\)\s*\{(.*)", firmware, re.DOTALL) guard = ( @@ -250,15 +250,15 @@ def test_tiny_p7_target_capability_parity() -> None: @pytest.mark.parametrize(("source", "old", "new"), [ - ("firmware", "UINT32_C(0x00007FFF)", "UINT32_C(0x00003FFF)"), - ("top", "15'h07fff", "15'h03fff"), - *[("firmware", "UINT32_C(0x00007FFF)", f"UINT32_C({value})") - for value in ("0x0000FFFF", "0x00017FFF", "0x00027FFF", "0x0003FFFF")], + ("firmware", "UINT32_C(0x00017FFF)", "UINT32_C(0x00003FFF)"), + ("top", "17'h17fff", "17'h03fff"), + *[("firmware", "UINT32_C(0x00017FFF)", f"UINT32_C({value})") + for value in ("0x00007FFF", "0x0000FFFF", "0x00027FFF", "0x0003FFFF")], ("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY", "false"), ("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY", "(snapshot.target_capability & RS_P7_EXPECTED_TARGET_CAPABILITY) != RS_P7_EXPECTED_TARGET_CAPABILITY"), ("firmware", "#define RS_P7_EXPECTED_TARGET_CAPABILITY", "#define RS_P7_UNUSED_CAPABILITY"), - ("top", ".TargetMask (15'h07fff)", ".UnusedMask (15'h07fff)"), + ("top", ".TargetMask (17'h17fff)", ".UnusedMask (17'h17fff)"), ]) def test_tiny_p7_target_capability_rejects_drift(source: str, old: str, new: str) -> None: sources = {"top": (ROOT / "rtl/tiny/top/retrosoc_tiny.sv").read_text(), diff --git a/tests/test_tiny_r2_p9.py b/tests/test_tiny_r2_p9.py index 8b74576e..92719030 100644 --- a/tests/test_tiny_r2_p9.py +++ b/tests/test_tiny_r2_p9.py @@ -40,9 +40,9 @@ def test_p9_topology_and_camera_routes() -> None: memory = json.loads((TINY / "address_map/memory_map.json").read_text()) regions = {item["symbol"]: item for item in memory["regions"]} assert regions["APB4_DVP"]["base"] == "0x1000E000" - assert topology["fabric"]["target_count"] == 28 - assert topology["fabric"]["decode_error_target"] == 26 - assert topology["fabric"]["protocol_error_target"] == 27 + assert topology["fabric"]["target_count"] == 29 + assert topology["fabric"]["decode_error_target"] == 27 + assert topology["fabric"]["protocol_error_target"] == 28 assert any(item["name"] == "dvp" and item["regions"] == ["APB4_DVP"] for item in topology["apb_targets"]) assert {item["core_bit"] for item in topology["interrupts"] if item["name"] == "dvp"} == {15} @@ -55,7 +55,7 @@ def test_p9_topology_and_camera_routes() -> None: assert routes[22]["alt1"]["inputs"] == ["u_dvp_if.vsync_i"] assert routes[23]["alt1"]["do"] == "s_clkout" assert read_topology(TINY / "integration/soc_topology.json", - TINY / "address_map/memory_map.json")["dma_request_mask"] == "0x39ff" + TINY / "address_map/memory_map.json")["dma_request_mask"] == "0x339ff" def test_p9_generated_and_hal_bindings() -> None: