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.
This commit is contained in:
maksyuki committed 2026-10-22 11:57:29 +08:00
1 parent 3b9b83657a
commit 31daf3e84c
58 files changed
+1978 -472

No files matched your search

+16 -1
View File
@@ -91,6 +91,7 @@ TINY_P9_ACCEPTANCE ?= NO
TINY_P9_QUICK ?= NO TINY_P9_QUICK ?= NO
TINY_P9_WORKLOAD ?= QVGA_RGB565 TINY_P9_WORKLOAD ?= QVGA_RGB565
TINY_P10_ACCEPTANCE ?= NO TINY_P10_ACCEPTANCE ?= NO
TINY_SPI_P3_ACCEPTANCE ?= NO
COREMARK_MODE ?= quick COREMARK_MODE ?= quick
NPU_P5_ACCEPTANCE ?= NO NPU_P5_ACCEPTANCE ?= NO
NPU_P6_ACCEPTANCE ?= NO NPU_P6_ACCEPTANCE ?= NO
@@ -151,6 +152,9 @@ endif
ifeq ($(TINY_P10_ACCEPTANCE),YES) ifeq ($(TINY_P10_ACCEPTANCE),YES)
CONFIG_KEY_VARS += TINY_P10_ACCEPTANCE CONFIG_KEY_VARS += TINY_P10_ACCEPTANCE
endif 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 \ VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \ --lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
$(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))) | tail -n 1)) $(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_ACCEPTANCE,$(VALID_BOOL))
$(call validate_value,TINY_P9_QUICK,$(VALID_BOOL)) $(call validate_value,TINY_P9_QUICK,$(VALID_BOOL))
$(call validate_value,TINY_P10_ACCEPTANCE,$(VALID_BOOL)) $(call validate_value,TINY_P10_ACCEPTANCE,$(VALID_BOOL))
$(call validate_value,TINY_SPI_P3_ACCEPTANCE,$(VALID_BOOL))
ifeq ($(TINY_P10_ACCEPTANCE),YES) 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) 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) $(error TINY_P10_ACCEPTANCE requires Tiny ci_smoke with CSR and no P7/P8/P9 acceptance image)
endif endif
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) $(call validate_value,TINY_P9_WORKLOAD,QVGA_RGB565 VGA_YUV422 RECOVERY)
ifneq ($(TINY_P9_WORKLOAD),QVGA_RGB565) ifneq ($(TINY_P9_WORKLOAD),QVGA_RGB565)
ifneq ($(TINY_P9_ACCEPTANCE)/$(TINY_P9_QUICK),YES/NO) 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-%,%,$@) \ $(PYTHON) $(ROOT_PATH)/scripts/spi_p2.py $(patsubst spi-p2-%,%,$@) \
--variant-root $(VARIANT_ROOT) --pdk $(PDK) --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 .PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report
crypto-p0-constants: manifest crypto-p0-constants: manifest
$(PYTHON) $(ROOT_PATH)/scripts/crypto_p0.py constants --variant-root $(VARIANT_ROOT) $(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)) python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY --pdk $(PDK))
sim-asm: asm sim-asm: asm
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
+4 -1
View File
@@ -22,4 +22,7 @@ endif
endif endif
ifeq ($(TINY_P10_ACCEPTANCE),YES) ifeq ($(TINY_P10_ACCEPTANCE),YES)
APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p10.c APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p10.c
endif endif
ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES)
APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_spi_p3.c
endif
+3 -2
View File
@@ -11,8 +11,9 @@
#include <retrosoc/hal/timer.h> #include <retrosoc/hal/timer.h>
#include <pwm_regs.h> #include <pwm_regs.h>
/* Current Tiny includes DVP (target 14); later expansion targets stay absent. */ /* Current Tiny includes DVP (target 14) and SPI (RCU target 16); the other
#define RS_P7_EXPECTED_TARGET_CAPABILITY UINT32_C(0x00007FFF) * 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 /* Short P7 firmware: execute in real main SRAM and use real SYSCTRL/APB
* crossings. The full peripheral firmware remains a separate regression. */ * crossings. The full peripheral firmware remains a separate regression. */
+73
View File
@@ -0,0 +1,73 @@
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/arch/riscv/system_base.h>
#include <retrosoc/core/irq.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/rcu.h>
#include <retrosoc/hal/rcu_regs.h>
#include <retrosoc/hal/spi.h>
#include <retrosoc/hal/sysctrl.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/lib/printf.h>
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 (;;) {
}
}
+1
View File
@@ -39,6 +39,7 @@
#define RS_TINY_RCU_STATUS_FAULT UINT32_C(1U << 4) #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_PLL_PRESENT UINT32_C(1U << 8)
#define RS_TINY_RCU_CAP_TARGET_MASK UINT32_C(0x0003FFFF) #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_FAULT_MASK UINT32_C(0x0000001F)
#define RS_TINY_RCU_RESET_CAUSE_MASK UINT32_C(0x0000003F) #define RS_TINY_RCU_RESET_CAUSE_MASK UINT32_C(0x0000003F)
#define RS_TINY_RCU_CSR_MSLEEP 0xBF0 #define RS_TINY_RCU_CSR_MSLEEP 0xBF0
+67
View File
@@ -0,0 +1,67 @@
#ifndef RETROSOC_HAL_SPI_H
#define RETROSOC_HAL_SPI_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#include <retrosoc/hal/spi_regs.h>
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
+13 -4
View File
@@ -3,6 +3,11 @@
#include <stddef.h> #include <stddef.h>
#include <retrosoc/hal/dma.h> #include <retrosoc/hal/dma.h>
#include <retrosoc/hal/spi_regs.h>
#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) { static bool rs_dma_address_valid(uintptr_t address, uint32_t byte_count, bool increment) {
uintptr_t last_address; uintptr_t last_address;
@@ -30,11 +35,13 @@ static bool rs_dma_request_valid(const rs_dma_config_t *config) {
#ifdef RS_SOC_TINY #ifdef RS_SOC_TINY
if ((config->request == RS_DMA_REQUEST_SPI_TX) || if ((config->request == RS_DMA_REQUEST_SPI_TX) ||
(config->request == RS_DMA_REQUEST_SPI_RX)) { (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) && return ((config->byte_count % sizeof(uint32_t)) == 0U) &&
(((config->request == RS_DMA_REQUEST_SPI_TX) && config->source_increment && (((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->request == RS_DMA_REQUEST_SPI_RX) && !config->source_increment &&
config->destination_increment)); config->destination_increment && config->source == rx_aperture));
} }
#endif #endif
return (config->request == RS_DMA_REQUEST_SOFTWARE) || 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 #ifdef RS_SOC_TINY
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_TX) && 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_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; return RS_EINVAL;
} }
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_RX) && 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_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; return RS_EINVAL;
} }
#endif #endif
+206
View File
@@ -0,0 +1,206 @@
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/spi.h>
#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
}
+20
View File
@@ -0,0 +1,20 @@
#include <retrosoc/hal/spi.h>
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;
}
+25
View File
@@ -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/<variant>/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.
+149
View File
@@ -112,6 +112,155 @@ SPI request allocation, the full post-fix Pytest campaign and product
regressions are unrun. Product instances regressions are unrun. Product instances
retain V2.0/default-disabled SPI. 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/<variant>/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) ## SPI-P1 implementation checkpoint (2026-10-18, historical)
The approved standalone implementation starts from clean The approved standalone implementation starts from clean
@@ -25,6 +25,8 @@ module apb4_spi_block_top (
output logic output_enable_o, output logic output_enable_o,
output logic irq_o, output logic irq_o,
output logic [ 1:0] credit_commit_o, output logic [ 1:0] credit_commit_o,
output logic session_owned_o,
output logic lifecycle_idle_o,
input logic [31:0] paddr_i, input logic [31:0] paddr_i,
pwdata_i, pwdata_i,
input logic [ 3:0] pstrb_i, input logic [ 3:0] pstrb_i,
@@ -75,6 +77,8 @@ module apb4_spi_block_top (
.output_enable_o (output_enable_o), .output_enable_o (output_enable_o),
.irq_o (irq_o), .irq_o (irq_o),
.credit_commit_o (credit_commit_o), .credit_commit_o (credit_commit_o),
.session_owned_o (session_owned_o),
.lifecycle_idle_o (lifecycle_idle_o),
.apb4 (s_apb4) .apb4 (s_apb4)
); );
endmodule endmodule
@@ -125,7 +125,7 @@
{ {
"file": "rtl/tiny/top/tiny_axi4_fabric.sv", "file": "rtl/tiny/top/tiny_axi4_fabric.sv",
"text": [ "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 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 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);", "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", "file": "crt/src/hal/dma_math.c",
"text": [ "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)", ".RTC_CLOCK_HZ(24_000_000)",
".WDG_CLOCK_HZ(24_000_000)", ".WDG_CLOCK_HZ(24_000_000)",
".RESET_PULSE_CYCLES(8)", ".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", "file": "rtl/tiny/top/tiny_axi4_target.sv",
"text": [ "text": [
"assign s_accept = (s_req_q.state == Idle) && enable_i && s_valid;", "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;", "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_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});", "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)", ".flush_i(1'b0)",
+3
View File
@@ -237,6 +237,9 @@ module apu_dma (
.write_done_o(s_write_done), .write_done_o(s_write_done),
.write_resp_o(s_write_resp), .write_resp_o(s_write_resp),
.write_id_error_o(s_write_id_error), .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) .axi4(axi4)
); );
+34 -31
View File
@@ -440,37 +440,40 @@ module jpeg_dma #(
.DataWidth (DataWidth), .DataWidth (DataWidth),
.MaxBurstBeats(MaxBurstBeats) .MaxBurstBeats(MaxBurstBeats)
) u_axi4_master ( ) u_axi4_master (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (s_read_start_valid), .read_start_valid_i (s_read_start_valid),
.read_start_ready_o (s_read_start_ready), .read_start_ready_o (s_read_start_ready),
.read_addr_i (s_read_addr_q), .read_addr_i (s_read_addr_q),
.read_beats_i (s_read_beats), .read_beats_i (s_read_beats),
.read_fixed_i (1'b0), .read_fixed_i (1'b0),
.read_busy_o (s_read_master_busy), .read_busy_o (s_read_master_busy),
.read_beat_valid_o (s_read_beat_valid), .read_beat_valid_o (s_read_beat_valid),
.read_beat_ready_i (s_read_beat_ready), .read_beat_ready_i (s_read_beat_ready),
.read_data_o (s_read_data), .read_data_o (s_read_data),
.read_resp_o (s_read_resp), .read_resp_o (s_read_resp),
.read_last_o (s_read_last), .read_last_o (s_read_last),
.read_expected_last_o(s_read_expected_last), .read_expected_last_o (s_read_expected_last),
.read_id_error_o (s_read_id_err), .read_id_error_o (s_read_id_err),
.read_done_o (s_read_master_done), .read_done_o (s_read_master_done),
.write_start_valid_i (s_write_start_valid), .write_start_valid_i (s_write_start_valid),
.write_start_ready_o (s_write_start_ready), .write_start_ready_o (s_write_start_ready),
.write_addr_i (s_write_addr_q), .write_addr_i (s_write_addr_q),
.write_beats_i (s_write_beats), .write_beats_i (s_write_beats),
.write_fixed_i (1'b0), .write_fixed_i (1'b0),
.write_busy_o (s_write_master_busy), .write_busy_o (s_write_master_busy),
.write_data_valid_i (s_write_data_valid), .write_data_valid_i (s_write_data_valid),
.write_data_ready_o (s_write_data_ready), .write_data_ready_o (s_write_data_ready),
.write_data_i (s_write_data), .write_data_i (s_write_data),
.write_strb_i (s_write_strobe), .write_strb_i (s_write_strobe),
.write_done_o (s_write_master_done), .write_done_o (s_write_master_done),
.write_resp_o (s_write_resp), .write_resp_o (s_write_resp),
.write_id_error_o (s_write_id_err), .write_id_error_o (s_write_id_err),
.axi4 (axi4) .origin_enable_i (1'b0),
.read_origin_channel_i (3'd0),
.write_origin_channel_i(3'd0),
.axi4 (axi4)
); );
dffr #( dffr #(
+36 -20
View File
@@ -9,16 +9,19 @@
// See the Mulan PSL v2 for more details. // See the Mulan PSL v2 for more details.
module apb4_dma #( module apb4_dma #(
parameter int AddrWidth = 32, parameter int AddrWidth = 32,
parameter int DataWidth = 32, parameter int DataWidth = 32,
parameter int NumChannels = 4, parameter int NumChannels = 4,
parameter int MaxBurstBeats = 16, parameter int MaxBurstBeats = 16,
parameter int FifoDepth = 16, parameter int FifoDepth = 16,
parameter logic [31:0] RequestMask = 32'h0000_3fff, parameter logic [31:0] RequestMask = 32'h0000_3fff,
parameter logic [31:0] SupportedRequests = RequestMask, parameter logic [31:0] SupportedRequests = RequestMask,
parameter logic [31:0] IpVersion = 32'h0002_0000, parameter logic [31:0] IpVersion = 32'h0002_0000,
parameter bit EnableStreams = 1'b1, parameter bit EnableStreams = 1'b1,
parameter bit EnableSpi = 1'b0 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. // verilog_format: off -- integration ports retain established peripheral endpoint names.
output logic lifecycle_idle_o, output logic lifecycle_idle_o,
@@ -33,7 +36,12 @@ module apb4_dma #(
axi4_stream_if.sink i2s_rx_axis, axi4_stream_if.sink i2s_rx_axis,
axi4_stream_if.sink dvp_rx_axis, axi4_stream_if.sink dvp_rx_axis,
axi4_stream_if.source crypto_in_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 // verilog_format: on
); );
logic [ NumChannels*32-1:0] s_ch_cfg; logic [ NumChannels*32-1:0] s_ch_cfg;
@@ -134,14 +142,17 @@ module apb4_dma #(
); );
dma_core #( dma_core #(
.RequestMask (RequestMask), .RequestMask (RequestMask),
.EnableSpi (EnableSpi), .EnableSpi (EnableSpi),
.EnableStreams(EnableStreams), .EnableOriginQualifier(EnableOriginQualifier),
.AddrWidth (AddrWidth), .SpiTxAddress (SpiTxAddress),
.DataWidth (DataWidth), .SpiRxAddress (SpiRxAddress),
.NumChannels (NumChannels), .EnableStreams (EnableStreams),
.MaxBurstBeats(MaxBurstBeats), .AddrWidth (AddrWidth),
.FifoDepth (FifoDepth) .DataWidth (DataWidth),
.NumChannels (NumChannels),
.MaxBurstBeats (MaxBurstBeats),
.FifoDepth (FifoDepth)
) u_dma_core ( ) u_dma_core (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
@@ -190,7 +201,12 @@ module apb4_dma #(
.i2s_rx_axis (i2s_rx_axis), .i2s_rx_axis (i2s_rx_axis),
.dvp_rx_axis (dvp_rx_axis), .dvp_rx_axis (dvp_rx_axis),
.crypto_in_axis (crypto_in_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; logic s_bus_busy;
assign s_bus_busy = axi4.awvalid || axi4.arvalid || axi4.wvalid; assign s_bus_busy = axi4.awvalid || axi4.arvalid || axi4.wvalid;
+14 -1
View File
@@ -21,7 +21,13 @@ module apb4_gpio #(
input logic rst_n_i, input logic rst_n_i,
apb4_if.slave apb4, apb4_if.slave apb4,
gpio_if.dut gpio, 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 // verilog_format: on
); );
@@ -108,4 +114,11 @@ module apb4_gpio #(
.user_gpio (user_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 endmodule
+44 -33
View File
@@ -46,6 +46,9 @@ module dma_axi4_master #(
output logic write_done_o, output logic write_done_o,
output logic [ 1:0] write_resp_o, output logic [ 1:0] write_resp_o,
output logic write_id_error_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 axi4_if.master axi4
// verilog_format: on // verilog_format: on
); );
@@ -74,6 +77,8 @@ module dma_axi4_master #(
logic [ 4:0] s_write_beat_q; logic [ 4:0] s_write_beat_q;
logic s_read_fixed_q; logic s_read_fixed_q;
logic s_write_fixed_q; logic s_write_fixed_q;
logic [ 2:0] s_read_origin_q;
logic [ 2:0] s_write_origin_q;
`ifndef SYNTHESIS `ifndef SYNTHESIS
initial begin initial begin
@@ -112,12 +117,12 @@ module dma_axi4_master #(
assign axi4.awprot = `AXI4_PROT_DATA; assign axi4.awprot = `AXI4_PROT_DATA;
assign axi4.awqos = `AXI4_QOS_NORMAL; assign axi4.awqos = `AXI4_QOS_NORMAL;
assign axi4.awregion = `AXI4_REGION_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.awvalid = s_write_state_q == WriteAddress;
assign axi4.wdata = write_data_i; assign axi4.wdata = write_data_i;
assign axi4.wstrb = write_strb_i; assign axi4.wstrb = write_strb_i;
assign axi4.wlast = (s_write_beat_q + 1'b1) == s_write_beats_q; 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.wvalid = (s_write_state_q == WriteData) && write_data_valid_i;
assign axi4.bready = s_write_state_q == WriteResponse; assign axi4.bready = s_write_state_q == WriteResponse;
@@ -131,32 +136,35 @@ module dma_axi4_master #(
assign axi4.arprot = `AXI4_PROT_DATA; assign axi4.arprot = `AXI4_PROT_DATA;
assign axi4.arqos = `AXI4_QOS_NORMAL; assign axi4.arqos = `AXI4_QOS_NORMAL;
assign axi4.arregion = `AXI4_REGION_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.arvalid = s_read_state_q == ReadAddress;
assign axi4.rready = (s_read_state_q == ReadData) && read_beat_ready_i; assign axi4.rready = (s_read_state_q == ReadData) && read_beat_ready_i;
always_ff @(posedge clk_i or negedge rst_n_i) begin always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin if (!rst_n_i) begin
s_read_state_q <= ReadIdle; s_read_state_q <= ReadIdle;
s_read_addr_q <= '0; s_read_addr_q <= '0;
s_read_beats_q <= 5'd1; s_read_beats_q <= 5'd1;
s_read_beat_q <= '0; s_read_beat_q <= '0;
s_read_fixed_q <= 1'b0; s_read_fixed_q <= 1'b0;
s_read_origin_q <= 3'd0;
end else if (clear_i) begin end else if (clear_i) begin
s_read_state_q <= ReadIdle; s_read_state_q <= ReadIdle;
s_read_addr_q <= '0; s_read_addr_q <= '0;
s_read_beats_q <= 5'd1; s_read_beats_q <= 5'd1;
s_read_beat_q <= '0; s_read_beat_q <= '0;
s_read_fixed_q <= 1'b0; s_read_fixed_q <= 1'b0;
s_read_origin_q <= 3'd0;
end else begin end else begin
unique case (s_read_state_q) unique case (s_read_state_q)
ReadIdle: begin ReadIdle: begin
if (read_start_valid_i && read_start_ready_o) begin if (read_start_valid_i && read_start_ready_o) begin
s_read_addr_q <= read_addr_i; s_read_addr_q <= read_addr_i;
s_read_beats_q <= read_beats_i; s_read_beats_q <= read_beats_i;
s_read_beat_q <= '0; s_read_beat_q <= '0;
s_read_fixed_q <= read_fixed_i; s_read_fixed_q <= read_fixed_i;
s_read_state_q <= ReadAddress; s_read_origin_q <= read_origin_channel_i;
s_read_state_q <= ReadAddress;
end end
end end
ReadAddress: begin ReadAddress: begin
@@ -180,26 +188,29 @@ module dma_axi4_master #(
always_ff @(posedge clk_i or negedge rst_n_i) begin always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin if (!rst_n_i) begin
s_write_state_q <= WriteIdle; s_write_state_q <= WriteIdle;
s_write_addr_q <= '0; s_write_addr_q <= '0;
s_write_beats_q <= 5'd1; s_write_beats_q <= 5'd1;
s_write_beat_q <= '0; s_write_beat_q <= '0;
s_write_fixed_q <= 1'b0; s_write_fixed_q <= 1'b0;
s_write_origin_q <= 3'd0;
end else if (clear_i) begin end else if (clear_i) begin
s_write_state_q <= WriteIdle; s_write_state_q <= WriteIdle;
s_write_addr_q <= '0; s_write_addr_q <= '0;
s_write_beats_q <= 5'd1; s_write_beats_q <= 5'd1;
s_write_beat_q <= '0; s_write_beat_q <= '0;
s_write_fixed_q <= 1'b0; s_write_fixed_q <= 1'b0;
s_write_origin_q <= 3'd0;
end else begin end else begin
unique case (s_write_state_q) unique case (s_write_state_q)
WriteIdle: begin WriteIdle: begin
if (write_start_valid_i && write_start_ready_o) begin if (write_start_valid_i && write_start_ready_o) begin
s_write_addr_q <= write_addr_i; s_write_addr_q <= write_addr_i;
s_write_beats_q <= write_beats_i; s_write_beats_q <= write_beats_i;
s_write_beat_q <= '0; s_write_beat_q <= '0;
s_write_fixed_q <= write_fixed_i; s_write_fixed_q <= write_fixed_i;
s_write_state_q <= WriteAddress; s_write_origin_q <= write_origin_channel_i;
s_write_state_q <= WriteAddress;
end end
end end
WriteAddress: begin WriteAddress: begin
+116 -56
View File
@@ -12,16 +12,19 @@
`include "axi4_define.svh" `include "axi4_define.svh"
module dma_core #( module dma_core #(
parameter int AddrWidth = 32, parameter int AddrWidth = 32,
parameter int DataWidth = 32, parameter int DataWidth = 32,
parameter int NumChannels = 4, parameter int NumChannels = 4,
parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1,
parameter int MaxBurstBeats = 16, parameter int MaxBurstBeats = 16,
parameter int FifoDepth = 16, parameter int FifoDepth = 16,
parameter bit EnableCrc = 1'b1, parameter bit EnableCrc = 1'b1,
parameter logic [31:0] RequestMask = 32'h0000_3fff, parameter logic [31:0] RequestMask = 32'h0000_3fff,
parameter bit EnableStreams = 1'b1, parameter bit EnableStreams = 1'b1,
parameter bit EnableSpi = 1'b0 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. // verilog_format: off -- channel vectors are kept aligned with the register-bank ABI.
input logic clk_i, input logic clk_i,
@@ -64,6 +67,11 @@ module dma_core #(
output logic [8:0] first_error_status_o, output logic [8:0] first_error_status_o,
output logic [15:0] first_error_addr_hi_o, output logic [15:0] first_error_addr_hi_o,
output logic [31:0] request_status_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, output logic xpi_xfer_done_o,
dma_req_if.dut req, dma_req_if.dut req,
axi4_if.master axi4, axi4_if.master axi4,
@@ -183,6 +191,7 @@ module dma_core #(
logic [ 31:0] s_axi_read_start_addr; logic [ 31:0] s_axi_read_start_addr;
logic [ 4:0] s_axi_read_start_beats; logic [ 4:0] s_axi_read_start_beats;
logic s_axi_read_start_fixed; logic s_axi_read_start_fixed;
logic [ChannelIndexWidth-1:0] s_axi_read_origin_channel;
logic s_axi_read_busy; logic s_axi_read_busy;
logic s_axi_read_beat_valid; logic s_axi_read_beat_valid;
logic s_axi_read_beat_ready; logic s_axi_read_beat_ready;
@@ -198,6 +207,7 @@ module dma_core #(
logic [ 31:0] s_axi_write_start_addr; logic [ 31:0] s_axi_write_start_addr;
logic [ 4:0] s_axi_write_start_beats; logic [ 4:0] s_axi_write_start_beats;
logic s_axi_write_start_fixed; logic s_axi_write_start_fixed;
logic [ChannelIndexWidth-1:0] s_axi_write_origin_channel;
logic s_axi_write_busy; logic s_axi_write_busy;
logic s_axi_write_data_valid; logic s_axi_write_data_valid;
logic s_axi_write_data_ready; logic s_axi_write_data_ready;
@@ -293,6 +303,10 @@ module dma_core #(
((FifoDepth & (FifoDepth - 1)) != 0)) begin ((FifoDepth & (FifoDepth - 1)) != 0)) begin
$fatal(1, "dma_core: MVP requires 32-bit AXI4, 2+ channels, and a power-of-two FIFO"); $fatal(1, "dma_core: MVP requires 32-bit AXI4, 2+ channels, and a power-of-two FIFO");
end end
if (EnableOriginQualifier && (ChannelIndexWidth > 3)) begin
$fatal(1,
"dma_core: origin qualifier packs three-bit channels, so NumChannels must stay <= 8");
end
end end
`endif `endif
@@ -343,39 +357,74 @@ module dma_core #(
.DataWidth (DataWidth), .DataWidth (DataWidth),
.MaxBurstBeats(MaxBurstBeats) .MaxBurstBeats(MaxBurstBeats)
) u_dma_axi4_master ( ) u_dma_axi4_master (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (s_axi_read_start_valid), .read_start_valid_i (s_axi_read_start_valid),
.read_start_ready_o (s_axi_read_start_ready), .read_start_ready_o (s_axi_read_start_ready),
.read_addr_i (s_axi_read_start_addr), .read_addr_i (s_axi_read_start_addr),
.read_beats_i (s_axi_read_start_beats), .read_beats_i (s_axi_read_start_beats),
.read_fixed_i (s_axi_read_start_fixed), .read_fixed_i (s_axi_read_start_fixed),
.read_busy_o (s_axi_read_busy), .read_busy_o (s_axi_read_busy),
.read_beat_valid_o (s_axi_read_beat_valid), .read_beat_valid_o (s_axi_read_beat_valid),
.read_beat_ready_i (s_axi_read_beat_ready), .read_beat_ready_i (s_axi_read_beat_ready),
.read_data_o (s_axi_read_data), .read_data_o (s_axi_read_data),
.read_resp_o (s_axi_read_resp), .read_resp_o (s_axi_read_resp),
.read_last_o (s_axi_read_last), .read_last_o (s_axi_read_last),
.read_expected_last_o(s_axi_read_expected_last), .read_expected_last_o (s_axi_read_expected_last),
.read_id_error_o (s_axi_read_id_error), .read_id_error_o (s_axi_read_id_error),
.read_done_o (s_axi_read_done), .read_done_o (s_axi_read_done),
.write_start_valid_i (s_axi_write_start_valid), .write_start_valid_i (s_axi_write_start_valid),
.write_start_ready_o (s_axi_write_start_ready), .write_start_ready_o (s_axi_write_start_ready),
.write_addr_i (s_axi_write_start_addr), .write_addr_i (s_axi_write_start_addr),
.write_beats_i (s_axi_write_start_beats), .write_beats_i (s_axi_write_start_beats),
.write_fixed_i (s_axi_write_start_fixed), .write_fixed_i (s_axi_write_start_fixed),
.write_busy_o (s_axi_write_busy), .write_busy_o (s_axi_write_busy),
.write_data_valid_i (s_axi_write_data_valid), .write_data_valid_i (s_axi_write_data_valid),
.write_data_ready_o (s_axi_write_data_ready), .write_data_ready_o (s_axi_write_data_ready),
.write_data_i (s_axi_write_data), .write_data_i (s_axi_write_data),
.write_strb_i (s_axi_write_strb), .write_strb_i (s_axi_write_strb),
.write_done_o (s_axi_write_done), .write_done_o (s_axi_write_done),
.write_resp_o (s_axi_write_resp), .write_resp_o (s_axi_write_resp),
.write_id_error_o (s_axi_write_id_error), .write_id_error_o (s_axi_write_id_error),
.axi4 (axi4) .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 always_comb begin
s_req_ready = '0; s_req_ready = '0;
s_req_ready[DMA_REQUEST_SOFTWARE] = 1'b1; s_req_ready[DMA_REQUEST_SOFTWARE] = 1'b1;
@@ -480,9 +529,11 @@ module dma_core #(
(!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) || (!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) ||
(s_cfg_req[channel] == DMA_REQUEST_SPI_RX))) || (s_cfg_req[channel] == DMA_REQUEST_SPI_RX))) ||
((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) && ((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_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_valid[channel] = 1'b0;
s_start_err_code[channel] = s_start_err_code[channel] =
((s_cfg_src_addr[channel][1:0] != 2'b00) || ((s_cfg_src_addr[channel][1:0] != 2'b00) ||
@@ -680,10 +731,11 @@ module dma_core #(
end end
always_comb begin always_comb begin
s_axi_read_start_valid = s_read_rr_valid && (|s_read_rr_grant) && !s_axi_read_busy; 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_addr = '0;
s_axi_read_start_beats = 5'd1; s_axi_read_start_beats = 5'd1;
s_axi_read_start_fixed = 1'b0; s_axi_read_start_fixed = 1'b0;
s_axi_read_origin_channel = '0;
if (s_read_rr_valid) begin if (s_read_rr_valid) begin
s_axi_read_start_addr = s_src_base_q[s_read_rr_selected] + s_axi_read_start_addr = s_src_base_q[s_read_rr_selected] +
(s_src_increment_q[s_read_rr_selected] (s_src_increment_q[s_read_rr_selected]
@@ -691,18 +743,21 @@ module dma_core #(
: 32'd0); : 32'd0);
s_axi_read_start_beats = s_read_candidate_beats[s_read_rr_selected]; 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_start_fixed = !s_src_increment_q[s_read_rr_selected];
s_axi_read_origin_channel = s_read_rr_selected;
end end
if (s_tcd_fetch_valid) begin if (s_tcd_fetch_valid) begin
s_axi_read_start_valid = !s_axi_read_busy; 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_addr = s_tcd_ptr_q[s_tcd_fetch_selected];
s_axi_read_start_beats = 5'd16; s_axi_read_start_beats = 5'd16;
s_axi_read_start_fixed = 1'b0; s_axi_read_start_fixed = 1'b0;
s_axi_read_origin_channel = s_tcd_fetch_selected;
end end
s_axi_write_start_valid = s_write_rr_valid && (|s_write_rr_grant) && !s_axi_write_busy; 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_addr = '0;
s_axi_write_start_beats = 5'd1; s_axi_write_start_beats = 5'd1;
s_axi_write_start_fixed = 1'b0; s_axi_write_start_fixed = 1'b0;
s_axi_write_origin_channel = '0;
if (s_write_rr_valid) begin if (s_write_rr_valid) begin
s_axi_write_start_addr = s_dst_base_q[s_write_rr_selected] + s_axi_write_start_addr = s_dst_base_q[s_write_rr_selected] +
(s_dst_increment_q[s_write_rr_selected] (s_dst_increment_q[s_write_rr_selected]
@@ -710,6 +765,7 @@ module dma_core #(
: 32'd0); : 32'd0);
s_axi_write_start_beats = s_write_candidate_beats[s_write_rr_selected]; 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_start_fixed = !s_dst_increment_q[s_write_rr_selected];
s_axi_write_origin_channel = s_write_rr_selected;
end end
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][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][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][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_MM_TO_STREAM) ||
(s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_STREAM_TO_MM)) && (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)) || (s_tcd_word3_q[s_tcd_read_owner_q][1:0] != 2'b00)) ||
+7 -1
View File
@@ -26,11 +26,14 @@ module apb4_spi #(
cs_n_o, cs_n_o,
output_enable_o, output_enable_o,
irq_o, irq_o,
session_owned_o,
lifecycle_idle_o,
output logic [ 1:0] credit_commit_o, output logic [ 1:0] credit_commit_o,
apb4_if.slave apb4 apb4_if.slave apb4
); );
spi_ctrl_if u_ctrl (); spi_ctrl_if u_ctrl ();
spi_pkg::configuration_t s_cfg; spi_pkg::configuration_t s_cfg;
spi_pkg::status_t s_stat;
logic [31:0] s_claim, s_ready; logic [31:0] s_claim, s_ready;
logic s_handback, s_credit_fault, s_reg_credit_fault, s_pacing_fault; logic s_handback, s_credit_fault, s_reg_credit_fault, s_pacing_fault;
logic s_fifo_pending; logic s_fifo_pending;
@@ -39,6 +42,7 @@ module apb4_spi #(
logic [3:0] s_dma_stat; logic [3:0] s_dma_stat;
logic s_unused; logic s_unused;
assign s_cfg = u_ctrl.cfg; 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. // Only personality bits feed the GPIO guard; other fields feed the core/reg blocks.
assign s_unused = ^s_cfg; assign s_unused = ^s_cfg;
assign s_credit_fault = s_reg_credit_fault | s_pacing_fault; assign s_credit_fault = s_reg_credit_fault | s_pacing_fault;
@@ -94,7 +98,9 @@ module apb4_spi #(
.apb4 (apb4), .apb4 (apb4),
.ctrl (u_ctrl) .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 ( spi_master_core u_master_core (
.apb_pending_i (s_fifo_pending), .apb_pending_i (s_fifo_pending),
.clk_i (clk_i), .clk_i (clk_i),
+34 -31
View File
@@ -220,37 +220,40 @@ module sdio_dma #(
.DataWidth (DataWidth), .DataWidth (DataWidth),
.MaxBurstBeats(16) .MaxBurstBeats(16)
) u_dma_axi4_master ( ) u_dma_axi4_master (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (s_read_start_valid), .read_start_valid_i (s_read_start_valid),
.read_start_ready_o (s_read_start_ready), .read_start_ready_o (s_read_start_ready),
.read_addr_i (s_read_addr), .read_addr_i (s_read_addr),
.read_beats_i (s_read_beats), .read_beats_i (s_read_beats),
.read_fixed_i (1'b0), .read_fixed_i (1'b0),
.read_busy_o (s_read_busy), .read_busy_o (s_read_busy),
.read_beat_valid_o (s_read_beat_valid), .read_beat_valid_o (s_read_beat_valid),
.read_beat_ready_i (s_read_beat_ready), .read_beat_ready_i (s_read_beat_ready),
.read_data_o (s_read_data), .read_data_o (s_read_data),
.read_resp_o (s_read_resp), .read_resp_o (s_read_resp),
.read_last_o (s_read_last), .read_last_o (s_read_last),
.read_expected_last_o(s_read_expected_last), .read_expected_last_o (s_read_expected_last),
.read_id_error_o (s_read_id_error), .read_id_error_o (s_read_id_error),
.read_done_o (s_read_done), .read_done_o (s_read_done),
.write_start_valid_i (s_write_start_valid), .write_start_valid_i (s_write_start_valid),
.write_start_ready_o (s_write_start_ready), .write_start_ready_o (s_write_start_ready),
.write_addr_i (s_write_addr), .write_addr_i (s_write_addr),
.write_beats_i (s_write_beats), .write_beats_i (s_write_beats),
.write_fixed_i (1'b0), .write_fixed_i (1'b0),
.write_busy_o (s_write_busy), .write_busy_o (s_write_busy),
.write_data_valid_i (s_write_data_valid), .write_data_valid_i (s_write_data_valid),
.write_data_ready_o (s_write_data_ready), .write_data_ready_o (s_write_data_ready),
.write_data_i (s_write_data), .write_data_i (s_write_data),
.write_strb_i (s_write_strb), .write_strb_i (s_write_strb),
.write_done_o (s_write_done), .write_done_o (s_write_done),
.write_resp_o (s_write_resp), .write_resp_o (s_write_resp),
.write_id_error_o (s_write_id_error), .write_id_error_o (s_write_id_error),
.axi4 (dma_axi4) .origin_enable_i (1'b0),
.read_origin_channel_i (3'd0),
.write_origin_channel_i(3'd0),
.axi4 (dma_axi4)
); );
always_comb begin always_comb begin
+34 -31
View File
@@ -299,37 +299,40 @@ module usb2_dma #(
.DataWidth (DataWidth), .DataWidth (DataWidth),
.MaxBurstBeats(16) .MaxBurstBeats(16)
) u_axi4_master ( ) u_axi4_master (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (s_read_start_valid), .read_start_valid_i (s_read_start_valid),
.read_start_ready_o (s_read_start_ready), .read_start_ready_o (s_read_start_ready),
.read_addr_i (s_read_addr), .read_addr_i (s_read_addr),
.read_beats_i (s_read_beats), .read_beats_i (s_read_beats),
.read_fixed_i (1'b0), .read_fixed_i (1'b0),
.read_busy_o (s_read_busy), .read_busy_o (s_read_busy),
.read_beat_valid_o (s_read_beat_valid), .read_beat_valid_o (s_read_beat_valid),
.read_beat_ready_i (s_read_beat_ready), .read_beat_ready_i (s_read_beat_ready),
.read_data_o (s_read_data), .read_data_o (s_read_data),
.read_resp_o (s_read_resp), .read_resp_o (s_read_resp),
.read_last_o (s_read_last), .read_last_o (s_read_last),
.read_expected_last_o(s_read_expected_last), .read_expected_last_o (s_read_expected_last),
.read_id_error_o (s_read_id_err), .read_id_error_o (s_read_id_err),
.read_done_o (s_read_done), .read_done_o (s_read_done),
.write_start_valid_i (s_write_start_valid), .write_start_valid_i (s_write_start_valid),
.write_start_ready_o (s_write_start_ready), .write_start_ready_o (s_write_start_ready),
.write_addr_i (s_write_addr), .write_addr_i (s_write_addr),
.write_beats_i (s_write_beats), .write_beats_i (s_write_beats),
.write_fixed_i (1'b0), .write_fixed_i (1'b0),
.write_busy_o (s_write_busy), .write_busy_o (s_write_busy),
.write_data_valid_i (s_write_data_valid), .write_data_valid_i (s_write_data_valid),
.write_data_ready_o (s_write_data_ready), .write_data_ready_o (s_write_data_ready),
.write_data_i (s_write_data), .write_data_i (s_write_data),
.write_strb_i (s_write_strb), .write_strb_i (s_write_strb),
.write_done_o (s_write_done), .write_done_o (s_write_done),
.write_resp_o (s_write_resp), .write_resp_o (s_write_resp),
.write_id_error_o (s_write_id_err), .write_id_error_o (s_write_id_err),
.axi4 (dma_axi4) .origin_enable_i (1'b0),
.read_origin_channel_i (3'd0),
.write_origin_channel_i(3'd0),
.axi4 (dma_axi4)
); );
always_comb begin always_comb begin
+4 -2
View File
@@ -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_math.c \
$(ROOT_PATH)/crt/src/hal/jpeg.c \ $(ROOT_PATH)/crt/src/hal/jpeg.c \
$(ROOT_PATH)/crt/src/hal/dvp.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/perf.c \
$(ROOT_PATH)/crt/src/hal/lcd.c \ $(ROOT_PATH)/crt/src/hal/lcd.c \
$(ROOT_PATH)/crt/src/hal/onchip_sram.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 \ 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 \ 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 \ 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 := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source)))
CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c
endif endif
@@ -307,4 +309,4 @@ firmware: $(FIRMWARE_ELF)
firmware asm: | manifest firmware asm: | manifest
.PHONY: FORCE_VERSION .PHONY: FORCE_VERSION
+9
View File
@@ -444,6 +444,15 @@
"kind": "active", "kind": "active",
"public": true, "public": true,
"user_access": "none" "user_access": "none"
},
{
"symbol": "APB4_SPI",
"base": "0x1001D000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
} }
], ],
"legacy_rib": false, "legacy_rib": false,
+9
View File
@@ -63,6 +63,15 @@
/ip/serial/i2s_send.sv /ip/serial/i2s_send.sv
/ip/serial/i2s_core.sv /ip/serial/i2s_core.sv
/ip/serial/apb4_i2s.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_if.sv
/ip/serial/ws2812_reg.sv /ip/serial/ws2812_reg.sv
/ip/serial/ws2812_core.sv /ip/serial/ws2812_core.sv
@@ -4,7 +4,7 @@
"phase": "TINY-R2-P7", "phase": "TINY-R2-P7",
"clock_domain": "system", "clock_domain": "system",
"target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller", "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", "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", "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", "hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active",
@@ -161,7 +161,8 @@
"crypto", "crypto",
"i2s", "i2s",
"sdio", "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", "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", "hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync",
@@ -480,7 +481,8 @@
"ws2812", "ws2812",
"crypto", "crypto",
"i2s", "i2s",
"dvp" "dvp",
"spi"
] ]
}, },
"same_clock_isolation": [ "same_clock_isolation": [
+24 -11
View File
@@ -37,9 +37,9 @@
], ],
"xpi_target": 4, "xpi_target": 4,
"apb_target_base": 5, "apb_target_base": 5,
"decode_error_target": 26, "decode_error_target": 27,
"protocol_error_target": 27, "protocol_error_target": 28,
"target_count": 28 "target_count": 29
}, },
"gpio_pins": 32, "gpio_pins": 32,
"irq_vector_width": 32, "irq_vector_width": 32,
@@ -172,6 +172,12 @@
"regions": [ "regions": [
"APB4_DVP" "APB4_DVP"
] ]
},
{
"name": "spi",
"regions": [
"APB4_SPI"
]
} }
], ],
"interrupts": [ "interrupts": [
@@ -265,6 +271,11 @@
"core_bit": 23, "core_bit": 23,
"signal": "s_crypto_irq" "signal": "s_crypto_irq"
}, },
{
"name": "spi",
"core_bit": 25,
"signal": "s_spi_irq"
},
{ {
"name": "rcu", "name": "rcu",
"core_bit": 31, "core_bit": 31,
@@ -677,8 +688,8 @@
"pin": 27, "pin": 27,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "s_spi_sck",
"oe": "1'b0" "oe": "s_spi_output_enable"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -695,8 +706,8 @@
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "s_spi_mosi",
"oe": "1'b0" "oe": "s_spi_output_enable"
} }
}, },
{ {
@@ -720,7 +731,9 @@
"oe": "1'b1" "oe": "1'b1"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [
"s_spi_miso"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
} }
@@ -734,12 +747,12 @@
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "s_spi_cs_n",
"oe": "1'b0" "oe": "s_spi_output_enable"
} }
} }
], ],
"dma_request_mask": "0x39ff", "dma_request_mask": "0x339ff",
"private_crypto": { "private_crypto": {
"banks": 6, "banks": 6,
"bank_bytes": 4096, "bank_bytes": 4096,
+117 -46
View File
@@ -89,15 +89,15 @@ module retrosoc_tiny (
logic [31:0] s_clkout_control; logic [31:0] s_clkout_control;
logic s_clkout, s_clkout_enable; logic s_clkout, s_clkout_enable;
logic s_clkout_idle_ref; logic s_clkout_idle_ref;
logic [14:0] s_gate_request, s_local_reset, s_actual_gate, s_reset_held, s_reset_ready; logic [16: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_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 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 [4:0] s_periph_fault;
logic s_bus_idle_ref; logic s_bus_idle_ref;
logic s_boot_release_d, s_boot_release_q; logic s_boot_release_d, s_boot_release_q;
logic s_wfi_gate_sys; 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_xpi_idle, s_xpi_idle_ref;
logic s_pwm_write, s_crc_write; logic s_pwm_write, s_crc_write;
logic [ 4:0] s_clock_fault; 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_tx_stall, s_i2s_rx_stall;
logic s_i2s_reset_ready; logic s_i2s_reset_ready;
logic s_dvp_irq, s_dvp_idle, s_dvp_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; logic s_done_pclk_rst_n, s_done_mem_rst_n;
`include "tiny_apb_interfaces.svh" `include "tiny_apb_interfaces.svh"
ahbl_if u_cpu_ahbl_if[2] ( ahbl_if u_cpu_ahbl_if[2] (
@@ -121,7 +135,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_sram_axi4_if[4] ( ) u_sram_axi4_if[4] (
.aclk (s_exec_clk), .aclk (s_exec_clk),
.aresetn(s_sram_rst_n) .aresetn(s_sram_rst_n)
@@ -151,7 +165,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_masters_axi4_if[3] ( ) u_masters_axi4_if[3] (
.aclk (s_exec_clk), .aclk (s_exec_clk),
.aresetn(s_master_rst_n) .aresetn(s_master_rst_n)
@@ -160,7 +174,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_dma_pclk_axi4_if ( ) u_dma_pclk_axi4_if (
.aclk (s_dma_clk), .aclk (s_dma_clk),
.aresetn(s_dma_rst_n) .aresetn(s_dma_rst_n)
@@ -169,7 +183,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_dma_admitted_axi4_if ( ) u_dma_admitted_axi4_if (
.aclk (s_backend_pclk), .aclk (s_backend_pclk),
.aresetn(s_dma_rst_n) .aresetn(s_dma_rst_n)
@@ -178,7 +192,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_sdio_sys_axi4_if ( ) u_sdio_sys_axi4_if (
.aclk (s_sdio_clk), .aclk (s_sdio_clk),
.aresetn(s_sdio_rst_n) .aresetn(s_sdio_rst_n)
@@ -187,7 +201,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] ( ) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (s_exec_clk), .aclk (s_exec_clk),
.aresetn(s_target_rst_n) .aresetn(s_target_rst_n)
@@ -196,7 +210,7 @@ ahbl_if u_cpu_ahbl_if[2] (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_xpi_mem_axi4_if ( ) u_xpi_mem_axi4_if (
.aclk (s_backend_mem_clk), .aclk (s_backend_mem_clk),
.aresetn(s_backend_mem_rst_n) .aresetn(s_backend_mem_rst_n)
@@ -392,11 +406,14 @@ ahbl_if u_cpu_ahbl_if[2] (
.dat_i (s_gate_request[0]), .dat_i (s_gate_request[0]),
.dat_o (s_wfi_gate_sys) .dat_o (s_wfi_gate_sys)
); );
assign s_reset_held[0] = 1'b0; assign s_reset_held[0] = 1'b0;
assign s_reset_ready[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 #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(15) .DATA_WIDTH(17)
) u_gate_feedback ( ) u_gate_feedback (
.clk_i (s_ref_clk), .clk_i (s_ref_clk),
.rst_n_i(s_por_rst_n), .rst_n_i(s_por_rst_n),
@@ -405,7 +422,7 @@ ahbl_if u_cpu_ahbl_if[2] (
); );
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(15) .DATA_WIDTH(17)
) u_held_feedback ( ) u_held_feedback (
.clk_i (s_ref_clk), .clk_i (s_ref_clk),
.rst_n_i(s_por_rst_n), .rst_n_i(s_por_rst_n),
@@ -414,7 +431,7 @@ ahbl_if u_cpu_ahbl_if[2] (
); );
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(15) .DATA_WIDTH(17)
) u_ready_feedback ( ) u_ready_feedback (
.clk_i (s_ref_clk), .clk_i (s_ref_clk),
.rst_n_i(s_por_rst_n), .rst_n_i(s_por_rst_n),
@@ -423,7 +440,7 @@ ahbl_if u_cpu_ahbl_if[2] (
); );
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(15) .DATA_WIDTH(17)
) u_disabled_sync ( ) u_disabled_sync (
.clk_i (s_exec_clk), .clk_i (s_exec_clk),
.rst_n_i(s_fabric_rst_n), .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]) .crc_idle_o (s_periph_idle[12])
); );
assign s_periph_idle[0] = s_cpu_bus_idle; 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[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 #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(15) .DATA_WIDTH(17)
) u_periph_idle_sync ( ) u_periph_idle_sync (
.clk_i (s_ref_clk), .clk_i (s_ref_clk),
.rst_n_i(s_por_rst_n), .rst_n_i(s_por_rst_n),
@@ -706,7 +726,8 @@ ahbl_if u_cpu_ahbl_if[2] (
); );
tiny_axi4_async_bridge #( tiny_axi4_async_bridge #(
.DataWidth(32), .DataWidth(32),
.IdWidth (1) .IdWidth (1),
.UserWidth(4)
) u_dma_cdc ( ) u_dma_cdc (
.src_clk_i (s_dma_clk), .src_clk_i (s_dma_clk),
.src_rst_n_i (s_dma_rst_n), .src_rst_n_i (s_dma_rst_n),
@@ -737,7 +758,8 @@ ahbl_if u_cpu_ahbl_if[2] (
); );
tiny_axi4_async_bridge #( tiny_axi4_async_bridge #(
.DataWidth(32), .DataWidth(32),
.IdWidth (1) .IdWidth (1),
.UserWidth(4)
) u_xpi_mem_cdc ( ) u_xpi_mem_cdc (
.src_clk_i (s_exec_clk), .src_clk_i (s_exec_clk),
.src_rst_n_i (s_fabric_rst_n), .src_rst_n_i (s_fabric_rst_n),
@@ -800,7 +822,7 @@ axi4_error_slave #(
.PllPresent (TinyPllPresent), .PllPresent (TinyPllPresent),
.PllProfiles(TinyPllPresent), .PllProfiles(TinyPllPresent),
.XinHzValue (`SOC_EXT_CLK_HZ), .XinHzValue (`SOC_EXT_CLK_HZ),
.TargetMask (15'h07fff) .TargetMask (17'h17fff)
) u_sysctrl ( ) u_sysctrl (
.clk_i (s_exec_clk), .clk_i (s_exec_clk),
.rst_n_i (s_sysctrl_rst_n), .rst_n_i (s_sysctrl_rst_n),
@@ -860,7 +882,7 @@ axi4_error_slave #(
.ref_clk_i (clk_i), .ref_clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.idle_i (s_clock_safe), .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), .warm_reset_i (u_wdg_if.reset_req_o),
.system_request_i (s_system_reset), .system_request_i (s_system_reset),
.command_ack_i (s_cpu_clock_idle && s_target_idle[`RETROSOC_TINY__TARGET_SYSCTRL]), .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), .HasPullUp (gpio_pad_caps_pkg::HasPullUp),
.HasPullDown (gpio_pad_caps_pkg::HasPullDown) .HasPullDown (gpio_pad_caps_pkg::HasPullDown)
) u_gpio ( ) u_gpio (
.clk_i (s_gpio_clk), .clk_i (s_gpio_clk),
.rst_n_i (s_gpio_rst_n), .rst_n_i (s_gpio_rst_n),
.apb4 (u_gpio_apb4_if), .apb4 (u_gpio_apb4_if),
.gpio (u_gpio_if), .gpio (u_gpio_if),
.user_gpio(u_user_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 ( gpio_pad_bridge u_gpio_pads (
.inner(u_gpio_if), .inner(u_gpio_if),
@@ -1002,25 +1062,36 @@ cdc_sync #(
.lifecycle_reset_ready_o(s_dvp_reset_ready) .lifecycle_reset_ready_o(s_dvp_reset_ready)
); );
apb4_dma #( apb4_dma #(
.NumChannels (8), .NumChannels (8),
.MaxBurstBeats(16), .MaxBurstBeats (16),
.FifoDepth (32), .FifoDepth (32),
.RequestMask (16'h39ff), .RequestMask (32'h0003_39ff),
.EnableStreams(1'b1) .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 ( ) u_dma (
.lifecycle_idle_o(s_periph_idle[9]), .lifecycle_idle_o (s_periph_idle[9]),
.clk_i (s_dma_clk), .clk_i (s_dma_clk),
.rst_n_i (s_dma_rst_n), .rst_n_i (s_dma_rst_n),
.dma_xfer_done_o (s_dma_done), .dma_xfer_done_o (s_dma_done),
.irq_o (s_dma_irq), .irq_o (s_dma_irq),
.hw_trg (u_dma_req_if), .hw_trg (u_dma_req_if),
.apb4 (u_dma_apb4_if), .apb4 (u_dma_apb4_if),
.axi4 (u_dma_pclk_axi4_if), .axi4 (u_dma_pclk_axi4_if),
.i2s_tx_axis (u_i2s_tx_axis_if), .i2s_tx_axis (u_i2s_tx_axis_if),
.i2s_rx_axis (u_i2s_rx_axis_if), .i2s_rx_axis (u_i2s_rx_axis_if),
.dvp_rx_axis (u_dvp_rx_axis_if), .dvp_rx_axis (u_dvp_rx_axis_if),
.crypto_in_axis (u_crypto_tx_axis_if), .crypto_in_axis (u_crypto_tx_axis_if),
.crypto_out_axis (u_crypto_rx_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_tx_proc = !s_i2s_tx_stall;
assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall; assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall;
+27 -15
View File
@@ -6,15 +6,19 @@
// error; it never replays it when the destination returns. The SYS endpoint // error; it never replays it when the destination returns. The SYS endpoint
// remains clocked so a stopped/disabled peripheral is still reachable. // remains clocked so a stopped/disabled peripheral is still reachable.
module tiny_apb_async_bridge ( module tiny_apb_async_bridge (
input logic src_clk_i, input logic src_clk_i,
input logic src_rst_n_i, input logic src_rst_n_i,
input logic dst_clk_i, input logic dst_clk_i,
input logic dst_rst_n_i, input logic dst_rst_n_i,
input logic disabled_i, input logic disabled_i,
output logic idle_o, input logic [1:0] src_origin_i,
output logic ready_o, input logic [2:0] src_channel_i,
apb4_if.slave src_apb4, output logic idle_o,
apb4_if.master dst_apb4 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] { typedef enum logic [1:0] {
SrcIdle, SrcIdle,
@@ -30,19 +34,19 @@ module tiny_apb_async_bridge (
} dst_state_e; } dst_state_e;
typedef struct packed { typedef struct packed {
src_state_e state; src_state_e state;
logic [71:0] req; logic [76:0] req;
logic [32:0] resp; logic [32:0] resp;
} src_t; } src_t;
typedef struct packed { typedef struct packed {
dst_state_e state; dst_state_e state;
logic [71:0] req; logic [76:0] req;
logic [32:0] resp; logic [32:0] resp;
} dst_t; } dst_t;
src_t s_src_d, s_src_q; src_t s_src_d, s_src_q;
dst_t s_dst_d, s_dst_q; dst_t s_dst_d, s_dst_q;
logic s_link_src_rst_n, s_link_dst_rst_n; 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 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; logic [32:0] s_resp_data;
cdc_reset_barrier #( cdc_reset_barrier #(
@@ -56,7 +60,7 @@ module tiny_apb_async_bridge (
.rst_b_n_o(s_link_dst_rst_n) .rst_b_n_o(s_link_dst_rst_n)
); );
cdc_2phase #( cdc_2phase #(
.DATA_WIDTH(72) .DATA_WIDTH(77)
) u_req_cdc ( ) u_req_cdc (
.src_clk_i (src_clk_i), .src_clk_i (src_clk_i),
.src_rst_n_i(s_link_src_rst_n), .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.pslverr = s_src_q.resp[32];
assign src_apb4.prdata = s_src_q.resp[31:0]; assign src_apb4.prdata = s_src_q.resp[31:0];
assign {dst_apb4.paddr, dst_apb4.pprot, dst_apb4.pwrite, 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 && assign dst_apb4.psel = s_link_dst_rst_n &&
((s_dst_q.state == DstSetup) || (s_dst_q.state == DstAccess)); ((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_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 always_comb begin
s_src_d = s_src_q; s_src_d = s_src_q;
@@ -100,7 +106,13 @@ module tiny_apb_async_bridge (
SrcIdle: SrcIdle:
if (src_apb4.psel && src_apb4.penable) begin if (src_apb4.psel && src_apb4.penable) begin
s_src_d.req = { 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.resp = {1'b1, 32'd0};
s_src_d.state = disabled_i ? SrcResponse : SrcSend; s_src_d.state = disabled_i ? SrcResponse : SrcSend;
+20 -4
View File
@@ -4,10 +4,12 @@
// APB setup/access are separate cycles; rejected bursts never assert PSEL. // APB setup/access are separate cycles; rejected bursts never assert PSEL.
module tiny_axi42apb4 ( module tiny_axi42apb4 (
input logic clk_i, input logic clk_i,
input logic rst_n_i, input logic rst_n_i,
axi4_if.slave axi4, axi4_if.slave axi4,
apb4_if.master apb4 apb4_if.master apb4,
output logic [1:0] origin_o,
output logic [2:0] channel_o
); );
typedef enum logic [2:0] { typedef enum logic [2:0] {
Idle, Idle,
@@ -30,6 +32,7 @@ module tiny_axi42apb4 (
logic [1:0] s_resp_d, s_resp_q; logic [1:0] s_resp_d, s_resp_q;
logic [7:0] s_len_d, s_len_q; logic [7:0] s_len_d, s_len_q;
logic [7:0] s_beat_d, s_beat_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 s_access, s_enable, s_done, s_err;
logic [31:0] s_read_data; logic [31:0] s_read_data;
@@ -61,6 +64,8 @@ module tiny_axi42apb4 (
assign apb4.pprot = 3'd0; assign apb4.pprot = 3'd0;
assign apb4.psel = s_access; assign apb4.psel = s_access;
assign apb4.penable = s_enable; 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 always_comb begin
s_state_d = s_state_q; s_state_d = s_state_q;
@@ -73,6 +78,7 @@ module tiny_axi42apb4 (
s_data_d = s_data_q; s_data_d = s_data_q;
s_len_d = s_len_q; s_len_d = s_len_q;
s_beat_d = s_beat_q; s_beat_d = s_beat_q;
s_user_d = s_user_q;
unique case (s_state_q) unique case (s_state_q)
Idle: begin Idle: begin
s_beat_d = '0; s_beat_d = '0;
@@ -82,6 +88,7 @@ module tiny_axi42apb4 (
s_addr_d = axi4.araddr; s_addr_d = axi4.araddr;
s_id_d = axi4.arid; s_id_d = axi4.arid;
s_write_d = 1'b0; s_write_d = 1'b0;
s_user_d = axi4.aruser;
s_len_d = axi4.arlen; s_len_d = axi4.arlen;
if ((axi4.arlen != 8'd0) || (axi4.arburst[1]) || axi4.arlock || if ((axi4.arlen != 8'd0) || (axi4.arburst[1]) || axi4.arlock ||
(axi4.arsize > 3'd2) || (axi4.araddr[1:0] != 2'd0)) begin (axi4.arsize > 3'd2) || (axi4.araddr[1:0] != 2'd0)) begin
@@ -92,6 +99,7 @@ module tiny_axi42apb4 (
s_addr_d = axi4.awaddr; s_addr_d = axi4.awaddr;
s_id_d = axi4.awid; s_id_d = axi4.awid;
s_write_d = 1'b1; s_write_d = 1'b1;
s_user_d = axi4.awuser;
s_len_d = axi4.awlen; s_len_d = axi4.awlen;
if ((axi4.awlen != 8'd0) || (axi4.awburst[1]) || axi4.awlock || if ((axi4.awlen != 8'd0) || (axi4.awburst[1]) || axi4.awlock ||
(axi4.awsize > 3'd2) || (axi4.awaddr[1:0] != 2'd0)) begin (axi4.awsize > 3'd2) || (axi4.awaddr[1:0] != 2'd0)) begin
@@ -210,4 +218,12 @@ module tiny_axi42apb4 (
.dat_i (s_beat_d), .dat_i (s_beat_d),
.dat_o (s_beat_q) .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 endmodule
+1 -1
View File
@@ -23,7 +23,7 @@ module tiny_axi4_admission (
logic [2:0] prot; logic [2:0] prot;
logic [3:0] qos; logic [3:0] qos;
logic [3:0] region; logic [3:0] region;
logic [0:0] user; logic [3:0] user;
} address_t; } address_t;
address_t s_aw_d, s_aw_q, s_ar_d, s_ar_q; 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; logic [1:0] s_pending_d, s_pending_q, s_sent_d, s_sent_q;
+5 -5
View File
@@ -29,10 +29,10 @@ module tiny_axi4_fabric (
logic s_cpu_prefer_write_d, s_cpu_prefer_write_q; logic s_cpu_prefer_write_d, s_cpu_prefer_write_q;
logic [2:0][4:0] s_read_target, s_write_target; 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] 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][39:0] s_rdata;
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 3:0] s_bdata; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 6:0] s_bdata;
logic [ 2:0][36:0] s_read_return; logic [ 2:0][39:0] s_read_return;
logic [ 2:0][ 3:0] s_write_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_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid;
logic [2:0] s_arvalid, s_awvalid; logic [2:0] s_arvalid, s_awvalid;
logic [1:0][63:0] s_wait_d, s_wait_q; logic [1:0][63:0] s_wait_d, s_wait_q;
@@ -191,7 +191,7 @@ module tiny_axi4_fabric (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_routes[3] ( ) u_routes[3] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
+34 -20
View File
@@ -36,7 +36,7 @@ module tiny_axi4_target (
logic [3:0] qos; logic [3:0] qos;
logic [3:0] region; logic [3:0] region;
logic id; logic id;
logic user; logic [3:0] user;
} addr_t; } addr_t;
typedef struct packed { typedef struct packed {
state_e state; state_e state;
@@ -56,20 +56,21 @@ module tiny_axi4_target (
logic [31:0] s_req_addr; logic [31:0] s_req_addr;
logic s_req_id; logic s_req_id;
addr_t s_read_addr[3], s_write_addr[3]; 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][31:0] s_wdata;
logic [2:0][ 3:0] s_wstrb; logic [2:0][ 3:0] s_wstrb;
logic [2:0] s_owner_req, s_grant, s_prefer_write_d, s_prefer_write_q; logic [2:0] s_owner_req, s_grant, s_prefer_write_d, s_prefer_write_q;
logic [1:0] s_selected; logic [1:0] s_selected;
logic s_valid, s_selected_write, s_accept, s_terminal; logic s_valid, s_selected_write, s_accept, s_terminal;
logic s_r_valid, s_r_ready, s_b_valid, s_b_ready; logic s_r_valid, s_r_ready, s_b_valid, s_b_ready;
logic [36:0] s_r_data; logic [39:0] s_r_data;
logic [ 3:0] s_b_data; logic [ 6:0] s_b_data;
logic s_read_capture, s_write_capture; logic s_read_capture, s_write_capture;
logic s_abort_read_valid, s_abort_write_valid; logic s_abort_read_valid, s_abort_write_valid;
logic s_resp_active, s_rin_valid, s_bin_valid; logic s_resp_active, s_rin_valid, s_bin_valid;
logic [36:0] s_rin_data; logic [39:0] s_rin_data;
logic [ 3:0] s_bin_data; logic [ 6:0] s_bin_data;
assign s_saved_addr = s_req_q.addr; assign s_saved_addr = s_req_q.addr;
assign s_req_addr = s_saved_addr.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; s_req_q.write_done && !s_req_q.b_captured;
assign s_terminal = s_resp_active && assign s_terminal = s_resp_active &&
(s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (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_valid_o = s_terminal && s_req_q.error_seen;
assign fault_addr_o = s_req_q.error_addr; assign fault_addr_o = s_req_q.error_addr;
assign fault_master_o = s_req_q.owner; 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; !s_req_q.write && target.rvalid) || s_abort_read_valid;
assign s_rin_data = s_abort_read_valid ? assign s_rin_data = s_abort_read_valid ?
{s_req_id, 32'd0, 2'b11, {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}; {target.rid, target.rdata, target.rresp, target.rlast, target.ruser};
assign s_bin_valid = ((s_req_q.state == Response) && enable_i && assign s_bin_valid = ((s_req_q.state == Response) && enable_i &&
s_req_q.write && target.bvalid) || s_abort_write_valid; 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}; {target.bid, target.bresp, target.buser};
assign s_read_capture = s_rin_valid && s_r_ready; assign s_read_capture = s_rin_valid && s_r_ready;
assign s_write_capture = s_bin_valid && s_b_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.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; assign target.bready = (s_req_q.state == Response) && enable_i && s_req_q.write && s_b_ready;
spill_register #( spill_register #(
.DATA_WIDTH(37) .DATA_WIDTH(40)
) u_read_response ( ) u_read_response (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(rst_n_i), .rst_n_i(rst_n_i),
@@ -231,7 +232,7 @@ module tiny_axi4_target (
.data_o (s_r_data) .data_o (s_r_data)
); );
spill_register #( spill_register #(
.DATA_WIDTH(4) .DATA_WIDTH(7)
) u_write_response ( ) u_write_response (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(rst_n_i), .rst_n_i(rst_n_i),
@@ -248,11 +249,24 @@ module tiny_axi4_target (
unique case (s_req_q.state) unique case (s_req_q.state)
Idle: Idle:
if (s_accept) begin if (s_accept) begin
s_req_d = '0; s_req_d = '0;
s_req_d.state = Address; s_req_d.state = Address;
s_req_d.owner = s_selected; s_req_d.owner = s_selected;
s_req_d.write = s_selected_write; s_req_d.write = s_selected_write;
s_req_d.addr = s_selected_addr; 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; s_req_d.read_addr = s_selected_addr.addr;
end end
Address: begin Address: begin
@@ -286,18 +300,18 @@ module tiny_axi4_target (
s_req_d.read_count = s_req_q.read_count + 9'd1; s_req_d.read_count = s_req_q.read_count + 9'd1;
if (s_saved_addr.burst == 2'b01) if (s_saved_addr.burst == 2'b01)
s_req_d.read_addr = s_req_q.read_addr + (32'd1 << s_saved_addr.size); 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_seen = 1'b1;
s_req_d.error_addr = s_req_q.read_addr; 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
end end
if (s_write_capture) begin if (s_write_capture) begin
s_req_d.b_captured = 1'b1; 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_seen = 1'b1;
s_req_d.error_addr = s_req_addr; 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 end
end end
+1 -1
View File
@@ -63,7 +63,7 @@ module tiny_cpu_mem #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) u_slow_axi4_if ( ) u_slow_axi4_if (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
+14 -14
View File
@@ -9,19 +9,19 @@ module tiny_peripheral_control (
input logic warm_reset_i, input logic warm_reset_i,
input logic apply_i, input logic apply_i,
input logic reset_command_i, input logic reset_command_i,
input logic [14:0] request_i, input logic [16:0] request_i,
input logic [14:0] idle_i, input logic [16:0] idle_i,
input logic bus_idle_i, input logic bus_idle_i,
input logic [14:0] gate_actual_i, input logic [16:0] gate_actual_i,
input logic [14:0] reset_held_i, input logic [16:0] reset_held_i,
input logic [14:0] reset_ready_i, input logic [16:0] reset_ready_i,
input logic [31:0] timeout_i, input logic [31:0] timeout_i,
output logic [14:0] gate_o, output logic [16:0] gate_o,
output logic [14:0] reset_o, output logic [16:0] reset_o,
output logic block_o, output logic block_o,
output logic done_o, output logic done_o,
output logic [ 4:0] fault_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] { typedef enum logic [2:0] {
Idle, Idle,
@@ -34,20 +34,20 @@ module tiny_peripheral_control (
typedef struct packed { typedef struct packed {
state_e state; state_e state;
logic reset_command; 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 [31:0] count;
logic done; logic done;
logic [4:0] fault; logic [4:0] fault;
} control_t; } control_t;
control_t s_ctrl_d, s_ctrl_q; 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 s_affected = s_ctrl_q.affected;
assign gate_o = s_ctrl_q.gates; assign gate_o = s_ctrl_q.gates;
assign reset_o = s_ctrl_q.resets; assign reset_o = s_ctrl_q.resets;
assign reset_done_o = s_ctrl_q.reset_done; assign reset_done_o = s_ctrl_q.reset_done;
// Arming the WFI leaf permission must not pause an unrelated DMA job. // 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. // 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 done_o = s_ctrl_q.done;
assign fault_o = s_ctrl_q.fault; assign fault_o = s_ctrl_q.fault;
always_comb begin always_comb begin
@@ -74,8 +74,8 @@ module tiny_peripheral_control (
end end
end end
Drain: begin Drain: begin
if (s_ctrl_q.count >= 16 && ((s_affected[14:1] == 0) || (bus_idle_i && idle_i[9])) && if (s_ctrl_q.count >= 16 && ((s_affected[16:1] == 0) || (bus_idle_i && idle_i[9])) &&
((idle_i[14:1] & s_affected[14:1]) == s_affected[14:1])) begin ((idle_i[16:1] & s_affected[16:1]) == s_affected[16:1])) begin
s_ctrl_d.count = 0; s_ctrl_d.count = 0;
if (s_ctrl_q.reset_command) begin if (s_ctrl_q.reset_command) begin
s_ctrl_d.resets = s_ctrl_q.request_mask; s_ctrl_d.resets = s_ctrl_q.request_mask;
@@ -90,7 +90,7 @@ module tiny_peripheral_control (
s_ctrl_d.state = Idle; s_ctrl_d.state = Idle;
end end
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: Hold:
if ((reset_held_i & s_ctrl_q.request_mask) == s_ctrl_q.request_mask) begin if ((reset_held_i & s_ctrl_q.request_mask) == s_ctrl_q.request_mask) begin
s_ctrl_d.resets = 0; s_ctrl_d.resets = 0;
+32 -28
View File
@@ -7,7 +7,7 @@ module tiny_sysctrl #(
parameter bit PllPresent = 1'b0, parameter bit PllPresent = 1'b0,
parameter bit PllProfiles = 1'b0, parameter bit PllProfiles = 1'b0,
parameter int unsigned XinHzValue = 24_000_000, 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 clk_i,
input logic rst_n_i, input logic rst_n_i,
@@ -37,11 +37,11 @@ module tiny_sysctrl #(
output logic [31:0] clock_timeout_o, output logic [31:0] clock_timeout_o,
input logic periph_done_i, input logic periph_done_i,
input logic [ 4:0] periph_fault_i, input logic [ 4:0] periph_fault_i,
input logic [14:0] gate_actual_i, input logic [16:0] gate_actual_i,
input logic [14:0] reset_done_i, input logic [16:0] reset_done_i,
output logic periph_apply_o, output logic periph_apply_o,
output logic periph_reset_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, output logic [31:0] clkout_control_o,
input logic clkout_idle_i, input logic clkout_idle_i,
output logic perf_enable_o, output logic perf_enable_o,
@@ -106,7 +106,7 @@ module tiny_sysctrl #(
logic [1:0] s_clk_latched_d, s_clk_latched_q; logic [1:0] s_clk_latched_d, s_clk_latched_q;
logic s_system_reset_d, s_system_reset_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 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 [3:0] s_active_cmd_d, s_active_cmd_q;
logic s_clock_apply_d, s_clock_apply_q; logic s_clock_apply_d, s_clock_apply_q;
logic s_rcu_busy_d, s_rcu_busy_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.psel = apb4.psel && s_rcu_selected;
assign u_rcu_src.penable = apb4.penable; assign u_rcu_src.penable = apb4.penable;
tiny_apb_async_bridge u_rcu_cdc ( tiny_apb_async_bridge u_rcu_cdc (
.src_clk_i (clk_i), .src_clk_i (clk_i),
.src_rst_n_i(rst_n_i), .src_rst_n_i (rst_n_i),
.dst_clk_i (clk_ref_i), .dst_clk_i (clk_ref_i),
.dst_rst_n_i(rst_ref_n_i), .dst_rst_n_i (rst_ref_n_i),
.disabled_i (1'b0), .disabled_i (1'b0),
.idle_o (), .src_origin_i (2'd0),
.ready_o (rcu_link_ready_o), .src_channel_i(3'd0),
.src_apb4 (u_rcu_src), .dst_origin_o (),
.dst_apb4 (u_rcu_dst) .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_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; 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_read_data = s_gate_req_q;
s_rcu_writable = 1'b1; s_rcu_writable = 1'b1;
end end
GateStatus: s_rcu_read_data = {17'd0, gate_actual_i}; GateStatus: s_rcu_read_data = {15'd0, gate_actual_i};
ResetRequest: begin ResetRequest: begin
s_rcu_read_data = s_rst_req_q; s_rcu_read_data = s_rst_req_q;
s_rcu_writable = 1'b1; s_rcu_writable = 1'b1;
@@ -334,7 +338,7 @@ module tiny_sysctrl #(
MemHz: s_rcu_read_data = s_mem_hz_q; MemHz: s_rcu_read_data = s_mem_hz_q;
PclkHz: s_rcu_read_data = s_pclk_hz_q; PclkHz: s_rcu_read_data = s_pclk_hz_q;
ClintHz: s_rcu_read_data = 32'd1_000_000; 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 ClkoutControl: begin
s_rcu_read_data = s_clkout_control_q; s_rcu_read_data = s_clkout_control_q;
s_rcu_writable = 1'b1; 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; 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; TimeoutRefCycles: s_rcu_config_err = s_rcu_busy_q || u_rcu_dst.pwdata == 0;
GateRequest: 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: 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; ResetCause: s_rcu_config_err = u_rcu_dst.pwdata[31:6] != 0;
RcuFault: s_rcu_config_err = u_rcu_dst.pwdata[31:5] != 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; 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_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 == 0 || (s_clk_req_q == 1 && !PllPresent) ||
(s_clk_req_q >= 2 && PllPresent && PllProfiles)); (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: 32'd4:
s_rcu_config_err |= s_rst_req_q == 0 || 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 & ~{15'd0, TargetMask[16:1], 1'b0}) != 0 ||
(s_rst_req_q & {17'd0, gate_actual_i}) != 0; (s_rst_req_q & {15'd0, gate_actual_i}) != 0;
32'd8: begin 32'd8: begin
end end
default: s_rcu_config_err = 1'b1; default: s_rcu_config_err = 1'b1;
@@ -432,16 +436,16 @@ module tiny_sysctrl #(
s_rcu_busy_d = 1'b1; s_rcu_busy_d = 1'b1;
s_active_cmd_d = 4'd8; s_active_cmd_d = 4'd8;
end else if (TargetMask != 0 && u_rcu_dst.pwdata == 2 && end else if (TargetMask != 0 && u_rcu_dst.pwdata == 2 &&
(s_gate_req_q & ~{17'd0, TargetMask}) == 0) begin (s_gate_req_q & ~{15'd0, TargetMask}) == 0) begin
s_periph_mask_d = s_gate_req_q[14:0]; s_periph_mask_d = s_gate_req_q[16:0];
s_periph_reset_d = 1'b0; s_periph_reset_d = 1'b0;
s_periph_apply_d = 1'b1; s_periph_apply_d = 1'b1;
s_rcu_busy_d = 1'b1; s_rcu_busy_d = 1'b1;
s_active_cmd_d = 4'd2; s_active_cmd_d = 4'd2;
end else if (u_rcu_dst.pwdata == 4 && s_rst_req_q != 0 && 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 & ~{15'd0, TargetMask[16:1], 1'b0}) == 0 &&
(s_rst_req_q & {17'd0, gate_actual_i}) == 0) begin (s_rst_req_q & {15'd0, gate_actual_i}) == 0) begin
s_periph_mask_d = s_rst_req_q[14:0]; s_periph_mask_d = s_rst_req_q[16:0];
s_periph_reset_d = 1'b1; s_periph_reset_d = 1'b1;
s_periph_apply_d = 1'b1; s_periph_apply_d = 1'b1;
s_reset_done_d = 0; s_reset_done_d = 0;
@@ -501,7 +505,7 @@ module tiny_sysctrl #(
if (periph_done_i) begin if (periph_done_i) begin
s_rcu_busy_d = 1'b0; s_rcu_busy_d = 1'b0;
s_active_cmd_d = 0; 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; if (s_periph_reset_q && reset_done_i != 0) s_reset_cause_d[5] = 1'b1;
s_rcu_irq_state_d[0] = 1'b1; s_rcu_irq_state_d[0] = 1'b1;
end end
@@ -607,7 +611,7 @@ module tiny_sysctrl #(
.dat_o (s_clk_latched_q) .dat_o (s_clk_latched_q)
); );
dffr #( dffr #(
.DATA_WIDTH(21) .DATA_WIDTH(23)
) u_periph_command ( ) u_periph_command (
.clk_i (clk_ref_i), .clk_i (clk_ref_i),
.rst_n_i(rst_ref_n_i), .rst_n_i(rst_ref_n_i),
+2 -1
View File
@@ -140,7 +140,8 @@ def validate(document_path: Path, root: Path) -> None:
raise ValueError("unrecognized generated APB crossing contract") raise ValueError("unrecognized generated APB crossing contract")
expected = {"aon": ["clint", "archinfo"], "memory": ["xpi"], expected = {"aon": ["clint", "archinfo"], "memory": ["xpi"],
"peripheral": ["gpio", "uart0", "timer0", "timer1", "i2c0", "dma", "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 if (generated.get("destinations") != expected or
generated.get("same_clock_isolation") != ["sdio"] or generated.get("same_clock_isolation") != ["sdio"] or
generated.get("native_system") != ["sysctrl", "sram"] or generated.get("native_system") != ["sysctrl", "sram"] or
+22 -9
View File
@@ -18,15 +18,15 @@ from scripts.setup_helpers import atomic_write # noqa: E402
TINY = ROOT / "rtl/tiny" TINY = ROOT / "rtl/tiny"
RCU_TARGETS = {"gpio": 1, "uart0": 2, "i2c0": 3, "timer0": 4, "timer1": 5, RCU_TARGETS = {"gpio": 1, "uart0": 2, "i2c0": 3, "timer0": 4, "timer1": 5,
"pwm": 6, "i2s": 7, "sdio": 8, "dma": 9, "ws2812": 10, "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: def read_topology(path: Path, memory_map: Path) -> dict:
document = json.loads(path.read_text()) document = json.loads(path.read_text())
regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]} 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 if (document.get("schema_version") != 1 or document.get("dma_channels") != 8
or document.get("dma_request_mask") != "0x39ff"): or document.get("dma_request_mask") != "0x339ff"):
raise ValueError("Tiny P9 requires eight DMA channels and request mask 0x39ff") raise ValueError("Tiny SPI-P3 requires eight DMA channels and request mask 0x339ff")
if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2, if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2,
"data_outstanding": 1}: "data_outstanding": 1}:
raise ValueError("Tiny P4 requires independent I/D and one ordered data operation") 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, "cpu_combined_outstanding": 1, "dma_direction_outstanding": 1,
"sdio_direction_outstanding": 1, "target_combined_outstanding": 1, "sdio_direction_outstanding": 1, "target_combined_outstanding": 1,
"sram_targets": [0, 1, 2, 3], "xpi_target": 4, "apb_target_base": 5, "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: 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") raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs")
names: set[str] = set() names: set[str] = set()
@@ -65,10 +65,10 @@ def read_topology(path: Path, memory_map: Path) -> dict:
assigned.add(region) assigned.add(region)
if assigned != {r for r in regions if r.startswith("APB4_")}: if assigned != {r for r in regions if r.startswith("APB4_")}:
raise ValueError("Tiny topology must route every APB region") raise ValueError("Tiny topology must route every APB region")
if len(document["apb_targets"]) != 21: if len(document["apb_targets"]) != 22:
raise ValueError("Tiny P9 requires twenty-one independent APB targets") raise ValueError("Tiny SPI-P3 requires twenty-two independent APB targets")
target_names = [target["name"] for target in document["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): for name in target_names):
raise ValueError("Tiny APB target names must be unique identifiers") raise ValueError("Tiny APB target names must be unique identifiers")
if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)): 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_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_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_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_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n"
f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}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") 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: if target_bit is not None:
declarations.append(f"logic {ip_clock};") declarations.append(f"logic {ip_clock};")
declarations.append(f"logic s_{name}_rst_n;") 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));") declarations.append(f"apb4_if u_{name}_apb4_if (.pclk({ip_clock}), .presetn(s_{name}_rst_n));")
if target_bit is None: if target_bit is None:
reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];") 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;") 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 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" 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 (" bridges.append(f"tiny_axi42apb4 u_{name}_bridge ("
f".clk_i(s_exec_clk), .rst_n_i(s_fabric_rst_n), " 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: if crossed:
disabled = f"s_disabled_sys[{target_bit}]" if target_bit is not None else "1'b0" 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));") 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 (" 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".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".dst_clk_i({ip_clock}), .dst_rst_n_i(s_{name}_rst_n), "
f".disabled_i({disabled}), " 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".idle_o(s_apb_cdc_idle[{index}]), "
f".ready_o(s_apb_cdc_ready[{index}]), " f".ready_o(s_apb_cdc_ready[{index}]), "
f".src_apb4({endpoint}), .dst_apb4(u_{name}_apb4_if));") f".src_apb4({endpoint}), .dst_apb4(u_{name}_apb4_if));")
+1
View File
@@ -33,6 +33,7 @@ TEST_SOURCES = (
"crt/src/hal/usb2_math.c", "crt/src/hal/usb2_math.c",
"crt/src/hal/spisd_math.c", "crt/src/hal/spisd_math.c",
"crt/src/hal/i2s_math.c", "crt/src/hal/i2s_math.c",
"crt/src/hal/spi_math.c",
"crt/src/hal/dma_math.c", "crt/src/hal/dma_math.c",
"crt/src/hal/dma.c", "crt/src/hal/dma.c",
"crt/src/hal/dvp.c", "crt/src/hal/dvp.c",
+50
View File
@@ -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())
+125
View File
@@ -30,6 +30,7 @@
#include <retrosoc/hal/resource.h> #include <retrosoc/hal/resource.h>
#include <retrosoc/hal/sdram.h> #include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/sdio.h> #include <retrosoc/hal/sdio.h>
#include <retrosoc/hal/spi.h>
#include <retrosoc/hal/spisd.h> #include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/timer.h> #include <retrosoc/hal/timer.h>
#include <retrosoc/hal/uart.h> #include <retrosoc/hal/uart.h>
@@ -1178,6 +1179,128 @@ static int test_tiny_dma_config_validation(void) {
return 0; 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, &divider, &actual_hz) != RS_OK) ||
(divider != 12U) || (actual_hz != 1000000U)) {
return 1;
}
if ((rs_spi_divider_from_hz(24000000U, 1100000U, &divider, &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, &divider, &actual_hz) != RS_OK) ||
(divider != 1U) || (actual_hz != 12000000U)) {
return 3;
}
if ((rs_spi_divider_from_hz(24000000U, 13000000U, &divider, &actual_hz) != RS_OK) ||
(divider != 1U) || (actual_hz != 12000000U)) {
return 4;
}
if ((rs_spi_divider_from_hz(131070U, 1U, &divider, &actual_hz) != RS_OK) ||
(divider != 65535U) || (actual_hz != 1U)) {
return 5;
}
/* Divider 65536 cannot be represented. */
if (rs_spi_divider_from_hz(131072U, 1U, &divider, &actual_hz) != RS_ENOTSUP) {
return 6;
}
if ((rs_spi_divider_from_hz(0U, 1000000U, &divider, &actual_hz) != RS_EINVAL) ||
(rs_spi_divider_from_hz(24000000U, 0U, &divider, &actual_hz) != RS_EINVAL) ||
(rs_spi_divider_from_hz(24000000U, 1000000U, NULL, &actual_hz) != RS_EINVAL) ||
(rs_spi_divider_from_hz(24000000U, 1000000U, &divider, 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_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_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, 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_gpio_helpers(),
test_dma_config_validation(), test_dma_config_validation(),
test_tiny_dma_config_validation(), test_tiny_dma_config_validation(),
test_spi_divider_from_hz(),
test_tiny_dma_spi_aperture(),
test_dvp_hal(), test_dvp_hal(),
test_p9_frame_layout(), test_p9_frame_layout(),
test_p10_frame_statistics(), test_p10_frame_statistics(),
+334
View File
@@ -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
+3
View File
@@ -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 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_begin = 0, model_enable = 0, model_abort = 0;
logic model_segment = 0, model_pause = 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 [15:0] model_divider = 1, model_setup = 1, model_hold = 1, model_inactive = 1;
logic [8:0] model_faults; logic [8:0] model_faults;
logic cpol = 0, cpha = 0, lsb = 0, frame16 = 0; logic cpol = 0, cpha = 0, lsb = 0, frame16 = 0;
@@ -67,6 +68,8 @@ realtime access_accept_time;
.output_enable_o (output_enable_o), .output_enable_o (output_enable_o),
.irq_o (irq_o), .irq_o (irq_o),
.credit_commit_o (credit_commit_o), .credit_commit_o (credit_commit_o),
.session_owned_o (session_owned),
.lifecycle_idle_o (lifecycle_idle),
.paddr_i (paddr_i), .paddr_i (paddr_i),
.pwdata_i (pwdata_i), .pwdata_i (pwdata_i),
.pstrb_i (pstrb_i), .pstrb_i (pstrb_i),
+2 -2
View File
@@ -9,7 +9,7 @@ module tiny_axi4_fabric_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) masters[3] ( ) masters[3] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
@@ -18,7 +18,7 @@ module tiny_axi4_fabric_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) targets[`RETROSOC_TINY__TARGET_COUNT] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
+34 -31
View File
@@ -80,37 +80,40 @@ module tiny_p7_source_reset_tb;
logic [1:0] rd_resp, wr_resp; logic [1:0] rd_resp, wr_resp;
logic rd_id_error, wr_id_error; logic rd_id_error, wr_id_error;
dma_axi4_master u_dma_master ( dma_axi4_master u_dma_master (
.clk_i (s_dma_clk), .clk_i (s_dma_clk),
.rst_n_i (s_dma_rst_n), .rst_n_i (s_dma_rst_n),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (rd_start), .read_start_valid_i (rd_start),
.read_start_ready_o (rd_start_ready), .read_start_ready_o (rd_start_ready),
.read_addr_i (rd_addr), .read_addr_i (rd_addr),
.read_beats_i (rd_beats), .read_beats_i (rd_beats),
.read_fixed_i (1'b0), .read_fixed_i (1'b0),
.read_busy_o (rd_busy), .read_busy_o (rd_busy),
.read_beat_valid_o (rd_valid), .read_beat_valid_o (rd_valid),
.read_beat_ready_i (rd_ready), .read_beat_ready_i (rd_ready),
.read_data_o (rd_data), .read_data_o (rd_data),
.read_resp_o (rd_resp), .read_resp_o (rd_resp),
.read_last_o (rd_last), .read_last_o (rd_last),
.read_expected_last_o(), .read_expected_last_o (),
.read_id_error_o (rd_id_error), .read_id_error_o (rd_id_error),
.read_done_o (rd_done), .read_done_o (rd_done),
.write_start_valid_i (wr_start), .write_start_valid_i (wr_start),
.write_start_ready_o (wr_start_ready), .write_start_ready_o (wr_start_ready),
.write_addr_i (wr_addr), .write_addr_i (wr_addr),
.write_beats_i (wr_beats), .write_beats_i (wr_beats),
.write_fixed_i (1'b0), .write_fixed_i (1'b0),
.write_busy_o (wr_busy), .write_busy_o (wr_busy),
.write_data_valid_i (wr_valid), .write_data_valid_i (wr_valid),
.write_data_ready_o (wr_ready), .write_data_ready_o (wr_ready),
.write_data_i (wr_data), .write_data_i (wr_data),
.write_strb_i (4'hf), .write_strb_i (4'hf),
.write_done_o (wr_done), .write_done_o (wr_done),
.write_resp_o (wr_resp), .write_resp_o (wr_resp),
.write_id_error_o (wr_id_error), .write_id_error_o (wr_id_error),
.axi4 (u_dma_pclk_axi4_if) .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}; assign s_periph_idle = {4'hf, !(rd_busy || wr_busy || rd_start || wr_start), 9'h1ff};
+2 -2
View File
@@ -16,7 +16,7 @@ module tiny_r2_p5_fabric_formal (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) masters[3] ( ) masters[3] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
@@ -25,7 +25,7 @@ module tiny_r2_p5_fabric_formal (
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) target ( ) target (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
+2 -2
View File
@@ -9,7 +9,7 @@ module tiny_r2_p5_fabric_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) masters[3] ( ) masters[3] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
@@ -18,7 +18,7 @@ module tiny_r2_p5_fabric_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) targets[`RETROSOC_TINY__TARGET_COUNT] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
+38 -35
View File
@@ -8,7 +8,7 @@ module tiny_r2_p5_reset_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) masters[3] ( ) masters[3] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
@@ -17,7 +17,7 @@ module tiny_r2_p5_reset_tb;
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(4)
) targets[`RETROSOC_TINY__TARGET_COUNT] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(target_rst_n) .aresetn(target_rst_n)
@@ -76,37 +76,40 @@ module tiny_r2_p5_reset_tb;
// Production master: BREADY cannot rise until its last W handshake. // Production master: BREADY cannot rise until its last W handshake.
dma_axi4_master u_dma ( dma_axi4_master u_dma (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clear_i (1'b0), .clear_i (1'b0),
.read_start_valid_i (1'b0), .read_start_valid_i (1'b0),
.read_start_ready_o (), .read_start_ready_o (),
.read_addr_i (32'd0), .read_addr_i (32'd0),
.read_beats_i (5'd1), .read_beats_i (5'd1),
.read_fixed_i (1'b0), .read_fixed_i (1'b0),
.read_busy_o (), .read_busy_o (),
.read_beat_valid_o (), .read_beat_valid_o (),
.read_beat_ready_i (1'b0), .read_beat_ready_i (1'b0),
.read_data_o (), .read_data_o (),
.read_resp_o (), .read_resp_o (),
.read_last_o (), .read_last_o (),
.read_expected_last_o(), .read_expected_last_o (),
.read_id_error_o (), .read_id_error_o (),
.read_done_o (), .read_done_o (),
.write_start_valid_i (dma_start), .write_start_valid_i (dma_start),
.write_start_ready_o (dma_start_ready), .write_start_ready_o (dma_start_ready),
.write_addr_i (dma_addr), .write_addr_i (dma_addr),
.write_beats_i (dma_beats), .write_beats_i (dma_beats),
.write_fixed_i (1'b0), .write_fixed_i (1'b0),
.write_busy_o (dma_busy), .write_busy_o (dma_busy),
.write_data_valid_i (dma_valid), .write_data_valid_i (dma_valid),
.write_data_ready_o (dma_ready), .write_data_ready_o (dma_ready),
.write_data_i (dma_data), .write_data_i (dma_data),
.write_strb_i (4'hf), .write_strb_i (4'hf),
.write_done_o (dma_done), .write_done_o (dma_done),
.write_resp_o (dma_resp), .write_resp_o (dma_resp),
.write_id_error_o (dma_id_error), .write_id_error_o (dma_id_error),
.axi4 (masters[1]) .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 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. // Independent upstream scoreboards also run in Icarus without SVA.
integer rleft = 0, wleft = 0, bleft = 0; integer rleft = 0, wleft = 0, bleft = 0;
logic rheld = 0, bheld = 0; logic rheld = 0, bheld = 0;
logic [36:0] saved_r; logic [39:0] saved_r;
logic [ 3:0] saved_b; logic [ 6:0] saved_b;
always @(posedge clk_i) begin always @(posedge clk_i) begin
if (!rst_n_i) begin if (!rst_n_i) begin
rleft = 0; rleft = 0;
+67
View File
@@ -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 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: def test_dma_apb_registers_and_irq(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog") iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp") vvp = shutil.which("vvp")
+1 -1
View File
@@ -37,7 +37,7 @@ def test_current_source_cannot_relabel_historical_publication():
@pytest.mark.parametrize("before,after", [ @pytest.mark.parametrize("before,after", [
("s_req_q.write_done && !s_req_q.b_captured", "1'b1"), ("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)"), (".flush_i(1'b0)", ".flush_i(!enable_i)"),
]) ])
def test_current_reset_binding_still_rejects_unsafe_completion(tmp_path, before, after): def test_current_reset_binding_still_rejects_unsafe_completion(tmp_path, before, after):
+2 -2
View File
@@ -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 "logic [ NumChannels-1:0][ 4:0] s_req_q" in core
assert "!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX)" in core assert "!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX)" in core
assert "s_tcd_parse_q &&" in core assert "s_tcd_parse_q &&" in core
assert ".RequestMask (16'h39ff)" in tiny assert ".RequestMask" in tiny and "0003_39ff" in tiny
assert ".EnableSpi" not in tiny assert ".EnableSpi(1'b1)" in re.sub(r"\s+", "", tiny)
def test_dma_v22_tcd_request_split_is_handwritten_and_stable(): def test_dma_v22_tcd_request_split_is_handwritten_and_stable():
+61
View File
@@ -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
+6 -5
View File
@@ -49,9 +49,10 @@ def test_frozen_spi_register_mirrors():
assert rtl['IRQ_TX_LEVEL'] == 8 and rtl['IRQ_RX_LEVEL'] == 9 assert rtl['IRQ_TX_LEVEL'] == 8 and rtl['IRQ_RX_LEVEL'] == 9
def test_spi_is_not_advertised_by_product_integration(): def test_spi_is_advertised_only_by_tiny_product_integration():
for product in ('tiny', 'mini'): assert any('/serial/apb4_spi.sv' in path.read_text()
for filelist in (ROOT / f'rtl/{product}/filelist').glob('*.fl'): for path in (ROOT / 'rtl/tiny/filelist').glob('*.fl'))
assert '/serial/apb4_spi.sv' not in filelist.read_text() assert all('/serial/apb4_spi.sv' not in path.read_text()
assert 'spi' not in [row['name'] for row in __import__('json').loads( 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']] (ROOT / 'rtl/tiny/integration/soc_topology.json').read_text())['apb_targets']]
+1 -1
View File
@@ -36,7 +36,7 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
name = target["name"] name = target["name"]
assert f".presetn(s_{name}_rst_n)" in interfaces assert f".presetn(s_{name}_rst_n)" in interfaces
if name in {"gpio", "uart0", "i2c0", "timer0", "timer1", "pwm", "i2s", "sdio", 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"tiny_peripheral_leaf u_{name}_lifecycle" in interfaces
assert f".rst_n_i(s_leaf_rst_n[{index}])" 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 assert f".rst_n_o(s_{name}_rst_n)" in interfaces
+1 -1
View File
@@ -59,7 +59,7 @@ def test_reset_inventory_has_active_domains_and_all_existing_targets() -> None:
reset = inventory["reset_distribution"] reset = inventory["reset_distribution"]
assert reset["release_edges"] == 5 assert reset["release_edges"] == 5
assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])] 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"]} == { assert {domain["name"] for domain in inventory["domains"]} == {
"aon", "system", "memory", "peripheral", "jtag", "audio", "dvp_pixel"} "aon", "system", "memory", "peripheral", "jtag", "audio", "dvp_pixel"}
assert reset["clock_domain"] == "domain-specific" and reset["main_sram_payload_reset"] == "none" assert reset["clock_domain"] == "domain-specific" and reset["main_sram_payload_reset"] == "none"
+3 -2
View File
@@ -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")} == { assert {k: irqs[k] for k in ("rng", "ws2812", "crypto", "i2s", "sdio")} == {
"rng": 16, "ws2812": 17, "crypto": 23, "i2s": 8, "sdio": 10} "rng": 16, "ws2812": 17, "crypto": 23, "i2s": 8, "sdio": 10}
assert irqs["dvp"] == 15 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 assert irqs["rcu"] == 31
pins = topology["gpio_alt_functions"] pins = topology["gpio_alt_functions"]
assert pins[9]["alt0"]["inputs"] == ["u_uart0_if.rx_i"] 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(): def test_p6_reset_and_macro_policy_remains_explicit():
text = (TINY / "top/retrosoc_tiny.sv").read_text() text = (TINY / "top/retrosoc_tiny.sv").read_text()
assert ".clk_aud_i" in text and "s_audio_rst_n" in 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 assert "apb4_crypto u_crypto" in text
tree = (TINY / "top/tiny_reset_tree.sv").read_text() tree = (TINY / "top/tiny_reset_tree.sv").read_text()
assert "s_audio" not in tree and "crypto" not in tree assert "s_audio" not in tree and "crypto" not in tree
+7 -7
View File
@@ -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) instances = re.findall(r"\btiny_sysctrl\s*#\(([^;]*?)\)\s*u_sysctrl\s*\(", top)
if len(instances) != 1: if len(instances) != 1:
raise ValueError("missing or ambiguous production SYSCTRL instance") 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( expected = re.findall(
r"^\s*#define RS_P7_EXPECTED_TARGET_CAPABILITY\s+UINT32_C\(0x([0-9a-fA-F]+)\)\s*$", r"^\s*#define RS_P7_EXPECTED_TARGET_CAPABILITY\s+UINT32_C\(0x([0-9a-fA-F]+)\)\s*$",
firmware, re.MULTILINE) 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") raise ValueError("missing or ambiguous target capability definition")
# Freeze the implemented set independently of both handwritten sources. # Freeze the implemented set independently of both handwritten sources.
# The HAL's 0x3ffff architecture mask also admits unimplemented targets. # 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") raise ValueError("target capability differs from the DVP-inclusive contract")
main = re.findall(r"\bint\s+main\s*\(void\)\s*\{(.*)", firmware, re.DOTALL) main = re.findall(r"\bint\s+main\s*\(void\)\s*\{(.*)", firmware, re.DOTALL)
guard = ( guard = (
@@ -250,15 +250,15 @@ def test_tiny_p7_target_capability_parity() -> None:
@pytest.mark.parametrize(("source", "old", "new"), [ @pytest.mark.parametrize(("source", "old", "new"), [
("firmware", "UINT32_C(0x00007FFF)", "UINT32_C(0x00003FFF)"), ("firmware", "UINT32_C(0x00017FFF)", "UINT32_C(0x00003FFF)"),
("top", "15'h07fff", "15'h03fff"), ("top", "17'h17fff", "17'h03fff"),
*[("firmware", "UINT32_C(0x00007FFF)", f"UINT32_C({value})") *[("firmware", "UINT32_C(0x00017FFF)", f"UINT32_C({value})")
for value in ("0x0000FFFF", "0x00017FFF", "0x00027FFF", "0x0003FFFF")], 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", "false"),
("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY", ("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY",
"(snapshot.target_capability & RS_P7_EXPECTED_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"), ("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: 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(), sources = {"top": (ROOT / "rtl/tiny/top/retrosoc_tiny.sv").read_text(),
+4 -4
View File
@@ -40,9 +40,9 @@ def test_p9_topology_and_camera_routes() -> None:
memory = json.loads((TINY / "address_map/memory_map.json").read_text()) memory = json.loads((TINY / "address_map/memory_map.json").read_text())
regions = {item["symbol"]: item for item in memory["regions"]} regions = {item["symbol"]: item for item in memory["regions"]}
assert regions["APB4_DVP"]["base"] == "0x1000E000" assert regions["APB4_DVP"]["base"] == "0x1000E000"
assert topology["fabric"]["target_count"] == 28 assert topology["fabric"]["target_count"] == 29
assert topology["fabric"]["decode_error_target"] == 26 assert topology["fabric"]["decode_error_target"] == 27
assert topology["fabric"]["protocol_error_target"] == 27 assert topology["fabric"]["protocol_error_target"] == 28
assert any(item["name"] == "dvp" and item["regions"] == ["APB4_DVP"] assert any(item["name"] == "dvp" and item["regions"] == ["APB4_DVP"]
for item in topology["apb_targets"]) for item in topology["apb_targets"])
assert {item["core_bit"] for item in topology["interrupts"] if item["name"] == "dvp"} == {15} 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[22]["alt1"]["inputs"] == ["u_dvp_if.vsync_i"]
assert routes[23]["alt1"]["do"] == "s_clkout" assert routes[23]["alt1"]["do"] == "s_clkout"
assert read_topology(TINY / "integration/soc_topology.json", 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: def test_p9_generated_and_hal_bindings() -> None: