mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
58 files changed
+1978
-472
No files matched your search
@@ -91,6 +91,7 @@ TINY_P9_ACCEPTANCE ?= NO
|
||||
TINY_P9_QUICK ?= NO
|
||||
TINY_P9_WORKLOAD ?= QVGA_RGB565
|
||||
TINY_P10_ACCEPTANCE ?= NO
|
||||
TINY_SPI_P3_ACCEPTANCE ?= NO
|
||||
COREMARK_MODE ?= quick
|
||||
NPU_P5_ACCEPTANCE ?= NO
|
||||
NPU_P6_ACCEPTANCE ?= NO
|
||||
@@ -151,6 +152,9 @@ endif
|
||||
ifeq ($(TINY_P10_ACCEPTANCE),YES)
|
||||
CONFIG_KEY_VARS += TINY_P10_ACCEPTANCE
|
||||
endif
|
||||
ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES)
|
||||
CONFIG_KEY_VARS += TINY_SPI_P3_ACCEPTANCE
|
||||
endif
|
||||
VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \
|
||||
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
|
||||
$(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))) | tail -n 1))
|
||||
@@ -316,11 +320,17 @@ $(call validate_value,TINY_P8_ACCEPTANCE,$(VALID_BOOL))
|
||||
$(call validate_value,TINY_P9_ACCEPTANCE,$(VALID_BOOL))
|
||||
$(call validate_value,TINY_P9_QUICK,$(VALID_BOOL))
|
||||
$(call validate_value,TINY_P10_ACCEPTANCE,$(VALID_BOOL))
|
||||
$(call validate_value,TINY_SPI_P3_ACCEPTANCE,$(VALID_BOOL))
|
||||
ifeq ($(TINY_P10_ACCEPTANCE),YES)
|
||||
ifneq ($(SOC)/$(APP)/$(HAVE_CSR)/$(TINY_P7_ACCEPTANCE)/$(TINY_P8_ACCEPTANCE)/$(TINY_P9_ACCEPTANCE)/$(TINY_P9_QUICK),TINY/ci_smoke/YES/NO/NO/NO/NO)
|
||||
$(error TINY_P10_ACCEPTANCE requires Tiny ci_smoke with CSR and no P7/P8/P9 acceptance image)
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TINY_SPI_P3_ACCEPTANCE),YES)
|
||||
ifneq ($(SOC)/$(APP)/$(HAVE_CSR)/$(TINY_P7_ACCEPTANCE)/$(TINY_P8_ACCEPTANCE)/$(TINY_P9_ACCEPTANCE)/$(TINY_P10_ACCEPTANCE),TINY/ci_smoke/YES/NO/NO/NO/NO)
|
||||
$(error TINY_SPI_P3_ACCEPTANCE requires Tiny ci_smoke with CSR and no R2 acceptance image)
|
||||
endif
|
||||
endif
|
||||
$(call validate_value,TINY_P9_WORKLOAD,QVGA_RGB565 VGA_YUV422 RECOVERY)
|
||||
ifneq ($(TINY_P9_WORKLOAD),QVGA_RGB565)
|
||||
ifneq ($(TINY_P9_ACCEPTANCE)/$(TINY_P9_QUICK),YES/NO)
|
||||
@@ -546,6 +556,11 @@ spi-p2-lint spi-p2-rtl spi-p2-report: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/spi_p2.py $(patsubst spi-p2-%,%,$@) \
|
||||
--variant-root $(VARIANT_ROOT) --pdk $(PDK)
|
||||
|
||||
.PHONY: spi-p3-lint spi-p3-rtl spi-p3-report
|
||||
spi-p3-lint spi-p3-rtl spi-p3-report: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/spi_p3.py $(patsubst spi-p3-%,%,$@) \
|
||||
--variant-root $(VARIANT_ROOT) --pdk $(PDK)
|
||||
|
||||
.PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report
|
||||
crypto-p0-constants: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/crypto_p0.py constants --variant-root $(VARIANT_ROOT)
|
||||
@@ -1301,4 +1316,4 @@ regress-nightly:
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY --pdk $(PDK))
|
||||
|
||||
sim-asm: asm
|
||||
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
||||
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
||||
@@ -22,4 +22,7 @@ endif
|
||||
endif
|
||||
ifeq ($(TINY_P10_ACCEPTANCE),YES)
|
||||
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
|
||||
@@ -11,8 +11,9 @@
|
||||
#include <retrosoc/hal/timer.h>
|
||||
#include <pwm_regs.h>
|
||||
|
||||
/* Current Tiny includes DVP (target 14); later expansion targets stay absent. */
|
||||
#define RS_P7_EXPECTED_TARGET_CAPABILITY UINT32_C(0x00007FFF)
|
||||
/* Current Tiny includes DVP (target 14) and SPI (RCU target 16); the other
|
||||
* expansion targets stay absent. */
|
||||
#define RS_P7_EXPECTED_TARGET_CAPABILITY UINT32_C(0x00017FFF)
|
||||
|
||||
/* Short P7 firmware: execute in real main SRAM and use real SYSCTRL/APB
|
||||
* crossings. The full peripheral firmware remains a separate regression. */
|
||||
|
||||
@@ -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 (;;) {
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
#define RS_TINY_RCU_STATUS_FAULT UINT32_C(1U << 4)
|
||||
#define RS_TINY_RCU_CAP_PLL_PRESENT UINT32_C(1U << 8)
|
||||
#define RS_TINY_RCU_CAP_TARGET_MASK UINT32_C(0x0003FFFF)
|
||||
#define RS_TINY_RCU_TARGET_SPI0 (UINT32_C(1) << 16U)
|
||||
#define RS_TINY_RCU_FAULT_MASK UINT32_C(0x0000001F)
|
||||
#define RS_TINY_RCU_RESET_CAUSE_MASK UINT32_C(0x0000003F)
|
||||
#define RS_TINY_RCU_CSR_MSLEEP 0xBF0
|
||||
|
||||
@@ -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
@@ -3,6 +3,11 @@
|
||||
#include <stddef.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) {
|
||||
uintptr_t last_address;
|
||||
@@ -30,11 +35,13 @@ static bool rs_dma_request_valid(const rs_dma_config_t *config) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if ((config->request == RS_DMA_REQUEST_SPI_TX) ||
|
||||
(config->request == RS_DMA_REQUEST_SPI_RX)) {
|
||||
const uintptr_t tx_aperture = (uintptr_t)RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_TXDATA;
|
||||
const uintptr_t rx_aperture = (uintptr_t)RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_RXDATA;
|
||||
return ((config->byte_count % sizeof(uint32_t)) == 0U) &&
|
||||
(((config->request == RS_DMA_REQUEST_SPI_TX) && config->source_increment &&
|
||||
!config->destination_increment) ||
|
||||
!config->destination_increment && config->destination == tx_aperture) ||
|
||||
((config->request == RS_DMA_REQUEST_SPI_RX) && !config->source_increment &&
|
||||
config->destination_increment));
|
||||
config->destination_increment && config->source == rx_aperture));
|
||||
}
|
||||
#endif
|
||||
return (config->request == RS_DMA_REQUEST_SOFTWARE) ||
|
||||
@@ -141,12 +148,14 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_TX) &&
|
||||
(((tcd->control & RS_DMA_TCD_SRC_INC) == UINT32_C(0)) ||
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) != UINT32_C(0)))) {
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) != UINT32_C(0)) ||
|
||||
(tcd->destination != (uint32_t)(RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_TXDATA)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_RX) &&
|
||||
(((tcd->control & RS_DMA_TCD_SRC_INC) != UINT32_C(0)) ||
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) == UINT32_C(0)))) {
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) == UINT32_C(0)) ||
|
||||
(tcd->source != (uint32_t)(RS_SOC_APB4_SPI_BASE + RS_SPI_ABI_DMA_RXDATA)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -112,6 +112,155 @@ SPI request allocation, the full post-fix Pytest campaign and product
|
||||
regressions are unrun. Product instances
|
||||
retain V2.0/default-disabled SPI.
|
||||
|
||||
## SPI-P3 implementation checkpoint (2026-10-21)
|
||||
|
||||
The current authorized worktree integrates SPI-P2 into Tiny with APB4
|
||||
`0x1001D000..0x1001DFFF`, IRQ25, RCU target16, the four frozen Gen1 ALT
|
||||
routes and the ICS55 default/Tiny-IHP130 compatibility profiles. Tiny fabric
|
||||
source qualification is carried through AXI user metadata and APB CDC; CPU,
|
||||
DMA and other-owner accesses remain distinguishable at the SPI aliases. Tiny
|
||||
DMA enables V2.2 requests 16/17 and checks the exact fixed FIFO MMIO addresses.
|
||||
|
||||
The GPIO guard uses the existing USER_SELECT/handoff and ALT/electrical state,
|
||||
with raw GPIO30 input for MISO. GPIO/SPI lifecycle idle vectors include the
|
||||
latched SPI session, so RCU gate/reset/rate changes cannot strand accepted
|
||||
SPI work. The bounded `rs_spi_*` HAL is added under the freestanding SDK and
|
||||
the `TINY_SPI_P3_ACCEPTANCE` image covers discovery, ownership and RCU target
|
||||
checks.
|
||||
|
||||
Focused P3 source checks and Tiny firmware/lint records are retained below
|
||||
`build/<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)
|
||||
|
||||
The approved standalone implementation starts from clean
|
||||
|
||||
@@ -25,6 +25,8 @@ module apb4_spi_block_top (
|
||||
output logic output_enable_o,
|
||||
output logic irq_o,
|
||||
output logic [ 1:0] credit_commit_o,
|
||||
output logic session_owned_o,
|
||||
output logic lifecycle_idle_o,
|
||||
input logic [31:0] paddr_i,
|
||||
pwdata_i,
|
||||
input logic [ 3:0] pstrb_i,
|
||||
@@ -75,6 +77,8 @@ module apb4_spi_block_top (
|
||||
.output_enable_o (output_enable_o),
|
||||
.irq_o (irq_o),
|
||||
.credit_commit_o (credit_commit_o),
|
||||
.session_owned_o (session_owned_o),
|
||||
.lifecycle_idle_o (lifecycle_idle_o),
|
||||
.apb4 (s_apb4)
|
||||
);
|
||||
endmodule
|
||||
@@ -125,7 +125,7 @@
|
||||
{
|
||||
"file": "rtl/tiny/top/tiny_axi4_fabric.sv",
|
||||
"text": [
|
||||
" function automatic logic [4:0] checked_target(\n input logic [31:0] addr_i, input logic [7:0] len_i,\n input logic [2:0] size_i, input logic [1:0] burst_i,\n input logic lock_i, input logic instruction_i);\n logic [32:0] last_addr;\n logic [4:0] decoded;\n decoded = decode_target(addr_i);\n last_addr = {1'b0, addr_i} + (({25'd0, len_i} + 33'd1) << size_i) - 33'd1;\n if (lock_i || (size_i > 3'd2) || (len_i > 8'd15) ||\n ((addr_i & ((32'd1 << size_i) - 32'd1)) != 32'd0) ||\n !((burst_i == 2'b01) || ((burst_i == 2'b00) && (len_i == 8'd0))) ||\n last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||\n (decode_target(last_addr[31:0]) != decoded) ||\n ((decoded >= 5'd5) && (decoded <= 5'd20) && (len_i != 8'd0)))\n return 5'd22;\n if (instruction_i && !(`SOC_ADDR_IS_FLASH(addr_i) ||\n ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000))))\n return ((decoded >= 5'd5) && (decoded <= 5'd20)) ? 5'd22 : 5'd21;\n return decoded;\n endfunction",
|
||||
" function automatic logic [4:0] checked_target(input logic [31:0] addr_i, input logic [7:0] len_i,\n input logic [2:0] size_i, input logic [1:0] burst_i,\n input logic lock_i, input logic instruction_i);\n logic [32:0] last_addr;\n logic [ 4:0] decoded;\n decoded = decode_target(addr_i);\n last_addr = {1'b0, addr_i} + (({25'd0, len_i} + 33'd1) << size_i) - 33'd1;\n if (lock_i || (size_i > 3'd2) || (len_i > 8'd15) ||\n ((addr_i & ((32'd1 << size_i) - 32'd1)) != 32'd0) ||\n !((burst_i == 2'b01) || ((burst_i == 2'b00) && (len_i == 8'd0))) ||\n last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||\n (decode_target(\n last_addr[31:0]\n ) != decoded) || ((decoded >= 5'd5) && (decoded < DecodeErrorTarget) && (len_i != 8'd0)))\n return ProtocolErrorTarget;\n if (instruction_i && !(\n `SOC_ADDR_IS_FLASH(addr_i)\n || ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000)))) begin\n if ((decoded >= 5'd5) && (decoded < DecodeErrorTarget)) return ProtocolErrorTarget;\n return DecodeErrorTarget;\n end\n return decoded;\n endfunction",
|
||||
"assign s_busy_d = (s_busy_q & ~s_done) | s_admit;",
|
||||
"assign owner_idle_o[owner] = !(s_busy_q[2*owner] || s_busy_q[2*owner+1]);",
|
||||
"assign s_eligible[2*owner] = rst_n_i && owner_idle_o[owner] && masters[owner].arvalid && (!masters[owner].awvalid || !s_cpu_prefer_write_q);",
|
||||
@@ -135,7 +135,7 @@
|
||||
{
|
||||
"file": "crt/src/hal/dma_math.c",
|
||||
"text": [
|
||||
"#ifdef RS_SOC_TINY\n if (config->kind != RS_DMA_KIND_MM_TO_MM) {\n return false;\n }\n#endif"
|
||||
"#ifdef RS_SOC_TINY\n const uint32_t request_mask = UINT32_C(0x000339FF);\n if (((uint32_t)config->request >= 32U) ||\n ((request_mask & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {\n return false;\n }\n#endif"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -150,7 +150,7 @@
|
||||
".RTC_CLOCK_HZ(24_000_000)",
|
||||
".WDG_CLOCK_HZ(24_000_000)",
|
||||
".RESET_PULSE_CYCLES(8)",
|
||||
"assign u_uart1_if.cts_n_i = 1'b0;"
|
||||
"assign uart1_tx_o = 1'b1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -187,9 +187,9 @@
|
||||
"file": "rtl/tiny/top/tiny_axi4_target.sv",
|
||||
"text": [
|
||||
"assign s_accept = (s_req_q.state == Idle) && enable_i && s_valid;",
|
||||
"assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]));",
|
||||
"assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (s_r_valid && s_rready[s_req_q.owner] && s_r_data[4]));",
|
||||
"assign fault_valid_o = s_terminal && s_req_q.error_seen;",
|
||||
"if (!s_req_q.error_seen && (s_rin_data[3:2] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; s_req_d.error_resp = s_rin_data[3:2]; end",
|
||||
"if (!s_req_q.error_seen && (s_rin_data[6:5] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; s_req_d.error_resp = s_rin_data[6:5]; end",
|
||||
"assign s_abort_write_valid = (s_req_q.state == AbortResponse) && s_req_q.write && s_req_q.write_done && !s_req_q.b_captured;",
|
||||
"assign s_abort_read_valid = (s_req_q.state == AbortResponse) && !s_req_q.write && (s_req_q.read_count <= {1'b0, s_saved_addr.len});",
|
||||
".flush_i(1'b0)",
|
||||
|
||||
@@ -237,6 +237,9 @@ module apu_dma (
|
||||
.write_done_o(s_write_done),
|
||||
.write_resp_o(s_write_resp),
|
||||
.write_id_error_o(s_write_id_error),
|
||||
.origin_enable_i(1'b0),
|
||||
.read_origin_channel_i(3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4(axi4)
|
||||
);
|
||||
|
||||
|
||||
@@ -440,37 +440,40 @@ module jpeg_dma #(
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats(MaxBurstBeats)
|
||||
) u_axi4_master (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr_q),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_master_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o(s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_err),
|
||||
.read_done_o (s_read_master_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr_q),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_master_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strobe),
|
||||
.write_done_o (s_write_master_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_err),
|
||||
.axi4 (axi4)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr_q),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_master_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o (s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_err),
|
||||
.read_done_o (s_read_master_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr_q),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_master_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strobe),
|
||||
.write_done_o (s_write_master_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_err),
|
||||
.origin_enable_i (1'b0),
|
||||
.read_origin_channel_i (3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4 (axi4)
|
||||
);
|
||||
|
||||
dffr #(
|
||||
|
||||
@@ -9,16 +9,19 @@
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
module apb4_dma #(
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int NumChannels = 4,
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter logic [31:0] SupportedRequests = RequestMask,
|
||||
parameter logic [31:0] IpVersion = 32'h0002_0000,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int NumChannels = 4,
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter logic [31:0] SupportedRequests = RequestMask,
|
||||
parameter logic [31:0] IpVersion = 32'h0002_0000,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0,
|
||||
parameter bit EnableOriginQualifier = 1'b0,
|
||||
parameter logic [31:0] SpiTxAddress = 32'd0,
|
||||
parameter logic [31:0] SpiRxAddress = 32'd0
|
||||
) (
|
||||
// verilog_format: off -- integration ports retain established peripheral endpoint names.
|
||||
output logic lifecycle_idle_o,
|
||||
@@ -33,7 +36,12 @@ module apb4_dma #(
|
||||
axi4_stream_if.sink i2s_rx_axis,
|
||||
axi4_stream_if.sink dvp_rx_axis,
|
||||
axi4_stream_if.source crypto_in_axis,
|
||||
axi4_stream_if.sink crypto_out_axis
|
||||
axi4_stream_if.sink crypto_out_axis,
|
||||
output logic [1:0] spi_credit_valid_o,
|
||||
output logic [1:0] spi_credit_retire_o,
|
||||
output logic [1:0] spi_start_pending_o,
|
||||
output logic [3:0] spi_status_o,
|
||||
output logic spi_error_o
|
||||
// verilog_format: on
|
||||
);
|
||||
logic [ NumChannels*32-1:0] s_ch_cfg;
|
||||
@@ -134,14 +142,17 @@ module apb4_dma #(
|
||||
);
|
||||
|
||||
dma_core #(
|
||||
.RequestMask (RequestMask),
|
||||
.EnableSpi (EnableSpi),
|
||||
.EnableStreams(EnableStreams),
|
||||
.AddrWidth (AddrWidth),
|
||||
.DataWidth (DataWidth),
|
||||
.NumChannels (NumChannels),
|
||||
.MaxBurstBeats(MaxBurstBeats),
|
||||
.FifoDepth (FifoDepth)
|
||||
.RequestMask (RequestMask),
|
||||
.EnableSpi (EnableSpi),
|
||||
.EnableOriginQualifier(EnableOriginQualifier),
|
||||
.SpiTxAddress (SpiTxAddress),
|
||||
.SpiRxAddress (SpiRxAddress),
|
||||
.EnableStreams (EnableStreams),
|
||||
.AddrWidth (AddrWidth),
|
||||
.DataWidth (DataWidth),
|
||||
.NumChannels (NumChannels),
|
||||
.MaxBurstBeats (MaxBurstBeats),
|
||||
.FifoDepth (FifoDepth)
|
||||
) u_dma_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -190,7 +201,12 @@ module apb4_dma #(
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis),
|
||||
.crypto_in_axis (crypto_in_axis),
|
||||
.crypto_out_axis (crypto_out_axis)
|
||||
.crypto_out_axis (crypto_out_axis),
|
||||
.spi_credit_valid_o (spi_credit_valid_o),
|
||||
.spi_credit_retire_o (spi_credit_retire_o),
|
||||
.spi_start_pending_o (spi_start_pending_o),
|
||||
.spi_status_o (spi_status_o),
|
||||
.spi_error_o (spi_error_o)
|
||||
);
|
||||
logic s_bus_busy;
|
||||
assign s_bus_busy = axi4.awvalid || axi4.arvalid || axi4.wvalid;
|
||||
|
||||
@@ -21,7 +21,13 @@ module apb4_gpio #(
|
||||
input logic rst_n_i,
|
||||
apb4_if.slave apb4,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.padctrl user_gpio
|
||||
user_gpio_if.padctrl user_gpio,
|
||||
output logic [31:0] user_select_o,
|
||||
output logic [31:0] user_handoff_o,
|
||||
output logic [31:0] alt_enable_o,
|
||||
output logic [31:0] alt_select_o,
|
||||
output logic [31:0] output_enable_o,
|
||||
output logic [31:0] open_drain_o
|
||||
// verilog_format: on
|
||||
);
|
||||
|
||||
@@ -108,4 +114,11 @@ module apb4_gpio #(
|
||||
.user_gpio (user_gpio)
|
||||
);
|
||||
|
||||
assign user_select_o = s_user_sel;
|
||||
assign user_handoff_o = s_user_handoff;
|
||||
assign alt_enable_o = s_alt_en;
|
||||
assign alt_select_o = s_alt_sel;
|
||||
assign output_enable_o = s_output_en;
|
||||
assign open_drain_o = s_open_drain;
|
||||
|
||||
endmodule
|
||||
@@ -46,6 +46,9 @@ module dma_axi4_master #(
|
||||
output logic write_done_o,
|
||||
output logic [ 1:0] write_resp_o,
|
||||
output logic write_id_error_o,
|
||||
input logic origin_enable_i,
|
||||
input logic [ 2:0] read_origin_channel_i,
|
||||
input logic [ 2:0] write_origin_channel_i,
|
||||
axi4_if.master axi4
|
||||
// verilog_format: on
|
||||
);
|
||||
@@ -74,6 +77,8 @@ module dma_axi4_master #(
|
||||
logic [ 4:0] s_write_beat_q;
|
||||
logic s_read_fixed_q;
|
||||
logic s_write_fixed_q;
|
||||
logic [ 2:0] s_read_origin_q;
|
||||
logic [ 2:0] s_write_origin_q;
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
@@ -112,12 +117,12 @@ module dma_axi4_master #(
|
||||
assign axi4.awprot = `AXI4_PROT_DATA;
|
||||
assign axi4.awqos = `AXI4_QOS_NORMAL;
|
||||
assign axi4.awregion = `AXI4_REGION_NORMAL;
|
||||
assign axi4.awuser = '0;
|
||||
assign axi4.awuser = origin_enable_i ? {1'b1, s_write_origin_q} : '0;
|
||||
assign axi4.awvalid = s_write_state_q == WriteAddress;
|
||||
assign axi4.wdata = write_data_i;
|
||||
assign axi4.wstrb = write_strb_i;
|
||||
assign axi4.wlast = (s_write_beat_q + 1'b1) == s_write_beats_q;
|
||||
assign axi4.wuser = '0;
|
||||
assign axi4.wuser = origin_enable_i ? {1'b1, s_write_origin_q} : '0;
|
||||
assign axi4.wvalid = (s_write_state_q == WriteData) && write_data_valid_i;
|
||||
assign axi4.bready = s_write_state_q == WriteResponse;
|
||||
|
||||
@@ -131,32 +136,35 @@ module dma_axi4_master #(
|
||||
assign axi4.arprot = `AXI4_PROT_DATA;
|
||||
assign axi4.arqos = `AXI4_QOS_NORMAL;
|
||||
assign axi4.arregion = `AXI4_REGION_NORMAL;
|
||||
assign axi4.aruser = '0;
|
||||
assign axi4.aruser = origin_enable_i ? {1'b1, s_read_origin_q} : '0;
|
||||
assign axi4.arvalid = s_read_state_q == ReadAddress;
|
||||
assign axi4.rready = (s_read_state_q == ReadData) && read_beat_ready_i;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_read_state_q <= ReadIdle;
|
||||
s_read_addr_q <= '0;
|
||||
s_read_beats_q <= 5'd1;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= 1'b0;
|
||||
s_read_state_q <= ReadIdle;
|
||||
s_read_addr_q <= '0;
|
||||
s_read_beats_q <= 5'd1;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= 1'b0;
|
||||
s_read_origin_q <= 3'd0;
|
||||
end else if (clear_i) begin
|
||||
s_read_state_q <= ReadIdle;
|
||||
s_read_addr_q <= '0;
|
||||
s_read_beats_q <= 5'd1;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= 1'b0;
|
||||
s_read_state_q <= ReadIdle;
|
||||
s_read_addr_q <= '0;
|
||||
s_read_beats_q <= 5'd1;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= 1'b0;
|
||||
s_read_origin_q <= 3'd0;
|
||||
end else begin
|
||||
unique case (s_read_state_q)
|
||||
ReadIdle: begin
|
||||
if (read_start_valid_i && read_start_ready_o) begin
|
||||
s_read_addr_q <= read_addr_i;
|
||||
s_read_beats_q <= read_beats_i;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= read_fixed_i;
|
||||
s_read_state_q <= ReadAddress;
|
||||
s_read_addr_q <= read_addr_i;
|
||||
s_read_beats_q <= read_beats_i;
|
||||
s_read_beat_q <= '0;
|
||||
s_read_fixed_q <= read_fixed_i;
|
||||
s_read_origin_q <= read_origin_channel_i;
|
||||
s_read_state_q <= ReadAddress;
|
||||
end
|
||||
end
|
||||
ReadAddress: begin
|
||||
@@ -180,26 +188,29 @@ module dma_axi4_master #(
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_write_state_q <= WriteIdle;
|
||||
s_write_addr_q <= '0;
|
||||
s_write_beats_q <= 5'd1;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= 1'b0;
|
||||
s_write_state_q <= WriteIdle;
|
||||
s_write_addr_q <= '0;
|
||||
s_write_beats_q <= 5'd1;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= 1'b0;
|
||||
s_write_origin_q <= 3'd0;
|
||||
end else if (clear_i) begin
|
||||
s_write_state_q <= WriteIdle;
|
||||
s_write_addr_q <= '0;
|
||||
s_write_beats_q <= 5'd1;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= 1'b0;
|
||||
s_write_state_q <= WriteIdle;
|
||||
s_write_addr_q <= '0;
|
||||
s_write_beats_q <= 5'd1;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= 1'b0;
|
||||
s_write_origin_q <= 3'd0;
|
||||
end else begin
|
||||
unique case (s_write_state_q)
|
||||
WriteIdle: begin
|
||||
if (write_start_valid_i && write_start_ready_o) begin
|
||||
s_write_addr_q <= write_addr_i;
|
||||
s_write_beats_q <= write_beats_i;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= write_fixed_i;
|
||||
s_write_state_q <= WriteAddress;
|
||||
s_write_addr_q <= write_addr_i;
|
||||
s_write_beats_q <= write_beats_i;
|
||||
s_write_beat_q <= '0;
|
||||
s_write_fixed_q <= write_fixed_i;
|
||||
s_write_origin_q <= write_origin_channel_i;
|
||||
s_write_state_q <= WriteAddress;
|
||||
end
|
||||
end
|
||||
WriteAddress: begin
|
||||
|
||||
+116
-56
@@ -12,16 +12,19 @@
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module dma_core #(
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int NumChannels = 4,
|
||||
parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1,
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter bit EnableCrc = 1'b1,
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0
|
||||
parameter int AddrWidth = 32,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int NumChannels = 4,
|
||||
parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1,
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter bit EnableCrc = 1'b1,
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0,
|
||||
parameter bit EnableOriginQualifier = 1'b0,
|
||||
parameter logic [31:0] SpiTxAddress = 32'd0,
|
||||
parameter logic [31:0] SpiRxAddress = 32'd0
|
||||
) (
|
||||
// verilog_format: off -- channel vectors are kept aligned with the register-bank ABI.
|
||||
input logic clk_i,
|
||||
@@ -64,6 +67,11 @@ module dma_core #(
|
||||
output logic [8:0] first_error_status_o,
|
||||
output logic [15:0] first_error_addr_hi_o,
|
||||
output logic [31:0] request_status_o,
|
||||
output logic [1:0] spi_credit_valid_o,
|
||||
output logic [1:0] spi_credit_retire_o,
|
||||
output logic [1:0] spi_start_pending_o,
|
||||
output logic [3:0] spi_status_o,
|
||||
output logic spi_error_o,
|
||||
output logic xpi_xfer_done_o,
|
||||
dma_req_if.dut req,
|
||||
axi4_if.master axi4,
|
||||
@@ -183,6 +191,7 @@ module dma_core #(
|
||||
logic [ 31:0] s_axi_read_start_addr;
|
||||
logic [ 4:0] s_axi_read_start_beats;
|
||||
logic s_axi_read_start_fixed;
|
||||
logic [ChannelIndexWidth-1:0] s_axi_read_origin_channel;
|
||||
logic s_axi_read_busy;
|
||||
logic s_axi_read_beat_valid;
|
||||
logic s_axi_read_beat_ready;
|
||||
@@ -198,6 +207,7 @@ module dma_core #(
|
||||
logic [ 31:0] s_axi_write_start_addr;
|
||||
logic [ 4:0] s_axi_write_start_beats;
|
||||
logic s_axi_write_start_fixed;
|
||||
logic [ChannelIndexWidth-1:0] s_axi_write_origin_channel;
|
||||
logic s_axi_write_busy;
|
||||
logic s_axi_write_data_valid;
|
||||
logic s_axi_write_data_ready;
|
||||
@@ -293,6 +303,10 @@ module dma_core #(
|
||||
((FifoDepth & (FifoDepth - 1)) != 0)) begin
|
||||
$fatal(1, "dma_core: MVP requires 32-bit AXI4, 2+ channels, and a power-of-two FIFO");
|
||||
end
|
||||
if (EnableOriginQualifier && (ChannelIndexWidth > 3)) begin
|
||||
$fatal(1,
|
||||
"dma_core: origin qualifier packs three-bit channels, so NumChannels must stay <= 8");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
@@ -343,39 +357,74 @@ module dma_core #(
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats(MaxBurstBeats)
|
||||
) u_dma_axi4_master (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_axi_read_start_valid),
|
||||
.read_start_ready_o (s_axi_read_start_ready),
|
||||
.read_addr_i (s_axi_read_start_addr),
|
||||
.read_beats_i (s_axi_read_start_beats),
|
||||
.read_fixed_i (s_axi_read_start_fixed),
|
||||
.read_busy_o (s_axi_read_busy),
|
||||
.read_beat_valid_o (s_axi_read_beat_valid),
|
||||
.read_beat_ready_i (s_axi_read_beat_ready),
|
||||
.read_data_o (s_axi_read_data),
|
||||
.read_resp_o (s_axi_read_resp),
|
||||
.read_last_o (s_axi_read_last),
|
||||
.read_expected_last_o(s_axi_read_expected_last),
|
||||
.read_id_error_o (s_axi_read_id_error),
|
||||
.read_done_o (s_axi_read_done),
|
||||
.write_start_valid_i (s_axi_write_start_valid),
|
||||
.write_start_ready_o (s_axi_write_start_ready),
|
||||
.write_addr_i (s_axi_write_start_addr),
|
||||
.write_beats_i (s_axi_write_start_beats),
|
||||
.write_fixed_i (s_axi_write_start_fixed),
|
||||
.write_busy_o (s_axi_write_busy),
|
||||
.write_data_valid_i (s_axi_write_data_valid),
|
||||
.write_data_ready_o (s_axi_write_data_ready),
|
||||
.write_data_i (s_axi_write_data),
|
||||
.write_strb_i (s_axi_write_strb),
|
||||
.write_done_o (s_axi_write_done),
|
||||
.write_resp_o (s_axi_write_resp),
|
||||
.write_id_error_o (s_axi_write_id_error),
|
||||
.axi4 (axi4)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_axi_read_start_valid),
|
||||
.read_start_ready_o (s_axi_read_start_ready),
|
||||
.read_addr_i (s_axi_read_start_addr),
|
||||
.read_beats_i (s_axi_read_start_beats),
|
||||
.read_fixed_i (s_axi_read_start_fixed),
|
||||
.read_busy_o (s_axi_read_busy),
|
||||
.read_beat_valid_o (s_axi_read_beat_valid),
|
||||
.read_beat_ready_i (s_axi_read_beat_ready),
|
||||
.read_data_o (s_axi_read_data),
|
||||
.read_resp_o (s_axi_read_resp),
|
||||
.read_last_o (s_axi_read_last),
|
||||
.read_expected_last_o (s_axi_read_expected_last),
|
||||
.read_id_error_o (s_axi_read_id_error),
|
||||
.read_done_o (s_axi_read_done),
|
||||
.write_start_valid_i (s_axi_write_start_valid),
|
||||
.write_start_ready_o (s_axi_write_start_ready),
|
||||
.write_addr_i (s_axi_write_start_addr),
|
||||
.write_beats_i (s_axi_write_start_beats),
|
||||
.write_fixed_i (s_axi_write_start_fixed),
|
||||
.write_busy_o (s_axi_write_busy),
|
||||
.write_data_valid_i (s_axi_write_data_valid),
|
||||
.write_data_ready_o (s_axi_write_data_ready),
|
||||
.write_data_i (s_axi_write_data),
|
||||
.write_strb_i (s_axi_write_strb),
|
||||
.write_done_o (s_axi_write_done),
|
||||
.write_resp_o (s_axi_write_resp),
|
||||
.write_id_error_o (s_axi_write_id_error),
|
||||
.origin_enable_i (EnableOriginQualifier),
|
||||
.read_origin_channel_i (s_axi_read_origin_channel),
|
||||
.write_origin_channel_i(s_axi_write_origin_channel),
|
||||
.axi4 (axi4)
|
||||
);
|
||||
|
||||
always_comb begin
|
||||
logic s_spi_error;
|
||||
spi_credit_valid_o = '0;
|
||||
spi_credit_retire_o = '0;
|
||||
spi_start_pending_o = '0;
|
||||
spi_status_o = '0;
|
||||
s_spi_error = 1'b0;
|
||||
if (EnableSpi) begin
|
||||
if (s_read_owner_valid_q && (s_req_q[s_read_owner_q] == DMA_REQUEST_SPI_RX) &&
|
||||
(s_src_base_q[s_read_owner_q] == SpiRxAddress)) begin
|
||||
spi_credit_valid_o[1] = 1'b1;
|
||||
spi_status_o[1] = 1'b1;
|
||||
s_spi_error = s_spi_error | s_err_q[s_read_owner_q];
|
||||
if (s_axi_read_done) spi_credit_retire_o[1] = 1'b1;
|
||||
end
|
||||
if (s_write_owner_valid_q && (s_req_q[s_write_owner_q] == DMA_REQUEST_SPI_TX) &&
|
||||
(s_dst_base_q[s_write_owner_q] == SpiTxAddress)) begin
|
||||
spi_credit_valid_o[0] = 1'b1;
|
||||
spi_status_o[0] = 1'b1;
|
||||
s_spi_error = s_spi_error | s_err_q[s_write_owner_q];
|
||||
if (s_axi_write_done) spi_credit_retire_o[0] = 1'b1;
|
||||
end
|
||||
if (s_read_rr_valid && (s_req_q[s_read_rr_selected] == DMA_REQUEST_SPI_RX) &&
|
||||
(s_src_base_q[s_read_rr_selected] == SpiRxAddress) && !s_axi_read_busy)
|
||||
spi_start_pending_o[1] = 1'b1;
|
||||
if (s_write_rr_valid && (s_req_q[s_write_rr_selected] == DMA_REQUEST_SPI_TX) &&
|
||||
(s_dst_base_q[s_write_rr_selected] == SpiTxAddress) && !s_axi_write_busy)
|
||||
spi_start_pending_o[0] = 1'b1;
|
||||
end
|
||||
spi_error_o = s_spi_error;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_req_ready = '0;
|
||||
s_req_ready[DMA_REQUEST_SOFTWARE] = 1'b1;
|
||||
@@ -480,9 +529,11 @@ module dma_core #(
|
||||
(!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_SPI_RX))) ||
|
||||
((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) &&
|
||||
(!s_cfg_src_increment[channel] || s_cfg_dst_increment[channel])) ||
|
||||
(!s_cfg_src_increment[channel] || s_cfg_dst_increment[channel] ||
|
||||
s_cfg_dst_addr[channel] != SpiTxAddress)) ||
|
||||
((s_cfg_req[channel] == DMA_REQUEST_SPI_RX) &&
|
||||
(s_cfg_src_increment[channel] || !s_cfg_dst_increment[channel]))) begin
|
||||
(s_cfg_src_increment[channel] || !s_cfg_dst_increment[channel] ||
|
||||
s_cfg_src_addr[channel] != SpiRxAddress))) begin
|
||||
s_start_valid[channel] = 1'b0;
|
||||
s_start_err_code[channel] =
|
||||
((s_cfg_src_addr[channel][1:0] != 2'b00) ||
|
||||
@@ -680,10 +731,11 @@ module dma_core #(
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_axi_read_start_valid = s_read_rr_valid && (|s_read_rr_grant) && !s_axi_read_busy;
|
||||
s_axi_read_start_addr = '0;
|
||||
s_axi_read_start_beats = 5'd1;
|
||||
s_axi_read_start_fixed = 1'b0;
|
||||
s_axi_read_start_valid = s_read_rr_valid && (|s_read_rr_grant) && !s_axi_read_busy;
|
||||
s_axi_read_start_addr = '0;
|
||||
s_axi_read_start_beats = 5'd1;
|
||||
s_axi_read_start_fixed = 1'b0;
|
||||
s_axi_read_origin_channel = '0;
|
||||
if (s_read_rr_valid) begin
|
||||
s_axi_read_start_addr = s_src_base_q[s_read_rr_selected] +
|
||||
(s_src_increment_q[s_read_rr_selected]
|
||||
@@ -691,18 +743,21 @@ module dma_core #(
|
||||
: 32'd0);
|
||||
s_axi_read_start_beats = s_read_candidate_beats[s_read_rr_selected];
|
||||
s_axi_read_start_fixed = !s_src_increment_q[s_read_rr_selected];
|
||||
s_axi_read_origin_channel = s_read_rr_selected;
|
||||
end
|
||||
if (s_tcd_fetch_valid) begin
|
||||
s_axi_read_start_valid = !s_axi_read_busy;
|
||||
s_axi_read_start_addr = s_tcd_ptr_q[s_tcd_fetch_selected];
|
||||
s_axi_read_start_beats = 5'd16;
|
||||
s_axi_read_start_fixed = 1'b0;
|
||||
s_axi_read_start_valid = !s_axi_read_busy;
|
||||
s_axi_read_start_addr = s_tcd_ptr_q[s_tcd_fetch_selected];
|
||||
s_axi_read_start_beats = 5'd16;
|
||||
s_axi_read_start_fixed = 1'b0;
|
||||
s_axi_read_origin_channel = s_tcd_fetch_selected;
|
||||
end
|
||||
|
||||
s_axi_write_start_valid = s_write_rr_valid && (|s_write_rr_grant) && !s_axi_write_busy;
|
||||
s_axi_write_start_addr = '0;
|
||||
s_axi_write_start_beats = 5'd1;
|
||||
s_axi_write_start_fixed = 1'b0;
|
||||
s_axi_write_start_valid = s_write_rr_valid && (|s_write_rr_grant) && !s_axi_write_busy;
|
||||
s_axi_write_start_addr = '0;
|
||||
s_axi_write_start_beats = 5'd1;
|
||||
s_axi_write_start_fixed = 1'b0;
|
||||
s_axi_write_origin_channel = '0;
|
||||
if (s_write_rr_valid) begin
|
||||
s_axi_write_start_addr = s_dst_base_q[s_write_rr_selected] +
|
||||
(s_dst_increment_q[s_write_rr_selected]
|
||||
@@ -710,6 +765,7 @@ module dma_core #(
|
||||
: 32'd0);
|
||||
s_axi_write_start_beats = s_write_candidate_beats[s_write_rr_selected];
|
||||
s_axi_write_start_fixed = !s_dst_increment_q[s_write_rr_selected];
|
||||
s_axi_write_origin_channel = s_write_rr_selected;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1324,7 +1380,11 @@ module dma_core #(
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_RX) &&
|
||||
((s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM) ||
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][1] ||
|
||||
!s_tcd_word7_q[s_tcd_read_owner_q][2]))) ||
|
||||
!s_tcd_word7_q[s_tcd_read_owner_q][2] ||
|
||||
s_tcd_word1_q[s_tcd_read_owner_q] != SpiRxAddress))) ||
|
||||
(({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_TX) &&
|
||||
(s_tcd_word2_q[s_tcd_read_owner_q] != SpiTxAddress)) ||
|
||||
(((s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_MM_TO_STREAM) ||
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_STREAM_TO_MM)) &&
|
||||
(s_tcd_word3_q[s_tcd_read_owner_q][1:0] != 2'b00)) ||
|
||||
|
||||
@@ -26,11 +26,14 @@ module apb4_spi #(
|
||||
cs_n_o,
|
||||
output_enable_o,
|
||||
irq_o,
|
||||
session_owned_o,
|
||||
lifecycle_idle_o,
|
||||
output logic [ 1:0] credit_commit_o,
|
||||
apb4_if.slave apb4
|
||||
);
|
||||
spi_ctrl_if u_ctrl ();
|
||||
spi_pkg::configuration_t s_cfg;
|
||||
spi_pkg::status_t s_stat;
|
||||
logic [31:0] s_claim, s_ready;
|
||||
logic s_handback, s_credit_fault, s_reg_credit_fault, s_pacing_fault;
|
||||
logic s_fifo_pending;
|
||||
@@ -39,6 +42,7 @@ module apb4_spi #(
|
||||
logic [3:0] s_dma_stat;
|
||||
logic s_unused;
|
||||
assign s_cfg = u_ctrl.cfg;
|
||||
assign s_stat = u_ctrl.stat;
|
||||
// Only personality bits feed the GPIO guard; other fields feed the core/reg blocks.
|
||||
assign s_unused = ^s_cfg;
|
||||
assign s_credit_fault = s_reg_credit_fault | s_pacing_fault;
|
||||
@@ -94,7 +98,9 @@ module apb4_spi #(
|
||||
.apb4 (apb4),
|
||||
.ctrl (u_ctrl)
|
||||
);
|
||||
assign credit_commit_o = s_credit_commit;
|
||||
assign credit_commit_o = s_credit_commit;
|
||||
assign session_owned_o = s_stat.status[5];
|
||||
assign lifecycle_idle_o = s_stat.status[10] && s_stat.status[11] && !s_stat.status[5];
|
||||
spi_master_core u_master_core (
|
||||
.apb_pending_i (s_fifo_pending),
|
||||
.clk_i (clk_i),
|
||||
|
||||
+34
-31
@@ -220,37 +220,40 @@ module sdio_dma #(
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats(16)
|
||||
) u_dma_axi4_master (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o(s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_error),
|
||||
.read_done_o (s_read_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strb),
|
||||
.write_done_o (s_write_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_error),
|
||||
.axi4 (dma_axi4)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o (s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_error),
|
||||
.read_done_o (s_read_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strb),
|
||||
.write_done_o (s_write_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_error),
|
||||
.origin_enable_i (1'b0),
|
||||
.read_origin_channel_i (3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
always_comb begin
|
||||
|
||||
+34
-31
@@ -299,37 +299,40 @@ module usb2_dma #(
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats(16)
|
||||
) u_axi4_master (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o(s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_err),
|
||||
.read_done_o (s_read_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strb),
|
||||
.write_done_o (s_write_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_err),
|
||||
.axi4 (dma_axi4)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (s_read_start_valid),
|
||||
.read_start_ready_o (s_read_start_ready),
|
||||
.read_addr_i (s_read_addr),
|
||||
.read_beats_i (s_read_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (s_read_busy),
|
||||
.read_beat_valid_o (s_read_beat_valid),
|
||||
.read_beat_ready_i (s_read_beat_ready),
|
||||
.read_data_o (s_read_data),
|
||||
.read_resp_o (s_read_resp),
|
||||
.read_last_o (s_read_last),
|
||||
.read_expected_last_o (s_read_expected_last),
|
||||
.read_id_error_o (s_read_id_err),
|
||||
.read_done_o (s_read_done),
|
||||
.write_start_valid_i (s_write_start_valid),
|
||||
.write_start_ready_o (s_write_start_ready),
|
||||
.write_addr_i (s_write_addr),
|
||||
.write_beats_i (s_write_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (s_write_busy),
|
||||
.write_data_valid_i (s_write_data_valid),
|
||||
.write_data_ready_o (s_write_data_ready),
|
||||
.write_data_i (s_write_data),
|
||||
.write_strb_i (s_write_strb),
|
||||
.write_done_o (s_write_done),
|
||||
.write_resp_o (s_write_resp),
|
||||
.write_id_error_o (s_write_id_err),
|
||||
.origin_enable_i (1'b0),
|
||||
.read_origin_channel_i (3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
always_comb begin
|
||||
|
||||
+4
-2
@@ -151,6 +151,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
|
||||
$(ROOT_PATH)/crt/src/hal/jpeg_math.c \
|
||||
$(ROOT_PATH)/crt/src/hal/jpeg.c \
|
||||
$(ROOT_PATH)/crt/src/hal/dvp.c \
|
||||
$(ROOT_PATH)/crt/src/hal/spi_math.c \
|
||||
$(ROOT_PATH)/crt/src/hal/spi.c \
|
||||
$(ROOT_PATH)/crt/src/hal/perf.c \
|
||||
$(ROOT_PATH)/crt/src/hal/lcd.c \
|
||||
$(ROOT_PATH)/crt/src/hal/onchip_sram.c \
|
||||
@@ -183,7 +185,7 @@ TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \
|
||||
stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \
|
||||
gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \
|
||||
dma_math.c dma.c onchip_sram.c xpi.c xpi_psram.c xpi_psram_math.c rng.c crc.c ws2812_math.c ws2812.c \
|
||||
crypto.c crypto_lifecycle.c i2s_math.c i2s.c dvp.c sdio_math.c sdio.c rcu.c
|
||||
crypto.c crypto_lifecycle.c i2s_math.c i2s.c dvp.c sdio_math.c sdio.c spi_math.c spi.c rcu.c
|
||||
CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source)))
|
||||
CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c
|
||||
endif
|
||||
@@ -307,4 +309,4 @@ firmware: $(FIRMWARE_ELF)
|
||||
|
||||
firmware asm: | manifest
|
||||
|
||||
.PHONY: FORCE_VERSION
|
||||
.PHONY: FORCE_VERSION
|
||||
@@ -444,6 +444,15 @@
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_SPI",
|
||||
"base": "0x1001D000",
|
||||
"size": "0x00001000",
|
||||
"route": "apb4_periph",
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
}
|
||||
],
|
||||
"legacy_rib": false,
|
||||
|
||||
@@ -63,6 +63,15 @@
|
||||
/ip/serial/i2s_send.sv
|
||||
/ip/serial/i2s_core.sv
|
||||
/ip/serial/apb4_i2s.sv
|
||||
/ip/serial/spi_pkg.sv
|
||||
/ip/serial/spi_ctrl_if.sv
|
||||
/ip/serial/spi_phase_ctrl.sv
|
||||
/ip/serial/spi_word_packer.sv
|
||||
/ip/serial/spi_gpio_guard.sv
|
||||
/ip/serial/spi_master_core.sv
|
||||
/ip/serial/spi_reg.sv
|
||||
/ip/serial/spi_dma_pacing.sv
|
||||
/ip/serial/apb4_spi.sv
|
||||
/ip/serial/ws2812_if.sv
|
||||
/ip/serial/ws2812_reg.sv
|
||||
/ip/serial/ws2812_core.sv
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"phase": "TINY-R2-P7",
|
||||
"clock_domain": "system",
|
||||
"target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller",
|
||||
"target_count": 28,
|
||||
"target_count": 29,
|
||||
"apb_bridges": "SYS frontends; acknowledged per-endpoint crossings to AON, PCLK and MEM",
|
||||
"fabric_reset": "fabric leaf; target reset blocks admission and aborts accepted target work with DECERR",
|
||||
"hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active",
|
||||
@@ -161,7 +161,8 @@
|
||||
"crypto",
|
||||
"i2s",
|
||||
"sdio",
|
||||
"dvp"
|
||||
"dvp",
|
||||
"spi"
|
||||
],
|
||||
"cpu_prerequisite": "all global leaf resets and actual APB/DMA/XPI/RCU barriers; boot readiness latches until the next system reset",
|
||||
"hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync",
|
||||
@@ -480,7 +481,8 @@
|
||||
"ws2812",
|
||||
"crypto",
|
||||
"i2s",
|
||||
"dvp"
|
||||
"dvp",
|
||||
"spi"
|
||||
]
|
||||
},
|
||||
"same_clock_isolation": [
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
],
|
||||
"xpi_target": 4,
|
||||
"apb_target_base": 5,
|
||||
"decode_error_target": 26,
|
||||
"protocol_error_target": 27,
|
||||
"target_count": 28
|
||||
"decode_error_target": 27,
|
||||
"protocol_error_target": 28,
|
||||
"target_count": 29
|
||||
},
|
||||
"gpio_pins": 32,
|
||||
"irq_vector_width": 32,
|
||||
@@ -172,6 +172,12 @@
|
||||
"regions": [
|
||||
"APB4_DVP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spi",
|
||||
"regions": [
|
||||
"APB4_SPI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"interrupts": [
|
||||
@@ -265,6 +271,11 @@
|
||||
"core_bit": 23,
|
||||
"signal": "s_crypto_irq"
|
||||
},
|
||||
{
|
||||
"name": "spi",
|
||||
"core_bit": 25,
|
||||
"signal": "s_spi_irq"
|
||||
},
|
||||
{
|
||||
"name": "rcu",
|
||||
"core_bit": 31,
|
||||
@@ -677,8 +688,8 @@
|
||||
"pin": 27,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "s_spi_sck",
|
||||
"oe": "s_spi_output_enable"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -695,8 +706,8 @@
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "s_spi_mosi",
|
||||
"oe": "s_spi_output_enable"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -720,7 +731,9 @@
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
"inputs": [
|
||||
"s_spi_miso"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
}
|
||||
@@ -734,12 +747,12 @@
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "s_spi_cs_n",
|
||||
"oe": "s_spi_output_enable"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dma_request_mask": "0x39ff",
|
||||
"dma_request_mask": "0x339ff",
|
||||
"private_crypto": {
|
||||
"banks": 6,
|
||||
"bank_bytes": 4096,
|
||||
|
||||
+117
-46
@@ -89,15 +89,15 @@ module retrosoc_tiny (
|
||||
logic [31:0] s_clkout_control;
|
||||
logic s_clkout, s_clkout_enable;
|
||||
logic s_clkout_idle_ref;
|
||||
logic [14:0] s_gate_request, s_local_reset, s_actual_gate, s_reset_held, s_reset_ready;
|
||||
logic [14:0] s_gate_ref, s_held_ref, s_ready_ref, s_disabled_sys;
|
||||
logic [16:0] s_gate_request, s_local_reset, s_actual_gate, s_reset_held, s_reset_ready;
|
||||
logic [16:0] s_gate_ref, s_held_ref, s_ready_ref, s_disabled_sys;
|
||||
logic s_periph_apply, s_periph_reset_command, s_periph_block, s_periph_done;
|
||||
logic [14:0] s_periph_request, s_periph_reset_done;
|
||||
logic [16:0] s_periph_request, s_periph_reset_done;
|
||||
logic [4:0] s_periph_fault;
|
||||
logic s_bus_idle_ref;
|
||||
logic s_boot_release_d, s_boot_release_q;
|
||||
logic s_wfi_gate_sys;
|
||||
logic [14:0] s_periph_idle, s_periph_idle_ref;
|
||||
logic [16:0] s_periph_idle, s_periph_idle_ref;
|
||||
logic s_xpi_idle, s_xpi_idle_ref;
|
||||
logic s_pwm_write, s_crc_write;
|
||||
logic [ 4:0] s_clock_fault;
|
||||
@@ -110,6 +110,20 @@ module retrosoc_tiny (
|
||||
logic s_i2s_tx_stall, s_i2s_rx_stall;
|
||||
logic s_i2s_reset_ready;
|
||||
logic s_dvp_irq, s_dvp_idle, s_dvp_reset_ready;
|
||||
logic s_spi_sck, s_spi_mosi, s_spi_cs_n, s_spi_output_enable, s_spi_miso;
|
||||
logic s_spi_irq, s_spi_session_owned, s_spi_lifecycle_idle;
|
||||
logic [1:0] s_spi_origin;
|
||||
logic [2:0] s_spi_channel;
|
||||
logic [31:0] s_gpio_user_select, s_gpio_user_handoff, s_gpio_alt_enable;
|
||||
logic [31:0] s_gpio_alt_select, s_gpio_output_enable, s_gpio_open_drain;
|
||||
logic [1:0] s_spi_credit_valid, s_spi_credit_retire, s_spi_dma_start_pending;
|
||||
logic [3:0] s_spi_dma_status;
|
||||
logic s_spi_dma_error;
|
||||
// Commit strobes are register-visible inside the SPI shell; the SoC has no consumer yet.
|
||||
/* verilator lint_off UNUSEDSIGNAL */
|
||||
logic [1:0] s_spi_credit_commit;
|
||||
/* verilator lint_on UNUSEDSIGNAL */
|
||||
logic [4:0] s_spi_request;
|
||||
logic s_done_pclk_rst_n, s_done_mem_rst_n;
|
||||
`include "tiny_apb_interfaces.svh"
|
||||
ahbl_if u_cpu_ahbl_if[2] (
|
||||
@@ -121,7 +135,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_sram_axi4_if[4] (
|
||||
.aclk (s_exec_clk),
|
||||
.aresetn(s_sram_rst_n)
|
||||
@@ -151,7 +165,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_masters_axi4_if[3] (
|
||||
.aclk (s_exec_clk),
|
||||
.aresetn(s_master_rst_n)
|
||||
@@ -160,7 +174,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_dma_pclk_axi4_if (
|
||||
.aclk (s_dma_clk),
|
||||
.aresetn(s_dma_rst_n)
|
||||
@@ -169,7 +183,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_dma_admitted_axi4_if (
|
||||
.aclk (s_backend_pclk),
|
||||
.aresetn(s_dma_rst_n)
|
||||
@@ -178,7 +192,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_sdio_sys_axi4_if (
|
||||
.aclk (s_sdio_clk),
|
||||
.aresetn(s_sdio_rst_n)
|
||||
@@ -187,7 +201,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (s_exec_clk),
|
||||
.aresetn(s_target_rst_n)
|
||||
@@ -196,7 +210,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_xpi_mem_axi4_if (
|
||||
.aclk (s_backend_mem_clk),
|
||||
.aresetn(s_backend_mem_rst_n)
|
||||
@@ -392,11 +406,14 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.dat_i (s_gate_request[0]),
|
||||
.dat_o (s_wfi_gate_sys)
|
||||
);
|
||||
assign s_reset_held[0] = 1'b0;
|
||||
assign s_reset_ready[0] = 1'b0;
|
||||
assign s_reset_held[0] = 1'b0;
|
||||
assign s_reset_ready[0] = 1'b0;
|
||||
assign s_actual_gate[15] = 1'b0;
|
||||
assign s_reset_held[15] = 1'b0;
|
||||
assign s_reset_ready[15] = 1'b0;
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(15)
|
||||
.DATA_WIDTH(17)
|
||||
) u_gate_feedback (
|
||||
.clk_i (s_ref_clk),
|
||||
.rst_n_i(s_por_rst_n),
|
||||
@@ -405,7 +422,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
);
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(15)
|
||||
.DATA_WIDTH(17)
|
||||
) u_held_feedback (
|
||||
.clk_i (s_ref_clk),
|
||||
.rst_n_i(s_por_rst_n),
|
||||
@@ -414,7 +431,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
);
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(15)
|
||||
.DATA_WIDTH(17)
|
||||
) u_ready_feedback (
|
||||
.clk_i (s_ref_clk),
|
||||
.rst_n_i(s_por_rst_n),
|
||||
@@ -423,7 +440,7 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
);
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(15)
|
||||
.DATA_WIDTH(17)
|
||||
) u_disabled_sync (
|
||||
.clk_i (s_exec_clk),
|
||||
.rst_n_i(s_fabric_rst_n),
|
||||
@@ -477,11 +494,14 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
.crc_idle_o (s_periph_idle[12])
|
||||
);
|
||||
assign s_periph_idle[0] = s_cpu_bus_idle;
|
||||
assign s_periph_idle[1] = 1'b1; // GPIO has no independent transaction engine; USER is absent.
|
||||
assign s_periph_idle[1] = !s_spi_session_owned && !(|s_gpio_user_select);
|
||||
assign s_periph_idle[11] = !s_rng_en && !s_rng_valid;
|
||||
assign s_periph_idle[15] = 1'b1;
|
||||
assign s_periph_idle[16] = s_spi_lifecycle_idle;
|
||||
assign s_spi_request = u_spi_apb4_if.pwrite ? 5'd16 : 5'd17;
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(15)
|
||||
.DATA_WIDTH(17)
|
||||
) u_periph_idle_sync (
|
||||
.clk_i (s_ref_clk),
|
||||
.rst_n_i(s_por_rst_n),
|
||||
@@ -706,7 +726,8 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
);
|
||||
tiny_axi4_async_bridge #(
|
||||
.DataWidth(32),
|
||||
.IdWidth (1)
|
||||
.IdWidth (1),
|
||||
.UserWidth(4)
|
||||
) u_dma_cdc (
|
||||
.src_clk_i (s_dma_clk),
|
||||
.src_rst_n_i (s_dma_rst_n),
|
||||
@@ -737,7 +758,8 @@ ahbl_if u_cpu_ahbl_if[2] (
|
||||
);
|
||||
tiny_axi4_async_bridge #(
|
||||
.DataWidth(32),
|
||||
.IdWidth (1)
|
||||
.IdWidth (1),
|
||||
.UserWidth(4)
|
||||
) u_xpi_mem_cdc (
|
||||
.src_clk_i (s_exec_clk),
|
||||
.src_rst_n_i (s_fabric_rst_n),
|
||||
@@ -800,7 +822,7 @@ axi4_error_slave #(
|
||||
.PllPresent (TinyPllPresent),
|
||||
.PllProfiles(TinyPllPresent),
|
||||
.XinHzValue (`SOC_EXT_CLK_HZ),
|
||||
.TargetMask (15'h07fff)
|
||||
.TargetMask (17'h17fff)
|
||||
) u_sysctrl (
|
||||
.clk_i (s_exec_clk),
|
||||
.rst_n_i (s_sysctrl_rst_n),
|
||||
@@ -860,7 +882,7 @@ axi4_error_slave #(
|
||||
.ref_clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.idle_i (s_clock_safe),
|
||||
.clients_idle_i ((&s_periph_idle_ref[14:1]) && s_xpi_idle_ref),
|
||||
.clients_idle_i ((&s_periph_idle_ref[16:1]) && s_xpi_idle_ref),
|
||||
.warm_reset_i (u_wdg_if.reset_req_o),
|
||||
.system_request_i (s_system_reset),
|
||||
.command_ack_i (s_cpu_clock_idle && s_target_idle[`RETROSOC_TINY__TARGET_SYSCTRL]),
|
||||
@@ -900,11 +922,49 @@ axi4_error_slave #(
|
||||
.HasPullUp (gpio_pad_caps_pkg::HasPullUp),
|
||||
.HasPullDown (gpio_pad_caps_pkg::HasPullDown)
|
||||
) u_gpio (
|
||||
.clk_i (s_gpio_clk),
|
||||
.rst_n_i (s_gpio_rst_n),
|
||||
.apb4 (u_gpio_apb4_if),
|
||||
.gpio (u_gpio_if),
|
||||
.user_gpio(u_user_gpio_if)
|
||||
.clk_i (s_gpio_clk),
|
||||
.rst_n_i (s_gpio_rst_n),
|
||||
.apb4 (u_gpio_apb4_if),
|
||||
.gpio (u_gpio_if),
|
||||
.user_gpio (u_user_gpio_if),
|
||||
.user_select_o (s_gpio_user_select),
|
||||
.user_handoff_o (s_gpio_user_handoff),
|
||||
.alt_enable_o (s_gpio_alt_enable),
|
||||
.alt_select_o (s_gpio_alt_select),
|
||||
.output_enable_o(s_gpio_output_enable),
|
||||
.open_drain_o (s_gpio_open_drain)
|
||||
);
|
||||
apb4_spi #(
|
||||
.EnableDma(1'b1)
|
||||
) u_spi (
|
||||
.clk_i (s_spi_clk),
|
||||
.rst_n_i (s_spi_rst_n),
|
||||
.clock_hz_i (s_pclk_report_q),
|
||||
.origin_i (s_spi_origin),
|
||||
.request_i (s_spi_request),
|
||||
.channel_i (s_spi_channel),
|
||||
.credit_valid_i (s_spi_credit_valid),
|
||||
.credit_retire_i (s_spi_credit_retire),
|
||||
.dma_status_i (s_spi_dma_status),
|
||||
.dma_start_pending_i(s_spi_dma_start_pending),
|
||||
.dma_error_i (s_spi_dma_error),
|
||||
.user_select_i (s_gpio_user_select),
|
||||
.handoff_i (s_gpio_user_handoff),
|
||||
.alt_enable_i (s_gpio_alt_enable),
|
||||
.alt_select_i (s_gpio_alt_select),
|
||||
.output_enable_i (s_gpio_output_enable),
|
||||
.open_drain_i (s_gpio_open_drain),
|
||||
.gpio_ready_i (s_gpio_rst_n && s_apb_cdc_ready[0]),
|
||||
.miso_i (s_spi_miso),
|
||||
.sck_o (s_spi_sck),
|
||||
.mosi_o (s_spi_mosi),
|
||||
.cs_n_o (s_spi_cs_n),
|
||||
.output_enable_o (s_spi_output_enable),
|
||||
.irq_o (s_spi_irq),
|
||||
.credit_commit_o (s_spi_credit_commit),
|
||||
.session_owned_o (s_spi_session_owned),
|
||||
.lifecycle_idle_o (s_spi_lifecycle_idle),
|
||||
.apb4 (u_spi_apb4_if)
|
||||
);
|
||||
gpio_pad_bridge u_gpio_pads (
|
||||
.inner(u_gpio_if),
|
||||
@@ -1002,25 +1062,36 @@ cdc_sync #(
|
||||
.lifecycle_reset_ready_o(s_dvp_reset_ready)
|
||||
);
|
||||
apb4_dma #(
|
||||
.NumChannels (8),
|
||||
.MaxBurstBeats(16),
|
||||
.FifoDepth (32),
|
||||
.RequestMask (16'h39ff),
|
||||
.EnableStreams(1'b1)
|
||||
.NumChannels (8),
|
||||
.MaxBurstBeats (16),
|
||||
.FifoDepth (32),
|
||||
.RequestMask (32'h0003_39ff),
|
||||
.SupportedRequests (32'h0003_39ff),
|
||||
.IpVersion (32'h0002_0002),
|
||||
.EnableStreams (1'b1),
|
||||
.EnableSpi (1'b1),
|
||||
.EnableOriginQualifier(1'b1),
|
||||
.SpiTxAddress (32'h1001_d054),
|
||||
.SpiRxAddress (32'h1001_d058)
|
||||
) u_dma (
|
||||
.lifecycle_idle_o(s_periph_idle[9]),
|
||||
.clk_i (s_dma_clk),
|
||||
.rst_n_i (s_dma_rst_n),
|
||||
.dma_xfer_done_o (s_dma_done),
|
||||
.irq_o (s_dma_irq),
|
||||
.hw_trg (u_dma_req_if),
|
||||
.apb4 (u_dma_apb4_if),
|
||||
.axi4 (u_dma_pclk_axi4_if),
|
||||
.i2s_tx_axis (u_i2s_tx_axis_if),
|
||||
.i2s_rx_axis (u_i2s_rx_axis_if),
|
||||
.dvp_rx_axis (u_dvp_rx_axis_if),
|
||||
.crypto_in_axis (u_crypto_tx_axis_if),
|
||||
.crypto_out_axis (u_crypto_rx_axis_if)
|
||||
.lifecycle_idle_o (s_periph_idle[9]),
|
||||
.clk_i (s_dma_clk),
|
||||
.rst_n_i (s_dma_rst_n),
|
||||
.dma_xfer_done_o (s_dma_done),
|
||||
.irq_o (s_dma_irq),
|
||||
.hw_trg (u_dma_req_if),
|
||||
.apb4 (u_dma_apb4_if),
|
||||
.axi4 (u_dma_pclk_axi4_if),
|
||||
.i2s_tx_axis (u_i2s_tx_axis_if),
|
||||
.i2s_rx_axis (u_i2s_rx_axis_if),
|
||||
.dvp_rx_axis (u_dvp_rx_axis_if),
|
||||
.crypto_in_axis (u_crypto_tx_axis_if),
|
||||
.crypto_out_axis (u_crypto_rx_axis_if),
|
||||
.spi_credit_valid_o (s_spi_credit_valid),
|
||||
.spi_credit_retire_o(s_spi_credit_retire),
|
||||
.spi_start_pending_o(s_spi_dma_start_pending),
|
||||
.spi_status_o (s_spi_dma_status),
|
||||
.spi_error_o (s_spi_dma_error)
|
||||
);
|
||||
assign u_dma_req_if.i2s_tx_proc = !s_i2s_tx_stall;
|
||||
assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall;
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
// error; it never replays it when the destination returns. The SYS endpoint
|
||||
// remains clocked so a stopped/disabled peripheral is still reachable.
|
||||
module tiny_apb_async_bridge (
|
||||
input logic src_clk_i,
|
||||
input logic src_rst_n_i,
|
||||
input logic dst_clk_i,
|
||||
input logic dst_rst_n_i,
|
||||
input logic disabled_i,
|
||||
output logic idle_o,
|
||||
output logic ready_o,
|
||||
apb4_if.slave src_apb4,
|
||||
apb4_if.master dst_apb4
|
||||
input logic src_clk_i,
|
||||
input logic src_rst_n_i,
|
||||
input logic dst_clk_i,
|
||||
input logic dst_rst_n_i,
|
||||
input logic disabled_i,
|
||||
input logic [1:0] src_origin_i,
|
||||
input logic [2:0] src_channel_i,
|
||||
output logic idle_o,
|
||||
output logic ready_o,
|
||||
output logic [1:0] dst_origin_o,
|
||||
output logic [2:0] dst_channel_o,
|
||||
apb4_if.slave src_apb4,
|
||||
apb4_if.master dst_apb4
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
SrcIdle,
|
||||
@@ -30,19 +34,19 @@ module tiny_apb_async_bridge (
|
||||
} dst_state_e;
|
||||
typedef struct packed {
|
||||
src_state_e state;
|
||||
logic [71:0] req;
|
||||
logic [76:0] req;
|
||||
logic [32:0] resp;
|
||||
} src_t;
|
||||
typedef struct packed {
|
||||
dst_state_e state;
|
||||
logic [71:0] req;
|
||||
logic [76:0] req;
|
||||
logic [32:0] resp;
|
||||
} dst_t;
|
||||
src_t s_src_d, s_src_q;
|
||||
dst_t s_dst_d, s_dst_q;
|
||||
logic s_link_src_rst_n, s_link_dst_rst_n;
|
||||
logic s_req_ready, s_req_valid, s_resp_ready, s_resp_valid;
|
||||
logic [71:0] s_req_data;
|
||||
logic [76:0] s_req_data;
|
||||
logic [32:0] s_resp_data;
|
||||
|
||||
cdc_reset_barrier #(
|
||||
@@ -56,7 +60,7 @@ module tiny_apb_async_bridge (
|
||||
.rst_b_n_o(s_link_dst_rst_n)
|
||||
);
|
||||
cdc_2phase #(
|
||||
.DATA_WIDTH(72)
|
||||
.DATA_WIDTH(77)
|
||||
) u_req_cdc (
|
||||
.src_clk_i (src_clk_i),
|
||||
.src_rst_n_i(s_link_src_rst_n),
|
||||
@@ -89,10 +93,12 @@ module tiny_apb_async_bridge (
|
||||
assign src_apb4.pslverr = s_src_q.resp[32];
|
||||
assign src_apb4.prdata = s_src_q.resp[31:0];
|
||||
assign {dst_apb4.paddr, dst_apb4.pprot, dst_apb4.pwrite,
|
||||
dst_apb4.pwdata, dst_apb4.pstrb} = s_dst_q.req;
|
||||
dst_apb4.pwdata, dst_apb4.pstrb} = s_dst_q.req[71:0];
|
||||
assign dst_apb4.psel = s_link_dst_rst_n &&
|
||||
((s_dst_q.state == DstSetup) || (s_dst_q.state == DstAccess));
|
||||
assign dst_apb4.penable = s_link_dst_rst_n && (s_dst_q.state == DstAccess);
|
||||
assign dst_origin_o = s_dst_q.req[76:75];
|
||||
assign dst_channel_o = s_dst_q.req[74:72];
|
||||
|
||||
always_comb begin
|
||||
s_src_d = s_src_q;
|
||||
@@ -100,7 +106,13 @@ module tiny_apb_async_bridge (
|
||||
SrcIdle:
|
||||
if (src_apb4.psel && src_apb4.penable) begin
|
||||
s_src_d.req = {
|
||||
src_apb4.paddr, src_apb4.pprot, src_apb4.pwrite, src_apb4.pwdata, src_apb4.pstrb
|
||||
src_origin_i,
|
||||
src_channel_i,
|
||||
src_apb4.paddr,
|
||||
src_apb4.pprot,
|
||||
src_apb4.pwrite,
|
||||
src_apb4.pwdata,
|
||||
src_apb4.pstrb
|
||||
};
|
||||
s_src_d.resp = {1'b1, 32'd0};
|
||||
s_src_d.state = disabled_i ? SrcResponse : SrcSend;
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
// APB setup/access are separate cycles; rejected bursts never assert PSEL.
|
||||
module tiny_axi42apb4 (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
axi4_if.slave axi4,
|
||||
apb4_if.master apb4
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
axi4_if.slave axi4,
|
||||
apb4_if.master apb4,
|
||||
output logic [1:0] origin_o,
|
||||
output logic [2:0] channel_o
|
||||
);
|
||||
typedef enum logic [2:0] {
|
||||
Idle,
|
||||
@@ -30,6 +32,7 @@ module tiny_axi42apb4 (
|
||||
logic [1:0] s_resp_d, s_resp_q;
|
||||
logic [7:0] s_len_d, s_len_q;
|
||||
logic [7:0] s_beat_d, s_beat_q;
|
||||
logic [3:0] s_user_d, s_user_q;
|
||||
logic s_access, s_enable, s_done, s_err;
|
||||
logic [31:0] s_read_data;
|
||||
|
||||
@@ -61,6 +64,8 @@ module tiny_axi42apb4 (
|
||||
assign apb4.pprot = 3'd0;
|
||||
assign apb4.psel = s_access;
|
||||
assign apb4.penable = s_enable;
|
||||
assign origin_o = s_user_q[3] ? 2'd1 : (s_user_q == 4'd0 ? 2'd0 : 2'd2);
|
||||
assign channel_o = s_user_q[2:0];
|
||||
|
||||
always_comb begin
|
||||
s_state_d = s_state_q;
|
||||
@@ -73,6 +78,7 @@ module tiny_axi42apb4 (
|
||||
s_data_d = s_data_q;
|
||||
s_len_d = s_len_q;
|
||||
s_beat_d = s_beat_q;
|
||||
s_user_d = s_user_q;
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
s_beat_d = '0;
|
||||
@@ -82,6 +88,7 @@ module tiny_axi42apb4 (
|
||||
s_addr_d = axi4.araddr;
|
||||
s_id_d = axi4.arid;
|
||||
s_write_d = 1'b0;
|
||||
s_user_d = axi4.aruser;
|
||||
s_len_d = axi4.arlen;
|
||||
if ((axi4.arlen != 8'd0) || (axi4.arburst[1]) || axi4.arlock ||
|
||||
(axi4.arsize > 3'd2) || (axi4.araddr[1:0] != 2'd0)) begin
|
||||
@@ -92,6 +99,7 @@ module tiny_axi42apb4 (
|
||||
s_addr_d = axi4.awaddr;
|
||||
s_id_d = axi4.awid;
|
||||
s_write_d = 1'b1;
|
||||
s_user_d = axi4.awuser;
|
||||
s_len_d = axi4.awlen;
|
||||
if ((axi4.awlen != 8'd0) || (axi4.awburst[1]) || axi4.awlock ||
|
||||
(axi4.awsize > 3'd2) || (axi4.awaddr[1:0] != 2'd0)) begin
|
||||
@@ -210,4 +218,12 @@ module tiny_axi42apb4 (
|
||||
.dat_i (s_beat_d),
|
||||
.dat_o (s_beat_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(4)
|
||||
) u_user_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_user_d),
|
||||
.dat_o (s_user_q)
|
||||
);
|
||||
endmodule
|
||||
@@ -23,7 +23,7 @@ module tiny_axi4_admission (
|
||||
logic [2:0] prot;
|
||||
logic [3:0] qos;
|
||||
logic [3:0] region;
|
||||
logic [0:0] user;
|
||||
logic [3:0] user;
|
||||
} address_t;
|
||||
address_t s_aw_d, s_aw_q, s_ar_d, s_ar_q;
|
||||
logic [1:0] s_pending_d, s_pending_q, s_sent_d, s_sent_q;
|
||||
|
||||
@@ -29,10 +29,10 @@ module tiny_axi4_fabric (
|
||||
logic s_cpu_prefer_write_d, s_cpu_prefer_write_q;
|
||||
logic [2:0][4:0] s_read_target, s_write_target;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][2:0] s_arready, s_awready, s_wready, s_rvalid, s_bvalid;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][36:0] s_rdata;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 3:0] s_bdata;
|
||||
logic [ 2:0][36:0] s_read_return;
|
||||
logic [ 2:0][ 3:0] s_write_return;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][39:0] s_rdata;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 6:0] s_bdata;
|
||||
logic [ 2:0][39:0] s_read_return;
|
||||
logic [ 2:0][ 6:0] s_write_return;
|
||||
logic [2:0] s_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid;
|
||||
logic [2:0] s_arvalid, s_awvalid;
|
||||
logic [1:0][63:0] s_wait_d, s_wait_q;
|
||||
@@ -191,7 +191,7 @@ module tiny_axi4_fabric (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_routes[3] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
|
||||
@@ -36,7 +36,7 @@ module tiny_axi4_target (
|
||||
logic [3:0] qos;
|
||||
logic [3:0] region;
|
||||
logic id;
|
||||
logic user;
|
||||
logic [3:0] user;
|
||||
} addr_t;
|
||||
typedef struct packed {
|
||||
state_e state;
|
||||
@@ -56,20 +56,21 @@ module tiny_axi4_target (
|
||||
logic [31:0] s_req_addr;
|
||||
logic s_req_id;
|
||||
addr_t s_read_addr[3], s_write_addr[3];
|
||||
logic [2:0] s_arvalid, s_awvalid, s_wvalid, s_wlast, s_bready, s_rready, s_wuser;
|
||||
logic [2:0] s_arvalid, s_awvalid, s_wvalid, s_wlast, s_bready, s_rready;
|
||||
logic [2:0][ 3:0] s_wuser;
|
||||
logic [2:0][31:0] s_wdata;
|
||||
logic [2:0][ 3:0] s_wstrb;
|
||||
logic [2:0] s_owner_req, s_grant, s_prefer_write_d, s_prefer_write_q;
|
||||
logic [1:0] s_selected;
|
||||
logic s_valid, s_selected_write, s_accept, s_terminal;
|
||||
logic s_r_valid, s_r_ready, s_b_valid, s_b_ready;
|
||||
logic [36:0] s_r_data;
|
||||
logic [ 3:0] s_b_data;
|
||||
logic [39:0] s_r_data;
|
||||
logic [ 6:0] s_b_data;
|
||||
logic s_read_capture, s_write_capture;
|
||||
logic s_abort_read_valid, s_abort_write_valid;
|
||||
logic s_resp_active, s_rin_valid, s_bin_valid;
|
||||
logic [36:0] s_rin_data;
|
||||
logic [ 3:0] s_bin_data;
|
||||
logic [39:0] s_rin_data;
|
||||
logic [ 6:0] s_bin_data;
|
||||
|
||||
assign s_saved_addr = s_req_q.addr;
|
||||
assign s_req_addr = s_saved_addr.addr;
|
||||
@@ -89,7 +90,7 @@ module tiny_axi4_target (
|
||||
s_req_q.write_done && !s_req_q.b_captured;
|
||||
assign s_terminal = s_resp_active &&
|
||||
(s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) :
|
||||
(s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]));
|
||||
(s_r_valid && s_rready[s_req_q.owner] && s_r_data[4]));
|
||||
assign fault_valid_o = s_terminal && s_req_q.error_seen;
|
||||
assign fault_addr_o = s_req_q.error_addr;
|
||||
assign fault_master_o = s_req_q.owner;
|
||||
@@ -100,11 +101,11 @@ module tiny_axi4_target (
|
||||
!s_req_q.write && target.rvalid) || s_abort_read_valid;
|
||||
assign s_rin_data = s_abort_read_valid ?
|
||||
{s_req_id, 32'd0, 2'b11,
|
||||
(s_req_q.read_count == {1'b0, s_saved_addr.len}), 1'b0} :
|
||||
(s_req_q.read_count == {1'b0, s_saved_addr.len}), 4'd0} :
|
||||
{target.rid, target.rdata, target.rresp, target.rlast, target.ruser};
|
||||
assign s_bin_valid = ((s_req_q.state == Response) && enable_i &&
|
||||
s_req_q.write && target.bvalid) || s_abort_write_valid;
|
||||
assign s_bin_data = s_abort_write_valid ? {s_req_id, 2'b11, 1'b0} :
|
||||
assign s_bin_data = s_abort_write_valid ? {s_req_id, 2'b11, 4'd0} :
|
||||
{target.bid, target.bresp, target.buser};
|
||||
assign s_read_capture = s_rin_valid && s_r_ready;
|
||||
assign s_write_capture = s_bin_valid && s_b_ready;
|
||||
@@ -218,7 +219,7 @@ module tiny_axi4_target (
|
||||
assign target.rready = (s_req_q.state == Response) && enable_i && !s_req_q.write && s_r_ready;
|
||||
assign target.bready = (s_req_q.state == Response) && enable_i && s_req_q.write && s_b_ready;
|
||||
spill_register #(
|
||||
.DATA_WIDTH(37)
|
||||
.DATA_WIDTH(40)
|
||||
) u_read_response (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
@@ -231,7 +232,7 @@ module tiny_axi4_target (
|
||||
.data_o (s_r_data)
|
||||
);
|
||||
spill_register #(
|
||||
.DATA_WIDTH(4)
|
||||
.DATA_WIDTH(7)
|
||||
) u_write_response (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
@@ -248,11 +249,24 @@ module tiny_axi4_target (
|
||||
unique case (s_req_q.state)
|
||||
Idle:
|
||||
if (s_accept) begin
|
||||
s_req_d = '0;
|
||||
s_req_d.state = Address;
|
||||
s_req_d.owner = s_selected;
|
||||
s_req_d.write = s_selected_write;
|
||||
s_req_d.addr = s_selected_addr;
|
||||
s_req_d = '0;
|
||||
s_req_d.state = Address;
|
||||
s_req_d.owner = s_selected;
|
||||
s_req_d.write = s_selected_write;
|
||||
s_req_d.addr = {
|
||||
s_selected_addr.addr,
|
||||
s_selected_addr.len,
|
||||
s_selected_addr.size,
|
||||
s_selected_addr.burst,
|
||||
s_selected_addr.lock,
|
||||
s_selected_addr.cache,
|
||||
s_selected_addr.prot,
|
||||
s_selected_addr.qos,
|
||||
s_selected_addr.region,
|
||||
s_selected_addr.id,
|
||||
(s_selected == 2'd1) ? {1'b1, s_selected_addr.user[2:0]} :
|
||||
(s_selected == 2'd2) ? 4'b0100 : 4'b0000
|
||||
};
|
||||
s_req_d.read_addr = s_selected_addr.addr;
|
||||
end
|
||||
Address: begin
|
||||
@@ -286,18 +300,18 @@ module tiny_axi4_target (
|
||||
s_req_d.read_count = s_req_q.read_count + 9'd1;
|
||||
if (s_saved_addr.burst == 2'b01)
|
||||
s_req_d.read_addr = s_req_q.read_addr + (32'd1 << s_saved_addr.size);
|
||||
if (!s_req_q.error_seen && (s_rin_data[3:2] != 2'b00)) begin
|
||||
if (!s_req_q.error_seen && (s_rin_data[6:5] != 2'b00)) begin
|
||||
s_req_d.error_seen = 1'b1;
|
||||
s_req_d.error_addr = s_req_q.read_addr;
|
||||
s_req_d.error_resp = s_rin_data[3:2];
|
||||
s_req_d.error_resp = s_rin_data[6:5];
|
||||
end
|
||||
end
|
||||
if (s_write_capture) begin
|
||||
s_req_d.b_captured = 1'b1;
|
||||
if (!s_req_q.error_seen && (s_bin_data[2:1] != 2'b00)) begin
|
||||
if (!s_req_q.error_seen && (s_bin_data[5:4] != 2'b00)) begin
|
||||
s_req_d.error_seen = 1'b1;
|
||||
s_req_d.error_addr = s_req_addr;
|
||||
s_req_d.error_resp = s_bin_data[2:1];
|
||||
s_req_d.error_resp = s_bin_data[5:4];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ module tiny_cpu_mem #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) u_slow_axi4_if (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
|
||||
@@ -9,19 +9,19 @@ module tiny_peripheral_control (
|
||||
input logic warm_reset_i,
|
||||
input logic apply_i,
|
||||
input logic reset_command_i,
|
||||
input logic [14:0] request_i,
|
||||
input logic [14:0] idle_i,
|
||||
input logic [16:0] request_i,
|
||||
input logic [16:0] idle_i,
|
||||
input logic bus_idle_i,
|
||||
input logic [14:0] gate_actual_i,
|
||||
input logic [14:0] reset_held_i,
|
||||
input logic [14:0] reset_ready_i,
|
||||
input logic [16:0] gate_actual_i,
|
||||
input logic [16:0] reset_held_i,
|
||||
input logic [16:0] reset_ready_i,
|
||||
input logic [31:0] timeout_i,
|
||||
output logic [14:0] gate_o,
|
||||
output logic [14:0] reset_o,
|
||||
output logic [16:0] gate_o,
|
||||
output logic [16:0] reset_o,
|
||||
output logic block_o,
|
||||
output logic done_o,
|
||||
output logic [ 4:0] fault_o,
|
||||
output logic [14:0] reset_done_o
|
||||
output logic [16:0] reset_done_o
|
||||
);
|
||||
typedef enum logic [2:0] {
|
||||
Idle,
|
||||
@@ -34,20 +34,20 @@ module tiny_peripheral_control (
|
||||
typedef struct packed {
|
||||
state_e state;
|
||||
logic reset_command;
|
||||
logic [14:0] request_mask, gates, previous_gates, resets, reset_done, affected;
|
||||
logic [16:0] request_mask, gates, previous_gates, resets, reset_done, affected;
|
||||
logic [31:0] count;
|
||||
logic done;
|
||||
logic [4:0] fault;
|
||||
} control_t;
|
||||
control_t s_ctrl_d, s_ctrl_q;
|
||||
logic [14:0] s_affected;
|
||||
logic [16:0] s_affected;
|
||||
assign s_affected = s_ctrl_q.affected;
|
||||
assign gate_o = s_ctrl_q.gates;
|
||||
assign reset_o = s_ctrl_q.resets;
|
||||
assign reset_done_o = s_ctrl_q.reset_done;
|
||||
// Arming the WFI leaf permission must not pause an unrelated DMA job.
|
||||
// The CPU leaf itself waits for its accepted I/D work before stopping.
|
||||
assign block_o = (s_ctrl_q.state != Idle) && (s_affected[14:1] != 0);
|
||||
assign block_o = (s_ctrl_q.state != Idle) && (s_affected[16:1] != 0);
|
||||
assign done_o = s_ctrl_q.done;
|
||||
assign fault_o = s_ctrl_q.fault;
|
||||
always_comb begin
|
||||
@@ -74,8 +74,8 @@ module tiny_peripheral_control (
|
||||
end
|
||||
end
|
||||
Drain: begin
|
||||
if (s_ctrl_q.count >= 16 && ((s_affected[14:1] == 0) || (bus_idle_i && idle_i[9])) &&
|
||||
((idle_i[14:1] & s_affected[14:1]) == s_affected[14:1])) begin
|
||||
if (s_ctrl_q.count >= 16 && ((s_affected[16:1] == 0) || (bus_idle_i && idle_i[9])) &&
|
||||
((idle_i[16:1] & s_affected[16:1]) == s_affected[16:1])) begin
|
||||
s_ctrl_d.count = 0;
|
||||
if (s_ctrl_q.reset_command) begin
|
||||
s_ctrl_d.resets = s_ctrl_q.request_mask;
|
||||
@@ -90,7 +90,7 @@ module tiny_peripheral_control (
|
||||
s_ctrl_d.state = Idle;
|
||||
end
|
||||
end
|
||||
GateWait: if (gate_actual_i[14:1] == s_ctrl_q.gates[14:1]) s_ctrl_d.state = Complete;
|
||||
GateWait: if (gate_actual_i[16:1] == s_ctrl_q.gates[16:1]) s_ctrl_d.state = Complete;
|
||||
Hold:
|
||||
if ((reset_held_i & s_ctrl_q.request_mask) == s_ctrl_q.request_mask) begin
|
||||
s_ctrl_d.resets = 0;
|
||||
|
||||
@@ -7,7 +7,7 @@ module tiny_sysctrl #(
|
||||
parameter bit PllPresent = 1'b0,
|
||||
parameter bit PllProfiles = 1'b0,
|
||||
parameter int unsigned XinHzValue = 24_000_000,
|
||||
parameter logic [14:0] TargetMask = 15'd0
|
||||
parameter logic [16:0] TargetMask = 17'd0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
@@ -37,11 +37,11 @@ module tiny_sysctrl #(
|
||||
output logic [31:0] clock_timeout_o,
|
||||
input logic periph_done_i,
|
||||
input logic [ 4:0] periph_fault_i,
|
||||
input logic [14:0] gate_actual_i,
|
||||
input logic [14:0] reset_done_i,
|
||||
input logic [16:0] gate_actual_i,
|
||||
input logic [16:0] reset_done_i,
|
||||
output logic periph_apply_o,
|
||||
output logic periph_reset_o,
|
||||
output logic [14:0] periph_request_o,
|
||||
output logic [16:0] periph_request_o,
|
||||
output logic [31:0] clkout_control_o,
|
||||
input logic clkout_idle_i,
|
||||
output logic perf_enable_o,
|
||||
@@ -106,7 +106,7 @@ module tiny_sysctrl #(
|
||||
logic [1:0] s_clk_latched_d, s_clk_latched_q;
|
||||
logic s_system_reset_d, s_system_reset_q;
|
||||
logic s_periph_apply_d, s_periph_apply_q, s_periph_reset_d, s_periph_reset_q;
|
||||
logic [14:0] s_periph_mask_d, s_periph_mask_q;
|
||||
logic [16:0] s_periph_mask_d, s_periph_mask_q;
|
||||
logic [3:0] s_active_cmd_d, s_active_cmd_q;
|
||||
logic s_clock_apply_d, s_clock_apply_q;
|
||||
logic s_rcu_busy_d, s_rcu_busy_q;
|
||||
@@ -147,15 +147,19 @@ module tiny_sysctrl #(
|
||||
assign u_rcu_src.psel = apb4.psel && s_rcu_selected;
|
||||
assign u_rcu_src.penable = apb4.penable;
|
||||
tiny_apb_async_bridge u_rcu_cdc (
|
||||
.src_clk_i (clk_i),
|
||||
.src_rst_n_i(rst_n_i),
|
||||
.dst_clk_i (clk_ref_i),
|
||||
.dst_rst_n_i(rst_ref_n_i),
|
||||
.disabled_i (1'b0),
|
||||
.idle_o (),
|
||||
.ready_o (rcu_link_ready_o),
|
||||
.src_apb4 (u_rcu_src),
|
||||
.dst_apb4 (u_rcu_dst)
|
||||
.src_clk_i (clk_i),
|
||||
.src_rst_n_i (rst_n_i),
|
||||
.dst_clk_i (clk_ref_i),
|
||||
.dst_rst_n_i (rst_ref_n_i),
|
||||
.disabled_i (1'b0),
|
||||
.src_origin_i (2'd0),
|
||||
.src_channel_i(3'd0),
|
||||
.dst_origin_o (),
|
||||
.dst_channel_o(),
|
||||
.idle_o (),
|
||||
.ready_o (rcu_link_ready_o),
|
||||
.src_apb4 (u_rcu_src),
|
||||
.dst_apb4 (u_rcu_dst)
|
||||
);
|
||||
assign s_rcu_offset = {20'd0, u_rcu_dst.paddr[11:0]};
|
||||
assign s_rcu_req = u_rcu_dst.psel && u_rcu_dst.penable && !s_rcu_ready_q;
|
||||
@@ -306,7 +310,7 @@ module tiny_sysctrl #(
|
||||
s_rcu_read_data = s_gate_req_q;
|
||||
s_rcu_writable = 1'b1;
|
||||
end
|
||||
GateStatus: s_rcu_read_data = {17'd0, gate_actual_i};
|
||||
GateStatus: s_rcu_read_data = {15'd0, gate_actual_i};
|
||||
ResetRequest: begin
|
||||
s_rcu_read_data = s_rst_req_q;
|
||||
s_rcu_writable = 1'b1;
|
||||
@@ -334,7 +338,7 @@ module tiny_sysctrl #(
|
||||
MemHz: s_rcu_read_data = s_mem_hz_q;
|
||||
PclkHz: s_rcu_read_data = s_pclk_hz_q;
|
||||
ClintHz: s_rcu_read_data = 32'd1_000_000;
|
||||
TargetCapability: s_rcu_read_data = {17'd0, TargetMask};
|
||||
TargetCapability: s_rcu_read_data = {15'd0, TargetMask};
|
||||
ClkoutControl: begin
|
||||
s_rcu_read_data = s_clkout_control_q;
|
||||
s_rcu_writable = 1'b1;
|
||||
@@ -350,9 +354,9 @@ module tiny_sysctrl #(
|
||||
ClockRequest: s_rcu_config_err = s_rcu_busy_q || u_rcu_dst.pwdata[31:2] != 0;
|
||||
TimeoutRefCycles: s_rcu_config_err = s_rcu_busy_q || u_rcu_dst.pwdata == 0;
|
||||
GateRequest:
|
||||
s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{17'd0, TargetMask}) != 0;
|
||||
s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{15'd0, TargetMask}) != 0;
|
||||
ResetRequest:
|
||||
s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{17'd0, TargetMask[14:1], 1'b0}) != 0;
|
||||
s_rcu_config_err = s_rcu_busy_q || (u_rcu_dst.pwdata & ~{15'd0, TargetMask[16:1], 1'b0}) != 0;
|
||||
ResetCause: s_rcu_config_err = u_rcu_dst.pwdata[31:6] != 0;
|
||||
RcuFault: s_rcu_config_err = u_rcu_dst.pwdata[31:5] != 0;
|
||||
RcuIrqState, RcuIrqEnable: s_rcu_config_err = u_rcu_dst.pwdata[31:2] != 0;
|
||||
@@ -363,11 +367,11 @@ module tiny_sysctrl #(
|
||||
s_rcu_config_err |= s_clkout_control_q[2:0] != 0 || !clkout_idle_i ||
|
||||
!(s_clk_req_q == 0 || (s_clk_req_q == 1 && !PllPresent) ||
|
||||
(s_clk_req_q >= 2 && PllPresent && PllProfiles));
|
||||
32'd2: s_rcu_config_err |= TargetMask == 0 || (s_gate_req_q & ~{17'd0, TargetMask}) != 0;
|
||||
32'd2: s_rcu_config_err |= TargetMask == 0 || (s_gate_req_q & ~{15'd0, TargetMask}) != 0;
|
||||
32'd4:
|
||||
s_rcu_config_err |= s_rst_req_q == 0 ||
|
||||
(s_rst_req_q & ~{17'd0, TargetMask[14:1], 1'b0}) != 0 ||
|
||||
(s_rst_req_q & {17'd0, gate_actual_i}) != 0;
|
||||
(s_rst_req_q & ~{15'd0, TargetMask[16:1], 1'b0}) != 0 ||
|
||||
(s_rst_req_q & {15'd0, gate_actual_i}) != 0;
|
||||
32'd8: begin
|
||||
end
|
||||
default: s_rcu_config_err = 1'b1;
|
||||
@@ -432,16 +436,16 @@ module tiny_sysctrl #(
|
||||
s_rcu_busy_d = 1'b1;
|
||||
s_active_cmd_d = 4'd8;
|
||||
end else if (TargetMask != 0 && u_rcu_dst.pwdata == 2 &&
|
||||
(s_gate_req_q & ~{17'd0, TargetMask}) == 0) begin
|
||||
s_periph_mask_d = s_gate_req_q[14:0];
|
||||
(s_gate_req_q & ~{15'd0, TargetMask}) == 0) begin
|
||||
s_periph_mask_d = s_gate_req_q[16:0];
|
||||
s_periph_reset_d = 1'b0;
|
||||
s_periph_apply_d = 1'b1;
|
||||
s_rcu_busy_d = 1'b1;
|
||||
s_active_cmd_d = 4'd2;
|
||||
end else if (u_rcu_dst.pwdata == 4 && s_rst_req_q != 0 &&
|
||||
(s_rst_req_q & ~{17'd0, TargetMask[14:1], 1'b0}) == 0 &&
|
||||
(s_rst_req_q & {17'd0, gate_actual_i}) == 0) begin
|
||||
s_periph_mask_d = s_rst_req_q[14:0];
|
||||
(s_rst_req_q & ~{15'd0, TargetMask[16:1], 1'b0}) == 0 &&
|
||||
(s_rst_req_q & {15'd0, gate_actual_i}) == 0) begin
|
||||
s_periph_mask_d = s_rst_req_q[16:0];
|
||||
s_periph_reset_d = 1'b1;
|
||||
s_periph_apply_d = 1'b1;
|
||||
s_reset_done_d = 0;
|
||||
@@ -501,7 +505,7 @@ module tiny_sysctrl #(
|
||||
if (periph_done_i) begin
|
||||
s_rcu_busy_d = 1'b0;
|
||||
s_active_cmd_d = 0;
|
||||
s_reset_done_d = {17'd0, reset_done_i};
|
||||
s_reset_done_d = {15'd0, reset_done_i};
|
||||
if (s_periph_reset_q && reset_done_i != 0) s_reset_cause_d[5] = 1'b1;
|
||||
s_rcu_irq_state_d[0] = 1'b1;
|
||||
end
|
||||
@@ -607,7 +611,7 @@ module tiny_sysctrl #(
|
||||
.dat_o (s_clk_latched_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(21)
|
||||
.DATA_WIDTH(23)
|
||||
) u_periph_command (
|
||||
.clk_i (clk_ref_i),
|
||||
.rst_n_i(rst_ref_n_i),
|
||||
|
||||
@@ -140,7 +140,8 @@ def validate(document_path: Path, root: Path) -> None:
|
||||
raise ValueError("unrecognized generated APB crossing contract")
|
||||
expected = {"aon": ["clint", "archinfo"], "memory": ["xpi"],
|
||||
"peripheral": ["gpio", "uart0", "timer0", "timer1", "i2c0", "dma",
|
||||
"pwm", "rtc", "wdg", "rng", "crc", "ws2812", "crypto", "i2s", "dvp"]}
|
||||
"pwm", "rtc", "wdg", "rng", "crc", "ws2812", "crypto", "i2s", "dvp",
|
||||
"spi"]}
|
||||
if (generated.get("destinations") != expected or
|
||||
generated.get("same_clock_isolation") != ["sdio"] or
|
||||
generated.get("native_system") != ["sysctrl", "sram"] or
|
||||
|
||||
@@ -18,15 +18,15 @@ from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
TINY = ROOT / "rtl/tiny"
|
||||
RCU_TARGETS = {"gpio": 1, "uart0": 2, "i2c0": 3, "timer0": 4, "timer1": 5,
|
||||
"pwm": 6, "i2s": 7, "sdio": 8, "dma": 9, "ws2812": 10,
|
||||
"rng": 11, "crc": 12, "crypto": 13, "dvp": 14}
|
||||
"rng": 11, "crc": 12, "crypto": 13, "dvp": 14, "spi": 16}
|
||||
|
||||
|
||||
def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
document = json.loads(path.read_text())
|
||||
regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]}
|
||||
if (document.get("schema_version") != 1 or document.get("dma_channels") != 8
|
||||
or document.get("dma_request_mask") != "0x39ff"):
|
||||
raise ValueError("Tiny P9 requires eight DMA channels and request mask 0x39ff")
|
||||
or document.get("dma_request_mask") != "0x339ff"):
|
||||
raise ValueError("Tiny SPI-P3 requires eight DMA channels and request mask 0x339ff")
|
||||
if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2,
|
||||
"data_outstanding": 1}:
|
||||
raise ValueError("Tiny P4 requires independent I/D and one ordered data operation")
|
||||
@@ -43,9 +43,9 @@ def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
"cpu_combined_outstanding": 1, "dma_direction_outstanding": 1,
|
||||
"sdio_direction_outstanding": 1, "target_combined_outstanding": 1,
|
||||
"sram_targets": [0, 1, 2, 3], "xpi_target": 4, "apb_target_base": 5,
|
||||
"decode_error_target": 26, "protocol_error_target": 27, "target_count": 28,
|
||||
"decode_error_target": 27, "protocol_error_target": 28, "target_count": 29,
|
||||
}:
|
||||
raise ValueError("Tiny P9 requires three active owners and 28 targets")
|
||||
raise ValueError("Tiny SPI-P3 requires three active owners and 29 targets")
|
||||
if document.get("irq_vector_width") != 32 or document.get("external_irq_count") != 30:
|
||||
raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs")
|
||||
names: set[str] = set()
|
||||
@@ -65,10 +65,10 @@ def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
assigned.add(region)
|
||||
if assigned != {r for r in regions if r.startswith("APB4_")}:
|
||||
raise ValueError("Tiny topology must route every APB region")
|
||||
if len(document["apb_targets"]) != 21:
|
||||
raise ValueError("Tiny P9 requires twenty-one independent APB targets")
|
||||
if len(document["apb_targets"]) != 22:
|
||||
raise ValueError("Tiny SPI-P3 requires twenty-two independent APB targets")
|
||||
target_names = [target["name"] for target in document["apb_targets"]]
|
||||
if len(set(target_names)) != 21 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None
|
||||
if len(set(target_names)) != 22 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None
|
||||
for name in target_names):
|
||||
raise ValueError("Tiny APB target names must be unique identifiers")
|
||||
if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)):
|
||||
@@ -149,6 +149,8 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
"#define RS_SOC_HAS_I2S 1U\n#define RS_SOC_HAS_SDIO0 1U\n"
|
||||
"#define RS_SOC_HAS_DVP 1U\n#define RS_SOC_DVP_DMA_CHANNEL 2U\n"
|
||||
"#define RS_SOC_DVP_DMA_BURST_BEATS 4U\n#define RS_SOC_RNG_QUALIFIED 0U\n"
|
||||
"#define RS_SOC_HAS_SPI 1U\n#define RS_SOC_SPI_DMA_TX 16U\n"
|
||||
"#define RS_SOC_SPI_DMA_RX 17U\n"
|
||||
f"#define RS_SOC_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n"
|
||||
f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}U\n"
|
||||
f"#define RS_SOC_GPIO_HAS_PULLS {platform['gpio_pulls']}U\n#endif\n")
|
||||
@@ -176,6 +178,10 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
if target_bit is not None:
|
||||
declarations.append(f"logic {ip_clock};")
|
||||
declarations.append(f"logic s_{name}_rst_n;")
|
||||
if name == "spi":
|
||||
# Only SPI consumes the origin/channel qualifier; other crossings tie off below.
|
||||
declarations.append(f"logic [1:0] s_{name}_origin_sys;")
|
||||
declarations.append(f"logic [2:0] s_{name}_channel_sys;")
|
||||
declarations.append(f"apb4_if u_{name}_apb4_if (.pclk({ip_clock}), .presetn(s_{name}_rst_n));")
|
||||
if target_bit is None:
|
||||
reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];")
|
||||
@@ -201,16 +207,23 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
bridges.append(f"assign s_target_rst_n[{index + 5}] = s_fabric_rst_n;")
|
||||
crossed = clock != "s_exec_clk" or target_bit is not None
|
||||
endpoint = f"u_{name}_sys_apb4_if" if crossed else f"u_{name}_apb4_if"
|
||||
qualifier = (f".origin_o(s_{name}_origin_sys), .channel_o(s_{name}_channel_sys)"
|
||||
if name == "spi" else ".origin_o(), .channel_o()")
|
||||
bridges.append(f"tiny_axi42apb4 u_{name}_bridge ("
|
||||
f".clk_i(s_exec_clk), .rst_n_i(s_fabric_rst_n), "
|
||||
f".axi4(u_targets_axi4_if[{index + 5}]), .apb4({endpoint}));")
|
||||
f".axi4(u_targets_axi4_if[{index + 5}]), .apb4({endpoint}), {qualifier});")
|
||||
if crossed:
|
||||
disabled = f"s_disabled_sys[{target_bit}]" if target_bit is not None else "1'b0"
|
||||
source = (f".src_origin_i(s_{name}_origin_sys), .src_channel_i(s_{name}_channel_sys)"
|
||||
if name == "spi" else ".src_origin_i(2'd0), .src_channel_i(3'd0)")
|
||||
declarations.append(f"apb4_if {endpoint} (.pclk(s_exec_clk), .presetn(s_fabric_rst_n));")
|
||||
bridges.append(f"tiny_apb_async_bridge u_{name}_cfg_cdc ("
|
||||
f".src_clk_i(s_exec_clk), .src_rst_n_i(s_fabric_rst_n), "
|
||||
f".dst_clk_i({ip_clock}), .dst_rst_n_i(s_{name}_rst_n), "
|
||||
f".disabled_i({disabled}), "
|
||||
f"{source}, "
|
||||
f".dst_origin_o({ 's_spi_origin' if name == 'spi' else ''}), "
|
||||
f".dst_channel_o({ 's_spi_channel' if name == 'spi' else ''}), "
|
||||
f".idle_o(s_apb_cdc_idle[{index}]), "
|
||||
f".ready_o(s_apb_cdc_ready[{index}]), "
|
||||
f".src_apb4({endpoint}), .dst_apb4(u_{name}_apb4_if));")
|
||||
|
||||
@@ -33,6 +33,7 @@ TEST_SOURCES = (
|
||||
"crt/src/hal/usb2_math.c",
|
||||
"crt/src/hal/spisd_math.c",
|
||||
"crt/src/hal/i2s_math.c",
|
||||
"crt/src/hal/spi_math.c",
|
||||
"crt/src/hal/dma_math.c",
|
||||
"crt/src/hal/dma.c",
|
||||
"crt/src/hal/dvp.c",
|
||||
|
||||
@@ -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())
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <retrosoc/hal/resource.h>
|
||||
#include <retrosoc/hal/sdram.h>
|
||||
#include <retrosoc/hal/sdio.h>
|
||||
#include <retrosoc/hal/spi.h>
|
||||
#include <retrosoc/hal/spisd.h>
|
||||
#include <retrosoc/hal/timer.h>
|
||||
#include <retrosoc/hal/uart.h>
|
||||
@@ -1178,6 +1179,128 @@ static int test_tiny_dma_config_validation(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spi_divider_from_hz(void) {
|
||||
uint32_t divider;
|
||||
uint32_t actual_hz;
|
||||
|
||||
/* Ceiling rounding keeps the actual SCK at or below the request. */
|
||||
if ((rs_spi_divider_from_hz(24000000U, 1000000U, ÷r, &actual_hz) != RS_OK) ||
|
||||
(divider != 12U) || (actual_hz != 1000000U)) {
|
||||
return 1;
|
||||
}
|
||||
if ((rs_spi_divider_from_hz(24000000U, 1100000U, ÷r, &actual_hz) != RS_OK) ||
|
||||
(divider != 11U) || (actual_hz != 1090909U)) {
|
||||
return 2;
|
||||
}
|
||||
/* Requests at or above half PCLK saturate at divider 1. */
|
||||
if ((rs_spi_divider_from_hz(24000000U, 12000000U, ÷r, &actual_hz) != RS_OK) ||
|
||||
(divider != 1U) || (actual_hz != 12000000U)) {
|
||||
return 3;
|
||||
}
|
||||
if ((rs_spi_divider_from_hz(24000000U, 13000000U, ÷r, &actual_hz) != RS_OK) ||
|
||||
(divider != 1U) || (actual_hz != 12000000U)) {
|
||||
return 4;
|
||||
}
|
||||
if ((rs_spi_divider_from_hz(131070U, 1U, ÷r, &actual_hz) != RS_OK) ||
|
||||
(divider != 65535U) || (actual_hz != 1U)) {
|
||||
return 5;
|
||||
}
|
||||
/* Divider 65536 cannot be represented. */
|
||||
if (rs_spi_divider_from_hz(131072U, 1U, ÷r, &actual_hz) != RS_ENOTSUP) {
|
||||
return 6;
|
||||
}
|
||||
if ((rs_spi_divider_from_hz(0U, 1000000U, ÷r, &actual_hz) != RS_EINVAL) ||
|
||||
(rs_spi_divider_from_hz(24000000U, 0U, ÷r, &actual_hz) != RS_EINVAL) ||
|
||||
(rs_spi_divider_from_hz(24000000U, 1000000U, NULL, &actual_hz) != RS_EINVAL) ||
|
||||
(rs_spi_divider_from_hz(24000000U, 1000000U, ÷r, NULL) != RS_EINVAL)) {
|
||||
return 7;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_tiny_dma_spi_aperture(void) {
|
||||
rs_dma_config_t config = {
|
||||
.kind = RS_DMA_KIND_MM_TO_MM,
|
||||
.request = RS_DMA_REQUEST_SPI_TX,
|
||||
.source = (uintptr_t)UINT32_C(0x30000000),
|
||||
.destination = (uintptr_t)UINT32_C(0x1001D054),
|
||||
.byte_count = 64U,
|
||||
.width = RS_DMA_WIDTH_32,
|
||||
.source_increment = true,
|
||||
.destination_increment = false,
|
||||
.burst_beats = 16U,
|
||||
};
|
||||
static _Alignas(64) rs_dma_tcd_t descriptor;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_OK) {
|
||||
return 1;
|
||||
}
|
||||
config.request = RS_DMA_REQUEST_SPI_RX;
|
||||
config.source = (uintptr_t)UINT32_C(0x1001D058);
|
||||
config.destination = (uintptr_t)UINT32_C(0x30001000);
|
||||
config.source_increment = false;
|
||||
config.destination_increment = true;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_OK) {
|
||||
return 2;
|
||||
}
|
||||
/* Wrong fixed-FIFO address, increment direction, count, or kind fails. */
|
||||
config.request = RS_DMA_REQUEST_SPI_TX;
|
||||
config.source = (uintptr_t)UINT32_C(0x30000000);
|
||||
config.destination = (uintptr_t)UINT32_C(0x1001D050);
|
||||
config.source_increment = true;
|
||||
config.destination_increment = false;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) {
|
||||
return 3;
|
||||
}
|
||||
config.destination = (uintptr_t)UINT32_C(0x1001D054);
|
||||
config.destination_increment = true;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) {
|
||||
return 4;
|
||||
}
|
||||
config.destination_increment = false;
|
||||
config.byte_count = 6U;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) {
|
||||
return 5;
|
||||
}
|
||||
config.byte_count = 64U;
|
||||
config.kind = RS_DMA_KIND_MM_TO_STREAM;
|
||||
if (rs_tiny_dma_config_validate(3U, &config) != RS_EINVAL) {
|
||||
return 6;
|
||||
}
|
||||
/* The default Mini selection has no SPI request route at all. */
|
||||
config.kind = RS_DMA_KIND_MM_TO_MM;
|
||||
if (rs_dma_config_validate(3U, &config) != RS_EINVAL) {
|
||||
return 7;
|
||||
}
|
||||
descriptor.source = UINT32_C(0x30000000);
|
||||
descriptor.destination = UINT32_C(0x1001D054);
|
||||
descriptor.byte_count = 64U;
|
||||
descriptor.y_count = 1U;
|
||||
/* Request 16 needs the high bit; the low nibble field stays zero. */
|
||||
descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_REQUEST_HIGH |
|
||||
((uint32_t)RS_DMA_KIND_MM_TO_MM << RS_DMA_TCD_KIND_SHIFT);
|
||||
if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_OK) {
|
||||
return 8;
|
||||
}
|
||||
descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_DST_INC | RS_DMA_TCD_REQUEST_HIGH |
|
||||
(UINT32_C(1) << RS_DMA_TCD_REQUEST_SHIFT) |
|
||||
((uint32_t)RS_DMA_KIND_MM_TO_MM << RS_DMA_TCD_KIND_SHIFT);
|
||||
descriptor.source = UINT32_C(0x1001D058);
|
||||
descriptor.destination = UINT32_C(0x30001000);
|
||||
if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_OK) {
|
||||
return 9;
|
||||
}
|
||||
descriptor.source = UINT32_C(0x1001D054);
|
||||
if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_EINVAL) {
|
||||
return 10;
|
||||
}
|
||||
descriptor.source = UINT32_C(0x1001D058);
|
||||
descriptor.control |= RS_DMA_TCD_SRC_INC;
|
||||
if (rs_tiny_dma_tcd_validate(3U, &descriptor) != RS_EINVAL) {
|
||||
return 11;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
rs_status_t rs_tiny_dvp_configure(const rs_dvp_config_t *config);
|
||||
rs_status_t rs_tiny_dvp_capture_dma(uintptr_t destination, uint32_t capacity, rs_timeout_t timeout);
|
||||
rs_status_t rs_tiny_dvp_capture_pio(uintptr_t destination, uint32_t capacity, uint32_t expected,
|
||||
@@ -3525,6 +3648,8 @@ int main(void) {
|
||||
test_gpio_helpers(),
|
||||
test_dma_config_validation(),
|
||||
test_tiny_dma_config_validation(),
|
||||
test_spi_divider_from_hz(),
|
||||
test_tiny_dma_spi_aperture(),
|
||||
test_dvp_hal(),
|
||||
test_p9_frame_layout(),
|
||||
test_p10_frame_statistics(),
|
||||
|
||||
@@ -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
|
||||
@@ -29,6 +29,7 @@ logic [3:0] pstrb_i = 0;
|
||||
logic psel_i = 0, penable_i = 0, pwrite_i = 0, pready_o, pslverr_o;
|
||||
logic model_begin = 0, model_enable = 0, model_abort = 0;
|
||||
logic model_segment = 0, model_pause = 0;
|
||||
logic session_owned, lifecycle_idle;
|
||||
logic [15:0] model_divider = 1, model_setup = 1, model_hold = 1, model_inactive = 1;
|
||||
logic [8:0] model_faults;
|
||||
logic cpol = 0, cpha = 0, lsb = 0, frame16 = 0;
|
||||
@@ -67,6 +68,8 @@ realtime access_accept_time;
|
||||
.output_enable_o (output_enable_o),
|
||||
.irq_o (irq_o),
|
||||
.credit_commit_o (credit_commit_o),
|
||||
.session_owned_o (session_owned),
|
||||
.lifecycle_idle_o (lifecycle_idle),
|
||||
.paddr_i (paddr_i),
|
||||
.pwdata_i (pwdata_i),
|
||||
.pstrb_i (pstrb_i),
|
||||
|
||||
@@ -9,7 +9,7 @@ module tiny_axi4_fabric_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) masters[3] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
@@ -18,7 +18,7 @@ module tiny_axi4_fabric_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
|
||||
@@ -80,37 +80,40 @@ module tiny_p7_source_reset_tb;
|
||||
logic [1:0] rd_resp, wr_resp;
|
||||
logic rd_id_error, wr_id_error;
|
||||
dma_axi4_master u_dma_master (
|
||||
.clk_i (s_dma_clk),
|
||||
.rst_n_i (s_dma_rst_n),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (rd_start),
|
||||
.read_start_ready_o (rd_start_ready),
|
||||
.read_addr_i (rd_addr),
|
||||
.read_beats_i (rd_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (rd_busy),
|
||||
.read_beat_valid_o (rd_valid),
|
||||
.read_beat_ready_i (rd_ready),
|
||||
.read_data_o (rd_data),
|
||||
.read_resp_o (rd_resp),
|
||||
.read_last_o (rd_last),
|
||||
.read_expected_last_o(),
|
||||
.read_id_error_o (rd_id_error),
|
||||
.read_done_o (rd_done),
|
||||
.write_start_valid_i (wr_start),
|
||||
.write_start_ready_o (wr_start_ready),
|
||||
.write_addr_i (wr_addr),
|
||||
.write_beats_i (wr_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (wr_busy),
|
||||
.write_data_valid_i (wr_valid),
|
||||
.write_data_ready_o (wr_ready),
|
||||
.write_data_i (wr_data),
|
||||
.write_strb_i (4'hf),
|
||||
.write_done_o (wr_done),
|
||||
.write_resp_o (wr_resp),
|
||||
.write_id_error_o (wr_id_error),
|
||||
.axi4 (u_dma_pclk_axi4_if)
|
||||
.clk_i (s_dma_clk),
|
||||
.rst_n_i (s_dma_rst_n),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (rd_start),
|
||||
.read_start_ready_o (rd_start_ready),
|
||||
.read_addr_i (rd_addr),
|
||||
.read_beats_i (rd_beats),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (rd_busy),
|
||||
.read_beat_valid_o (rd_valid),
|
||||
.read_beat_ready_i (rd_ready),
|
||||
.read_data_o (rd_data),
|
||||
.read_resp_o (rd_resp),
|
||||
.read_last_o (rd_last),
|
||||
.read_expected_last_o (),
|
||||
.read_id_error_o (rd_id_error),
|
||||
.read_done_o (rd_done),
|
||||
.write_start_valid_i (wr_start),
|
||||
.write_start_ready_o (wr_start_ready),
|
||||
.write_addr_i (wr_addr),
|
||||
.write_beats_i (wr_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (wr_busy),
|
||||
.write_data_valid_i (wr_valid),
|
||||
.write_data_ready_o (wr_ready),
|
||||
.write_data_i (wr_data),
|
||||
.write_strb_i (4'hf),
|
||||
.write_done_o (wr_done),
|
||||
.write_resp_o (wr_resp),
|
||||
.write_id_error_o (wr_id_error),
|
||||
.origin_enable_i (1'b0),
|
||||
.read_origin_channel_i (3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4 (u_dma_pclk_axi4_if)
|
||||
);
|
||||
assign s_periph_idle = {4'hf, !(rd_busy || wr_busy || rd_start || wr_start), 9'h1ff};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ module tiny_r2_p5_fabric_formal (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) masters[3] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
@@ -25,7 +25,7 @@ module tiny_r2_p5_fabric_formal (
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) target (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
|
||||
@@ -9,7 +9,7 @@ module tiny_r2_p5_fabric_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) masters[3] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
@@ -18,7 +18,7 @@ module tiny_r2_p5_fabric_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
|
||||
@@ -8,7 +8,7 @@ module tiny_r2_p5_reset_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) masters[3] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
@@ -17,7 +17,7 @@ module tiny_r2_p5_reset_tb;
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
.USER_WIDTH(4)
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(target_rst_n)
|
||||
@@ -76,37 +76,40 @@ module tiny_r2_p5_reset_tb;
|
||||
|
||||
// Production master: BREADY cannot rise until its last W handshake.
|
||||
dma_axi4_master u_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (1'b0),
|
||||
.read_start_ready_o (),
|
||||
.read_addr_i (32'd0),
|
||||
.read_beats_i (5'd1),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (),
|
||||
.read_beat_valid_o (),
|
||||
.read_beat_ready_i (1'b0),
|
||||
.read_data_o (),
|
||||
.read_resp_o (),
|
||||
.read_last_o (),
|
||||
.read_expected_last_o(),
|
||||
.read_id_error_o (),
|
||||
.read_done_o (),
|
||||
.write_start_valid_i (dma_start),
|
||||
.write_start_ready_o (dma_start_ready),
|
||||
.write_addr_i (dma_addr),
|
||||
.write_beats_i (dma_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (dma_busy),
|
||||
.write_data_valid_i (dma_valid),
|
||||
.write_data_ready_o (dma_ready),
|
||||
.write_data_i (dma_data),
|
||||
.write_strb_i (4'hf),
|
||||
.write_done_o (dma_done),
|
||||
.write_resp_o (dma_resp),
|
||||
.write_id_error_o (dma_id_error),
|
||||
.axi4 (masters[1])
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_i (1'b0),
|
||||
.read_start_valid_i (1'b0),
|
||||
.read_start_ready_o (),
|
||||
.read_addr_i (32'd0),
|
||||
.read_beats_i (5'd1),
|
||||
.read_fixed_i (1'b0),
|
||||
.read_busy_o (),
|
||||
.read_beat_valid_o (),
|
||||
.read_beat_ready_i (1'b0),
|
||||
.read_data_o (),
|
||||
.read_resp_o (),
|
||||
.read_last_o (),
|
||||
.read_expected_last_o (),
|
||||
.read_id_error_o (),
|
||||
.read_done_o (),
|
||||
.write_start_valid_i (dma_start),
|
||||
.write_start_ready_o (dma_start_ready),
|
||||
.write_addr_i (dma_addr),
|
||||
.write_beats_i (dma_beats),
|
||||
.write_fixed_i (1'b0),
|
||||
.write_busy_o (dma_busy),
|
||||
.write_data_valid_i (dma_valid),
|
||||
.write_data_ready_o (dma_ready),
|
||||
.write_data_i (dma_data),
|
||||
.write_strb_i (4'hf),
|
||||
.write_done_o (dma_done),
|
||||
.write_resp_o (dma_resp),
|
||||
.write_id_error_o (dma_id_error),
|
||||
.origin_enable_i (1'b0),
|
||||
.read_origin_channel_i (3'd0),
|
||||
.write_origin_channel_i(3'd0),
|
||||
.axi4 (masters[1])
|
||||
);
|
||||
|
||||
for (genvar m = 0; m < 3; m++) begin : gen_master
|
||||
@@ -158,8 +161,8 @@ module tiny_r2_p5_reset_tb;
|
||||
// Independent upstream scoreboards also run in Icarus without SVA.
|
||||
integer rleft = 0, wleft = 0, bleft = 0;
|
||||
logic rheld = 0, bheld = 0;
|
||||
logic [36:0] saved_r;
|
||||
logic [ 3:0] saved_b;
|
||||
logic [39:0] saved_r;
|
||||
logic [ 6:0] saved_b;
|
||||
always @(posedge clk_i) begin
|
||||
if (!rst_n_i) begin
|
||||
rleft = 0;
|
||||
|
||||
@@ -78,6 +78,73 @@ def test_dma_reports_native_axi4_errors_bursts_and_streams(tmp_path: Path) -> No
|
||||
assert "DMA native AXI4 burst, backpressure, stream, and error test passed" in result.stdout
|
||||
|
||||
|
||||
def test_dma_origin_qualifier_stays_staged_per_burst(tmp_path: Path) -> None:
|
||||
iverilog = shutil.which("iverilog")
|
||||
vvp = shutil.which("vvp")
|
||||
if iverilog is None or vvp is None:
|
||||
return
|
||||
|
||||
memory_map = tmp_path / "memory_map"
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/rtl/generate_memory_map.py"),
|
||||
"--map",
|
||||
str(ROOT / "rtl/mini/address_map/memory_map.json"),
|
||||
"--output-dir",
|
||||
str(memory_map),
|
||||
"--have-sram-if",
|
||||
"NO",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
source_list = tmp_path / "dma_origin.fl"
|
||||
source_list.write_text(
|
||||
"\n".join(
|
||||
[
|
||||
"+define+SV_ASSRT_DISABLE",
|
||||
f"+incdir+{memory_map / 'rtl'}",
|
||||
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
|
||||
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/interface'}",
|
||||
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/stream'}",
|
||||
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/utils'}",
|
||||
f"+incdir+{ROOT / 'rtl/mini/top'}",
|
||||
f"+incdir+{ROOT / 'rtl/ip/peripheral'}",
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv"),
|
||||
str(ROOT / "rtl/ip/peripheral/dma_pkg.sv"),
|
||||
str(ROOT / "rtl/ip/peripheral/dma_req_if.sv"),
|
||||
str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"),
|
||||
str(ROOT / "rtl/ip/peripheral/dma_core.sv"),
|
||||
str(ROOT / "tests/rtl/dma_origin_tb.sv"),
|
||||
"",
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
converted = tmp_path / "dma_origin_tb.v"
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/rtl/convt_sv2v.py"),
|
||||
"-f",
|
||||
str(source_list),
|
||||
"--output",
|
||||
str(converted),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
simulation = tmp_path / "dma_origin_tb"
|
||||
subprocess.run(
|
||||
[iverilog, "-g2012", "-s", "dma_origin_tb", "-o", str(simulation), str(converted)],
|
||||
check=True,
|
||||
)
|
||||
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
|
||||
assert "DMA origin qualifier staging test passed" in result.stdout
|
||||
|
||||
|
||||
def test_dma_apb_registers_and_irq(tmp_path: Path) -> None:
|
||||
iverilog = shutil.which("iverilog")
|
||||
vvp = shutil.which("vvp")
|
||||
|
||||
@@ -37,7 +37,7 @@ def test_current_source_cannot_relabel_historical_publication():
|
||||
|
||||
@pytest.mark.parametrize("before,after", [
|
||||
("s_req_q.write_done && !s_req_q.b_captured", "1'b1"),
|
||||
("s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]", "s_r_valid"),
|
||||
("s_r_valid && s_rready[s_req_q.owner] && s_r_data[4]", "s_r_valid"),
|
||||
(".flush_i(1'b0)", ".flush_i(!enable_i)"),
|
||||
])
|
||||
def test_current_reset_binding_still_rejects_unsafe_completion(tmp_path, before, after):
|
||||
|
||||
@@ -23,8 +23,8 @@ def test_dma_v22_abi_and_product_defaults():
|
||||
assert "logic [ NumChannels-1:0][ 4:0] s_req_q" in core
|
||||
assert "!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX)" in core
|
||||
assert "s_tcd_parse_q &&" in core
|
||||
assert ".RequestMask (16'h39ff)" in tiny
|
||||
assert ".EnableSpi" not in tiny
|
||||
assert ".RequestMask" in tiny and "0003_39ff" in tiny
|
||||
assert ".EnableSpi(1'b1)" in re.sub(r"\s+", "", tiny)
|
||||
|
||||
|
||||
def test_dma_v22_tcd_request_split_is_handwritten_and_stable():
|
||||
|
||||
@@ -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
|
||||
@@ -49,9 +49,10 @@ def test_frozen_spi_register_mirrors():
|
||||
assert rtl['IRQ_TX_LEVEL'] == 8 and rtl['IRQ_RX_LEVEL'] == 9
|
||||
|
||||
|
||||
def test_spi_is_not_advertised_by_product_integration():
|
||||
for product in ('tiny', 'mini'):
|
||||
for filelist in (ROOT / f'rtl/{product}/filelist').glob('*.fl'):
|
||||
assert '/serial/apb4_spi.sv' not in filelist.read_text()
|
||||
assert 'spi' not in [row['name'] for row in __import__('json').loads(
|
||||
def test_spi_is_advertised_only_by_tiny_product_integration():
|
||||
assert any('/serial/apb4_spi.sv' in path.read_text()
|
||||
for path in (ROOT / 'rtl/tiny/filelist').glob('*.fl'))
|
||||
assert all('/serial/apb4_spi.sv' not in path.read_text()
|
||||
for path in (ROOT / 'rtl/mini/filelist').glob('*.fl'))
|
||||
assert 'spi' in [row['name'] for row in __import__('json').loads(
|
||||
(ROOT / 'rtl/tiny/integration/soc_topology.json').read_text())['apb_targets']]
|
||||
+1
-1
@@ -36,7 +36,7 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
|
||||
name = target["name"]
|
||||
assert f".presetn(s_{name}_rst_n)" in interfaces
|
||||
if name in {"gpio", "uart0", "i2c0", "timer0", "timer1", "pwm", "i2s", "sdio",
|
||||
"dma", "ws2812", "rng", "crc", "crypto", "dvp"}:
|
||||
"dma", "ws2812", "rng", "crc", "crypto", "dvp", "spi"}:
|
||||
assert f"tiny_peripheral_leaf u_{name}_lifecycle" in interfaces
|
||||
assert f".rst_n_i(s_leaf_rst_n[{index}])" in interfaces
|
||||
assert f".rst_n_o(s_{name}_rst_n)" in interfaces
|
||||
|
||||
@@ -59,7 +59,7 @@ def test_reset_inventory_has_active_domains_and_all_existing_targets() -> None:
|
||||
reset = inventory["reset_distribution"]
|
||||
assert reset["release_edges"] == 5
|
||||
assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])]
|
||||
assert len(reset["leaf_names"]) == 22
|
||||
assert len(reset["leaf_names"]) == 23
|
||||
assert {domain["name"] for domain in inventory["domains"]} == {
|
||||
"aon", "system", "memory", "peripheral", "jtag", "audio", "dvp_pixel"}
|
||||
assert reset["clock_domain"] == "domain-specific" and reset["main_sram_payload_reset"] == "none"
|
||||
|
||||
@@ -406,7 +406,8 @@ def test_p6_map_irq_request_and_pin_contract():
|
||||
assert {k: irqs[k] for k in ("rng", "ws2812", "crypto", "i2s", "sdio")} == {
|
||||
"rng": 16, "ws2812": 17, "crypto": 23, "i2s": 8, "sdio": 10}
|
||||
assert irqs["dvp"] == 15
|
||||
assert {19, 24, 25, 26, 27}.isdisjoint(irqs.values())
|
||||
assert irqs["spi"] == 25
|
||||
assert {19, 24, 26, 27}.isdisjoint(irqs.values())
|
||||
assert irqs["rcu"] == 31
|
||||
pins = topology["gpio_alt_functions"]
|
||||
assert pins[9]["alt0"]["inputs"] == ["u_uart0_if.rx_i"]
|
||||
@@ -542,7 +543,7 @@ int main(void) {
|
||||
def test_p6_reset_and_macro_policy_remains_explicit():
|
||||
text = (TINY / "top/retrosoc_tiny.sv").read_text()
|
||||
assert ".clk_aud_i" in text and "s_audio_rst_n" in text
|
||||
assert ".EnableStreams" in text and "16'h39ff" in text
|
||||
assert ".EnableStreams" in text and "32'h0003_39ff" in text
|
||||
assert "apb4_crypto u_crypto" in text
|
||||
tree = (TINY / "top/tiny_reset_tree.sv").read_text()
|
||||
assert "s_audio" not in tree and "crypto" not in tree
|
||||
|
||||
@@ -223,7 +223,7 @@ def check_p7_target_capability(top: str, firmware: str) -> None:
|
||||
instances = re.findall(r"\btiny_sysctrl\s*#\(([^;]*?)\)\s*u_sysctrl\s*\(", top)
|
||||
if len(instances) != 1:
|
||||
raise ValueError("missing or ambiguous production SYSCTRL instance")
|
||||
targets = re.findall(r"\.TargetMask\s*\(\s*15'h([0-9a-fA-F_]+)\s*\)", instances[0])
|
||||
targets = re.findall(r"\.TargetMask\s*\(\s*17'h([0-9a-fA-F_]+)\s*\)", instances[0])
|
||||
expected = re.findall(
|
||||
r"^\s*#define RS_P7_EXPECTED_TARGET_CAPABILITY\s+UINT32_C\(0x([0-9a-fA-F]+)\)\s*$",
|
||||
firmware, re.MULTILINE)
|
||||
@@ -231,7 +231,7 @@ def check_p7_target_capability(top: str, firmware: str) -> None:
|
||||
raise ValueError("missing or ambiguous target capability definition")
|
||||
# Freeze the implemented set independently of both handwritten sources.
|
||||
# The HAL's 0x3ffff architecture mask also admits unimplemented targets.
|
||||
if int(targets[0].replace("_", ""), 16) != 0x7FFF or int(expected[0], 16) != 0x7FFF:
|
||||
if int(targets[0].replace("_", ""), 16) != 0x17FFF or int(expected[0], 16) != 0x17FFF:
|
||||
raise ValueError("target capability differs from the DVP-inclusive contract")
|
||||
main = re.findall(r"\bint\s+main\s*\(void\)\s*\{(.*)", firmware, re.DOTALL)
|
||||
guard = (
|
||||
@@ -250,15 +250,15 @@ def test_tiny_p7_target_capability_parity() -> None:
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("source", "old", "new"), [
|
||||
("firmware", "UINT32_C(0x00007FFF)", "UINT32_C(0x00003FFF)"),
|
||||
("top", "15'h07fff", "15'h03fff"),
|
||||
*[("firmware", "UINT32_C(0x00007FFF)", f"UINT32_C({value})")
|
||||
for value in ("0x0000FFFF", "0x00017FFF", "0x00027FFF", "0x0003FFFF")],
|
||||
("firmware", "UINT32_C(0x00017FFF)", "UINT32_C(0x00003FFF)"),
|
||||
("top", "17'h17fff", "17'h03fff"),
|
||||
*[("firmware", "UINT32_C(0x00017FFF)", f"UINT32_C({value})")
|
||||
for value in ("0x00007FFF", "0x0000FFFF", "0x00027FFF", "0x0003FFFF")],
|
||||
("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY", "false"),
|
||||
("firmware", "snapshot.target_capability != RS_P7_EXPECTED_TARGET_CAPABILITY",
|
||||
"(snapshot.target_capability & RS_P7_EXPECTED_TARGET_CAPABILITY) != RS_P7_EXPECTED_TARGET_CAPABILITY"),
|
||||
("firmware", "#define RS_P7_EXPECTED_TARGET_CAPABILITY", "#define RS_P7_UNUSED_CAPABILITY"),
|
||||
("top", ".TargetMask (15'h07fff)", ".UnusedMask (15'h07fff)"),
|
||||
("top", ".TargetMask (17'h17fff)", ".UnusedMask (17'h17fff)"),
|
||||
])
|
||||
def test_tiny_p7_target_capability_rejects_drift(source: str, old: str, new: str) -> None:
|
||||
sources = {"top": (ROOT / "rtl/tiny/top/retrosoc_tiny.sv").read_text(),
|
||||
|
||||
@@ -40,9 +40,9 @@ def test_p9_topology_and_camera_routes() -> None:
|
||||
memory = json.loads((TINY / "address_map/memory_map.json").read_text())
|
||||
regions = {item["symbol"]: item for item in memory["regions"]}
|
||||
assert regions["APB4_DVP"]["base"] == "0x1000E000"
|
||||
assert topology["fabric"]["target_count"] == 28
|
||||
assert topology["fabric"]["decode_error_target"] == 26
|
||||
assert topology["fabric"]["protocol_error_target"] == 27
|
||||
assert topology["fabric"]["target_count"] == 29
|
||||
assert topology["fabric"]["decode_error_target"] == 27
|
||||
assert topology["fabric"]["protocol_error_target"] == 28
|
||||
assert any(item["name"] == "dvp" and item["regions"] == ["APB4_DVP"]
|
||||
for item in topology["apb_targets"])
|
||||
assert {item["core_bit"] for item in topology["interrupts"] if item["name"] == "dvp"} == {15}
|
||||
@@ -55,7 +55,7 @@ def test_p9_topology_and_camera_routes() -> None:
|
||||
assert routes[22]["alt1"]["inputs"] == ["u_dvp_if.vsync_i"]
|
||||
assert routes[23]["alt1"]["do"] == "s_clkout"
|
||||
assert read_topology(TINY / "integration/soc_topology.json",
|
||||
TINY / "address_map/memory_map.json")["dma_request_mask"] == "0x39ff"
|
||||
TINY / "address_map/memory_map.json")["dma_request_mask"] == "0x339ff"
|
||||
|
||||
|
||||
def test_p9_generated_and_hal_bindings() -> None:
|
||||
|
||||
Reference in new issue
Block a user