mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
65 files changed
+4139
-1003
No files matched your search
@@ -420,6 +420,8 @@ endif
|
||||
|
||||
ifeq ($(HAVE_SVA), NO)
|
||||
DEF_LIST += +define+SV_ASSRT_DISABLE
|
||||
else ifeq ($(SOC),TINY)
|
||||
DEF_LIST += +define+HAVE_SVA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_HP), YES)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
|
||||
APP_CFLAGS += -Os
|
||||
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
@@ -20,12 +20,15 @@
|
||||
#include <retrosoc/lib/printf.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_destination[80];
|
||||
static volatile uint32_t rs_mcu_irq_count;
|
||||
static volatile uint32_t rs_mcu_exception_count;
|
||||
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) {
|
||||
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) {
|
||||
volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer;
|
||||
|
||||
if ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5))) {
|
||||
rs_mcu_handler_failed = true;
|
||||
if (!rs_mcu_probes_active || ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5)))) {
|
||||
rs_mcu_finish(false, 11U);
|
||||
}
|
||||
++rs_mcu_exception_count;
|
||||
/* 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_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_start(RS_DMA_CHANNEL_BULK) != 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;
|
||||
}
|
||||
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) {
|
||||
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)};
|
||||
uint32_t receive[4] = {0U};
|
||||
rs_gpio_capabilities_t capabilities;
|
||||
rs_uart_timing_t timing;
|
||||
bool high;
|
||||
uint32_t timeout = UINT32_C(10000);
|
||||
uint8_t payload = UINT8_C(0x55);
|
||||
const rs_i2c_transfer_t transfer = {
|
||||
.address = UINT16_C(0x50),
|
||||
@@ -175,54 +176,51 @@ static bool rs_mcu_io(void) {
|
||||
};
|
||||
rs_i2c_status_t status;
|
||||
|
||||
rs_mcu_io_code = 40U;
|
||||
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
|
||||
((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)
|
||||
: 0U)) ||
|
||||
(rs_gpio_configure(15U, &pull_up) !=
|
||||
(rs_gpio_configure(27U, &pull_up) !=
|
||||
((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_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) ||
|
||||
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) ||
|
||||
(rs_gpio_read(15U, &high) != RS_OK) || high) {
|
||||
(rs_gpio_configure(27U, &output) != RS_OK) || (rs_gpio_write(27U, true) != RS_OK) ||
|
||||
(rs_gpio_read(27U, &high) != RS_OK) || !high || (rs_gpio_write(27U, false) != RS_OK) ||
|
||||
(rs_gpio_read(27U, &high) != RS_OK) || high) {
|
||||
return false;
|
||||
}
|
||||
rs_mcu_io_code = 41U;
|
||||
if ((rs_uart_configure(&loopback, 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)) {
|
||||
return false;
|
||||
}
|
||||
rs_mcu_io_code = 42U;
|
||||
for (uint32_t index = 0U; index < 4U; ++index) {
|
||||
if (transmit[index] != receive[index]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
rs_mcu_io_code = 43U;
|
||||
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)) {
|
||||
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))) {
|
||||
(rs_i2c_init(RS_I2C_BUS_1, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_ENOTSUP)) {
|
||||
return false;
|
||||
}
|
||||
/* 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. */
|
||||
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;
|
||||
if ((rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_OK) ||
|
||||
(rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
|
||||
rs_status_t init_result = rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000));
|
||||
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) ||
|
||||
((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) {
|
||||
return false;
|
||||
@@ -255,6 +253,7 @@ int main(void) {
|
||||
rs_watchdog_status_t watchdog = {0};
|
||||
uint64_t before;
|
||||
uint64_t after;
|
||||
uint8_t p6_result;
|
||||
const rs_watchdog_config_t watchdog_config = {
|
||||
.prescale_divider = 1U,
|
||||
.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) ||
|
||||
(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_probes_active = true;
|
||||
rs_mcu_atomic_probe(rs_mcu_source);
|
||||
rs_mcu_unmapped_probe();
|
||||
rs_mcu_probes_active = false;
|
||||
if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed ||
|
||||
(rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending ||
|
||||
(fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) {
|
||||
@@ -307,7 +309,11 @@ int main(void) {
|
||||
rs_mcu_finish(false, 8U);
|
||||
}
|
||||
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");
|
||||
if ((rs_watchdog_configure(&watchdog_config) != RS_OK) ||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
|
||||
APP_CFLAGS += -Os
|
||||
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,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;
|
||||
}
|
||||
@@ -50,6 +50,15 @@ typedef enum {
|
||||
RS_DMA_CHANNEL_BULK = 3,
|
||||
RS_DMA_CHANNEL_CRYPTO_IN = 4,
|
||||
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;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -77,15 +77,13 @@
|
||||
#define RS_DMA_ERROR_READ UINT32_C(0x00000040)
|
||||
#define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100)
|
||||
|
||||
#ifdef RS_SOC_TINY
|
||||
#define RS_DMA_CHANNEL_COUNT 4U
|
||||
#else
|
||||
#define RS_DMA_CHANNEL_COUNT 8U
|
||||
#endif
|
||||
#define RS_DMA_CHANNEL_COUNT 8U
|
||||
#ifndef RS_SOC_TINY
|
||||
#define RS_DMA_CHANNEL_HP 6U
|
||||
#define RS_DMA_CHANNEL_RESERVED 7U
|
||||
#define RS_DMA_MAX_BURST_BEATS 16U
|
||||
#define RS_DMA_DATA_WIDTH_BITS 32U
|
||||
#endif
|
||||
#define RS_DMA_MAX_BURST_BEATS 16U
|
||||
#define RS_DMA_DATA_WIDTH_BITS 32U
|
||||
|
||||
#define RS_DMA_CHANNEL_OFFSET(channel, offset) \
|
||||
(RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset))
|
||||
|
||||
+5
-5
@@ -427,9 +427,9 @@ void ip_dma_test(void) {
|
||||
.lowbound = 32U,
|
||||
}) != 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_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");
|
||||
return;
|
||||
}
|
||||
@@ -448,9 +448,9 @@ void ip_dma_test(void) {
|
||||
.lowbound = 32U,
|
||||
}) != RS_OK) ||
|
||||
(rs_i2s_enable(false, true) != RS_OK) ||
|
||||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &rx_config) != 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_configure(RS_DMA_CHANNEL_I2S_RX, &rx_config) != RS_OK) ||
|
||||
(rs_dma_start(RS_DMA_CHANNEL_I2S_RX) != RS_OK) ||
|
||||
(rs_dma_wait(RS_DMA_CHANNEL_I2S_RX, RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
||||
printf("dma receive failed\n");
|
||||
(void)rs_i2s_disable();
|
||||
return;
|
||||
|
||||
@@ -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) {
|
||||
#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;
|
||||
}
|
||||
#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);
|
||||
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -49,8 +49,12 @@
|
||||
#define RS_I2C_TIMEOUT_MAX_CYCLES UINT32_C(0xFFFFFF)
|
||||
|
||||
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
|
||||
: (uintptr_t)RS_SOC_APB4_I2C1_BASE;
|
||||
#endif
|
||||
}
|
||||
|
||||
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,
|
||||
.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 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);
|
||||
|
||||
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_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_i2c_timing_t timing;
|
||||
uint32_t stretch_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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
const rs_i2c_config_t config = {
|
||||
.source_clock_hz = source_clock_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_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
if (transfer == NULL) {
|
||||
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,
|
||||
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];
|
||||
size_t prefix_length;
|
||||
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,
|
||||
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];
|
||||
size_t prefix_length;
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
if (!rs_i2c_bus_valid(bus)) {
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t levels;
|
||||
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#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)) {
|
||||
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,
|
||||
const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace,
|
||||
rs_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
size_t index;
|
||||
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,
|
||||
size_t length, rs_i2c_dma_workspace_t *workspace,
|
||||
rs_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (bus == RS_I2C_BUS_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
size_t index;
|
||||
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
|
||||
|
||||
+215
-5
@@ -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_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) {
|
||||
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) {
|
||||
case RS_SDIO_INSTANCE_0:
|
||||
return (uintptr_t)RS_SOC_APB4_SDIO0_BASE;
|
||||
#ifndef RS_SOC_TINY
|
||||
case RS_SDIO_INSTANCE_1:
|
||||
return (uintptr_t)RS_SOC_APB4_SDIO1_BASE;
|
||||
#endif
|
||||
default:
|
||||
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,
|
||||
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) ||
|
||||
(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) ||
|
||||
@@ -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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
volatile uint32_t *status_reg;
|
||||
|
||||
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,
|
||||
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;
|
||||
uint32_t value;
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t value;
|
||||
|
||||
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,
|
||||
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) ||
|
||||
(data_timeout == 0U) || (busy_timeout == 0U)) {
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t value;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_sdio_clock_t clock;
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t present;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t cfg;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
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_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_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
|
||||
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_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;
|
||||
|
||||
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,
|
||||
const rs_sdio_data_config_t *config) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t value;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
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_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_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;
|
||||
rs_status_t status;
|
||||
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_timeout_t timeout) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint8_t *bytes = (uint8_t *)buffer;
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
|
||||
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,
|
||||
uint16_t count, uint32_t total_bytes) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uintptr_t address;
|
||||
|
||||
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) {
|
||||
#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
|
||||
* 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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
if (!rs_sdio_instance_valid(instance)) {
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t dma_error;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_status_t status;
|
||||
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
if (!rs_sdio_instance_valid(instance) || (mask == NULL)) {
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#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)) {
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
rs_sdio_status_t status;
|
||||
uint32_t ip_id;
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
info->actual_clock_hz = clock.actual_hz;
|
||||
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_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_clock_t clock;
|
||||
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) {
|
||||
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)) {
|
||||
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 = 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) {
|
||||
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,
|
||||
uint32_t block, uint32_t count, void *buffer,
|
||||
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);
|
||||
}
|
||||
|
||||
rs_status_t rs_sd_memory_write_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
|
||||
uint32_t block, uint32_t count, const void *buffer,
|
||||
rs_timeout_t timeout) {
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
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 = {
|
||||
.function = function,
|
||||
.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,
|
||||
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 = {
|
||||
.function = function,
|
||||
.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_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_clock_t clock;
|
||||
uint32_t payload;
|
||||
@@ -1345,8 +1528,10 @@ rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t so
|
||||
} else {
|
||||
info->high_speed_fallback = true;
|
||||
}
|
||||
status = rs_sdio_clock_set(instance, source_clock_hz,
|
||||
high_speed_enabled ? 24000000U : 25000000U, &clock);
|
||||
status = rs_sdio_clock_set(
|
||||
instance, source_clock_hz,
|
||||
rs_sdio_transfer_clock(source_clock_hz, high_speed_enabled ? 24000000U : 25000000U),
|
||||
&clock);
|
||||
if (status != RS_OK) {
|
||||
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,
|
||||
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 ready;
|
||||
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,
|
||||
bool write, bool block_mode, bool fixed_address,
|
||||
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_data_config_t data_config;
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t mask;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
uint32_t mask;
|
||||
|
||||
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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (instance == RS_SDIO_INSTANCE_1) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
return rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DAT1);
|
||||
}
|
||||
@@ -136,7 +136,9 @@ rs_status_t rs_sd_memory_parse_csd(const rs_sdio_response_t *response, rs_sd_mem
|
||||
return RS_EINVAL;
|
||||
}
|
||||
block_length = UINT32_C(1) << read_bl_len;
|
||||
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT64_C(1) << (c_size_mult + 2U)) *
|
||||
/* 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;
|
||||
capacity_blocks = capacity_bytes / (uint64_t)RS_SD_MEMORY_BLOCK_SIZE;
|
||||
info->card_type = RS_SD_MEMORY_SDSC;
|
||||
|
||||
@@ -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;
|
||||
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef RS_SOC_TINY
|
||||
if (config->auto_cts_enable || config->auto_rts_enable) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
RS_UART_CTRL = 0U;
|
||||
while (((RS_UART_STATUS & (RS_UART_STATUS_TX_BUSY | RS_UART_STATUS_RX_BUSY)) != 0U) &&
|
||||
(timeout != 0U)) {
|
||||
|
||||
@@ -18,7 +18,7 @@ TOP := retrosoc_tiny_asic
|
||||
# validated against rtl/tiny/integration/clock_reset_domains.json when the
|
||||
# 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.
|
||||
COMMERCIAL_CLOCK_DOMAINS := system jtag
|
||||
COMMERCIAL_CLOCK_DOMAINS := system jtag audio
|
||||
|
||||
# Qualified-I/O interface groups and the budget variables each group requires.
|
||||
COMMERCIAL_IO_INTERFACES := JTAG XPI ASYNC
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
# retroSoC Tiny (retrosoc_tiny_asic) product overlay.
|
||||
#
|
||||
# Clock intent: the canonical contract (rtl/tiny/integration/
|
||||
# clock_reset_domains.json) owns two domains, system (SAFE24 external clock)
|
||||
# and jtag. The ICS55 PLL_TOP macro is present but parked off (EN=0) and is
|
||||
# clock_reset_domains.json) owns system (SAFE24 external clock), jtag and
|
||||
# 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
|
||||
# overlay clock is created here. See docs/ip/tiny-soc.md (TINY-055).
|
||||
|
||||
namespace eval product {}
|
||||
|
||||
proc product::expected_clock_domains {} {
|
||||
return {system jtag}
|
||||
return {system jtag audio}
|
||||
}
|
||||
|
||||
proc product::custom_clock_domains {} {
|
||||
@@ -27,7 +27,7 @@ proc product::custom_clock_domains {} {
|
||||
}
|
||||
|
||||
proc product::clock_names {} {
|
||||
return {clk_system clk_jtag}
|
||||
return {clk_system clk_jtag clk_audio}
|
||||
}
|
||||
|
||||
proc product::io_interface_groups {} {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
set audit_dir [file dirname $::env(OPENSTA_REPORT)]
|
||||
set pll [get_cells -hierarchical -filter {ref_name == PLL_TOP}]
|
||||
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" }
|
||||
report_clock_properties > $audit_dir/clocks.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_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 {}
|
||||
for {set group 0} {$group < 4} {incr group} {
|
||||
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
|
||||
}
|
||||
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 {}
|
||||
foreach pin [get_pins -of_objects $cell] {
|
||||
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]"
|
||||
close $p4_clock_report
|
||||
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"
|
||||
@@ -125,6 +125,26 @@ def clean_source(text: str) -> str:
|
||||
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:
|
||||
for relative in dependencies(spec):
|
||||
path = root / relative
|
||||
@@ -193,7 +213,7 @@ def collect_programming(root: Path, spec: dict, ids: set[str], revision: str) ->
|
||||
result = copy.deepcopy(spec)
|
||||
sdk_text = (root / "crt/include/retrosoc/hal/dma.h").read_text()
|
||||
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.
|
||||
# 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(),
|
||||
|
||||
+13
-14
@@ -24,14 +24,14 @@ module i2s_recv (
|
||||
logic s_lrck_trg;
|
||||
logic [4:0] s_bit_cnt_d, s_bit_cnt_q;
|
||||
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_data_d, s_data_q;
|
||||
|
||||
|
||||
assign s_bit_num = bitmode_i ? 5'd23 : 5'd15;
|
||||
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;
|
||||
|
||||
|
||||
@@ -89,26 +89,25 @@ module i2s_recv (
|
||||
);
|
||||
|
||||
|
||||
always_comb begin
|
||||
s_first_bit_d = s_first_bit_q;
|
||||
if (s_lrck_trg) s_first_bit_d = adcdat_i;
|
||||
end
|
||||
dffer #(
|
||||
// I2S changes LRCK one bit before the next sample's MSB. The bit
|
||||
// sampled on that transition is the current sample's LSB, not the next
|
||||
// sample's first bit. Publish completion after data_o has captured it.
|
||||
assign s_done_d = sclk_pos_i && s_lrck_trg;
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_first_bit_dffer (
|
||||
) u_done_dffr (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.en_i (sclk_pos_i),
|
||||
.dat_i (s_first_bit_d),
|
||||
.dat_o (s_first_bit_q)
|
||||
.dat_i (s_done_d),
|
||||
.dat_o (s_done_q)
|
||||
);
|
||||
|
||||
|
||||
always_comb begin
|
||||
s_data_d = s_data_q;
|
||||
if ((!s_lrck_trg) && (s_bit_cnt_q == (s_bit_num - 1'b1))) begin
|
||||
if (bitmode_i) s_data_d = {s_recv_data_d[22:0], s_first_bit_q};
|
||||
else s_data_d = {8'd0, s_recv_data_d[14:0], s_first_bit_q};
|
||||
if (s_lrck_trg) begin
|
||||
if (bitmode_i) s_data_d = {s_recv_data_q[22:0], adcdat_i};
|
||||
else s_data_d = {8'd0, s_recv_data_q[14:0], adcdat_i};
|
||||
end
|
||||
end
|
||||
dffer #(
|
||||
|
||||
@@ -92,7 +92,7 @@ module i2s_reg (
|
||||
endfunction
|
||||
|
||||
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_offset = apb4.paddr[11:0];
|
||||
assign s_aligned = apb4.paddr[1:0] == 2'b00;
|
||||
|
||||
@@ -88,7 +88,10 @@ module sdio_command #(
|
||||
assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1];
|
||||
assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q;
|
||||
|
||||
assign cmd_oe_o = (s_state_q == Send) || (s_tx_final_hold_q && !sample_tick_i);
|
||||
// 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 busy_o = (s_state_q != Idle) && (s_state_q != Complete);
|
||||
assign done_o = s_done_q;
|
||||
|
||||
@@ -102,7 +102,7 @@ module sdio_core #(
|
||||
logic s_data_tx_last;
|
||||
logic s_data_rx_valid;
|
||||
logic s_data_rx_ready;
|
||||
logic s_data_rx_stall_q;
|
||||
logic s_data_rx_stall;
|
||||
logic [ 31:0] s_data_rx_data;
|
||||
logic [ 3:0] s_data_rx_strb;
|
||||
logic s_data_rx_last;
|
||||
@@ -174,8 +174,8 @@ module sdio_core #(
|
||||
assign s_clock_en = host_enable_i && clock_enable_i &&
|
||||
(s_cmd_busy || s_data_busy || s_dma_busy || cmd_start_i ||
|
||||
data_start_i) &&
|
||||
!s_data_rx_stall_q &&
|
||||
!(s_data_busy && s_data_tx_wait && (s_data_oe == 4'b0000));
|
||||
!s_data_rx_stall &&
|
||||
!(s_data_busy && s_data_tx_wait);
|
||||
assign s_total_bytes = {16'd0, block_size_i} * {16'd0, block_count_i};
|
||||
assign s_abort_request = host_abort_i || dma_abort_i ||
|
||||
(s_dma_done && s_dma_err) ||
|
||||
@@ -200,6 +200,9 @@ module sdio_core #(
|
||||
assign s_dma_data_in_strb = s_data_rx_strb;
|
||||
assign s_dma_data_in_last = s_data_rx_last;
|
||||
assign s_data_rx_ready = data_dma_enable_i ? s_dma_data_in_ready : !s_pio_valid_q;
|
||||
// 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 busy_o = s_core_busy;
|
||||
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_sync_q <= 1'b1;
|
||||
s_dat1_seen_q <= 1'b0;
|
||||
s_data_rx_stall_q <= 1'b0;
|
||||
s_pio_rdata_q <= '0;
|
||||
s_pio_valid_q <= 1'b0;
|
||||
s_pio_tx_rd_q <= '0;
|
||||
@@ -418,10 +420,9 @@ module sdio_core #(
|
||||
s_pio_tx_strb_q[index] <= '0;
|
||||
end
|
||||
end else begin
|
||||
s_dat1_meta_q <= sdio.dat_di_i[1];
|
||||
s_dat1_sync_q <= s_dat1_meta_q;
|
||||
s_dat1_seen_q <= s_dat1_seen_d;
|
||||
s_data_rx_stall_q <= s_data_rx_valid && !s_data_rx_ready;
|
||||
s_dat1_meta_q <= sdio.dat_di_i[1];
|
||||
s_dat1_sync_q <= s_dat1_meta_q;
|
||||
s_dat1_seen_q <= s_dat1_seen_d;
|
||||
|
||||
if (data_start_i) begin
|
||||
s_pio_tx_rd_q <= '0;
|
||||
|
||||
@@ -160,7 +160,12 @@ module sdio_data #(
|
||||
assign tx_ready_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
|
||||
((s_state_q == WritePayload) && launch_tick_i && s_tx_word_final &&
|
||||
(s_tx_total_q < s_target_bytes_q));
|
||||
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord);
|
||||
// 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_data_o = s_rx_pending_data_q;
|
||||
assign rx_strb_o = s_rx_pending_strb_q;
|
||||
|
||||
@@ -516,11 +516,18 @@ module sdio_dma #(
|
||||
s_err_addr_d = s_payload_addr + s_burst_offset;
|
||||
end
|
||||
if (s_write_final_sent_q) begin
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
|
||||
s_state_d = WritebackStart;
|
||||
// A retired AXI burst is not necessarily the whole descriptor.
|
||||
// Keep ownership while subsequent bursts receive its remaining
|
||||
// bytes, just as the memory-to-card direction does.
|
||||
if (s_fault_d || (s_desc_progress_d >= s_desc_count_q)) begin
|
||||
s_writeback_control_d = (s_desc_control_q &
|
||||
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
|
||||
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
|
||||
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
|
||||
s_state_d = WritebackStart;
|
||||
end else begin
|
||||
s_state_d = PayloadWriteStart;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+5
-4
@@ -179,7 +179,8 @@ CFLAGS += -DRS_SOC_TINY
|
||||
TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \
|
||||
stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \
|
||||
gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \
|
||||
dma_math.c dma.c onchip_sram.c xpi.c
|
||||
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 += $(ROOT_PATH)/crt/src/hal/sysctrl.c
|
||||
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.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
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/crypto_constants.py --root $(ROOT_PATH) --output $(SW_BUILD_DIR)/include
|
||||
|
||||
endif
|
||||
|
||||
CRYPTO_CONSTANTS_DEP := $(if $(filter TINY,$(SOC)),,$(CRYPTO_CONSTANTS_HEADER))
|
||||
CRYPTO_CONSTANTS_DEP := $(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) \
|
||||
$(ROOT_PATH)/rtl/mk/software.mk $(APP_MK)
|
||||
|
||||
+7
-3
@@ -60,6 +60,7 @@ OPENSTA_CONFIG := $(SYN_BUILD_ROOT)/out/$(RTL_NAME)_yosys.config
|
||||
NETLIST_PATH := $(OPENSTA_NETLIST)
|
||||
TINY_CONVERTED := $(SIM_BUILD_ROOT)/converted_soc.v
|
||||
TINY_DEPFILE := $(SIM_BUILD_ROOT)/sources.d
|
||||
TINY_TB_DEPFILE := $(SIM_BUILD_ROOT)/testbench.d
|
||||
TINY_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log
|
||||
SOC_SIM_TIME ?= 1800
|
||||
TINY_SIM_TIMEOUT ?= 20000000
|
||||
@@ -69,7 +70,7 @@ IVERILOG ?= iverilog
|
||||
VVP ?= vvp
|
||||
|
||||
include $(ROOT_PATH)/rtl/mk/software.mk
|
||||
-include $(TINY_DEPFILE)
|
||||
-include $(TINY_DEPFILE) $(TINY_TB_DEPFILE)
|
||||
|
||||
$(TINY_CONVERTED): $(FILELIST_STAMP)
|
||||
@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 -- \
|
||||
$(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)
|
||||
$(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
|
||||
TINY_RUN := $(VVP) $(SIM_BUILD_ROOT)/simv
|
||||
endif
|
||||
@@ -204,7 +208,7 @@ tiny-r2-p3-report:
|
||||
|
||||
# P4 preserves old image identity and replays each selected image exactly once.
|
||||
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
|
||||
$(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) \
|
||||
@@ -220,7 +224,7 @@ tiny-r2-p4-report:
|
||||
# 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.
|
||||
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
|
||||
$(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) \
|
||||
|
||||
@@ -300,15 +300,6 @@
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_I2C1",
|
||||
"base": "0x10011000",
|
||||
"size": "0x00001000",
|
||||
"route": "apb4_periph",
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "rw"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_GPIO_ADMIN",
|
||||
"base": "0x10014000",
|
||||
@@ -327,15 +318,6 @@
|
||||
"public": true,
|
||||
"user_access": "none"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_UART1",
|
||||
"base": "0x10018000",
|
||||
"size": "0x00001000",
|
||||
"route": "apb4_periph",
|
||||
"kind": "active",
|
||||
"public": true,
|
||||
"user_access": "rw"
|
||||
},
|
||||
{
|
||||
"symbol": "APB4_ARCHINFO",
|
||||
"base": "0x20000000",
|
||||
@@ -399,6 +381,60 @@
|
||||
"public": true,
|
||||
"user_access": "rw",
|
||||
"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,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
`include "tiny_topology.svh"
|
||||
// Copyright (c) 2026 Yuchi Miao
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
`timescale 1ns / 1ps
|
||||
@@ -8,24 +9,27 @@ module retrosoc_tiny_tb;
|
||||
logic s_reset_driver = 1'b0;
|
||||
wire s_rst_n = s_reset_driver;
|
||||
wire s_uart0_tx, s_uart1_tx;
|
||||
wire s_uart0_rx = 1'b1;
|
||||
wire s_uart1_rx = 1'b1;
|
||||
logic s_jtag_tck_driver = 1'b0;
|
||||
logic s_jtag_tms_driver = 1'b1;
|
||||
logic s_jtag_tdi_driver = 1'b0;
|
||||
wire s_jtag_tck = s_jtag_tck_driver;
|
||||
wire s_jtag_tms = s_jtag_tms_driver;
|
||||
wire s_jtag_tdi = s_jtag_tdi_driver;
|
||||
wire s_jtag_trst_n = s_rst_n;
|
||||
wire s_jtag_tdo;
|
||||
tri1 [31:0] s_gpio;
|
||||
wire s_xpi_sck;
|
||||
wire [ 3:0] s_xpi_nss;
|
||||
tri [ 3:0] s_xpi_data;
|
||||
integer s_cycles;
|
||||
integer s_max_cycles = 20000000;
|
||||
integer s_done_cycles = 0;
|
||||
logic [ 7:0] s_uart_byte;
|
||||
wire s_uart0_rx = 1'b1;
|
||||
wire s_uart1_rx = 1'b1;
|
||||
logic s_jtag_tck_driver = 1'b0;
|
||||
logic s_jtag_tms_driver = 1'b1;
|
||||
logic s_jtag_tdi_driver = 1'b0;
|
||||
wire s_jtag_tck = s_jtag_tck_driver;
|
||||
wire s_jtag_tms = s_jtag_tms_driver;
|
||||
wire s_jtag_tdi = s_jtag_tdi_driver;
|
||||
wire s_jtag_trst_n = s_rst_n;
|
||||
wire s_jtag_tdo;
|
||||
`include "tiny_tb_gpio.svh"
|
||||
wire s_xpi_sck;
|
||||
wire [3:0] s_xpi_nss;
|
||||
tri [3:0] s_xpi_data;
|
||||
integer s_cycles;
|
||||
`ifndef RETROSOC_SOC__TINY_NETLIST
|
||||
`include "tiny_p6_devices.svh"
|
||||
`endif
|
||||
integer s_max_cycles = 20000000;
|
||||
integer s_done_cycles = 0;
|
||||
logic [7:0] s_uart_byte;
|
||||
`include "tiny_jtag_smoke.svh"
|
||||
|
||||
`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);
|
||||
end
|
||||
|
||||
`ifdef RETROSOC_SOC__TINY_NETLIST
|
||||
`ifdef SIMU_IVERILOG
|
||||
localparam bit ObserveUartAlways = 1'b1;
|
||||
`else
|
||||
localparam bit ObserveUartAlways = 1'b0;
|
||||
`endif
|
||||
`ifdef SIMU_IVERILOG
|
||||
localparam bit ObserveUartBaseline = 1'b1;
|
||||
`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.
|
||||
// The Verilator RTL prints accepted TX bytes. Icarus observes serial output for
|
||||
// all firmware measurements; SYSCTRL remains the authoritative verdict.
|
||||
initial begin
|
||||
if (ObserveUartAlways || (ObserveUartBaseline && ($test$plusargs(
|
||||
"tiny_r2_baseline"
|
||||
) || $test$plusargs(
|
||||
"tiny_r2_p3"
|
||||
)))) begin
|
||||
if (ObserveUartAlways) begin
|
||||
forever begin
|
||||
@(negedge s_uart0_tx);
|
||||
#1627.604;
|
||||
@@ -239,10 +234,13 @@ module retrosoc_tiny_tb;
|
||||
// Icarus 13 miscounts the temporary expression $countones(~a_23_bit_vector).
|
||||
// Count declared bits explicitly and reject unknowns rather than correcting
|
||||
// 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;
|
||||
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++;
|
||||
else if (idle_i[target_index] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P5 unknown target state");
|
||||
end
|
||||
@@ -250,11 +248,13 @@ module retrosoc_tiny_tb;
|
||||
endfunction
|
||||
initial begin : p5_monitor_check
|
||||
if ($test$plusargs("tiny_r2_p5_monitor_check")) begin
|
||||
for (integer active = 0; active <= 23; active++) begin
|
||||
if (p5_active_targets(23'h7fffff ^ ((23'd1 << active) - 23'd1)) != active)
|
||||
for (integer active = 0; active <= `RETROSOC_TINY__TARGET_COUNT; active++) begin
|
||||
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");
|
||||
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;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -22,3 +22,10 @@
|
||||
/clusterip/wdg/rtl/wdg_core.sv
|
||||
/clusterip/wdg/rtl/wdg_reg.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
|
||||
@@ -17,3 +17,6 @@
|
||||
+incdir+/ip/serial
|
||||
+incdir+/ip/peripheral
|
||||
+incdir+/ip/core
|
||||
+incdir+/ip/security
|
||||
+incdir+/clusterip/rng/rtl
|
||||
+incdir+/clusterip/crc/rtl
|
||||
@@ -47,3 +47,40 @@
|
||||
/ip/core/mgmt_debug_wrapper.sv
|
||||
/ip/core/mgmt_core_wrapper.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,2 +1,3 @@
|
||||
/dv/tiny_qspi_nor.sv
|
||||
/tests/rtl/sdio_native_model.sv
|
||||
/dv/retrosoc_tiny_tb.sv
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"external_fabric": {
|
||||
"phase": "TINY-R2-P5",
|
||||
"phase": "TINY-R2-P6",
|
||||
"clock_domain": "system",
|
||||
"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",
|
||||
"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",
|
||||
@@ -61,9 +61,24 @@
|
||||
"sta": {
|
||||
"source_port": "jtag_tck_i_pad",
|
||||
"object_type": "port",
|
||||
"period_ns": 100.0,
|
||||
"period_ns": 100,
|
||||
"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": {
|
||||
@@ -73,8 +88,27 @@
|
||||
"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_names": [
|
||||
"fabric", "gpio", "uart0", "uart1", "timer0", "timer1", "i2c0", "i2c1",
|
||||
"xpi", "dma", "sysctrl", "clint", "sram", "archinfo", "pwm", "rtc", "wdg"
|
||||
"fabric",
|
||||
"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",
|
||||
"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",
|
||||
"watchdog_functional_reset": "POR only; APB uses wdg leaf",
|
||||
"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"
|
||||
},
|
||||
"crossings": [
|
||||
@@ -101,6 +135,54 @@
|
||||
"primitive": "cdc_sync",
|
||||
"path": "rtl/ip/core/mgmt_debug_wrapper.sv",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"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": {
|
||||
"groups": 4,
|
||||
"group_bytes": 32768,
|
||||
@@ -11,23 +15,36 @@
|
||||
"external_attachment": "per-target-P5"
|
||||
},
|
||||
"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,
|
||||
"sdio_direction_outstanding": 1,
|
||||
"target_combined_outstanding": 1,
|
||||
"sram_targets": [0, 1, 2, 3],
|
||||
"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
|
||||
},
|
||||
"gpio_pins": 32,
|
||||
"irq_vector_width": 32,
|
||||
"external_irq_count": 30,
|
||||
"dma_channels": 4,
|
||||
"dma_channels": 8,
|
||||
"apb_targets": [
|
||||
{
|
||||
"name": "gpio",
|
||||
@@ -42,12 +59,6 @@
|
||||
"APB4_UART0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "uart1",
|
||||
"regions": [
|
||||
"APB4_UART1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "timer0",
|
||||
"regions": [
|
||||
@@ -66,12 +77,6 @@
|
||||
"APB4_I2C0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "i2c1",
|
||||
"regions": [
|
||||
"APB4_I2C1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "xpi",
|
||||
"regions": [
|
||||
@@ -125,6 +130,42 @@
|
||||
"regions": [
|
||||
"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": [
|
||||
@@ -158,11 +199,21 @@
|
||||
"core_bit": 7,
|
||||
"signal": "u_i2c0_if.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "i2s",
|
||||
"core_bit": 8,
|
||||
"signal": "u_i2s_if.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "xpi",
|
||||
"core_bit": 9,
|
||||
"signal": "s_xpi_irq"
|
||||
},
|
||||
{
|
||||
"name": "sdio",
|
||||
"core_bit": 10,
|
||||
"signal": "u_sdio_if.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "pwm",
|
||||
"core_bit": 11,
|
||||
@@ -178,36 +229,39 @@
|
||||
"core_bit": 14,
|
||||
"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",
|
||||
"core_bit": 18,
|
||||
"signal": "u_gpio_if.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "i2c1",
|
||||
"core_bit": 19,
|
||||
"signal": "u_i2c1_if.irq_o"
|
||||
},
|
||||
{
|
||||
"name": "dma",
|
||||
"core_bit": 20,
|
||||
"signal": "s_dma_irq"
|
||||
},
|
||||
{
|
||||
"name": "uart1",
|
||||
"core_bit": 26,
|
||||
"signal": "u_uart1_if.irq_o"
|
||||
"name": "crypto",
|
||||
"core_bit": 23,
|
||||
"signal": "s_crypto_irq"
|
||||
}
|
||||
],
|
||||
"gpio_alt_functions": [
|
||||
{
|
||||
"pin": 0,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_uart0_if.cts_n_i"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"inputs": [],
|
||||
"do": "u_sdio_if.sck_o",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -218,9 +272,11 @@
|
||||
{
|
||||
"pin": 1,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "u_uart0_if.rts_n_o",
|
||||
"oe": "1'b1"
|
||||
"inputs": [
|
||||
"u_sdio_if.cmd_di_i"
|
||||
],
|
||||
"do": "u_sdio_if.cmd_do_o",
|
||||
"oe": "u_sdio_if.cmd_oe_o"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -232,10 +288,10 @@
|
||||
"pin": 2,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_pwm_if.sync_i"
|
||||
"u_sdio_if.dat_di_i[0]"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "u_sdio_if.dat_do_o[0]",
|
||||
"oe": "u_sdio_if.dat_oe_o[0]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -245,36 +301,243 @@
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
"inputs": [],
|
||||
"do": "u_pwm_if.pwm_o[0]",
|
||||
"oe": "u_pwm_if.oe_o[0]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
"u_i2c1_if.scl_i"
|
||||
],
|
||||
"do": "u_i2c1_if.scl_o",
|
||||
"oe": "u_i2c1_if.scl_oe_o"
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pin": 4,
|
||||
"pin": 19,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "u_pwm_if.pwm_o[1]",
|
||||
"oe": "u_pwm_if.oe_o[1]"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
"u_i2c1_if.sda_i"
|
||||
],
|
||||
"do": "u_i2c1_if.sda_o",
|
||||
"oe": "u_i2c1_if.sda_oe_o"
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pin": 5,
|
||||
"pin": 20,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "u_pwm_if.pwm_o[2]",
|
||||
@@ -287,7 +550,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"pin": 6,
|
||||
"pin": 21,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "u_pwm_if.pwm_o[3]",
|
||||
@@ -300,37 +563,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"pin": 7,
|
||||
"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,
|
||||
"pin": 22,
|
||||
"alt0": {
|
||||
"inputs": [
|
||||
"u_pwm_if.fault_i"
|
||||
@@ -344,179 +577,12 @@
|
||||
"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,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"inputs": [
|
||||
"u_pwm_if.sync_i"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
},
|
||||
@@ -529,7 +595,9 @@
|
||||
{
|
||||
"pin": 24,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"inputs": [
|
||||
"u_pwm_if.capture_i[0]"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
},
|
||||
@@ -542,7 +610,9 @@
|
||||
{
|
||||
"pin": 25,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"inputs": [
|
||||
"u_pwm_if.capture_i[1]"
|
||||
],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
},
|
||||
@@ -556,8 +626,8 @@
|
||||
"pin": 26,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "u_ws2812_if.dat_o",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -595,8 +665,8 @@
|
||||
"pin": 29,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "xpi.nss_o[1]",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [],
|
||||
@@ -608,13 +678,11 @@
|
||||
"pin": 30,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "xpi.nss_o[2]",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
"u_pwm_if.capture_i[0]"
|
||||
],
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
}
|
||||
@@ -623,16 +691,22 @@
|
||||
"pin": 31,
|
||||
"alt0": {
|
||||
"inputs": [],
|
||||
"do": "1'b0",
|
||||
"oe": "1'b0"
|
||||
"do": "xpi.nss_o[3]",
|
||||
"oe": "1'b1"
|
||||
},
|
||||
"alt1": {
|
||||
"inputs": [
|
||||
"u_pwm_if.capture_i[1]"
|
||||
],
|
||||
"inputs": [],
|
||||
"do": "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)"
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ ifneq ($(ISA),RV32IM)
|
||||
$(error SOC=TINY firmware uses ISA=RV32IM)
|
||||
endif
|
||||
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
|
||||
ifneq ($(filter YES,$(APU_ENABLE_P7) $(NPU_P5_ACCEPTANCE) $(NPU_P6_ACCEPTANCE)),)
|
||||
$(error SOC=TINY does not include multimedia accelerators)
|
||||
|
||||
@@ -205,38 +205,38 @@
|
||||
"jtag_tdi_i_pad": "s_jtag_tdi",
|
||||
"jtag_trst_n_i_pad": "s_jtag_trst_n",
|
||||
"jtag_tdo_o_pad": "s_jtag_tdo",
|
||||
"gpio_0_io_pad": "s_gpio[0]",
|
||||
"gpio_1_io_pad": "s_gpio[1]",
|
||||
"gpio_2_io_pad": "s_gpio[2]",
|
||||
"gpio_3_io_pad": "s_gpio[3]",
|
||||
"gpio_4_io_pad": "s_gpio[4]",
|
||||
"gpio_5_io_pad": "s_gpio[5]",
|
||||
"gpio_6_io_pad": "s_gpio[6]",
|
||||
"gpio_7_io_pad": "s_gpio[7]",
|
||||
"gpio_8_io_pad": "s_gpio[8]",
|
||||
"gpio_9_io_pad": "s_gpio[9]",
|
||||
"gpio_10_io_pad": "s_gpio[10]",
|
||||
"gpio_11_io_pad": "s_gpio[11]",
|
||||
"gpio_12_io_pad": "s_gpio[12]",
|
||||
"gpio_13_io_pad": "s_gpio[13]",
|
||||
"gpio_14_io_pad": "s_gpio[14]",
|
||||
"gpio_15_io_pad": "s_gpio[15]",
|
||||
"gpio_16_io_pad": "s_gpio[16]",
|
||||
"gpio_17_io_pad": "s_gpio[17]",
|
||||
"gpio_18_io_pad": "s_gpio[18]",
|
||||
"gpio_19_io_pad": "s_gpio[19]",
|
||||
"gpio_20_io_pad": "s_gpio[20]",
|
||||
"gpio_21_io_pad": "s_gpio[21]",
|
||||
"gpio_22_io_pad": "s_gpio[22]",
|
||||
"gpio_23_io_pad": "s_gpio[23]",
|
||||
"gpio_24_io_pad": "s_gpio[24]",
|
||||
"gpio_25_io_pad": "s_gpio[25]",
|
||||
"gpio_26_io_pad": "s_gpio[26]",
|
||||
"gpio_27_io_pad": "s_gpio[27]",
|
||||
"gpio_28_io_pad": "s_gpio[28]",
|
||||
"gpio_29_io_pad": "s_gpio[29]",
|
||||
"gpio_30_io_pad": "s_gpio[30]",
|
||||
"gpio_31_io_pad": "s_gpio[31]",
|
||||
"gpio_0_io_pad": "s_gpio_0",
|
||||
"gpio_1_io_pad": "s_gpio_1",
|
||||
"gpio_2_io_pad": "s_gpio_2",
|
||||
"gpio_3_io_pad": "s_gpio_3",
|
||||
"gpio_4_io_pad": "s_gpio_4",
|
||||
"gpio_5_io_pad": "s_gpio_5",
|
||||
"gpio_6_io_pad": "s_gpio_6",
|
||||
"gpio_7_io_pad": "s_gpio_7",
|
||||
"gpio_8_io_pad": "s_gpio_8",
|
||||
"gpio_9_io_pad": "s_gpio_9",
|
||||
"gpio_10_io_pad": "s_gpio_10",
|
||||
"gpio_11_io_pad": "s_gpio_11",
|
||||
"gpio_12_io_pad": "s_gpio_12",
|
||||
"gpio_13_io_pad": "s_gpio_13",
|
||||
"gpio_14_io_pad": "s_gpio_14",
|
||||
"gpio_15_io_pad": "s_gpio_15",
|
||||
"gpio_16_io_pad": "s_gpio_16",
|
||||
"gpio_17_io_pad": "s_gpio_17",
|
||||
"gpio_18_io_pad": "s_gpio_18",
|
||||
"gpio_19_io_pad": "s_gpio_19",
|
||||
"gpio_20_io_pad": "s_gpio_20",
|
||||
"gpio_21_io_pad": "s_gpio_21",
|
||||
"gpio_22_io_pad": "s_gpio_22",
|
||||
"gpio_23_io_pad": "s_gpio_23",
|
||||
"gpio_24_io_pad": "s_gpio_24",
|
||||
"gpio_25_io_pad": "s_gpio_25",
|
||||
"gpio_26_io_pad": "s_gpio_26",
|
||||
"gpio_27_io_pad": "s_gpio_27",
|
||||
"gpio_28_io_pad": "s_gpio_28",
|
||||
"gpio_29_io_pad": "s_gpio_29",
|
||||
"gpio_30_io_pad": "s_gpio_30",
|
||||
"gpio_31_io_pad": "s_gpio_31",
|
||||
"uart0_tx_o_pad": "s_uart0_tx",
|
||||
"uart0_rx_i_pad": "s_uart0_rx",
|
||||
"uart1_tx_o_pad": "s_uart1_tx",
|
||||
@@ -261,38 +261,38 @@
|
||||
"jtag_tdi_i_pad": "s_jtag_tdi",
|
||||
"jtag_trst_n_i_pad": "s_jtag_trst_n",
|
||||
"jtag_tdo_o_pad": "s_jtag_tdo",
|
||||
"gpio_0_io_pad": "s_gpio[0]",
|
||||
"gpio_1_io_pad": "s_gpio[1]",
|
||||
"gpio_2_io_pad": "s_gpio[2]",
|
||||
"gpio_3_io_pad": "s_gpio[3]",
|
||||
"gpio_4_io_pad": "s_gpio[4]",
|
||||
"gpio_5_io_pad": "s_gpio[5]",
|
||||
"gpio_6_io_pad": "s_gpio[6]",
|
||||
"gpio_7_io_pad": "s_gpio[7]",
|
||||
"gpio_8_io_pad": "s_gpio[8]",
|
||||
"gpio_9_io_pad": "s_gpio[9]",
|
||||
"gpio_10_io_pad": "s_gpio[10]",
|
||||
"gpio_11_io_pad": "s_gpio[11]",
|
||||
"gpio_12_io_pad": "s_gpio[12]",
|
||||
"gpio_13_io_pad": "s_gpio[13]",
|
||||
"gpio_14_io_pad": "s_gpio[14]",
|
||||
"gpio_15_io_pad": "s_gpio[15]",
|
||||
"gpio_16_io_pad": "s_gpio[16]",
|
||||
"gpio_17_io_pad": "s_gpio[17]",
|
||||
"gpio_18_io_pad": "s_gpio[18]",
|
||||
"gpio_19_io_pad": "s_gpio[19]",
|
||||
"gpio_20_io_pad": "s_gpio[20]",
|
||||
"gpio_21_io_pad": "s_gpio[21]",
|
||||
"gpio_22_io_pad": "s_gpio[22]",
|
||||
"gpio_23_io_pad": "s_gpio[23]",
|
||||
"gpio_24_io_pad": "s_gpio[24]",
|
||||
"gpio_25_io_pad": "s_gpio[25]",
|
||||
"gpio_26_io_pad": "s_gpio[26]",
|
||||
"gpio_27_io_pad": "s_gpio[27]",
|
||||
"gpio_28_io_pad": "s_gpio[28]",
|
||||
"gpio_29_io_pad": "s_gpio[29]",
|
||||
"gpio_30_io_pad": "s_gpio[30]",
|
||||
"gpio_31_io_pad": "s_gpio[31]",
|
||||
"gpio_0_io_pad": "s_gpio_0",
|
||||
"gpio_1_io_pad": "s_gpio_1",
|
||||
"gpio_2_io_pad": "s_gpio_2",
|
||||
"gpio_3_io_pad": "s_gpio_3",
|
||||
"gpio_4_io_pad": "s_gpio_4",
|
||||
"gpio_5_io_pad": "s_gpio_5",
|
||||
"gpio_6_io_pad": "s_gpio_6",
|
||||
"gpio_7_io_pad": "s_gpio_7",
|
||||
"gpio_8_io_pad": "s_gpio_8",
|
||||
"gpio_9_io_pad": "s_gpio_9",
|
||||
"gpio_10_io_pad": "s_gpio_10",
|
||||
"gpio_11_io_pad": "s_gpio_11",
|
||||
"gpio_12_io_pad": "s_gpio_12",
|
||||
"gpio_13_io_pad": "s_gpio_13",
|
||||
"gpio_14_io_pad": "s_gpio_14",
|
||||
"gpio_15_io_pad": "s_gpio_15",
|
||||
"gpio_16_io_pad": "s_gpio_16",
|
||||
"gpio_17_io_pad": "s_gpio_17",
|
||||
"gpio_18_io_pad": "s_gpio_18",
|
||||
"gpio_19_io_pad": "s_gpio_19",
|
||||
"gpio_20_io_pad": "s_gpio_20",
|
||||
"gpio_21_io_pad": "s_gpio_21",
|
||||
"gpio_22_io_pad": "s_gpio_22",
|
||||
"gpio_23_io_pad": "s_gpio_23",
|
||||
"gpio_24_io_pad": "s_gpio_24",
|
||||
"gpio_25_io_pad": "s_gpio_25",
|
||||
"gpio_26_io_pad": "s_gpio_26",
|
||||
"gpio_27_io_pad": "s_gpio_27",
|
||||
"gpio_28_io_pad": "s_gpio_28",
|
||||
"gpio_29_io_pad": "s_gpio_29",
|
||||
"gpio_30_io_pad": "s_gpio_30",
|
||||
"gpio_31_io_pad": "s_gpio_31",
|
||||
"uart0_tx_o_pad": "s_uart0_tx",
|
||||
"uart0_rx_i_pad": "s_uart0_rx",
|
||||
"uart1_tx_o_pad": "s_uart1_tx",
|
||||
|
||||
+112
-97
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2026 Yuchi Miao
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
`include "mmap_define.svh"
|
||||
`include "tiny_topology.svh"
|
||||
|
||||
module retrosoc_tiny (
|
||||
input logic clk_i,
|
||||
@@ -21,10 +22,10 @@ module retrosoc_tiny (
|
||||
output logic [7:0] test_code_o
|
||||
);
|
||||
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_master_rst_n [ 3];
|
||||
logic s_target_rst_n [23];
|
||||
logic s_master_rst_n [ 3];
|
||||
logic s_target_rst_n [`RETROSOC_TINY__TARGET_COUNT];
|
||||
logic s_debug_halted;
|
||||
logic s_core_rst_n, s_cpu_bus_idle, s_cpu_quiesce;
|
||||
logic [31:0] s_irq;
|
||||
@@ -42,10 +43,14 @@ module retrosoc_tiny (
|
||||
logic [63:0] s_cpu_wait, s_dma_wait;
|
||||
logic s_timer0_irq, s_timer1_irq, s_xpi_irq, s_dma_irq;
|
||||
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 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] (
|
||||
.hclk (clk_i),
|
||||
.hresetn(s_core_rst_n)
|
||||
@@ -62,14 +67,15 @@ module retrosoc_tiny (
|
||||
);
|
||||
`include "tiny_apb_interfaces.svh"
|
||||
uart_if u_uart0_if ();
|
||||
uart_if u_uart1_if ();
|
||||
i2c_if u_i2c0_if ();
|
||||
i2c_if u_i2c1_if ();
|
||||
pwm_if u_pwm_if ();
|
||||
clint_if u_clint_if ();
|
||||
gpio_if u_gpio_if ();
|
||||
user_gpio_if u_user_gpio_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_clk_i (clk_i),
|
||||
.rtc_rst_n_i(s_rtc_rst_n)
|
||||
@@ -94,7 +100,7 @@ uart_if u_uart0_if ();
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) u_targets_axi4_if[23] (
|
||||
) u_targets_axi4_if[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(s_target_rst_n)
|
||||
);
|
||||
@@ -128,46 +134,14 @@ uart_if u_uart0_if ();
|
||||
.aclk (clk_i),
|
||||
.aresetn(s_dma_rst_n)
|
||||
);
|
||||
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_target_rst_n[4] = s_xpi_rst_n;
|
||||
assign s_target_rst_n[21] = s_fabric_rst_n;
|
||||
assign s_target_rst_n[22] = s_fabric_rst_n;
|
||||
// Owner 2 is a reserved native read/write ingress, not an integrated SDIO.
|
||||
// P6 supplies its real producer; no capability or interrupt is enabled here.
|
||||
assign u_masters_axi4_if[2].awid = '0;
|
||||
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 (
|
||||
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_sdio_rst_n};
|
||||
assign s_target_rst_n[4] = s_xpi_rst_n;
|
||||
assign s_target_rst_n[`RETROSOC_TINY__DECODE_ERROR] = s_fabric_rst_n;
|
||||
assign s_target_rst_n[`RETROSOC_TINY__PROTOCOL_ERROR] = s_fabric_rst_n;
|
||||
tiny_reset_tree #(
|
||||
.LeafCount(`RETROSOC_TINY__LEAF_COUNT)
|
||||
) u_reset_tree (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.watchdog_reset_req_i(u_wdg_if.reset_req_o),
|
||||
@@ -267,14 +241,14 @@ axi4_error_slave #(
|
||||
) u_decode_error (
|
||||
.clk_i (clk_i),
|
||||
.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 #(
|
||||
.Response(2'b10)
|
||||
) u_protocol_error (
|
||||
.clk_i (clk_i),
|
||||
.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 (
|
||||
.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.oe_o = '0;
|
||||
assign u_uart0_if.rx_i = uart0_rx_i;
|
||||
assign uart0_tx_o = u_uart0_if.tx_o;
|
||||
assign u_uart1_if.rx_i = uart1_rx_i;
|
||||
assign u_uart1_if.cts_n_i = 1'b0;
|
||||
assign uart1_tx_o = u_uart1_if.tx_o;
|
||||
// Legacy ASIC observation pads are retained, not a Gen1 package binding.
|
||||
assign uart1_tx_o = 1'b1;
|
||||
assign u_uart0_if.cts_n_i = 1'b0;
|
||||
assign u_wdg_if.debug_halted_i = s_debug_halted;
|
||||
`include "tiny_gpio.svh"
|
||||
`include "tiny_irq.svh"
|
||||
@@ -362,22 +335,6 @@ apb4_uart u_uart0 (
|
||||
.apb4 (u_timer0_apb4_if),
|
||||
.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 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (s_timer1_rst_n),
|
||||
@@ -413,11 +370,11 @@ apb4_uart u_uart0 (
|
||||
.wdg (u_wdg_if)
|
||||
);
|
||||
apb4_dma #(
|
||||
.NumChannels (4),
|
||||
.NumChannels (8),
|
||||
.MaxBurstBeats(16),
|
||||
.FifoDepth (32),
|
||||
.RequestMask (16'h07f9),
|
||||
.EnableStreams(1'b0)
|
||||
.RequestMask (16'h31ff),
|
||||
.EnableStreams(1'b1)
|
||||
) u_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (s_dma_rst_n),
|
||||
@@ -432,26 +389,18 @@ apb4_uart u_uart0 (
|
||||
.crypto_in_axis (u_crypto_tx_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_rx_proc = 1'b0;
|
||||
assign u_dma_req_if.crypto_in_proc = 1'b0;
|
||||
assign u_dma_req_if.crypto_out_proc = 1'b0;
|
||||
assign u_dma_req_if.i2s_tx_proc = !s_i2s_tx_stall;
|
||||
assign u_dma_req_if.i2s_rx_proc = !s_i2s_rx_stall;
|
||||
assign u_dma_req_if.crypto_in_proc = s_crypto_input_ready;
|
||||
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_rx_proc = !s_dma_stall[1];
|
||||
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.i2c0_tx_proc = !s_dma_stall[4];
|
||||
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_rx_proc = !s_dma_stall[7];
|
||||
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_dma_req_if.i2c1_tx_proc = 1'b0;
|
||||
assign u_dma_req_if.i2c1_rx_proc = 1'b0;
|
||||
assign u_dvp_rx_axis_if.tvalid = '0;
|
||||
assign u_dvp_rx_axis_if.tdata = '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.tdest = '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;
|
||||
assign u_crypto_rx_axis_if.tstrb = '0;
|
||||
assign u_crypto_rx_axis_if.tkeep = '0;
|
||||
assign u_crypto_rx_axis_if.tlast = '0;
|
||||
assign u_crypto_rx_axis_if.tid = '0;
|
||||
assign u_crypto_rx_axis_if.tdest = '0;
|
||||
assign u_crypto_rx_axis_if.tuser = '0;
|
||||
assign u_i2s_tx_axis_if.tready = 1'b0;
|
||||
assign u_crypto_tx_axis_if.tready = 1'b0;
|
||||
|
||||
// Host logic remains SAFE24. Audio reset waits for its own clock, never CPU boot.
|
||||
tc_clk_buf u_audio_clock_buffer (
|
||||
.clk_i(s_audio_pad_clk),
|
||||
.clk_o(s_audio_clk)
|
||||
);
|
||||
rst_sync #(
|
||||
.STAGE(5)
|
||||
) u_audio_rst_sync (
|
||||
.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
|
||||
@@ -57,7 +57,7 @@ module tiny_axi42apb4 (
|
||||
assign apb4.paddr = s_addr_q;
|
||||
assign apb4.pwrite = s_write_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.psel = s_access;
|
||||
assign apb4.penable = s_enable;
|
||||
|
||||
@@ -1,47 +1,50 @@
|
||||
// Copyright (c) 2026 Yuchi Miao
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
`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
|
||||
// unchanged: saved target ownership, not ID expansion, routes all responses.
|
||||
module tiny_axi4_fabric (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic perf_enable_i,
|
||||
input logic perf_clear_i,
|
||||
input logic target_rst_n_i [23],
|
||||
axi4_if.slave masters [ 3],
|
||||
axi4_if.master targets [23],
|
||||
output logic [ 5:0] fault_valid_o,
|
||||
output logic [ 5:0][31:0] fault_addr_o,
|
||||
output logic [ 5:0][ 1:0] fault_resp_o,
|
||||
output logic [ 5:0] fault_instruction_o,
|
||||
output logic [ 2:0] owner_idle_o,
|
||||
output logic [22:0] target_idle_o,
|
||||
output logic [63:0] cpu_wait_o,
|
||||
output logic [63:0] dma_wait_o
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic perf_enable_i,
|
||||
input logic perf_clear_i,
|
||||
input logic target_rst_n_i[`RETROSOC_TINY__TARGET_COUNT],
|
||||
axi4_if.slave masters[3],
|
||||
axi4_if.master targets[`RETROSOC_TINY__TARGET_COUNT],
|
||||
output logic [5:0] fault_valid_o,
|
||||
output logic [5:0][31:0] fault_addr_o,
|
||||
output logic [5:0][1:0] fault_resp_o,
|
||||
output logic [5:0] fault_instruction_o,
|
||||
output logic [2:0] owner_idle_o,
|
||||
output logic [`RETROSOC_TINY__TARGET_COUNT-1:0] target_idle_o,
|
||||
output logic [63:0] cpu_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 s_cpu_prefer_write_d, s_cpu_prefer_write_q;
|
||||
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 [22:0][ 2:0][36:0] s_rdata;
|
||||
logic [22:0][ 2:0][ 3:0] s_bdata;
|
||||
logic [ 2:0][36:0] s_read_return;
|
||||
logic [ 2:0][ 3:0] s_write_return;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][2:0] s_arready, s_awready, s_wready, s_rvalid, s_bvalid;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][36:0] s_rdata;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][ 2:0][ 3:0] s_bdata;
|
||||
logic [ 2:0][36:0] s_read_return;
|
||||
logic [ 2:0][ 3:0] s_write_return;
|
||||
logic [2:0] s_master_arready, s_master_awready, s_master_wready, s_master_rvalid, s_master_bvalid;
|
||||
logic [2:0] s_arvalid, s_awvalid;
|
||||
logic [1:0][63:0] s_wait_d, s_wait_q;
|
||||
logic [22:0] s_fault_valid, s_fault_write, s_fault_instruction;
|
||||
logic [22:0][31:0] s_fault_addr;
|
||||
logic [22:0][1:0] s_fault_master, s_fault_resp;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0] s_fault_valid, s_fault_write, s_fault_instruction;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0][31:0] s_fault_addr;
|
||||
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);
|
||||
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;
|
||||
`include "tiny_fabric_decode.svh"
|
||||
return 5'd21;
|
||||
return DecodeErrorTarget;
|
||||
endfunction
|
||||
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,
|
||||
@@ -56,12 +59,14 @@ module tiny_axi4_fabric (
|
||||
last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||
|
||||
(decode_target(
|
||||
last_addr[31:0]
|
||||
) != decoded) || ((decoded >= 5'd5) && (decoded <= 5'd20) && (len_i != 8'd0)))
|
||||
return 5'd22;
|
||||
) != decoded) || ((decoded >= 5'd5) && (decoded < DecodeErrorTarget) && (len_i != 8'd0)))
|
||||
return ProtocolErrorTarget;
|
||||
if (instruction_i && !(
|
||||
`SOC_ADDR_IS_FLASH(addr_i)
|
||||
|| ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000))))
|
||||
return ((decoded >= 5'd5) && (decoded <= 5'd20)) ? 5'd22 : 5'd21;
|
||||
|| ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000)))) begin
|
||||
if ((decoded >= 5'd5) && (decoded < DecodeErrorTarget)) return ProtocolErrorTarget;
|
||||
return DecodeErrorTarget;
|
||||
end
|
||||
return decoded;
|
||||
endfunction
|
||||
|
||||
@@ -106,7 +111,7 @@ module tiny_axi4_fabric (
|
||||
fault_addr_o = '0;
|
||||
fault_resp_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_awready |= s_awready[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];
|
||||
assign {masters[owner].bid, masters[owner].bresp, masters[owner].buser} = s_write_return[owner];
|
||||
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 #(
|
||||
.ADDR_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_done & ~s_busy_q) == 6'd0);
|
||||
for (genvar owner = 0; owner < 3; owner++) begin : gen_route_assert
|
||||
logic [22:0] s_reads, s_writes;
|
||||
for (genvar t = 0; t < 23; t++) begin : gen_column
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0] s_reads, s_writes;
|
||||
for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_column
|
||||
assign s_reads[t] = s_rvalid[t][owner];
|
||||
assign s_writes[t] = s_bvalid[t][owner];
|
||||
end
|
||||
|
||||
+45
-10
@@ -21,8 +21,9 @@ TINY = ROOT / "rtl/tiny"
|
||||
def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
document = json.loads(path.read_text())
|
||||
regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]}
|
||||
if document.get("schema_version") != 1 or document.get("dma_channels") != 4:
|
||||
raise ValueError("Tiny requires topology schema 1 and four DMA channels")
|
||||
if (document.get("schema_version") != 1 or document.get("dma_channels") != 8
|
||||
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,
|
||||
"data_outstanding": 1}:
|
||||
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()):
|
||||
raise ValueError("Tiny SRAM geometry/P5 attachment differs from its frozen contract")
|
||||
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,
|
||||
"sdio_direction_outstanding": 1, "target_combined_outstanding": 1,
|
||||
"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:
|
||||
raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs")
|
||||
names: set[str] = set()
|
||||
@@ -61,14 +62,24 @@ def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
assigned.add(region)
|
||||
if assigned != {r for r in regions if r.startswith("APB4_")}:
|
||||
raise ValueError("Tiny topology must route every APB region")
|
||||
if len(document["apb_targets"]) != 16:
|
||||
raise ValueError("Tiny's fixed APB bridge requires sixteen targets")
|
||||
if len(document["apb_targets"]) != 20:
|
||||
raise ValueError("Tiny P6 requires twenty independent 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):
|
||||
raise ValueError("Tiny APB target names must be unique identifiers")
|
||||
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")
|
||||
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
|
||||
|
||||
|
||||
@@ -84,6 +95,22 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
have_pll: bool = False) -> None:
|
||||
platform = platform_values(pdk, have_pll)
|
||||
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;"]
|
||||
header = ["#ifndef RETROSOC_GENERATED_IRQ_METADATA_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"
|
||||
"#define RETROSOC_GENERATED_SOC_FEATURES_H\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_PLL_PRESENT {platform['pll_present']}U\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",
|
||||
"tech": ROOT / "rtl/tech", "clusterip": ROOT / "rtl/managed/clusterip",
|
||||
"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 ""
|
||||
pdk_name = "pdk_ihp130.fl" if pdk == "IHP130" else f"pdk_ics55{suffix}.fl"
|
||||
templates = [*sorted((TINY / "filelist").glob("*.fl")),
|
||||
|
||||
@@ -15,7 +15,7 @@ from scripts.setup_helpers import ensure_git_repo # noqa: E402
|
||||
|
||||
TINY_SOURCES = (
|
||||
"hazard3", "cluster_common", "cluster_archinfo", "cluster_pwm",
|
||||
"cluster_rtc", "cluster_wdg", "pdk_ihp130",
|
||||
"cluster_rtc", "cluster_wdg", "cluster_rng", "cluster_crc", "pdk_ihp130",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -177,7 +177,18 @@ def inspect_netlist(document: dict) -> dict:
|
||||
system = canonical(f"{top}:{clock[0]}")
|
||||
if system in ("0", "1", "x", "z"):
|
||||
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"]
|
||||
if any(c["type"] == "ics55_ecos_sram_4096x32_m8" for c in leaves):
|
||||
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},
|
||||
"cpu_clocked_cells": len(cpu),
|
||||
"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"}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -92,9 +92,13 @@ void rs_dma_test_irq_restore(uint32_t saved) {
|
||||
dma_irq_result = rs_dma_session_acquire(&dma_irq_owner, 3U);
|
||||
if (dma_irq_result == RS_OK) {
|
||||
const rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM,
|
||||
.source = 0x3000U, .destination = 0x4000U, .byte_count = 16U,
|
||||
.width = RS_DMA_WIDTH_32, .source_increment = true,
|
||||
.destination_increment = true, .burst_beats = 1U};
|
||||
.source = 0x3000U,
|
||||
.destination = 0x4000U,
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -147,18 +151,21 @@ static void ws_test_reset(void) {
|
||||
}
|
||||
|
||||
static int test_dma_tcd_preemption(void) {
|
||||
static _Alignas(64) rs_dma_tcd_t tcd = {
|
||||
.source = 0x1000U, .destination = 0x2000U, .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)};
|
||||
static _Alignas(64)
|
||||
rs_dma_tcd_t tcd = {.source = 0x1000U,
|
||||
.destination = 0x2000U,
|
||||
.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;
|
||||
bool valid;
|
||||
ws_test_reset();
|
||||
dma_irq_pending = true;
|
||||
dma_irq_result = RS_OK;
|
||||
result = rs_dma_submit_tcd(3U, &tcd, 1U);
|
||||
valid = (result == RS_ETIMEOUT) && (dma_irq_result == RS_EIO) &&
|
||||
!dma_irq_pending && (dma_irq_enabled == 8U) &&
|
||||
valid = (result == RS_ETIMEOUT) && (dma_irq_result == RS_EIO) && !dma_irq_pending &&
|
||||
(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_COUNT) == 1U) &&
|
||||
(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;
|
||||
}
|
||||
(void)rs_ws2812_dma_status(&transfer);
|
||||
if (transfer.active || (transfer.result != RS_OK) ||
|
||||
(ws_registers[0x24U / 4U] != 3U)) {
|
||||
if (transfer.active || (transfer.result != RS_OK) || (ws_registers[0x24U / 4U] != 3U)) {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -248,9 +254,14 @@ static int test_ws2812_completion_race(void) {
|
||||
|
||||
static int test_dma_sessions(void) {
|
||||
rs_dma_session_t first = {0}, second = {0};
|
||||
rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM, .source = 0x1000U,
|
||||
.destination = 0x2000U, .byte_count = 32U, .width = RS_DMA_WIDTH_32,
|
||||
.source_increment = true, .destination_increment = true, .burst_beats = 1U};
|
||||
rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM,
|
||||
.source = 0x1000U,
|
||||
.destination = 0x2000U,
|
||||
.byte_count = 32U,
|
||||
.width = RS_DMA_WIDTH_32,
|
||||
.source_increment = true,
|
||||
.destination_increment = true,
|
||||
.burst_beats = 1U};
|
||||
ws_test_reset();
|
||||
if ((rs_dma_session_acquire(&first, 3U) != RS_OK) ||
|
||||
(rs_dma_session_acquire(&first, 2U) != RS_EIO) ||
|
||||
@@ -262,13 +273,11 @@ static int test_dma_sessions(void) {
|
||||
return 1;
|
||||
}
|
||||
RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_BUSY;
|
||||
if ((rs_dma_session_release(&first) != RS_EIO) ||
|
||||
(rs_dma_session_abort(&first) != RS_OK)) {
|
||||
if ((rs_dma_session_release(&first) != RS_EIO) || (rs_dma_session_abort(&first) != RS_OK)) {
|
||||
return 2;
|
||||
}
|
||||
RS_DMA_CH_REG(3U, RS_DMA_CH_REG_STATUS) = RS_DMA_STATUS_ABORTED;
|
||||
if ((rs_dma_session_release(&first) != RS_OK) ||
|
||||
(rs_dma_session_release(&first) != RS_EIO) ||
|
||||
if ((rs_dma_session_release(&first) != RS_OK) || (rs_dma_session_release(&first) != RS_EIO) ||
|
||||
(rs_dma_session_acquire(&second, 3U) != RS_OK) ||
|
||||
(rs_dma_session_release(&second) != RS_OK)) {
|
||||
return 3;
|
||||
@@ -301,15 +310,13 @@ static int test_ws2812_refill(void) {
|
||||
ws_pixels[32] = 0U;
|
||||
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) ||
|
||||
(rs_ws2812_start(1U) != RS_EIO) ||
|
||||
(rs_dma_session_acquire(&competitor, 3U) != RS_EIO)) {
|
||||
(rs_ws2812_start(1U) != RS_EIO) || (rs_dma_session_acquire(&competitor, 3U) != RS_EIO)) {
|
||||
return 4;
|
||||
}
|
||||
ws_registers[0x30U / 4U] = RS_WS2812_INTR_FIFO_LOW;
|
||||
(void)rs_ws2812_dma_service(1U);
|
||||
(void)rs_ws2812_dma_status(&transfer);
|
||||
if ((transfer.batches != 0U) ||
|
||||
((ws_registers[0x30U / 4U] & RS_WS2812_INTR_FIFO_LOW) != 0U)) {
|
||||
if ((transfer.batches != 0U) || ((ws_registers[0x30U / 4U] & RS_WS2812_INTR_FIFO_LOW) != 0U)) {
|
||||
return 5;
|
||||
}
|
||||
/* 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;
|
||||
if ((rs_ws2812_dma_service(15U) != RS_OK) ||
|
||||
(rs_dma_session_acquire(&competitor, 3U) != RS_OK) ||
|
||||
(rs_dma_session_release(&competitor) != RS_OK) ||
|
||||
(ws_registers[0x24U / 4U] != 3U)) {
|
||||
(rs_dma_session_release(&competitor) != RS_OK) || (ws_registers[0x24U / 4U] != 3U)) {
|
||||
return 11;
|
||||
}
|
||||
/* 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};
|
||||
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) ||
|
||||
(rs_sdio_clock_calculate(100000000U, 50000000U, &clock) != RS_OK) ||
|
||||
(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) {
|
||||
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),
|
||||
.destination = (uintptr_t)UINT32_C(0x30001000), .byte_count = 259U,
|
||||
.width = RS_DMA_WIDTH_32, .source_increment = true, .destination_increment = true,
|
||||
.destination = (uintptr_t)UINT32_C(0x30001000),
|
||||
.byte_count = 259U,
|
||||
.width = RS_DMA_WIDTH_32,
|
||||
.source_increment = true,
|
||||
.destination_increment = true,
|
||||
.burst_beats = 16U,
|
||||
};
|
||||
static _Alignas(64) rs_dma_tcd_t descriptor;
|
||||
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;
|
||||
}
|
||||
config.kind = RS_DMA_KIND_MM_TO_STREAM;
|
||||
config.request = RS_DMA_REQUEST_I2S_TX;
|
||||
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;
|
||||
}
|
||||
descriptor.source = UINT32_C(0x30000000);
|
||||
descriptor.byte_count = 256U;
|
||||
descriptor.y_count = 1U;
|
||||
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_REQUEST_I2S_TX << RS_DMA_TCD_REQUEST_SHIFT);
|
||||
if (rs_tiny_dma_tcd_validate(0U, &descriptor) != RS_ENOTSUP) {
|
||||
((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);
|
||||
if (rs_tiny_dma_tcd_validate(6U, &descriptor) != RS_OK) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2992,9 +3018,12 @@ static int test_npu_p6_reference_edge_ops(void) {
|
||||
uint8_t depthwise_weights[72] = {0U};
|
||||
int32_t depthwise_parameters[4] = {1, INT32_C(1073741824), 1, 0};
|
||||
rs_npu_p6_memory_t depthwise_memory = {
|
||||
.arena = {.base = UINT32_C(0x1000), .bytes = sizeof(depthwise_arena), .data = depthwise_arena},
|
||||
.weights =
|
||||
{.base = UINT32_C(0x3000), .bytes = sizeof(depthwise_weights), .data = depthwise_weights},
|
||||
.arena = {.base = UINT32_C(0x1000),
|
||||
.bytes = sizeof(depthwise_arena),
|
||||
.data = depthwise_arena},
|
||||
.weights = {.base = UINT32_C(0x3000),
|
||||
.bytes = sizeof(depthwise_weights),
|
||||
.data = depthwise_weights},
|
||||
.params =
|
||||
{
|
||||
.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};
|
||||
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},
|
||||
.params = {.base = UINT32_C(0x6000), .bytes = 1U, .data = average_arena},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -98,6 +98,28 @@ module i2s_tb;
|
||||
end
|
||||
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;
|
||||
|
||||
initial begin
|
||||
@@ -132,6 +154,9 @@ module i2s_tb;
|
||||
repeat (4000) @(posedge clk_aud_i);
|
||||
if (sclk_edges == 0) $fatal(1, "I2S SCLK did not toggle");
|
||||
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_0009);
|
||||
repeat (2000) @(posedge clk_aud_i);
|
||||
|
||||
@@ -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.rlast = s_read_bad_last_q || ((s_read_index_q + 1'b1) >= (s_read_len_q + 1'b1));
|
||||
assign axi4.ruser = '0;
|
||||
assign axi4.rvalid = s_read_active_q;
|
||||
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.wready = s_write_active_q && !s_w_stall_q;
|
||||
|
||||
@@ -32,6 +32,8 @@ module sdio_command_tb;
|
||||
integer launch_count;
|
||||
integer captured_bits;
|
||||
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;
|
||||
|
||||
@@ -77,8 +79,8 @@ module sdio_command_tb;
|
||||
end
|
||||
|
||||
always @(posedge sck) begin
|
||||
if (cmd_oe_o) begin
|
||||
captured_command = {captured_command[46:0], cmd_do_o};
|
||||
if (captured_bits < 48 && (captured_bits != 0 || command_pad == 1'b0)) begin
|
||||
captured_command = {captured_command[46:0], command_pad};
|
||||
captured_bits = captured_bits + 1;
|
||||
end
|
||||
end
|
||||
@@ -95,6 +97,7 @@ module sdio_command_tb;
|
||||
start_i = 1'b0;
|
||||
clock_enable_i = 1'b1;
|
||||
wait (done_o);
|
||||
@(negedge clk_i);
|
||||
if (error_o || (launch_count != 48) || (captured_bits != 48) ||
|
||||
(captured_command[47:46] != 2'b01) || (captured_command[0] != 1'b1) ||
|
||||
(captured_command[7:1] != sdio_pkg::sdio_crc7_calc(
|
||||
@@ -104,6 +107,33 @@ module sdio_command_tb;
|
||||
error_o, launch_count, captured_bits, captured_command, cmd_oe_o);
|
||||
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);
|
||||
resp_type_i = sdio_pkg::SdioRespR1;
|
||||
timeout_cycles_i = 32'd4;
|
||||
|
||||
+104
-71
@@ -14,13 +14,14 @@
|
||||
// implements only public command/data behavior exercised by this repository;
|
||||
// it is not a normative-specification replacement.
|
||||
module sdio_native_card_model #(
|
||||
parameter bit SdioOnly = 1'b0,
|
||||
parameter bit HighCapacity = 1'b1,
|
||||
parameter int BlockBytes = 512,
|
||||
parameter int BlockCount = 128,
|
||||
parameter int FunctionCount = 1,
|
||||
parameter int FunctionBytes = 4096,
|
||||
parameter int BusyCycles = 2
|
||||
parameter bit MemoryInitialization = 1'b0,
|
||||
parameter bit SdioOnly = 1'b0,
|
||||
parameter bit HighCapacity = 1'b1,
|
||||
parameter int BlockBytes = 512,
|
||||
parameter int BlockCount = 128,
|
||||
parameter int FunctionCount = 1,
|
||||
parameter int FunctionBytes = 4096,
|
||||
parameter int BusyCycles = 2
|
||||
) (
|
||||
input logic rst_n_i,
|
||||
input logic sck_i,
|
||||
@@ -48,60 +49,63 @@ module sdio_native_card_model #(
|
||||
WriteBusy
|
||||
} data_state_e;
|
||||
|
||||
logic [ 7:0] s_memory [ 0:MemoryBytes-1];
|
||||
logic [ 7:0] s_function_memory [0:FunctionStorageBytes-1];
|
||||
logic [7:0] s_memory[0:MemoryBytes-1];
|
||||
logic [7:0] s_function_memory[0:FunctionStorageBytes-1];
|
||||
|
||||
logic [ 47:0] s_cmd_shift;
|
||||
integer s_cmd_bits;
|
||||
logic s_cmd_response_active;
|
||||
logic [135:0] s_cmd_response;
|
||||
integer s_cmd_response_length;
|
||||
integer s_cmd_response_index;
|
||||
integer s_cmd_response_delay;
|
||||
integer s_response_delay;
|
||||
integer s_data_delay_cfg;
|
||||
logic s_inject_crc_error;
|
||||
logic s_inject_timeout;
|
||||
logic s_inject_write_error;
|
||||
logic s_next_cmd_crc_error;
|
||||
logic [47:0] s_cmd_shift;
|
||||
integer s_cmd_bits;
|
||||
logic s_cmd_response_active;
|
||||
logic [135:0] s_cmd_response;
|
||||
integer s_cmd_response_length;
|
||||
integer s_cmd_response_index;
|
||||
integer s_cmd_response_delay;
|
||||
integer s_response_delay;
|
||||
integer s_data_delay_cfg;
|
||||
logic s_inject_crc_error;
|
||||
logic s_inject_timeout;
|
||||
logic s_inject_write_error;
|
||||
logic s_next_cmd_crc_error;
|
||||
|
||||
logic [ 5:0] s_last_cmd;
|
||||
logic [ 31:0] s_last_arg;
|
||||
logic s_last_cmd_write;
|
||||
logic s_last_cmd_data;
|
||||
logic s_pending_busy;
|
||||
integer s_busy_count;
|
||||
logic [5:0] s_last_cmd;
|
||||
logic [31:0] s_last_arg;
|
||||
logic s_last_cmd_write;
|
||||
logic s_last_cmd_data;
|
||||
logic s_memory_width4;
|
||||
logic s_pending_busy;
|
||||
integer s_busy_count;
|
||||
|
||||
data_state_e s_data_state;
|
||||
logic s_data_width4;
|
||||
logic s_data_fixed;
|
||||
integer s_data_address;
|
||||
integer s_data_bytes;
|
||||
integer s_data_byte_index;
|
||||
integer s_data_bit_index;
|
||||
integer s_data_nibble_index;
|
||||
integer s_data_crc_index;
|
||||
integer s_data_delay;
|
||||
logic [ 15:0] s_read_crc;
|
||||
logic [ 15:0] s_read_crc_lane [ 0:3];
|
||||
logic [ 15:0] s_write_crc;
|
||||
logic [ 15:0] s_write_crc_lane [ 0:3];
|
||||
logic [ 15:0] s_received_crc;
|
||||
logic [ 15:0] s_received_crc_lane [ 0:3];
|
||||
logic [ 7:0] s_write_byte;
|
||||
logic [ 7:0] s_write_byte_lane [ 0:3];
|
||||
integer s_write_bit_count;
|
||||
integer s_write_nibble_count;
|
||||
logic [ 4:0] s_write_response_token;
|
||||
integer s_write_response_index;
|
||||
logic s_write_response_started;
|
||||
logic s_data_crc_error;
|
||||
logic s_data_timeout;
|
||||
logic s_irq_pending;
|
||||
logic [ 2:0] s_cmd_function;
|
||||
logic [ 31:0] s_cmd_address;
|
||||
logic s_cmd_fixed_address;
|
||||
integer s_cmd_count;
|
||||
data_state_e s_data_state;
|
||||
logic s_data_width4;
|
||||
logic s_data_fixed;
|
||||
integer s_data_address;
|
||||
integer s_data_bytes;
|
||||
wire [3:0] s_token_lanes = !s_data_width4 ? 4'b0001 :
|
||||
(s_data_bytes >= 4 ? 4'b1111 : (4'b0001 << s_data_bytes) - 1'b1);
|
||||
integer s_data_byte_index;
|
||||
integer s_data_bit_index;
|
||||
integer s_data_nibble_index;
|
||||
integer s_data_crc_index;
|
||||
integer s_data_delay;
|
||||
logic [15:0] s_read_crc;
|
||||
logic [15:0] s_read_crc_lane[0:3];
|
||||
logic [15:0] s_write_crc;
|
||||
logic [15:0] s_write_crc_lane[0:3];
|
||||
logic [15:0] s_received_crc;
|
||||
logic [15:0] s_received_crc_lane[0:3];
|
||||
logic [7:0] s_write_byte;
|
||||
logic [7:0] s_write_byte_lane[0:3];
|
||||
integer s_write_bit_count;
|
||||
integer s_write_nibble_count;
|
||||
logic [4:0] s_write_response_token;
|
||||
integer s_write_response_index;
|
||||
logic s_write_response_started;
|
||||
logic s_data_crc_error;
|
||||
logic s_data_timeout;
|
||||
logic s_irq_pending;
|
||||
logic [2:0] s_cmd_function;
|
||||
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);
|
||||
logic [6:0] crc;
|
||||
@@ -404,17 +408,20 @@ module sdio_native_card_model #(
|
||||
endtask
|
||||
|
||||
task automatic command_received(input logic [47:0] frame_i);
|
||||
logic [ 5:0] index;
|
||||
logic [31:0] argument;
|
||||
logic [ 6:0] expected_crc;
|
||||
logic [39:0] body;
|
||||
logic [31:0] response_payload;
|
||||
integer address;
|
||||
logic [ 5:0] index;
|
||||
logic [ 31:0] argument;
|
||||
logic [ 6:0] expected_crc;
|
||||
logic [ 39:0] body;
|
||||
logic [ 31:0] response_payload;
|
||||
logic [127:0] memory_csd_body;
|
||||
logic application_command;
|
||||
integer address;
|
||||
begin
|
||||
index = frame_i[45:40];
|
||||
argument = frame_i[39:8];
|
||||
body = frame_i[47:8];
|
||||
expected_crc = crc7_40(body);
|
||||
application_command = s_last_cmd == 6'd55;
|
||||
s_last_cmd = index;
|
||||
s_last_arg = argument;
|
||||
s_last_cmd_data = 1'b0;
|
||||
@@ -471,7 +478,15 @@ module sdio_native_card_model #(
|
||||
cmd_do_o = 1'b1;
|
||||
end
|
||||
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_index = 0;
|
||||
s_cmd_response_delay = s_response_delay;
|
||||
@@ -492,6 +507,15 @@ module sdio_native_card_model #(
|
||||
s_busy_count = BusyCycles;
|
||||
end
|
||||
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);
|
||||
s_inject_crc_error = 1'b0;
|
||||
end
|
||||
@@ -654,9 +678,10 @@ module sdio_native_card_model #(
|
||||
s_last_arg = 32'd0;
|
||||
s_last_cmd_write = 1'b0;
|
||||
s_last_cmd_data = 1'b0;
|
||||
s_memory_width4 = 1'b0;
|
||||
s_pending_busy = 1'b0;
|
||||
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
|
||||
s_cmd_shift = {{47{1'b0}}, cmd_do_i};
|
||||
s_cmd_bits = 1;
|
||||
@@ -717,6 +742,7 @@ module sdio_native_card_model #(
|
||||
s_last_arg = 32'd0;
|
||||
s_last_cmd_write = 1'b0;
|
||||
s_last_cmd_data = 1'b0;
|
||||
s_memory_width4 = 1'b0;
|
||||
s_pending_busy = 1'b0;
|
||||
s_busy_count = 0;
|
||||
s_data_state = DataIdle;
|
||||
@@ -828,7 +854,7 @@ module sdio_native_card_model #(
|
||||
dat_do_o = 4'b1111;
|
||||
for (integer lane = 0; lane < 4; lane++) 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
|
||||
@@ -838,7 +864,7 @@ module sdio_native_card_model #(
|
||||
end else begin
|
||||
s_data_crc_index = s_data_crc_index + 1;
|
||||
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
|
||||
WriteResponse: begin
|
||||
@@ -850,7 +876,9 @@ module sdio_native_card_model #(
|
||||
s_write_response_index = s_write_response_index + 1;
|
||||
dat_do_o[0] = s_write_response_token[4-s_write_response_index];
|
||||
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;
|
||||
dat_do_o[0] = (s_write_response_token == 5'b00101) ? 1'b0 : 1'b1;
|
||||
end
|
||||
@@ -879,7 +907,7 @@ module sdio_native_card_model #(
|
||||
end else begin
|
||||
case (s_data_state)
|
||||
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_write_bit_count = 0;
|
||||
s_write_nibble_count = 0;
|
||||
@@ -903,7 +931,8 @@ module sdio_native_card_model #(
|
||||
s_data_crc_error = 1'b0;
|
||||
if (s_data_width4) 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;
|
||||
end
|
||||
end
|
||||
@@ -938,6 +967,9 @@ module sdio_native_card_model #(
|
||||
end
|
||||
|
||||
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
|
||||
$fatal(1, "sdio_native_card_model: invalid storage geometry");
|
||||
end
|
||||
@@ -955,6 +987,7 @@ module sdio_native_card_model #(
|
||||
s_last_arg = 32'd0;
|
||||
s_last_cmd_write = 1'b0;
|
||||
s_last_cmd_data = 1'b0;
|
||||
s_memory_width4 = 1'b0;
|
||||
s_pending_busy = 1'b0;
|
||||
s_busy_count = 0;
|
||||
s_data_state = DataIdle;
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
module sdio_standalone_tb #(
|
||||
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] ClockCtrl = 32'h010;
|
||||
@@ -65,8 +67,21 @@ module sdio_standalone_tb #(
|
||||
integer timeout_count_before;
|
||||
integer crc_count_before;
|
||||
|
||||
assign sdio.cmd_di_i = sdio.cmd_oe_o ? sdio.cmd_do_o : card_cmd_do;
|
||||
assign sdio.dat_di_i = (sdio.dat_oe_o & sdio.dat_do_o) | (~sdio.dat_oe_o & card_dat_do);
|
||||
tri1 command_pad, data0_pad, data1_pad, data2_pad, data3_pad;
|
||||
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;
|
||||
|
||||
@@ -87,15 +102,15 @@ module sdio_standalone_tb #(
|
||||
.HighCapacity(CardHighCapacity),
|
||||
.BlockBytes (512),
|
||||
.BlockCount (128),
|
||||
.BusyCycles (8)
|
||||
.BusyCycles (CardBusyCycles)
|
||||
) u_card (
|
||||
.rst_n_i (rst_n_i),
|
||||
.sck_i (sdio.sck_o),
|
||||
.cmd_oe_i(sdio.cmd_oe_o),
|
||||
.cmd_do_i(sdio.cmd_do_o),
|
||||
.cmd_do_i(PadLoopback ? command_pad : sdio.cmd_do_o),
|
||||
.cmd_do_o(card_cmd_do),
|
||||
.dat_oe_i(sdio.dat_oe_o),
|
||||
.dat_do_i(sdio.dat_do_o),
|
||||
.dat_do_i(PadLoopback ? data_pads : sdio.dat_do_o),
|
||||
.dat_do_o(card_dat_do),
|
||||
.irq_o (card_irq)
|
||||
);
|
||||
@@ -204,7 +219,13 @@ module sdio_standalone_tb #(
|
||||
end
|
||||
end
|
||||
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
|
||||
endtask
|
||||
@@ -552,6 +573,84 @@ module sdio_standalone_tb #(
|
||||
$fatal(1, "memory-to-card DMA end-to-end direction failed");
|
||||
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);
|
||||
timeout_count_before = apb_value;
|
||||
u_axi_memory.clear_memory();
|
||||
@@ -675,3 +774,10 @@ endmodule
|
||||
module sdio_onebit_tb;
|
||||
sdio_standalone_tb #(.RunFourBit(1'b0)) u_tb ();
|
||||
endmodule
|
||||
|
||||
module tiny_p6_sdio_pads_tb;
|
||||
sdio_standalone_tb #(
|
||||
.PadLoopback (1'b1),
|
||||
.CardBusyCycles(2)
|
||||
) u_tb ();
|
||||
endmodule
|
||||
@@ -1,8 +1,9 @@
|
||||
`include "tiny_topology.svh"
|
||||
`timescale 1ns / 1ps
|
||||
module tiny_axi4_fabric_tb;
|
||||
logic clk_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;
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
@@ -18,7 +19,7 @@ module tiny_axi4_fabric_tb;
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) targets[23] (
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
@@ -27,8 +28,8 @@ module tiny_axi4_fabric_tb;
|
||||
logic [5:0][ 1:0] fault_resp;
|
||||
logic [63:0] cpu_wait, dma_wait;
|
||||
integer faults = 0;
|
||||
integer completions [ 3];
|
||||
integer target_requests[23];
|
||||
integer completions [ 3];
|
||||
integer target_requests[`RETROSOC_TINY__TARGET_COUNT];
|
||||
logic [2:0][31:0] awaddr;
|
||||
logic [2:0][ 7:0] awlen;
|
||||
logic [2:0][ 2:0] awsize;
|
||||
@@ -78,7 +79,7 @@ module tiny_axi4_fabric_tb;
|
||||
.cpu_wait_o (cpu_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;
|
||||
end
|
||||
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].wuser = '0;
|
||||
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,
|
||||
// burst length and ID preservation without sharing the fabric decoder.
|
||||
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 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
@@ -273,7 +274,7 @@ module tiny_axi4_fabric_tb;
|
||||
join
|
||||
if (completions[0]!=7 || completions[1]!=6 || faults!=7 ||
|
||||
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);
|
||||
read_request(0, 32'h30000000, 1, 2, 1, 0);
|
||||
arprot[0] = 3'b100;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
`include "tiny_topology.svh"
|
||||
`timescale 1ns / 1ps
|
||||
module tiny_r2_p5_fabric_tb;
|
||||
logic clk_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;
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
@@ -18,7 +19,7 @@ module tiny_r2_p5_fabric_tb;
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) targets[23] (
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
@@ -27,14 +28,15 @@ module tiny_r2_p5_fabric_tb;
|
||||
logic [5:0][ 1:0] fault_resp;
|
||||
logic [63:0] cpu_wait, dma_wait;
|
||||
integer faults = 0;
|
||||
logic [22:0] hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0;
|
||||
integer error_beat [23];
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0]
|
||||
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 release_error = 0;
|
||||
integer simultaneous_faults = 0;
|
||||
logic [31:0] seed = 32'h12345678;
|
||||
integer completions [ 3];
|
||||
integer target_requests [23];
|
||||
integer completions [ 3];
|
||||
integer target_requests [`RETROSOC_TINY__TARGET_COUNT];
|
||||
logic [ 2:0][31:0] awaddr;
|
||||
logic [ 2:0][ 7:0] awlen;
|
||||
logic [ 2:0][ 2:0] awsize;
|
||||
@@ -84,7 +86,7 @@ module tiny_r2_p5_fabric_tb;
|
||||
.cpu_wait_o (cpu_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;
|
||||
end
|
||||
always @(posedge clk_i) begin
|
||||
@@ -140,9 +142,9 @@ module tiny_r2_p5_fabric_tb;
|
||||
assign masters[m].aruser = '0;
|
||||
assign masters[m].wuser = '0;
|
||||
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 #(
|
||||
.ErrorResponse(t == 21 ? 3 : (t == 22 ? 2 : 0))
|
||||
.ErrorResponse(t == `RETROSOC_TINY__DECODE_ERROR ? 3 : (t == `RETROSOC_TINY__PROTOCOL_ERROR ? 2 : 0))
|
||||
) u_response (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -346,13 +348,13 @@ module tiny_r2_p5_fabric_tb;
|
||||
end
|
||||
join
|
||||
// A waiting APB FIFO branch cannot block independent control admission.
|
||||
hold_read[12] = 1;
|
||||
hold_read[`RETROSOC_TINY__TARGET_XPI] = 1;
|
||||
fork
|
||||
read_request(1, 32'h10009000, 1, 2, 1, 0);
|
||||
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);
|
||||
hold_read[12] = 0;
|
||||
hold_read[`RETROSOC_TINY__TARGET_XPI] = 0;
|
||||
end
|
||||
join
|
||||
fork
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
`include "tiny_topology.svh"
|
||||
`timescale 1ns / 1ps
|
||||
module tiny_r2_p5_reset_tb;
|
||||
logic clk_i = 0, rst_n_i = 0;
|
||||
always #5 clk_i = !clk_i;
|
||||
logic target_rst_n[23];
|
||||
logic target_rst_n[`RETROSOC_TINY__TARGET_COUNT];
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
@@ -17,22 +18,26 @@ module tiny_r2_p5_reset_tb;
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) targets[23] (
|
||||
) targets[`RETROSOC_TINY__TARGET_COUNT] (
|
||||
.aclk (clk_i),
|
||||
.aresetn(target_rst_n)
|
||||
);
|
||||
apb4_if apb (
|
||||
.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;
|
||||
integer error_beat[23];
|
||||
integer captures[23], addresses[23], writes[23];
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0]
|
||||
hold_address = '0, hold_read = '0, hold_write = '0, hold_b = '0;
|
||||
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][31:0] fault_addr;
|
||||
logic [ 5:0][ 1:0] fault_resp;
|
||||
logic [ 2:0] owner_idle;
|
||||
logic [22:0] target_idle;
|
||||
logic [ 5:0][31:0] fault_addr;
|
||||
logic [ 5:0][ 1:0] fault_resp;
|
||||
logic [ 2:0] owner_idle;
|
||||
logic [`RETROSOC_TINY__TARGET_COUNT-1:0] target_idle;
|
||||
logic [63:0] cpu_wait, dma_wait;
|
||||
logic [2:0] arvalid = 0, awvalid = 0, wvalid = 0, rready = 0, bready = 0;
|
||||
logic [2:0][31:0] araddr = 0, awaddr = 0, wdata = 0;
|
||||
@@ -200,8 +205,8 @@ module tiny_r2_p5_reset_tb;
|
||||
end
|
||||
end
|
||||
|
||||
for (genvar t = 0; t < 23; t++) begin : gen_target
|
||||
if (t == 12) begin : gen_apb
|
||||
for (genvar t = 0; t < `RETROSOC_TINY__TARGET_COUNT; t++) begin : gen_target
|
||||
if (t == `RETROSOC_TINY__TARGET_XPI) begin : gen_apb
|
||||
tiny_axi42apb4 u_bridge (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(target_rst_n[t]),
|
||||
@@ -232,8 +237,8 @@ module tiny_r2_p5_reset_tb;
|
||||
end
|
||||
end
|
||||
end
|
||||
assign apb.pready = apb.psel && apb.penable && !hold_read[12] && !hold_b[12];
|
||||
assign apb.prdata = 32'hcafe_f00d;
|
||||
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.pslverr = 0;
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_n_i) begin
|
||||
@@ -249,7 +254,7 @@ module tiny_r2_p5_reset_tb;
|
||||
|
||||
task automatic quiet;
|
||||
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");
|
||||
endtask
|
||||
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
|
||||
for (integer n = 0; n < beats; n++) begin
|
||||
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);
|
||||
end
|
||||
target_rst_n[t] = 1;
|
||||
@@ -412,7 +417,7 @@ module tiny_r2_p5_reset_tb;
|
||||
endtask
|
||||
|
||||
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;
|
||||
captures[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;
|
||||
repeat (4) @(negedge clk_i);
|
||||
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
|
||||
// beats under backpressure, and already captured successful completion.
|
||||
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);
|
||||
error_beat[0] = -1;
|
||||
// Actual APB bridge, with a withheld peripheral completion.
|
||||
read_case(32'h10009000, 12, 1, 0, 1, 1);
|
||||
read_case(32'h10009000, 12, 1, 0, 0, 1);
|
||||
read_case(32'h10009000, 12, 1, 1, 0, 1);
|
||||
write_case(32'h10009000, 12, 1, 0, 0, 1);
|
||||
write_case(32'h10009000, 12, 1, 1, 0, 0);
|
||||
write_case(32'h10009000, 12, 1, 1, 1, 0);
|
||||
dma_case(32'h10009000, 12, 1, 0);
|
||||
read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 1, 1);
|
||||
read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 0, 1);
|
||||
read_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 0, 1);
|
||||
write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 0, 0, 1);
|
||||
write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 0, 0);
|
||||
write_case(32'h10009000, `RETROSOC_TINY__TARGET_XPI, 1, 1, 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("SIM_TEST_PASS Tiny P5 target reset isolation");
|
||||
$finish;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
`include "tiny_topology.svh"
|
||||
`timescale 1ns / 1ps
|
||||
module tiny_r2_p5_system_tb;
|
||||
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)
|
||||
overlap_cycles++;
|
||||
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)
|
||||
controls++;
|
||||
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);
|
||||
$display("P5_SYSTEM_STAGE reset-request pending=%b", u_dut.u_cpu.s_reset_pending);
|
||||
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");
|
||||
hart_reset_observed = 1;
|
||||
$display("P5_SYSTEM_STAGE hart-reset");
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -421,7 +421,7 @@ def test_commercial_timing_contract_supports_tiny(tmp_path: Path) -> None:
|
||||
check=True,
|
||||
)
|
||||
assert result.stdout.splitlines() == [
|
||||
"jtag,system",
|
||||
"audio,jtag,system",
|
||||
"ext_rst_n_i_pad,jtag_trst_n_i_pad",
|
||||
]
|
||||
assert "observation {u_clock_buffer/clk_o}" in output.read_text(
|
||||
@@ -660,7 +660,7 @@ CONSTRAINT_SMOKE_CASES = {
|
||||
"TINY": {
|
||||
"domains": "rtl/tiny/integration/clock_reset_domains.json",
|
||||
"pin_map": "rtl/tiny/pin_map/pin_map.json",
|
||||
"masters": "clk_system clk_jtag",
|
||||
"masters": "clk_system clk_jtag clk_audio",
|
||||
"generated": "",
|
||||
"groups": "1",
|
||||
},
|
||||
|
||||
@@ -96,6 +96,21 @@ def test_channel_coverage_cannot_drop_the_boot_channel(spec):
|
||||
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():
|
||||
with pytest.raises(ValueError, match="unsupported credit function"):
|
||||
pr.credit_values("function automatic int limits(input int master); return compute(master); endfunction", "limits", 8)
|
||||
|
||||
+23
-195
@@ -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
|
||||
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
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]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def data():
|
||||
return tiny.collect(tiny.read(ROOT / tiny.BOOK / "tiny.json"), check_snapshot=False)
|
||||
@pytest.mark.parametrize("revision", [ORIGINAL_BOOK, P5_CONTRACT])
|
||||
def test_historical_publication_contracts(tmp_path, revision):
|
||||
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):
|
||||
assert (data["facts"]["hardware_isa"], data["facts"]["firmware_isa"]) == ("RV32IMC", "RV32IM")
|
||||
assert data["facts"]["sram"] == {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1}
|
||||
assert data["facts"]["dma"]["NumChannels"] == 4
|
||||
assert data["facts"]["dma"]["RequestMask"] == 0x7F9
|
||||
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
|
||||
def test_current_source_cannot_relabel_historical_publication():
|
||||
config = tiny.read(ROOT / tiny.BOOK / "tiny.json")
|
||||
assert config["source_revision"] == "9c706ba0ff1da1028c299cfff573010a86dfd7ce"
|
||||
with pytest.raises(ValueError, match="DMA parameters changed"):
|
||||
tiny.collect(config)
|
||||
|
||||
|
||||
@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"),
|
||||
(".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")
|
||||
binding = next(item for item in contract["bindings"]
|
||||
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))
|
||||
with pytest.raises(ValueError, match="Tiny behavior binding changed"):
|
||||
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
@@ -51,7 +51,9 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
|
||||
assert "RS_SOC_PSRAM_BASE" not in sdk
|
||||
irq = (output / "include/retrosoc/generated/irq_metadata.h").read_text()
|
||||
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_NPU" not in irq
|
||||
# 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"):
|
||||
probe = tmp_path / "consumer.c"
|
||||
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"),
|
||||
"-I" + str(output / "memory_map/include"), "-I" + str(output / "include"),
|
||||
"-c", str(probe), "-o", str(tmp_path / "consumer.o")])
|
||||
|
||||
@@ -114,6 +114,8 @@ def mapped_netlist():
|
||||
"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":
|
||||
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": {
|
||||
"retrosoc_tiny_asic": {"ports": {"extclk_i_pad": {"direction": "inout", "bits": [1]},
|
||||
"jtag_tck_i_pad": {"direction": "inout", "bits": [11]}},
|
||||
@@ -126,6 +128,7 @@ def mapped_netlist():
|
||||
def test_safe24_structural_binding(mapped_netlist):
|
||||
result = inspect_netlist(mapped_netlist)
|
||||
assert len(result["sram"]) == 32 and result["cpu_clocked_cells"] == 1
|
||||
assert len(result["crypto_sram"]) == 6
|
||||
assert result["pll_enable"] == 0
|
||||
assert result["system_clock_source"]["port"] == "extclk_i_pad"
|
||||
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",
|
||||
"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):
|
||||
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_crypto = "u_soc.u_crypto.u_storage.gen_bank[0].gen_macro.u_sram.u_mem"
|
||||
if mutation == "missing_pll":
|
||||
del cells["pll"]
|
||||
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]
|
||||
elif mutation == "wrong_cpu":
|
||||
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:
|
||||
cells["pll"]["connections"]["CKOUT1"] = [2]
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@@ -59,8 +59,8 @@ def test_reset_inventory_has_one_clock_and_all_existing_targets() -> None:
|
||||
reset = inventory["reset_distribution"]
|
||||
assert reset["release_edges"] == 5
|
||||
assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])]
|
||||
assert len(reset["leaf_names"]) == 17
|
||||
assert {domain["name"] for domain in inventory["domains"]} == {"system", "jtag"}
|
||||
assert len(reset["leaf_names"]) == 21
|
||||
assert {domain["name"] for domain in inventory["domains"]} == {"system", "jtag", "audio"}
|
||||
assert reset["clock_domain"] == "system" and reset["main_sram_payload_reset"] == "none"
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user