feat(tiny): integrate P6 shared IP and eight-channel DMA

Integrate RNG, CRC, WS2812, Crypto, I2S master and the SDIO private AXI owner on SAFE24. Preserve the 128 KiB main SRAM and six private Crypto macros, add product-specific DMA assignments and reject unsupported SDK operations.

Correct APB read strobes, I2S receive assembly and SDIO command timing, backpressure and descriptor retirement. Add pin-level workloads, source-bound evidence checks and publication/commercial consumer compatibility.

Validation of the final P6 worktree: 1726 pytest tests passed with one optional pypdf skip (exit 0, 4021.61 seconds; terminal session 36439). Both PDKs passed both RTL simulators, netlist boot and 38-macro binding checks. Shared formal, Mini compatibility and software/source quality gates passed.

Evidence remains attributed to the retained dirty-worktree snapshots based on b3b30a72cd under build/ics55-tiny-2026-10-12-11-50-2eb98c0e1fae/meta/tiny-r2-p6/. Timing is negative and warning observations are not promoted. This commit does not accept P6, qualify physical operation or enable P7.
This commit is contained in:
maksyuki committed 2026-10-12 21:43:30 +08:00
1 parent b3b30a72cd
commit 952b060a70
65 files changed
+4142 -1006

No files matched your search

+2
View File
@@ -420,6 +420,8 @@ endif
ifeq ($(HAVE_SVA), NO) ifeq ($(HAVE_SVA), NO)
DEF_LIST += +define+SV_ASSRT_DISABLE DEF_LIST += +define+SV_ASSRT_DISABLE
else ifeq ($(SOC),TINY)
DEF_LIST += +define+HAVE_SVA
endif endif
ifeq ($(HAVE_HP), YES) ifeq ($(HAVE_HP), YES)
+2 -1
View File
@@ -1,3 +1,4 @@
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
APP_CFLAGS += -Os APP_CFLAGS += -Os
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p6.c
@@ -0,0 +1,9 @@
#ifndef RETROSOC_APP_MCU_SHARED_ACCEPTANCE_H
#define RETROSOC_APP_MCU_SHARED_ACCEPTANCE_H
#include <stdint.h>
/* Zero means every P6 acceptance case completed; other values identify failure. */
uint8_t rs_tiny_p6_acceptance(void);
#endif
+38 -32
View File
@@ -20,12 +20,15 @@
#include <retrosoc/lib/printf.h> #include <retrosoc/lib/printf.h>
#include "mcu_acceptance.h" #include "mcu_acceptance.h"
#include "mcu_shared_acceptance.h"
static _Alignas(64) uint32_t rs_mcu_source[80]; static _Alignas(64) uint32_t rs_mcu_source[80];
static _Alignas(64) uint32_t rs_mcu_destination[80]; static _Alignas(64) uint32_t rs_mcu_destination[80];
static volatile uint32_t rs_mcu_irq_count; static volatile uint32_t rs_mcu_irq_count;
static volatile uint32_t rs_mcu_exception_count; static volatile uint32_t rs_mcu_exception_count;
static volatile bool rs_mcu_handler_failed; static volatile bool rs_mcu_handler_failed;
static volatile bool rs_mcu_probes_active;
static uint8_t rs_mcu_io_code;
static void rs_mcu_finish(bool pass, uint8_t code) { static void rs_mcu_finish(bool pass, uint8_t code) {
printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code); printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code);
@@ -39,8 +42,8 @@ static void rs_mcu_finish(bool pass, uint8_t code) {
static void rs_mcu_exception(uintptr_t mcause, uintptr_t stack_pointer) { static void rs_mcu_exception(uintptr_t mcause, uintptr_t stack_pointer) {
volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer; volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer;
if ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5))) { if (!rs_mcu_probes_active || ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5)))) {
rs_mcu_handler_failed = true; rs_mcu_finish(false, 11U);
} }
++rs_mcu_exception_count; ++rs_mcu_exception_count;
/* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test /* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test
@@ -75,7 +78,7 @@ static bool rs_mcu_dma(void) {
rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index; rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index;
rs_mcu_destination[index] = UINT32_C(0xcccccccc); rs_mcu_destination[index] = UINT32_C(0xcccccccc);
} }
if ((rs_dma_configure(4U, &config) != RS_EINVAL) || if ((rs_dma_configure(8U, &config) != RS_EINVAL) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) || (rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) || (rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) { (rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
@@ -91,7 +94,7 @@ static bool rs_mcu_dma(void) {
return false; return false;
} }
config.kind = RS_DMA_KIND_MM_TO_STREAM; config.kind = RS_DMA_KIND_MM_TO_STREAM;
config.request = RS_DMA_REQUEST_I2S_TX; config.request = RS_DMA_REQUEST_DVP_RX;
if (rs_dma_config_validate(0U, &config) == RS_OK) { if (rs_dma_config_validate(0U, &config) == RS_OK) {
return false; return false;
} }
@@ -164,9 +167,7 @@ static bool rs_mcu_io(void) {
const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)}; const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)};
uint32_t receive[4] = {0U}; uint32_t receive[4] = {0U};
rs_gpio_capabilities_t capabilities; rs_gpio_capabilities_t capabilities;
rs_uart_timing_t timing;
bool high; bool high;
uint32_t timeout = UINT32_C(10000);
uint8_t payload = UINT8_C(0x55); uint8_t payload = UINT8_C(0x55);
const rs_i2c_transfer_t transfer = { const rs_i2c_transfer_t transfer = {
.address = UINT16_C(0x50), .address = UINT16_C(0x50),
@@ -175,54 +176,51 @@ static bool rs_mcu_io(void) {
}; };
rs_i2c_status_t status; rs_i2c_status_t status;
rs_mcu_io_code = 40U;
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) || if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
((capabilities.pad_features & (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)) != ((capabilities.pad_features & (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN) ((RS_SOC_GPIO_HAS_PULLS != 0U) ? (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)
: 0U)) || : 0U)) ||
(rs_gpio_configure(15U, &pull_up) != (rs_gpio_configure(27U, &pull_up) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) || ((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &pull_down) != (rs_gpio_configure(27U, &pull_down) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) || ((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) || (rs_gpio_configure(27U, &output) != RS_OK) || (rs_gpio_write(27U, true) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) || (rs_gpio_read(27U, &high) != RS_OK) || !high || (rs_gpio_write(27U, false) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || high) { (rs_gpio_read(27U, &high) != RS_OK) || high) {
return false; return false;
} }
rs_mcu_io_code = 41U;
if ((rs_uart_configure(&loopback, RS_TIMEOUT_DEFAULT) != RS_OK) || if ((rs_uart_configure(&loopback, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_write_dma(transmit, 4U, RS_TIMEOUT_DEFAULT) != RS_OK) || (rs_uart_write_dma(transmit, 4U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_read_dma(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) { (rs_uart_read_dma(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false; return false;
} }
rs_mcu_io_code = 42U;
for (uint32_t index = 0U; index < 4U; ++index) { for (uint32_t index = 0U; index < 4U; ++index) {
if (transmit[index] != receive[index]) { if (transmit[index] != receive[index]) {
return false; return false;
} }
} }
rs_mcu_io_code = 43U;
if ((rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) || if ((rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) ||
(rs_uart_timing_calculate(RS_CPU_CLOCK_HZ, UART_BPS, &timing) != RS_OK)) { (rs_i2c_init(RS_I2C_BUS_1, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_ENOTSUP)) {
return false;
}
/* UART1 uses the same register ABI; the current shared high-level UART
* API selects UART0. Exercise UART1 PIO and its internal loopback here. */
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x00)) = timing.baud_integer;
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x04)) = timing.baud_fraction;
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x08)) = UINT32_C(3);
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x0c)) = UINT32_C(7);
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x10)) = UINT32_C(0x5a);
while (((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x18)) & UINT32_C(0x40)) != 0U) &&
(timeout != 0U)) {
--timeout;
}
if ((timeout == 0U) || ((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x14)) &
UINT32_C(0xff)) != UINT32_C(0x5a))) {
return false; return false;
} }
/* There is no slave on the pulled-up acceptance board: a real address /* There is no slave on the pulled-up acceptance board: a real address
* transaction must terminate as NACK rather than stick waiting on the bus. */ * transaction must terminate as NACK rather than stick waiting on the bus. */
for (uint32_t bus = 0U; bus < 2U; ++bus) { for (uint32_t bus = 0U; bus < 1U; ++bus) {
rs_mcu_io_code = 44U;
const rs_i2c_bus_t instance = (bus == 0U) ? RS_I2C_BUS_0 : RS_I2C_BUS_1; const rs_i2c_bus_t instance = (bus == 0U) ? RS_I2C_BUS_0 : RS_I2C_BUS_1;
if ((rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_OK) || rs_status_t init_result = rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000));
(rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT) != RS_EIO) || rs_status_t transfer_result = init_result == RS_OK
? rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT)
: init_result;
printf("R2_P6_IO i2c_init=%d i2c_transfer=%d\n", (int)init_result, (int)transfer_result);
if (rs_i2c_get_status(instance, &status) == RS_OK) {
printf("R2_P6_IO i2c_errors=%lx\n", (unsigned long)status.errors);
}
if ((init_result != RS_OK) || (transfer_result != RS_EIO) ||
(rs_i2c_get_status(instance, &status) != RS_OK) || (rs_i2c_get_status(instance, &status) != RS_OK) ||
((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) { ((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) {
return false; return false;
@@ -255,6 +253,7 @@ int main(void) {
rs_watchdog_status_t watchdog = {0}; rs_watchdog_status_t watchdog = {0};
uint64_t before; uint64_t before;
uint64_t after; uint64_t after;
uint8_t p6_result;
const rs_watchdog_config_t watchdog_config = { const rs_watchdog_config_t watchdog_config = {
.prescale_divider = 1U, .prescale_divider = 1U,
.timeout_ticks = UINT32_C(20000), .timeout_ticks = UINT32_C(20000),
@@ -285,11 +284,14 @@ int main(void) {
} }
if ((rs_mcu_compressed_probe() != 42U) || ((__RV_CSR_READ(CSR_MISA) & UINT32_C(1)) != 0U) || if ((rs_mcu_compressed_probe() != 42U) || ((__RV_CSR_READ(CSR_MISA) & UINT32_C(1)) != 0U) ||
(rs_irq_register_exception(2U, rs_mcu_exception) != RS_OK) || (rs_irq_register_exception(2U, rs_mcu_exception) != RS_OK) ||
(rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK)) { (rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK) ||
(rs_irq_register_exception(7U, rs_mcu_exception) != RS_OK)) {
rs_mcu_finish(false, 4U); rs_mcu_finish(false, 4U);
} }
rs_mcu_probes_active = true;
rs_mcu_atomic_probe(rs_mcu_source); rs_mcu_atomic_probe(rs_mcu_source);
rs_mcu_unmapped_probe(); rs_mcu_unmapped_probe();
rs_mcu_probes_active = false;
if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed || if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed ||
(rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending || (rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending ||
(fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) { (fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) {
@@ -307,7 +309,11 @@ int main(void) {
rs_mcu_finish(false, 8U); rs_mcu_finish(false, 8U);
} }
if (!rs_mcu_io()) { if (!rs_mcu_io()) {
rs_mcu_finish(false, 10U); rs_mcu_finish(false, rs_mcu_io_code);
}
p6_result = rs_tiny_p6_acceptance();
if (p6_result != 0U) {
rs_mcu_finish(false, p6_result);
} }
printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n"); printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n");
if ((rs_watchdog_configure(&watchdog_config) != RS_OK) || if ((rs_watchdog_configure(&watchdog_config) != RS_OK) ||
+2 -1
View File
@@ -1,3 +1,4 @@
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
APP_CFLAGS += -Os APP_CFLAGS += -Os
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p6.c
+801
View File
@@ -0,0 +1,801 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/arch/riscv/system_base.h>
#include <retrosoc/core/irq.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/generated/irq_metadata.h>
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/crc.h>
#include <retrosoc/hal/crypto.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/i2s.h>
#include <retrosoc/hal/rng.h>
#include <retrosoc/hal/sdio.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
#include "mcu_shared_acceptance.h"
static volatile uint32_t rs_p6_irq_count;
static uint32_t rs_p6_irq_selected;
static volatile bool rs_p6_irq_error;
static _Alignas(64) uint32_t rs_p6_source[256];
static _Alignas(64) uint32_t rs_p6_destination[258];
static _Alignas(64) uint32_t rs_p6_audio_tx[768];
static _Alignas(64) uint32_t rs_p6_audio_rx[562];
static _Alignas(64) rs_dma_tcd_t rs_p6_descriptors[4];
static rs_dma_session_t rs_p6_audio_tx_owner;
static rs_dma_session_t rs_p6_audio_rx_owner;
static rs_dma_session_t rs_p6_background_owner;
static uint32_t rs_p6_pixels[65];
static void rs_p6_irq_handler(uintptr_t cause, uintptr_t stack) {
(void)cause;
(void)stack;
++rs_p6_irq_count;
if (rs_irq_disable_external(rs_p6_irq_selected) != RS_OK) {
rs_p6_irq_error = true;
}
}
static bool rs_p6_irq_arm(uint32_t irq) {
rs_p6_irq_selected = irq;
rs_p6_irq_count = 0U;
rs_p6_irq_error = false;
if ((rs_irq_register_external(irq, rs_p6_irq_handler) != RS_OK) ||
(rs_irq_enable_external(irq, rs_p6_irq_handler) != RS_OK)) {
return false;
}
__RV_CSR_SET(CSR_MSTATUS, UINT32_C(8));
return true;
}
static bool rs_p6_irq_received(void) {
uint32_t timeout = UINT32_C(100000);
while ((rs_p6_irq_count == 0U) && (timeout != 0U)) {
--timeout;
}
return (rs_p6_irq_count == 1U) && !rs_p6_irq_error;
}
static bool rs_p6_route(uint32_t pin, bool open_drain) {
const rs_gpio_config_t config = {
.mode = RS_GPIO_MODE_ALT0,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.open_drain = open_drain,
.filter_enable = false,
};
return rs_gpio_configure(pin, &config) == RS_OK;
}
static bool rs_p6_uart(void) {
const uint8_t transmit[4] = {0xA5U, 0x5AU, 0x96U, 0x69U};
rs_uart_rx_data_t receive[4];
const rs_uart_config_t unsupported = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.baud_rate = UART_BPS,
.data_bits = 8U,
.stop_bits = 1U,
.parity = RS_UART_PARITY_NONE,
.rx_watermark = 1U,
.rx_timeout_bits = 16U,
.tx_enable = true,
.rx_enable = true,
.auto_cts_enable = true,
};
if ((rs_uart_configure(&unsupported, RS_TIMEOUT_DEFAULT) != RS_ENOTSUP) ||
(rs_uart_write(transmit, sizeof(transmit), RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_read(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < 4U; ++index) {
if (receive[index].data != transmit[index] || receive[index].errors != 0U) {
return false;
}
}
printf("\nR2_P6_UART pads=1 echo_bytes=4 flow_rejected=1\n");
return true;
}
static bool rs_p6_rng(void) {
const rs_rng_config_t config = {.fifo_watermark = 1U};
rs_rng_snapshot_t snapshot;
uint32_t word = 0U;
if ((rs_rng_init(&config) != RS_OK) ||
(rs_rng_read_entropy(&word, RS_TIMEOUT_DEFAULT) != RS_ENOTSUP) ||
(rs_rng_read_diagnostic(&word, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_rng_get_status(&snapshot) != RS_OK) || snapshot.source_qualified ||
!rs_p6_irq_arm(RS_SOC_EXT_IRQ_RNG) ||
(rs_rng_interrupt_enable(RS_RNG_INTERRUPT_SOURCE_FAULT) != RS_OK) ||
(rs_rng_interrupt_test(RS_RNG_INTERRUPT_SOURCE_FAULT) != RS_OK) || !rs_p6_irq_received() ||
(rs_rng_interrupt_enable(0U) != RS_OK) ||
(rs_rng_interrupt_clear(RS_RNG_INTERRUPT_ALL) != RS_OK)) {
return false;
}
printf("R2_P6_RNG unqualified=1 diagnostic=1 irq=1\n");
return true;
}
static bool rs_p6_dma_capabilities(void) {
const uint32_t capability = RS_DMA_REG(RS_DMA_REG_CAPABILITY);
rs_dma_config_t config = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = (uintptr_t)rs_p6_destination,
.byte_count = 4U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 1U,
};
rs_dma_status_t status;
const uint32_t unsupported[] = {9U, 10U, 11U, 14U, 15U};
if (((capability >> 8U) & UINT32_C(0xFF)) != 8U) {
return false;
}
for (size_t index = 0U; index < sizeof(unsupported) / sizeof(unsupported[0]); ++index) {
rs_p6_destination[0] = UINT32_C(0x12345678);
if (rs_dma_configure(RS_DMA_CHANNEL_DVP, &config) != RS_OK) {
return false;
}
/* Exercise hardware rejection independently of the SDK's earlier gate. */
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_REQUEST_SEL) = unsupported[index];
if ((rs_dma_start(RS_DMA_CHANNEL_DVP) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
(rs_dma_get_status(RS_DMA_CHANNEL_DVP, &status) != RS_OK) || !status.error ||
status.busy || (status.bytes_done != 0U) ||
(rs_p6_destination[0] != UINT32_C(0x12345678))) {
return false;
}
}
printf("R2_P6_DMA channels=8 rejected_requests=5\n");
return true;
}
static uint32_t rs_p6_crc_reference(const uint8_t *data, size_t bytes) {
uint32_t value = UINT32_MAX;
for (size_t index = 0U; index < bytes; ++index) {
value ^= (uint32_t)data[index];
for (uint32_t bit = 0U; bit < 8U; ++bit) {
value = (value >> 1U) ^ (((value & 1U) != 0U) ? UINT32_C(0xEDB88320) : 0U);
}
}
return ~value;
}
static bool rs_p6_crc(void) {
rs_crc_config_t crc;
static rs_dma_session_t lease;
uint32_t result;
rs_dma_config_t transfer = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = RS_SOC_APB4_CRC_BASE + CRC_DATA_OFFSET,
.byte_count = UINT32_C(64),
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.burst_beats = 1U,
};
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x12345678) ^ (index * UINT32_C(0x01020408));
}
if ((rs_crc_get_profile(RS_CRC_PROFILE_CRC32_ISO_HDLC, &crc) != RS_OK) ||
(rs_dma_session_acquire(&lease, RS_DMA_CHANNEL_BULK) != RS_OK)) {
return false;
}
for (size_t tail = 0U; tail < 4U; ++tail) {
if ((rs_crc_init(&crc) != RS_OK) || (rs_crc_start() != RS_OK) ||
(rs_dma_session_configure(&lease, &transfer) != RS_OK) ||
(rs_dma_session_start(&lease) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crc_update((const uint8_t *)rs_p6_source + 64U, tail) != RS_OK) ||
(rs_crc_finish(&result) != RS_OK) ||
(result != rs_p6_crc_reference((const uint8_t *)rs_p6_source, 64U + tail))) {
return false;
}
}
if ((rs_dma_session_release(&lease) != RS_OK) ||
(rs_crc_compute(&crc, rs_p6_source, 0U, &result) != RS_OK) || (result != 0U)) {
return false;
}
printf("R2_P6_CRC dma=1 tails=4 empty=1\n");
return true;
}
static bool rs_p6_crypto(void) {
static uint32_t modulus[RS_CRYPTO_RSA_WORDS];
static uint32_t base[RS_CRYPTO_RSA_WORDS];
static uint32_t exponent[RS_CRYPTO_RSA_WORDS];
static uint32_t rsa_result[RS_CRYPTO_RSA_WORDS];
static const uint8_t key[16] = {
0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
0x08U, 0x09U, 0x0AU, 0x0BU, 0x0CU, 0x0DU, 0x0EU, 0x0FU,
};
static const uint32_t input[4] = {
UINT32_C(0x33221100),
UINT32_C(0x77665544),
UINT32_C(0xBBAA9988),
UINT32_C(0xFFEEDDCC),
};
static const uint32_t expected[4] = {
UINT32_C(0xD8E0C469),
UINT32_C(0x30047B6A),
UINT32_C(0x80B7CDD8),
UINT32_C(0x5AC5B470),
};
if ((rs_crypto_zeroize_wait(1U) != RS_ETIMEOUT) ||
(rs_crypto_zeroize_wait(RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_init(2050U) != RS_ETIMEOUT) ||
(rs_crypto_selftest(RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_aes_set_key(key, sizeof(key), RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < 16U; ++index) {
rs_p6_source[index] = input[index % 4U];
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[17] = UINT32_C(0xA5A55A5A);
if (rs_crypto_aes_crypt_dma(RS_CRYPTO_AES_ECB, false, NULL, rs_p6_source, &rs_p6_destination[1],
64U, RS_TIMEOUT_DEFAULT) != RS_OK) {
return false;
}
for (uint32_t index = 0U; index < 16U; ++index) {
if (rs_p6_destination[index + 1U] != expected[index % 4U]) {
return false;
}
}
/* A small public-operation integration vector exercises the full 2048-bit
* storage/control path. Full private vectors remain in the shared suite. */
for (uint32_t index = 0U; index < RS_CRYPTO_RSA_WORDS; ++index) {
modulus[index] = UINT32_MAX;
}
base[0] = 2U;
exponent[0] = 3U;
if ((rs_crypto_rsa_prepare(modulus, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_rsa_modexp(base, exponent, 2U, false, rsa_result, RS_TIMEOUT_DEFAULT) !=
RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < RS_CRYPTO_RSA_WORDS; ++index) {
if (rsa_result[index] != ((index == 0U) ? 8U : 0U)) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[17] != UINT32_C(0xA5A55A5A)) || !rs_p6_irq_arm(RS_SOC_EXT_IRQ_CRYPTO)) {
return false;
}
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_STATE) = RS_CRYPTO_IRQ_ALL;
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_ENABLE) = RS_CRYPTO_IRQ_ZEROIZED;
if ((rs_crypto_zeroize_wait(0U) != RS_ETIMEOUT) ||
(rs_crypto_zeroize_wait(RS_TIMEOUT_DEFAULT) != RS_OK) || !rs_p6_irq_received()) {
return false;
}
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_ENABLE) = 0U;
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_STATE) = RS_CRYPTO_IRQ_ALL;
printf("R2_P6_CRYPTO init=1 aes_sha=1 dma45=1 rsa_public=1 zeroize=1\n");
return true;
}
static bool rs_p6_stop_session(rs_dma_session_t *owner) {
rs_dma_status_t status;
uint32_t timeout = UINT32_C(100000);
if ((rs_dma_get_status(owner->channel, &status) != RS_OK) ||
(status.busy && (rs_dma_session_abort(owner) != RS_OK))) {
return false;
}
do {
if ((rs_dma_get_status(owner->channel, &status) != RS_OK) || (timeout == 0U)) {
return false;
}
--timeout;
} while (status.busy);
return rs_dma_session_release(owner) == RS_OK;
}
static uint32_t rs_p6_audio_sample(bool wide, uint32_t index) {
const uint32_t word = rs_p6_audio_rx[1U + (wide ? index : index / 2U)];
return wide ? word : ((word >> ((index % 2U) * 16U)) & UINT32_C(0xFFFF));
}
static bool rs_p6_audio_words(bool wide, uint32_t received_words) {
const uint32_t samples = wide ? received_words : received_words * 2U;
const uint32_t first = wide ? UINT32_C(0x120020) : UINT32_C(0x2020);
uint32_t offset = 0U;
/* Ignore only the bounded PHY startup preamble, never a missing payload.
* Exactly 256 stereo frames must then match consecutive independent samples. */
while ((offset < 40U) && (rs_p6_audio_sample(wide, offset) != first)) {
++offset;
}
if ((offset >= 40U) || ((offset + 512U) > samples)) {
return false;
}
for (uint32_t sample = 0U; sample < 512U; ++sample) {
if (rs_p6_audio_sample(wide, offset + sample) != (first + sample)) {
printf("R2_P6_AUDIO mismatch wide=%u sample=%lu got=%lx expected=%lx\n", wide ? 1U : 0U,
(unsigned long)sample, (unsigned long)rs_p6_audio_sample(wide, offset + sample),
(unsigned long)(first + sample));
return false;
}
}
return (rs_p6_audio_rx[0] == UINT32_C(0x5A5AA5A5)) &&
(rs_p6_audio_rx[received_words + 1U] == UINT32_C(0xA5A55A5A));
}
static bool rs_p6_audio_ws(bool wide) {
printf("R2_P6_STAGE audio_bits=%u cycles=%lu\n", wide ? 24U : 16U,
(unsigned long)__RV_CSR_READ(CSR_MCYCLE));
const uint32_t rx_words = wide ? 548U : 280U;
const rs_i2s_config_t audio = {
.stream_tx = true,
.stream_rx = true,
.preset = wide ? RS_I2S_PRESET_24B_48K : RS_I2S_PRESET_16B_48K,
.upbound = 96U,
.lowbound = 32U,
};
const rs_ws2812_config_t ws = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.bit_period_ns = 1250U,
.t0h_ns = 350U,
.t1h_ns = 700U,
.reset_ns = 300000U,
.fifo_watermark = 8U,
};
rs_dma_config_t tx = {
.kind = RS_DMA_KIND_MM_TO_STREAM,
.request = RS_DMA_REQUEST_I2S_TX,
.source = (uintptr_t)rs_p6_audio_tx,
.byte_count = sizeof(rs_p6_audio_tx),
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.priority = 3U,
.burst_beats = 16U,
};
rs_dma_config_t rx = {
.kind = RS_DMA_KIND_STREAM_TO_MM,
.request = RS_DMA_REQUEST_I2S_RX,
.destination = (uintptr_t)&rs_p6_audio_rx[1],
.byte_count = rx_words * 4U,
.width = RS_DMA_WIDTH_32,
.destination_increment = true,
.priority = 3U,
.burst_beats = 16U,
};
rs_dma_config_t background = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = (uintptr_t)&rs_p6_destination[1],
.byte_count = 256U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U,
};
rs_i2s_status_t audio_status;
rs_dma_status_t rx_status, bg_status;
rs_ws2812_dma_status_t ws_status;
uint64_t now, deadline, start;
uint32_t cycles, instructions;
uint32_t frame = 0U, background_jobs = 0U;
for (uint32_t pin = 12U; pin <= 17U; ++pin) {
if (!rs_p6_route(pin, false)) {
return false;
}
}
if (!rs_p6_route(26U, false) || (rs_i2s_disable() != RS_OK) ||
(rs_i2s_flush(true, true, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_i2s_configure(&audio) != RS_OK) || (rs_ws2812_init(&ws) != RS_OK)) {
return false;
}
printf("R2_P6_STAGE audio_host_ready=1 cycles=%lu\n", (unsigned long)__RV_CSR_READ(CSR_MCYCLE));
for (uint32_t index = 0U; index < 768U; ++index) {
rs_p6_audio_tx[index] =
wide ? UINT32_C(0x120000) + index
: ((UINT32_C(0x2001) + 2U * index) << 16U) | (UINT32_C(0x2000) + 2U * index);
}
for (uint32_t index = 0U; index < 65U; ++index) {
rs_p6_pixels[index] = index * UINT32_C(0x010203);
}
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x5A123456) ^ index;
rs_p6_destination[index + 1U] = 0U;
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[257] = UINT32_C(0xA5A55A5A);
rs_p6_audio_rx[0] = UINT32_C(0x5A5AA5A5);
rs_p6_audio_rx[rx_words + 1U] = UINT32_C(0xA5A55A5A);
for (uint32_t index = 0U; index < 4U; ++index) {
rs_p6_descriptors[index] = (rs_dma_tcd_t){
.source = (uint32_t)(uintptr_t)&rs_p6_source[index * 64U],
.destination = (uint32_t)(uintptr_t)&rs_p6_destination[1U + index * 64U],
.byte_count = 256U,
.next_ptr = index == 3U ? 0U : (uint32_t)(uintptr_t)&rs_p6_descriptors[index + 1U],
.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC |
(UINT32_C(16) << RS_DMA_TCD_BURST_SHIFT),
};
}
if (!rs_p6_irq_arm(RS_SOC_EXT_IRQ_I2S) ||
(rs_i2s_interrupt_enable(RS_I2S_INTERRUPT_TX_LOW) != RS_OK) ||
(rs_i2s_interrupt_test(RS_I2S_INTERRUPT_TX_LOW) != RS_OK) || !rs_p6_irq_received() ||
(rs_i2s_interrupt_enable(0U) != RS_OK) ||
(rs_i2s_interrupt_clear(RS_I2S_INTERRUPT_ALL) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_audio_tx_owner, RS_DMA_CHANNEL_I2S_TX) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_audio_rx_owner, RS_DMA_CHANNEL_I2S_RX) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_background_owner, RS_DMA_CHANNEL_DVP) != RS_OK) ||
(rs_dma_session_configure(&rs_p6_audio_tx_owner, &tx) != RS_OK) ||
(rs_dma_session_configure(&rs_p6_audio_rx_owner, &rx) != RS_OK) ||
(rs_dma_session_start(&rs_p6_audio_tx_owner) != RS_OK) ||
(rs_dma_session_start(&rs_p6_audio_rx_owner) != RS_OK) ||
(rs_clint_get_time(&start) != RS_OK)) {
return false;
}
deadline = start + UINT64_C(50000);
printf("R2_P6_STAGE audio_dma_started=1 cycles=%lu\n",
(unsigned long)__RV_CSR_READ(CSR_MCYCLE));
do {
if ((rs_i2s_get_status(&audio_status) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
(now >= deadline)) {
return false;
}
} while (audio_status.tx_level < 96U);
printf("R2_P6_STAGE audio_prefilled=1 cycles=%lu\n", (unsigned long)__RV_CSR_READ(CSR_MCYCLE));
cycles = (uint32_t)__RV_CSR_READ(CSR_MCYCLE);
instructions = (uint32_t)__RV_CSR_READ(CSR_MINSTRET);
if (!rs_p6_irq_arm(RS_SOC_EXT_IRQ_WS2812) ||
(rs_ws2812_irq_enable(RS_WS2812_INTR_FIFO_LOW | RS_WS2812_INTR_DONE) != RS_OK) ||
(rs_i2s_enable(true, true) != RS_OK) ||
(rs_ws2812_dma_begin(rs_p6_pixels, 33U, now, deadline) != RS_OK)) {
return false;
}
do {
if ((rs_clint_get_time(&now) != RS_OK) || (now >= deadline) ||
(rs_ws2812_dma_service(now) != RS_OK) || (rs_ws2812_dma_status(&ws_status) != RS_OK) ||
(rs_dma_get_status(RS_DMA_CHANNEL_I2S_RX, &rx_status) != RS_OK) ||
(rs_dma_get_status(RS_DMA_CHANNEL_DVP, &bg_status) != RS_OK) || rx_status.error ||
bg_status.error) {
return false;
}
if (!bg_status.busy && (background_jobs < 64U)) {
if (rs_dma_session_configure(&rs_p6_background_owner, &background) != RS_OK) {
return false;
}
/* This application exclusively leases channel 2; the ordinary TCD
* helper is blocking, so publish the same finite hardware chain. */
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_TCD_HEAD) =
wide ? (uint32_t)(uintptr_t)rs_p6_descriptors : 0U;
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_TCD_COUNT) = wide ? 4U : 0U;
if (rs_dma_session_start(&rs_p6_background_owner) != RS_OK) {
return false;
}
++background_jobs;
}
if (!ws_status.active && (frame == 0U)) {
if ((ws_status.result != RS_OK) ||
(rs_ws2812_dma_begin(rs_p6_pixels, 65U, now, deadline) != RS_OK)) {
return false;
}
frame = 1U;
ws_status.active = true;
}
} while (rx_status.busy || ws_status.active);
cycles = (uint32_t)__RV_CSR_READ(CSR_MCYCLE) - cycles;
instructions = (uint32_t)__RV_CSR_READ(CSR_MINSTRET) - instructions;
if ((rx_status.bytes_done != rx_words * 4U) || !rx_status.done || (ws_status.result != RS_OK) ||
(rs_p6_irq_count != 1U) || (rs_i2s_get_status(&audio_status) != RS_OK) ||
((audio_status.interrupt_state &
(RS_I2S_INTERRUPT_TX_UNDERRUN | RS_I2S_INTERRUPT_RX_OVERRUN)) != 0U) ||
!rs_p6_stop_session(&rs_p6_audio_tx_owner) || !rs_p6_stop_session(&rs_p6_audio_rx_owner) ||
(rs_i2s_disable() != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_dma_session_release(&rs_p6_background_owner) != RS_OK) ||
(rs_i2s_flush(true, true, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_irq_enable(0U) != RS_OK) || !rs_p6_audio_words(wide, rx_words)) {
return false;
}
for (uint32_t index = 0U; index < (wide ? 256U : 64U); ++index) {
if (rs_p6_destination[index + 1U] != rs_p6_source[index]) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[257] != UINT32_C(0xA5A55A5A))) {
return false;
}
if (!wide) {
for (uint32_t index = 65U; index <= 256U; ++index) {
if (rs_p6_destination[index] != 0U) {
return false;
}
}
}
printf("R2_P6_AUDIO bits=%u frames=256 ws_frames=2 background_jobs=%lu tcd=%u elapsed_us=%lu "
"cycles=%lu instructions=%lu\n",
wide ? 24U : 16U, (unsigned long)background_jobs, wide ? 1U : 0U,
(unsigned long)(now - start), (unsigned long)cycles, (unsigned long)instructions);
return background_jobs != 0U;
}
static bool rs_p6_audio_ws_recovery(void) {
const rs_ws2812_config_t config = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.bit_period_ns = 1250U,
.t0h_ns = 350U,
.t1h_ns = 700U,
.reset_ns = 300000U,
.fifo_watermark = 8U,
};
uint64_t now, began;
rs_i2s_status_t audio;
rs_ws2812_status_t ws;
for (uint32_t phase = 0U; phase < 2U; ++phase) {
if ((rs_gpio_write(27U, true) != RS_OK) || (rs_clint_get_time(&began) != RS_OK)) {
return false;
}
do {
if (rs_clint_get_time(&now) != RS_OK) {
return false;
}
} while ((now - began) < 10U);
if ((rs_i2s_flush(true, true, 16U) != RS_ETIMEOUT) ||
(rs_i2s_get_status(&audio) != RS_OK)) {
return false;
}
/* A timed-out command remains owned by the controller. Do not issue
* another COMMAND while BUSY: that write is correctly rejected. */
do {
if ((rs_i2s_get_status(&audio) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
((now - began) > 1000U)) {
return false;
}
} while ((audio.status & (RS_I2S_STATUS_TX_FLUSH_BUSY | RS_I2S_STATUS_RX_FLUSH_BUSY)) !=
0U);
if (rs_gpio_write(27U, false) != RS_OK) {
return false;
}
}
if ((rs_ws2812_init(&config) != RS_OK) || (rs_ws2812_push(0U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_start(2U) != RS_OK) || (rs_ws2812_wait(RS_TIMEOUT_DEFAULT) != RS_EIO) ||
(rs_ws2812_get_status(&ws) != RS_OK) ||
((ws.error_status & RS_WS2812_ERROR_UNDERFLOW) == 0U) ||
(rs_clint_get_time(&began) != RS_OK)) {
return false;
}
do {
if ((rs_ws2812_get_status(&ws) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
((now - began) > 1000U)) {
return false;
}
} while (ws.busy);
if ((rs_ws2812_init(&config) != RS_OK) || (rs_ws2812_push(0U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_start(65U) != RS_OK) || (rs_ws2812_abort(RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
printf("R2_P6_RECOVERY audio_stops=2 ws_underflow=1 ws_abort=1\n");
return true;
}
static bool rs_p6_sdio_step(rs_status_t result, uint32_t step) {
rs_sdio_status_t status;
if (result == RS_OK) {
return true;
}
if (rs_sdio_status_get(RS_SDIO_INSTANCE_0, &status) == RS_OK) {
printf("R2_P6_SDIO_ERROR step=%lu result=%ld command=%lx data=%lx dma=%lx errors=%lx\n",
(unsigned long)step, (long)result, (unsigned long)status.command_status,
(unsigned long)status.data_status, (unsigned long)status.dma_status,
(unsigned long)status.error_status);
}
return false;
}
static bool rs_p6_sdio(void) {
static _Alignas(16) rs_sdio_descriptor_t descriptor;
rs_sd_memory_info_t card;
rs_sdio_clock_t clock;
rs_sdio_response_t response;
rs_dma_config_t background = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_audio_tx,
.destination = (uintptr_t)&rs_p6_audio_rx[1],
.byte_count = 1024U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U,
};
rs_sdio_command_t command = {
.index = 17U,
.argument = 1U,
.response = RS_SDIO_RESPONSE_R1,
.crc_check = true,
.index_check = true,
};
const rs_sdio_data_config_t pio = {
.block_size = 512U,
.block_count = 1U,
.direction = RS_SDIO_DATA_FROM_CARD,
.dma = false,
.block_mode = true,
};
const rs_sdio_data_config_t dma_write = {
.block_size = 512U,
.block_count = 2U,
.direction = RS_SDIO_DATA_TO_CARD,
.dma = true,
.block_mode = true,
};
uint32_t id, version, capability;
rs_status_t initialized;
printf("R2_P6_STAGE sdio_begin=1\n");
for (uint32_t pin = 0U; pin < 6U; ++pin) {
if (!rs_p6_route(pin, false)) {
return false;
}
}
if ((rs_sdio_probe(RS_SDIO_INSTANCE_1, &id, &version, &capability) != RS_ENOTSUP) ||
(rs_sdio_controller_selftest(RS_SDIO_INSTANCE_0) != RS_OK)) {
return false;
}
initialized =
rs_sd_memory_initialize(RS_SDIO_INSTANCE_0, RS_CPU_CLOCK_HZ, &card, RS_TIMEOUT_DEFAULT);
printf("R2_P6_STAGE sdio_init=%ld clock=%lu width=%u rca=%u\n", (long)initialized,
(unsigned long)card.actual_clock_hz, (unsigned)card.bus_width, (unsigned)card.rca);
if ((initialized != RS_OK) || (card.actual_clock_hz != UINT32_C(12000000)) ||
!card.high_capacity || (card.rca != 1U) || (card.bus_width != RS_SDIO_BUS_WIDTH_4)) {
return false;
}
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x13579BDF) ^ index;
rs_p6_destination[index + 1U] = 0U;
rs_p6_audio_tx[index] = UINT32_C(0x55AA0000) + index;
rs_p6_audio_rx[index + 1U] = ~rs_p6_audio_tx[index];
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[257] = UINT32_C(0xA5A55A5A);
command.index = 25U;
if (!rs_p6_sdio_step(rs_dma_session_acquire(&rs_p6_background_owner, RS_DMA_CHANNEL_DVP), 1U) ||
!rs_p6_sdio_step(rs_dma_session_configure(&rs_p6_background_owner, &background), 2U) ||
!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
3U) ||
!rs_p6_sdio_step(rs_sdio_descriptor_prepare(&descriptor, (uintptr_t)rs_p6_source, 1024U,
(uintptr_t)0U, true, false),
4U) ||
!rs_p6_sdio_step(rs_sdio_dma_setup(RS_SDIO_INSTANCE_0, &descriptor, 1U, 1024U), 5U) ||
!rs_p6_sdio_step(rs_sdio_data_configure(RS_SDIO_INSTANCE_0, &dma_write), 6U) ||
!rs_p6_sdio_step(rs_sdio_data_start(RS_SDIO_INSTANCE_0), 7U) ||
!rs_p6_sdio_step(rs_dma_session_start(&rs_p6_background_owner), 8U) ||
!rs_p6_sdio_step(rs_sdio_data_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 9U) ||
!rs_p6_sdio_step(rs_sdio_dma_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 10U)) {
return false;
}
printf("R2_P6_STAGE sdio_write=1\n");
command.index = 12U;
command.argument = 0U;
command.response = RS_SDIO_RESPONSE_R1B;
if (!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
11U) ||
!rs_p6_sdio_step(rs_sd_memory_read_blocks(RS_SDIO_INSTANCE_0, &card, 1U, 2U,
&rs_p6_destination[1], RS_TIMEOUT_DEFAULT),
12U) ||
!rs_p6_sdio_step(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT), 13U) ||
!rs_p6_sdio_step(rs_dma_session_release(&rs_p6_background_owner), 14U)) {
return false;
}
printf("R2_P6_STAGE sdio_read=1\n");
for (uint32_t index = 0U; index < 256U; ++index) {
if ((rs_p6_destination[index + 1U] != rs_p6_source[index]) ||
(rs_p6_audio_rx[index + 1U] != rs_p6_audio_tx[index])) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[257] != UINT32_C(0xA5A55A5A)) ||
(rs_sdio_clock_set(RS_SDIO_INSTANCE_0, RS_CPU_CLOCK_HZ, 400000U, &clock) != RS_OK) ||
(clock.actual_hz != 400000U)) {
return false;
}
/* Select one-bit card and host consistently before the PIO read. */
command.index = 55U;
command.argument = (uint32_t)card.rca << 16U;
command.response = RS_SDIO_RESPONSE_R1;
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK) {
return false;
}
command.index = 6U;
command.argument = 0U;
if ((rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK) ||
(rs_sdio_bus_width_set(RS_SDIO_INSTANCE_0, RS_SDIO_BUS_WIDTH_1) != RS_OK)) {
return false;
}
command.index = 17U;
command.argument = 1U;
if (!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
15U) ||
!rs_p6_sdio_step(rs_sdio_data_configure(RS_SDIO_INSTANCE_0, &pio), 16U) ||
!rs_p6_sdio_step(rs_sdio_data_start(RS_SDIO_INSTANCE_0), 17U) ||
!rs_p6_sdio_step(
rs_sdio_pio_read(RS_SDIO_INSTANCE_0, &rs_p6_destination[1], 512U, RS_TIMEOUT_DEFAULT),
18U) ||
!rs_p6_sdio_step(rs_sdio_data_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 19U)) {
return false;
}
printf("R2_P6_STAGE sdio_pio=1\n");
for (uint32_t index = 0U; index < 128U; ++index) {
if (rs_p6_destination[index + 1U] != rs_p6_source[index]) {
return false;
}
}
if ((rs_sdio_irq_clear(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
!rs_p6_irq_arm(RS_SOC_EXT_IRQ_SDIO) ||
(rs_sdio_irq_enable(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
(rs_sdio_irq_test(RS_SDIO_INSTANCE_0, 1U) != RS_OK) || !rs_p6_irq_received() ||
(rs_sdio_irq_enable(RS_SDIO_INSTANCE_0, 0U) != RS_OK) ||
(rs_sdio_irq_clear(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
(rs_sdio_timeouts_set(RS_SDIO_INSTANCE_0, 24000U, 24000U, 24000U) != RS_OK)) {
return false;
}
/* Verification card injects faults on these documented CMD13 arguments. */
command.index = 13U;
command.argument = UINT32_C(0xBADC0DE0);
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_EFORMAT) {
return false;
}
command.argument = UINT32_C(0xBADC0DE1);
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_ETIMEOUT) {
return false;
}
command.argument = (uint32_t)card.rca << 16U;
if ((rs_sdio_error_clear(RS_SDIO_INSTANCE_0, RS_SDIO_ERROR_ALL) != RS_OK) ||
(rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK)) {
return false;
}
printf("R2_P6_SDIO dma_bytes=2048 pio_bytes=512 width1=1 width4=1 irq=1 errors=2\n");
return true;
}
uint8_t rs_tiny_p6_acceptance(void) {
if (!rs_p6_uart()) {
return 27U;
}
if (!rs_p6_dma_capabilities()) {
return 19U;
}
if (!rs_p6_rng()) {
return 20U;
}
if (!rs_p6_crc()) {
return 21U;
}
if (!rs_p6_crypto()) {
return 22U;
}
if (!rs_p6_audio_ws(false)) {
return 23U;
}
if (!rs_p6_audio_ws(true)) {
return 24U;
}
if (!rs_p6_audio_ws_recovery()) {
return 26U;
}
if (!rs_p6_sdio()) {
return 25U;
}
return 0U;
}
+9
View File
@@ -50,6 +50,15 @@ typedef enum {
RS_DMA_CHANNEL_BULK = 3, RS_DMA_CHANNEL_BULK = 3,
RS_DMA_CHANNEL_CRYPTO_IN = 4, RS_DMA_CHANNEL_CRYPTO_IN = 4,
RS_DMA_CHANNEL_CRYPTO_OUT = 5, RS_DMA_CHANNEL_CRYPTO_OUT = 5,
#ifdef RS_SOC_TINY
RS_DMA_CHANNEL_DVP = 2,
RS_DMA_CHANNEL_I2S_TX = 6,
RS_DMA_CHANNEL_I2S_RX = 7,
#else
RS_DMA_CHANNEL_DVP = RS_DMA_CHANNEL_BULK,
RS_DMA_CHANNEL_I2S_TX = RS_DMA_CHANNEL_BULK,
RS_DMA_CHANNEL_I2S_RX = RS_DMA_CHANNEL_BULK,
#endif
} rs_dma_channel_t; } rs_dma_channel_t;
typedef struct { typedef struct {
+5 -7
View File
@@ -77,15 +77,13 @@
#define RS_DMA_ERROR_READ UINT32_C(0x00000040) #define RS_DMA_ERROR_READ UINT32_C(0x00000040)
#define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100) #define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100)
#ifdef RS_SOC_TINY #define RS_DMA_CHANNEL_COUNT 8U
#define RS_DMA_CHANNEL_COUNT 4U #ifndef RS_SOC_TINY
#else
#define RS_DMA_CHANNEL_COUNT 8U
#endif
#define RS_DMA_CHANNEL_HP 6U #define RS_DMA_CHANNEL_HP 6U
#define RS_DMA_CHANNEL_RESERVED 7U #define RS_DMA_CHANNEL_RESERVED 7U
#define RS_DMA_MAX_BURST_BEATS 16U #endif
#define RS_DMA_DATA_WIDTH_BITS 32U #define RS_DMA_MAX_BURST_BEATS 16U
#define RS_DMA_DATA_WIDTH_BITS 32U
#define RS_DMA_CHANNEL_OFFSET(channel, offset) \ #define RS_DMA_CHANNEL_OFFSET(channel, offset) \
(RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset)) (RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset))
+5 -5
View File
@@ -427,9 +427,9 @@ void ip_dma_test(void) {
.lowbound = 32U, .lowbound = 32U,
}) != RS_OK) || }) != RS_OK) ||
(rs_i2s_enable(true, false) != RS_OK) || (rs_i2s_enable(true, false) != RS_OK) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &tx_config) != RS_OK) || (rs_dma_configure(RS_DMA_CHANNEL_I2S_TX, &tx_config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) || (rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) { (rs_dma_wait(RS_DMA_CHANNEL_I2S_TX, RS_TIMEOUT_DEFAULT) != RS_OK)) {
printf("dma transmit failed\n"); printf("dma transmit failed\n");
return; return;
} }
@@ -448,9 +448,9 @@ void ip_dma_test(void) {
.lowbound = 32U, .lowbound = 32U,
}) != RS_OK) || }) != RS_OK) ||
(rs_i2s_enable(false, true) != RS_OK) || (rs_i2s_enable(false, true) != RS_OK) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &rx_config) != RS_OK) || (rs_dma_configure(RS_DMA_CHANNEL_I2S_RX, &rx_config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) || (rs_dma_start(RS_DMA_CHANNEL_I2S_RX) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) { (rs_dma_wait(RS_DMA_CHANNEL_I2S_RX, RS_TIMEOUT_DEFAULT) != RS_OK)) {
printf("dma receive failed\n"); printf("dma receive failed\n");
(void)rs_i2s_disable(); (void)rs_i2s_disable();
return; return;
+3 -2
View File
@@ -19,7 +19,8 @@ static bool rs_dma_address_valid(uintptr_t address, uint32_t byte_count, bool in
static bool rs_dma_request_valid(const rs_dma_config_t *config) { static bool rs_dma_request_valid(const rs_dma_config_t *config) {
#ifdef RS_SOC_TINY #ifdef RS_SOC_TINY
if (config->kind != RS_DMA_KIND_MM_TO_MM) { if (((uint32_t)config->request >= 16U) ||
((UINT32_C(0x31FF) & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {
return false; return false;
} }
#endif #endif
@@ -97,7 +98,7 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) {
request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF); request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF);
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F); burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
#ifdef RS_SOC_TINY #ifdef RS_SOC_TINY
if (kind != (uint32_t)RS_DMA_KIND_MM_TO_MM) { if ((UINT32_C(0x31FF) & (UINT32_C(1) << request)) == 0U) {
return RS_ENOTSUP; return RS_ENOTSUP;
} }
#endif #endif
+74
View File
@@ -49,8 +49,12 @@
#define RS_I2C_TIMEOUT_MAX_CYCLES UINT32_C(0xFFFFFF) #define RS_I2C_TIMEOUT_MAX_CYCLES UINT32_C(0xFFFFFF)
static uintptr_t rs_i2c_base(rs_i2c_bus_t bus) { static uintptr_t rs_i2c_base(rs_i2c_bus_t bus) {
#ifdef RS_SOC_TINY
return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_APB4_I2C0_BASE : (uintptr_t)0U;
#else
return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_APB4_I2C0_BASE return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_APB4_I2C0_BASE
: (uintptr_t)RS_SOC_APB4_I2C1_BASE; : (uintptr_t)RS_SOC_APB4_I2C1_BASE;
#endif
} }
static bool rs_i2c_bus_valid(rs_i2c_bus_t bus) { static bool rs_i2c_bus_valid(rs_i2c_bus_t bus) {
@@ -92,8 +96,13 @@ static rs_status_t rs_i2c_gpio_configure(rs_i2c_bus_t bus) {
.filter_enable = false, .filter_enable = false,
.interrupt_enable = false, .interrupt_enable = false,
}; };
#ifdef RS_SOC_TINY
const uint32_t scl_pin = UINT32_C(10);
const uint32_t sda_pin = UINT32_C(11);
#else
const uint32_t scl_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(7) : UINT32_C(3); const uint32_t scl_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(7) : UINT32_C(3);
const uint32_t sda_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(8) : UINT32_C(4); const uint32_t sda_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(8) : UINT32_C(4);
#endif
rs_status_t status = rs_gpio_configure(scl_pin, &pin_config); rs_status_t status = rs_gpio_configure(scl_pin, &pin_config);
if (status == RS_OK) { if (status == RS_OK) {
@@ -228,6 +237,11 @@ static rs_status_t rs_i2c_wait_done(rs_i2c_bus_t bus, rs_timeout_t timeout) {
rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config, rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_i2c_timing_t timing; rs_i2c_timing_t timing;
uint32_t stretch_timeout; uint32_t stretch_timeout;
uint32_t bus_idle_timeout; uint32_t bus_idle_timeout;
@@ -287,6 +301,11 @@ rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config,
} }
rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz) { rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
const rs_i2c_config_t config = { const rs_i2c_config_t config = {
.source_clock_hz = source_clock_hz, .source_clock_hz = source_clock_hz,
.bus_hz = bus_hz, .bus_hz = bus_hz,
@@ -303,6 +322,11 @@ rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus
rs_status_t rs_i2c_transfer(rs_i2c_bus_t bus, const rs_i2c_transfer_t *transfer, rs_status_t rs_i2c_transfer(rs_i2c_bus_t bus, const rs_i2c_transfer_t *transfer,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (transfer == NULL) { if (transfer == NULL) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -338,6 +362,11 @@ static rs_status_t rs_i2c_register_prefix(const rs_i2c_register_access_t *access
rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access, rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
const uint8_t *data, size_t length, rs_timeout_t timeout) { const uint8_t *data, size_t length, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint8_t prefix[2]; uint8_t prefix[2];
size_t prefix_length; size_t prefix_length;
rs_status_t status; rs_status_t status;
@@ -355,6 +384,11 @@ rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access
rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access, rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
uint8_t *data, size_t length, rs_timeout_t timeout) { uint8_t *data, size_t length, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint8_t prefix[2]; uint8_t prefix[2];
size_t prefix_length; size_t prefix_length;
rs_status_t status; rs_status_t status;
@@ -371,6 +405,11 @@ rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_
} }
rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) { rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus)) { if (!rs_i2c_bus_valid(bus)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -382,6 +421,11 @@ rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) {
} }
rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) { rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
if (!rs_i2c_bus_valid(bus)) { if (!rs_i2c_bus_valid(bus)) {
@@ -408,6 +452,11 @@ rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) {
} }
rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) { rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint32_t levels; uint32_t levels;
if (!rs_i2c_bus_valid(bus) || (status == NULL)) { if (!rs_i2c_bus_valid(bus) || (status == NULL)) {
@@ -423,6 +472,11 @@ rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) {
} }
rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) { rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) { if (!rs_i2c_bus_valid(bus) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -431,6 +485,11 @@ rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) {
} }
rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) { rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) { if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -439,6 +498,11 @@ rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) {
} }
rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) { rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) { if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -449,6 +513,11 @@ rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) {
rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace, const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
size_t index; size_t index;
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1; const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
@@ -494,6 +563,11 @@ rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_ad
rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, uint8_t *data, rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, uint8_t *data,
size_t length, rs_i2c_dma_workspace_t *workspace, size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
size_t index; size_t index;
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1; const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
+215 -5
View File
@@ -53,6 +53,12 @@
RS_SD_R1_CARD_ECC_FAILED | RS_SD_R1_CC_ERROR | RS_SD_R1_ERROR | RS_SD_R1_CSD_OVERWRITE | \ RS_SD_R1_CARD_ECC_FAILED | RS_SD_R1_CC_ERROR | RS_SD_R1_ERROR | RS_SD_R1_CSD_OVERWRITE | \
RS_SD_R1_WP_ERASE_SKIP) RS_SD_R1_WP_ERASE_SKIP)
/* Preserve protocol ceilings while supporting slower product input clocks. */
static uint32_t rs_sdio_transfer_clock(uint32_t source_hz, uint32_t ceiling_hz) {
const uint32_t maximum_hz = source_hz / UINT32_C(2);
return (maximum_hz < ceiling_hz) ? maximum_hz : ceiling_hz;
}
static bool rs_sdio_instance_valid(rs_sdio_instance_t instance) { static bool rs_sdio_instance_valid(rs_sdio_instance_t instance) {
return (instance == RS_SDIO_INSTANCE_0) || (instance == RS_SDIO_INSTANCE_1); return (instance == RS_SDIO_INSTANCE_0) || (instance == RS_SDIO_INSTANCE_1);
} }
@@ -61,8 +67,10 @@ static uintptr_t rs_sdio_base(rs_sdio_instance_t instance) {
switch (instance) { switch (instance) {
case RS_SDIO_INSTANCE_0: case RS_SDIO_INSTANCE_0:
return (uintptr_t)RS_SOC_APB4_SDIO0_BASE; return (uintptr_t)RS_SOC_APB4_SDIO0_BASE;
#ifndef RS_SOC_TINY
case RS_SDIO_INSTANCE_1: case RS_SDIO_INSTANCE_1:
return (uintptr_t)RS_SOC_APB4_SDIO1_BASE; return (uintptr_t)RS_SOC_APB4_SDIO1_BASE;
#endif
default: default:
return (uintptr_t)0U; return (uintptr_t)0U;
} }
@@ -257,6 +265,11 @@ static rs_status_t rs_sdio_send_app_command(rs_sdio_instance_t instance, uint16_
rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t *version, rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t *version,
uint32_t *capability) { uint32_t *capability) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (ip_id == NULL) || (version == NULL) || if (!rs_sdio_instance_valid(instance) || (ip_id == NULL) || (version == NULL) ||
(capability == NULL) || (rs_sdio_read(instance, RS_SDIO_REG_IP_ID, ip_id) != RS_OK) || (capability == NULL) || (rs_sdio_read(instance, RS_SDIO_REG_IP_ID, ip_id) != RS_OK) ||
(rs_sdio_read(instance, RS_SDIO_REG_IP_VERSION, version) != RS_OK) || (rs_sdio_read(instance, RS_SDIO_REG_IP_VERSION, version) != RS_OK) ||
@@ -270,6 +283,11 @@ rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t
} }
rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout) { rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
volatile uint32_t *status_reg; volatile uint32_t *status_reg;
if (!rs_sdio_instance_valid(instance)) { if (!rs_sdio_instance_valid(instance)) {
@@ -294,6 +312,11 @@ rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout) {
rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock_hz, rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock_hz,
uint32_t target_clock_hz, rs_sdio_clock_t *clock) { uint32_t target_clock_hz, rs_sdio_clock_t *clock) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_clock_t calculated; rs_sdio_clock_t calculated;
uint32_t value; uint32_t value;
uint32_t enabled; uint32_t enabled;
@@ -318,6 +341,11 @@ rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock
} }
rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width_t width) { rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width_t width) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value; uint32_t value;
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance)) { if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance)) {
@@ -338,6 +366,11 @@ rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width
rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_timeout, rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_timeout,
uint32_t data_timeout, uint32_t busy_timeout) { uint32_t data_timeout, uint32_t busy_timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) || (command_timeout == 0U) || if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) || (command_timeout == 0U) ||
(data_timeout == 0U) || (busy_timeout == 0U)) { (data_timeout == 0U) || (busy_timeout == 0U)) {
return RS_EINVAL; return RS_EINVAL;
@@ -351,6 +384,11 @@ rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_t
} }
rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable) { rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value; uint32_t value;
if (!rs_sdio_instance_valid(instance) || if (!rs_sdio_instance_valid(instance) ||
@@ -366,6 +404,11 @@ rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable) {
} }
rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_t *config) { rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_t *config) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_clock_t clock; rs_sdio_clock_t clock;
rs_status_t status; rs_status_t status;
@@ -393,6 +436,11 @@ rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_
} }
rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *status) { rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *status) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t present; uint32_t present;
if (!rs_sdio_instance_valid(instance) || (status == NULL)) { if (!rs_sdio_instance_valid(instance) || (status == NULL)) {
@@ -421,6 +469,11 @@ rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *st
} }
rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_command_t *command) { rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_command_t *command) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t cfg; uint32_t cfg;
if (!rs_sdio_instance_valid(instance) || (command == NULL) || (command->index > 63U) || if (!rs_sdio_instance_valid(instance) || (command == NULL) || (command->index > 63U) ||
@@ -449,6 +502,11 @@ rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_com
} }
rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t *response) { rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t *response) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (response == NULL) || if (!rs_sdio_instance_valid(instance) || (response == NULL) ||
(rs_sdio_read(instance, RS_SDIO_REG_RESP0, &response->words[0]) != RS_OK) || (rs_sdio_read(instance, RS_SDIO_REG_RESP0, &response->words[0]) != RS_OK) ||
(rs_sdio_read(instance, RS_SDIO_REG_RESP1, &response->words[1]) != RS_OK) || (rs_sdio_read(instance, RS_SDIO_REG_RESP1, &response->words[1]) != RS_OK) ||
@@ -462,6 +520,11 @@ rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t
rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t *response, rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t *response,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
if (!rs_sdio_instance_valid(instance) || (response == NULL)) { if (!rs_sdio_instance_valid(instance) || (response == NULL)) {
@@ -485,6 +548,11 @@ rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t
rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_command_t *command, rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_command_t *command,
rs_sdio_response_t *response, rs_timeout_t timeout) { rs_sdio_response_t *response, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
status = rs_sdio_command_start(instance, command); status = rs_sdio_command_start(instance, command);
@@ -500,6 +568,11 @@ rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_c
rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance, rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance,
const rs_sdio_data_config_t *config) { const rs_sdio_data_config_t *config) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value; uint32_t value;
if (!rs_sdio_instance_valid(instance) || (config == NULL) || (config->block_size == 0U) || if (!rs_sdio_instance_valid(instance) || (config == NULL) || (config->block_size == 0U) ||
@@ -532,6 +605,11 @@ rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance,
} }
rs_status_t rs_sdio_data_start(rs_sdio_instance_t instance) { rs_status_t rs_sdio_data_start(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) || if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) ||
(rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DATA_DONE | RS_SDIO_IRQ_DATA_ERROR) != RS_OK) || (rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DATA_DONE | RS_SDIO_IRQ_DATA_ERROR) != RS_OK) ||
(rs_sdio_error_clear(instance, RS_SDIO_ERROR_DATA_CRC | RS_SDIO_ERROR_DATA_TIMEOUT | (rs_sdio_error_clear(instance, RS_SDIO_ERROR_DATA_CRC | RS_SDIO_ERROR_DATA_TIMEOUT |
@@ -582,6 +660,11 @@ static rs_status_t rs_sdio_pio_store(rs_sdio_instance_t instance, const uint8_t
rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, size_t byte_count, rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, size_t byte_count,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
const uint8_t *bytes = (const uint8_t *)buffer; const uint8_t *bytes = (const uint8_t *)buffer;
rs_status_t status; rs_status_t status;
size_t offset = 0U; size_t offset = 0U;
@@ -612,6 +695,11 @@ rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, s
rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t byte_count, rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t byte_count,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint8_t *bytes = (uint8_t *)buffer; uint8_t *bytes = (uint8_t *)buffer;
size_t offset = 0U; size_t offset = 0U;
@@ -645,6 +733,11 @@ rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t b
} }
rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) { rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
if (!rs_sdio_instance_valid(instance)) { if (!rs_sdio_instance_valid(instance)) {
@@ -664,6 +757,11 @@ rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout)
rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t *descriptors, rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t *descriptors,
uint16_t count, uint32_t total_bytes) { uint16_t count, uint32_t total_bytes) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uintptr_t address; uintptr_t address;
if (!rs_sdio_instance_valid(instance) || (total_bytes == 0U) || if (!rs_sdio_instance_valid(instance) || (total_bytes == 0U) ||
@@ -686,6 +784,11 @@ rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t
} }
rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance) { rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
/* /*
* DATA_START is the coordinated DMA/data-engine launch. Keep this * DATA_START is the coordinated DMA/data-engine launch. Keep this
* compatibility entry point as an alias so software cannot start a DMA * compatibility entry point as an alias so software cannot start a DMA
@@ -695,6 +798,11 @@ rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance) {
} }
rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance) { rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance)) { if (!rs_sdio_instance_valid(instance)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -702,6 +810,11 @@ rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance) {
} }
rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_status_t *status) { rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_status_t *status) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t dma_error; uint32_t dma_error;
if (!rs_sdio_instance_valid(instance) || (status == NULL) || if (!rs_sdio_instance_valid(instance) || (status == NULL) ||
@@ -720,6 +833,11 @@ rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_stat
} }
rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) { rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status; rs_status_t status;
if (!rs_sdio_instance_valid(instance)) { if (!rs_sdio_instance_valid(instance)) {
@@ -738,6 +856,11 @@ rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout)
} }
rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask) { rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) { if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -745,6 +868,11 @@ rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask) {
} }
rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask) { rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (mask == NULL)) { if (!rs_sdio_instance_valid(instance) || (mask == NULL)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -752,6 +880,11 @@ rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask) {
} }
rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask) { rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) { if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -759,6 +892,11 @@ rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask) {
} }
rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask) { rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) { if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -766,6 +904,11 @@ rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask) {
} }
rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask) { rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_ERROR_ALL) != 0U)) { if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_ERROR_ALL) != 0U)) {
return RS_EINVAL; return RS_EINVAL;
} }
@@ -773,6 +916,11 @@ rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask) {
} }
rs_status_t rs_sdio_controller_selftest(rs_sdio_instance_t instance) { rs_status_t rs_sdio_controller_selftest(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_status_t status; rs_sdio_status_t status;
uint32_t ip_id; uint32_t ip_id;
uint32_t version; uint32_t version;
@@ -916,7 +1064,9 @@ static rs_status_t rs_sd_memory_try_high_speed(rs_sdio_instance_t instance,
return RS_EIO; return RS_EIO;
} }
if (rs_sdio_clock_set(instance, source_clock_hz, if (rs_sdio_clock_set(instance, source_clock_hz,
(source_clock_hz >= 100000000U) ? 50000000U : 36000000U, rs_sdio_transfer_clock(source_clock_hz, (source_clock_hz >= 100000000U)
? 50000000U
: 36000000U),
&clock) == RS_OK) { &clock) == RS_OK) {
info->actual_clock_hz = clock.actual_hz; info->actual_clock_hz = clock.actual_hz;
info->high_speed = true; info->high_speed = true;
@@ -928,6 +1078,11 @@ static rs_status_t rs_sd_memory_try_high_speed(rs_sdio_instance_t instance,
rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz, rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
rs_sd_memory_info_t *info, rs_timeout_t timeout) { rs_sd_memory_info_t *info, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_response_t response; rs_sdio_response_t response;
rs_sdio_clock_t clock; rs_sdio_clock_t clock;
uint32_t payload; uint32_t payload;
@@ -1008,7 +1163,9 @@ rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source
if (status != RS_OK) { if (status != RS_OK) {
return status; return status;
} }
if ((rs_sdio_response_field(&response, 31U, 16U, &payload) != RS_OK) || /* RESP retains the complete 48-bit frame: the R6 payload is [39:8],
* so its upper sixteen RCA bits are [39:24], above the status/CRC. */
if ((rs_sdio_response_field(&response, 39U, 24U, &payload) != RS_OK) ||
(payload > UINT16_MAX)) { (payload > UINT16_MAX)) {
return RS_EFORMAT; return RS_EFORMAT;
} }
@@ -1046,7 +1203,8 @@ rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source
info->bus_width_fallback = true; info->bus_width_fallback = true;
info->bus_width = RS_SDIO_BUS_WIDTH_1; info->bus_width = RS_SDIO_BUS_WIDTH_1;
} }
status = rs_sdio_clock_set(instance, source_clock_hz, 36000000U, &clock); status = rs_sdio_clock_set(instance, source_clock_hz,
rs_sdio_transfer_clock(source_clock_hz, 36000000U), &clock);
if (status != RS_OK) { if (status != RS_OK) {
return status; return status;
} }
@@ -1150,12 +1308,22 @@ static rs_status_t rs_sd_memory_transfer(rs_sdio_instance_t instance,
rs_status_t rs_sd_memory_read_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info, rs_status_t rs_sd_memory_read_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
uint32_t block, uint32_t count, void *buffer, uint32_t block, uint32_t count, void *buffer,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sd_memory_transfer(instance, info, block, count, buffer, false, timeout); return rs_sd_memory_transfer(instance, info, block, count, buffer, false, timeout);
} }
rs_status_t rs_sd_memory_write_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info, rs_status_t rs_sd_memory_write_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
uint32_t block, uint32_t count, const void *buffer, uint32_t block, uint32_t count, const void *buffer,
rs_timeout_t timeout) { rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sd_memory_transfer(instance, info, block, count, (void *)buffer, true, timeout); return rs_sd_memory_transfer(instance, info, block, count, (void *)buffer, true, timeout);
} }
@@ -1175,6 +1343,11 @@ static rs_status_t rs_sdio_function_command(rs_sdio_instance_t instance, uint8_t
rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t function, rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t function,
uint32_t address, uint8_t *data, rs_timeout_t timeout) { uint32_t address, uint8_t *data, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_cmd52_t command = { rs_sdio_cmd52_t command = {
.function = function, .function = function,
.address = address, .address = address,
@@ -1203,6 +1376,11 @@ rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t fun
rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t function, rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t function,
uint32_t address, uint8_t data, rs_timeout_t timeout) { uint32_t address, uint8_t data, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
const rs_sdio_cmd52_t command = { const rs_sdio_cmd52_t command = {
.function = function, .function = function,
.address = address, .address = address,
@@ -1223,6 +1401,11 @@ rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t fu
rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz, rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
rs_sdio_function_info_t *info, rs_timeout_t timeout) { rs_sdio_function_info_t *info, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_response_t response; rs_sdio_response_t response;
rs_sdio_clock_t clock; rs_sdio_clock_t clock;
uint32_t payload; uint32_t payload;
@@ -1345,8 +1528,10 @@ rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t so
} else { } else {
info->high_speed_fallback = true; info->high_speed_fallback = true;
} }
status = rs_sdio_clock_set(instance, source_clock_hz, status = rs_sdio_clock_set(
high_speed_enabled ? 24000000U : 25000000U, &clock); instance, source_clock_hz,
rs_sdio_transfer_clock(source_clock_hz, high_speed_enabled ? 24000000U : 25000000U),
&clock);
if (status != RS_OK) { if (status != RS_OK) {
return status; return status;
} }
@@ -1356,6 +1541,11 @@ rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t so
rs_status_t rs_sdio_function_enable(rs_sdio_instance_t instance, rs_sdio_function_info_t *info, rs_status_t rs_sdio_function_enable(rs_sdio_instance_t instance, rs_sdio_function_info_t *info,
uint8_t function, rs_timeout_t timeout) { uint8_t function, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint8_t enable; uint8_t enable;
uint8_t ready; uint8_t ready;
uint32_t attempts; uint32_t attempts;
@@ -1394,6 +1584,11 @@ rs_status_t rs_sdio_function_cmd53_transfer(rs_sdio_instance_t instance, uint8_t
uint32_t address, void *buffer, size_t byte_count, uint32_t address, void *buffer, size_t byte_count,
bool write, bool block_mode, bool fixed_address, bool write, bool block_mode, bool fixed_address,
uint16_t block_size, rs_timeout_t timeout) { uint16_t block_size, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_cmd53_t command; rs_sdio_cmd53_t command;
rs_sdio_data_config_t data_config; rs_sdio_data_config_t data_config;
rs_sdio_response_t response; rs_sdio_response_t response;
@@ -1482,6 +1677,11 @@ rs_status_t rs_sdio_function_cmd53_transfer(rs_sdio_instance_t instance, uint8_t
} }
rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable) { rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t mask; uint32_t mask;
if (!rs_sdio_instance_valid(instance) || if (!rs_sdio_instance_valid(instance) ||
@@ -1501,6 +1701,11 @@ rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable
} }
rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pending) { rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pending) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t mask; uint32_t mask;
if ((pending == NULL) || (rs_sdio_irq_pending(instance, &mask) != RS_OK)) { if ((pending == NULL) || (rs_sdio_irq_pending(instance, &mask) != RS_OK)) {
@@ -1511,5 +1716,10 @@ rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pend
} }
rs_status_t rs_sdio_function_irq_ack(rs_sdio_instance_t instance) { rs_status_t rs_sdio_function_irq_ack(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DAT1); return rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DAT1);
} }
+3 -1
View File
@@ -136,7 +136,9 @@ rs_status_t rs_sd_memory_parse_csd(const rs_sdio_response_t *response, rs_sd_mem
return RS_EINVAL; return RS_EINVAL;
} }
block_length = UINT32_C(1) << read_bl_len; block_length = UINT32_C(1) << read_bl_len;
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT64_C(1) << (c_size_mult + 2U)) * /* C_SIZE_MULT is a three-bit CSD field: the shift is at most nine.
* Keep the factor RV32-native; the enclosing product remains 64-bit. */
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT32_C(1) << (c_size_mult + 2U)) *
(uint64_t)block_length; (uint64_t)block_length;
capacity_blocks = capacity_bytes / (uint64_t)RS_SD_MEMORY_BLOCK_SIZE; capacity_blocks = capacity_bytes / (uint64_t)RS_SD_MEMORY_BLOCK_SIZE;
info->card_type = RS_SD_MEMORY_SDSC; info->card_type = RS_SD_MEMORY_SDSC;
+11
View File
@@ -84,6 +84,12 @@ static rs_status_t rs_uart_flow_gpio_configure(const rs_uart_config_t *config) {
}; };
rs_status_t status = RS_OK; rs_status_t status = RS_OK;
#ifdef RS_SOC_TINY
status = rs_gpio_configure(8U, &pin_config);
if (status == RS_OK) {
status = rs_gpio_configure(9U, &pin_config);
}
#endif
if (config->auto_cts_enable) { if (config->auto_cts_enable) {
status = rs_gpio_configure(RS_UART_CTS_GPIO, &pin_config); status = rs_gpio_configure(RS_UART_CTS_GPIO, &pin_config);
} }
@@ -106,6 +112,11 @@ rs_status_t rs_uart_configure(const rs_uart_config_t *config, rs_timeout_t timeo
return RS_EINVAL; return RS_EINVAL;
} }
#ifdef RS_SOC_TINY
if (config->auto_cts_enable || config->auto_rts_enable) {
return RS_ENOTSUP;
}
#endif
RS_UART_CTRL = 0U; RS_UART_CTRL = 0U;
while (((RS_UART_STATUS & (RS_UART_STATUS_TX_BUSY | RS_UART_STATUS_RX_BUSY)) != 0U) && while (((RS_UART_STATUS & (RS_UART_STATUS_TX_BUSY | RS_UART_STATUS_RX_BUSY)) != 0U) &&
(timeout != 0U)) { (timeout != 0U)) {
+1 -1
View File
@@ -18,7 +18,7 @@ TOP := retrosoc_tiny_asic
# validated against rtl/tiny/integration/clock_reset_domains.json when the # validated against rtl/tiny/integration/clock_reset_domains.json when the
# commercial timing contract is generated. Tiny boots SAFE24: the PLL_TOP # commercial timing contract is generated. Tiny boots SAFE24: the PLL_TOP
# macro is present in the netlist but parked off and is not a clock source. # macro is present in the netlist but parked off and is not a clock source.
COMMERCIAL_CLOCK_DOMAINS := system jtag COMMERCIAL_CLOCK_DOMAINS := system jtag audio
# Qualified-I/O interface groups and the budget variables each group requires. # Qualified-I/O interface groups and the budget variables each group requires.
COMMERCIAL_IO_INTERFACES := JTAG XPI ASYNC COMMERCIAL_IO_INTERFACES := JTAG XPI ASYNC
@@ -11,15 +11,15 @@
# retroSoC Tiny (retrosoc_tiny_asic) product overlay. # retroSoC Tiny (retrosoc_tiny_asic) product overlay.
# #
# Clock intent: the canonical contract (rtl/tiny/integration/ # Clock intent: the canonical contract (rtl/tiny/integration/
# clock_reset_domains.json) owns two domains, system (SAFE24 external clock) # clock_reset_domains.json) owns system (SAFE24 external clock), jtag and
# and jtag. The ICS55 PLL_TOP macro is present but parked off (EN=0) and is # the independent GPIO17 AUDIO reference. The ICS55 PLL_TOP macro is present but parked off (EN=0) and is
# not a clock source; it carries no characterized timing arcs yet, so no PLL # not a clock source; it carries no characterized timing arcs yet, so no PLL
# overlay clock is created here. See docs/ip/tiny-soc.md (TINY-055). # overlay clock is created here. See docs/ip/tiny-soc.md (TINY-055).
namespace eval product {} namespace eval product {}
proc product::expected_clock_domains {} { proc product::expected_clock_domains {} {
return {system jtag} return {system jtag audio}
} }
proc product::custom_clock_domains {} { proc product::custom_clock_domains {} {
@@ -27,7 +27,7 @@ proc product::custom_clock_domains {} {
} }
proc product::clock_names {} { proc product::clock_names {} {
return {clk_system clk_jtag} return {clk_system clk_jtag clk_audio}
} }
proc product::io_interface_groups {} { proc product::io_interface_groups {} {
@@ -4,7 +4,7 @@
set audit_dir [file dirname $::env(OPENSTA_REPORT)] set audit_dir [file dirname $::env(OPENSTA_REPORT)]
set pll [get_cells -hierarchical -filter {ref_name == PLL_TOP}] set pll [get_cells -hierarchical -filter {ref_name == PLL_TOP}]
set ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}] set ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}]
if {[llength $pll] != 1 || [llength $ram] != 32} { error "Tiny ICS55 macro binding mismatch" } if {[llength $pll] != 1 || [llength $ram] != 38} { error "Tiny ICS55 macro binding mismatch" }
if {[llength [get_clocks clk_system]] != 1} { error "Tiny SYS clock missing" } if {[llength [get_clocks clk_system]] != 1} { error "Tiny SYS clock missing" }
report_clock_properties > $audit_dir/clocks.rpt report_clock_properties > $audit_dir/clocks.rpt
check_setup -verbose > $audit_dir/coverage.rpt check_setup -verbose > $audit_dir/coverage.rpt
@@ -7,7 +7,29 @@ if {[llength $p4_ram] == 0} {
set p4_ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}] set p4_ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}]
set p4_clock_pin CLK set p4_clock_pin CLK
} }
if {[llength $p4_ram] != 32} { error "P4 requires 32 main SRAM macros" } if {[llength $p4_ram] != 38} { error "P6 requires 32 main SRAM and six private Crypto macros" }
set p6_all_ram $p4_ram
set p4_ram {}
set p6_crypto {}
set p6_crypto_expected {}
for {set bank 0} {$bank < 6} {incr bank} {
lappend p6_crypto_expected [format {u_soc.u_crypto.u_storage.gen_bank[%d].gen_macro.u_sram.u_mem} $bank]
}
set p6_crypto_seen {}
foreach cell $p6_all_ram {
set name [string map {/ .} [get_full_name $cell]]
set is_crypto 0
foreach expected $p6_crypto_expected {
if {[string equal [string range $name end-[expr {[string length $expected]-1}] end] $expected]} {
lappend p6_crypto_seen $expected
set is_crypto 1
}
}
if {$is_crypto} { lappend p6_crypto $cell } else { lappend p4_ram $cell }
}
if {[llength $p4_ram] != 32 || [lsort $p6_crypto_seen] ne [lsort $p6_crypto_expected]} {
error "P6 private/main SRAM hierarchy mismatch"
}
set p4_expected {} set p4_expected {}
for {set group 0} {$group < 4} {incr group} { for {set group 0} {$group < 4} {incr group} {
for {set bank 0} {$bank < 8} {incr bank} { for {set bank 0} {$bank < 8} {incr bank} {
@@ -31,7 +53,7 @@ foreach pin [get_fanout -from $p4_source -flat -pin_levels 1 -trace_arcs all] {
dict set p4_direct [get_full_name $pin] 1 dict set p4_direct [get_full_name $pin] 1
} }
set p4_clock_report [open $p4_dir/p4-clock-bindings.rpt w] set p4_clock_report [open $p4_dir/p4-clock-bindings.rpt w]
foreach cell $p4_ram { foreach cell [concat $p4_ram $p6_crypto] {
set clocks {} set clocks {}
foreach pin [get_pins -of_objects $cell] { foreach pin [get_pins -of_objects $cell] {
if {[string match "*/$p4_clock_pin" [get_full_name $pin]]} { lappend clocks $pin } if {[string match "*/$p4_clock_pin" [get_full_name $pin]]} { lappend clocks $pin }
@@ -52,3 +74,4 @@ puts $p4_clock_report "CPU clock endpoints: [llength $p4_cpu]"
puts $p4_clock_report "Frontend clock endpoints: [llength $p4_front]" puts $p4_clock_report "Frontend clock endpoints: [llength $p4_front]"
close $p4_clock_report close $p4_clock_report
puts "TINY_R2_P4_BINDING_PASS: four groups, 32 macros, common CPU/SRAM SYS; not timing qualification" puts "TINY_R2_P4_BINDING_PASS: four groups, 32 macros, common CPU/SRAM SYS; not timing qualification"
puts "TINY_R2_P6_BINDING_PASS: six private Crypto macros on SAFE24 PCLK; no capacity alias"
+21 -1
View File
@@ -125,6 +125,26 @@ def clean_source(text: str) -> str:
return re.sub(r"\s+", "", re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S)) return re.sub(r"\s+", "", re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S))
def mini_channel_enum(text: str) -> str:
"""Select Mini's channel roles, excluding the Tiny product branch.
Helper aliases share an allocated context; they do not allocate another
channel. Keep their Mini bulk binding checked instead of parsing Tiny's
literal 6/7 values from an inactive preprocessor branch.
"""
match = re.search(r"typedef enum\s*\{([^{}]*)\}\s*rs_dma_channel_t;", text, re.S)
if match is None:
raise ValueError("missing DMA channel enumeration")
body = re.sub(r"#ifdef RS_SOC_TINY\s*\n[^#]*#else\s*\n([^#]*)#endif", r"\1", match[1])
if "#" in body:
raise ValueError("unreviewed Mini DMA channel selection")
aliases = dict(re.findall(r"(RS_DMA_CHANNEL_\w+)\s*=\s*(RS_DMA_CHANNEL_\w+)\s*,", body))
expected = {"RS_DMA_CHANNEL_DVP", "RS_DMA_CHANNEL_I2S_TX", "RS_DMA_CHANNEL_I2S_RX"}
if any(name not in expected or value != "RS_DMA_CHANNEL_BULK" for name, value in aliases.items()):
raise ValueError("Mini DMA helper alias binding changed")
return body
def validate_programming(spec: dict, ids: set[str], root: Path, revision: str) -> None: def validate_programming(spec: dict, ids: set[str], root: Path, revision: str) -> None:
for relative in dependencies(spec): for relative in dependencies(spec):
path = root / relative path = root / relative
@@ -193,7 +213,7 @@ def collect_programming(root: Path, spec: dict, ids: set[str], revision: str) ->
result = copy.deepcopy(spec) result = copy.deepcopy(spec)
sdk_text = (root / "crt/include/retrosoc/hal/dma.h").read_text() sdk_text = (root / "crt/include/retrosoc/hal/dma.h").read_text()
sdk = constants(sdk_text, "RS_DMA_REQUEST_") sdk = constants(sdk_text, "RS_DMA_REQUEST_")
channels = constants(sdk_text, "RS_DMA_CHANNEL_") channels = constants(mini_channel_enum(sdk_text), "RS_DMA_CHANNEL_")
# Capacity is selected by product; it is not a named channel allocation. # Capacity is selected by product; it is not a named channel allocation.
# Keep the Mini publication's role inventory independent of Tiny's count. # Keep the Mini publication's role inventory independent of Tiny's count.
channels.update(constants((root / "crt/include/retrosoc/hal/dma_regs.h").read_text(), channels.update(constants((root / "crt/include/retrosoc/hal/dma_regs.h").read_text(),
+13 -14
View File
@@ -24,14 +24,14 @@ module i2s_recv (
logic s_lrck_trg; logic s_lrck_trg;
logic [4:0] s_bit_cnt_d, s_bit_cnt_q; logic [4:0] s_bit_cnt_d, s_bit_cnt_q;
logic [4:0] s_bit_num; logic [4:0] s_bit_num;
logic s_first_bit_d, s_first_bit_q; logic s_done_d, s_done_q;
logic [23:0] s_recv_data_d, s_recv_data_q; logic [23:0] s_recv_data_d, s_recv_data_q;
logic [23:0] s_data_d, s_data_q; logic [23:0] s_data_d, s_data_q;
assign s_bit_num = bitmode_i ? 5'd23 : 5'd15; assign s_bit_num = bitmode_i ? 5'd23 : 5'd15;
assign data_o = s_data_q; assign data_o = s_data_q;
assign done_o = s_bit_cnt_q == s_bit_num; assign done_o = s_done_q;
assign s_lrck_trg = lrck_i ^ s_lrck_dely_q; assign s_lrck_trg = lrck_i ^ s_lrck_dely_q;
@@ -89,26 +89,25 @@ module i2s_recv (
); );
always_comb begin // I2S changes LRCK one bit before the next sample's MSB. The bit
s_first_bit_d = s_first_bit_q; // sampled on that transition is the current sample's LSB, not the next
if (s_lrck_trg) s_first_bit_d = adcdat_i; // sample's first bit. Publish completion after data_o has captured it.
end assign s_done_d = sclk_pos_i && s_lrck_trg;
dffer #( dffr #(
.DATA_WIDTH(1) .DATA_WIDTH(1)
) u_first_bit_dffer ( ) u_done_dffr (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(rst_n_i), .rst_n_i(rst_n_i),
.en_i (sclk_pos_i), .dat_i (s_done_d),
.dat_i (s_first_bit_d), .dat_o (s_done_q)
.dat_o (s_first_bit_q)
); );
always_comb begin always_comb begin
s_data_d = s_data_q; s_data_d = s_data_q;
if ((!s_lrck_trg) && (s_bit_cnt_q == (s_bit_num - 1'b1))) begin if (s_lrck_trg) begin
if (bitmode_i) s_data_d = {s_recv_data_d[22:0], s_first_bit_q}; if (bitmode_i) s_data_d = {s_recv_data_q[22:0], adcdat_i};
else s_data_d = {8'd0, s_recv_data_d[14:0], s_first_bit_q}; else s_data_d = {8'd0, s_recv_data_q[14:0], adcdat_i};
end end
end end
dffer #( dffer #(
+1 -1
View File
@@ -92,7 +92,7 @@ module i2s_reg (
endfunction endfunction
assign s_req = apb4.psel && apb4.penable && !s_ready_q; assign s_req = apb4.psel && apb4.penable && !s_ready_q;
assign s_write = |apb4.pstrb; assign s_write = apb4.pwrite;
assign s_accept = s_req; assign s_accept = s_req;
assign s_offset = apb4.paddr[11:0]; assign s_offset = apb4.paddr[11:0];
assign s_aligned = apb4.paddr[1:0] == 2'b00; assign s_aligned = apb4.paddr[1:0] == 2'b00;
+4 -1
View File
@@ -88,7 +88,10 @@ module sdio_command #(
assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1]; assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1];
assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q; assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q;
assign cmd_oe_o = (s_state_q == Send) || (s_tx_final_hold_q && !sample_tick_i); // Admission can coincide with an SDCLK rising edge. Do not expose the
// start bit until the first scheduled launch, or the card sees it twice.
assign cmd_oe_o = ((s_state_q == Send) && (s_tx_started_q || launch_tick_i)) ||
(s_tx_final_hold_q && !sample_tick_i);
assign cmd_do_o = ((s_state_q == Send) || s_tx_final_hold_q) ? s_tx_shift_q[47] : 1'b1; assign cmd_do_o = ((s_state_q == Send) || s_tx_final_hold_q) ? s_tx_shift_q[47] : 1'b1;
assign busy_o = (s_state_q != Idle) && (s_state_q != Complete); assign busy_o = (s_state_q != Idle) && (s_state_q != Complete);
assign done_o = s_done_q; assign done_o = s_done_q;
+9 -8
View File
@@ -102,7 +102,7 @@ module sdio_core #(
logic s_data_tx_last; logic s_data_tx_last;
logic s_data_rx_valid; logic s_data_rx_valid;
logic s_data_rx_ready; logic s_data_rx_ready;
logic s_data_rx_stall_q; logic s_data_rx_stall;
logic [ 31:0] s_data_rx_data; logic [ 31:0] s_data_rx_data;
logic [ 3:0] s_data_rx_strb; logic [ 3:0] s_data_rx_strb;
logic s_data_rx_last; logic s_data_rx_last;
@@ -174,8 +174,8 @@ module sdio_core #(
assign s_clock_en = host_enable_i && clock_enable_i && assign s_clock_en = host_enable_i && clock_enable_i &&
(s_cmd_busy || s_data_busy || s_dma_busy || cmd_start_i || (s_cmd_busy || s_data_busy || s_dma_busy || cmd_start_i ||
data_start_i) && data_start_i) &&
!s_data_rx_stall_q && !s_data_rx_stall &&
!(s_data_busy && s_data_tx_wait && (s_data_oe == 4'b0000)); !(s_data_busy && s_data_tx_wait);
assign s_total_bytes = {16'd0, block_size_i} * {16'd0, block_count_i}; assign s_total_bytes = {16'd0, block_size_i} * {16'd0, block_count_i};
assign s_abort_request = host_abort_i || dma_abort_i || assign s_abort_request = host_abort_i || dma_abort_i ||
(s_dma_done && s_dma_err) || (s_dma_done && s_dma_err) ||
@@ -200,6 +200,9 @@ module sdio_core #(
assign s_dma_data_in_strb = s_data_rx_strb; assign s_dma_data_in_strb = s_data_rx_strb;
assign s_dma_data_in_last = s_data_rx_last; assign s_dma_data_in_last = s_data_rx_last;
assign s_data_rx_ready = data_dma_enable_i ? s_dma_data_in_ready : !s_pio_valid_q; assign s_data_rx_ready = data_dma_enable_i ? s_dma_data_in_ready : !s_pio_valid_q;
// ReadEmit begins on the sampling edge. Stop before the very next launch
// if its word cannot retire; registering this condition loses one card bit.
assign s_data_rx_stall = s_data_rx_valid && !s_data_rx_ready;
assign s_core_busy = s_cmd_busy || s_data_busy || s_dma_busy; assign s_core_busy = s_cmd_busy || s_data_busy || s_dma_busy;
assign busy_o = s_core_busy; assign busy_o = s_core_busy;
assign s_pio_tx_pop = !data_dma_enable_i && s_data_tx_valid && s_data_tx_ready; assign s_pio_tx_pop = !data_dma_enable_i && s_data_tx_valid && s_data_tx_ready;
@@ -401,7 +404,6 @@ module sdio_core #(
s_dat1_meta_q <= 1'b1; s_dat1_meta_q <= 1'b1;
s_dat1_sync_q <= 1'b1; s_dat1_sync_q <= 1'b1;
s_dat1_seen_q <= 1'b0; s_dat1_seen_q <= 1'b0;
s_data_rx_stall_q <= 1'b0;
s_pio_rdata_q <= '0; s_pio_rdata_q <= '0;
s_pio_valid_q <= 1'b0; s_pio_valid_q <= 1'b0;
s_pio_tx_rd_q <= '0; s_pio_tx_rd_q <= '0;
@@ -418,10 +420,9 @@ module sdio_core #(
s_pio_tx_strb_q[index] <= '0; s_pio_tx_strb_q[index] <= '0;
end end
end else begin end else begin
s_dat1_meta_q <= sdio.dat_di_i[1]; s_dat1_meta_q <= sdio.dat_di_i[1];
s_dat1_sync_q <= s_dat1_meta_q; s_dat1_sync_q <= s_dat1_meta_q;
s_dat1_seen_q <= s_dat1_seen_d; s_dat1_seen_q <= s_dat1_seen_d;
s_data_rx_stall_q <= s_data_rx_valid && !s_data_rx_ready;
if (data_start_i) begin if (data_start_i) begin
s_pio_tx_rd_q <= '0; s_pio_tx_rd_q <= '0;
+6 -1
View File
@@ -160,7 +160,12 @@ module sdio_data #(
assign tx_ready_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) || assign tx_ready_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
((s_state_q == WritePayload) && launch_tick_i && s_tx_word_final && ((s_state_q == WritePayload) && launch_tick_i && s_tx_word_final &&
(s_tx_total_q < s_target_bytes_q)); (s_tx_total_q < s_target_bytes_q));
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord); // Stop before launching the next word if memory has not supplied it.
// Waiting only after that launch exposes an idle bit to the card and
// corrupts the next word's MSB when the clock resumes.
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
((s_state_q == WritePayload) && s_tx_word_final &&
(s_tx_total_q < s_target_bytes_q) && !tx_valid_i);
assign rx_valid_o = (s_state_q == ReadEmit); assign rx_valid_o = (s_state_q == ReadEmit);
assign rx_data_o = s_rx_pending_data_q; assign rx_data_o = s_rx_pending_data_q;
assign rx_strb_o = s_rx_pending_strb_q; assign rx_strb_o = s_rx_pending_strb_q;
+12 -5
View File
@@ -516,11 +516,18 @@ module sdio_dma #(
s_err_addr_d = s_payload_addr + s_burst_offset; s_err_addr_d = s_payload_addr + s_burst_offset;
end end
if (s_write_final_sent_q) begin if (s_write_final_sent_q) begin
s_writeback_control_d = (s_desc_control_q & // A retired AXI burst is not necessarily the whole descriptor.
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) | // Keep ownership while subsequent bursts receive its remaining
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) : // bytes, just as the memory-to-card direction does.
(32'd1 << sdio_pkg::SDIO_DESC_DONE)); if (s_fault_d || (s_desc_progress_d >= s_desc_count_q)) begin
s_state_d = WritebackStart; s_writeback_control_d = (s_desc_control_q &
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
s_state_d = WritebackStart;
end else begin
s_state_d = PayloadWriteStart;
end
end end
end end
end end
+5 -4
View File
@@ -179,7 +179,8 @@ CFLAGS += -DRS_SOC_TINY
TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \ TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \
stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \ stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \
gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \ gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \
dma_math.c dma.c onchip_sram.c xpi.c dma_math.c dma.c onchip_sram.c xpi.c rng.c crc.c ws2812_math.c ws2812.c \
crypto.c crypto_lifecycle.c i2s_math.c i2s.c sdio_math.c sdio.c
CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source))) CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source)))
CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c
endif endif
@@ -282,12 +283,12 @@ asm: $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP)
cp $(SW_BUILD_DIR)/asm/hello-asm.bin $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).bin cp $(SW_BUILD_DIR)/asm/hello-asm.bin $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).bin
cp $(SW_BUILD_DIR)/asm/hello-asm.txt $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME)_all.txt cp $(SW_BUILD_DIR)/asm/hello-asm.txt $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME)_all.txt
endif
$(CRYPTO_CONSTANTS_HEADER): $(ROOT_PATH)/scripts/crypto_constants.py $(ROOT_PATH)/scripts/data/crypto_constants.json $(CRYPTO_CONSTANTS_HEADER): $(ROOT_PATH)/scripts/crypto_constants.py $(ROOT_PATH)/scripts/data/crypto_constants.json
$(PYTHON) $(ROOT_PATH)/scripts/crypto_constants.py --root $(ROOT_PATH) --output $(SW_BUILD_DIR)/include $(PYTHON) $(ROOT_PATH)/scripts/crypto_constants.py --root $(ROOT_PATH) --output $(SW_BUILD_DIR)/include
endif CRYPTO_CONSTANTS_DEP := $(CRYPTO_CONSTANTS_HEADER)
CRYPTO_CONSTANTS_DEP := $(if $(filter TINY,$(SOC)),,$(CRYPTO_CONSTANTS_HEADER))
$(FIRMWARE_ELF): $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) $(SOC_IRQ_METADATA) $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(VERSION_HEADER) $(CRYPTO_CONSTANTS_DEP) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \ $(FIRMWARE_ELF): $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) $(SOC_IRQ_METADATA) $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(VERSION_HEADER) $(CRYPTO_CONSTANTS_DEP) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \
$(ROOT_PATH)/rtl/mk/software.mk $(APP_MK) $(ROOT_PATH)/rtl/mk/software.mk $(APP_MK)
+8 -4
View File
@@ -60,6 +60,7 @@ OPENSTA_CONFIG := $(SYN_BUILD_ROOT)/out/$(RTL_NAME)_yosys.config
NETLIST_PATH := $(OPENSTA_NETLIST) NETLIST_PATH := $(OPENSTA_NETLIST)
TINY_CONVERTED := $(SIM_BUILD_ROOT)/converted_soc.v TINY_CONVERTED := $(SIM_BUILD_ROOT)/converted_soc.v
TINY_DEPFILE := $(SIM_BUILD_ROOT)/sources.d TINY_DEPFILE := $(SIM_BUILD_ROOT)/sources.d
TINY_TB_DEPFILE := $(SIM_BUILD_ROOT)/testbench.d
TINY_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log TINY_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log
SOC_SIM_TIME ?= 1800 SOC_SIM_TIME ?= 1800
TINY_SIM_TIMEOUT ?= 20000000 TINY_SIM_TIMEOUT ?= 20000000
@@ -69,7 +70,7 @@ IVERILOG ?= iverilog
VVP ?= vvp VVP ?= vvp
include $(ROOT_PATH)/rtl/mk/software.mk include $(ROOT_PATH)/rtl/mk/software.mk
-include $(TINY_DEPFILE) -include $(TINY_DEPFILE) $(TINY_TB_DEPFILE)
$(TINY_CONVERTED): $(FILELIST_STAMP) $(TINY_CONVERTED): $(FILELIST_STAMP)
@mkdir -p $(@D) @mkdir -p $(@D)
@@ -95,6 +96,9 @@ $(SIM_BUILD_ROOT)/simv: $(TINY_CONVERTED) $(wildcard $(RTL_PATH)/dv/*.sv)
--log $(SIM_BUILD_ROOT)/compile.log --result $(SIM_BUILD_ROOT)/result-compile.json -- \ --log $(SIM_BUILD_ROOT)/compile.log --result $(SIM_BUILD_ROOT)/result-compile.json -- \
$(IVERILOG) -g2012 -gno-specify -s $(RTL_TOP) -o $@ -f $(GENERATED_FL_DIR)/def.fl \ $(IVERILOG) -g2012 -gno-specify -s $(RTL_TOP) -o $@ -f $(GENERATED_FL_DIR)/def.fl \
-f $(GENERATED_FL_DIR)/inc.fl -f $(PDK_FILELIST) $(TINY_TB_FLIST) $(TINY_CONVERTED) -f $(GENERATED_FL_DIR)/inc.fl -f $(PDK_FILELIST) $(TINY_TB_FLIST) $(TINY_CONVERTED)
$(PYTHON) $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $(GENERATED_FL_DIR)/def.fl \
-f $(GENERATED_FL_DIR)/inc.fl $(TINY_TB_FLIST) \
--target $@ --output $(TINY_TB_DEPFILE)
comp: $(SIM_BUILD_ROOT)/simv comp: $(SIM_BUILD_ROOT)/simv
TINY_RUN := $(VVP) $(SIM_BUILD_ROOT)/simv TINY_RUN := $(VVP) $(SIM_BUILD_ROOT)/simv
endif endif
@@ -204,7 +208,7 @@ tiny-r2-p3-report:
# P4 preserves old image identity and replays each selected image exactly once. # P4 preserves old image identity and replays each selected image exactly once.
TINY_P4_REFERENCE ?= TINY_P4_REFERENCE ?=
TINY_P4_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked TINY_P4_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked
tiny-r2-p4-replay: | manifest tiny-r2-p4-replay: | manifest
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p4.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \ $(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p4.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
--reference $(TINY_P4_REFERENCE) --cases $(TINY_P4_CASES) --simulator $(SIM_TOOL_NAME) \ --reference $(TINY_P4_REFERENCE) --cases $(TINY_P4_CASES) --simulator $(SIM_TOOL_NAME) \
@@ -220,7 +224,7 @@ tiny-r2-p4-report:
# P5 compares the same P3 binaries to retained P4 observations, with its own # P5 compares the same P3 binaries to retained P4 observations, with its own
# phase identity and evidence root. The historical P3/P4 commands stay intact. # phase identity and evidence root. The historical P3/P4 commands stay intact.
TINY_P5_REFERENCE ?= TINY_P5_REFERENCE ?=
TINY_P5_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked TINY_P5_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked
tiny-r2-p5-replay: | manifest tiny-r2-p5-replay: | manifest
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \ $(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
--reference $(TINY_P5_REFERENCE) --cases $(TINY_P5_CASES) --simulator $(SIM_TOOL_NAME) \ --reference $(TINY_P5_REFERENCE) --cases $(TINY_P5_CASES) --simulator $(SIM_TOOL_NAME) \
@@ -231,4 +235,4 @@ tiny-r2-p5-replay: | manifest
tiny-r2-p5-report: tiny-r2-p5-report:
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) --report $(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) --report
.PHONY: tiny-r2-p5-replay tiny-r2-p5-report .PHONY: tiny-r2-p5-replay tiny-r2-p5-report
+54 -18
View File
@@ -300,15 +300,6 @@
"public": true, "public": true,
"user_access": "none" "user_access": "none"
}, },
{
"symbol": "APB4_I2C1",
"base": "0x10011000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{ {
"symbol": "APB4_GPIO_ADMIN", "symbol": "APB4_GPIO_ADMIN",
"base": "0x10014000", "base": "0x10014000",
@@ -327,15 +318,6 @@
"public": true, "public": true,
"user_access": "none" "user_access": "none"
}, },
{
"symbol": "APB4_UART1",
"base": "0x10018000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{ {
"symbol": "APB4_ARCHINFO", "symbol": "APB4_ARCHINFO",
"base": "0x20000000", "base": "0x20000000",
@@ -399,6 +381,60 @@
"public": true, "public": true,
"user_access": "rw", "user_access": "rw",
"burst": "incr16" "burst": "incr16"
},
{
"symbol": "APB4_RNG",
"base": "0x20001000",
"size": "0x00001000",
"route": "apb4_system",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_CRC",
"base": "0x20006000",
"size": "0x00001000",
"route": "apb4_system",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_WS2812",
"base": "0x10008000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_CRYPTO",
"base": "0x1000C000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_I2S",
"base": "0x10007000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_SDIO0",
"base": "0x1000F000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
} }
], ],
"legacy_rib": false, "legacy_rib": false,
+36 -36
View File
@@ -1,3 +1,4 @@
`include "tiny_topology.svh"
// Copyright (c) 2026 Yuchi Miao // Copyright (c) 2026 Yuchi Miao
// SPDX-License-Identifier: MulanPSL-2.0 // SPDX-License-Identifier: MulanPSL-2.0
`timescale 1ns / 1ps `timescale 1ns / 1ps
@@ -8,24 +9,27 @@ module retrosoc_tiny_tb;
logic s_reset_driver = 1'b0; logic s_reset_driver = 1'b0;
wire s_rst_n = s_reset_driver; wire s_rst_n = s_reset_driver;
wire s_uart0_tx, s_uart1_tx; wire s_uart0_tx, s_uart1_tx;
wire s_uart0_rx = 1'b1; wire s_uart0_rx = 1'b1;
wire s_uart1_rx = 1'b1; wire s_uart1_rx = 1'b1;
logic s_jtag_tck_driver = 1'b0; logic s_jtag_tck_driver = 1'b0;
logic s_jtag_tms_driver = 1'b1; logic s_jtag_tms_driver = 1'b1;
logic s_jtag_tdi_driver = 1'b0; logic s_jtag_tdi_driver = 1'b0;
wire s_jtag_tck = s_jtag_tck_driver; wire s_jtag_tck = s_jtag_tck_driver;
wire s_jtag_tms = s_jtag_tms_driver; wire s_jtag_tms = s_jtag_tms_driver;
wire s_jtag_tdi = s_jtag_tdi_driver; wire s_jtag_tdi = s_jtag_tdi_driver;
wire s_jtag_trst_n = s_rst_n; wire s_jtag_trst_n = s_rst_n;
wire s_jtag_tdo; wire s_jtag_tdo;
tri1 [31:0] s_gpio; `include "tiny_tb_gpio.svh"
wire s_xpi_sck; wire s_xpi_sck;
wire [ 3:0] s_xpi_nss; wire [3:0] s_xpi_nss;
tri [ 3:0] s_xpi_data; tri [3:0] s_xpi_data;
integer s_cycles; integer s_cycles;
integer s_max_cycles = 20000000; `ifndef RETROSOC_SOC__TINY_NETLIST
integer s_done_cycles = 0; `include "tiny_p6_devices.svh"
logic [ 7:0] s_uart_byte; `endif
integer s_max_cycles = 20000000;
integer s_done_cycles = 0;
logic [7:0] s_uart_byte;
`include "tiny_jtag_smoke.svh" `include "tiny_jtag_smoke.svh"
`ifdef RETROSOC_SOC__TINY_NETLIST `ifdef RETROSOC_SOC__TINY_NETLIST
@@ -64,24 +68,15 @@ module retrosoc_tiny_tb;
if (s_done_cycles != 128) $fatal(1, "SIM_TEST_TIMEOUT Tiny cycles=%0d", s_cycles); if (s_done_cycles != 128) $fatal(1, "SIM_TEST_TIMEOUT Tiny cycles=%0d", s_cycles);
end end
`ifdef RETROSOC_SOC__TINY_NETLIST `ifdef SIMU_IVERILOG
localparam bit ObserveUartAlways = 1'b1; localparam bit ObserveUartAlways = 1'b1;
`else `else
localparam bit ObserveUartAlways = 1'b0; localparam bit ObserveUartAlways = 1'b0;
`endif `endif
`ifdef SIMU_IVERILOG // The Verilator RTL prints accepted TX bytes. Icarus observes serial output for
localparam bit ObserveUartBaseline = 1'b1; // all firmware measurements; SYSCTRL remains the authoritative verdict.
`else
localparam bit ObserveUartBaseline = 1'b0;
`endif
// The Verilator RTL already prints accepted TX bytes. The Icarus observer is
// opt-in for measurements; SYSCTRL remains the authoritative verdict.
initial begin initial begin
if (ObserveUartAlways || (ObserveUartBaseline && ($test$plusargs( if (ObserveUartAlways) begin
"tiny_r2_baseline"
) || $test$plusargs(
"tiny_r2_p3"
)))) begin
forever begin forever begin
@(negedge s_uart0_tx); @(negedge s_uart0_tx);
#1627.604; #1627.604;
@@ -239,10 +234,13 @@ module retrosoc_tiny_tb;
// Icarus 13 miscounts the temporary expression $countones(~a_23_bit_vector). // Icarus 13 miscounts the temporary expression $countones(~a_23_bit_vector).
// Count declared bits explicitly and reject unknowns rather than correcting // Count declared bits explicitly and reject unknowns rather than correcting
// a report afterward. This function changes only opt-in P5 observations. // a report afterward. This function changes only opt-in P5 observations.
function automatic integer p5_active_targets(input logic [22:0] idle_i); function automatic integer p5_active_targets(
input logic [`RETROSOC_TINY__TARGET_COUNT-1:0] idle_i);
integer count; integer count;
count = 0; count = 0;
for (integer target_index = 0; target_index < 23; target_index++) begin for (
integer target_index = 0; target_index < `RETROSOC_TINY__TARGET_COUNT; target_index++
) begin
if (idle_i[target_index] === 1'b0) count++; if (idle_i[target_index] === 1'b0) count++;
else if (idle_i[target_index] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P5 unknown target state"); else if (idle_i[target_index] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P5 unknown target state");
end end
@@ -250,11 +248,13 @@ module retrosoc_tiny_tb;
endfunction endfunction
initial begin : p5_monitor_check initial begin : p5_monitor_check
if ($test$plusargs("tiny_r2_p5_monitor_check")) begin if ($test$plusargs("tiny_r2_p5_monitor_check")) begin
for (integer active = 0; active <= 23; active++) begin for (integer active = 0; active <= `RETROSOC_TINY__TARGET_COUNT; active++) begin
if (p5_active_targets(23'h7fffff ^ ((23'd1 << active) - 23'd1)) != active) if (p5_active_targets(
{`RETROSOC_TINY__TARGET_COUNT{1'b1}} ^ ((`RETROSOC_TINY__TARGET_COUNT'(1) << active) - `RETROSOC_TINY__TARGET_COUNT'(1))
) != active)
$fatal(1, "SIM_TEST_FAIL P5 monitor population count"); $fatal(1, "SIM_TEST_FAIL P5 monitor population count");
end end
$display("SIM_TEST_PASS Tiny P5 monitor-check counts=0..23; instrumentation only"); $display("SIM_TEST_PASS Tiny P5 monitor-check counts=0..27; instrumentation only");
$finish; $finish;
end end
end end
+329
View File
@@ -0,0 +1,329 @@
// Verification-only external devices on the Gen1 GPIO routes. No DUT state is driven.
logic s_p6_io_trace_started = 0;
always @(negedge s_clk) begin
if ($test$plusargs("p6_debug_io")) begin
if (u_dut.u_soc.u_i2c0.s_cmd_valid && !s_p6_io_trace_started) begin
s_p6_io_trace_started = 1;
$display("P6_IO_DIAGNOSTIC start pads=%b raw=%b%b filtered=%b%b drive=%b%b", s_gpio[11:10],
u_dut.u_soc.u_i2c0_if.sda_i, u_dut.u_soc.u_i2c0_if.scl_i,
u_dut.u_soc.u_i2c0.s_sda_filtered, u_dut.u_soc.u_i2c0.s_scl_filtered,
u_dut.u_soc.u_i2c0.s_sda_pull_low, u_dut.u_soc.u_i2c0.s_scl_pull_low);
$fflush();
end
if (u_dut.u_soc.u_i2c0.s_addr_nack_event || u_dut.u_soc.u_i2c0.s_bus_timeout_event ||
u_dut.u_soc.u_i2c0.s_cmd_timeout_event || u_dut.u_soc.u_i2c0.s_stretch_timeout_event ||
u_dut.u_soc.u_i2c0.s_arb_lost_event) begin
$display(
"P6_IO_DIAGNOSTIC nack=%b bus_timeout=%b command_timeout=%b stretch=%b arbitration=%b pads=%b drive=%b%b",
u_dut.u_soc.u_i2c0.s_addr_nack_event, u_dut.u_soc.u_i2c0.s_bus_timeout_event,
u_dut.u_soc.u_i2c0.s_cmd_timeout_event, u_dut.u_soc.u_i2c0.s_stretch_timeout_event,
u_dut.u_soc.u_i2c0.s_arb_lost_event, s_gpio[11:10], u_dut.u_soc.u_i2c0.s_sda_pull_low,
u_dut.u_soc.u_i2c0.s_scl_pull_low);
$finish; // Diagnostic only: deliberately no SIM_TEST_PASS verdict.
end
end
end
`define _P6_WS u_dut.u_soc.u_ws2812
logic s_p6_uart_rx = 1;
logic [7:0] s_p6_uart_byte;
logic [7:0] s_p6_uart_packet [4];
integer s_p6_uart_index = 0, s_p6_uart_routes = 0;
assign s_gpio_9 = s_p6_uart_rx;
initial begin
forever begin
@(negedge s_gpio_8);
#1627.604;
for (integer bit_index = 0; bit_index < 8; bit_index++) begin
s_p6_uart_byte[bit_index] = s_gpio_8;
#1085.069;
end
if (s_p6_uart_index != 0 || s_p6_uart_byte == 8'ha5) begin
s_p6_uart_packet[s_p6_uart_index] = s_p6_uart_byte;
s_p6_uart_index++;
if (s_p6_uart_index == 4) begin
if (s_p6_uart_packet[0] != 8'ha5 || s_p6_uart_packet[1] != 8'h5a ||
s_p6_uart_packet[2] != 8'h96 || s_p6_uart_packet[3] != 8'h69)
$fatal(1, "SIM_TEST_FAIL P6 UART GPIO8 framing/data");
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
s_p6_uart_rx = 0;
#1085.069;
for (integer bit_index = 0; bit_index < 8; bit_index++) begin
s_p6_uart_rx = s_p6_uart_packet[byte_index][bit_index];
#1085.069;
end
s_p6_uart_rx = 1;
#1085.069;
end
s_p6_uart_index = 0;
s_p6_uart_routes++;
end
end
end
end
integer s_p6_frames = 0, s_p6_completed = 0, s_p6_underflows = 0, s_p6_aborts = 0;
longint unsigned s_p6_sdio_overlap = 0, s_p6_overlap_fetches = 0;
always @(posedge s_clk) begin
if (u_dut.u_soc.u_dma.s_busy[2] && u_dut.u_soc.u_sdio.s_dma_status[0]) begin
s_p6_sdio_overlap++;
if (u_dut.u_soc.u_cpu_ahbl_if[0].hready && u_dut.u_soc.u_cpu_ahbl_if[0].htrans[1])
s_p6_overlap_fetches++;
end
end
integer s_p6_word = 0, s_p6_bit = 0, s_p6_bit_cycle = 0, s_p6_reset_cycles = 0;
integer s_p6_ws_wait = 0, s_p6_ws_wait_max = 0, s_p6_fifo_max = 0;
integer s_p6_clock_stops = 0;
integer s_p6_wire_cases = 0, s_p6_wire_matched = 0, s_p6_wire_examined = 0;
integer s_p6_wire_edges = 0;
logic s_p6_wire_found = 0, s_p6_wire_boundary = 0, s_p6_wire_lrck = 0;
logic [23:0] s_p6_wire_shift = 0;
logic [31:0] s_p6_wire_word;
wire s_p6_audio_enabled = u_dut.u_soc.u_i2s.u_i2s_core.enable_i;
wire s_p6_audio_wide = u_dut.u_soc.u_i2s.u_i2s_core.s_bitmode;
// Decode actual BCLK/LRCLK/DOUT pads, independently of the receive engine.
always @(posedge s_gpio[12] or negedge s_p6_audio_enabled) begin
if (!s_p6_audio_enabled) begin
if (s_p6_wire_found) begin
if (s_p6_wire_matched != 512) $fatal(1, "SIM_TEST_FAIL P6 truncated I2S wire payload");
s_p6_wire_cases++;
$display("R2_P6_I2S_WIRE bits=%0d samples=%0d", s_p6_audio_wide ? 24 : 16, s_p6_wire_matched);
end
s_p6_wire_matched = 0;
s_p6_wire_examined = 0;
s_p6_wire_edges = 0;
s_p6_wire_found = 0;
s_p6_wire_boundary = 0;
s_p6_wire_lrck = 0;
s_p6_wire_shift = 0;
end else begin
if (s_gpio[14] !== 1'b0 && s_gpio[14] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P6 unknown I2S DOUT");
s_p6_wire_shift = {s_p6_wire_shift[22:0], s_gpio[14]};
s_p6_wire_edges++;
if (s_gpio[13] != s_p6_wire_lrck) begin
if (s_p6_wire_boundary && s_p6_wire_edges != (s_p6_audio_wide ? 24 : 16))
$fatal(1, "SIM_TEST_FAIL P6 I2S slot length");
s_p6_wire_boundary = 1;
s_p6_wire_edges = 0;
s_p6_wire_word = s_p6_audio_wide ? {8'd0, s_p6_wire_shift} : {16'd0, s_p6_wire_shift[15:0]};
if (!s_p6_wire_found && s_p6_wire_word == (s_p6_audio_wide ? 32'h120020 : 32'h2020))
s_p6_wire_found = 1;
if (s_p6_wire_found && s_p6_wire_matched < 512) begin
if (s_p6_wire_word != (s_p6_audio_wide ? 32'h120020 : 32'h2020) + 32'(s_p6_wire_matched))
$fatal(1, "SIM_TEST_FAIL P6 I2S wire ordering/data index=%0d", s_p6_wire_matched);
s_p6_wire_matched++;
end
s_p6_wire_examined++;
if (!s_p6_wire_found && s_p6_wire_examined > 40)
$fatal(1, "SIM_TEST_FAIL P6 missing I2S wire payload");
s_p6_wire_lrck = s_gpio[13];
end
end
end
logic s_p6_busy_previous = 0;
logic [23:0] s_p6_pixel;
logic s_p6_expected_high;
logic s_p6_audio_reference = 1'b0;
logic s_p6_audio_running = 1'b0;
initial begin
repeat (200) @(negedge s_clk);
if (!u_dut.u_soc.s_cpu_ready_rst_n)
$fatal(1, "SIM_TEST_FAIL CPU boot waited for absent AUDIO clock");
s_p6_audio_running = 1'b1;
end
// GPIO27 is a verification-board control only after the four normal WS frames.
// Stop low and high in separate cases, then restore the source without DUT forcing.
always @(posedge s_gpio[27]) begin
if (s_p6_frames == 4 && s_p6_clock_stops < 2) begin
wait (s_p6_audio_reference == (s_p6_clock_stops != 0));
s_p6_audio_running = 0;
repeat (4800) @(negedge s_clk);
s_p6_clock_stops++;
s_p6_audio_running = 1;
end
end
always @(negedge s_clk) begin
if (`_P6_WS.s_busy && !s_p6_busy_previous) begin
s_p6_frames++;
s_p6_word = 0;
s_p6_bit = 0;
s_p6_bit_cycle = 0;
s_p6_reset_cycles = 0;
end
if (`_P6_WS.s_underflow) s_p6_underflows++;
if (`_P6_WS.s_aborted) s_p6_aborts++;
if (`_P6_WS.s_busy && !`_P6_WS.s_reset_active && s_p6_frames <= 4) begin
s_p6_pixel = 24'(s_p6_word * 32'h010203);
s_p6_expected_high = s_p6_bit_cycle < (s_p6_pixel[23-s_p6_bit] ? 17 : 8);
if (s_gpio[26] !== s_p6_expected_high)
$fatal(
1,
"SIM_TEST_FAIL Tiny P6 WS pad waveform frame=%0d word=%0d bit=%0d cycle=%0d",
s_p6_frames,
s_p6_word,
s_p6_bit,
s_p6_bit_cycle
);
s_p6_bit_cycle++;
if (s_p6_bit_cycle == 30) begin
s_p6_bit_cycle = 0;
s_p6_bit++;
if (s_p6_bit == 24) begin
s_p6_bit = 0;
s_p6_word++;
end
end
if (`_P6_WS.u_ws2812_reg.s_fifo_level > s_p6_fifo_max)
s_p6_fifo_max = integer'(`_P6_WS.u_ws2812_reg.s_fifo_level);
if (`_P6_WS.u_ws2812_reg.s_fifo_low && !`_P6_WS.u_ws2812_reg.s_tx_push) s_p6_ws_wait++;
else s_p6_ws_wait = 0;
if (s_p6_ws_wait > s_p6_ws_wait_max) s_p6_ws_wait_max = s_p6_ws_wait;
if (s_p6_ws_wait > 4800 || s_p6_fifo_max > 16)
$fatal(1, "SIM_TEST_FAIL Tiny P6 WS service budget/occupancy");
end else s_p6_ws_wait = 0;
if (`_P6_WS.s_reset_active) begin
if (s_gpio[26] !== 1'b0) $fatal(1, "SIM_TEST_FAIL Tiny P6 WS reset-low");
s_p6_reset_cycles++;
end
if (!`_P6_WS.s_busy && s_p6_busy_previous) begin
if (s_p6_reset_cycles < 7200) $fatal(1, "SIM_TEST_FAIL Tiny P6 WS reset interval");
if (s_p6_frames <= 4) begin
if (s_p6_word != ((s_p6_frames % 2) ? 33 : 65) || s_p6_bit != 0 || s_p6_bit_cycle != 0)
$fatal(1, "SIM_TEST_FAIL Tiny P6 WS exact frame length");
s_p6_completed++;
end
end
s_p6_busy_previous = `_P6_WS.s_busy;
end
final begin
if (u_dut.s_test_done && u_dut.s_test_pass && s_p6_frames != 0) begin
if (s_p6_sdio_overlap == 0 || s_p6_overlap_fetches == 0)
$fatal(1, "SIM_TEST_FAIL P6 missing actual SDIO/DMA/CPU contention");
$display("R2_P6_CONTENTION sdio_dma_cycles=%0d cpu_fetches=%0d", s_p6_sdio_overlap,
s_p6_overlap_fetches);
if (s_p6_completed != 4 || s_p6_underflows != 1 || s_p6_aborts != 1 ||
s_p6_clock_stops != 2 || s_p6_wire_cases != 2 || s_p6_uart_routes != 1)
$fatal(1, "SIM_TEST_FAIL Tiny P6 incomplete device coverage");
$display(
"R2_P6_DEVICES ws_frames=%0d ws_wait_max=%0d fifo_max=%0d underflows=%0d aborts=%0d clock_stops=%0d",
s_p6_completed, s_p6_ws_wait_max, s_p6_fifo_max, s_p6_underflows, s_p6_aborts,
s_p6_clock_stops);
end
end
`undef _P6_WS
// Counts describe observed bus handshakes over the complete acceptance run.
// Read bytes require OKAY; write bytes are accepted W strobes, not rollback claims.
for (genvar owner = 0; owner < 3; owner++) begin : gen_p6_axi_observer
longint unsigned read_bytes = 0, write_bytes = 0, descriptor_bytes = 0;
longint unsigned ar_wait = 0, aw_wait = 0, w_wait = 0, r_wait = 0, b_wait = 0;
longint unsigned ar_max = 0, aw_max = 0, w_max = 0, r_max = 0, b_max = 0;
logic pending_read = 0, pending_write = 0, descriptor = 0;
logic [2:0] read_size = 0;
`define _P6_AXI u_dut.u_soc.u_masters_axi4_if[owner]
always @(posedge s_clk) begin
if (s_rst_n) begin
if (`_P6_AXI.arvalid && `_P6_AXI.arready) begin
if (pending_read) $fatal(1, "SIM_TEST_FAIL P6 duplicate read admission");
pending_read = 1;
read_size = `_P6_AXI.arsize;
descriptor = owner == 1 && u_dut.u_soc.u_dma.s_tcd_count[64+:32] != 0 &&
`_P6_AXI.araddr >= u_dut.u_soc.u_dma.s_tcd_head[64+:32] &&
`_P6_AXI.araddr < u_dut.u_soc.u_dma.s_tcd_head[64+:32] + 32'd256;
end
if (`_P6_AXI.rvalid && `_P6_AXI.rready) begin
if (!pending_read) $fatal(1, "SIM_TEST_FAIL P6 unsolicited read response");
if (`_P6_AXI.rresp == 0) begin
read_bytes += 64'd1 << read_size;
if (descriptor) descriptor_bytes += 64'd1 << read_size;
end
if (`_P6_AXI.rlast) pending_read = 0;
end
if (`_P6_AXI.awvalid && `_P6_AXI.awready) begin
if (pending_write) $fatal(1, "SIM_TEST_FAIL P6 duplicate write admission");
pending_write = 1;
end
if (`_P6_AXI.wvalid && `_P6_AXI.wready) begin
if (!pending_write) $fatal(1, "SIM_TEST_FAIL P6 W without saved AW");
for (integer lane = 0; lane < 4; lane++) if (`_P6_AXI.wstrb[lane]) write_bytes++;
end
if (`_P6_AXI.bvalid && `_P6_AXI.bready) begin
if (!pending_write) $fatal(1, "SIM_TEST_FAIL P6 unsolicited write response");
pending_write = 0;
end
ar_wait = `_P6_AXI.arvalid && !`_P6_AXI.arready ? ar_wait + 1 : 0;
aw_wait = `_P6_AXI.awvalid && !`_P6_AXI.awready ? aw_wait + 1 : 0;
w_wait = `_P6_AXI.wvalid && !`_P6_AXI.wready ? w_wait + 1 : 0;
r_wait = `_P6_AXI.rvalid && !`_P6_AXI.rready ? r_wait + 1 : 0;
b_wait = `_P6_AXI.bvalid && !`_P6_AXI.bready ? b_wait + 1 : 0;
if (ar_wait > ar_max) ar_max = ar_wait;
if (aw_wait > aw_max) aw_max = aw_wait;
if (w_wait > w_max) w_max = w_wait;
if (r_wait > r_max) r_max = r_wait;
if (b_wait > b_max) b_max = b_wait;
end
end
final begin
if (u_dut.s_test_done && u_dut.s_test_pass && s_p6_frames != 0) begin
if (pending_read || pending_write) $fatal(1, "SIM_TEST_FAIL P6 unretired accepted work");
$display(
"R2_P6_AXI owner=%0d read_bytes=%0d write_bytes=%0d descriptor_bytes=%0d ar_stall_max=%0d aw_stall_max=%0d w_stall_max=%0d r_stall_max=%0d b_stall_max=%0d",
owner, read_bytes, write_bytes, descriptor_bytes, ar_max, aw_max, w_max, r_max, b_max);
end
end
`undef _P6_AXI
end
always #27.126736111 if (s_p6_audio_running) s_p6_audio_reference = !s_p6_audio_reference;
assign s_gpio_17 = s_p6_audio_reference;
// The first integration case is an external codec loopback, across actual pads.
// Independent wire decoding and error injection supplement firmware readback.
assign s_gpio_15 = s_gpio_14;
wire s_p6_card_cmd;
wire [3:0] s_p6_card_data;
wire s_p6_card_irq;
sdio_native_card_model #(
.MemoryInitialization(1'b1),
.BlockCount (1024)
) u_p6_card (
.rst_n_i (s_rst_n),
.sck_i (s_gpio[0]),
.cmd_oe_i(u_dut.u_soc.u_sdio_if.cmd_oe_o),
.cmd_do_i(s_gpio[1]),
.cmd_do_o(s_p6_card_cmd),
.dat_oe_i(u_dut.u_soc.u_sdio_if.dat_oe_o),
.dat_do_i(s_gpio[5:2]),
.dat_do_o(s_p6_card_data),
.irq_o (s_p6_card_irq)
);
assign s_gpio_1 = u_dut.u_soc.u_sdio_if.cmd_oe_o ? 1'bz : s_p6_card_cmd;
assign s_gpio_2 = u_dut.u_soc.u_sdio_if.dat_oe_o[0] ? 1'bz : s_p6_card_data[0];
assign s_gpio_3 = u_dut.u_soc.u_sdio_if.dat_oe_o[1] ? 1'bz : s_p6_card_data[1];
assign s_gpio_4 = u_dut.u_soc.u_sdio_if.dat_oe_o[2] ? 1'bz : s_p6_card_data[2];
assign s_gpio_5 = u_dut.u_soc.u_sdio_if.dat_oe_o[3] ? 1'bz : s_p6_card_data[3];
always @(negedge s_clk) begin
// Reuse the standalone card's explicit data-stimulus API. The wire command
// fixes direction/address/length; host DATA_START only schedules admission.
// This is repository controller integration evidence, not card compliance.
if (u_dut.u_soc.u_sdio.s_data_start) begin
if (u_p6_card.s_last_cmd != 17 && u_p6_card.s_last_cmd != 18 && u_p6_card.s_last_cmd != 25)
$fatal(1, "SIM_TEST_FAIL P6 unexpected SDIO data command");
if (u_dut.u_soc.u_sdio.s_block_size != 512 ||
u_dut.u_soc.u_sdio.s_block_count != (u_p6_card.s_last_cmd == 17 ? 1 : 2) ||
u_dut.u_soc.u_sdio.s_data_direction != (u_p6_card.s_last_cmd == 25) ||
u_dut.u_soc.u_sdio.s_bus_width != (u_p6_card.s_memory_width4 ? 1 : 0))
$fatal(1, "SIM_TEST_FAIL P6 SDIO command/data configuration disagreement");
u_p6_card.set_data_delay(1);
if (u_p6_card.s_last_cmd == 25)
u_p6_card.arm_write(integer'(u_p6_card.s_last_arg * 512), 1024, u_p6_card.s_memory_width4,
1'b0);
else
u_p6_card.arm_read(integer'(u_p6_card.s_last_arg * 512),
u_p6_card.s_last_cmd == 17 ? 512 : 1024, u_p6_card.s_memory_width4, 1'b0);
end
if ($test$plusargs("p6_debug_sdio") && u_dut.u_soc.u_sdio.s_cmd_start) begin
$display("P6_SDIO_DIAGNOSTIC cmd=%0d argument=%h status=%h", u_dut.u_soc.u_sdio.s_cmd_index,
u_dut.u_soc.u_sdio.s_cmd_arg, u_dut.u_soc.u_sdio.s_status);
$fflush();
end
if (u_dut.u_soc.u_sdio.s_cmd_start && u_dut.u_soc.u_sdio.s_cmd_index == 13) begin
if (u_dut.u_soc.u_sdio.s_cmd_arg == 32'hbadc0de0) u_p6_card.inject_next_crc_error();
if (u_dut.u_soc.u_sdio.s_cmd_arg == 32'hbadc0de1) u_p6_card.inject_next_timeout();
end
end
+7
View File
@@ -22,3 +22,10 @@
/clusterip/wdg/rtl/wdg_core.sv /clusterip/wdg/rtl/wdg_core.sv
/clusterip/wdg/rtl/wdg_reg.sv /clusterip/wdg/rtl/wdg_reg.sv
/clusterip/wdg/rtl/apb4_wdg.sv /clusterip/wdg/rtl/apb4_wdg.sv
/clusterip/rng/rtl/rng_core.sv
/clusterip/rng/rtl/rng_reg.sv
/clusterip/rng/rtl/rng_deterministic_source.sv
/clusterip/rng/rtl/apb4_rng.sv
/clusterip/crc/rtl/crc_core.sv
/clusterip/crc/rtl/crc_reg.sv
/clusterip/crc/rtl/apb4_crc.sv
+3
View File
@@ -17,3 +17,6 @@
+incdir+/ip/serial +incdir+/ip/serial
+incdir+/ip/peripheral +incdir+/ip/peripheral
+incdir+/ip/core +incdir+/ip/core
+incdir+/ip/security
+incdir+/clusterip/rng/rtl
+incdir+/clusterip/crc/rtl
+37
View File
@@ -47,3 +47,40 @@
/ip/core/mgmt_debug_wrapper.sv /ip/core/mgmt_debug_wrapper.sv
/ip/core/mgmt_core_wrapper.sv /ip/core/mgmt_core_wrapper.sv
/ip/peripheral/gpio_pad_bridge.sv /ip/peripheral/gpio_pad_bridge.sv
/ip/storage/sdio_if.sv
/ip/storage/sdio_pkg.sv
/ip/storage/sdio_define.svh
/ip/storage/sdio_crc7.sv
/ip/storage/sdio_crc16.sv
/ip/storage/sdio_clock.sv
/ip/storage/sdio_command.sv
/ip/storage/sdio_data.sv
/ip/storage/sdio_dma_descriptor.sv
/ip/serial/i2s_pkg.sv
/ip/serial/i2s_reg.sv
/ip/serial/i2s_clkgen.sv
/ip/serial/i2s_recv.sv
/ip/serial/i2s_send.sv
/ip/serial/i2s_core.sv
/ip/serial/apb4_i2s.sv
/ip/serial/ws2812_if.sv
/ip/serial/ws2812_reg.sv
/ip/serial/ws2812_core.sv
/ip/serial/apb4_ws2812.sv
/ip/security/crypto_pkg.sv
/ip/security/crypto_mem_pkg.sv
/ip/security/crypto_define.svh
/ip/security/crypto_sram_store.sv
/ip/security/crypto_scrubber.sv
/ip/security/crypto_clearable_fifo.sv
/ip/security/crypto_mem_ctrl.sv
/ip/security/crypto_aes_sram_core.sv
/ip/security/crypto_aes_sram_engine.sv
/ip/security/crypto_sha2_sram_engine.sv
/ip/security/crypto_montgomery_sram.sv
/ip/security/crypto_rsa_sram_core.sv
/ip/security/apb4_crypto.sv
/ip/storage/sdio_dma.sv
/ip/storage/sdio_reg.sv
/ip/storage/sdio_core.sv
/ip/storage/apb4_sdio.sv
+1
View File
@@ -1,2 +1,3 @@
/dv/tiny_qspi_nor.sv /dv/tiny_qspi_nor.sv
/tests/rtl/sdio_native_model.sv
/dv/retrosoc_tiny_tb.sv /dv/retrosoc_tiny_tb.sv
+88 -6
View File
@@ -1,10 +1,10 @@
{ {
"schema_version": 2, "schema_version": 2,
"external_fabric": { "external_fabric": {
"phase": "TINY-R2-P5", "phase": "TINY-R2-P6",
"clock_domain": "system", "clock_domain": "system",
"target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller", "target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller",
"target_count": 23, "target_count": 27,
"apb_bridges": "u_soc.u_<peripheral>_bridge; corresponding existing peripheral reset leaf", "apb_bridges": "u_soc.u_<peripheral>_bridge; corresponding existing peripheral reset leaf",
"fabric_reset": "fabric leaf; target reset blocks admission and aborts accepted target work with DECERR", "fabric_reset": "fabric leaf; target reset blocks admission and aborts accepted target work with DECERR",
"hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active", "hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active",
@@ -61,9 +61,24 @@
"sta": { "sta": {
"source_port": "jtag_tck_i_pad", "source_port": "jtag_tck_i_pad",
"object_type": "port", "object_type": "port",
"period_ns": 100.0, "period_ns": 100,
"async_group": "jtag" "async_group": "jtag"
} }
},
{
"name": "audio",
"clock": "s_audio_clk",
"reset": "s_audio_rst_n",
"reset_source": "s_i2s_rst_n",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_audio_rst_sync",
"sta": {
"source_port": "gpio_17_io_pad",
"pin": "u_soc.u_audio_clock_buffer/clk_o",
"period_ns": 54.253472222,
"async_group": "audio"
}
} }
], ],
"reset_distribution": { "reset_distribution": {
@@ -73,8 +88,27 @@
"system_source": "por_rst_n_o && !watchdog_reset_req_i", "system_source": "por_rst_n_o && !watchdog_reset_req_i",
"leaf_instance_pattern": "u_soc.u_reset_tree.gen_leaf[%d].u_leaf_rst_sync", "leaf_instance_pattern": "u_soc.u_reset_tree.gen_leaf[%d].u_leaf_rst_sync",
"leaf_names": [ "leaf_names": [
"fabric", "gpio", "uart0", "uart1", "timer0", "timer1", "i2c0", "i2c1", "fabric",
"xpi", "dma", "sysctrl", "clint", "sram", "archinfo", "pwm", "rtc", "wdg" "gpio",
"uart0",
"timer0",
"timer1",
"i2c0",
"xpi",
"dma",
"sysctrl",
"clint",
"sram",
"archinfo",
"pwm",
"rtc",
"wdg",
"rng",
"crc",
"ws2812",
"crypto",
"i2s",
"sdio"
], ],
"cpu_prerequisite": "all leaf resets released", "cpu_prerequisite": "all leaf resets released",
"hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync", "hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync",
@@ -82,7 +116,7 @@
"dmi": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_dmi_rst_sync", "dmi": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_dmi_rst_sync",
"watchdog_functional_reset": "POR only; APB uses wdg leaf", "watchdog_functional_reset": "POR only; APB uses wdg leaf",
"main_sram_payload_reset": "none", "main_sram_payload_reset": "none",
"crypto": "not integrated; physical erasure contract unchanged", "crypto": "six private macro banks; reset invalidates then scrubs/readbacks; CPU does not wait for READY",
"clock_domain": "system" "clock_domain": "system"
}, },
"crossings": [ "crossings": [
@@ -101,6 +135,54 @@
"primitive": "cdc_sync", "primitive": "cdc_sync",
"path": "rtl/ip/core/mgmt_debug_wrapper.sv", "path": "rtl/ip/core/mgmt_debug_wrapper.sv",
"instance": "u_dmihardreset_cdc_sync" "instance": "u_dmihardreset_cdc_sync"
},
{
"name": "i2s_configuration",
"source": "system",
"destination": "audio",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_i2s_cfg_cdc"
},
{
"name": "i2s_commands",
"source": "system",
"destination": "audio",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_i2s_cmd_cdc"
},
{
"name": "i2s_tx_samples",
"source": "system",
"destination": "audio",
"primitive": "cdc_fifo_warm_flush",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_tx_cdc_fifo"
},
{
"name": "i2s_rx_samples",
"source": "audio",
"destination": "system",
"primitive": "cdc_fifo_warm_flush",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_rx_cdc_fifo"
},
{
"name": "i2s_tx_occupancy",
"source": "audio",
"destination": "system",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_tx_pop_cnt_cdc"
},
{
"name": "i2s_rx_occupancy",
"source": "audio",
"destination": "system",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_rx_push_cnt_cdc"
} }
] ]
} }
+344 -270
View File
@@ -1,6 +1,10 @@
{ {
"schema_version": 1, "schema_version": 1,
"cpu": {"module": "hazard3_cpu_2port", "local_ports": 2, "data_outstanding": 1}, "cpu": {
"module": "hazard3_cpu_2port",
"local_ports": 2,
"data_outstanding": 1
},
"main_sram": { "main_sram": {
"groups": 4, "groups": 4,
"group_bytes": 32768, "group_bytes": 32768,
@@ -11,23 +15,36 @@
"external_attachment": "per-target-P5" "external_attachment": "per-target-P5"
}, },
"fabric": { "fabric": {
"owners": ["cpu_slow", "central_dma", "sdio_reserved"], "owners": [
"active_owners": [0, 1], "cpu_slow",
"central_dma",
"sdio"
],
"active_owners": [
0,
1,
2
],
"cpu_combined_outstanding": 1, "cpu_combined_outstanding": 1,
"dma_direction_outstanding": 1, "dma_direction_outstanding": 1,
"sdio_direction_outstanding": 1, "sdio_direction_outstanding": 1,
"target_combined_outstanding": 1, "target_combined_outstanding": 1,
"sram_targets": [0, 1, 2, 3], "sram_targets": [
0,
1,
2,
3
],
"xpi_target": 4, "xpi_target": 4,
"apb_target_base": 5, "apb_target_base": 5,
"decode_error_target": 21, "decode_error_target": 25,
"protocol_error_target": 22, "protocol_error_target": 26,
"target_count": 23 "target_count": 27
}, },
"gpio_pins": 32, "gpio_pins": 32,
"irq_vector_width": 32, "irq_vector_width": 32,
"external_irq_count": 30, "external_irq_count": 30,
"dma_channels": 4, "dma_channels": 8,
"apb_targets": [ "apb_targets": [
{ {
"name": "gpio", "name": "gpio",
@@ -42,12 +59,6 @@
"APB4_UART0" "APB4_UART0"
] ]
}, },
{
"name": "uart1",
"regions": [
"APB4_UART1"
]
},
{ {
"name": "timer0", "name": "timer0",
"regions": [ "regions": [
@@ -66,12 +77,6 @@
"APB4_I2C0" "APB4_I2C0"
] ]
}, },
{
"name": "i2c1",
"regions": [
"APB4_I2C1"
]
},
{ {
"name": "xpi", "name": "xpi",
"regions": [ "regions": [
@@ -125,6 +130,42 @@
"regions": [ "regions": [
"APB4_WDG" "APB4_WDG"
] ]
},
{
"name": "rng",
"regions": [
"APB4_RNG"
]
},
{
"name": "crc",
"regions": [
"APB4_CRC"
]
},
{
"name": "ws2812",
"regions": [
"APB4_WS2812"
]
},
{
"name": "crypto",
"regions": [
"APB4_CRYPTO"
]
},
{
"name": "i2s",
"regions": [
"APB4_I2S"
]
},
{
"name": "sdio",
"regions": [
"APB4_SDIO0"
]
} }
], ],
"interrupts": [ "interrupts": [
@@ -158,11 +199,21 @@
"core_bit": 7, "core_bit": 7,
"signal": "u_i2c0_if.irq_o" "signal": "u_i2c0_if.irq_o"
}, },
{
"name": "i2s",
"core_bit": 8,
"signal": "u_i2s_if.irq_o"
},
{ {
"name": "xpi", "name": "xpi",
"core_bit": 9, "core_bit": 9,
"signal": "s_xpi_irq" "signal": "s_xpi_irq"
}, },
{
"name": "sdio",
"core_bit": 10,
"signal": "u_sdio_if.irq_o"
},
{ {
"name": "pwm", "name": "pwm",
"core_bit": 11, "core_bit": 11,
@@ -178,36 +229,39 @@
"core_bit": 14, "core_bit": 14,
"signal": "u_wdg_if.irq_o" "signal": "u_wdg_if.irq_o"
}, },
{
"name": "rng",
"core_bit": 16,
"signal": "s_rng_irq"
},
{
"name": "ws2812",
"core_bit": 17,
"signal": "u_ws2812_if.irq_o"
},
{ {
"name": "gpio", "name": "gpio",
"core_bit": 18, "core_bit": 18,
"signal": "u_gpio_if.irq_o" "signal": "u_gpio_if.irq_o"
}, },
{
"name": "i2c1",
"core_bit": 19,
"signal": "u_i2c1_if.irq_o"
},
{ {
"name": "dma", "name": "dma",
"core_bit": 20, "core_bit": 20,
"signal": "s_dma_irq" "signal": "s_dma_irq"
}, },
{ {
"name": "uart1", "name": "crypto",
"core_bit": 26, "core_bit": 23,
"signal": "u_uart1_if.irq_o" "signal": "s_crypto_irq"
} }
], ],
"gpio_alt_functions": [ "gpio_alt_functions": [
{ {
"pin": 0, "pin": 0,
"alt0": { "alt0": {
"inputs": [ "inputs": [],
"u_uart0_if.cts_n_i" "do": "u_sdio_if.sck_o",
], "oe": "1'b1"
"do": "1'b0",
"oe": "1'b0"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -218,9 +272,11 @@
{ {
"pin": 1, "pin": 1,
"alt0": { "alt0": {
"inputs": [], "inputs": [
"do": "u_uart0_if.rts_n_o", "u_sdio_if.cmd_di_i"
"oe": "1'b1" ],
"do": "u_sdio_if.cmd_do_o",
"oe": "u_sdio_if.cmd_oe_o"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -232,10 +288,10 @@
"pin": 2, "pin": 2,
"alt0": { "alt0": {
"inputs": [ "inputs": [
"u_pwm_if.sync_i" "u_sdio_if.dat_di_i[0]"
], ],
"do": "1'b0", "do": "u_sdio_if.dat_do_o[0]",
"oe": "1'b0" "oe": "u_sdio_if.dat_oe_o[0]"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -245,36 +301,243 @@
}, },
{ {
"pin": 3, "pin": 3,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[1]"
],
"do": "u_sdio_if.dat_do_o[1]",
"oe": "u_sdio_if.dat_oe_o[1]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 4,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[2]"
],
"do": "u_sdio_if.dat_do_o[2]",
"oe": "u_sdio_if.dat_oe_o[2]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 5,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[3]"
],
"do": "u_sdio_if.dat_do_o[3]",
"oe": "u_sdio_if.dat_oe_o[3]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 6,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 7,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 8,
"alt0": {
"inputs": [],
"do": "u_uart0_if.tx_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[0]",
"oe": "u_pwm_if.oe_o[0]"
}
},
{
"pin": 9,
"alt0": {
"inputs": [
"u_uart0_if.rx_i"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[1]",
"oe": "u_pwm_if.oe_o[1]"
}
},
{
"pin": 10,
"alt0": {
"inputs": [
"u_i2c0_if.scl_i"
],
"do": "u_i2c0_if.scl_o",
"oe": "u_i2c0_if.scl_oe_o"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[2]",
"oe": "u_pwm_if.oe_o[2]"
}
},
{
"pin": 11,
"alt0": {
"inputs": [
"u_i2c0_if.sda_i"
],
"do": "u_i2c0_if.sda_o",
"oe": "u_i2c0_if.sda_oe_o"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[3]",
"oe": "u_pwm_if.oe_o[3]"
}
},
{
"pin": 12,
"alt0": {
"inputs": [],
"do": "u_i2s_if.sclk_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 13,
"alt0": {
"inputs": [],
"do": "u_i2s_if.lrck_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 14,
"alt0": {
"inputs": [],
"do": "u_i2s_if.dacdat_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 15,
"alt0": {
"inputs": [
"u_i2s_if.adcdat_i"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 16,
"alt0": {
"inputs": [],
"do": "u_i2s_if.mclk_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 17,
"alt0": {
"inputs": [
"s_audio_pad_clk"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 18,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "u_pwm_if.pwm_o[0]", "do": "u_pwm_if.pwm_o[0]",
"oe": "u_pwm_if.oe_o[0]" "oe": "u_pwm_if.oe_o[0]"
}, },
"alt1": { "alt1": {
"inputs": [ "inputs": [],
"u_i2c1_if.scl_i" "do": "1'b0",
], "oe": "1'b0"
"do": "u_i2c1_if.scl_o",
"oe": "u_i2c1_if.scl_oe_o"
} }
}, },
{ {
"pin": 4, "pin": 19,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "u_pwm_if.pwm_o[1]", "do": "u_pwm_if.pwm_o[1]",
"oe": "u_pwm_if.oe_o[1]" "oe": "u_pwm_if.oe_o[1]"
}, },
"alt1": { "alt1": {
"inputs": [ "inputs": [],
"u_i2c1_if.sda_i" "do": "1'b0",
], "oe": "1'b0"
"do": "u_i2c1_if.sda_o",
"oe": "u_i2c1_if.sda_oe_o"
} }
}, },
{ {
"pin": 5, "pin": 20,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "u_pwm_if.pwm_o[2]", "do": "u_pwm_if.pwm_o[2]",
@@ -287,7 +550,7 @@
} }
}, },
{ {
"pin": 6, "pin": 21,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "u_pwm_if.pwm_o[3]", "do": "u_pwm_if.pwm_o[3]",
@@ -300,37 +563,7 @@
} }
}, },
{ {
"pin": 7, "pin": 22,
"alt0": {
"inputs": [
"u_i2c0_if.scl_i"
],
"do": "u_i2c0_if.scl_o",
"oe": "u_i2c0_if.scl_oe_o"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 8,
"alt0": {
"inputs": [
"u_i2c0_if.sda_i"
],
"do": "u_i2c0_if.sda_o",
"oe": "u_i2c0_if.sda_oe_o"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 9,
"alt0": { "alt0": {
"inputs": [ "inputs": [
"u_pwm_if.fault_i" "u_pwm_if.fault_i"
@@ -344,179 +577,12 @@
"oe": "1'b0" "oe": "1'b0"
} }
}, },
{
"pin": 10,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 11,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 12,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 13,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 14,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 15,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 16,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 17,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 18,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 19,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 20,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 21,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 22,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{ {
"pin": 23, "pin": 23,
"alt0": { "alt0": {
"inputs": [], "inputs": [
"u_pwm_if.sync_i"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
}, },
@@ -529,7 +595,9 @@
{ {
"pin": 24, "pin": 24,
"alt0": { "alt0": {
"inputs": [], "inputs": [
"u_pwm_if.capture_i[0]"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
}, },
@@ -542,7 +610,9 @@
{ {
"pin": 25, "pin": 25,
"alt0": { "alt0": {
"inputs": [], "inputs": [
"u_pwm_if.capture_i[1]"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
}, },
@@ -556,8 +626,8 @@
"pin": 26, "pin": 26,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "u_ws2812_if.dat_o",
"oe": "1'b0" "oe": "1'b1"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -595,8 +665,8 @@
"pin": 29, "pin": 29,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "xpi.nss_o[1]",
"oe": "1'b0" "oe": "1'b1"
}, },
"alt1": { "alt1": {
"inputs": [], "inputs": [],
@@ -608,13 +678,11 @@
"pin": 30, "pin": 30,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "xpi.nss_o[2]",
"oe": "1'b0" "oe": "1'b1"
}, },
"alt1": { "alt1": {
"inputs": [ "inputs": [],
"u_pwm_if.capture_i[0]"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
} }
@@ -623,16 +691,22 @@
"pin": 31, "pin": 31,
"alt0": { "alt0": {
"inputs": [], "inputs": [],
"do": "1'b0", "do": "xpi.nss_o[3]",
"oe": "1'b0" "oe": "1'b1"
}, },
"alt1": { "alt1": {
"inputs": [ "inputs": [],
"u_pwm_if.capture_i[1]"
],
"do": "1'b0", "do": "1'b0",
"oe": "1'b0" "oe": "1'b0"
} }
} }
] ],
"dma_request_mask": "0x31ff",
"private_crypto": {
"banks": 6,
"bank_bytes": 4096,
"macro_instance": "u_soc.u_crypto.u_storage.gen_bank[{bank}].gen_macro.u_sram.u_mem",
"clock_domain": "system",
"logical_domain": "PCLK (SAFE24 alias)"
}
} }
+1 -1
View File
@@ -56,7 +56,7 @@ ifneq ($(ISA),RV32IM)
$(error SOC=TINY firmware uses ISA=RV32IM) $(error SOC=TINY firmware uses ISA=RV32IM)
endif endif
ifneq ($(WS2812_P3_ACCEPTANCE),NO) ifneq ($(WS2812_P3_ACCEPTANCE),NO)
$(error SOC=TINY does not yet integrate WS2812) $(error SOC=TINY uses its integrated P6 acceptance, not the Mini WS2812 P3 image)
endif endif
ifneq ($(filter YES,$(APU_ENABLE_P7) $(NPU_P5_ACCEPTANCE) $(NPU_P6_ACCEPTANCE)),) ifneq ($(filter YES,$(APU_ENABLE_P7) $(NPU_P5_ACCEPTANCE) $(NPU_P6_ACCEPTANCE)),)
$(error SOC=TINY does not include multimedia accelerators) $(error SOC=TINY does not include multimedia accelerators)
+64 -64
View File
@@ -205,38 +205,38 @@
"jtag_tdi_i_pad": "s_jtag_tdi", "jtag_tdi_i_pad": "s_jtag_tdi",
"jtag_trst_n_i_pad": "s_jtag_trst_n", "jtag_trst_n_i_pad": "s_jtag_trst_n",
"jtag_tdo_o_pad": "s_jtag_tdo", "jtag_tdo_o_pad": "s_jtag_tdo",
"gpio_0_io_pad": "s_gpio[0]", "gpio_0_io_pad": "s_gpio_0",
"gpio_1_io_pad": "s_gpio[1]", "gpio_1_io_pad": "s_gpio_1",
"gpio_2_io_pad": "s_gpio[2]", "gpio_2_io_pad": "s_gpio_2",
"gpio_3_io_pad": "s_gpio[3]", "gpio_3_io_pad": "s_gpio_3",
"gpio_4_io_pad": "s_gpio[4]", "gpio_4_io_pad": "s_gpio_4",
"gpio_5_io_pad": "s_gpio[5]", "gpio_5_io_pad": "s_gpio_5",
"gpio_6_io_pad": "s_gpio[6]", "gpio_6_io_pad": "s_gpio_6",
"gpio_7_io_pad": "s_gpio[7]", "gpio_7_io_pad": "s_gpio_7",
"gpio_8_io_pad": "s_gpio[8]", "gpio_8_io_pad": "s_gpio_8",
"gpio_9_io_pad": "s_gpio[9]", "gpio_9_io_pad": "s_gpio_9",
"gpio_10_io_pad": "s_gpio[10]", "gpio_10_io_pad": "s_gpio_10",
"gpio_11_io_pad": "s_gpio[11]", "gpio_11_io_pad": "s_gpio_11",
"gpio_12_io_pad": "s_gpio[12]", "gpio_12_io_pad": "s_gpio_12",
"gpio_13_io_pad": "s_gpio[13]", "gpio_13_io_pad": "s_gpio_13",
"gpio_14_io_pad": "s_gpio[14]", "gpio_14_io_pad": "s_gpio_14",
"gpio_15_io_pad": "s_gpio[15]", "gpio_15_io_pad": "s_gpio_15",
"gpio_16_io_pad": "s_gpio[16]", "gpio_16_io_pad": "s_gpio_16",
"gpio_17_io_pad": "s_gpio[17]", "gpio_17_io_pad": "s_gpio_17",
"gpio_18_io_pad": "s_gpio[18]", "gpio_18_io_pad": "s_gpio_18",
"gpio_19_io_pad": "s_gpio[19]", "gpio_19_io_pad": "s_gpio_19",
"gpio_20_io_pad": "s_gpio[20]", "gpio_20_io_pad": "s_gpio_20",
"gpio_21_io_pad": "s_gpio[21]", "gpio_21_io_pad": "s_gpio_21",
"gpio_22_io_pad": "s_gpio[22]", "gpio_22_io_pad": "s_gpio_22",
"gpio_23_io_pad": "s_gpio[23]", "gpio_23_io_pad": "s_gpio_23",
"gpio_24_io_pad": "s_gpio[24]", "gpio_24_io_pad": "s_gpio_24",
"gpio_25_io_pad": "s_gpio[25]", "gpio_25_io_pad": "s_gpio_25",
"gpio_26_io_pad": "s_gpio[26]", "gpio_26_io_pad": "s_gpio_26",
"gpio_27_io_pad": "s_gpio[27]", "gpio_27_io_pad": "s_gpio_27",
"gpio_28_io_pad": "s_gpio[28]", "gpio_28_io_pad": "s_gpio_28",
"gpio_29_io_pad": "s_gpio[29]", "gpio_29_io_pad": "s_gpio_29",
"gpio_30_io_pad": "s_gpio[30]", "gpio_30_io_pad": "s_gpio_30",
"gpio_31_io_pad": "s_gpio[31]", "gpio_31_io_pad": "s_gpio_31",
"uart0_tx_o_pad": "s_uart0_tx", "uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx", "uart0_rx_i_pad": "s_uart0_rx",
"uart1_tx_o_pad": "s_uart1_tx", "uart1_tx_o_pad": "s_uart1_tx",
@@ -261,38 +261,38 @@
"jtag_tdi_i_pad": "s_jtag_tdi", "jtag_tdi_i_pad": "s_jtag_tdi",
"jtag_trst_n_i_pad": "s_jtag_trst_n", "jtag_trst_n_i_pad": "s_jtag_trst_n",
"jtag_tdo_o_pad": "s_jtag_tdo", "jtag_tdo_o_pad": "s_jtag_tdo",
"gpio_0_io_pad": "s_gpio[0]", "gpio_0_io_pad": "s_gpio_0",
"gpio_1_io_pad": "s_gpio[1]", "gpio_1_io_pad": "s_gpio_1",
"gpio_2_io_pad": "s_gpio[2]", "gpio_2_io_pad": "s_gpio_2",
"gpio_3_io_pad": "s_gpio[3]", "gpio_3_io_pad": "s_gpio_3",
"gpio_4_io_pad": "s_gpio[4]", "gpio_4_io_pad": "s_gpio_4",
"gpio_5_io_pad": "s_gpio[5]", "gpio_5_io_pad": "s_gpio_5",
"gpio_6_io_pad": "s_gpio[6]", "gpio_6_io_pad": "s_gpio_6",
"gpio_7_io_pad": "s_gpio[7]", "gpio_7_io_pad": "s_gpio_7",
"gpio_8_io_pad": "s_gpio[8]", "gpio_8_io_pad": "s_gpio_8",
"gpio_9_io_pad": "s_gpio[9]", "gpio_9_io_pad": "s_gpio_9",
"gpio_10_io_pad": "s_gpio[10]", "gpio_10_io_pad": "s_gpio_10",
"gpio_11_io_pad": "s_gpio[11]", "gpio_11_io_pad": "s_gpio_11",
"gpio_12_io_pad": "s_gpio[12]", "gpio_12_io_pad": "s_gpio_12",
"gpio_13_io_pad": "s_gpio[13]", "gpio_13_io_pad": "s_gpio_13",
"gpio_14_io_pad": "s_gpio[14]", "gpio_14_io_pad": "s_gpio_14",
"gpio_15_io_pad": "s_gpio[15]", "gpio_15_io_pad": "s_gpio_15",
"gpio_16_io_pad": "s_gpio[16]", "gpio_16_io_pad": "s_gpio_16",
"gpio_17_io_pad": "s_gpio[17]", "gpio_17_io_pad": "s_gpio_17",
"gpio_18_io_pad": "s_gpio[18]", "gpio_18_io_pad": "s_gpio_18",
"gpio_19_io_pad": "s_gpio[19]", "gpio_19_io_pad": "s_gpio_19",
"gpio_20_io_pad": "s_gpio[20]", "gpio_20_io_pad": "s_gpio_20",
"gpio_21_io_pad": "s_gpio[21]", "gpio_21_io_pad": "s_gpio_21",
"gpio_22_io_pad": "s_gpio[22]", "gpio_22_io_pad": "s_gpio_22",
"gpio_23_io_pad": "s_gpio[23]", "gpio_23_io_pad": "s_gpio_23",
"gpio_24_io_pad": "s_gpio[24]", "gpio_24_io_pad": "s_gpio_24",
"gpio_25_io_pad": "s_gpio[25]", "gpio_25_io_pad": "s_gpio_25",
"gpio_26_io_pad": "s_gpio[26]", "gpio_26_io_pad": "s_gpio_26",
"gpio_27_io_pad": "s_gpio[27]", "gpio_27_io_pad": "s_gpio_27",
"gpio_28_io_pad": "s_gpio[28]", "gpio_28_io_pad": "s_gpio_28",
"gpio_29_io_pad": "s_gpio[29]", "gpio_29_io_pad": "s_gpio_29",
"gpio_30_io_pad": "s_gpio[30]", "gpio_30_io_pad": "s_gpio_30",
"gpio_31_io_pad": "s_gpio[31]", "gpio_31_io_pad": "s_gpio_31",
"uart0_tx_o_pad": "s_uart0_tx", "uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx", "uart0_rx_i_pad": "s_uart0_rx",
"uart1_tx_o_pad": "s_uart1_tx", "uart1_tx_o_pad": "s_uart1_tx",
+112 -97
View File
@@ -1,6 +1,7 @@
// Copyright (c) 2026 Yuchi Miao // Copyright (c) 2026 Yuchi Miao
// SPDX-License-Identifier: MulanPSL-2.0 // SPDX-License-Identifier: MulanPSL-2.0
`include "mmap_define.svh" `include "mmap_define.svh"
`include "tiny_topology.svh"
module retrosoc_tiny ( module retrosoc_tiny (
input logic clk_i, input logic clk_i,
@@ -21,10 +22,10 @@ module retrosoc_tiny (
output logic [7:0] test_code_o output logic [7:0] test_code_o
); );
logic s_por_rst_n, s_rst_n; logic s_por_rst_n, s_rst_n;
logic [16:0] s_leaf_rst_n; logic [`RETROSOC_TINY__LEAF_COUNT-1:0] s_leaf_rst_n;
logic s_fabric_rst_n, s_cpu_ready_rst_n; logic s_fabric_rst_n, s_cpu_ready_rst_n;
logic s_master_rst_n [ 3]; logic s_master_rst_n [ 3];
logic s_target_rst_n [23]; logic s_target_rst_n [`RETROSOC_TINY__TARGET_COUNT];
logic s_debug_halted; logic s_debug_halted;
logic s_core_rst_n, s_cpu_bus_idle, s_cpu_quiesce; logic s_core_rst_n, s_cpu_bus_idle, s_cpu_quiesce;
logic [31:0] s_irq; logic [31:0] s_irq;
@@ -42,10 +43,14 @@ module retrosoc_tiny (
logic [63:0] s_cpu_wait, s_dma_wait; logic [63:0] s_cpu_wait, s_dma_wait;
logic s_timer0_irq, s_timer1_irq, s_xpi_irq, s_dma_irq; logic s_timer0_irq, s_timer1_irq, s_xpi_irq, s_dma_irq;
logic s_dma_done; logic s_dma_done;
logic [7:0] s_dma_stall; logic [5:0] s_dma_stall;
logic [4:0] s_tick_count_d, s_tick_count_q; logic [4:0] s_tick_count_d, s_tick_count_q;
logic s_tick; logic s_tick;
logic s_unused_uart1_tx_stall, s_unused_uart1_rx_stall; logic s_audio_pad_clk, s_audio_clk, s_audio_rst_n;
logic s_rng_en, s_rng_ready, s_rng_valid, s_rng_qualified, s_rng_fault, s_rng_irq;
logic [31:0] s_rng_data;
logic s_crypto_irq, s_crypto_input_ready, s_crypto_output_ready;
logic s_i2s_tx_stall, s_i2s_rx_stall;
ahbl_if u_cpu_ahbl_if[2] ( ahbl_if u_cpu_ahbl_if[2] (
.hclk (clk_i), .hclk (clk_i),
.hresetn(s_core_rst_n) .hresetn(s_core_rst_n)
@@ -62,14 +67,15 @@ module retrosoc_tiny (
); );
`include "tiny_apb_interfaces.svh" `include "tiny_apb_interfaces.svh"
uart_if u_uart0_if (); uart_if u_uart0_if ();
uart_if u_uart1_if ();
i2c_if u_i2c0_if (); i2c_if u_i2c0_if ();
i2c_if u_i2c1_if ();
pwm_if u_pwm_if (); pwm_if u_pwm_if ();
clint_if u_clint_if (); clint_if u_clint_if ();
gpio_if u_gpio_if (); gpio_if u_gpio_if ();
user_gpio_if u_user_gpio_if (); user_gpio_if u_user_gpio_if ();
dma_req_if u_dma_req_if (); dma_req_if u_dma_req_if ();
i2s_if u_i2s_if ();
ws2812_if u_ws2812_if ();
sdio_if u_sdio_if ();
rtc_if u_rtc_if ( rtc_if u_rtc_if (
.rtc_clk_i (clk_i), .rtc_clk_i (clk_i),
.rtc_rst_n_i(s_rtc_rst_n) .rtc_rst_n_i(s_rtc_rst_n)
@@ -94,7 +100,7 @@ uart_if u_uart0_if ();
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(1)
) u_targets_axi4_if[23] ( ) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(s_target_rst_n) .aresetn(s_target_rst_n)
); );
@@ -128,46 +134,14 @@ uart_if u_uart0_if ();
.aclk (clk_i), .aclk (clk_i),
.aresetn(s_dma_rst_n) .aresetn(s_dma_rst_n)
); );
assign s_fabric_rst_n = s_leaf_rst_n[0]; assign s_fabric_rst_n = s_leaf_rst_n[0];
assign s_master_rst_n = '{s_cpu_ready_rst_n, s_dma_rst_n, s_fabric_rst_n}; assign s_master_rst_n = '{s_cpu_ready_rst_n, s_dma_rst_n, s_sdio_rst_n};
assign s_target_rst_n[4] = s_xpi_rst_n; assign s_target_rst_n[4] = s_xpi_rst_n;
assign s_target_rst_n[21] = s_fabric_rst_n; assign s_target_rst_n[`RETROSOC_TINY__DECODE_ERROR] = s_fabric_rst_n;
assign s_target_rst_n[22] = s_fabric_rst_n; assign s_target_rst_n[`RETROSOC_TINY__PROTOCOL_ERROR] = s_fabric_rst_n;
// Owner 2 is a reserved native read/write ingress, not an integrated SDIO. tiny_reset_tree #(
// P6 supplies its real producer; no capability or interrupt is enabled here. .LeafCount(`RETROSOC_TINY__LEAF_COUNT)
assign u_masters_axi4_if[2].awid = '0; ) u_reset_tree (
assign u_masters_axi4_if[2].awaddr = '0;
assign u_masters_axi4_if[2].awlen = '0;
assign u_masters_axi4_if[2].awsize = '0;
assign u_masters_axi4_if[2].awburst = '0;
assign u_masters_axi4_if[2].awlock = 1'b0;
assign u_masters_axi4_if[2].awcache = '0;
assign u_masters_axi4_if[2].awprot = '0;
assign u_masters_axi4_if[2].awqos = '0;
assign u_masters_axi4_if[2].awregion = '0;
assign u_masters_axi4_if[2].awuser = '0;
assign u_masters_axi4_if[2].awvalid = 1'b0;
assign u_masters_axi4_if[2].arid = '0;
assign u_masters_axi4_if[2].araddr = '0;
assign u_masters_axi4_if[2].arlen = '0;
assign u_masters_axi4_if[2].arsize = '0;
assign u_masters_axi4_if[2].arburst = '0;
assign u_masters_axi4_if[2].arlock = 1'b0;
assign u_masters_axi4_if[2].arcache = '0;
assign u_masters_axi4_if[2].arprot = '0;
assign u_masters_axi4_if[2].arqos = '0;
assign u_masters_axi4_if[2].arregion = '0;
assign u_masters_axi4_if[2].aruser = '0;
assign u_masters_axi4_if[2].arvalid = 1'b0;
assign u_masters_axi4_if[2].wdata = '0;
assign u_masters_axi4_if[2].wstrb = '0;
assign u_masters_axi4_if[2].wlast = 1'b0;
assign u_masters_axi4_if[2].wuser = '0;
assign u_masters_axi4_if[2].wvalid = 1'b0;
assign u_masters_axi4_if[2].rready = 1'b0;
assign u_masters_axi4_if[2].bready = 1'b0;
tiny_reset_tree u_reset_tree (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.watchdog_reset_req_i(u_wdg_if.reset_req_o), .watchdog_reset_req_i(u_wdg_if.reset_req_o),
@@ -267,14 +241,14 @@ axi4_error_slave #(
) u_decode_error ( ) u_decode_error (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(s_fabric_rst_n), .rst_n_i(s_fabric_rst_n),
.axi4 (u_targets_axi4_if[21]) .axi4 (u_targets_axi4_if[`RETROSOC_TINY__DECODE_ERROR])
); );
axi4_error_slave #( axi4_error_slave #(
.Response(2'b10) .Response(2'b10)
) u_protocol_error ( ) u_protocol_error (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(s_fabric_rst_n), .rst_n_i(s_fabric_rst_n),
.axi4 (u_targets_axi4_if[22]) .axi4 (u_targets_axi4_if[`RETROSOC_TINY__PROTOCOL_ERROR])
); );
tiny_fault_collector u_fault_collector ( tiny_fault_collector u_fault_collector (
.local_valid_i (s_local_fault_valid), .local_valid_i (s_local_fault_valid),
@@ -331,11 +305,10 @@ axi4_error_slave #(
); );
assign u_user_gpio_if.do_o = '0; assign u_user_gpio_if.do_o = '0;
assign u_user_gpio_if.oe_o = '0; assign u_user_gpio_if.oe_o = '0;
assign u_uart0_if.rx_i = uart0_rx_i;
assign uart0_tx_o = u_uart0_if.tx_o; assign uart0_tx_o = u_uart0_if.tx_o;
assign u_uart1_if.rx_i = uart1_rx_i; // Legacy ASIC observation pads are retained, not a Gen1 package binding.
assign u_uart1_if.cts_n_i = 1'b0; assign uart1_tx_o = 1'b1;
assign uart1_tx_o = u_uart1_if.tx_o; assign u_uart0_if.cts_n_i = 1'b0;
assign u_wdg_if.debug_halted_i = s_debug_halted; assign u_wdg_if.debug_halted_i = s_debug_halted;
`include "tiny_gpio.svh" `include "tiny_gpio.svh"
`include "tiny_irq.svh" `include "tiny_irq.svh"
@@ -362,22 +335,6 @@ apb4_uart u_uart0 (
.apb4 (u_timer0_apb4_if), .apb4 (u_timer0_apb4_if),
.irq_o (s_timer0_irq) .irq_o (s_timer0_irq)
); );
apb4_uart u_uart1 (
.clk_i (clk_i),
.rst_n_i (s_uart1_rst_n),
.apb4 (u_uart1_apb4_if),
.uart (u_uart1_if),
.dma_tx_stall_o(s_unused_uart1_tx_stall),
.dma_rx_stall_o(s_unused_uart1_rx_stall)
);
apb4_i2c u_i2c1 (
.clk_i (clk_i),
.rst_n_i (s_i2c1_rst_n),
.apb4 (u_i2c1_apb4_if),
.i2c (u_i2c1_if),
.dma_tx_stall_o(s_dma_stall[6]),
.dma_rx_stall_o(s_dma_stall[7])
);
apb4_timer u_timer1 ( apb4_timer u_timer1 (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (s_timer1_rst_n), .rst_n_i (s_timer1_rst_n),
@@ -413,11 +370,11 @@ apb4_uart u_uart0 (
.wdg (u_wdg_if) .wdg (u_wdg_if)
); );
apb4_dma #( apb4_dma #(
.NumChannels (4), .NumChannels (8),
.MaxBurstBeats(16), .MaxBurstBeats(16),
.FifoDepth (32), .FifoDepth (32),
.RequestMask (16'h07f9), .RequestMask (16'h31ff),
.EnableStreams(1'b0) .EnableStreams(1'b1)
) u_dma ( ) u_dma (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (s_dma_rst_n), .rst_n_i (s_dma_rst_n),
@@ -432,26 +389,18 @@ apb4_uart u_uart0 (
.crypto_in_axis (u_crypto_tx_axis_if), .crypto_in_axis (u_crypto_tx_axis_if),
.crypto_out_axis(u_crypto_rx_axis_if) .crypto_out_axis(u_crypto_rx_axis_if)
); );
assign u_dma_req_if.i2s_tx_proc = 1'b0; assign u_dma_req_if.i2s_tx_proc = !s_i2s_tx_stall;
assign u_dma_req_if.i2s_rx_proc = 1'b0; assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall;
assign u_dma_req_if.crypto_in_proc = 1'b0; assign u_dma_req_if.crypto_in_proc = s_crypto_input_ready;
assign u_dma_req_if.crypto_out_proc = 1'b0; assign u_dma_req_if.crypto_out_proc = s_crypto_output_ready;
assign u_dma_req_if.qspi_tx_proc = !s_dma_stall[0]; assign u_dma_req_if.qspi_tx_proc = !s_dma_stall[0];
assign u_dma_req_if.qspi_rx_proc = !s_dma_stall[1]; assign u_dma_req_if.qspi_rx_proc = !s_dma_stall[1];
assign u_dma_req_if.uart_tx_proc = !s_dma_stall[2]; assign u_dma_req_if.uart_tx_proc = !s_dma_stall[2];
assign u_dma_req_if.uart_rx_proc = !s_dma_stall[3]; assign u_dma_req_if.uart_rx_proc = !s_dma_stall[3];
assign u_dma_req_if.i2c0_tx_proc = !s_dma_stall[4]; assign u_dma_req_if.i2c0_tx_proc = !s_dma_stall[4];
assign u_dma_req_if.i2c0_rx_proc = !s_dma_stall[5]; assign u_dma_req_if.i2c0_rx_proc = !s_dma_stall[5];
assign u_dma_req_if.i2c1_tx_proc = !s_dma_stall[6]; assign u_dma_req_if.i2c1_tx_proc = 1'b0;
assign u_dma_req_if.i2c1_rx_proc = !s_dma_stall[7]; assign u_dma_req_if.i2c1_rx_proc = 1'b0;
assign u_i2s_rx_axis_if.tvalid = '0;
assign u_i2s_rx_axis_if.tdata = '0;
assign u_i2s_rx_axis_if.tstrb = '0;
assign u_i2s_rx_axis_if.tkeep = '0;
assign u_i2s_rx_axis_if.tlast = '0;
assign u_i2s_rx_axis_if.tid = '0;
assign u_i2s_rx_axis_if.tdest = '0;
assign u_i2s_rx_axis_if.tuser = '0;
assign u_dvp_rx_axis_if.tvalid = '0; assign u_dvp_rx_axis_if.tvalid = '0;
assign u_dvp_rx_axis_if.tdata = '0; assign u_dvp_rx_axis_if.tdata = '0;
assign u_dvp_rx_axis_if.tstrb = '0; assign u_dvp_rx_axis_if.tstrb = '0;
@@ -460,14 +409,80 @@ apb4_uart u_uart0 (
assign u_dvp_rx_axis_if.tid = '0; assign u_dvp_rx_axis_if.tid = '0;
assign u_dvp_rx_axis_if.tdest = '0; assign u_dvp_rx_axis_if.tdest = '0;
assign u_dvp_rx_axis_if.tuser = '0; assign u_dvp_rx_axis_if.tuser = '0;
assign u_crypto_rx_axis_if.tvalid = '0;
assign u_crypto_rx_axis_if.tdata = '0; // Host logic remains SAFE24. Audio reset waits for its own clock, never CPU boot.
assign u_crypto_rx_axis_if.tstrb = '0; tc_clk_buf u_audio_clock_buffer (
assign u_crypto_rx_axis_if.tkeep = '0; .clk_i(s_audio_pad_clk),
assign u_crypto_rx_axis_if.tlast = '0; .clk_o(s_audio_clk)
assign u_crypto_rx_axis_if.tid = '0; );
assign u_crypto_rx_axis_if.tdest = '0; rst_sync #(
assign u_crypto_rx_axis_if.tuser = '0; .STAGE(5)
assign u_i2s_tx_axis_if.tready = 1'b0; ) u_audio_rst_sync (
assign u_crypto_tx_axis_if.tready = 1'b0; .clk_i (s_audio_clk),
.rst_n_i(s_i2s_rst_n),
.rst_n_o(s_audio_rst_n)
);
apb4_i2s u_i2s (
.clk_i (clk_i),
.rst_n_i (s_i2s_rst_n),
.clk_aud_i (s_audio_clk),
.rst_aud_n_i (s_audio_rst_n),
.dma_tx_stall_o (s_i2s_tx_stall),
.dma_rx_stall_o (s_i2s_rx_stall),
.tx_underrun_evt_o(), // Sticky status and IRQ retain these events.
.rx_overrun_evt_o (),
.rx_flush_busy_o (), // No APU consumer in Tiny.
.apb4 (u_i2s_apb4_if),
.tx_axis (u_i2s_tx_axis_if),
.rx_axis (u_i2s_rx_axis_if),
.i2s (u_i2s_if)
);
apb4_sdio #(
.InputClockHz(24_000_000)
) u_sdio (
.clk_i (clk_i),
.rst_n_i (s_sdio_rst_n),
.apb4 (u_sdio_apb4_if),
.dma_axi4(u_masters_axi4_if[2]),
.sdio (u_sdio_if)
);
apb4_ws2812 u_ws2812 (
.clk_i (clk_i),
.rst_n_i(s_ws2812_rst_n),
.apb4 (u_ws2812_apb4_if),
.ws2812 (u_ws2812_if)
);
apb4_crypto u_crypto (
.clk_i (clk_i),
.rst_n_i (s_crypto_rst_n),
.dma_input_proc_o (s_crypto_input_ready),
.dma_output_proc_o(s_crypto_output_ready),
.irq_o (s_crypto_irq),
.apb4 (u_crypto_apb4_if),
.crypto_in_axis (u_crypto_tx_axis_if),
.crypto_out_axis (u_crypto_rx_axis_if)
);
apb4_crc u_crc (.apb4(u_crc_apb4_if));
rng_deterministic_source u_rng_source (
.clk_i (clk_i),
.rst_n_i (s_rng_rst_n),
.enable_i (s_rng_en),
.ready_i (s_rng_ready),
.valid_o (s_rng_valid),
.data_o (s_rng_data),
.qualified_o(s_rng_qualified),
.fault_o (s_rng_fault)
);
apb4_rng #(
.FIFO_DEPTH(8)
) u_rng (
.entropy_enable_o (s_rng_en),
.entropy_ready_o (s_rng_ready),
.entropy_valid_i (s_rng_valid),
.entropy_data_i (s_rng_data),
.entropy_qualified_i(s_rng_qualified),
.entropy_fault_i (s_rng_fault),
.irq_o (s_rng_irq),
.apb4 (u_rng_apb4_if)
);
endmodule endmodule
+1 -1
View File
@@ -57,7 +57,7 @@ module tiny_axi42apb4 (
assign apb4.paddr = s_addr_q; assign apb4.paddr = s_addr_q;
assign apb4.pwrite = s_write_q; assign apb4.pwrite = s_write_q;
assign apb4.pwdata = s_wdata_q; assign apb4.pwdata = s_wdata_q;
assign apb4.pstrb = s_wstrb_q; assign apb4.pstrb = s_write_q ? s_wstrb_q : 4'd0;
assign apb4.pprot = 3'd0; assign apb4.pprot = 3'd0;
assign apb4.psel = s_access; assign apb4.psel = s_access;
assign apb4.penable = s_enable; assign apb4.penable = s_enable;
+40 -33
View File
@@ -1,47 +1,50 @@
// Copyright (c) 2026 Yuchi Miao // Copyright (c) 2026 Yuchi Miao
// SPDX-License-Identifier: MulanPSL-2.0 // SPDX-License-Identifier: MulanPSL-2.0
`include "mmap_define.svh" `include "mmap_define.svh"
`include "tiny_topology.svh"
// Three reserved owner slots, 23 independently owned targets. CPU admits one // Three active owners and topology-selected independent targets. CPU admits one
// combined operation; DMA and SDIO each admit one read and one write. IDs are // combined operation; DMA and SDIO each admit one read and one write. IDs are
// unchanged: saved target ownership, not ID expansion, routes all responses. // unchanged: saved target ownership, not ID expansion, routes all responses.
module tiny_axi4_fabric ( module tiny_axi4_fabric (
input logic clk_i, input logic clk_i,
input logic rst_n_i, input logic rst_n_i,
input logic perf_enable_i, input logic perf_enable_i,
input logic perf_clear_i, input logic perf_clear_i,
input logic target_rst_n_i [23], input logic target_rst_n_i[`RETROSOC_TINY__TARGET_COUNT],
axi4_if.slave masters [ 3], axi4_if.slave masters[3],
axi4_if.master targets [23], axi4_if.master targets[`RETROSOC_TINY__TARGET_COUNT],
output logic [ 5:0] fault_valid_o, output logic [5:0] fault_valid_o,
output logic [ 5:0][31:0] fault_addr_o, output logic [5:0][31:0] fault_addr_o,
output logic [ 5:0][ 1:0] fault_resp_o, output logic [5:0][1:0] fault_resp_o,
output logic [ 5:0] fault_instruction_o, output logic [5:0] fault_instruction_o,
output logic [ 2:0] owner_idle_o, output logic [2:0] owner_idle_o,
output logic [22:0] target_idle_o, output logic [`RETROSOC_TINY__TARGET_COUNT-1:0] target_idle_o,
output logic [63:0] cpu_wait_o, output logic [63:0] cpu_wait_o,
output logic [63:0] dma_wait_o output logic [63:0] dma_wait_o
); );
localparam logic [4:0] DecodeErrorTarget = 5'(`RETROSOC_TINY__DECODE_ERROR);
localparam logic [4:0] ProtocolErrorTarget = 5'(`RETROSOC_TINY__PROTOCOL_ERROR);
logic [5:0] s_busy_d, s_busy_q, s_admit, s_done, s_eligible; logic [5:0] s_busy_d, s_busy_q, s_admit, s_done, s_eligible;
logic s_cpu_prefer_write_d, s_cpu_prefer_write_q; logic s_cpu_prefer_write_d, s_cpu_prefer_write_q;
logic [2:0][4:0] s_read_target, s_write_target; logic [2:0][4:0] s_read_target, s_write_target;
logic [22:0][2:0] s_arready, s_awready, s_wready, s_rvalid, s_bvalid; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][2:0] s_arready, s_awready, s_wready, s_rvalid, s_bvalid;
logic [22:0][ 2:0][36:0] s_rdata; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][36:0] s_rdata;
logic [22:0][ 2:0][ 3:0] s_bdata; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 3:0] s_bdata;
logic [ 2:0][36:0] s_read_return; logic [ 2:0][36:0] s_read_return;
logic [ 2:0][ 3:0] s_write_return; logic [ 2:0][ 3:0] s_write_return;
logic [2:0] s_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid; logic [2:0] s_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid;
logic [2:0] s_arvalid, s_awvalid; logic [2:0] s_arvalid, s_awvalid;
logic [1:0][63:0] s_wait_d, s_wait_q; logic [1:0][63:0] s_wait_d, s_wait_q;
logic [22:0] s_fault_valid, s_fault_write, s_fault_instruction; logic [`RETROSOC_TINY__TARGET_COUNT-1:0] s_fault_valid, s_fault_write, s_fault_instruction;
logic [22:0][31:0] s_fault_addr; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][31:0] s_fault_addr;
logic [22:0][1:0] s_fault_master, s_fault_resp; logic [`RETROSOC_TINY__TARGET_COUNT-1:0][1:0] s_fault_master, s_fault_resp;
function automatic logic [4:0] decode_target(input logic [31:0] addr_i); function automatic logic [4:0] decode_target(input logic [31:0] addr_i);
if (`SOC_ADDR_IS_SRAM(addr_i)) return {3'd0, addr_i[16:15]}; if (`SOC_ADDR_IS_SRAM(addr_i)) return {3'd0, addr_i[16:15]};
if (`SOC_ADDR_IS_FLASH(addr_i) || `SOC_ADDR_IS_XPI(addr_i)) return 5'd4; if (`SOC_ADDR_IS_FLASH(addr_i) || `SOC_ADDR_IS_XPI(addr_i)) return 5'd4;
`include "tiny_fabric_decode.svh" `include "tiny_fabric_decode.svh"
return 5'd21; return DecodeErrorTarget;
endfunction endfunction
function automatic logic [4:0] checked_target(input logic [31:0] addr_i, input logic [7:0] len_i, function automatic logic [4:0] checked_target(input logic [31:0] addr_i, input logic [7:0] len_i,
input logic [2:0] size_i, input logic [1:0] burst_i, input logic [2:0] size_i, input logic [1:0] burst_i,
@@ -56,12 +59,14 @@ module tiny_axi4_fabric (
last_addr[32] || (last_addr[31:12] != addr_i[31:12]) || last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||
(decode_target( (decode_target(
last_addr[31:0] last_addr[31:0]
) != decoded) || ((decoded >= 5'd5) && (decoded <= 5'd20) && (len_i != 8'd0))) ) != decoded) || ((decoded >= 5'd5) && (decoded < DecodeErrorTarget) && (len_i != 8'd0)))
return 5'd22; return ProtocolErrorTarget;
if (instruction_i && !( if (instruction_i && !(
`SOC_ADDR_IS_FLASH(addr_i) `SOC_ADDR_IS_FLASH(addr_i)
|| ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000)))) || ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000)))) begin
return ((decoded >= 5'd5) && (decoded <= 5'd20)) ? 5'd22 : 5'd21; if ((decoded >= 5'd5) && (decoded < DecodeErrorTarget)) return ProtocolErrorTarget;
return DecodeErrorTarget;
end
return decoded; return decoded;
endfunction endfunction
@@ -106,7 +111,7 @@ module tiny_axi4_fabric (
fault_addr_o = '0; fault_addr_o = '0;
fault_resp_o = '0; fault_resp_o = '0;
fault_instruction_o = '0; fault_instruction_o = '0;
for (int target_index = 0; target_index < 23; target_index++) begin for (int target_index = 0; target_index < `RETROSOC_TINY__TARGET_COUNT; target_index++) begin
s_master_arready |= s_arready[target_index]; s_master_arready |= s_arready[target_index];
s_master_awready |= s_awready[target_index]; s_master_awready |= s_awready[target_index];
s_master_wready |= s_wready[target_index]; s_master_wready |= s_wready[target_index];
@@ -179,7 +184,9 @@ module tiny_axi4_fabric (
masters[owner].rlast, masters[owner].ruser} = s_read_return[owner]; masters[owner].rlast, masters[owner].ruser} = s_read_return[owner];
assign {masters[owner].bid, masters[owner].bresp, masters[owner].buser} = s_write_return[owner]; assign {masters[owner].bid, masters[owner].bresp, masters[owner].buser} = s_write_return[owner];
end end
for (genvar target_index = 0; target_index < 23; target_index++) begin : gen_target for (
genvar target_index = 0; target_index < `RETROSOC_TINY__TARGET_COUNT; target_index++
) begin : gen_target
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
@@ -260,8 +267,8 @@ module tiny_axi4_fabric (
assert property (@(posedge clk_i) disable iff (!rst_n_i) (s_admit & s_busy_q) == 6'd0); assert property (@(posedge clk_i) disable iff (!rst_n_i) (s_admit & s_busy_q) == 6'd0);
assert property (@(posedge clk_i) disable iff (!rst_n_i) (s_done & ~s_busy_q) == 6'd0); assert property (@(posedge clk_i) disable iff (!rst_n_i) (s_done & ~s_busy_q) == 6'd0);
for (genvar owner = 0; owner < 3; owner++) begin : gen_route_assert for (genvar owner = 0; owner < 3; owner++) begin : gen_route_assert
logic [22:0] s_reads, s_writes; logic [`RETROSOC_TINY__TARGET_COUNT-1:0] s_reads, s_writes;
for (genvar t = 0; t < 23; t++) begin : gen_column for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_column
assign s_reads[t] = s_rvalid[t][owner]; assign s_reads[t] = s_rvalid[t][owner];
assign s_writes[t] = s_bvalid[t][owner]; assign s_writes[t] = s_bvalid[t][owner];
end end
+45 -10
View File
@@ -21,8 +21,9 @@ TINY = ROOT / "rtl/tiny"
def read_topology(path: Path, memory_map: Path) -> dict: def read_topology(path: Path, memory_map: Path) -> dict:
document = json.loads(path.read_text()) document = json.loads(path.read_text())
regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]} regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]}
if document.get("schema_version") != 1 or document.get("dma_channels") != 4: if (document.get("schema_version") != 1 or document.get("dma_channels") != 8
raise ValueError("Tiny requires topology schema 1 and four DMA channels") or document.get("dma_request_mask") != "0x31ff"):
raise ValueError("Tiny P6 requires eight DMA channels and request mask 0x31ff")
if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2, if document.get("cpu") != {"module": "hazard3_cpu_2port", "local_ports": 2,
"data_outstanding": 1}: "data_outstanding": 1}:
raise ValueError("Tiny P4 requires independent I/D and one ordered data operation") raise ValueError("Tiny P4 requires independent I/D and one ordered data operation")
@@ -35,13 +36,13 @@ def read_topology(path: Path, memory_map: Path) -> dict:
}.items()): }.items()):
raise ValueError("Tiny SRAM geometry/P5 attachment differs from its frozen contract") raise ValueError("Tiny SRAM geometry/P5 attachment differs from its frozen contract")
if document.get("fabric") != { if document.get("fabric") != {
"owners": ["cpu_slow", "central_dma", "sdio_reserved"], "active_owners": [0, 1], "owners": ["cpu_slow", "central_dma", "sdio"], "active_owners": [0, 1, 2],
"cpu_combined_outstanding": 1, "dma_direction_outstanding": 1, "cpu_combined_outstanding": 1, "dma_direction_outstanding": 1,
"sdio_direction_outstanding": 1, "target_combined_outstanding": 1, "sdio_direction_outstanding": 1, "target_combined_outstanding": 1,
"sram_targets": [0, 1, 2, 3], "xpi_target": 4, "apb_target_base": 5, "sram_targets": [0, 1, 2, 3], "xpi_target": 4, "apb_target_base": 5,
"decode_error_target": 21, "protocol_error_target": 22, "target_count": 23, "decode_error_target": 25, "protocol_error_target": 26, "target_count": 27,
}: }:
raise ValueError("Tiny P5 requires three owner slots, two active owners and 23 targets") raise ValueError("Tiny P6 requires three active owners and 27 targets")
if document.get("irq_vector_width") != 32 or document.get("external_irq_count") != 30: if document.get("irq_vector_width") != 32 or document.get("external_irq_count") != 30:
raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs") raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs")
names: set[str] = set() names: set[str] = set()
@@ -61,14 +62,24 @@ def read_topology(path: Path, memory_map: Path) -> dict:
assigned.add(region) assigned.add(region)
if assigned != {r for r in regions if r.startswith("APB4_")}: if assigned != {r for r in regions if r.startswith("APB4_")}:
raise ValueError("Tiny topology must route every APB region") raise ValueError("Tiny topology must route every APB region")
if len(document["apb_targets"]) != 16: if len(document["apb_targets"]) != 20:
raise ValueError("Tiny's fixed APB bridge requires sixteen targets") raise ValueError("Tiny P6 requires twenty independent APB targets")
target_names = [target["name"] for target in document["apb_targets"]] target_names = [target["name"] for target in document["apb_targets"]]
if len(set(target_names)) != 16 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None if len(set(target_names)) != 20 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None
for name in target_names): for name in target_names):
raise ValueError("Tiny APB target names must be unique identifiers") raise ValueError("Tiny APB target names must be unique identifiers")
if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)): if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)):
raise ValueError("Tiny GPIO map must contain all 32 pins exactly once") raise ValueError("Tiny GPIO map must contain all 32 pins exactly once")
inputs = [signal for pin in document["gpio_alt_functions"] for mode in ("alt0", "alt1")
for signal in pin[mode]["inputs"]]
if len(inputs) != len(set(inputs)):
raise ValueError("Tiny peripheral inputs require one exclusive route")
if document.get("private_crypto") != {
"banks": 6, "bank_bytes": 4096,
"macro_instance": "u_soc.u_crypto.u_storage.gen_bank[{bank}].gen_macro.u_sram.u_mem",
"clock_domain": "system", "logical_domain": "PCLK (SAFE24 alias)",
}:
raise ValueError("Tiny P6 requires six private Crypto banks on SAFE24 PCLK")
return document return document
@@ -84,6 +95,22 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
have_pll: bool = False) -> None: have_pll: bool = False) -> None:
platform = platform_values(pdk, have_pll) platform = platform_values(pdk, have_pll)
comment = "// Generated by scripts/generate_tiny.py; do not edit.\n" comment = "// Generated by scripts/generate_tiny.py; do not edit.\n"
constants = [comment, "`ifndef RETROSOC_TINY__TOPOLOGY_SVH",
"`define RETROSOC_TINY__TOPOLOGY_SVH",
f"`define RETROSOC_TINY__TARGET_COUNT {document['fabric']['target_count']}",
f"`define RETROSOC_TINY__LEAF_COUNT {len(document['apb_targets']) + 1}",
f"`define RETROSOC_TINY__DECODE_ERROR {document['fabric']['decode_error_target']}",
f"`define RETROSOC_TINY__PROTOCOL_ERROR {document['fabric']['protocol_error_target']}"]
constants += [f"`define RETROSOC_TINY__TARGET_{target['name'].upper()} {index + 5}"
for index, target in enumerate(document["apb_targets"])]
constants.append("`endif\n")
atomic_write(output / "rtl/tiny_topology.svh", "\n".join(constants))
# Keep board pulls independently resolved. A packed tri1 with partial
# external drivers loses its weak pulls in the locked Verilator 5.046.
board = [comment, *[f"tri1 s_gpio_{pin};" for pin in range(document["gpio_pins"])]]
board.append("wire [31:0] s_gpio = {" +
", ".join(f"s_gpio_{pin}" for pin in reversed(range(32))) + "};\n")
atomic_write(output / "rtl/tiny_tb_gpio.svh", "\n".join(board))
irq = [comment, "always_comb begin", " s_irq = '0;"] irq = [comment, "always_comb begin", " s_irq = '0;"]
header = ["#ifndef RETROSOC_GENERATED_IRQ_METADATA_H", header = ["#ifndef RETROSOC_GENERATED_IRQ_METADATA_H",
"#define RETROSOC_GENERATED_IRQ_METADATA_H", "#include <stdint.h>", "#define RETROSOC_GENERATED_IRQ_METADATA_H", "#include <stdint.h>",
@@ -109,7 +136,15 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
"#ifndef RETROSOC_GENERATED_SOC_FEATURES_H\n" "#ifndef RETROSOC_GENERATED_SOC_FEATURES_H\n"
"#define RETROSOC_GENERATED_SOC_FEATURES_H\n" "#define RETROSOC_GENERATED_SOC_FEATURES_H\n"
"#define RS_SOC_TINY 1\n#define RS_SOC_HAS_PSRAM 0U\n" "#define RS_SOC_TINY 1\n#define RS_SOC_HAS_PSRAM 0U\n"
"#define RS_SOC_DMA_CHANNELS 4U\n#define RS_SOC_ID 0x54494E59U\n" "#define RS_SOC_DMA_CHANNELS 8U\n#define RS_SOC_ID 0x54494E59U\n"
"#define RS_SOC_DMA_REQUEST_MASK 0x31FFU\n"
"#define RS_SOC_HAS_I2C1 0U\n#define RS_SOC_HAS_SDIO1 0U\n"
"#define RS_SOC_HAS_UART0 1U\n#define RS_SOC_HAS_UART1 0U\n"
"#define RS_SOC_HAS_I2C0 1U\n"
"#define RS_SOC_HAS_RNG 1U\n#define RS_SOC_HAS_CRC 1U\n"
"#define RS_SOC_HAS_WS2812 1U\n#define RS_SOC_HAS_CRYPTO 1U\n"
"#define RS_SOC_HAS_I2S 1U\n#define RS_SOC_HAS_SDIO0 1U\n"
"#define RS_SOC_HAS_DVP 0U\n#define RS_SOC_RNG_QUALIFIED 0U\n"
f"#define RS_SOC_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n" f"#define RS_SOC_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n"
f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}U\n" f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}U\n"
f"#define RS_SOC_GPIO_HAS_PULLS {platform['gpio_pulls']}U\n#endif\n") f"#define RS_SOC_GPIO_HAS_PULLS {platform['gpio_pulls']}U\n#endif\n")
@@ -149,7 +184,7 @@ def generate_filelists(output: Path, generated: Path, defines: list[str],
prefixes = {"top": TINY / "top", "dv": TINY / "dv", "ip": ROOT / "rtl/ip", prefixes = {"top": TINY / "top", "dv": TINY / "dv", "ip": ROOT / "rtl/ip",
"tech": ROOT / "rtl/tech", "clusterip": ROOT / "rtl/managed/clusterip", "tech": ROOT / "rtl/tech", "clusterip": ROOT / "rtl/managed/clusterip",
"hazard3": ROOT / "rtl/managed/hazard3/hdl", "physical": ROOT / "physical", "hazard3": ROOT / "rtl/managed/hazard3/hdl", "physical": ROOT / "physical",
"cache": ROOT / ".cache"} "cache": ROOT / ".cache", "tests": ROOT / "tests"}
suffix = "_yosys" if synthesis == "YOSYS" else "_verilator" if simulator == "VERILATOR" else "" suffix = "_yosys" if synthesis == "YOSYS" else "_verilator" if simulator == "VERILATOR" else ""
pdk_name = "pdk_ihp130.fl" if pdk == "IHP130" else f"pdk_ics55{suffix}.fl" pdk_name = "pdk_ihp130.fl" if pdk == "IHP130" else f"pdk_ics55{suffix}.fl"
templates = [*sorted((TINY / "filelist").glob("*.fl")), templates = [*sorted((TINY / "filelist").glob("*.fl")),
+1 -1
View File
@@ -15,7 +15,7 @@ from scripts.setup_helpers import ensure_git_repo # noqa: E402
TINY_SOURCES = ( TINY_SOURCES = (
"hazard3", "cluster_common", "cluster_archinfo", "cluster_pwm", "hazard3", "cluster_common", "cluster_archinfo", "cluster_pwm",
"cluster_rtc", "cluster_wdg", "pdk_ihp130", "cluster_rtc", "cluster_wdg", "cluster_rng", "cluster_crc", "pdk_ihp130",
) )
+13 -1
View File
@@ -177,7 +177,18 @@ def inspect_netlist(document: dict) -> dict:
system = canonical(f"{top}:{clock[0]}") system = canonical(f"{top}:{clock[0]}")
if system in ("0", "1", "x", "z"): if system in ("0", "1", "x", "z"):
raise ValueError("SYS is not a live clock net") raise ValueError("SYS is not a live clock net")
ram = [c for c in leaves if c["type"] == RAM] all_ram = [c for c in leaves if c["type"] == RAM]
crypto_names = {f"u_soc.u_crypto.u_storage.gen_bank[{n}].gen_macro.u_sram.u_mem"
for n in range(6)}
crypto = [c for c in all_ram if any(c["path"].replace("/", ".").endswith("." + name)
for name in crypto_names)]
observed_crypto = {name for name in crypto_names for c in crypto
if c["path"].replace("/", ".").endswith("." + name)}
if len(all_ram) != 38 or len(crypto) != 6 or observed_crypto != crypto_names:
raise ValueError("Tiny P6 requires exactly 32 main SRAM and six private Crypto macros")
if any(c["ports"].get("CLK") != [system] for c in crypto):
raise ValueError("private Crypto SRAM does not use SAFE24 PCLK")
ram = [c for c in all_ram if c not in crypto]
pll = [c for c in leaves if c["type"] == "PLL_TOP"] pll = [c for c in leaves if c["type"] == "PLL_TOP"]
if any(c["type"] == "ics55_ecos_sram_4096x32_m8" for c in leaves): if any(c["type"] == "ics55_ecos_sram_4096x32_m8" for c in leaves):
raise ValueError("Tiny instantiated the unsupported 16 KiB SRAM interface stub") raise ValueError("Tiny instantiated the unsupported 16 KiB SRAM interface stub")
@@ -219,6 +230,7 @@ def inspect_netlist(document: dict) -> dict:
"system_clock_source": {"port": "extclk_i_pad", "net": reference, "path": clock_path}, "system_clock_source": {"port": "extclk_i_pad", "net": reference, "path": clock_path},
"cpu_clocked_cells": len(cpu), "cpu_clocked_cells": len(cpu),
"sram": [{"path": c["path"], "clock": c["ports"]["CLK"]} for c in ram], "sram": [{"path": c["path"], "clock": c["ports"]["CLK"]} for c in ram],
"crypto_sram": [{"path": c["path"], "clock": c["ports"]["CLK"]} for c in crypto],
"pll": pll[0], "pll_enable": 0, "qualification": "SAFE24 digital connectivity only"} "pll": pll[0], "pll_enable": 0, "qualification": "SAFE24 digital connectivity only"}
+386
View File
@@ -0,0 +1,386 @@
#!/usr/bin/env python3
"""Capture and check source-bound Tiny P6 acceptance without promoting a phase."""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
from pathlib import Path
import re
import sys
import tarfile
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from scripts import tiny_r2_baseline as b # noqa: E402
from scripts import tiny_r2_p5 as previous # noqa: E402
from scripts.check_simulation import DEFAULT_FAILURE # noqa: E402
PHASE = "TINY-R2-P6"
MODEL = "tests/rtl/sdio_native_model.sv"
VERIFICATION_UPDATE_PATHS = {MODEL, "scripts/tiny_r2_p6.py", "tests/test_tiny_r2_p6.py"}
CONSUMER_UPDATE_PATHS = {
"publications/programming_reference.py", "tests/test_publication_programming.py",
"physical/commercial/config/products/tiny.mk", "physical/commercial/tcl/common/products/tiny.tcl",
"tests/test_commercial_flow.py", "scripts/tiny_r2_p6.py", "tests/test_tiny_r2_p6.py",
}
COLLECTION_UPDATE_PATHS = {"scripts/tiny_r2_p6.py", "tests/test_tiny_r2_p6.py"}
RECORDS = {
"UART": {"pads": 1, "echo_bytes": 4, "flow_rejected": 1},
"DMA": {"channels": 8, "rejected_requests": 5},
"RNG": {"unqualified": 1, "diagnostic": 1, "irq": 1},
"CRC": {"dma": 1, "tails": 4, "empty": 1},
"CRYPTO": {"init": 1, "aes_sha": 1, "dma45": 1, "rsa_public": 1, "zeroize": 1},
"RECOVERY": {"audio_stops": 2, "ws_underflow": 1, "ws_abort": 1},
"SDIO": {"dma_bytes": 2048, "pio_bytes": 512, "width1": 1, "width4": 1, "irq": 1, "errors": 2},
}
def parse_log(content: str, flow: dict) -> dict:
if (flow.get("status") != "passed" or flow.get("exit_code") != 0
or not re.search(r"SIM_TEST_PASS Tiny cycles=[0-9]+(?:\r?\n|$)", content)
or DEFAULT_FAILURE.search(content)):
raise ValueError("P6 requires successful command, terminal marker and no forbidden errors")
result = {}
for name, expected in RECORDS.items():
rows = [b.parse_fields(line, set(expected)) for line in content.splitlines()
if line.startswith("R2_P6_" + name + " ")]
if rows != [expected]:
raise ValueError(f"missing, duplicate or invalid P6 {name} record")
result[name.lower()] = rows[0]
fields = {"bits", "frames", "ws_frames", "background_jobs", "tcd", "elapsed_us", "cycles", "instructions"}
audio = [b.parse_fields(line, fields) for line in content.splitlines()
if line.startswith("R2_P6_AUDIO ")]
if [row["bits"] for row in audio] != [16, 24]:
raise ValueError("P6 needs exactly both audio formats")
for row in audio:
if (row["frames"] != 256 or row["ws_frames"] != 2 or
not 1 <= row["background_jobs"] <= 64 or
row["tcd"] != int(row["bits"] == 24) or not 0 < row["elapsed_us"] < 50000 or
not 0 < row["instructions"] <= row["cycles"]):
raise ValueError("invalid P6 audio/contention observation")
result["audio"] = audio
wires = [b.parse_fields(line, {"bits", "samples"}) for line in content.splitlines()
if line.startswith("R2_P6_I2S_WIRE ")]
if wires != [{"bits": 16, "samples": 512}, {"bits": 24, "samples": 512}]:
raise ValueError("missing or incorrect independent audio wire observations")
result["audio_wires"] = wires
fields = {"owner", "read_bytes", "write_bytes", "descriptor_bytes", "ar_stall_max",
"aw_stall_max", "w_stall_max", "r_stall_max", "b_stall_max"}
bus = sorted([b.parse_fields(line, fields) for line in content.splitlines()
if line.startswith("R2_P6_AXI ")], key=lambda row: row["owner"])
if [row["owner"] for row in bus] != [0, 1, 2]:
raise ValueError("P6 needs each actual AXI owner observation")
for row in bus:
if (any(v < 0 for v in row.values()) or row["read_bytes"] == 0 or row["write_bytes"] == 0
or row["descriptor_bytes"] > row["read_bytes"]):
raise ValueError("invalid P6 AXI payload/backpressure observation")
if bus[1]["descriptor_bytes"] == 0:
raise ValueError("no actual background descriptor fetch observed")
result["bus"] = bus
contention = [b.parse_fields(line, {"sdio_dma_cycles", "cpu_fetches"})
for line in content.splitlines() if line.startswith("R2_P6_CONTENTION ")]
if len(contention) != 1 or any(value <= 0 for value in contention[0].values()):
raise ValueError("missing actual SDIO/DMA overlap and CPU progress")
result["contention"] = contention[0]
fields = {"ws_frames", "ws_wait_max", "fifo_max", "underflows", "aborts", "clock_stops"}
devices = [b.parse_fields(line, fields) for line in content.splitlines()
if line.startswith("R2_P6_DEVICES ")]
if len(devices) != 1:
raise ValueError("missing or duplicate independent device observations")
d = devices[0]
if (d["ws_frames"] != 4 or not 0 <= d["ws_wait_max"] <= 4800 or
not 1 <= d["fifo_max"] <= 16 or d["underflows"] != 1 or
d["aborts"] != 1 or d["clock_stops"] != 2):
raise ValueError("P6 device coverage/service budget failed")
result["devices"] = d
return result
def inputs(variants: list[Path]) -> dict:
result = {}
for variant in variants:
config = b.configuration(variant)["configuration"]
if config.get("APP") != "ci_smoke":
raise ValueError("P6 qualification requires the ci_smoke composition")
result.update(previous.inputs(ROOT, variant))
for name in (".bake.toml", ".clang-format", "docs/misra-c-2012.md",
"quality/misra/deviations.md", "docs/ip/dma.md", "docs/ip/i2s.md",
"docs/ip/sdio.md", "docs/ip/crypto.md", "docs/ip/ws2812.md",
"physical/commercial/config/products/tiny.mk",
"physical/commercial/tcl/common/products/tiny.tcl"):
result[name] = b.sha256(ROOT / name)
return result
def current_tools() -> dict:
tools = b.tools_identity(ROOT)
tools["vvp"] = b.vvp_identity("vvp", tools["iverilog"])
return tools
def checked_flow(path: Path, captured_at: str) -> dict:
flow = b.read_json(path)
if flow.get("status") != "passed" or flow.get("exit_code") != 0:
raise ValueError(f"unsuccessful gate: {path}")
if (not flow.get("started_at") or not flow.get("finished_at")
or flow["started_at"] < captured_at or flow["finished_at"] < flow["started_at"]):
raise ValueError(f"gate lacks successful post-capture chronology: {path}")
return flow
def check_model_declaration_move(before: str, after: str) -> None:
"""Permit only relocation of one unchanged continuous net declaration.
Verilator/sv2v accept its forward reference; native Icarus needs the integer
first. Every other source byte, including all procedural behavior, must match.
"""
pattern = re.compile(r"^ wire \[3:0\] s_token_lanes = [^;]+;\n", re.M)
old, new = pattern.findall(before), pattern.findall(after)
if (len(old) != 1 or old != new or pattern.sub("", before) != pattern.sub("", after)
or after.index(" integer s_data_bytes;") > after.index(new[0])):
raise ValueError("not the permitted SDIO model declaration-only relocation")
def snapshot_model(source: dict) -> str:
with tarfile.open(b.check_artifact(source["snapshot"])) as archive:
return archive.extractfile(MODEL).read().decode()
def check_update(directory: Path, saved: dict, update: dict, current: dict) -> dict:
if (update.get("phase") != PHASE or update.get("kind") != "native_model_declaration_order"
or update["original_source"] != b.artifact(directory / "source.json")):
raise ValueError("verification update does not bind the original campaign")
old = b.read_json(b.check_artifact(saved["source"]["inputs"]))
new = b.read_json(b.check_artifact(update["source"]["inputs"]))
changed = {name for name in old.keys() | new.keys() if old.get(name) != new.get(name)}
if (MODEL not in changed or not changed <= VERIFICATION_UPDATE_PATHS
or sorted(changed) != update["changed_inputs"] or new != current):
raise ValueError("verification update changed DUT/firmware/constraints or unapproved inputs")
check_model_declaration_move(snapshot_model(saved["source"]), snapshot_model(update["source"]))
for source in (saved["source"], update["source"]):
for name in ("inputs", "snapshot", "patch"):
b.check_artifact(source[name])
if update["tools"] != saved["tools"] or update["captured_at"] < saved["captured_at"]:
raise ValueError("verification update changed tools or chronology")
return old
def capture_verification_update(directory: Path, variants: list[Path]) -> None:
path = directory / "verification-update.json"
if path.exists():
raise ValueError("never overwrite a verification update")
saved = b.read_json(directory / "source.json")
current = inputs(variants)
old = b.read_json(b.check_artifact(saved["source"]["inputs"]))
changed = sorted(name for name in old.keys() | current.keys() if old.get(name) != current.get(name))
if MODEL not in changed or not set(changed) <= VERIFICATION_UPDATE_PATHS:
raise ValueError("only the native model and its collector tests may change")
check_model_declaration_move(snapshot_model(saved["source"]), (ROOT / MODEL).read_text())
update = {"phase": PHASE, "kind": "native_model_declaration_order",
"original_source": b.artifact(directory / "source.json"), "changed_inputs": changed,
"source": b.snapshot(ROOT, directory / "inputs-native-model", current),
"tools": current_tools(), "captured_at": datetime.now(timezone.utc).isoformat()}
check_update(directory, saved, update, current)
b.dump(path, update)
def check_consumer_update(directory: Path, previous_update: dict, update: dict, current: dict) -> None:
"""Permit only consumers not used by the running RTL/smoke flows."""
if (update.get("phase") != PHASE or update.get("kind") != "publication_and_commercial_consumers"
or update["previous_update"] != b.artifact(directory / "verification-update.json")):
raise ValueError("consumer update does not bind the verified model snapshot")
old = b.read_json(b.check_artifact(previous_update["source"]["inputs"]))
new = b.read_json(b.check_artifact(update["source"]["inputs"]))
changed = {name for name in old.keys() | new.keys() if old.get(name) != new.get(name)}
if (not changed or not changed <= CONSUMER_UPDATE_PATHS or
sorted(changed) != update["changed_inputs"] or new != current):
raise ValueError("consumer update changed simulation, firmware, smoke constraints or unrelated inputs")
for field in ("inputs", "snapshot", "patch"):
b.check_artifact(update["source"][field])
if (update["tools"] != previous_update["tools"] or
update["captured_at"] < previous_update["captured_at"]):
raise ValueError("consumer update changed tools or chronology")
def capture_consumer_update(directory: Path, variants: list[Path]) -> None:
path = directory / "consumer-update.json"
if path.exists():
raise ValueError("never overwrite a consumer update")
previous_update = b.read_json(directory / "verification-update.json")
current = inputs(variants)
old = b.read_json(b.check_artifact(previous_update["source"]["inputs"]))
changed = sorted(name for name in old.keys() | current.keys() if old.get(name) != current.get(name))
if not changed or not set(changed) <= CONSUMER_UPDATE_PATHS:
raise ValueError("unexpected non-consumer source changes")
update = {"phase": PHASE, "kind": "publication_and_commercial_consumers",
"previous_update": b.artifact(directory / "verification-update.json"),
"changed_inputs": changed,
"source": b.snapshot(ROOT, directory / "inputs-consumers", current),
"tools": current_tools(), "captured_at": datetime.now(timezone.utc).isoformat()}
check_consumer_update(directory, previous_update, update, current)
b.dump(path, update)
def check_collection_update(directory: Path, previous_update: dict, update: dict, current: dict) -> None:
if (update.get("phase") != PHASE or update.get("kind") != "terminal_log_collection"
or update["previous_update"] != b.artifact(directory / "consumer-update.json")):
raise ValueError("collection update does not bind the consumer snapshot")
old = b.read_json(b.check_artifact(previous_update["source"]["inputs"]))
new = b.read_json(b.check_artifact(update["source"]["inputs"]))
changed = {name for name in old.keys() | new.keys() if old.get(name) != new.get(name)}
if (not changed or not changed <= COLLECTION_UPDATE_PATHS or
sorted(changed) != update["changed_inputs"] or new != current):
raise ValueError("collection update changed inputs outside the collector/tests")
for field in ("inputs", "snapshot", "patch"):
b.check_artifact(update["source"][field])
if (update["tools"] != previous_update["tools"] or
update["captured_at"] < previous_update["captured_at"]):
raise ValueError("collection update changed tools or chronology")
def capture_collection_update(directory: Path, variants: list[Path]) -> None:
path = directory / "collection-update.json"
if path.exists():
raise ValueError("never overwrite a collection update")
previous_update = b.read_json(directory / "consumer-update.json")
current = inputs(variants)
old = b.read_json(b.check_artifact(previous_update["source"]["inputs"]))
changed = sorted(name for name in old.keys() | current.keys() if old.get(name) != current.get(name))
if not changed or not set(changed) <= COLLECTION_UPDATE_PATHS:
raise ValueError("unexpected non-collector source changes")
update = {"phase": PHASE, "kind": "terminal_log_collection",
"previous_update": b.artifact(directory / "consumer-update.json"),
"changed_inputs": changed,
"source": b.snapshot(ROOT, directory / "inputs-collection", current),
"tools": current_tools(), "captured_at": datetime.now(timezone.utc).isoformat()}
check_collection_update(directory, previous_update, update, current)
b.dump(path, update)
def capture(directory: Path, variants: list[Path]) -> None:
if (directory / "source.json").exists():
raise ValueError("capture needs a fresh evidence directory; never overwrite provenance")
if {b.configuration(v)["configuration"]["PDK"] for v in variants} != {"ICS55", "IHP130"}:
raise ValueError("capture both Tiny PDK input sets before validation")
source = b.snapshot(ROOT, directory / "inputs", inputs(variants))
tools = current_tools()
b.dump(directory / "source.json", {"phase": PHASE, "source": source, "tools": tools,
"captured_at": datetime.now(timezone.utc).isoformat(),
"variants": [str(p.resolve()) for p in variants],
"configurations": [b.configuration(p) for p in variants]})
def report(directory: Path, gates: list[Path], variants: list[Path]) -> None:
saved = b.read_json(directory / "source.json")
if saved.get("phase") != PHASE:
raise ValueError("wrong evidence phase")
if saved["tools"] != current_tools():
raise ValueError("locked tool identity changed after capture")
selected = [Path(p) for p in saved["variants"]]
current = inputs(selected)
original = b.read_json(b.check_artifact(saved["source"]["inputs"]))
update_path = directory / "verification-update.json"
update = b.read_json(update_path) if update_path.exists() else None
consumer_path = directory / "consumer-update.json"
consumer = b.read_json(consumer_path) if consumer_path.exists() else None
collection_path = directory / "collection-update.json"
collection = b.read_json(collection_path) if collection_path.exists() else None
consumer_inputs = current
if collection:
if not consumer:
raise ValueError("collection update requires the consumer snapshot")
check_collection_update(directory, consumer, collection, current)
consumer_inputs = b.read_json(b.check_artifact(consumer["source"]["inputs"]))
verified_inputs = current
if consumer:
if not update:
raise ValueError("consumer update requires the model snapshot")
check_consumer_update(directory, update, consumer, consumer_inputs)
verified_inputs = b.read_json(b.check_artifact(update["source"]["inputs"]))
if update:
original = check_update(directory, saved, update, verified_inputs)
elif current != original:
raise ValueError("P6 technical inputs changed after capture")
for name in ("snapshot", "patch"):
b.check_artifact(saved["source"][name])
checked = {}
for path in gates:
gate = checked_flow(path, saved["captured_at"])
checked[str(path)] = {"result": b.artifact(path), "log": b.artifact(Path(gate["log"]))}
simulations = {}
selections = [(variant, simulator) for variant in variants for simulator in ("iverilog", "verilator")
if (variant / "sim" / simulator / "result-sim.json").is_file()]
for variant, simulator in selections:
config = b.configuration(variant)
if config["configuration"].get("APP") != "ci_smoke":
raise ValueError("simulation must use the complete ci_smoke composition")
pdk = config["configuration"]["PDK"]
key = pdk + "/" + simulator
if key in simulations or simulator not in ("iverilog", "verilator"):
raise ValueError("duplicate or unsupported simulation variant")
folder = variant / "sim" / simulator
model_source = update if update and simulator == "iverilog" else saved
flow = checked_flow(folder / "result-sim.json", model_source["captured_at"])
compilation = checked_flow(folder / "result-compile.json", model_source["captured_at"])
if flow["started_at"] < compilation["finished_at"]:
raise ValueError("simulation predates completed model compilation")
check = b.read_json(folder / "result-sim-check.json")
if check.get("status") != "passed":
raise ValueError("missing strict simulation verdict")
samples = parse_log((folder / "sim.log").read_text(), flow)
paths = [folder / name for name in ("result-sim.json", "result-sim-check.json", "sim.log",
"result-compile.json", "compile.log")]
paths += [folder / ("emu" if simulator == "verilator" else "simv"),
variant / "sw/firmware", variant / "sw/retrosoc_fw.hex", variant / "sw/retrosoc_fw.map",
variant / "sw/retrosoc_fw.bin", variant / "sw/ld2_all_sram.lds",
variant / "sw/memory_regions.ld",
variant / "meta/manifest.json"]
paths += sorted(path for path in (variant / "sw/include").rglob("*") if path.is_file())
runtime = b.runtime_inputs(ROOT, variant, simulator)
if update and simulator == "verilator":
if runtime.get(MODEL) != current[MODEL]:
raise ValueError("missing native card model runtime input")
runtime[MODEL] = original[MODEL]
simulations[key] = {"configuration": config, "simulator": simulator, "samples": samples,
"source": model_source["source"], "runtime_inputs": runtime,
"artifacts": [b.artifact(path) for path in paths]}
if set(simulations) != {p + "/" + s for p in ("ICS55", "IHP130") for s in ("iverilog", "verilator")}:
raise ValueError("P6 needs both simulators on both Tiny PDKs")
# Physical/quality reports remain independent gates; this status never
# promotes functional collection into phase or physical qualification.
b.dump(directory / "report.json", {"phase": PHASE, "status": "functional_evidence_collected",
"tools": saved["tools"],
"verification_update": b.artifact(update_path) if update else None,
"consumer_update": b.artifact(consumer_path) if consumer else None,
"collection_update": b.artifact(collection_path) if collection else None,
"source": saved["source"], "source_inputs_rechecked": len(current), "gates": checked,
"simulations": simulations, "limitations": ["human review pending", "timing observational",
"no QFN64/package, routed/PVT/CDC/RDC or publication qualification"]})
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("action", choices=("capture", "capture-verification-update", "capture-consumer-update",
"capture-collection-update", "report"))
parser.add_argument("--evidence-root", type=Path, required=True)
parser.add_argument("--variant-root", action="append", type=Path, required=True)
parser.add_argument("--gate", action="append", type=Path, default=[])
args = parser.parse_args()
directory = args.evidence_root.resolve()
if not directory.is_relative_to(ROOT / "build"):
parser.error("evidence must stay under the repository build root")
if args.action == "capture":
capture(directory, args.variant_root)
elif args.action == "capture-verification-update":
capture_verification_update(directory, args.variant_root)
elif args.action == "capture-consumer-update":
capture_consumer_update(directory, args.variant_root)
elif args.action == "capture-collection-update":
capture_collection_update(directory, args.variant_root)
else:
report(directory, args.gate, args.variant_root)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+68 -37
View File
@@ -92,9 +92,13 @@ void rs_dma_test_irq_restore(uint32_t saved) {
dma_irq_result = rs_dma_session_acquire(&dma_irq_owner, 3U); dma_irq_result = rs_dma_session_acquire(&dma_irq_owner, 3U);
if (dma_irq_result == RS_OK) { if (dma_irq_result == RS_OK) {
const rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM, const rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM,
.source = 0x3000U, .destination = 0x4000U, .byte_count = 16U, .source = 0x3000U,
.width = RS_DMA_WIDTH_32, .source_increment = true, .destination = 0x4000U,
.destination_increment = true, .burst_beats = 1U}; .byte_count = 16U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 1U};
dma_irq_result = rs_dma_session_configure(&dma_irq_owner, &config); dma_irq_result = rs_dma_session_configure(&dma_irq_owner, &config);
} }
} }
@@ -147,18 +151,21 @@ static void ws_test_reset(void) {
} }
static int test_dma_tcd_preemption(void) { static int test_dma_tcd_preemption(void) {
static _Alignas(64) rs_dma_tcd_t tcd = { static _Alignas(64)
.source = 0x1000U, .destination = 0x2000U, .byte_count = 32U, .y_count = 1U, rs_dma_tcd_t tcd = {.source = 0x1000U,
.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC | .destination = 0x2000U,
(UINT32_C(1) << RS_DMA_TCD_BURST_SHIFT)}; .byte_count = 32U,
.y_count = 1U,
.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC |
(UINT32_C(1) << RS_DMA_TCD_BURST_SHIFT)};
rs_status_t result; rs_status_t result;
bool valid; bool valid;
ws_test_reset(); ws_test_reset();
dma_irq_pending = true; dma_irq_pending = true;
dma_irq_result = RS_OK; dma_irq_result = RS_OK;
result = rs_dma_submit_tcd(3U, &tcd, 1U); result = rs_dma_submit_tcd(3U, &tcd, 1U);
valid = (result == RS_ETIMEOUT) && (dma_irq_result == RS_EIO) && valid = (result == RS_ETIMEOUT) && (dma_irq_result == RS_EIO) && !dma_irq_pending &&
!dma_irq_pending && (dma_irq_enabled == 8U) && (dma_irq_enabled == 8U) &&
(RS_DMA_CH_REG(3U, RS_DMA_CH_REG_TCD_HEAD) == (uint32_t)(uintptr_t)&tcd) && (RS_DMA_CH_REG(3U, RS_DMA_CH_REG_TCD_HEAD) == (uint32_t)(uintptr_t)&tcd) &&
(RS_DMA_CH_REG(3U, RS_DMA_CH_REG_TCD_COUNT) == 1U) && (RS_DMA_CH_REG(3U, RS_DMA_CH_REG_TCD_COUNT) == 1U) &&
(RS_DMA_CH_REG(3U, RS_DMA_CH_REG_SRC_ADDR) == tcd.source); (RS_DMA_CH_REG(3U, RS_DMA_CH_REG_SRC_ADDR) == tcd.source);
@@ -211,8 +218,7 @@ static int test_ws2812_completion_race(void) {
return 4; return 4;
} }
(void)rs_ws2812_dma_status(&transfer); (void)rs_ws2812_dma_status(&transfer);
if (transfer.active || (transfer.result != RS_OK) || if (transfer.active || (transfer.result != RS_OK) || (ws_registers[0x24U / 4U] != 3U)) {
(ws_registers[0x24U / 4U] != 3U)) {
return 5; return 5;
} }
} }
@@ -248,9 +254,14 @@ static int test_ws2812_completion_race(void) {
static int test_dma_sessions(void) { static int test_dma_sessions(void) {
rs_dma_session_t first = {0}, second = {0}; rs_dma_session_t first = {0}, second = {0};
rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM, .source = 0x1000U, rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM,
.destination = 0x2000U, .byte_count = 32U, .width = RS_DMA_WIDTH_32, .source = 0x1000U,
.source_increment = true, .destination_increment = true, .burst_beats = 1U}; .destination = 0x2000U,
.byte_count = 32U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 1U};
ws_test_reset(); ws_test_reset();
if ((rs_dma_session_acquire(&first, 3U) != RS_OK) || if ((rs_dma_session_acquire(&first, 3U) != RS_OK) ||
(rs_dma_session_acquire(&first, 2U) != RS_EIO) || (rs_dma_session_acquire(&first, 2U) != RS_EIO) ||
@@ -262,13 +273,11 @@ static int test_dma_sessions(void) {
return 1; return 1;
} }
RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_BUSY; RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_BUSY;
if ((rs_dma_session_release(&first) != RS_EIO) || if ((rs_dma_session_release(&first) != RS_EIO) || (rs_dma_session_abort(&first) != RS_OK)) {
(rs_dma_session_abort(&first) != RS_OK)) {
return 2; return 2;
} }
RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_ABORTED; RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_ABORTED;
if ((rs_dma_session_release(&first) != RS_OK) || if ((rs_dma_session_release(&first) != RS_OK) || (rs_dma_session_release(&first) != RS_EIO) ||
(rs_dma_session_release(&first) != RS_EIO) ||
(rs_dma_session_acquire(&second, 3U) != RS_OK) || (rs_dma_session_acquire(&second, 3U) != RS_OK) ||
(rs_dma_session_release(&second) != RS_OK)) { (rs_dma_session_release(&second) != RS_OK)) {
return 3; return 3;
@@ -301,15 +310,13 @@ static int test_ws2812_refill(void) {
ws_pixels[32] = 0U; ws_pixels[32] = 0U;
if ((rs_ws2812_dma_begin(ws_pixels, 33U, 0U, 100U) != RS_OK) || (ws_pushed != 16U) || if ((rs_ws2812_dma_begin(ws_pixels, 33U, 0U, 100U) != RS_OK) || (ws_pushed != 16U) ||
(ws_registers[0x24U / 4U] != 8U) || (rs_ws2812_push(0U, 1U) != RS_EIO) || (ws_registers[0x24U / 4U] != 8U) || (rs_ws2812_push(0U, 1U) != RS_EIO) ||
(rs_ws2812_start(1U) != RS_EIO) || (rs_ws2812_start(1U) != RS_EIO) || (rs_dma_session_acquire(&competitor, 3U) != RS_EIO)) {
(rs_dma_session_acquire(&competitor, 3U) != RS_EIO)) {
return 4; return 4;
} }
ws_registers[0x30U / 4U] = RS_WS2812_INTR_FIFO_LOW; ws_registers[0x30U / 4U] = RS_WS2812_INTR_FIFO_LOW;
(void)rs_ws2812_dma_service(1U); (void)rs_ws2812_dma_service(1U);
(void)rs_ws2812_dma_status(&transfer); (void)rs_ws2812_dma_status(&transfer);
if ((transfer.batches != 0U) || if ((transfer.batches != 0U) || ((ws_registers[0x30U / 4U] & RS_WS2812_INTR_FIFO_LOW) != 0U)) {
((ws_registers[0x30U / 4U] & RS_WS2812_INTR_FIFO_LOW) != 0U)) {
return 5; return 5;
} }
/* Hardware REMAINING includes queued words: it must never size the batch. */ /* Hardware REMAINING includes queued words: it must never size the batch. */
@@ -345,8 +352,7 @@ static int test_ws2812_refill(void) {
ws_registers[0x30U / 4U] = RS_WS2812_INTR_DONE; ws_registers[0x30U / 4U] = RS_WS2812_INTR_DONE;
if ((rs_ws2812_dma_service(15U) != RS_OK) || if ((rs_ws2812_dma_service(15U) != RS_OK) ||
(rs_dma_session_acquire(&competitor, 3U) != RS_OK) || (rs_dma_session_acquire(&competitor, 3U) != RS_OK) ||
(rs_dma_session_release(&competitor) != RS_OK) || (rs_dma_session_release(&competitor) != RS_OK) || (ws_registers[0x24U / 4U] != 3U)) {
(ws_registers[0x24U / 4U] != 3U)) {
return 11; return 11;
} }
/* Wire DONE may precede a delayed final B. Retain the lease until both /* Wire DONE may precede a delayed final B. Retain the lease until both
@@ -907,7 +913,11 @@ static int test_sdio_helpers(void) {
rs_sd_memory_info_t info = {0}; rs_sd_memory_info_t info = {0};
uint32_t value; uint32_t value;
if ((rs_sdio_clock_calculate(72000000U, 400000U, &clock) != RS_OK) || if ((rs_sdio_clock_calculate(24000000U, 400000U, &clock) != RS_OK) ||
(clock.half_period != 30U) || (clock.actual_hz != 400000U) ||
(rs_sdio_clock_calculate(24000000U, 12000000U, &clock) != RS_OK) ||
(clock.half_period != 1U) || (clock.actual_hz != 12000000U) ||
(rs_sdio_clock_calculate(72000000U, 400000U, &clock) != RS_OK) ||
(clock.half_period != 90U) || (clock.actual_hz != 400000U) || (clock.half_period != 90U) || (clock.actual_hz != 400000U) ||
(rs_sdio_clock_calculate(100000000U, 50000000U, &clock) != RS_OK) || (rs_sdio_clock_calculate(100000000U, 50000000U, &clock) != RS_OK) ||
(clock.half_period != 1U) || (clock.actual_hz != 50000000U)) { (clock.half_period != 1U) || (clock.actual_hz != 50000000U)) {
@@ -1075,32 +1085,48 @@ rs_status_t rs_tiny_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd);
static int test_tiny_dma_config_validation(void) { static int test_tiny_dma_config_validation(void) {
rs_dma_config_t config = { rs_dma_config_t config = {
.kind = RS_DMA_KIND_MM_TO_MM, .request = RS_DMA_REQUEST_SOFTWARE, .kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)UINT32_C(0x30000000), .source = (uintptr_t)UINT32_C(0x30000000),
.destination = (uintptr_t)UINT32_C(0x30001000), .byte_count = 259U, .destination = (uintptr_t)UINT32_C(0x30001000),
.width = RS_DMA_WIDTH_32, .source_increment = true, .destination_increment = true, .byte_count = 259U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U, .burst_beats = 16U,
}; };
static _Alignas(64) rs_dma_tcd_t descriptor; static _Alignas(64) rs_dma_tcd_t descriptor;
if ((rs_tiny_dma_config_validate(3U, &config) != RS_OK) || if ((rs_tiny_dma_config_validate(3U, &config) != RS_OK) ||
(rs_tiny_dma_config_validate(4U, &config) != RS_EINVAL)) { (rs_tiny_dma_config_validate(7U, &config) != RS_OK) ||
(rs_tiny_dma_config_validate(8U, &config) != RS_EINVAL)) {
return 1; return 1;
} }
config.kind = RS_DMA_KIND_MM_TO_STREAM; config.kind = RS_DMA_KIND_MM_TO_STREAM;
config.request = RS_DMA_REQUEST_I2S_TX; config.request = RS_DMA_REQUEST_I2S_TX;
config.byte_count = 256U; config.byte_count = 256U;
if (rs_tiny_dma_config_validate(0U, &config) != RS_EINVAL) { if (rs_tiny_dma_config_validate(6U, &config) != RS_OK) {
return 2; return 2;
} }
descriptor.source = UINT32_C(0x30000000); descriptor.source = UINT32_C(0x30000000);
descriptor.byte_count = 256U; descriptor.byte_count = 256U;
descriptor.y_count = 1U; descriptor.y_count = 1U;
descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC |
((uint32_t)RS_DMA_KIND_MM_TO_STREAM << RS_DMA_TCD_KIND_SHIFT) | ((uint32_t)RS_DMA_KIND_MM_TO_STREAM << RS_DMA_TCD_KIND_SHIFT) |
((uint32_t)RS_DMA_REQUEST_I2S_TX << RS_DMA_TCD_REQUEST_SHIFT); ((uint32_t)RS_DMA_REQUEST_I2S_TX << RS_DMA_TCD_REQUEST_SHIFT);
if (rs_tiny_dma_tcd_validate(0U, &descriptor) != RS_ENOTSUP) { if (rs_tiny_dma_tcd_validate(6U, &descriptor) != RS_OK) {
return 3; return 3;
} }
config.kind = RS_DMA_KIND_STREAM_TO_MM;
config.request = RS_DMA_REQUEST_DVP_RX;
if (rs_tiny_dma_config_validate(2U, &config) != RS_EINVAL) {
return 4;
}
descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_DST_INC |
((uint32_t)RS_DMA_KIND_STREAM_TO_MM << RS_DMA_TCD_KIND_SHIFT) |
((uint32_t)RS_DMA_REQUEST_DVP_RX << RS_DMA_TCD_REQUEST_SHIFT);
if (rs_tiny_dma_tcd_validate(2U, &descriptor) != RS_ENOTSUP) {
return 5;
}
return 0; return 0;
} }
@@ -2992,9 +3018,12 @@ static int test_npu_p6_reference_edge_ops(void) {
uint8_t depthwise_weights[72] = {0U}; uint8_t depthwise_weights[72] = {0U};
int32_t depthwise_parameters[4] = {1, INT32_C(1073741824), 1, 0}; int32_t depthwise_parameters[4] = {1, INT32_C(1073741824), 1, 0};
rs_npu_p6_memory_t depthwise_memory = { rs_npu_p6_memory_t depthwise_memory = {
.arena = {.base = UINT32_C(0x1000), .bytes = sizeof(depthwise_arena), .data = depthwise_arena}, .arena = {.base = UINT32_C(0x1000),
.weights = .bytes = sizeof(depthwise_arena),
{.base = UINT32_C(0x3000), .bytes = sizeof(depthwise_weights), .data = depthwise_weights}, .data = depthwise_arena},
.weights = {.base = UINT32_C(0x3000),
.bytes = sizeof(depthwise_weights),
.data = depthwise_weights},
.params = .params =
{ {
.base = UINT32_C(0x2000), .base = UINT32_C(0x2000),
@@ -3030,7 +3059,9 @@ static int test_npu_p6_reference_edge_ops(void) {
{ {
uint8_t average_arena[16] = {1U, 2U, 3U, 4U}; uint8_t average_arena[16] = {1U, 2U, 3U, 4U};
rs_npu_p6_memory_t average_memory = { rs_npu_p6_memory_t average_memory = {
.arena = {.base = UINT32_C(0x4000), .bytes = sizeof(average_arena), .data = average_arena}, .arena = {.base = UINT32_C(0x4000),
.bytes = sizeof(average_arena),
.data = average_arena},
.weights = {.base = UINT32_C(0x5000), .bytes = 1U, .data = average_arena}, .weights = {.base = UINT32_C(0x5000), .bytes = 1U, .data = average_arena},
.params = {.base = UINT32_C(0x6000), .bytes = 1U, .data = average_arena}, .params = {.base = UINT32_C(0x6000), .bytes = 1U, .data = average_arena},
}; };
+40
View File
@@ -0,0 +1,40 @@
"""Materialize immutable, locally available publication test inputs.
This is a test fixture, never a publication promotion or a current-RTL verdict.
Each exported tree keeps its own adapter, technical contract, tests and lock.
"""
from __future__ import annotations
import io
import json
import subprocess
import tarfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
ORIGINAL_BOOK = "2805ea91833a6bede32f3a82df3d17d20eb5ec77"
P5_CONTRACT = "b3b30a72cd6ef5340c28e5044c96cd13258ec5a6"
def export(repository: Path, revision: str, destination: Path, paths: list[str] = ()) -> None:
data = subprocess.check_output(["git", "-C", str(repository), "archive", revision, *paths])
destination.mkdir(parents=True, exist_ok=True)
with tarfile.open(fileobj=io.BytesIO(data)) as archive:
for member in archive.getmembers():
target = (destination / member.name).resolve()
if not target.is_relative_to(destination.resolve()) or member.issym() or member.islnk():
raise ValueError("unsafe publication fixture archive member")
archive.extractall(destination)
def snapshot(destination: Path, revision: str) -> Path:
export(ROOT, revision, destination, ["publications", "scripts", "rtl", "crt", "app",
"configs", "docs", "dependencies", "LICENSE",
"Makefile", "README.md", "tests"])
lock = json.loads((destination / "dependencies/dependencies.lock.json").read_text())
for name in ("cluster_common", "cluster_archinfo", "cluster_pwm", "cluster_rtc",
"cluster_wdg", "hazard3"):
source = lock["sources"][name]
export(ROOT / source["destination"], source["revision"], destination / source["destination"],
["hdl"] if name == "hazard3" else [])
return destination
+25
View File
@@ -98,6 +98,28 @@ module i2s_tb;
end end
endtask endtask
task automatic zero_strobe_rx_write;
logic [31:0] before_status, after_status;
read(32'h8, before_status);
if (before_status[2]) $fatal(1, "missing RX data for direction test");
@(negedge clk_i);
apb4.paddr = 32'h20;
apb4.pwdata = 0;
apb4.pstrb = 0;
apb4.pwrite = 1;
apb4.psel = 1;
apb4.penable = 0;
@(negedge clk_i);
apb4.penable = 1;
while (!apb4.pready) @(negedge clk_i);
if (!apb4.pslverr) $fatal(1, "zero-strobe write became an RX read");
apb4.psel = 0;
apb4.penable = 0;
apb4.pwrite = 0;
read(32'h8, after_status);
if (before_status[23:16] != after_status[23:16]) $fatal(1, "invalid write popped RX FIFO");
endtask
always @(posedge i2s.sclk_o) sclk_edges = sclk_edges + 1; always @(posedge i2s.sclk_o) sclk_edges = sclk_edges + 1;
initial begin initial begin
@@ -132,6 +154,9 @@ module i2s_tb;
repeat (4000) @(posedge clk_aud_i); repeat (4000) @(posedge clk_aud_i);
if (sclk_edges == 0) $fatal(1, "I2S SCLK did not toggle"); if (sclk_edges == 0) $fatal(1, "I2S SCLK did not toggle");
read(32'h0000_0008, value); read(32'h0000_0008, value);
write(32'h0000_0000, 32'h0000_0000);
repeat (64) @(negedge clk_i);
zero_strobe_rx_write();
write(32'h0000_0000, 32'h0000_0008); write(32'h0000_0000, 32'h0000_0008);
write(32'h0000_0000, 32'h0000_0009); write(32'h0000_0000, 32'h0000_0009);
repeat (2000) @(posedge clk_aud_i); repeat (2000) @(posedge clk_aud_i);
+11 -1
View File
@@ -141,7 +141,17 @@ module sdio_axi_memory_responder #(
assign axi4.rresp = (s_read_error_q) ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY; assign axi4.rresp = (s_read_error_q) ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY;
assign axi4.rlast = s_read_bad_last_q || ((s_read_index_q + 1'b1) >= (s_read_len_q + 1'b1)); assign axi4.rlast = s_read_bad_last_q || ((s_read_index_q + 1'b1) >= (s_read_len_q + 1'b1));
assign axi4.ruser = '0; assign axi4.ruser = '0;
assign axi4.rvalid = s_read_active_q; integer s_read_gap = 0;
integer s_read_gap_count = 0;
task automatic set_read_gap(input integer cycles_i);
s_read_gap = cycles_i < 0 ? 0 : cycles_i;
endtask
always @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) s_read_gap_count <= 0;
else if (axi4.rvalid && axi4.rready) s_read_gap_count <= s_read_gap;
else if (s_read_gap_count != 0) s_read_gap_count <= s_read_gap_count - 1;
end
assign axi4.rvalid = s_read_active_q && (s_read_gap_count == 0);
assign axi4.awready = !s_write_active_q && !axi4.bvalid && !s_aw_stall_q; assign axi4.awready = !s_write_active_q && !axi4.bvalid && !s_aw_stall_q;
assign axi4.wready = s_write_active_q && !s_w_stall_q; assign axi4.wready = s_write_active_q && !s_w_stall_q;
+32 -2
View File
@@ -32,6 +32,8 @@ module sdio_command_tb;
integer launch_count; integer launch_count;
integer captured_bits; integer captured_bits;
logic [ 47:0] captured_command; logic [ 47:0] captured_command;
tri1 command_pad;
assign command_pad = cmd_oe_o ? cmd_do_o : 1'bz;
always #1 clk_i = ~clk_i; always #1 clk_i = ~clk_i;
@@ -77,8 +79,8 @@ module sdio_command_tb;
end end
always @(posedge sck) begin always @(posedge sck) begin
if (cmd_oe_o) begin if (captured_bits < 48 && (captured_bits != 0 || command_pad == 1'b0)) begin
captured_command = {captured_command[46:0], cmd_do_o}; captured_command = {captured_command[46:0], command_pad};
captured_bits = captured_bits + 1; captured_bits = captured_bits + 1;
end end
end end
@@ -95,6 +97,7 @@ module sdio_command_tb;
start_i = 1'b0; start_i = 1'b0;
clock_enable_i = 1'b1; clock_enable_i = 1'b1;
wait (done_o); wait (done_o);
@(negedge clk_i);
if (error_o || (launch_count != 48) || (captured_bits != 48) || if (error_o || (launch_count != 48) || (captured_bits != 48) ||
(captured_command[47:46] != 2'b01) || (captured_command[0] != 1'b1) || (captured_command[47:46] != 2'b01) || (captured_command[0] != 1'b1) ||
(captured_command[7:1] != sdio_pkg::sdio_crc7_calc( (captured_command[7:1] != sdio_pkg::sdio_crc7_calc(
@@ -104,6 +107,33 @@ module sdio_command_tb;
error_o, launch_count, captured_bits, captured_command, cmd_oe_o); error_o, launch_count, captured_bits, captured_command, cmd_oe_o);
end end
// A running SDCLK can accept a command on either half-cycle. The
// stopped-clock first command above does not exercise this boundary.
for (integer phase = 0; phase < 16; phase++) begin
repeat (phase % 3 + 3) @(negedge clk_i);
captured_bits = 0;
captured_command = '0;
cmd_index_i = 6'd8;
cmd_arg_i = 32'h1aa ^ 32'(phase);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
wait (done_o);
@(negedge clk_i);
if (error_o || captured_bits != 48 || captured_command[47:40] != 8'h48 ||
captured_command[39:8] != cmd_arg_i || captured_command[0] != 1'b1 ||
captured_command[7:1] != sdio_pkg::sdio_crc7_calc(
captured_command[47:8]
))
$fatal(
1,
"SDIO running-clock frame phase=%0d bits=%0d frame=%h",
phase,
captured_bits,
captured_command
);
end
@(posedge clk_i); @(posedge clk_i);
resp_type_i = sdio_pkg::SdioRespR1; resp_type_i = sdio_pkg::SdioRespR1;
timeout_cycles_i = 32'd4; timeout_cycles_i = 32'd4;
+104 -71
View File
@@ -14,13 +14,14 @@
// implements only public command/data behavior exercised by this repository; // implements only public command/data behavior exercised by this repository;
// it is not a normative-specification replacement. // it is not a normative-specification replacement.
module sdio_native_card_model #( module sdio_native_card_model #(
parameter bit SdioOnly = 1'b0, parameter bit MemoryInitialization = 1'b0,
parameter bit HighCapacity = 1'b1, parameter bit SdioOnly = 1'b0,
parameter int BlockBytes = 512, parameter bit HighCapacity = 1'b1,
parameter int BlockCount = 128, parameter int BlockBytes = 512,
parameter int FunctionCount = 1, parameter int BlockCount = 128,
parameter int FunctionBytes = 4096, parameter int FunctionCount = 1,
parameter int BusyCycles = 2 parameter int FunctionBytes = 4096,
parameter int BusyCycles = 2
) ( ) (
input logic rst_n_i, input logic rst_n_i,
input logic sck_i, input logic sck_i,
@@ -48,60 +49,63 @@ module sdio_native_card_model #(
WriteBusy WriteBusy
} data_state_e; } data_state_e;
logic [ 7:0] s_memory [ 0:MemoryBytes-1]; logic [7:0] s_memory[0:MemoryBytes-1];
logic [ 7:0] s_function_memory [0:FunctionStorageBytes-1]; logic [7:0] s_function_memory[0:FunctionStorageBytes-1];
logic [ 47:0] s_cmd_shift; logic [47:0] s_cmd_shift;
integer s_cmd_bits; integer s_cmd_bits;
logic s_cmd_response_active; logic s_cmd_response_active;
logic [135:0] s_cmd_response; logic [135:0] s_cmd_response;
integer s_cmd_response_length; integer s_cmd_response_length;
integer s_cmd_response_index; integer s_cmd_response_index;
integer s_cmd_response_delay; integer s_cmd_response_delay;
integer s_response_delay; integer s_response_delay;
integer s_data_delay_cfg; integer s_data_delay_cfg;
logic s_inject_crc_error; logic s_inject_crc_error;
logic s_inject_timeout; logic s_inject_timeout;
logic s_inject_write_error; logic s_inject_write_error;
logic s_next_cmd_crc_error; logic s_next_cmd_crc_error;
logic [ 5:0] s_last_cmd; logic [5:0] s_last_cmd;
logic [ 31:0] s_last_arg; logic [31:0] s_last_arg;
logic s_last_cmd_write; logic s_last_cmd_write;
logic s_last_cmd_data; logic s_last_cmd_data;
logic s_pending_busy; logic s_memory_width4;
integer s_busy_count; logic s_pending_busy;
integer s_busy_count;
data_state_e s_data_state; data_state_e s_data_state;
logic s_data_width4; logic s_data_width4;
logic s_data_fixed; logic s_data_fixed;
integer s_data_address; integer s_data_address;
integer s_data_bytes; integer s_data_bytes;
integer s_data_byte_index; wire [3:0] s_token_lanes = !s_data_width4 ? 4'b0001 :
integer s_data_bit_index; (s_data_bytes >= 4 ? 4'b1111 : (4'b0001 << s_data_bytes) - 1'b1);
integer s_data_nibble_index; integer s_data_byte_index;
integer s_data_crc_index; integer s_data_bit_index;
integer s_data_delay; integer s_data_nibble_index;
logic [ 15:0] s_read_crc; integer s_data_crc_index;
logic [ 15:0] s_read_crc_lane [ 0:3]; integer s_data_delay;
logic [ 15:0] s_write_crc; logic [15:0] s_read_crc;
logic [ 15:0] s_write_crc_lane [ 0:3]; logic [15:0] s_read_crc_lane[0:3];
logic [ 15:0] s_received_crc; logic [15:0] s_write_crc;
logic [ 15:0] s_received_crc_lane [ 0:3]; logic [15:0] s_write_crc_lane[0:3];
logic [ 7:0] s_write_byte; logic [15:0] s_received_crc;
logic [ 7:0] s_write_byte_lane [ 0:3]; logic [15:0] s_received_crc_lane[0:3];
integer s_write_bit_count; logic [7:0] s_write_byte;
integer s_write_nibble_count; logic [7:0] s_write_byte_lane[0:3];
logic [ 4:0] s_write_response_token; integer s_write_bit_count;
integer s_write_response_index; integer s_write_nibble_count;
logic s_write_response_started; logic [4:0] s_write_response_token;
logic s_data_crc_error; integer s_write_response_index;
logic s_data_timeout; logic s_write_response_started;
logic s_irq_pending; logic s_data_crc_error;
logic [ 2:0] s_cmd_function; logic s_data_timeout;
logic [ 31:0] s_cmd_address; logic s_irq_pending;
logic s_cmd_fixed_address; logic [2:0] s_cmd_function;
integer s_cmd_count; logic [31:0] s_cmd_address;
logic s_cmd_fixed_address;
integer s_cmd_count;
function automatic logic [6:0] crc7_40(input logic [39:0] data_i); function automatic logic [6:0] crc7_40(input logic [39:0] data_i);
logic [6:0] crc; logic [6:0] crc;
@@ -404,17 +408,20 @@ module sdio_native_card_model #(
endtask endtask
task automatic command_received(input logic [47:0] frame_i); task automatic command_received(input logic [47:0] frame_i);
logic [ 5:0] index; logic [ 5:0] index;
logic [31:0] argument; logic [ 31:0] argument;
logic [ 6:0] expected_crc; logic [ 6:0] expected_crc;
logic [39:0] body; logic [ 39:0] body;
logic [31:0] response_payload; logic [ 31:0] response_payload;
integer address; logic [127:0] memory_csd_body;
logic application_command;
integer address;
begin begin
index = frame_i[45:40]; index = frame_i[45:40];
argument = frame_i[39:8]; argument = frame_i[39:8];
body = frame_i[47:8]; body = frame_i[47:8];
expected_crc = crc7_40(body); expected_crc = crc7_40(body);
application_command = s_last_cmd == 6'd55;
s_last_cmd = index; s_last_cmd = index;
s_last_arg = argument; s_last_arg = argument;
s_last_cmd_data = 1'b0; s_last_cmd_data = 1'b0;
@@ -471,7 +478,15 @@ module sdio_native_card_model #(
cmd_do_o = 1'b1; cmd_do_o = 1'b1;
end end
6'd2, 6'd9: begin 6'd2, 6'd9: begin
make_r2_response(index == 6'd9 ? {HighCapacity ? 1'b0 : 1'b0, 127'd0} : 128'd0); memory_csd_body = '0;
if (MemoryInitialization && index == 6'd9) begin
// The 136-bit wire frame holds the CSD at [127:0], including
// its CRC/end byte. make_r2_response appends that final byte.
memory_csd_body[119:118] = 2'd1;
memory_csd_body[75:72] = 4'd9;
memory_csd_body[61:40] = 22'(BlockCount / 1024 - 1);
end
make_r2_response(memory_csd_body);
s_cmd_response_active = 1'b1; s_cmd_response_active = 1'b1;
s_cmd_response_index = 0; s_cmd_response_index = 0;
s_cmd_response_delay = s_response_delay; s_cmd_response_delay = s_response_delay;
@@ -492,6 +507,15 @@ module sdio_native_card_model #(
s_busy_count = BusyCycles; s_busy_count = BusyCycles;
end end
6'd16, 6'd55, 6'd41, 6'd6, 6'd13: begin 6'd16, 6'd55, 6'd41, 6'd6, 6'd13: begin
if (MemoryInitialization) begin
if (index == 6'd41 && application_command) response_payload = 32'hc0ff8000;
if (index == 6'd6) begin
if (application_command) s_memory_width4 = argument == 2;
// This deterministic card supports default speed only.
else
response_payload = 32'h00400000;
end
end
queue_response(index, response_payload, 1'b0, s_inject_crc_error); queue_response(index, response_payload, 1'b0, s_inject_crc_error);
s_inject_crc_error = 1'b0; s_inject_crc_error = 1'b0;
end end
@@ -654,9 +678,10 @@ module sdio_native_card_model #(
s_last_arg = 32'd0; s_last_arg = 32'd0;
s_last_cmd_write = 1'b0; s_last_cmd_write = 1'b0;
s_last_cmd_data = 1'b0; s_last_cmd_data = 1'b0;
s_memory_width4 = 1'b0;
s_pending_busy = 1'b0; s_pending_busy = 1'b0;
s_busy_count = 0; s_busy_count = 0;
end else if (cmd_oe_i) begin end else if ((s_cmd_bits != 0) || (cmd_oe_i && cmd_do_i == 1'b0)) begin
if (s_cmd_bits == 0) begin if (s_cmd_bits == 0) begin
s_cmd_shift = {{47{1'b0}}, cmd_do_i}; s_cmd_shift = {{47{1'b0}}, cmd_do_i};
s_cmd_bits = 1; s_cmd_bits = 1;
@@ -717,6 +742,7 @@ module sdio_native_card_model #(
s_last_arg = 32'd0; s_last_arg = 32'd0;
s_last_cmd_write = 1'b0; s_last_cmd_write = 1'b0;
s_last_cmd_data = 1'b0; s_last_cmd_data = 1'b0;
s_memory_width4 = 1'b0;
s_pending_busy = 1'b0; s_pending_busy = 1'b0;
s_busy_count = 0; s_busy_count = 0;
s_data_state = DataIdle; s_data_state = DataIdle;
@@ -828,7 +854,7 @@ module sdio_native_card_model #(
dat_do_o = 4'b1111; dat_do_o = 4'b1111;
for (integer lane = 0; lane < 4; lane++) begin for (integer lane = 0; lane < 4; lane++) begin
if (lane < s_data_bytes) begin if (lane < s_data_bytes) begin
dat_do_o[lane] = s_read_crc_lane[lane][15-(s_data_crc_index+1)]; dat_do_o[lane] = s_read_crc_lane[lane][15-s_data_crc_index];
end end
end end
end end
@@ -838,7 +864,7 @@ module sdio_native_card_model #(
end else begin end else begin
s_data_crc_index = s_data_crc_index + 1; s_data_crc_index = s_data_crc_index + 1;
dat_do_o = 4'b1111; dat_do_o = 4'b1111;
dat_do_o[0] = s_read_crc[15-(s_data_crc_index+1)]; dat_do_o[0] = s_read_crc[15-s_data_crc_index];
end end
end end
WriteResponse: begin WriteResponse: begin
@@ -850,7 +876,9 @@ module sdio_native_card_model #(
s_write_response_index = s_write_response_index + 1; s_write_response_index = s_write_response_index + 1;
dat_do_o[0] = s_write_response_token[4-s_write_response_index]; dat_do_o[0] = s_write_response_token[4-s_write_response_index];
end else begin end else begin
s_data_state = (s_write_response_token == 5'b00101) ? WriteBusy : DataIdle; // Keep the enum assignment portable to Icarus's native SV parser.
if (s_write_response_token == 5'b00101) s_data_state = WriteBusy;
else s_data_state = DataIdle;
s_busy_count = BusyCycles; s_busy_count = BusyCycles;
dat_do_o[0] = (s_write_response_token == 5'b00101) ? 1'b0 : 1'b1; dat_do_o[0] = (s_write_response_token == 5'b00101) ? 1'b0 : 1'b1;
end end
@@ -879,7 +907,7 @@ module sdio_native_card_model #(
end else begin end else begin
case (s_data_state) case (s_data_state)
WriteWait: begin WriteWait: begin
if ((dat_oe_i != 4'b0000) && (dat_do_i == 4'b0000)) begin if ((dat_oe_i != 4'b0000) && ((dat_do_i & s_token_lanes) == 4'b0000)) begin
s_data_state = WritePayload; s_data_state = WritePayload;
s_write_bit_count = 0; s_write_bit_count = 0;
s_write_nibble_count = 0; s_write_nibble_count = 0;
@@ -903,7 +931,8 @@ module sdio_native_card_model #(
s_data_crc_error = 1'b0; s_data_crc_error = 1'b0;
if (s_data_width4) begin if (s_data_width4) begin
for (integer lane = 0; lane < 4; lane++) begin for (integer lane = 0; lane < 4; lane++) begin
if (s_received_crc_lane[lane] != s_write_crc_lane[lane]) begin if ((s_data_bytes % 4 == 0 || lane < s_data_bytes % 4) &&
s_received_crc_lane[lane] != s_write_crc_lane[lane]) begin
s_data_crc_error = 1'b1; s_data_crc_error = 1'b1;
end end
end end
@@ -938,6 +967,9 @@ module sdio_native_card_model #(
end end
initial begin initial begin
if (MemoryInitialization && (!HighCapacity || SdioOnly || BlockBytes != 512 ||
BlockCount < 1024 || BlockCount % 1024 != 0))
$fatal(1, "SDIO enumeration profile requires an exactly representable SDHC capacity");
if ((BlockBytes < 1) || (BlockCount < 1) || (FunctionCount < 1) || (FunctionBytes < 256)) begin if ((BlockBytes < 1) || (BlockCount < 1) || (FunctionCount < 1) || (FunctionBytes < 256)) begin
$fatal(1, "sdio_native_card_model: invalid storage geometry"); $fatal(1, "sdio_native_card_model: invalid storage geometry");
end end
@@ -955,6 +987,7 @@ module sdio_native_card_model #(
s_last_arg = 32'd0; s_last_arg = 32'd0;
s_last_cmd_write = 1'b0; s_last_cmd_write = 1'b0;
s_last_cmd_data = 1'b0; s_last_cmd_data = 1'b0;
s_memory_width4 = 1'b0;
s_pending_busy = 1'b0; s_pending_busy = 1'b0;
s_busy_count = 0; s_busy_count = 0;
s_data_state = DataIdle; s_data_state = DataIdle;
+113 -7
View File
@@ -12,7 +12,9 @@
module sdio_standalone_tb #( module sdio_standalone_tb #(
parameter bit CardHighCapacity = 1'b1, parameter bit CardHighCapacity = 1'b1,
parameter bit RunFourBit = 1'b1 parameter bit RunFourBit = 1'b1,
parameter bit PadLoopback = 1'b0,
parameter int CardBusyCycles = 8
); );
localparam logic [31:0] HostCtrl = 32'h00C; localparam logic [31:0] HostCtrl = 32'h00C;
localparam logic [31:0] ClockCtrl = 32'h010; localparam logic [31:0] ClockCtrl = 32'h010;
@@ -65,8 +67,21 @@ module sdio_standalone_tb #(
integer timeout_count_before; integer timeout_count_before;
integer crc_count_before; integer crc_count_before;
assign sdio.cmd_di_i = sdio.cmd_oe_o ? sdio.cmd_do_o : card_cmd_do; tri1 command_pad, data0_pad, data1_pad, data2_pad, data3_pad;
assign sdio.dat_di_i = (sdio.dat_oe_o & sdio.dat_do_o) | (~sdio.dat_oe_o & card_dat_do); wire [3:0] data_pads = {data3_pad, data2_pad, data1_pad, data0_pad};
assign command_pad = sdio.cmd_oe_o ? sdio.cmd_do_o : 1'bz;
assign command_pad = sdio.cmd_oe_o ? 1'bz : card_cmd_do;
assign data0_pad = sdio.dat_oe_o[0] ? sdio.dat_do_o[0] : 1'bz;
assign data1_pad = sdio.dat_oe_o[1] ? sdio.dat_do_o[1] : 1'bz;
assign data2_pad = sdio.dat_oe_o[2] ? sdio.dat_do_o[2] : 1'bz;
assign data3_pad = sdio.dat_oe_o[3] ? sdio.dat_do_o[3] : 1'bz;
assign data0_pad = sdio.dat_oe_o[0] ? 1'bz : card_dat_do[0];
assign data1_pad = sdio.dat_oe_o[1] ? 1'bz : card_dat_do[1];
assign data2_pad = sdio.dat_oe_o[2] ? 1'bz : card_dat_do[2];
assign data3_pad = sdio.dat_oe_o[3] ? 1'bz : card_dat_do[3];
assign sdio.cmd_di_i = PadLoopback ? command_pad : (sdio.cmd_oe_o ? sdio.cmd_do_o : card_cmd_do);
assign sdio.dat_di_i = PadLoopback ? data_pads :
((sdio.dat_oe_o & sdio.dat_do_o) | (~sdio.dat_oe_o & card_dat_do));
always #5 clk_i = ~clk_i; always #5 clk_i = ~clk_i;
@@ -87,15 +102,15 @@ module sdio_standalone_tb #(
.HighCapacity(CardHighCapacity), .HighCapacity(CardHighCapacity),
.BlockBytes (512), .BlockBytes (512),
.BlockCount (128), .BlockCount (128),
.BusyCycles (8) .BusyCycles (CardBusyCycles)
) u_card ( ) u_card (
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.sck_i (sdio.sck_o), .sck_i (sdio.sck_o),
.cmd_oe_i(sdio.cmd_oe_o), .cmd_oe_i(sdio.cmd_oe_o),
.cmd_do_i(sdio.cmd_do_o), .cmd_do_i(PadLoopback ? command_pad : sdio.cmd_do_o),
.cmd_do_o(card_cmd_do), .cmd_do_o(card_cmd_do),
.dat_oe_i(sdio.dat_oe_o), .dat_oe_i(sdio.dat_oe_o),
.dat_do_i(sdio.dat_do_o), .dat_do_i(PadLoopback ? data_pads : sdio.dat_do_o),
.dat_do_o(card_dat_do), .dat_do_o(card_dat_do),
.irq_o (card_irq) .irq_o (card_irq)
); );
@@ -204,7 +219,13 @@ module sdio_standalone_tb #(
end end
end end
if (timeout_guard >= 200000) begin if (timeout_guard >= 200000) begin
$fatal(1, "data path did not drain"); $fatal(
1,
"data path did not drain: data_state=%0d dma_state=%0d rx=%b%b dma_bytes=%0d card_state=%0d card_bytes=%0d clk=%b",
u_apb4_sdio.u_sdio_core.u_sdio_data.s_state_q,
u_apb4_sdio.u_sdio_core.u_sdio_dma.s_state_q, u_apb4_sdio.u_sdio_core.s_data_rx_valid,
u_apb4_sdio.u_sdio_core.s_data_rx_ready, u_apb4_sdio.s_bytes_done,
u_card.u_model.s_data_state, u_card.u_model.s_data_byte_index, sdio.sck_o);
end end
end end
endtask endtask
@@ -552,6 +573,84 @@ module sdio_standalone_tb #(
$fatal(1, "memory-to-card DMA end-to-end direction failed"); $fatal(1, "memory-to-card DMA end-to-end direction failed");
end end
// P6 exercises a full four-lane DMA payload at divider 1, with memory
// arbitration delays. Short one-lane tails do not cover its word pauses.
if (RunFourBit) begin
u_axi_memory.clear_memory();
u_axi_memory.set_read_gap(24);
u_axi_memory.write_word(32'h0, 32'h200);
u_axi_memory.write_word(32'h4, 32'd1024);
u_axi_memory.write_word(32'h8, 32'd0);
u_axi_memory.write_word(32'hc, 32'h5);
for (integer word_index = 0; word_index < 256; word_index++)
u_axi_memory.write_word(32'h200 + 32'(word_index * 4), 32'h13579bdf ^ 32'(word_index));
configure_dma_data(1'b1, 1'b1, 16'd512);
apb_write(BlockCount, 32'd2, 4'hf);
u_card.arm_write(24000, 1024, 1'b1, 1'b0);
apb_write(DataStart, 32'd1, 4'h1);
wait_data();
apb_read(DataStatus, apb_value);
for (integer byte_index = 0; byte_index < 1024; byte_index++) begin
if (u_card.read_backing(
24000 + byte_index
) != 8'((32'h13579bdf ^ 32'(byte_index / 4)) >> ((byte_index % 4) * 8)))
$fatal(
1,
"SDIO four-lane DMA payload byte=%0d got=%h expected=%h",
byte_index,
u_card.read_backing(
24000 + byte_index
),
8'((32'h13579bdf ^ 32'(byte_index / 4)) >> ((byte_index % 4) * 8))
);
end
if ((apb_value & 32'h1e) != 0)
$fatal(
1,
"SDIO four-lane DMA block status=%h card_crc=%b",
apb_value,
u_card.u_model.s_data_crc_error
);
if (u_axi_memory.read_word(32'hc) != 32'h00010004)
$fatal(1, "SDIO four-lane DMA descriptor did not retire");
// Delay descriptor reads while the card sends its first receive word.
u_axi_memory.clear_memory();
u_axi_memory.write_word(32'h0, 32'h800);
u_axi_memory.write_word(32'h4, 32'd1024);
u_axi_memory.write_word(32'h8, 32'd0);
u_axi_memory.write_word(32'hc, 32'h5);
configure_dma_data(1'b1, 1'b0, 16'd512);
apb_write(BlockCount, 32'd2, 4'hf);
u_card.arm_read(24000, 1024, 1'b1, 1'b0);
apb_write(DataStart, 32'd1, 4'h1);
wait_data();
apb_read(DataStatus, apb_value);
for (integer word_index = 0; word_index < 256; word_index++) begin
if (u_axi_memory.read_word(
32'h800 + 32'(word_index * 4)
) != (32'h13579bdf ^ 32'(word_index)))
$fatal(
1,
"SDIO delayed-descriptor receive word=%0d got=%h expected=%h",
word_index,
u_axi_memory.read_word(
32'h800 + 32'(word_index * 4)
),
32'h13579bdf ^ 32'(word_index)
);
end
if ((apb_value & 32'h1e) != 0 || u_axi_memory.read_word(32'hc) != 32'h00010004)
$fatal(
1,
"SDIO delayed-descriptor receive status=%h descriptor=%h",
apb_value,
u_axi_memory.read_word(
32'hc
)
);
u_axi_memory.set_read_gap(0);
end
apb_read(AxiCount, apb_value); apb_read(AxiCount, apb_value);
timeout_count_before = apb_value; timeout_count_before = apb_value;
u_axi_memory.clear_memory(); u_axi_memory.clear_memory();
@@ -675,3 +774,10 @@ endmodule
module sdio_onebit_tb; module sdio_onebit_tb;
sdio_standalone_tb #(.RunFourBit(1'b0)) u_tb (); sdio_standalone_tb #(.RunFourBit(1'b0)) u_tb ();
endmodule endmodule
module tiny_p6_sdio_pads_tb;
sdio_standalone_tb #(
.PadLoopback (1'b1),
.CardBusyCycles(2)
) u_tb ();
endmodule
+9 -8
View File
@@ -1,8 +1,9 @@
`include "tiny_topology.svh"
`timescale 1ns / 1ps `timescale 1ns / 1ps
module tiny_axi4_fabric_tb; module tiny_axi4_fabric_tb;
logic clk_i = 0; logic clk_i = 0;
logic rst_n_i = 0; logic rst_n_i = 0;
logic target_rst_n[23]; logic target_rst_n[`RETROSOC_TINY__TARGET_COUNT];
always #5 clk_i = !clk_i; always #5 clk_i = !clk_i;
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
@@ -18,7 +19,7 @@ module tiny_axi4_fabric_tb;
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(1)
) targets[23] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
); );
@@ -27,8 +28,8 @@ module tiny_axi4_fabric_tb;
logic [5:0][ 1:0] fault_resp; logic [5:0][ 1:0] fault_resp;
logic [63:0] cpu_wait, dma_wait; logic [63:0] cpu_wait, dma_wait;
integer faults = 0; integer faults = 0;
integer completions [ 3]; integer completions [ 3];
integer target_requests[23]; integer target_requests[`RETROSOC_TINY__TARGET_COUNT];
logic [2:0][31:0] awaddr; logic [2:0][31:0] awaddr;
logic [2:0][ 7:0] awlen; logic [2:0][ 7:0] awlen;
logic [2:0][ 2:0] awsize; logic [2:0][ 2:0] awsize;
@@ -78,7 +79,7 @@ module tiny_axi4_fabric_tb;
.cpu_wait_o (cpu_wait), .cpu_wait_o (cpu_wait),
.dma_wait_o (dma_wait) .dma_wait_o (dma_wait)
); );
for (genvar target = 0; target < 23; target++) begin : gen_target_reset for (genvar target = 0; target < `RETROSOC_TINY__TARGET_COUNT; target++) begin : gen_target_reset
assign target_rst_n[target] = rst_n_i; assign target_rst_n[target] = rst_n_i;
end end
always @(posedge clk_i) faults <= faults + $countones(fault_valid); always @(posedge clk_i) faults <= faults + $countones(fault_valid);
@@ -126,11 +127,11 @@ module tiny_axi4_fabric_tb;
assign masters[m].aruser = '0; assign masters[m].aruser = '0;
assign masters[m].wuser = '0; assign masters[m].wuser = '0;
end end
for (genvar t = 0; t < 23; t++) begin : gen_target for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_target
// Deterministic terminal responder allows independent checks of routing, // Deterministic terminal responder allows independent checks of routing,
// burst length and ID preservation without sharing the fabric decoder. // burst length and ID preservation without sharing the fabric decoder.
axi4_error_slave #( axi4_error_slave #(
.Response(t == 21 ? 2'b11 : (t == 22 ? 2'b10 : 2'b00)) .Response(t == `RETROSOC_TINY__DECODE_ERROR ? 2'b11 : (t == `RETROSOC_TINY__PROTOCOL_ERROR ? 2'b10 : 2'b00))
) u_response ( ) u_response (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(rst_n_i), .rst_n_i(rst_n_i),
@@ -273,7 +274,7 @@ module tiny_axi4_fabric_tb;
join join
if (completions[0]!=7 || completions[1]!=6 || faults!=7 || if (completions[0]!=7 || completions[1]!=6 || faults!=7 ||
target_requests[0]!=2 || target_requests[3]!=1 || target_requests[4]!=1 || target_requests[0]!=2 || target_requests[3]!=1 || target_requests[4]!=1 ||
target_requests[6]!=2 || target_requests[21]!=3 || target_requests[22]!=4) target_requests[6]!=2 || target_requests[`RETROSOC_TINY__DECODE_ERROR]!=3 || target_requests[`RETROSOC_TINY__PROTOCOL_ERROR]!=4)
$fatal(1, "accounting/fairness mismatch faults=%0d", faults); $fatal(1, "accounting/fairness mismatch faults=%0d", faults);
read_request(0, 32'h30000000, 1, 2, 1, 0); read_request(0, 32'h30000000, 1, 2, 1, 0);
arprot[0] = 3'b100; arprot[0] = 3'b100;
+14 -12
View File
@@ -1,8 +1,9 @@
`include "tiny_topology.svh"
`timescale 1ns / 1ps `timescale 1ns / 1ps
module tiny_r2_p5_fabric_tb; module tiny_r2_p5_fabric_tb;
logic clk_i = 0; logic clk_i = 0;
logic rst_n_i = 0; logic rst_n_i = 0;
logic target_rst_n[23]; logic target_rst_n[`RETROSOC_TINY__TARGET_COUNT];
always #5 clk_i = !clk_i; always #5 clk_i = !clk_i;
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
@@ -18,7 +19,7 @@ module tiny_r2_p5_fabric_tb;
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(1)
) targets[23] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(rst_n_i) .aresetn(rst_n_i)
); );
@@ -27,14 +28,15 @@ module tiny_r2_p5_fabric_tb;
logic [5:0][ 1:0] fault_resp; logic [5:0][ 1:0] fault_resp;
logic [63:0] cpu_wait, dma_wait; logic [63:0] cpu_wait, dma_wait;
integer faults = 0; integer faults = 0;
logic [22:0] hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0; logic [`RETROSOC_TINY__TARGET_COUNT-1:0]
integer error_beat [23]; hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0;
integer error_beat [`RETROSOC_TINY__TARGET_COUNT];
logic [ 2:0] error_last_wait = '0; logic [ 2:0] error_last_wait = '0;
logic release_error = 0; logic release_error = 0;
integer simultaneous_faults = 0; integer simultaneous_faults = 0;
logic [31:0] seed = 32'h12345678; logic [31:0] seed = 32'h12345678;
integer completions [ 3]; integer completions [ 3];
integer target_requests [23]; integer target_requests [`RETROSOC_TINY__TARGET_COUNT];
logic [ 2:0][31:0] awaddr; logic [ 2:0][31:0] awaddr;
logic [ 2:0][ 7:0] awlen; logic [ 2:0][ 7:0] awlen;
logic [ 2:0][ 2:0] awsize; logic [ 2:0][ 2:0] awsize;
@@ -84,7 +86,7 @@ module tiny_r2_p5_fabric_tb;
.cpu_wait_o (cpu_wait), .cpu_wait_o (cpu_wait),
.dma_wait_o (dma_wait) .dma_wait_o (dma_wait)
); );
for (genvar target = 0; target < 23; target++) begin : gen_target_reset for (genvar target = 0; target < `RETROSOC_TINY__TARGET_COUNT; target++) begin : gen_target_reset
assign target_rst_n[target] = rst_n_i; assign target_rst_n[target] = rst_n_i;
end end
always @(posedge clk_i) begin always @(posedge clk_i) begin
@@ -140,9 +142,9 @@ module tiny_r2_p5_fabric_tb;
assign masters[m].aruser = '0; assign masters[m].aruser = '0;
assign masters[m].wuser = '0; assign masters[m].wuser = '0;
end end
for (genvar t = 0; t < 23; t++) begin : gen_target for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_target
tiny_p5_target_model #( tiny_p5_target_model #(
.ErrorResponse(t == 21 ? 3 : (t == 22 ? 2 : 0)) .ErrorResponse(t == `RETROSOC_TINY__DECODE_ERROR ? 3 : (t == `RETROSOC_TINY__PROTOCOL_ERROR ? 2 : 0))
) u_response ( ) u_response (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
@@ -346,13 +348,13 @@ module tiny_r2_p5_fabric_tb;
end end
join join
// A waiting APB FIFO branch cannot block independent control admission. // A waiting APB FIFO branch cannot block independent control admission.
hold_read[12] = 1; hold_read[`RETROSOC_TINY__TARGET_XPI] = 1;
fork fork
read_request(1, 32'h10009000, 1, 2, 1, 0); read_request(1, 32'h10009000, 1, 2, 1, 0);
begin begin
wait (!u_dut.target_idle_o[12]); wait (!u_dut.target_idle_o[`RETROSOC_TINY__TARGET_XPI]);
read_request(0, 32'h1000a000, 1, 2, 1, 0); read_request(0, 32'h1000a000, 1, 2, 1, 0);
hold_read[12] = 0; hold_read[`RETROSOC_TINY__TARGET_XPI] = 0;
end end
join join
fork fork
+30 -25
View File
@@ -1,8 +1,9 @@
`include "tiny_topology.svh"
`timescale 1ns / 1ps `timescale 1ns / 1ps
module tiny_r2_p5_reset_tb; module tiny_r2_p5_reset_tb;
logic clk_i = 0, rst_n_i = 0; logic clk_i = 0, rst_n_i = 0;
always #5 clk_i = !clk_i; always #5 clk_i = !clk_i;
logic target_rst_n[23]; logic target_rst_n[`RETROSOC_TINY__TARGET_COUNT];
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
@@ -17,22 +18,26 @@ module tiny_r2_p5_reset_tb;
.DATA_WIDTH(32), .DATA_WIDTH(32),
.ID_WIDTH (1), .ID_WIDTH (1),
.USER_WIDTH(1) .USER_WIDTH(1)
) targets[23] ( ) targets[`RETROSOC_TINY__TARGET_COUNT] (
.aclk (clk_i), .aclk (clk_i),
.aresetn(target_rst_n) .aresetn(target_rst_n)
); );
apb4_if apb ( apb4_if apb (
.pclk (clk_i), .pclk (clk_i),
.presetn(target_rst_n[12]) .presetn(target_rst_n[`RETROSOC_TINY__TARGET_XPI])
); );
logic [22:0] hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0; logic [`RETROSOC_TINY__TARGET_COUNT-1:0]
integer error_beat[23]; hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0;
integer captures[23], addresses[23], writes[23]; integer error_beat[`RETROSOC_TINY__TARGET_COUNT];
integer
captures[`RETROSOC_TINY__TARGET_COUNT],
addresses[`RETROSOC_TINY__TARGET_COUNT],
writes[`RETROSOC_TINY__TARGET_COUNT];
logic [5:0] fault_valid, fault_instruction; logic [5:0] fault_valid, fault_instruction;
logic [ 5:0][31:0] fault_addr; logic [ 5:0][31:0] fault_addr;
logic [ 5:0][ 1:0] fault_resp; logic [ 5:0][ 1:0] fault_resp;
logic [ 2:0] owner_idle; logic [ 2:0] owner_idle;
logic [22:0] target_idle; logic [`RETROSOC_TINY__TARGET_COUNT-1:0] target_idle;
logic [63:0] cpu_wait, dma_wait; logic [63:0] cpu_wait, dma_wait;
logic [2:0] arvalid = 0, awvalid = 0, wvalid = 0, rready = 0, bready = 0; logic [2:0] arvalid = 0, awvalid = 0, wvalid = 0, rready = 0, bready = 0;
logic [2:0][31:0] araddr = 0, awaddr = 0, wdata = 0; logic [2:0][31:0] araddr = 0, awaddr = 0, wdata = 0;
@@ -200,8 +205,8 @@ module tiny_r2_p5_reset_tb;
end end
end end
for (genvar t = 0; t < 23; t++) begin : gen_target for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_target
if (t == 12) begin : gen_apb if (t == `RETROSOC_TINY__TARGET_XPI) begin : gen_apb
tiny_axi42apb4 u_bridge ( tiny_axi42apb4 u_bridge (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i(target_rst_n[t]), .rst_n_i(target_rst_n[t]),
@@ -232,8 +237,8 @@ module tiny_r2_p5_reset_tb;
end end
end end
end end
assign apb.pready = apb.psel && apb.penable && !hold_read[12] && !hold_b[12]; assign apb.pready = apb.psel && apb.penable && !hold_read[`RETROSOC_TINY__TARGET_XPI] && !hold_b[`RETROSOC_TINY__TARGET_XPI];
assign apb.prdata = 32'hcafe_f00d; assign apb.prdata = 32'hcafe_f00d;
assign apb.pslverr = 0; assign apb.pslverr = 0;
always @(posedge clk_i) begin always @(posedge clk_i) begin
if (rst_n_i) begin if (rst_n_i) begin
@@ -249,7 +254,7 @@ module tiny_r2_p5_reset_tb;
task automatic quiet; task automatic quiet;
repeat (4) @(negedge clk_i); repeat (4) @(negedge clk_i);
if (owner_idle != 7 || target_idle != 23'h7fffff || rvalid != 0 || bvalid != 0) if (owner_idle != 7 || target_idle != {`RETROSOC_TINY__TARGET_COUNT{1'b1}} || rvalid != 0 || bvalid != 0)
$fatal(1, "credit/response not retired"); $fatal(1, "credit/response not retired");
endtask endtask
task automatic receive_read(input int index, input int beats, input logic [1:0] expected, task automatic receive_read(input int index, input int beats, input logic [1:0] expected,
@@ -310,7 +315,7 @@ module tiny_r2_p5_reset_tb;
end end
for (integer n = 0; n < beats; n++) begin for (integer n = 0; n < beats; n++) begin
expected = inject_reset && n >= prefix ? 3 : (n == error_beat[t] ? 2 : 0); expected = inject_reset && n >= prefix ? 3 : (n == error_beat[t] ? 2 : 0);
data = expected == 3 ? 0 : (t == 12 ? 32'hcafe_f00d : (address + 32'(4 * n)) ^ 32'h10000000); data = expected == 3 ? 0 : (t == `RETROSOC_TINY__TARGET_XPI ? 32'hcafe_f00d : (address + 32'(4 * n)) ^ 32'h10000000);
receive_read(n, beats, expected, data); receive_read(n, beats, expected, data);
end end
target_rst_n[t] = 1; target_rst_n[t] = 1;
@@ -412,7 +417,7 @@ module tiny_r2_p5_reset_tb;
endtask endtask
initial begin initial begin
for (integer t = 0; t < 23; t++) begin for (integer t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin
target_rst_n[t] = 0; target_rst_n[t] = 0;
captures[t] = 0; captures[t] = 0;
addresses[t] = 0; addresses[t] = 0;
@@ -422,7 +427,7 @@ module tiny_r2_p5_reset_tb;
for (integer e = 0; e < 6; e++) faults[e] = 0; for (integer e = 0; e < 6; e++) faults[e] = 0;
repeat (4) @(negedge clk_i); repeat (4) @(negedge clk_i);
rst_n_i = 1; rst_n_i = 1;
for (integer t = 0; t < 23; t++) target_rst_n[t] = 1; for (integer t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) target_rst_n[t] = 1;
// SRAM and XPI: address cancellation, missing 16 beats, two captured // SRAM and XPI: address cancellation, missing 16 beats, two captured
// beats under backpressure, and already captured successful completion. // beats under backpressure, and already captured successful completion.
for (integer t = 0; t < 5; t += 4) begin for (integer t = 0; t < 5; t += 4) begin
@@ -441,13 +446,13 @@ module tiny_r2_p5_reset_tb;
read_case(32'h30000000, 0, 16, 1, 0, 1); read_case(32'h30000000, 0, 16, 1, 0, 1);
error_beat[0] = -1; error_beat[0] = -1;
// Actual APB bridge, with a withheld peripheral completion. // Actual APB bridge, with a withheld peripheral completion.
read_case(32'h10009000, 12, 1, 0, 1, 1); read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 1, 1);
read_case(32'h10009000, 12, 1, 0, 0, 1); read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 0, 1);
read_case(32'h10009000, 12, 1, 1, 0, 1); read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 0, 1);
write_case(32'h10009000, 12, 1, 0, 0, 1); write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 0, 1);
write_case(32'h10009000, 12, 1, 1, 0, 0); write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 0, 0);
write_case(32'h10009000, 12, 1, 1, 1, 0); write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 1, 0);
dma_case(32'h10009000, 12, 1, 0); dma_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0);
$display("P5_RESET cases=%0d production_dma=3 burst_beats=16 held_RB=1 first_fault=1", cases); $display("P5_RESET cases=%0d production_dma=3 burst_beats=16 held_RB=1 first_fault=1", cases);
$display("SIM_TEST_PASS Tiny P5 target reset isolation"); $display("SIM_TEST_PASS Tiny P5 target reset isolation");
$finish; $finish;
+3 -2
View File
@@ -1,3 +1,4 @@
`include "tiny_topology.svh"
`timescale 1ns / 1ps `timescale 1ns / 1ps
module tiny_r2_p5_system_tb; module tiny_r2_p5_system_tb;
logic clk_i = 0, rst_n_i = 0; logic clk_i = 0, rst_n_i = 0;
@@ -38,7 +39,7 @@ retrosoc_tiny u_dut (
u_dut.u_masters_axi4_if[1].wvalid && u_dut.u_masters_axi4_if[1].wready) u_dut.u_masters_axi4_if[1].wvalid && u_dut.u_masters_axi4_if[1].wready)
overlap_cycles++; overlap_cycles++;
if((u_dut.u_fabric.s_busy_q[2] || u_dut.u_fabric.s_busy_q[3]) && if((u_dut.u_fabric.s_busy_q[2] || u_dut.u_fabric.s_busy_q[3]) &&
(!u_dut.u_fabric.target_idle_o[4] || !u_dut.u_fabric.target_idle_o[12]) && (!u_dut.u_fabric.target_idle_o[4] || !u_dut.u_fabric.target_idle_o[`RETROSOC_TINY__TARGET_XPI]) &&
u_dut.u_sysctrl_apb4_if.psel && u_dut.u_sysctrl_apb4_if.penable && u_dut.u_sysctrl_apb4_if.pready) u_dut.u_sysctrl_apb4_if.psel && u_dut.u_sysctrl_apb4_if.penable && u_dut.u_sysctrl_apb4_if.pready)
controls++; controls++;
if(u_dut.u_cpu_local_if[1].req_valid && u_dut.u_cpu_local_if[1].req_ready && if(u_dut.u_cpu_local_if[1].req_valid && u_dut.u_cpu_local_if[1].req_ready &&
@@ -76,7 +77,7 @@ retrosoc_tiny u_dut (
jtag_dmi(1, 7'h10, 3, status); jtag_dmi(1, 7'h10, 3, status);
$display("P5_SYSTEM_STAGE reset-request pending=%b", u_dut.u_cpu.s_reset_pending); $display("P5_SYSTEM_STAGE reset-request pending=%b", u_dut.u_cpu.s_reset_pending);
wait (!u_dut.s_core_rst_n); wait (!u_dut.s_core_rst_n);
if (!u_dut.u_fabric.s_busy_q[3] || u_dut.u_fabric.target_idle_o[12]) if (!u_dut.u_fabric.s_busy_q[3] || u_dut.u_fabric.target_idle_o[`RETROSOC_TINY__TARGET_XPI])
$fatal(1, "hart reset cancelled unrelated DMA"); $fatal(1, "hart reset cancelled unrelated DMA");
hart_reset_observed = 1; hart_reset_observed = 1;
$display("P5_SYSTEM_STAGE hart-reset"); $display("P5_SYSTEM_STAGE hart-reset");
+87
View File
@@ -0,0 +1,87 @@
`timescale 1ns / 1ps
module tiny_r2_p6_bridge_tb;
logic clk_i = 0, rst_n_i = 0;
always #5 clk_i = !clk_i;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) axi (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if apb (
.pclk (clk_i),
.presetn(rst_n_i)
);
tiny_axi42apb4 u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (axi),
.apb4 (apb)
);
assign apb.pready = apb.psel && apb.penable;
assign apb.prdata = 32'h13579bdf;
// A strict APB4 receiver rejects nonzero read strobes independently of PWRITE.
assign apb.pslverr = apb.psel && apb.penable && !apb.pwrite && apb.pstrb != 0;
task automatic write_read(input logic [3:0] mask);
@(negedge clk_i);
axi.awvalid = 1;
do @(posedge clk_i); while (!axi.awready);
@(negedge clk_i);
axi.awvalid = 0;
axi.wvalid = 1;
axi.wstrb = mask;
do @(posedge clk_i); while (!axi.wready);
@(negedge clk_i);
axi.wvalid = 0;
do @(posedge clk_i); while (!apb.penable);
if (!apb.pwrite || apb.pstrb !== mask) $fatal(1, "SIM_TEST_FAIL lost write byte lanes");
do @(posedge clk_i); while (!axi.bvalid);
if (axi.bresp != 0) $fatal(1, "SIM_TEST_FAIL APB write response");
@(negedge clk_i);
axi.arvalid = 1;
do @(posedge clk_i); while (!axi.arready);
@(negedge clk_i);
axi.arvalid = 0;
do @(posedge clk_i); while (!axi.rvalid);
if (axi.rresp != 0 || axi.rdata !== 32'h13579bdf || !axi.rlast)
$fatal(1, "SIM_TEST_FAIL stale write strobes corrupted APB read");
@(negedge clk_i);
endtask
initial begin
axi.awvalid = 0;
axi.awaddr = 'h10007000;
axi.awlen = 0;
axi.awsize = 2;
axi.awburst = 1;
axi.awlock = 0;
axi.awid = 0;
axi.wvalid = 0;
axi.wdata = 'h12345678;
axi.wstrb = 0;
axi.wlast = 1;
axi.bready = 1;
axi.arvalid = 0;
axi.araddr = 'h10007008;
axi.arlen = 0;
axi.arsize = 2;
axi.arburst = 1;
axi.arlock = 0;
axi.arid = 0;
axi.rready = 1;
repeat (4) @(negedge clk_i);
rst_n_i = 1;
write_read(15);
write_read(1);
write_read(3);
write_read(10);
$display("SIM_TEST_PASS Tiny P6 APB read strobes");
$finish;
end
initial begin
#100000;
$fatal(1, "SIM_TEST_TIMEOUT P6 APB strobes");
end
endmodule
+153
View File
@@ -0,0 +1,153 @@
`timescale 1ns / 1ps
module tiny_r2_p6_shared_tb;
logic clk_i = 0, clk_aud_i = 0, rst_n_i = 0, rst_aud_n_i = 0;
always #20.833333 clk_i = !clk_i;
always #27.126736111 clk_aud_i = !clk_aud_i;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) tx (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) rx (
.aclk (clk_i),
.aresetn(rst_n_i)
);
i2s_if i2s ();
logic streaming = 0, wide = 0;
integer sent = 0, received = 0, matched = 0, examined = 0;
logic found = 0;
logic [31:0] value;
assign i2s.adcdat_i = i2s.dacdat_o;
assign tx.tvalid = streaming;
assign tx.tdata = wide ? 32'h120000 + 32'(sent) :
{16'(16'h2001 + 2*sent), 16'(16'h2000 + 2*sent)};
assign tx.tstrb = 4'hf;
assign tx.tkeep = 4'hf;
assign tx.tlast = 0;
assign tx.tuser = 0;
assign tx.tid = 0;
assign tx.tdest = 0;
assign rx.tready = 1;
apb4_i2s u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i),
.dma_tx_stall_o (),
.dma_rx_stall_o (),
.tx_underrun_evt_o(),
.rx_overrun_evt_o (),
.rx_flush_busy_o (),
.apb4 (apb4),
.tx_axis (tx),
.rx_axis (rx),
.i2s (i2s)
);
task automatic write(input logic [31:0] address, input logic [31:0] data);
@(negedge clk_i);
apb4.paddr = address;
apb4.pwdata = data;
apb4.pstrb = 15;
apb4.pwrite = 1;
apb4.psel = 1;
apb4.penable = 0;
@(negedge clk_i);
apb4.penable = 1;
do @(posedge clk_i); while (!apb4.pready);
if (apb4.pslverr) $fatal(1, "SIM_TEST_FAIL P6 I2S APB write %h", address);
@(negedge clk_i);
apb4.psel = 0;
apb4.penable = 0;
endtask
task automatic sample (input logic [31:0] observed);
logic [31:0] first;
first = wide ? 32'h120020 : 32'h2020;
if (!found && observed == first) found = 1;
if (found && matched < 512) begin
if (observed !== first + 32'(matched))
$fatal(
1,
"SIM_TEST_FAIL P6 I2S sample bits=%0d index=%0d got=%h expected=%h",
wide ? 24 : 16,
matched,
observed,
first + 32'(matched)
);
matched++;
end
examined++;
if (!found && examined > 40)
$fatal(
1,
"SIM_TEST_FAIL P6 I2S startup/payload missing bits=%0d last=%h",
wide ? 24 : 16,
observed
);
endtask
always @(posedge clk_i) begin
if (rst_n_i) begin
if (tx.tvalid && tx.tready) sent <= sent + 1;
if (rx.tvalid && rx.tready) begin
if (rx.tkeep !== 4'hf || rx.tstrb !== 4'hf)
$fatal(1, "SIM_TEST_FAIL P6 I2S receive qualifiers");
if (wide) sample (rx.tdata);
else begin
sample ({16'd0, rx.tdata[15:0]});
sample ({16'd0, rx.tdata[31:16]});
end
received++;
end
end
end
initial begin
apb4.psel = 0;
apb4.penable = 0;
apb4.pwrite = 0;
apb4.paddr = 0;
apb4.pwdata = 0;
apb4.pstrb = 0;
apb4.pprot = 0;
for (integer mode = 0; mode < 2; mode++) begin
@(negedge clk_i);
rst_n_i = 0;
rst_aud_n_i = 0;
streaming = 0;
sent = 0;
received = 0;
matched = 0;
examined = 0;
found = 0;
wide = mode != 0;
repeat (8) @(negedge clk_i);
rst_n_i = 1;
repeat (8) @(negedge clk_aud_i);
rst_aud_n_i = 1;
repeat (32) @(negedge clk_i);
write(4, 3);
repeat (256) @(negedge clk_i);
if (u_dut.s_tx_flush_busy || u_dut.s_rx_flush_busy)
$fatal(1, "SIM_TEST_FAIL P6 initial disabled audio flush");
write('h10, wide ? 2 : 0);
write('h18, 'h00200060);
write('hc, 3);
streaming = 1;
wait (sent >= 96);
write(0, 7);
wait (matched == 512);
$display("P6_I2S bits=%0d samples=%0d", wide ? 24 : 16, matched);
end
$display("SIM_TEST_PASS Tiny P6 I2S payload");
$finish;
end
initial begin
#30000000;
$fatal(1, "SIM_TEST_TIMEOUT P6 I2S payload");
end
endmodule
+2 -2
View File
@@ -421,7 +421,7 @@ def test_commercial_timing_contract_supports_tiny(tmp_path: Path) -> None:
check=True, check=True,
) )
assert result.stdout.splitlines() == [ assert result.stdout.splitlines() == [
"jtag,system", "audio,jtag,system",
"ext_rst_n_i_pad,jtag_trst_n_i_pad", "ext_rst_n_i_pad,jtag_trst_n_i_pad",
] ]
assert "observation {u_clock_buffer/clk_o}" in output.read_text( assert "observation {u_clock_buffer/clk_o}" in output.read_text(
@@ -660,7 +660,7 @@ CONSTRAINT_SMOKE_CASES = {
"TINY": { "TINY": {
"domains": "rtl/tiny/integration/clock_reset_domains.json", "domains": "rtl/tiny/integration/clock_reset_domains.json",
"pin_map": "rtl/tiny/pin_map/pin_map.json", "pin_map": "rtl/tiny/pin_map/pin_map.json",
"masters": "clk_system clk_jtag", "masters": "clk_system clk_jtag clk_audio",
"generated": "", "generated": "",
"groups": "1", "groups": "1",
}, },
+15
View File
@@ -96,6 +96,21 @@ def test_channel_coverage_cannot_drop_the_boot_channel(spec):
pr.collect_programming(ROOT, spec, IDS, REFERENCE["source_revision"]) pr.collect_programming(ROOT, spec, IDS, REFERENCE["source_revision"])
def test_mini_channel_selection_excludes_inactive_tiny_roles():
header = (ROOT / "crt/include/retrosoc/hal/dma.h").read_text()
selected = pr.mini_channel_enum(header)
channels = pr.constants(selected, "RS_DMA_CHANNEL_")
assert sorted(channels.values()) == list(range(6))
assert "RS_DMA_CHANNEL_I2S_TX = RS_DMA_CHANNEL_BULK" in selected
# A Tiny allocation change must not become a Mini allocation or alias.
assert pr.mini_channel_enum(header.replace("RS_DMA_CHANNEL_I2S_TX = 6", "RS_DMA_CHANNEL_I2S_TX = 7")) == selected
with pytest.raises(ValueError, match="alias binding"):
pr.mini_channel_enum(header.replace("RS_DMA_CHANNEL_I2S_TX = RS_DMA_CHANNEL_BULK",
"RS_DMA_CHANNEL_I2S_TX = RS_DMA_CHANNEL_CRYPTO_IN"))
with pytest.raises(ValueError, match="unreviewed"):
pr.mini_channel_enum(header.replace("#ifdef RS_SOC_TINY", "#ifdef UNKNOWN_PRODUCT"))
def test_credit_parser_fails_closed_on_unreviewed_shape(): def test_credit_parser_fails_closed_on_unreviewed_shape():
with pytest.raises(ValueError, match="unsupported credit function"): with pytest.raises(ValueError, match="unsupported credit function"):
pr.credit_values("function automatic int limits(input int master); return compute(master); endfunction", "limits", 8) pr.credit_values("function automatic int limits(input int master); return compute(master); endfunction", "limits", 8)
+23 -195
View File
@@ -1,57 +1,38 @@
"""Tiny publication is parameter-bound, complete and isolated from Mini inputs.""" """Historical Tiny publication contracts remain separate from P6 source support."""
from __future__ import annotations from __future__ import annotations
import copy import os
import json
import re
from pathlib import Path from pathlib import Path
import shutil import subprocess
import sys
import pytest import pytest
from publications import tiny_reference as tiny from publications import tiny_reference as tiny
from publications.register_reference import parse_definitions from publication_history import ORIGINAL_BOOK, P5_CONTRACT, snapshot
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
@pytest.fixture(scope="module") @pytest.mark.parametrize("revision", [ORIGINAL_BOOK, P5_CONTRACT])
def data(): def test_historical_publication_contracts(tmp_path, revision):
return tiny.collect(tiny.read(ROOT / tiny.BOOK / "tiny.json"), check_snapshot=False) historical = snapshot(tmp_path / "source", revision)
result = subprocess.run(
[sys.executable, "-m", "pytest", "-q", "tests/test_publication_tiny.py",
"--basetemp=" + str(tmp_path / "tests"), "--tb=short"],
cwd=historical, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
env={**os.environ, "PYTHONDONTWRITEBYTECODE": "1"}, timeout=180,
)
(tmp_path / "historical-tests.log").write_text(result.stdout)
assert result.returncode == 0, result.stdout
assert " passed" in result.stdout and " skipped" not in result.stdout
def test_tiny_configuration_and_inventory_are_derived(data): def test_current_source_cannot_relabel_historical_publication():
assert (data["facts"]["hardware_isa"], data["facts"]["firmware_isa"]) == ("RV32IMC", "RV32IM") config = tiny.read(ROOT / tiny.BOOK / "tiny.json")
assert data["facts"]["sram"] == {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1} assert config["source_revision"] == "9c706ba0ff1da1028c299cfff573010a86dfd7ce"
assert data["facts"]["dma"]["NumChannels"] == 4 with pytest.raises(ValueError, match="DMA parameters changed"):
assert data["facts"]["dma"]["RequestMask"] == 0x7F9 tiny.collect(config)
assert data["facts"]["dma"]["EnableStreams"] == 0
assert data["facts"]["cpu"]["ResetSyncStages"] == 5
assert data["facts"]["fabric"]["target_count"] == 23
assert data["facts"]["fabric"]["active_owners"] == [0, 1]
assert tiny.RESET_TREE in data["source_paths"]
assert len(data["catalog"]) == 14 and len(data["regions"]) == 20 and len(data["pads"]) == 52
assert len(data["interrupts"]) == 14 and len(data["gpio"]) == 32
assert not any(p.startswith("rtl/mini/") for p in data["source_paths"])
assert "mpw" not in {s["destination"].split("/")[-1] for s in data["managed_sources"]}
def test_tiny_collection_does_not_open_mini_files(monkeypatch):
original = Path.read_text
def read(path, *args, **kwargs):
name = path.as_posix()
if "/rtl/mini/" in name or ("/publications/datasheets/" in name and "/tiny/" not in name):
raise AssertionError("Tiny opened a Mini product input: " + name)
return original(path, *args, **kwargs)
monkeypatch.setattr(Path, "read_text", read)
tiny.collect(tiny.read(ROOT / tiny.BOOK / "tiny.json"), check_snapshot=False)
def test_common_register_default_remains_mini_compatible():
paths = ["rtl/ip/serial/apb4_uart_define.svh"]
assert parse_definitions(paths, {}) == parse_definitions(paths, {}, "rtl/mini/address_map/memory_map.json")
_, constants = parse_definitions(paths, {}, tiny.MAP)
assert "SOC_SYSCTRL_TEST_STATUS_OFFSET" in constants
@pytest.mark.parametrize("before,after", [ @pytest.mark.parametrize("before,after", [
@@ -59,7 +40,7 @@ def test_common_register_default_remains_mini_compatible():
("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[1]", "s_r_valid"),
(".flush_i(1'b0)", ".flush_i(!enable_i)"), (".flush_i(1'b0)", ".flush_i(!enable_i)"),
]) ])
def test_target_reset_contract_rejects_unsafe_completion(tmp_path, before, after): def test_current_reset_binding_still_rejects_unsafe_completion(tmp_path, before, after):
contract = tiny.read(ROOT / tiny.BOOK / "source-contract.json") contract = tiny.read(ROOT / tiny.BOOK / "source-contract.json")
binding = next(item for item in contract["bindings"] binding = next(item for item in contract["bindings"]
if item["file"] == "rtl/tiny/top/tiny_axi4_target.sv") if item["file"] == "rtl/tiny/top/tiny_axi4_target.sv")
@@ -70,156 +51,3 @@ def test_target_reset_contract_rejects_unsafe_completion(tmp_path, before, after
path.write_text(original.replace(before, after)) path.write_text(original.replace(before, after))
with pytest.raises(ValueError, match="Tiny behavior binding changed"): with pytest.raises(ValueError, match="Tiny behavior binding changed"):
tiny.require_snippets(tmp_path, binding["file"], binding["text"]) tiny.require_snippets(tmp_path, binding["file"], binding["text"])
def test_reset_update_does_not_bypass_reviewed_snapshot(monkeypatch):
original = tiny.subprocess.check_output
checked = []
def changed_snapshot(command, **kwargs):
if command[:3] == ["git", "diff", "--name-only"]:
assert tiny.RESET_TREE in command
checked.append(command)
return tiny.RESET_TREE + "\n"
return original(command, **kwargs)
monkeypatch.setattr(tiny.subprocess, "check_output", changed_snapshot)
with pytest.raises(ValueError, match="technical sources differ from reviewed snapshot"):
tiny.collect(tiny.read(ROOT / tiny.BOOK / "tiny.json"))
assert len(checked) == 1
@pytest.fixture
def source_tree(tmp_path):
sources = [tiny.TOP, tiny.RESET_TREE, tiny.ARCHINFO, tiny.SYSCTRL,
tiny.CPU, tiny.CPU_MEM, tiny.SRAM, "configs/ci/ihp130-tiny.mk",
tiny.MAP, tiny.TOPOLOGY, (tiny.BOOK / "source-contract.json").as_posix()]
for relative in sources:
target = tmp_path / relative
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(ROOT / relative, target)
return tmp_path
@pytest.mark.parametrize("path,before,after", [
(tiny.SRAM, ".CapacityKiB(128)", ".CapacityKiB(32)"),
(tiny.SRAM, "group < 4", "group < 2"),
(tiny.SRAM, ".DataBytes(4)", ".DataBytes(8)"),
(tiny.TOP, ".NumChannels (4)", ".NumChannels (8)"),
(tiny.TOP, ".EnableStreams(1'b0)", ".EnableStreams(1'b1)"),
(tiny.TOP, ".EnableAtomics (1'b0)", ".EnableAtomics (1'b1)"),
(tiny.TOP, ".ResetSyncStages (5)", ".ResetSyncStages (3)"),
("configs/ci/ihp130-tiny.mk", "ISA := RV32IM", "ISA := RV32IMC"),
(tiny.ARCHINFO, "32'h5449_4e59", "32'h4d49_4e49"),
(tiny.SYSCTRL, "!s_test_q[31] && apb4.pwdata[31]", "s_test_q[31] && apb4.pwdata[31]"),
])
def test_changed_hardware_or_isa_cannot_retain_publication_claim(source_tree, path, before, after):
file = source_tree / path
text = file.read_text(encoding="utf-8")
# Parameter alignment is formatting, while the altered value must still
# fail the source-bound publication contract.
pattern = r"\s*".join(re.escape(char) for char in before if not char.isspace())
assert re.search(pattern, text)
file.write_text(re.sub(pattern, lambda _: after, text, count=1), encoding="utf-8")
with pytest.raises(ValueError):
tiny.facts(source_tree)
@pytest.mark.parametrize("change", ["irq_missing", "irq_number", "region_address", "region_missing"])
def test_missing_or_wrong_maps_fail(source_tree, change):
path = source_tree / (tiny.TOPOLOGY if change.startswith("irq") else tiny.MAP)
document = tiny.read(path)
if change == "irq_missing":
document["interrupts"].pop()
elif change == "irq_number":
document["interrupts"][-1]["core_bit"] = 27
elif change == "region_address":
next(r for r in document["regions"] if r["symbol"] == "SRAM")["base"] = "0x31000000"
else:
document["regions"].pop()
path.write_text(json.dumps(document), encoding="utf-8")
with pytest.raises(ValueError):
tiny.validate_maps(source_tree, tiny.facts(source_tree))
def test_tiny_sysctrl_and_discovery_do_not_inherit_mini_controls(data):
control = data["registers"]["sysctrl"]["registers"]
assert {r["name"] for r in control} == tiny.SUPPORTED_SYSCTRL
assert not any(r["name"] in {"HP_CTRL", "PLL_CFG", "DEBUG_SELECT"} for r in control)
test = next(r for r in control if r["name"] == "TEST_STATUS")
assert [(f["name"], f["lsb"], f["msb"]) for f in test["fields"] if f["name"] != "Reserved"] == [
("PASS", 0, 0), ("CODE", 8, 15), ("VALID", 31, 31)]
info = data["registers"]["archinfo"]["registers"]
assert next(r for r in info if r["name"] == "SOC_ID")["reset"] == "0x54494E59"
assert next(r for r in info if r["name"] == "SRAM_BYTES")["reset"] == "0x00020000"
assert data["registers"]["dma"]["groups"][1]["count"] == 4
def test_tcd_layout_is_complete_and_bounded(data):
assert len(data["tcd"]) == 17
assert sum(f["bytes"] for f in data["tcd"]) == 64
assert data["tcd"][-1]["offset"] == 60
def test_local_capabilities_keep_fabric_and_encoding_restrictions(data):
capability = next(r for r in data["registers"]["dma"]["registers"] if r["name"] == "CAPABILITY")
assert capability["reset"] == "0x00200401"
burst = next(f for f in capability["fields"] if f["name"] == "MAX_BURST_FIELD")
assert (burst["lsb"], burst["msb"], burst["reset"]) == (24, 27, "0x0")
assert "encoding limitation" in burst["description"]
sram = next(r for r in data["registers"]["sram"]["registers"] if r["name"] == "CAPABILITY")
assert "rejects WRAP" in next(f for f in sram["fields"] if f["name"] == "WRAP")["description"]
@pytest.mark.parametrize("key,value", [("document_id", "RS-MINI-DS"), ("profile", "configs/ci/ihp130.mk"), ("status", "RELEASE")])
def test_wrong_product_configuration_cannot_enter_tiny_pipeline(key, value):
config = tiny.read(ROOT / tiny.BOOK / "tiny.json")
config[key] = value
with pytest.raises(ValueError, match="configuration"):
tiny.validate_configuration(config)
def test_existing_mini_output_is_never_overwritten(tmp_path, monkeypatch):
from publications import build_tiny_datasheet as builder
folder = tmp_path / "build/mini-existing"
folder.mkdir(parents=True)
manifest = folder / "manifest.json"
manifest.write_text('{"document":{"document_id":"RS-MINI-DS"}}')
before = manifest.read_bytes()
monkeypatch.setattr(builder, "ROOT", tmp_path)
with pytest.raises(ValueError, match="another product"):
builder.validate_output(folder, {"document_id": "RS-TINY-DS"})
assert manifest.read_bytes() == before
@pytest.mark.parametrize("change", ["missing", "offset"])
def test_register_inventory_cannot_silently_drop_or_move_a_definition(data, change):
refs = copy.deepcopy(data["registers"])
if change == "missing":
refs["uart"]["registers"].pop()
else:
refs["uart"]["registers"][0]["offset"] += 4
with pytest.raises(ValueError, match="register inventory"):
tiny.validate_register_inventory(refs, tiny.read(ROOT / tiny.BOOK / "register-contract.json"))
@pytest.mark.parametrize("change", ["missing", "duplicate"])
def test_pdf_must_render_each_extracted_register_once(data, change):
from publications.build_tiny_datasheet import validate_rendered_registers
items = [dict(kind="tiny-register", family=family, key=r["key"], page=1)
for family, value in data["registers"].items() for r in value["registers"]]
assert validate_rendered_registers(data, items) == 337
if change == "missing":
items.pop()
else:
items.append(items[0])
with pytest.raises(ValueError, match="omitted or duplicated"):
validate_rendered_registers(data, items)
@pytest.mark.parametrize("key,value", [("status", "qualified"), ("current_commit_reports", [{"passed": True}])])
def test_unmatched_qualification_claim_is_rejected(data, key, value):
evidence = copy.deepcopy(data["evidence"])
evidence[key] = value
with pytest.raises(ValueError, match="qualification"):
tiny.validate_evidence(evidence)
+4 -2
View File
@@ -51,7 +51,9 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
assert "RS_SOC_PSRAM_BASE" not in sdk assert "RS_SOC_PSRAM_BASE" not in sdk
irq = (output / "include/retrosoc/generated/irq_metadata.h").read_text() irq = (output / "include/retrosoc/generated/irq_metadata.h").read_text()
assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(30)" in irq assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(30)" in irq
assert "RS_SOC_EXT_IRQ_UART1 UINT32_C(24)" in irq assert "RS_SOC_EXT_IRQ_UART1" not in irq
assert "RS_SOC_EXT_IRQ_I2C1" not in irq
assert "RS_SOC_EXT_IRQ_SDIO UINT32_C(8)" in irq
assert "RS_SOC_EXT_IRQ_DMA UINT32_C(18)" in irq assert "RS_SOC_EXT_IRQ_DMA UINT32_C(18)" in irq
assert "RS_SOC_EXT_IRQ_NPU" not in irq assert "RS_SOC_EXT_IRQ_NPU" not in irq
# A consumer of public SDK headers must see Tiny capabilities without # A consumer of public SDK headers must see Tiny capabilities without
@@ -59,7 +61,7 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
if shutil.which("cc"): if shutil.which("cc"):
probe = tmp_path / "consumer.c" probe = tmp_path / "consumer.c"
probe.write_text('#include <retrosoc/hal/dma.h>\n' probe.write_text('#include <retrosoc/hal/dma.h>\n'
'_Static_assert(RS_DMA_CHANNEL_COUNT == 4, "wrong product");\n') '_Static_assert(RS_DMA_CHANNEL_COUNT == 8, "wrong product");\n')
run(["cc", "-std=c11", "-Werror", "-I" + str(ROOT / "crt/include"), run(["cc", "-std=c11", "-Werror", "-I" + str(ROOT / "crt/include"),
"-I" + str(output / "memory_map/include"), "-I" + str(output / "include"), "-I" + str(output / "memory_map/include"), "-I" + str(output / "include"),
"-c", str(probe), "-o", str(tmp_path / "consumer.o")]) "-c", str(probe), "-o", str(tmp_path / "consumer.o")])
+12 -1
View File
@@ -114,6 +114,8 @@ def mapped_netlist():
"low": cell("TIELOH7R", Z=3)} "low": cell("TIELOH7R", Z=3)}
cells.update({f"u_soc.u_sram.gen_group[{i // 8}].u_group.gen_bank[{i % 8}].u_ram.u_mem": cells.update({f"u_soc.u_sram.gen_group[{i // 8}].u_group.gen_bank[{i % 8}].u_ram.u_mem":
cell(RAM, CLK=2) for i in range(32)}) cell(RAM, CLK=2) for i in range(32)})
cells.update({f"u_soc.u_crypto.u_storage.gen_bank[{i}].gen_macro.u_sram.u_mem":
cell(RAM, CLK=2) for i in range(6)})
return {"modules": { return {"modules": {
"retrosoc_tiny_asic": {"ports": {"extclk_i_pad": {"direction": "inout", "bits": [1]}, "retrosoc_tiny_asic": {"ports": {"extclk_i_pad": {"direction": "inout", "bits": [1]},
"jtag_tck_i_pad": {"direction": "inout", "bits": [11]}}, "jtag_tck_i_pad": {"direction": "inout", "bits": [11]}},
@@ -126,6 +128,7 @@ def mapped_netlist():
def test_safe24_structural_binding(mapped_netlist): def test_safe24_structural_binding(mapped_netlist):
result = inspect_netlist(mapped_netlist) result = inspect_netlist(mapped_netlist)
assert len(result["sram"]) == 32 and result["cpu_clocked_cells"] == 1 assert len(result["sram"]) == 32 and result["cpu_clocked_cells"] == 1
assert len(result["crypto_sram"]) == 6
assert result["pll_enable"] == 0 assert result["pll_enable"] == 0
assert result["system_clock_source"]["port"] == "extclk_i_pad" assert result["system_clock_source"]["port"] == "extclk_i_pad"
assert [c["type"] for c in result["system_clock_source"]["path"]] == [ assert [c["type"] for c in result["system_clock_source"]["path"]] == [
@@ -199,10 +202,12 @@ def test_hierarchical_constant_output_is_resolved(mapped_netlist):
@pytest.mark.parametrize("mutation", ["missing_pll", "missing_ram", "live_pll", "unknown_enable", @pytest.mark.parametrize("mutation", ["missing_pll", "missing_ram", "live_pll", "unknown_enable",
"divided_ram", "wrong_cpu", "pll_sys"]) "divided_ram", "wrong_cpu", "pll_sys",
"missing_crypto", "misplaced_crypto", "divided_crypto"])
def test_unsafe_or_incomplete_mapping_is_rejected(mapped_netlist, mutation): def test_unsafe_or_incomplete_mapping_is_rejected(mapped_netlist, mutation):
cells = mapped_netlist["modules"]["retrosoc_tiny_asic"]["cells"] cells = mapped_netlist["modules"]["retrosoc_tiny_asic"]["cells"]
first_ram = "u_soc.u_sram.gen_group[0].u_group.gen_bank[0].u_ram.u_mem" first_ram = "u_soc.u_sram.gen_group[0].u_group.gen_bank[0].u_ram.u_mem"
first_crypto = "u_soc.u_crypto.u_storage.gen_bank[0].gen_macro.u_sram.u_mem"
if mutation == "missing_pll": if mutation == "missing_pll":
del cells["pll"] del cells["pll"]
elif mutation == "missing_ram": elif mutation == "missing_ram":
@@ -215,6 +220,12 @@ def test_unsafe_or_incomplete_mapping_is_rejected(mapped_netlist, mutation):
cells[first_ram]["connections"]["CLK"] = [99] cells[first_ram]["connections"]["CLK"] = [99]
elif mutation == "wrong_cpu": elif mutation == "wrong_cpu":
cells["u_soc.u_cpu.u_hazard3_cpu_2port.core.pc_reg"]["connections"]["CK"] = [99] cells["u_soc.u_cpu.u_hazard3_cpu_2port.core.pc_reg"]["connections"]["CK"] = [99]
elif mutation == "missing_crypto":
del cells[first_crypto]
elif mutation == "misplaced_crypto":
cells["unexpected_private_memory"] = cells.pop(first_crypto)
elif mutation == "divided_crypto":
cells[first_crypto]["connections"]["CLK"] = [99]
else: else:
cells["pll"]["connections"]["CKOUT1"] = [2] cells["pll"]["connections"]["CKOUT1"] = [2]
with pytest.raises(ValueError): with pytest.raises(ValueError):
+2 -2
View File
@@ -59,8 +59,8 @@ def test_reset_inventory_has_one_clock_and_all_existing_targets() -> None:
reset = inventory["reset_distribution"] reset = inventory["reset_distribution"]
assert reset["release_edges"] == 5 assert reset["release_edges"] == 5
assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])] assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])]
assert len(reset["leaf_names"]) == 17 assert len(reset["leaf_names"]) == 21
assert {domain["name"] for domain in inventory["domains"]} == {"system", "jtag"} assert {domain["name"] for domain in inventory["domains"]} == {"system", "jtag", "audio"}
assert reset["clock_domain"] == "system" and reset["main_sram_payload_reset"] == "none" assert reset["clock_domain"] == "system" and reset["main_sram_payload_reset"] == "none"
+544
View File
@@ -0,0 +1,544 @@
"""P6 product integration and fail-closed SDK/macro contracts."""
from __future__ import annotations
import json
import hashlib
import io
from pathlib import Path
import re
import shutil
import subprocess
import tarfile
import pytest
from scripts.generate_tiny import generate_bindings, read_topology
from scripts.rtl.generate_memory_map import generate as generate_memory_map
from scripts.check_clock_reset_domains import validate as validate_clocks
from test_tiny_r2_p4 import run
from test_sdio import SDIO_FULL_SOURCES, SDIO_FULL_INCDIRS
from scripts.check_simulation import DEFAULT_FAILURE
from scripts import tiny_r2_p6 as evidence
ROOT = Path(__file__).resolve().parents[1]
TINY = ROOT / "rtl/tiny"
def test_native_card_model_elaborates_without_sv2v(tmp_path):
run(["iverilog", "-g2012", "-s", "sdio_native_card_model", "-o", str(tmp_path / "sim"),
str(ROOT / evidence.MODEL)], tmp_path, "compile")
@pytest.mark.parametrize("mutation", ["none", "expression", "body", "still_forward"])
def test_only_continuous_declaration_relocation_is_equivalent(mutation):
after = (ROOT / evidence.MODEL).read_text()
declaration = re.search(r"^ wire \[3:0\] s_token_lanes = [^;]+;\n", after, re.M).group()
before = after.replace(declaration, "").replace(" integer s_data_bytes;\n",
declaration + " integer s_data_bytes;\n")
if mutation == "expression":
after = after.replace("s_data_bytes >= 4", "s_data_bytes >= 3")
elif mutation == "body":
after = after.replace("s_data_bytes = (bytes_i < 1) ? 1 : bytes_i;",
"s_data_bytes = 1;")
assert after != (ROOT / evidence.MODEL).read_text()
elif mutation == "still_forward":
after = before
if mutation == "none":
evidence.check_model_declaration_move(before, after)
else:
with pytest.raises(ValueError):
evidence.check_model_declaration_move(before, after)
@pytest.mark.parametrize("mutation", ["none", "dut", "model", "tool"])
def test_verification_update_cannot_relabel_dut_changes(tmp_path, mutation):
after = (ROOT / evidence.MODEL).read_text()
declaration = re.search(r"^ wire \[3:0\] s_token_lanes = [^;]+;\n", after, re.M).group()
before = after.replace(declaration, "").replace(" integer s_data_bytes;\n",
declaration + " integer s_data_bytes;\n")
if mutation == "model":
after = after.replace("s_data_bytes >= 4", "s_data_bytes >= 3")
dut = "rtl/tiny/top/retrosoc_tiny.sv"
documents = [{evidence.MODEL: before, dut: "unchanged DUT"},
{evidence.MODEL: after, dut: "changed DUT" if mutation == "dut" else "unchanged DUT"}]
snapshots = []
maps = []
for index, documents_by_name in enumerate(documents):
folder = tmp_path / str(index)
folder.mkdir()
hashes = {name: hashlib.sha256(text.encode()).hexdigest() for name, text in documents_by_name.items()}
maps.append(hashes)
evidence.b.dump(folder / "inputs.json", hashes)
(folder / "patch").write_text("unit-test snapshot")
with tarfile.open(folder / "source.tar", "w") as archive:
for name, text in documents_by_name.items():
info = tarfile.TarInfo(name)
info.size = len(text.encode())
archive.addfile(info, io.BytesIO(text.encode()))
snapshots.append({key: evidence.b.artifact(folder / name) for key, name in
(("inputs", "inputs.json"), ("patch", "patch"), ("snapshot", "source.tar"))})
saved = {"source": snapshots[0], "tools": {"unit-test": 1}, "captured_at": "2026-10-12T00:00:00+00:00"}
evidence.b.dump(tmp_path / "source.json", saved)
changed = sorted(name for name in maps[0] if maps[0][name] != maps[1][name])
update = {"phase": evidence.PHASE, "kind": "native_model_declaration_order",
"original_source": evidence.b.artifact(tmp_path / "source.json"), "source": snapshots[1],
"tools": {"unit-test": 2 if mutation == "tool" else 1}, "changed_inputs": changed,
"captured_at": "2026-10-12T00:01:00+00:00"}
if mutation == "none":
assert evidence.check_update(tmp_path, saved, update, maps[1]) == maps[0]
else:
with pytest.raises(ValueError):
evidence.check_update(tmp_path, saved, update, maps[1])
@pytest.mark.parametrize("scope", ["consumer", "collection"])
@pytest.mark.parametrize("mutation", ["none", "dut", "firmware", "smoke", "model", "clock_contract", "other_consumer", "tool"])
def test_consumer_snapshot_cannot_change_running_flow_inputs(tmp_path, mutation, scope):
consumer = "publications/programming_reference.py" if scope == "consumer" else "scripts/tiny_r2_p6.py"
forbidden = {
"dut": "rtl/tiny/top/retrosoc_tiny.sv", "firmware": "app/apps/ci_smoke/tiny.c",
"smoke": "physical/smoke/sta/opensta/generate_sdc.py", "model": evidence.MODEL,
"clock_contract": "rtl/tiny/integration/clock_reset_domains.json",
"other_consumer": "scripts/regress.py" if scope == "consumer" else "publications/programming_reference.py",
}
old = {consumer: "old", **{name: "same" for name in forbidden.values()}}
new = dict(old, **{consumer: "new"})
if mutation in forbidden:
new[forbidden[mutation]] = "changed"
snapshots = []
for index, values in enumerate((old, new)):
folder = tmp_path / str(index)
folder.mkdir()
evidence.b.dump(folder / "inputs", values)
for name in ("snapshot", "patch"):
(folder / name).write_text("unit-test consumer artifact")
snapshots.append({name: evidence.b.artifact(folder / name) for name in ("inputs", "snapshot", "patch")})
previous = {"source": snapshots[0], "tools": {"unit-test": 1}, "captured_at": "2026-10-12T00:00:00+00:00"}
prior = "verification-update.json" if scope == "consumer" else "consumer-update.json"
kind = "publication_and_commercial_consumers" if scope == "consumer" else "terminal_log_collection"
check = evidence.check_consumer_update if scope == "consumer" else evidence.check_collection_update
evidence.b.dump(tmp_path / prior, previous)
update = {"phase": evidence.PHASE, "kind": kind,
"previous_update": evidence.b.artifact(tmp_path / prior),
"source": snapshots[1], "tools": {"unit-test": 2 if mutation == "tool" else 1},
"changed_inputs": sorted(name for name in old if old[name] != new[name]),
"captured_at": "2026-10-12T00:01:00+00:00"}
if mutation == "none":
check(tmp_path, previous, update, new)
else:
with pytest.raises(ValueError):
check(tmp_path, previous, update, new)
@pytest.mark.parametrize("simulator,top", [("verilator", "sdio_standalone_tb"),
("verilator", "tiny_p6_sdio_pads_tb"),
("iverilog", "tiny_p6_sdio_pads_tb")])
def test_shared_sdio_native_data(tmp_path, simulator, top):
sources = [ROOT / name for name in (*SDIO_FULL_SOURCES, "tests/rtl/sdio_standalone_tb.sv")]
output = tmp_path / "sim"
includes = ["-I" + str(ROOT / p) for p in SDIO_FULL_INCDIRS]
if simulator == "iverilog":
converted = subprocess.run(["sv2v", *includes, *map(str, sources)], capture_output=True,
text=True, timeout=180)
assert converted.returncode == 0, converted.stderr
(tmp_path / "converted.v").write_text(converted.stdout)
run([simulator, "-g2012", "-s", top, "-o", str(output), str(tmp_path / "converted.v")],
tmp_path, "compile")
launch = ["vvp", str(output)]
else:
run([simulator, "--binary", "--timing", "--assert", "-Wno-fatal", "-j", "3",
"--top-module", top, *includes, *map(str, sources),
"--Mdir", str(tmp_path / "obj"), "-o", str(output)], tmp_path, "compile", timeout=180)
launch = [str(output)]
assert "SDIO standalone native SD model test passed" in run(launch, tmp_path, "sim")
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_shared_sdio_running_clock_commands(tmp_path, simulator):
sources = [ROOT / "rtl/ip/storage" / name for name in
("sdio_pkg.sv", "sdio_clock.sv", "sdio_command.sv")]
sources += [ROOT / "tests/rtl/sdio_command_tb.sv"]
output = tmp_path / "sim"
if simulator == "iverilog":
converted = subprocess.run(["sv2v", *map(str, sources)], capture_output=True,
text=True, timeout=60)
assert converted.returncode == 0, converted.stderr
(tmp_path / "converted.v").write_text(converted.stdout)
run([simulator, "-g2012", "-s", "sdio_command_tb", "-o", str(output),
str(tmp_path / "converted.v")], tmp_path, "compile")
launch = ["vvp", str(output)]
else:
run([simulator, "--binary", "--timing", "-Wno-fatal", "--top-module", "sdio_command_tb",
*map(str, sources), "--Mdir", str(tmp_path / "obj"), "-o", str(output)],
tmp_path, "compile", timeout=120)
launch = [str(output)]
assert "SDIO command engine test passed" in run(launch, tmp_path, "sim")
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_apb_read_strobes_after_writes(tmp_path, simulator):
common = ROOT / "rtl/managed/clusterip/common/rtl"
generate_memory_map(TINY / "address_map/memory_map.json", tmp_path / "map", "YES", 128)
sources = [common / "utils/register.sv", common / "interface/axi4_if.sv",
common / "interface/apb4_if.sv", TINY / "top/tiny_axi42apb4.sv",
ROOT / "tests/rtl/tiny_r2_p6_bridge_tb.sv"]
flags = ["-DSV_ASSRT_DISABLE", "-I" + str(common), "-I" + str(common / "interface"),
"-I" + str(tmp_path / "map/rtl")]
top = "tiny_r2_p6_bridge_tb"
output = tmp_path / "sim"
if simulator == "iverilog":
converted = subprocess.run(["sv2v", *flags, *map(str, sources)], capture_output=True,
text=True, timeout=60)
assert converted.returncode == 0, converted.stderr
(tmp_path / "converted.v").write_text(converted.stdout)
run([simulator, "-g2012", "-s", top, "-o", str(output), str(tmp_path / "converted.v")],
tmp_path, "compile")
launch = ["vvp", str(output)]
else:
run([simulator, "--binary", "--timing", "--assert", "-Wno-fatal", "--top-module", top,
*flags, *map(str, sources), "--Mdir", str(tmp_path / "obj"), "-o", str(output)],
tmp_path, "compile", timeout=120)
launch = [str(output)]
assert "SIM_TEST_PASS Tiny P6 APB read strobes" in run(launch, tmp_path, "sim")
def p6_log():
rows = ["R2_P6_" + name + " " + " ".join(f"{k}={v}" for k, v in fields.items())
for name, fields in evidence.RECORDS.items()]
rows += [f"R2_P6_AUDIO bits={bits} frames=256 ws_frames=2 background_jobs=8 "
f"tcd={int(bits == 24)} elapsed_us=7000 cycles=168000 instructions=40000"
for bits in (16, 24)]
rows += [f"R2_P6_I2S_WIRE bits={bits} samples=512" for bits in (16, 24)]
rows += [f"R2_P6_AXI owner={owner} read_bytes=1024 write_bytes=1024 "
f"descriptor_bytes={256 if owner == 1 else 0} ar_stall_max=3 aw_stall_max=3 "
"w_stall_max=3 r_stall_max=3 b_stall_max=3" for owner in range(3)]
rows += ["R2_P6_CONTENTION sdio_dma_cycles=100 cpu_fetches=10"]
return "\n".join([*rows, "R2_P6_DEVICES ws_frames=4 ws_wait_max=100 fifo_max=16 "
"underflows=1 aborts=1 clock_stops=2", "SIM_TEST_PASS Tiny cycles=200000"])
def test_terminal_verdict_survives_partial_uart_line():
flow = {"status": "passed", "exit_code": 0}
content = p6_log().replace("SIM_TEST_PASS Tiny cycles=", "Tiny acceptance passed coSIM_TEST_PASS Tiny cycles=")
assert evidence.parse_log(content, flow)["uart"]["echo_bytes"] == 4
for replacement in ("SIM_TEST_PASS Tiny JTAG transport halt/resume", "Hello retroSoC!",
"SIM_TEST_PASS Mini cycles=200000", "SIM_TEST_PASS Tiny cycles=unknown"):
with pytest.raises(ValueError):
evidence.parse_log(content.replace("SIM_TEST_PASS Tiny cycles=200000", replacement), flow)
@pytest.mark.parametrize("mutation", ["none", "failed_command", "missing", "duplicate", "budget",
"overflow", "false_qualification", "error", "no_terminal"])
def test_p6_evidence_is_fail_closed(mutation):
log = p6_log()
flow = {"status": "passed", "exit_code": 0}
if mutation == "failed_command":
flow["exit_code"] = 1
elif mutation == "missing":
log = "\n".join(line for line in log.splitlines() if not line.startswith("R2_P6_RNG"))
elif mutation == "duplicate":
log += "\n" + log.splitlines()[0]
elif mutation == "budget":
log = log.replace("ws_wait_max=100", "ws_wait_max=4801")
elif mutation == "overflow":
log = log.replace("fifo_max=16", "fifo_max=17")
elif mutation == "false_qualification":
log = log.replace("unqualified=1", "unqualified=0")
elif mutation == "error":
log += "\nSIM_TEST_FAIL"
elif mutation == "no_terminal":
log = log.replace("SIM_TEST_PASS", "Hello retroSoC!")
if mutation == "none":
assert len(evidence.parse_log(log, flow)["audio"]) == 2
else:
with pytest.raises(ValueError):
evidence.parse_log(log, flow)
@pytest.mark.parametrize("mutation", ["none", "failed", "old", "unfinished", "reversed"])
def test_p6_gate_chronology(tmp_path, mutation):
flow = {"status": "passed", "exit_code": 0,
"started_at": "2026-10-12T01:00:00+00:00", "finished_at": "2026-10-12T01:01:00+00:00"}
if mutation == "failed":
flow["exit_code"] = 1
elif mutation == "old":
flow["started_at"] = "2026-10-11T01:00:00+00:00"
elif mutation == "unfinished":
del flow["finished_at"]
elif mutation == "reversed":
flow["finished_at"] = "2026-10-12T00:30:00+00:00"
path = tmp_path / "gate.json"
path.write_text(json.dumps(flow))
if mutation == "none":
assert evidence.checked_flow(path, "2026-10-12T00:59:00+00:00") == flow
else:
with pytest.raises(ValueError):
evidence.checked_flow(path, "2026-10-12T00:59:00+00:00")
@pytest.mark.parametrize("mutation", ["none", "source", "tool", "old_model", "missing_image"])
def test_p6_complete_collection_requires_current_inputs(tmp_path, monkeypatch, mutation):
"""Exercise real artifact retention with synthetic, explicitly unit-test flows."""
directory = tmp_path / "evidence"
directory.mkdir()
source = tmp_path / "source.sv"
source.write_text("unit-test source\n")
source_inputs = {"source.sv": evidence.b.sha256(source)}
evidence.b.dump(directory / "inputs.json", source_inputs)
for name in ("patch", "snapshot"):
(directory / name).write_text("unit-test artifact\n")
saved_source = {name: evidence.b.artifact(directory / filename) for name, filename in
(("inputs", "inputs.json"), ("patch", "patch"), ("snapshot", "snapshot"))}
variants = [tmp_path / pdk for pdk in ("ICS55", "IHP130")]
captured = "2026-10-12T00:00:00+00:00"
evidence.b.dump(directory / "source.json", {"phase": evidence.PHASE, "source": saved_source,
"tools": {"unit-test": 1}, "captured_at": captured, "variants": list(map(str, variants))})
monkeypatch.setattr(evidence, "ROOT", tmp_path)
monkeypatch.setattr(evidence, "inputs", lambda _: {"source.sv": evidence.b.sha256(source)})
monkeypatch.setattr(evidence, "current_tools", lambda: {"unit-test": 2 if mutation == "tool" else 1})
monkeypatch.setattr(evidence.b, "configuration", lambda p: {
"configuration": {"PDK": p.name, "APP": "ci_smoke"}})
monkeypatch.setattr(evidence.b, "runtime_inputs", lambda *_: {"unit-test": "explicit fixture"})
for variant in variants:
for relative in ("sw/firmware", "sw/retrosoc_fw.hex", "sw/retrosoc_fw.map", "sw/retrosoc_fw.bin",
"sw/ld2_all_sram.lds", "sw/memory_regions.ld", "meta/manifest.json"):
path = variant / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("unit-test artifact\n")
for simulator in ("iverilog", "verilator"):
folder = variant / "sim" / simulator
folder.mkdir(parents=True)
for name in ("compile.log", "simv" if simulator == "iverilog" else "emu"):
(folder / name).write_text("unit-test artifact\n")
(folder / "sim.log").write_text(p6_log())
evidence.b.dump(folder / "result-sim-check.json", {"status": "passed"})
for label, start, end in (("compile", "01", "02"), ("sim", "03", "04")):
evidence.b.dump(folder / f"result-{label}.json", {"status": "passed", "exit_code": 0,
"started_at": f"2026-10-12T00:{start}:00+00:00",
"finished_at": f"2026-10-12T00:{end}:00+00:00"})
if mutation == "source":
source.write_text("changed unit-test source\n")
elif mutation == "old_model":
path = variants[0] / "sim/iverilog/result-compile.json"
flow = evidence.b.read_json(path)
flow["started_at"] = "2026-10-11T00:00:00+00:00"
evidence.b.dump(path, flow)
elif mutation == "missing_image":
(variants[0] / "sw/retrosoc_fw.hex").unlink()
if mutation == "none":
evidence.report(directory, [], variants)
report = evidence.b.read_json(directory / "report.json")
assert report["status"] == "functional_evidence_collected"
assert len(report["simulations"]) == 4
for simulation in report["simulations"].values():
for artifact in simulation["artifacts"]:
evidence.b.check_artifact(artifact)
else:
with pytest.raises((ValueError, FileNotFoundError)):
evidence.report(directory, [], variants)
assert not (directory / "report.json").exists()
@pytest.mark.parametrize("name", ["rng", "crc"])
def test_locked_controller_contracts(tmp_path, name):
for tool in ("iverilog", "vvp", "verilator", "cc"):
assert shutil.which(tool), f"required tool unavailable: {tool}"
source = ROOT / "rtl/managed/clusterip" / name
result = subprocess.run(["make", "-C", str(source), f"BUILD_DIR={tmp_path}",
"register-check", "test"], text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, timeout=180)
(tmp_path / "build.log").write_text(result.stdout)
assert result.returncode == 0, result.stdout[-12000:]
for simulator, marker in (("iverilog", f"{name.upper()}_TEST_PASS"),
("verilator", f"APB4_{name.upper()}_TEST_PASS")):
log = (tmp_path / simulator / "test.log").read_text()
assert marker in log and not DEFAULT_FAILURE.search(log), log
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_shared_i2s_payload(tmp_path, simulator):
common = ROOT / "rtl/managed/clusterip/common/rtl"
serial = ROOT / "rtl/ip/serial"
for tool in (simulator, "sv2v", "vvp"):
assert shutil.which(tool), f"required tool unavailable: {tool}"
sources = [common / name for name in (
"interface/apb4_if.sv", "interface/axi4_stream_if.sv", "utils/register.sv",
"utils/bin2gray.sv", "utils/gray2bin.sv", "utils/spill_register.sv",
"cdc/cdc_sync.sv", "cdc/cdc_rst_ctrlr.sv", "cdc/async_reqack.sv", "cdc/cdc_fifo.sv",
"cdc/cdc_2phase.sv", "cdc/cdc_warm_flush.sv", "utils/edge_det.sv", "clkrst/rst_sync.sv")]
sources += [ROOT / "rtl/tech/tc_clk.sv"]
sources += [serial / name for name in ("i2s_pkg.sv", "i2s_reg.sv", "i2s_clkgen.sv",
"i2s_recv.sv", "i2s_send.sv", "i2s_core.sv", "apb4_i2s.sv")]
sources += [ROOT / "tests/rtl/tiny_r2_p6_shared_tb.sv"]
flags = ["-DPDK_BEHAV", "-DSV_ASSRT_DISABLE", *[f"-I{p}" for p in (
common, common / "interface", common / "utils", common / "cdc", common / "clkrst", serial)]]
top = "tiny_r2_p6_shared_tb"
output = tmp_path / "sim"
if simulator == "iverilog":
converted = subprocess.run(["sv2v", *flags, *map(str, sources)], capture_output=True,
text=True, timeout=180)
assert converted.returncode == 0, converted.stderr
(tmp_path / "converted.v").write_text(converted.stdout)
run([simulator, "-g2012", "-s", top, "-o", str(output), str(tmp_path / "converted.v")],
tmp_path, "compile", timeout=180)
launch = ["vvp", str(output)]
else:
run([simulator, "--binary", "--timing", "--assert", "-Wno-fatal", "-j", "3",
"--top-module", top, *flags, *map(str, sources), "--Mdir", str(tmp_path / "obj"),
"-o", str(output)], tmp_path, "compile", timeout=180)
launch = [str(output)]
content = run(launch, tmp_path, "sim", timeout=180)
assert "SIM_TEST_PASS Tiny P6 I2S payload" in content
for bits in (16, 24):
assert f"P6_I2S bits={bits} samples=512" in content
def test_p6_map_irq_request_and_pin_contract():
topology = read_topology(TINY / "integration/soc_topology.json", TINY / "address_map/memory_map.json")
regions = {r["symbol"]: int(r["base"], 0) for r in json.loads(
(TINY / "address_map/memory_map.json").read_text())["regions"]}
for name, base in {"RNG": 0x20001000, "CRC": 0x20006000, "WS2812": 0x10008000,
"CRYPTO": 0x1000C000, "I2S": 0x10007000, "SDIO0": 0x1000F000}.items():
assert regions["APB4_" + name] == base
assert not {"APB4_UART1", "APB4_I2C1", "APB4_DVP", "APB4_SPI0", "APB4_PIOLITE"} & regions.keys()
irqs = {i["name"]: i["core_bit"] for i in topology["interrupts"]}
assert {k: irqs[k] for k in ("rng", "ws2812", "crypto", "i2s", "sdio")} == {
"rng": 16, "ws2812": 17, "crypto": 23, "i2s": 8, "sdio": 10}
assert {15, 19, 24, 25, 26, 27, 31}.isdisjoint(irqs.values())
pins = topology["gpio_alt_functions"]
assert pins[9]["alt0"]["inputs"] == ["u_uart0_if.rx_i"]
assert pins[10]["alt0"]["inputs"] == ["u_i2c0_if.scl_i"]
assert pins[11]["alt0"]["inputs"] == ["u_i2c0_if.sda_i"]
assert pins[26]["alt0"]["do"] == "u_ws2812_if.dat_o"
for pin in (24, 25):
assert pins[pin]["alt0"]["inputs"] == [f"u_pwm_if.capture_i[{pin - 24}]"]
assert pins[pin]["alt1"]["inputs"] == []
for pin in range(12, 24):
assert pins[pin]["alt1"]["oe"] == "1'b0"
validate_clocks(TINY / "integration/clock_reset_domains.json", ROOT)
def test_duplicate_input_routes_are_rejected(tmp_path):
topology = json.loads((TINY / "integration/soc_topology.json").read_text())
topology["gpio_alt_functions"][25]["alt1"]["inputs"] = ["u_uart0_if.rx_i"]
path = tmp_path / "topology.json"
path.write_text(json.dumps(topology))
with pytest.raises(ValueError, match="exclusive route"):
read_topology(path, TINY / "address_map/memory_map.json")
@pytest.mark.parametrize("enabled", ["YES", "NO"])
def test_tiny_sva_build_selection_reaches_owned_assertions(enabled):
result = subprocess.run(["make", "-n", "CONFIG=configs/ci/ics55-tiny.mk", "SOC=TINY",
"PDK=ICS55", "SIMU=VERILATOR", f"HAVE_SVA={enabled}",
"BUILD_TIMESTAMP=2099-01-01-00-00", "comp"], cwd=ROOT,
capture_output=True, text=True, check=True)
assert ("+define+HAVE_SVA" in result.stdout) == (enabled == "YES")
assert ("+define+SV_ASSRT_DISABLE" in result.stdout) == (enabled == "NO")
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_board_pullups_survive_partial_external_drivers(tmp_path, simulator):
assert shutil.which(simulator), f"required simulator unavailable: {simulator}"
generate_bindings(read_topology(TINY / "integration/soc_topology.json",
TINY / "address_map/memory_map.json"), tmp_path)
source = tmp_path / "board.sv"
drivers = "\n".join(f"assign s_gpio_{n}=oe[{n}] ? data[{n}] : 1'bz;" for n in range(32))
source.write_text('module board;\n`include "tiny_tb_gpio.svh"\n' +
"logic clk=0; logic [31:0] oe=0, data=0; always #5 clk=!clk;\n" + drivers +
"\nassign s_gpio_17=clk; assign s_gpio_15=s_gpio_14;\n" + r'''
initial begin
#21;
if (s_gpio[11:10] !== 2'b11) $fatal(1, "lost board pullups");
oe[10]=1;
#10;
if (s_gpio[11:10] !== 2'b10) $fatal(1, "open-drain drive/readback");
oe[10]=0;
#10;
if (s_gpio[11:10] !== 2'b11) $fatal(1, "open-drain release/readback");
$display("SIM_TEST_PASS Tiny P6 board pulls");
$finish;
end
endmodule
''')
output = tmp_path / "sim"
flags = ["-I" + str(tmp_path / "rtl")]
if simulator == "iverilog":
run([simulator, "-g2012", "-s", "board", *flags, "-o", str(output), str(source)],
tmp_path, "compile")
launch = ["vvp", str(output)]
else:
run([simulator, "--binary", "--timing", "-Wno-fatal", "--top-module", "board",
*flags, str(source), "--Mdir", str(tmp_path / "obj"), "-o", str(output)],
tmp_path, "compile", timeout=180)
launch = [str(output)]
assert "SIM_TEST_PASS Tiny P6 board pulls" in run(launch, tmp_path, "sim")
@pytest.mark.parametrize("tiny", [True, False])
def test_dma_sdk_request_matrix(tmp_path, tiny):
assert shutil.which("cc"), "host compiler required"
if tiny:
generate_bindings(read_topology(TINY / "integration/soc_topology.json",
TINY / "address_map/memory_map.json"), tmp_path)
else:
extension = tmp_path / "include/retrosoc/generated/user_extensions.h"
extension.parent.mkdir(parents=True)
extension.write_text("")
generate_memory_map(ROOT / ("rtl/tiny" if tiny else "rtl/mini") / "address_map/memory_map.json",
tmp_path / "map", "YES", 128 if tiny else 32)
probe = tmp_path / "probe.c"
probe.write_text(r'''
#include <assert.h>
#include <retrosoc/hal/dma.h>
int main(void) {
_Alignas(64) rs_dma_tcd_t tcd = {0};
rs_dma_config_t c = {.kind=RS_DMA_KIND_MM_TO_MM, .source=0x30000000U,
.destination=0x30010000U, .byte_count=64U, .width=RS_DMA_WIDTH_32,
.source_increment=true, .destination_increment=true, .burst_beats=16U};
for (unsigned channel=0; channel<8; ++channel) {
for (unsigned request=0; request<16; ++request) {
c.request=(rs_dma_request_t)request;
c.kind=(request==1 || request==12) ? RS_DMA_KIND_MM_TO_STREAM :
((request==2 || request==11 || request==13) ? RS_DMA_KIND_STREAM_TO_MM :
RS_DMA_KIND_MM_TO_MM);
#ifdef RS_SOC_TINY
bool supported=(0x31ffU & (1U << request)) != 0U;
#else
bool supported=request < 14U;
#endif
assert((rs_dma_config_validate(channel, &c)==RS_OK)==supported);
tcd.source=c.source; tcd.destination=c.destination; tcd.byte_count=64U;
tcd.control=RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC |
((unsigned)c.kind << RS_DMA_TCD_KIND_SHIFT) |
(request << RS_DMA_TCD_REQUEST_SHIFT) | (16U << RS_DMA_TCD_BURST_SHIFT);
assert((rs_dma_tcd_validate(channel, &tcd)==RS_OK)==supported);
}
}
assert(rs_dma_config_validate(8, &c)!=RS_OK);
c.request=(rs_dma_request_t)16;
assert(rs_dma_config_validate(0, &c)!=RS_OK);
c.request=(rs_dma_request_t)17;
assert(rs_dma_config_validate(0, &c)!=RS_OK);
c.request=(rs_dma_request_t)255;
assert(rs_dma_config_validate(0, &c)!=RS_OK);
return 0;
}
''')
binary = tmp_path / "probe"
command = ["cc", "-std=c11", "-Wall", "-Wextra", "-Werror",
"-I" + str(ROOT / "crt/include"), "-I" + str(tmp_path / "include"),
"-I" + str(tmp_path / "map/include"), str(probe),
str(ROOT / "crt/src/hal/dma_math.c"), "-o", str(binary)]
subprocess.run(command, check=True, capture_output=True, text=True)
subprocess.run([str(binary)], check=True, capture_output=True, text=True)
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'h31ff" 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
assert ".STAGE(5)" in tree