feat: add testbenches for on-chip RAM and resource controller, enhance AXI4 and APB4 support

- Introduced  to validate AXI4 interface for 64-bit on-chip SRAM.
- Added  to test resource controller functionality including ownership, IRQ handling, and cache handshake.
- Updated  to include resource controller interface.
- Enhanced  to cover additional IRQ scenarios.
- Added new tests in  for AXI4 target guard timeout and isolation.
- Updated  to reflect changes in the high-performance profile.
- Modified  to verify on-chip SRAM bank configurations.
- Added  for directed verification of resource controller.
- Updated  to check SDRAM mapped window coverage.
- Enhanced  to ensure correct IRQ mappings and APB target generation.
This commit is contained in:
maksyuki committed 2026-08-30 19:50:22 +08:00
1 parent 98eb8ebad6
commit 62d112c4e6
77 files changed
+2821 -513

No files matched your search

+2 -2
View File
@@ -44,7 +44,7 @@ SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32)
PDK_BEHAV ?= NO
HAVE_SVA ?= NO
HAVE_HP ?= YES
HP_CONFIG ?= rv32imafdc_max
HP_CONFIG ?= rv32imafdc_zicbom_max
BUILD_RELEASE ?= NO
JTAG_IDCODE ?= DEADBEEF
EXT_CLK_HZ ?= 72000000
@@ -159,7 +159,7 @@ VALID_SYNTH_RECIPE := balanced area speed
VALID_STA := NONE OPENSTA
VALID_PDK := ICS55 IHP130 SKY130 GF180
VALID_BOOL := YES NO
VALID_HP_CONFIG := rv32imafdc_max
VALID_HP_CONFIG := rv32imafdc_zicbom_max
VALID_ISA := RV32E RV32I RV32IM
VALID_APP := benchmark bringup ci_smoke coremark debug hp_boot shell xpi_flash_loader
VALID_LINK_TYPE := xip jtag_sram ld2_all_sram ld2_sram ld2_psram ld2_sdram
+2 -2
View File
@@ -91,8 +91,8 @@ used with commercial 32 KiB SRAM and PLL simulation models.
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
| [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. |
| [`configs/ci/ihp130-hp.mk`](configs/ci/ihp130-hp.mk) | LP RV32IM / HP RV32IMAFDC | `hp_boot` | Linux image/bundle flow and HP RTL validation on the fixed product topology. |
| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | LP RV32IM / HP RV32IMAFDC | `coremark` | Fixed four-iteration LP SRAM CoreMark measurement with the product HP core present. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | LP RV32IM / HP RV32IMAFDC | `bringup` | Site profile with PLL/SRAM intentionally disabled for regression compatibility. |
| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | LP RV32IM / HP RV32IMAFDC+Zicbom | `coremark` | Fixed four-iteration LP SRAM CoreMark measurement with the product HP core present. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | LP RV32IM / HP RV32IMAFDC+Zicbom | `bringup` | Site profile with PLL/SRAM intentionally disabled for regression compatibility. |
| [`configs/cluster/mini-mpw.mk`](configs/cluster/mini-mpw.mk) | RV32IM | `bringup` | Legacy MPW C0-C3/user-IP selection profile; not part of the Mini product ABI. |
CI Verilator firmware simulations explicitly select the `ci_smoke`
+4 -3
View File
@@ -262,9 +262,10 @@ static bool rs_ci_smoke_sdram_wait_ready(void) {
}
static bool rs_ci_smoke_sdram_access(void) {
volatile uint8_t *const bytes = (volatile uint8_t *)(uintptr_t)RS_SOC_SDRAM_BASE;
volatile uint16_t *const halfs = (volatile uint16_t *)(uintptr_t)RS_SOC_SDRAM_BASE;
volatile uint32_t *const words = (volatile uint32_t *)(uintptr_t)RS_SOC_SDRAM_BASE;
const uintptr_t scratch = (uintptr_t)(RS_SOC_SDRAM_END - RS_CI_SMOKE_SDRAM_SPAN + UINT32_C(1));
volatile uint8_t *const bytes = (volatile uint8_t *)scratch;
volatile uint16_t *const halfs = (volatile uint16_t *)scratch;
volatile uint32_t *const words = (volatile uint32_t *)scratch;
uint32_t index;
if (!rs_ci_smoke_sdram_wait_ready()) {
+3 -2
View File
@@ -24,8 +24,9 @@
clock-frequency = <72000000>;
mmu-type = "riscv,sv32";
riscv,isa-base = "rv32i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr",
"zifencei";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicbom",
"zicsr", "zifencei";
riscv,cbom-block-size = <64>;
hp_cpu_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
+1 -1
View File
@@ -14,7 +14,7 @@ CoreMark quick measurement; its `-standard` counterpart is reserved for a
runs the quick CoreMark profile.
`ci/ihp130-hp.mk` is the asymmetric Linux application profile. It starts HP
from the external 72 MHz safe clock and LP from REF24, runs `hp_boot` entirely
from 32 KiB on-chip SRAM, and
from 32 KiB on-chip SRAM, enables VexiiRiscv `Zicbom` with 64-byte blocks, and
generates VexiiRiscv RTL only below the selected build variant. It is not part
of the supported PR matrix until Linux boot, HP performance, synthesis, and
timing evidence are qualified.
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1 -1
View File
@@ -3,7 +3,7 @@ MINI_MODE := PRODUCT
PDK := IHP130
HAVE_PLL := NO
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -6,7 +6,7 @@ HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 32
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HP_CONFIG := rv32imafdc_zicbom_max
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+4
View File
@@ -52,6 +52,10 @@ MPW compatibility API; selector mutators return `RS_ENOTSUP` in product builds.
See the [extension contract](../docs/ip/extensions.md) and
[legacy user-IP contract](../docs/ip/user-ip.md).
`<retrosoc/hal/resource.h>` owns the central DMA, USB2, SDIO0/1, SPI-SD, and
EXT-H owner/lock, lifecycle-request, fault, and HP cache-maintenance handshake.
See the [Resource Controller contract](../docs/ip/resource-controller.md).
`<retrosoc/hal/memory.h>` provides the uniform Mini memory inventory for
SRAM, SDRAM, QPI PSRAM, OPI/HyperBus PSRAM, and XPI. It reports the fixed
window, device/initialization/ready/active state, shared-pad mode, DMA and
+48
View File
@@ -0,0 +1,48 @@
#ifndef RETROSOC_HAL_RESOURCE_H
#define RETROSOC_HAL_RESOURCE_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
typedef enum {
RS_RESOURCE_DMA = 0,
RS_RESOURCE_USB2 = 1,
RS_RESOURCE_SDIO0 = 2,
RS_RESOURCE_SDIO1 = 3,
RS_RESOURCE_SPISD = 4,
RS_RESOURCE_EXT_H = 5,
RS_RESOURCE_COUNT = 6,
} rs_resource_t;
typedef enum {
RS_RESOURCE_OWNER_LP = 0,
RS_RESOURCE_OWNER_HP = 1,
} rs_resource_owner_t;
typedef struct {
rs_resource_owner_t owner;
uint16_t handoff_count;
bool owner_locked;
bool blocked;
bool idle;
bool quiesced;
bool in_reset;
bool irq_pending;
bool fault;
} rs_resource_status_t;
typedef struct {
bool request;
bool clean;
} rs_resource_cache_status_t;
rs_status_t rs_resource_get_status(rs_resource_t resource, rs_resource_status_t *status);
rs_status_t rs_resource_set_owner(rs_resource_t resource, rs_resource_owner_t owner, bool lock);
rs_status_t rs_resource_set_lifecycle(rs_resource_t resource, bool quiesce, bool reset);
rs_status_t rs_resource_clear_fault(rs_resource_t resource);
rs_status_t rs_resource_get_cache_status(rs_resource_cache_status_t *status);
rs_status_t rs_resource_acknowledge_cache_clean(void);
#endif
+18
View File
@@ -3,6 +3,7 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/extension.h>
#include <retrosoc/hal/resource.h>
#define RS_EXTENSION_IDENTIFICATION_OFFSET UINT32_C(0x000)
#define RS_EXTENSION_VERSION_OFFSET UINT32_C(0x004)
@@ -110,6 +111,7 @@ rs_status_t rs_extension_get_status(rs_extension_slot_t slot, rs_extension_statu
rs_status_t rs_extension_set_owner(rs_extension_slot_t slot, rs_extension_owner_t owner,
bool lock) {
uint32_t value;
rs_status_t status;
if ((owner != RS_EXTENSION_OWNER_LP) && (owner != RS_EXTENSION_OWNER_HP)) {
return RS_EINVAL;
@@ -118,12 +120,19 @@ rs_status_t rs_extension_set_owner(rs_extension_slot_t slot, rs_extension_owner_
if (lock) {
value |= RS_EXTENSION_OWNER_LOCK;
}
if (slot == RS_EXTENSION_SLOT_H) {
status = rs_resource_set_owner(RS_RESOURCE_EXT_H, (rs_resource_owner_t)owner, lock);
if (status != RS_OK) {
return status;
}
}
return rs_extension_write(slot, RS_EXTENSION_OWNER_OFFSET, value);
}
rs_status_t rs_extension_set_lifecycle(rs_extension_slot_t slot, bool quiesce, bool reset,
bool clear_fault) {
uint32_t command = 0U;
rs_status_t status;
if (quiesce) {
command |= RS_EXTENSION_COMMAND_QUIESCE;
@@ -134,6 +143,15 @@ rs_status_t rs_extension_set_lifecycle(rs_extension_slot_t slot, bool quiesce, b
if (clear_fault) {
command |= RS_EXTENSION_COMMAND_CLEAR_FAULT;
}
if (slot == RS_EXTENSION_SLOT_H) {
status = rs_resource_set_lifecycle(RS_RESOURCE_EXT_H, quiesce, reset);
if (status != RS_OK) {
return status;
}
if (clear_fault && (rs_resource_clear_fault(RS_RESOURCE_EXT_H) != RS_OK)) {
return RS_EIO;
}
}
return rs_extension_write(slot, RS_EXTENSION_COMMAND_OFFSET, command);
}
+1 -1
View File
@@ -31,7 +31,7 @@ rs_status_t rs_onchip_sram_probe(rs_onchip_sram_info_t *info) {
info->present = (capability & RS_ONCHIP_SRAM_CAP_PRESENT) != 0U;
if ((bank_bytes != RS_ONCHIP_SRAM_BANK_BYTES_VALUE) || (info->max_burst_beats != 16U) ||
(info->data_bytes != 4U) ||
((info->data_bytes != 4U) && (info->data_bytes != 8U)) ||
((capability & (RS_ONCHIP_SRAM_CAP_NATIVE_AXI4 | RS_ONCHIP_SRAM_CAP_BYTE_WRITE |
RS_ONCHIP_SRAM_CAP_FIXED | RS_ONCHIP_SRAM_CAP_INCR |
RS_ONCHIP_SRAM_CAP_WRAP | RS_ONCHIP_SRAM_CAP_PERFORMANCE)) !=
+142
View File
@@ -0,0 +1,142 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/resource.h>
#define RS_RESOURCE_CACHE_CONTROL_OFFSET UINT32_C(0x010)
#define RS_RESOURCE_BASE_OFFSET UINT32_C(0x100)
#define RS_RESOURCE_STRIDE UINT32_C(0x020)
#define RS_RESOURCE_OWNER_OFFSET UINT32_C(0x000)
#define RS_RESOURCE_CONTROL_OFFSET UINT32_C(0x004)
#define RS_RESOURCE_STATUS_OFFSET UINT32_C(0x008)
#define RS_RESOURCE_FAULT_OFFSET UINT32_C(0x00C)
#define RS_RESOURCE_HANDOFF_COUNT_OFFSET UINT32_C(0x010)
#define RS_RESOURCE_OWNER_MASK UINT32_C(0x00000003)
#define RS_RESOURCE_OWNER_LOCK UINT32_C(0x00000100)
#define RS_RESOURCE_CONTROL_QUIESCE UINT32_C(0x00000001)
#define RS_RESOURCE_CONTROL_RESET UINT32_C(0x00000002)
#define RS_RESOURCE_STATUS_IDLE UINT32_C(0x00000010)
#define RS_RESOURCE_STATUS_FAULT UINT32_C(0x00000020)
#define RS_RESOURCE_STATUS_IRQ UINT32_C(0x00000040)
#define RS_RESOURCE_STATUS_BLOCKED UINT32_C(0x00000080)
#define RS_RESOURCE_CACHE_CLEAN UINT32_C(0x00000001)
#define RS_RESOURCE_CACHE_REQUEST UINT32_C(0x00000002)
static bool rs_resource_valid(rs_resource_t resource) {
return (uint32_t)resource < (uint32_t)RS_RESOURCE_COUNT;
}
static volatile uint32_t *rs_resource_register(uint32_t offset) {
return (volatile uint32_t *)(RS_SOC_APB4_RESOURCE_CTRL_BASE + (uintptr_t)offset);
}
static uint32_t rs_resource_offset(rs_resource_t resource, uint32_t offset) {
return RS_RESOURCE_BASE_OFFSET + ((uint32_t)resource * RS_RESOURCE_STRIDE) + offset;
}
rs_status_t rs_resource_get_status(rs_resource_t resource, rs_resource_status_t *status) {
uint32_t owner;
uint32_t state;
if ((status == NULL) || !rs_resource_valid(resource)) {
return RS_EINVAL;
}
owner = *rs_resource_register(rs_resource_offset(resource, RS_RESOURCE_OWNER_OFFSET));
state = *rs_resource_register(rs_resource_offset(resource, RS_RESOURCE_STATUS_OFFSET));
status->handoff_count = (uint16_t)*rs_resource_register(
rs_resource_offset(resource, RS_RESOURCE_HANDOFF_COUNT_OFFSET));
status->owner = (rs_resource_owner_t)(owner & RS_RESOURCE_OWNER_MASK);
status->owner_locked = (owner & RS_RESOURCE_OWNER_LOCK) != 0U;
status->blocked = (state & RS_RESOURCE_STATUS_BLOCKED) != 0U;
status->idle = (state & RS_RESOURCE_STATUS_IDLE) != 0U;
status->quiesced = (state & RS_RESOURCE_CONTROL_QUIESCE) != 0U;
status->in_reset = (state & RS_RESOURCE_CONTROL_RESET) != 0U;
status->fault = (state & RS_RESOURCE_STATUS_FAULT) != 0U;
status->irq_pending = (state & RS_RESOURCE_STATUS_IRQ) != 0U;
return RS_OK;
}
rs_status_t rs_resource_set_owner(rs_resource_t resource, rs_resource_owner_t owner, bool lock) {
rs_resource_status_t status;
uint32_t value;
uint32_t timeout;
if (!rs_resource_valid(resource) ||
((owner != RS_RESOURCE_OWNER_LP) && (owner != RS_RESOURCE_OWNER_HP))) {
return RS_EINVAL;
}
if ((rs_resource_get_status(resource, &status) != RS_OK) || status.owner_locked) {
return RS_EIO;
}
if (rs_resource_set_lifecycle(resource, true, false) != RS_OK) {
return RS_EIO;
}
for (timeout = 0U; timeout < RS_TIMEOUT_DEFAULT; ++timeout) {
if ((rs_resource_get_status(resource, &status) == RS_OK) && status.blocked && status.idle) {
break;
}
}
if (!status.blocked || !status.idle) {
(void)rs_resource_set_lifecycle(resource, false, false);
return RS_ETIMEOUT;
}
value = (uint32_t)owner;
if (lock) {
value |= RS_RESOURCE_OWNER_LOCK;
}
*rs_resource_register(rs_resource_offset(resource, RS_RESOURCE_OWNER_OFFSET)) = value;
if (rs_resource_get_status(resource, &status) != RS_OK) {
(void)rs_resource_set_lifecycle(resource, false, false);
return RS_EIO;
}
(void)rs_resource_set_lifecycle(resource, false, false);
return ((status.owner == owner) && (status.owner_locked == lock)) ? RS_OK : RS_EIO;
}
rs_status_t rs_resource_set_lifecycle(rs_resource_t resource, bool quiesce, bool reset) {
uint32_t value = 0U;
if (!rs_resource_valid(resource)) {
return RS_EINVAL;
}
if (quiesce) {
value |= RS_RESOURCE_CONTROL_QUIESCE;
}
if (reset) {
value |= RS_RESOURCE_CONTROL_RESET;
}
*rs_resource_register(rs_resource_offset(resource, RS_RESOURCE_CONTROL_OFFSET)) = value;
return RS_OK;
}
rs_status_t rs_resource_clear_fault(rs_resource_t resource) {
if (!rs_resource_valid(resource)) {
return RS_EINVAL;
}
*rs_resource_register(rs_resource_offset(resource, RS_RESOURCE_FAULT_OFFSET)) = UINT32_C(1);
return RS_OK;
}
rs_status_t rs_resource_get_cache_status(rs_resource_cache_status_t *status) {
uint32_t value;
if (status == NULL) {
return RS_EINVAL;
}
value = *rs_resource_register(RS_RESOURCE_CACHE_CONTROL_OFFSET);
status->request = (value & RS_RESOURCE_CACHE_REQUEST) != 0U;
status->clean = (value & RS_RESOURCE_CACHE_CLEAN) != 0U;
return RS_OK;
}
rs_status_t rs_resource_acknowledge_cache_clean(void) {
rs_resource_cache_status_t status;
if ((rs_resource_get_cache_status(&status) != RS_OK) || !status.request) {
return RS_EIO;
}
*rs_resource_register(RS_RESOURCE_CACHE_CONTROL_OFFSET) = RS_RESOURCE_CACHE_CLEAN;
return RS_OK;
}
+2
View File
@@ -61,6 +61,8 @@ the root README and subsystem guides.
behavior of the legacy 4 KiB user-IP window.
- [ip/extensions.md](ip/extensions.md) defines product EXT-L/EXT-H fixed
windows, lifecycle, capability, ACL, fault, and HAL contracts.
- [ip/resource-controller.md](ip/resource-controller.md) defines centralized
DMA/I/O/EXT-H ownership, IRQ routing, cache handoff, and fault contracts.
- [ip/clint.md](ip/clint.md) defines the standard CLINT register map, fixed
timebase, RV32 access rules, interrupt behavior, and verification contract.
- [ip/gpio.md](ip/gpio.md) defines the GPIO dual-window ABI, pad ownership,
+13 -12
View File
@@ -40,15 +40,16 @@ IDs. It has eight masters:
| 7 | EXT-H | PCLK-to-HP AXI64 CDC |
Each source receives a fixed three-bit master prefix. The crossbar maintains
independent read and write owners for each target, enabling read/write overlap
and cross-target concurrency. Different IDs from HP/DMA/EXT-H may be active on
different targets up to the master credit. The same ID is blocked until
completion, and each target remains single-outstanding per direction.
independent read and write arbitration for each target, enabling read/write
overlap and cross-target concurrency. Different IDs from HP/DMA/EXT-H may be
active on the same or different targets up to master and target credits. The
same source ID is blocked until completion. SRAM/SDRAM target credits are four
reads and two writes; serial and error targets use one read and one write.
| Target | Current backend |
| --- | --- |
| on-chip SRAM | AXI64-to-32, HP-to-LP CDC, native AXI32 SRAM |
| SDRAM | AXI64-to-32, HP-to-LP CDC, AXI32 controller |
| on-chip SRAM | native AXI64/ID6 striped technology macros in HP |
| SDRAM | HP-to-memory AXI64 CDC, local 64-to-32 SDRAM adaptation |
| QPI PSRAM | AXI64-to-32, CDC, selected QPI frontend |
| OPI/HyperBus | AXI64-to-32, CDC, selected OPI frontend |
| XPI/flash | AXI64-to-32, CDC, XPI frontend |
@@ -68,15 +69,15 @@ fail-closed according to the synchronized AON pad mode.
- `apb4_async_bridge` converts one APB request into a request/response CDC
transaction and guarantees a finite APB completion when the destination runs.
Normal HP shutdown blocks new addresses, drains accepted traffic, and performs
a coordinated warm flush. Forced timeout records a lifecycle fault. Synthetic
completion for every accepted transaction behind a permanently stopped target
is not yet claimed.
Normal HP shutdown requests software cache maintenance, blocks new addresses,
drains accepted traffic, and performs a coordinated warm flush. Target guards
provide bounded synthetic `SLVERR` completion and fail-closed isolation for a
stopped target.
## Verification boundary
`tests/test_axi4.py` retains LP fabric, downsizer, burst, response, and
backpressure coverage. Full-product Verilator/Icarus simulation exercises LP
boot, HP elaboration, the memory gateways, and product APB paths. The current
implementation does not claim same-target multi-outstanding reordering, cache
coherency, formal crossbar proof, CDC signoff, or physical bandwidth closure.
implementation does not claim cache coherency, full AXI formal proof, CDC
signoff, or physical bandwidth closure.
+12 -4
View File
@@ -30,10 +30,18 @@ gateway, and EXT-H use a 64-bit memory plane. APB remains a separate control
plane. Every data-plane initiator has a fixed identity, an outstanding-credit
limit, a QoS class, an access policy, and fault attribution.
The first delivery contract is target-aware rather than uniform: high
bandwidth initiators can issue up to four reads and two writes, while serial
memory targets advertise lower credits. Stable target-specific functional
clocks keep SDRAM refresh and serial-memory timing independent of HP DFS.
The independent Resource Controller applies the RT1180/TRDC-style resource
domain idea without claiming a security lifecycle: six fixed resources have
idle-gated owner changes, sticky owner lock, fault attribution, and mutually
exclusive LP/HP IRQ delivery. The AON shutdown sequence uses a 64-byte Zicbom
cache-maintenance request/ACK before blocking HP traffic. This is explicit
software-managed coherency, not a coherent interconnect.
The first delivery contract is target-aware rather than uniform: SRAM/SDRAM
admit four reads and two writes, while serial memory targets advertise lower
credits. A queued target guard bounds stalls and fail-closes after accepted
timeouts. Stable target-specific functional clocks keep SDRAM refresh and
serial-memory timing independent of HP DFS.
No release may describe the implementation as timing closed, CDC signed off,
silicon qualified, functionally safe, or cache coherent until the separate
+7 -5
View File
@@ -16,12 +16,14 @@
Regression Verilator firmware simulations override the profiles' manual
`bringup` default with `APP=ci_smoke`. The IHP130 PR simulation also uses the
32 KiB `ld2_all_sram` layout, the explicit Verilator `--fast-flash` backend,
and a 360-second wall-clock budget. The full serial XPI path remains covered
by the Icarus assembly self-test and directed XPI model tests; the accelerated
run is the broad software-visible integration test. This application verifies UART output,
and a 360-second wall-clock budget. GF180, ICS55, and SKY130 use the common
`ld2_sdram` execution layout, the same fast-flash backend, and a 600-second
budget so the broad smoke does not execute from the serial PSRAM model. The
full serial XPI and PSRAM path remains covered by the Icarus assembly self-test
and directed memory-model tests. This application verifies UART output,
archinfo APB readback, on-chip SRAM first/last-word and 8/16/32-bit access,
RNG integration, SDRAM 8/16/32-bit mapped-window access,
and test-status completion within the CI time budget;
RNG integration, SDRAM 8/16/32-bit access in a reserved tail scratch area, and
test-status completion within the CI time budget;
`bringup` retains the full automatic application-information report for
manual runs.
+1
View File
@@ -6,6 +6,7 @@ extension interfaces.
- [timer.md](timer.md) defines the dual APB4 general timer.
- [sysctrl.md](sysctrl.md) defines the APB4 SystemCtrl register ABI, control-plane contracts, HAL, and verification.
- [resource-controller.md](resource-controller.md) defines root-managed resource ownership, IRQ routing, and cache-maintenance handoff.
- [user-ip.md](user-ip.md) defines the selectable user-IP window and software ownership boundary.
- [ws2812.md](ws2812.md) defines the WS2812 transmitter.
- [clint.md](clint.md) defines the management-hart software and timer interrupt block.
+5 -3
View File
@@ -38,9 +38,11 @@ supports a byte-masked final beat and reports AXI response errors and
no-progress timeouts. Programmed ranges are enforced by the data-plane M7
firewall; M6 and control-plane addresses remain denied in hardware.
OWNER also controls interrupt routing. LP ownership delivers the slot event on
LP IRQ 28; HP ownership delivers it on HP PLIC source 3. The two routes are
mutually exclusive. Quiesce or reset blocks new EXT-H AXI addresses and STATUS
For EXT-H, the central Resource Controller owner is the interrupt-routing
authority. LP ownership delivers the slot event on LP IRQ 28; HP ownership
delivers it on HP PLIC source 3, never both. The local `OWNER` register remains
an ABI-compatible mirror, and `rs_extension_set_owner()` updates both through
the Resource HAL. Quiesce or reset blocks new EXT-H AXI addresses and STATUS
does not report idle until the DMA and address gate have drained.
`<retrosoc/hal/extension.h>` provides discovery, status, ownership, lifecycle,
+26 -19
View File
@@ -2,9 +2,12 @@
## Scope and maturity
The Mini SoC on-chip SRAM is a synthesis-time configurable, 32-bit native-AXI4
target backed by fixed `1024 x 32` single-port technology wrappers. Supported
capacities are 4, 16, 32, 64, and 128 KiB. The data aperture starts at
The Mini SoC on-chip SRAM is a synthesis-time configurable AXI4 target backed
by fixed 32-bit single-port technology wrappers. The reusable module keeps a
32-bit/ID1 compatibility mode; the Mini product instantiates native
64-bit/ID6 and stripes each beat over two wrappers. Supported capacities are
4, 16, 32, 64, and 128 KiB in compatibility mode; the product selects 32 KiB.
The data aperture starts at
`0x3000_0000`; its generated end address and linker length match the selected
physical capacity. A management-only APB4 window at `0x1001_7000` reports the
configuration and saturating performance counters.
@@ -29,10 +32,10 @@ dependencies and do not transfer a vendor's qualification to retroSoC.
| [CAST SRAM-CTRL and ECC-SRAM](https://www.cast-inc.com/peripherals/memory-controllers/sram-ctrl) | Commercial IP emphasizes native AXI/AHB/APB, byte accesses, low latency, lint/scan readiness, SECDED, testbenches, scripts, and integration documentation. | The products remain offered. Use the features and deliverables as an IP-readiness checklist, not as evidence for this implementation. |
| [Synopsys STAR Memory System](https://www.synopsys.com/articles/embedded-memory-test.html) | Hierarchical test, diagnosis, redundancy repair, tester patterns, and physical failed-bit mapping address manufacturing yield and field diagnosis. | The product family remains published. Keep DFT/repair as a physical-delivery phase instead of embedding an unqualified March engine in the AXI datapath. |
The selected architecture is static capacity configuration plus a native AXI
datapath. Dynamic bank reassignment, caches, multiple outstanding IDs, and
multi-master bank scheduling require changes above this target and are not
cost-effective in the current one-owner-per-target fabric.
The selected architecture is static capacity configuration plus a native AXI64
datapath. The data crossbar queues up to four SRAM reads and two writes while
this SRAM engine completes one transaction at a time; IDs are preserved to the
terminal response.
## Configuration and address contract
@@ -56,19 +59,20 @@ with `PRESENT=0`, zero memory bytes, and zero banks.
## AXI4 datapath
The target uses the project-wide 32-bit address/data and 1-bit ID/USER
geometry. It accepts one active read or write transaction, gives AR priority
when AR and AW are simultaneously valid, and preserves the ID to the terminal
response.
The product target uses 32-bit addresses, 64-bit data, six-bit ID, and one-bit
USER. It accepts one active read or write transaction, gives AR priority when
AR and AW are simultaneously valid, and preserves the ID to the terminal
response. The compatibility parameterization retains 32-bit data and one-bit
ID for standalone/formal reuse.
Supported requests are naturally aligned 1-, 2-, or 4-byte transfers using
Supported product requests are naturally aligned 1-, 2-, 4-, or 8-byte transfers using
`FIXED`, `INCR`, or legal 2/4/8/16-beat `WRAP` bursts. Bursts are limited to 16
beats and one 4 KiB page. Because a bank is exactly one 4 KiB page, a legal
transaction selects one bank for its lifetime.
transaction selects one striped bank pair for its lifetime.
The macro is issued directly on AR acceptance. The first response is available
one cycle later. With `RREADY` asserted, following beats issue on preceding
response cycles, giving one word per cycle without bubbles. Backpressure stops
response cycles, giving one 64-bit beat per cycle without bubbles. Backpressure stops
new reads and holds data, ID, response, and `RLAST` stable.
After AW acceptance the target consumes one W beat per cycle. Strobes outside
@@ -110,7 +114,8 @@ the register parity test prevents drift without adding a generator.
## Technology mapping
`tc_sram_1024x32` is the stable technology boundary. Behavioral simulation
`tc_sram_1024x32` and `tc_sram_4096x32` remain the stable technology
boundaries. Behavioral simulation
uses the Common byte-mask RAM model. IHP130 maps to
`RM_IHPSG13_1P_1024x32_c2_bm_bist`. GF180 composes four byte lanes across two
`gf180mcu_fd_ip_sram__sram512x8m8wm1` depth halves and registers the address
@@ -120,9 +125,9 @@ exports 32 data bits and hides OpenRAM's required physical spare column; a
future physical adapter must tie the spare enable and spare input low. The
required spare row expands the macro address port to 11 bits, so the wrapper
ties its high bit low and exposes exactly logical addresses 0 through 1023.
ICS55 retains its existing mapping but its supported profiles keep the macro
disabled. Bank count, rather than technology depth or width, implements every
product size.
ICS55 pairs two 4096x32 macros for the 32 KiB AXI64 product target, but its
committed profiles keep the macro disabled. Bank count, rather than technology
depth or width, implements every product size.
The wrapper preserves byte masks and one-cycle synchronous reads. PDK timing,
LEF/GDS placement, power hookup, BIST pins, and foundry signoff remain
@@ -130,13 +135,15 @@ physical-flow responsibilities.
## Verification and performance evidence
`tests/test_onchip_ram.py` elaborates all five capacities and covers bank
`tests/test_onchip_ram.py` elaborates all five 32-bit compatibility capacities and covers bank
boundaries, first/last words, byte/half/word masks, `FIXED`, `INCR`, `WRAP`,
16-beat continuous traffic, backpressure, AR/AW priority, absent-memory and
malformed accesses, counters, all four PDK wrapper polarities and masks, and
the GF180 511/512 depth boundary. The setup tests independently validate the
SKY130 artifact manifest and generated-file hashes. Full Verilator regression
firmware exercises the first and last locations through the SoC interconnect.
The native AXI64 test additionally covers six-bit IDs, continuous 64-bit bursts,
and upper/lower 32-bit byte strobes.
`make CONFIG=configs/ci/ihp130.mk formal-onchip-ram` runs a 20-cycle bounded
proof and cover set for AXI stability, channel exclusion, response bounds,
+79
View File
@@ -0,0 +1,79 @@
# Mini Resource Controller
## Scope
The Resource Controller at `0x2000_A000` is the root-management ownership and
interrupt-routing authority for central DMA, USB2, SDIO0, SDIO1, SPI-SD, and
EXT-H. Hazard3 has read/write access; HP MMIO may inspect status but writes are
rejected by the root-control firewall.
It is handwritten RTL and has a matching handwritten
`<retrosoc/hal/resource.h>` API; no register generator is used.
Resource indices are fixed:
| Index | Resource | HP PLIC source |
| ---: | --- | ---: |
| 0 | central DMA | 4 |
| 1 | USB2 | 5 |
| 2 | SDIO0 | 6 |
| 3 | SDIO1 | 7 |
| 4 | SPI-SD | 8 |
| 5 | EXT-H | 3 |
Owner `0` routes the resource interrupt to the existing LP vector. Owner `1`
removes it from LP and routes it to the listed HP PLIC source. Reset masks both
routes. Hardware never delivers one resource interrupt to both owners.
## Register ABI
| Offset | Name | Access | Contract |
| ---: | --- | --- | --- |
| `0x000` | `IP_ID` | RO | `0x52534354` (`RSCT`) |
| `0x004` | `IP_VERSION` | RO | `0x00010000` |
| `0x008` | `CAPABILITY` | RO | resource count and ABI capability |
| `0x00C` | `GLOBAL_STATUS` | RO | cache request/clean and resource fault summary |
| `0x010` | `CACHE_CONTROL` | RW | bit 0 clean ACK, bit 1 live request |
Each resource has a `0x20` stride starting at `0x100`:
| Relative offset | Name | Access | Contract |
| ---: | --- | --- | --- |
| `0x00` | `OWNER` | RW | bits 1:0 owner, bit 8 sticky owner lock |
| `0x04` | `CONTROL` | RW | bit 0 quiesce request, bit 1 reset request |
| `0x08` | `STATUS` | RO | owner, lifecycle request, idle, fault, raw IRQ, and HP block ACK |
| `0x0C` | `FAULT` | RW1C | rejected handoff fault |
| `0x10` | `HANDOFF_COUNT` | RO | saturating successful-owner-change count |
An owner change is accepted only after software requests quiesce and the
resource reports both HP block ACK and idle. The HAL performs block-new,
round-trip CDC acknowledgement, bounded drain, owner write,
and release in that order. An illegal owner, busy handoff, or write after owner lock returns APB `PSLVERR`,
leaves the owner unchanged, and raises the resource-fault interrupt on LP IRQ
29. The implementation currently uses the conservative whole-data-plane idle
condition for DMA and I/O resources; this is safe but can delay an otherwise
independent handoff.
## Cache Maintenance
VexiiRiscv implements `Zicbom` with a 64-byte CBO block. On HP shutdown the AON
lifecycle controller first asserts the Resource Controller cache request while
HP remains released and its MMIO path remains open. Software cleans and
invalidates shared ranges with CBO operations, reports completion to LP, and LP
writes `CACHE_CONTROL.CLEAN`. AON then blocks new addresses and drains the data
plane. Missing acknowledgement is bounded by the lifecycle timeout and records
a forced fault before reset proceeds.
This handshake provides an execution window and explicit evidence point; it
does not create hardware coherency. Buffer ownership, fences, CBO range policy,
and a Linux platform driver remain software responsibilities.
## Delivery Boundary
Central owner/lock, quiesce-gated handoff, cache request/ACK, fault IRQ, and
LP/HP IRQ routing are implemented and directed-tested. `CONTROL.QUIESCE`
blocks the corresponding data-crossbar master and waits per-master outstanding
zero; shared I/O gateways are conservatively blocked as a pair. `CONTROL.RESET`
also blocks new data and masks IRQ but is not yet connected to every peripheral
engine's internal reset state machine. It must not be
described as independent peripheral power isolation or reset containment until
those downstream acknowledgements and fault-injection tests exist.
+6 -3
View File
@@ -12,13 +12,16 @@ timings expressed in SDRAM-clock cycles.
| AXI4 data window | `0x38000000` - `0x3BFFFFFF` |
| Data window size | 64 MiB |
| APB4 configuration window | `0x1000D000` - `0x1000DFFF` |
| Data width | 32-bit AXI over SDRAM x16 |
| SoC data boundary | 64-bit AXI/ID6 from HP into the stable memory domain |
| Controller frontend | local 64-to-32 adaptation, then 32-bit AXI over SDRAM x16 |
| Address organization | 2-bit bank, 13-bit row, 10-bit column |
| Physical interface | `sdram_if` (`BA[1:0]`, `A[12:0]`, `DQ[15:0]`) |
| Clocking | One edge updates control/data; the opposite edge samples |
Configuration requests enter through the timed `sdram_cfg` APB4 port exported
by `apb4_periph`. AXI data requests enter `axi4` and are queued by `sdram_axi4`
Configuration requests cross PCLK into the stable memory domain through the
timed `sdram_cfg` APB4 port exported by `apb4_periph`. AXI64 data requests cross
directly from HP to memory and are adapted locally; AXI32 requests then enter
`axi4` and are queued by `sdram_axi4`
into native multi-beat commands for `sdram_core`. Register accesses stay on the
APB4 window and do not share the data-path arbiter. The physical SDRAM instance
remains in the SoC top.
+28 -14
View File
@@ -8,7 +8,7 @@ Every committed `MINI_MODE=PRODUCT` profile instantiates two fixed harts:
| --- | --- | --- |
| Core | Hazard3 | generated VexiiRiscv |
| Hart ID | 0 | 1 |
| ISA | profile RV32I/RV32IM | RV32IMAFDC, S/U mode, Sv32 |
| ISA | profile RV32I/RV32IM | RV32IMAFDC + Zicbom, S/U mode, Sv32 |
| Reset clock | REF24 at 24 MHz | external 72 MHz safe clock |
| Role | boot, control, diagnostics, recovery | high-throughput application/Linux |
| JTAG | reset owner | selectable while HP is held in reset |
@@ -73,12 +73,11 @@ same memory admission, inactive-pad, ACL, and fault path as HP and DMA.
## Native AXI64 data plane
`soc_data_plane` contains an 8-master, 6-target AXI64 crossbar. Read and write
ownership are independent, so one target may read and write concurrently and
different targets progress concurrently. Different source IDs from a
high-bandwidth master may be active against different targets. The same source
ID is blocked until completion, and each target currently remains
single-outstanding per direction. Global IDs contain a fixed master prefix
plus the source ID.
channels progress independently, and different source IDs may be active against
the same or different targets. The same source ID is blocked until completion.
SRAM and SDRAM accept four reads and two writes; serial memories and the error
target accept one per direction. Responses route by the global-ID master prefix,
and a Common FIFO preserves write-data order where AXI4 W has no ID.
| Master | Entry path |
| --- | --- |
@@ -91,11 +90,18 @@ plus the source ID.
| EXT-H | PCLK-to-HP AXI64 async bridge, ID prefix 7 |
Targets are SRAM, SDRAM, QPI PSRAM, OPI/HyperBus PSRAM, XPI/flash, and a
finite-latency error slave. Current 32-bit memory frontends use AXI64-to-32
downsizers. QPI, OPI, and XPI controllers run from the stable 36 MHz memory
root; their APB windows use PCLK-to-memory proxies. SRAM and SDRAM still use
LP-domain frontends and remain explicit native-64-bit follow-up work. Inactive
QPI/OPI windows route to `SLVERR`.
finite-latency error slave. SRAM is a native AXI64, six-bit-ID target in HP and
stripes each beat across two existing 32-bit technology macros. SDRAM crosses
HP to the stable memory domain as AXI64 and is downsized only beside the current
32-bit SDRAM frontend. QPI, OPI, and XPI retain transitional 32-bit LP staging
before their stable memory-domain engines. Inactive QPI/OPI windows return
`SLVERR`.
Every memory target has a queued guard. SRAM/SDRAM queue depth matches their
credits. Target stalls are bounded by target-specific timeouts; a pre-accept
stall receives synthetic `SLVERR`, while an accepted timeout flushes the CDC
boundary, completes the original burst with `SLVERR`, and permanently isolates
that target until hard reset so a late response cannot contaminate new traffic.
All async AXI channels use Common coordinated warm-flush FIFOs and expose a
source-domain epoch. HP shutdown holds a flush request until every bridge sees
@@ -131,6 +137,12 @@ EXT-H DMA performs aligned 64-bit memory copies with a partial final strobe.
Its IRQ is delivered to LP IRQ 28 or HP PLIC source 3 according to owner,
never both. Software discovers it through `<retrosoc/hal/extension.h>`.
The Resource Controller at `0x2000_A000` is the central owner and IRQ authority
for DMA, USB2, SDIO0/1, SPI-SD, and EXT-H. Handoff requires idle, owner lock is
sticky, and rejected handoffs raise LP IRQ 29. It also carries the AON cache
request/clean acknowledgement used before HP drain. See
[`ip/resource-controller.md`](ip/resource-controller.md).
The old `APB4_USER_IP` window is a read-only compatibility/capability window.
Product writes to `CORESEL`, `IPSEL`, `USER_CORE_RESET`, or
`USER_CORE_STATUS` return APB `PSLVERR`; legacy HAL mutators return
@@ -149,8 +161,10 @@ so absolute commercial paths are never tracked.
Behavioral RTL, firmware, directed data-plane/lifecycle/clock tests, manifest
parity, and quality checks are the evidence for this implementation. HP stop
implements drain, coordinated flush, actual-release status, and bounded forced
reset, but a forced reset cannot preserve dirty cache data. It must not be
implements a pre-drain cache request/ACK window, drain, coordinated flush,
actual-release status, and bounded forced reset. Vexii implements 64-byte
`Zicbom`; software still owns shared-range selection, and a forced reset cannot
preserve dirty cache data when the ACK times out. It must not be
called cache coherent, power isolated, timing closed, CDC/RDC signed off, or
silicon-qualified. Synthesis, netlist simulation, STA, MMMC, clock-tree, DFT,
and analogue PLL qualification are separate gates.
+29 -15
View File
@@ -13,16 +13,17 @@
| Requirement | Implementation owner | Required evidence | Status |
| --- | --- | --- | --- |
| Independent HP I-cache and D-cache paths | HP subsystem and data plane | Concurrent different-target AXI test | implemented |
| Target-aware multi-outstanding AXI64 | Data crossbar | Multi-ID, ordering, backpressure, and formal tests | partial: concurrent across targets; one per target |
| Target-aware multi-outstanding AXI64 | Data crossbar | Multi-ID, ordering, backpressure, and formal tests | implemented: SRAM/SDRAM use 4R/2W credits; serial targets use 1R/1W; full formal proof pending |
| LP access to every memory window through the data plane | LP data gateway | no-PLL boot and memory-window smoke | implemented |
| Bounded QoS and recovery priority | Data crossbar | starvation and emergency-promotion test | partial: priority and aging implemented; full bound proof pending |
| Initiator ACL and first-fault attribution | Data firewall and SYSCTRL fault manager | legal/illegal access and injection tests | implemented |
| Stable memory functional clocks | Memory integration | HP DFS while refresh/protocol engines continue | partial: QPI/OPI/XPI migrated; SRAM/SDRAM remain LP |
| Stable memory functional clocks | Memory integration | HP DFS while refresh/protocol engines continue | implemented digitally: SRAM is native HP AXI64; SDRAM AXI64 crosses to stable memory; serial targets retain transitional LP staging |
| Reset epoch and stale-response rejection | Async bridges | unilateral reset and clock-stop matrix | partial: coordinated warm flush and epoch implemented |
| HP normal and forced hot reset | AON lifecycle controller | drain, timeout, flush, and recovery tests | partial: cache-clean ACK remains software policy |
| HP normal and forced hot reset | AON lifecycle controller and Resource Controller | cache request/ACK, drain, timeout, flush, and recovery tests | implemented digitally: Zicbom and bounded ACK window exist; range policy and Linux service remain software work |
| Clock monitor and programmable timeout | AON clock/reset subsystem | 8x8 DFS and clock-loss tests | implemented |
| Operational EXT-H ACL, timeout, and data path | Extension subsystem | transfer, denial, hang, and quiesce tests | implemented |
| Ownership-aware IRQ handoff | Lifecycle and IRQ router | pending edge/level migration tests | partial: EXT-H LP/HP route implemented |
| Ownership-aware IRQ handoff | Resource Controller and HP PLIC | idle handoff, owner lock, LP/HP exclusion, and fault tests | implemented for DMA, USB2, SDIO0/1, SPI-SD, and EXT-H; downstream per-engine reset ACK remains pending |
| Bounded target timeout and isolation | Target guards | pre-/post-accept timeout, burst completion, and late-response isolation | implemented: synthetic SLVERR and fail-closed isolation; physical fault injection pending |
| QPI/OPI pad exclusion and inactive-window error | Memory pad manager | simulation and formal mutual-exclusion proof | implemented |
| Technology clock cells and physical closure | Technology/physical flow | synthesis, MMMC, SDF, CDC/RDC, and PVT | physical-pending |
@@ -32,17 +33,30 @@ sources of truth.
## Current verification evidence
- IHP130 `ci_smoke`, Verilator with SVA, the 32 KiB all-SRAM layout, and the
explicit fast-flash backend: `SIM_TEST_PASS code=0` at 5,770,694 cycles.
- IHP130 no-PLL Icarus assembly regression with the serial flash, QPI PSRAM,
and SDRAM models: `SIM_TEST_PASS` at 4.014 ms.
- Committed ICS55 compatibility profile with PLL/SRAM disabled: Icarus
`SIM_TEST_PASS` at 4.014 ms.
- Git-ignored local ICS55 profile with PLL, SRAM interface, SRAM macro, and
32 KiB enabled: Icarus `SIM_TEST_PASS` at 4.014 ms.
- Directed AXI64 crossbar, async warm-flush bridge, clock-monitor, HP lifecycle,
and EXT-H DMA tests pass. Repository RTL style/readiness, clock/reset-domain,
embedded-C policy/host, Ruff, and Pytest gates pass.
- The complete Python suite passes: 306 tests, including directed AXI64
crossbar, same-target IDs, target isolation, native AXI64 SRAM, async
warm-flush, clock monitor, HP lifecycle, Resource Controller, EXT-H DMA,
topology, clock/reset inventory, and physical macro mapping.
- Generated PRODUCT Vexii manifests select `rv32imafdc_zicbom_max`. IHP130
PRODUCT Icarus assembly passes at 3.998665187 ms; the matching Verilator
assembly run passes in 221,756 cycles. Its PR `ci_smoke` Verilator run with
SVA, fast flash, and the 32 KiB all-SRAM layout passes every peripheral test
and reports `SIM_TEST_PASS code=0` after 5,679,566 cycles.
- The committed ICS55 no-PLL/no-SRAM PRODUCT profile passes Icarus assembly at
3.998665187 ms. Both ignored local ICS55 variants, with 32 KiB SRAM and PLL
respectively off and on, pass the same PRODUCT Icarus test at 3.998665187 ms.
The local commercial SRAM and PLL sources remain outside Git.
- The committed ICS55 PRODUCT `ci_smoke` Verilator run with SVA, fast flash,
and the SDRAM execution layout passes every peripheral check and reports
`SIM_TEST_PASS code=0` after 6,984,536 cycles. GF180, ICS55, and SKY130 use
this bounded 600-second path in PR regression; serial PSRAM boot remains in
their Icarus assembly coverage.
- The Yosys/Slang frontend elaborates the corrected downsizer and HP AXI mux
with zero errors or warnings. The generated IRQ SVA now binds to the LP clock
and reset, and the top-level unused-domain vector has exact width.
- Repository format, full RTL style audit/readiness, clock/reset-domain,
embedded-C policy/host, Ruff, and Pytest gates pass. Focused post-CI-fix
Python/RTL coverage passes 91 tests.
- Product-mode SYSCTRL and the current 11-state PLL/RCU controller pass their
bounded proof and cover targets. The PLL/RCU proof covers successful switch,
unsupported-PLL failure, and timeout failure paths.
@@ -49,9 +49,10 @@ SRAM_INSTANCES = {
def onchip_sram_instances(capacity_kib: int) -> dict[str, tuple[list[int], str]]:
if capacity_kib <= 0 or capacity_kib % 4 != 0:
raise ValueError("SRAM_SIZE_KIB must be a positive multiple of 4")
count = capacity_kib // 4
count = 2 * ((capacity_kib + 7) // 8)
return {
f"u_retrosoc.u_onchip_ram.gen_memory.gen_bank[{index}].u_ram.u_mem": (
"u_retrosoc.u_onchip_ram.gen_memory64."
f"gen_bank[{index // 2}].u_ram_{'lo' if index % 2 == 0 else 'hi'}.u_mem": (
[2000 + (index % 4) * 500, 500 + (index // 4) * 750],
"N",
)
+9
View File
@@ -570,6 +570,15 @@
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_RESOURCE_CTRL",
"base": "0x2000A000",
"size": "0x00001000",
"route": "apb4_system",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "SRAM",
"base": "0x30000000",
+2
View File
@@ -14,6 +14,7 @@
/top/axi4_id_prefix.sv
/top/axi4_interconnect.sv
/top/axi4_data_crossbar.sv
/top/axi4_target_guard.sv
/top/soc_data_plane.sv
/top/axi4_error_slave.sv
/top/rib2axi4.sv
@@ -32,6 +33,7 @@
/top/retrosoc_core.sv
/top/onchip_ram_reg.sv
/top/onchip_ram.sv
/top/resource_controller.sv
/top/apb4_periph.sv
/top/apb4_system.sv
/top/rib_bus.sv
+12 -5
View File
@@ -108,11 +108,18 @@
{"name": "qpi_target", "source": "lp", "destination": "pclk", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_psram_target_cdc"},
{"name": "xpi_target", "source": "lp", "destination": "pclk", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_xpi_target_cdc"},
{"name": "opi_target", "source": "lp", "destination": "pclk", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_opi_target_cdc"},
{"name": "sdram_config", "source": "pclk", "destination": "lp", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_sdram_cfg_cdc"},
{"name": "sram_config", "source": "pclk", "destination": "lp", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_sram_cfg_cdc"},
{"name": "sdram_config", "source": "pclk", "destination": "memory", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_sdram_cfg_cdc"},
{"name": "sram_config", "source": "pclk", "destination": "hp", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_sram_cfg_cdc"},
{"name": "io_dma_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_io_cdc"},
{"name": "extension_h_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ext_h_cdc"},
{"name": "memory_data", "source": "hp", "destination": "lp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"},
{"name": "sdram_data", "source": "hp", "destination": "memory", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"},
{"name": "serial_memory_data", "source": "hp", "destination": "lp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"},
{"name": "sdram_abort", "source": "hp", "destination": "memory", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_abort_sync"},
{"name": "hp_cache_request", "source": "aon", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_hp_cache_req_sync"},
{"name": "hp_cache_clean", "source": "pclk", "destination": "aon", "primitive": "cdc_sync", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_hp_cache_clean_sync"},
{"name": "resource_block", "source": "pclk", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_resource_block_sync"},
{"name": "resource_idle", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_resource_idle_sync"},
{"name": "resource_block_ack", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_resource_block_ack_sync"},
{"name": "memory_pad_mode_hp", "source": "aon", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_mem_pad_mode_sync"},
{"name": "memory_pad_mode_lp", "source": "aon", "destination": "lp", "primitive": "cdc_sync", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_mem_pad_mode_sync"},
{"name": "dvp_enable", "source": "pclk", "destination": "dvp", "primitive": "cdc_2phase", "path": "rtl/ip/multimedia/axi4s_dvp.sv", "instance": "u_dvp_config_cdc"},
@@ -159,7 +166,7 @@
"same_domain_contracts": [
{
"name": "lp_pclk_generated_clock",
"source_domain": "lp",
"source_domain": "hp",
"destination_domain": "pclk",
"clock": "clk_pclk_i",
"reset": "rst_pclk_n_i",
@@ -173,7 +180,7 @@
"clock": "clk_mem_i",
"reset": "rst_mem_n_i",
"relationship": "declared_transition_boundary",
"description": "The stable 36 MHz memory root is exported; controller engine migration remains explicit in RTL and documentation."
"description": "SDRAM AXI64 commands cross from HP into the stable 36 MHz memory domain; refresh and protocol timing do not follow HP DFS."
}
]
}
@@ -27,7 +27,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("timer0", "apb4_periph", 3, 3, "s_tim0_irq"),
("timer1", "apb4_periph", 4, 4, "s_tim1_irq"),
("psram", "apb4_periph", 5, 5, "psram.irq_o"),
("spisd", "apb4_periph", 6, 6, "spisd.irq_o"),
("spisd", "apb4_periph", 6, 6, "resource_irq_lp_i[4]"),
("i2c0", "apb4_periph", 7, 7, "i2c0.irq_o"),
("i2s", "apb4_periph", 8, 8, "i2s.irq_o"),
("xpi", "apb4_periph", 9, 9, "s_xpi_irq"),
@@ -36,6 +36,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("rtc", "apb4_system", 2, 13, "u_rtc_if.irq_o"),
("watchdog_early_warning", "apb4_system", 3, 14, "u_wdg_if.irq_o"),
("rng", "apb4_system", 4, 16, "s_rng_irq"),
("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"),
("ws2812", "apb4_periph", 10, 17, "ws2812.irq_o"),
("gpio", "apb4_periph", 11, 18, "gpio.irq_o"),
("i2c1", "apb4_periph", 12, 19, "i2c1.irq_o"),
@@ -767,8 +768,8 @@ def render_irq_sva(vector_width: int, groups: list[IrqGroup], interrupts: list[I
)
lines.extend(
[
" .clk_i(clk_i),",
" .rst_n_i(rst_n_i),",
" .clk_i(clk_lp_i),",
" .rst_n_i(rst_lp_n_i),",
" .irq_i(s_irq),",
]
)
+22 -6
View File
@@ -10,7 +10,7 @@
},
{
"name": "apb4_system",
"width": 7,
"width": 8,
"top_signal": "s_apb4_system_irq"
}
],
@@ -71,7 +71,7 @@
"group": "apb4_periph",
"group_bit": 6,
"core_bit": 6,
"signal": "spisd.irq_o"
"signal": "resource_irq_lp_i[4]"
},
{
"name": "i2c0",
@@ -103,7 +103,7 @@
"group": "apb4_periph",
"group_bit": 16,
"core_bit": 10,
"signal": "sdio0.irq_o",
"signal": "resource_irq_lp_i[2]",
"user_visible": false
},
{
@@ -185,7 +185,7 @@
"group": "apb4_periph",
"group_bit": 14,
"core_bit": 20,
"signal": "s_dma_irq",
"signal": "resource_irq_lp_i[0]",
"user_visible": false
},
{
@@ -202,7 +202,7 @@
"group": "apb4_periph",
"group_bit": 17,
"core_bit": 21,
"signal": "sdio1.irq_o",
"signal": "resource_irq_lp_i[3]",
"user_visible": false
},
{
@@ -220,7 +220,7 @@
"group": "apb4_periph",
"group_bit": 19,
"core_bit": 24,
"signal": "s_usb2_irq",
"signal": "resource_irq_lp_i[1]",
"user_visible": false
},
{
@@ -258,6 +258,15 @@
"core_bit": 28,
"signal": "s_ext_h_irq",
"user_visible": false
},
{
"name": "resource_fault",
"description": "Resource ownership and handoff fault interrupt",
"group": "apb4_system",
"group_bit": 7,
"core_bit": 29,
"signal": "s_resource_fault_irq",
"user_visible": false
}
],
"wake_sources": [
@@ -581,6 +590,13 @@
"timed_interface": "u_ext_h_apb4_if",
"pure_interface": "u_ext_h_apb4_pure_if",
"region": "APB4_EXT_H"
},
{
"slot": 10,
"name": "resource_ctrl",
"timed_interface": "u_resource_ctrl_apb4_if",
"pure_interface": "u_resource_ctrl_apb4_pure_if",
"region": "APB4_RESOURCE_CTRL"
}
],
"gpio_alt_functions": [
+1
View File
@@ -119,6 +119,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/spisd_math.c \
$(ROOT_PATH)/crt/src/hal/spisd.c \
$(ROOT_PATH)/crt/src/hal/xpi.c \
$(ROOT_PATH)/crt/src/hal/resource.c \
$(ROOT_PATH)/crt/src/hal/extension.c \
$(ROOT_PATH)/crt/src/hal/user_ip.c \
$(ROOT_PATH)/crt/src/hal/user_core.c
+24 -15
View File
@@ -23,6 +23,8 @@ module apb4_periph (
input logic debug_halted_i,
input logic timebase_tick_i,
input logic ext_h_hp_irq_i,
input logic [4:0] resource_irq_lp_i,
input logic [4:0] resource_irq_hp_i,
axi4_if.slave cfg_axi4,
axi4_if.slave psram_axi4,
axi4_if.slave xpi_axi4,
@@ -62,6 +64,7 @@ module apb4_periph (
output logic hp_software_irq_o,
output logic hp_machine_external_irq_o,
output logic hp_supervisor_external_irq_o,
output logic [4:0] resource_irq_raw_o,
output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o
// verilog_format: on
);
@@ -184,23 +187,24 @@ axi4_stream_if #(
`include "apb4_periph_connections.svh"
);
assign u_dma_req_if.i2s_tx_proc = ~s_dma_i2s_tx_stall;
assign u_dma_req_if.i2s_rx_proc = ~s_dma_i2s_rx_stall;
assign u_dma_req_if.qspi_tx_proc = ~s_dma_xpi_tx_stall;
assign u_dma_req_if.qspi_rx_proc = ~s_dma_xpi_rx_stall;
assign u_dma_req_if.uart_tx_proc = ~s_dma_uart_tx_stall;
assign u_dma_req_if.uart_rx_proc = ~s_dma_uart_rx_stall;
assign u_dma_req_if.i2c0_tx_proc = ~s_dma_i2c0_tx_stall;
assign u_dma_req_if.i2c0_rx_proc = ~s_dma_i2c0_rx_stall;
assign u_dma_req_if.i2c1_tx_proc = ~s_dma_i2c1_tx_stall;
assign u_dma_req_if.i2c1_rx_proc = ~s_dma_i2c1_rx_stall;
assign u_dma_req_if.crypto_in_proc = s_dma_crypto_in_proc;
assign u_dma_req_if.i2s_tx_proc = ~s_dma_i2s_tx_stall;
assign u_dma_req_if.i2s_rx_proc = ~s_dma_i2s_rx_stall;
assign u_dma_req_if.qspi_tx_proc = ~s_dma_xpi_tx_stall;
assign u_dma_req_if.qspi_rx_proc = ~s_dma_xpi_rx_stall;
assign u_dma_req_if.uart_tx_proc = ~s_dma_uart_tx_stall;
assign u_dma_req_if.uart_rx_proc = ~s_dma_uart_rx_stall;
assign u_dma_req_if.i2c0_tx_proc = ~s_dma_i2c0_tx_stall;
assign u_dma_req_if.i2c0_rx_proc = ~s_dma_i2c0_rx_stall;
assign u_dma_req_if.i2c1_tx_proc = ~s_dma_i2c1_tx_stall;
assign u_dma_req_if.i2c1_rx_proc = ~s_dma_i2c1_rx_stall;
assign u_dma_req_if.crypto_in_proc = s_dma_crypto_in_proc;
assign u_dma_req_if.crypto_out_proc = s_dma_crypto_out_proc;
assign hp_time_o = u_hp_clint_if.mtime_o;
assign hp_timer_irq_o = u_hp_clint_if.timer_irq_o[1];
assign hp_software_irq_o = u_hp_clint_if.software_irq_o[1];
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_time_o = u_hp_clint_if.mtime_o;
assign hp_timer_irq_o = u_hp_clint_if.timer_irq_o[1];
assign hp_software_irq_o = u_hp_clint_if.software_irq_o[1];
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1];
assign resource_irq_raw_o = {spisd.irq_o, sdio1.irq_o, sdio0.irq_o, s_usb2_irq, s_dma_irq};
apb4_async_bridge u_psram_cfg_mem_cdc (
.src_clk_i (clk_i),
@@ -274,6 +278,11 @@ axi4_stream_if #(
s_hp_plic_source[1] = uart1.irq_o;
s_hp_plic_source[2] = s_mailbox_hp_irq;
s_hp_plic_source[3] = ext_h_hp_irq_i;
s_hp_plic_source[4] = resource_irq_hp_i[0];
s_hp_plic_source[5] = resource_irq_hp_i[1];
s_hp_plic_source[6] = resource_irq_hp_i[2];
s_hp_plic_source[7] = resource_irq_hp_i[3];
s_hp_plic_source[8] = resource_irq_hp_i[4];
end
`include "apb4_periph_irq_bindings.svh"
+60 -15
View File
@@ -23,6 +23,10 @@ module apb4_system (
input logic rst_aud_n_i,
input logic debug_halted_i,
input logic ext_h_data_idle_i,
input logic [5:0] resource_idle_i,
input logic [5:0] resource_block_ack_i,
input logic [4:0] resource_irq_i,
input logic cache_request_i,
axi4_if.slave axi4,
axi4_if.master ext_h_axi4,
pwm_if.dut pwm,
@@ -39,6 +43,12 @@ module apb4_system (
output logic [31:0] ext_h_timeout_o,
output logic ext_h_irq_raw_o,
output logic [1:0] ext_h_owner_o,
output logic cache_clean_o,
output logic [5:0][1:0] resource_owner_o,
output logic [5:0] resource_quiesce_o,
output logic [5:0] resource_reset_o,
output logic [4:0] resource_irq_lp_o,
output logic [4:0] resource_irq_hp_o,
output logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o
// verilog_format: on
);
@@ -75,24 +85,40 @@ module apb4_system (
{31'd0, ARCHINFO_HAVE_PLL} |
({31'd0, ARCHINFO_HAVE_SRAM_IF} << 1) | ({31'd0, ARCHINFO_HAVE_SRAM_MACRO} << 2);
logic s_rng_entropy_en;
logic s_rng_entropy_ready;
logic s_rng_entropy_valid;
logic [31:0] s_rng_entropy_data;
logic s_rng_entropy_qualified;
logic s_rng_entropy_fault;
logic s_rng_irq;
logic s_ext_l_irq;
logic s_ext_h_irq;
logic s_ext_h_irq_raw;
logic [ 1:0] s_ext_h_owner;
logic s_rng_entropy_en;
logic s_rng_entropy_ready;
logic s_rng_entropy_valid;
logic [31:0] s_rng_entropy_data;
logic s_rng_entropy_qualified;
logic s_rng_entropy_fault;
logic s_rng_irq;
logic s_ext_l_irq;
logic s_ext_h_irq;
logic s_ext_h_irq_raw;
logic [ 1:0] unused_ext_h_owner;
logic [ 5:0] s_resource_irq;
logic [ 5:0] s_resource_irq_lp;
logic [ 5:0] s_resource_irq_hp;
logic [ 5:0][1:0] s_resource_owner;
logic [ 5:0] s_resource_quiesce;
logic [ 5:0] s_resource_reset;
logic s_resource_fault_irq;
logic s_cache_clean;
logic s_unused_resource;
`ifdef MINI_PRODUCT
logic s_unused_product_input;
`endif
assign s_ext_h_irq = (s_ext_h_owner == 2'd0) ? s_ext_h_irq_raw : 1'b0;
assign ext_h_irq_raw_o = s_ext_h_irq_raw;
assign ext_h_owner_o = s_ext_h_owner;
assign s_resource_irq = {s_ext_h_irq_raw, resource_irq_i};
assign s_ext_h_irq = s_resource_irq_lp[5];
assign ext_h_irq_raw_o = s_ext_h_irq_raw;
assign ext_h_owner_o = s_resource_owner[5];
assign cache_clean_o = s_cache_clean;
assign resource_owner_o = s_resource_owner;
assign resource_quiesce_o = s_resource_quiesce;
assign resource_reset_o = s_resource_reset;
assign resource_irq_lp_o = s_resource_irq_lp[4:0];
assign resource_irq_hp_o = s_resource_irq_hp[4:0];
`ifdef PDK_IHP130
localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082;
@@ -203,6 +229,23 @@ module apb4_system (
apb4_crc u_apb4_crc (.apb4(u_crc_apb4_if));
resource_controller u_resource_controller (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.idle_i (resource_idle_i),
.block_ack_i (resource_block_ack_i),
.irq_i (s_resource_irq),
.cache_request_i(cache_request_i),
.cache_clean_o (s_cache_clean),
.owner_o (s_resource_owner),
.quiesce_o (s_resource_quiesce),
.reset_o (s_resource_reset),
.irq_lp_o (s_resource_irq_lp),
.irq_hp_o (s_resource_irq_hp),
.fault_irq_o (s_resource_fault_irq),
.apb4 (u_resource_ctrl_apb4_if)
);
// Generated IRQ ownership and core-vector bit assignments are topology checked.
`include "apb4_system_irq_bindings.svh"
@@ -243,7 +286,7 @@ axi42apb4_system u_axi42apb4_system (
.ext_l_irq_o (s_ext_l_irq),
.ext_h_irq_o (s_ext_h_irq_raw),
.ext_h_block_o (ext_h_block_o),
.ext_h_owner_o (s_ext_h_owner),
.ext_h_owner_o (unused_ext_h_owner),
.ext_h_read_base_o (ext_h_read_base_o),
.ext_h_read_limit_o (ext_h_read_limit_o),
.ext_h_write_base_o (ext_h_write_base_o),
@@ -251,4 +294,6 @@ axi42apb4_system u_axi42apb4_system (
.ext_h_timeout_o (ext_h_timeout_o)
);
assign s_unused_resource = ^unused_ext_h_owner;
endmodule
+118 -79
View File
@@ -12,6 +12,7 @@ module axi4_data_crossbar #(
input logic clk_i,
input logic rst_n_i,
input logic block_new_i,
input logic [NumMasters-1:0] master_block_i,
input logic recovery_i,
input logic [1:0] mem_pad_mode_i,
input logic [31:0] ext_h_read_base_i,
@@ -21,6 +22,7 @@ module axi4_data_crossbar #(
axi4_if.slave masters [NumMasters],
axi4_if.master targets [NumTargets],
output logic idle_o,
output logic [NumMasters-1:0] master_idle_o,
output logic [7:0] outstanding_read_o,
output logic [7:0] outstanding_write_o,
output logic fault_valid_o,
@@ -85,11 +87,11 @@ module axi4_data_crossbar #(
logic user;
} read_resp_t;
addr_channel_t m_aw [NumMasters];
addr_channel_t m_ar [NumMasters];
write_channel_t m_w [NumMasters];
write_resp_t m_b [NumMasters];
read_resp_t m_r [NumMasters];
addr_channel_t m_aw [NumMasters];
addr_channel_t m_ar [NumMasters];
write_channel_t m_w [NumMasters];
write_resp_t m_b [NumMasters];
read_resp_t m_r [NumMasters];
logic [NumMasters-1:0] m_awvalid;
logic [NumMasters-1:0] m_awready;
logic [NumMasters-1:0] m_wvalid;
@@ -101,11 +103,11 @@ module axi4_data_crossbar #(
logic [NumMasters-1:0] m_rvalid;
logic [NumMasters-1:0] m_rready;
addr_channel_t t_aw [NumTargets];
addr_channel_t t_ar [NumTargets];
write_channel_t t_w [NumTargets];
write_resp_t t_b [NumTargets];
read_resp_t t_r [NumTargets];
addr_channel_t t_aw [NumTargets];
addr_channel_t t_ar [NumTargets];
write_channel_t t_w [NumTargets];
write_resp_t t_b [NumTargets];
read_resp_t t_r [NumTargets];
logic [NumTargets-1:0] t_awvalid;
logic [NumTargets-1:0] t_awready;
logic [NumTargets-1:0] t_wvalid;
@@ -141,20 +143,24 @@ module axi4_data_crossbar #(
logic [NumMasters-1:0] s_read_terminal;
logic [NumMasters-1:0] s_write_terminal;
logic [NumTargets-1:0] s_read_owner_valid_d, s_read_owner_valid_q;
logic [NumTargets-1:0] s_write_owner_valid_d, s_write_owner_valid_q;
logic [NumTargets-1:0][MasterWidth-1:0] s_read_owner_d, s_read_owner_q;
logic [NumTargets-1:0][MasterWidth-1:0] s_write_owner_d, s_write_owner_q;
logic [NumTargets-1:0] s_target_write_owner_full;
logic [NumTargets-1:0] s_target_write_owner_empty;
logic [NumTargets-1:0] s_target_write_owner_push;
logic [NumTargets-1:0] s_target_write_owner_pop;
logic [NumTargets-1:0][MasterWidth-1:0] s_target_write_owner_data;
logic [NumTargets-1:0][ 1:0] unused_target_write_owner_count;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_decoded_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_decoded_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_routed_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_routed_target;
logic [NumMasters-1:0][ 3:0] s_read_fault_reason;
logic [NumMasters-1:0][ 3:0] s_write_fault_reason;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_decoded_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_decoded_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_routed_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_routed_target;
logic [NumMasters-1:0][ 3:0] s_read_fault_reason;
logic [NumMasters-1:0][ 3:0] s_write_fault_reason;
logic [NumMasters-1:0][CountWidth-1:0] s_master_read_count_d, s_master_read_count_q;
logic [NumMasters-1:0][CountWidth-1:0] s_master_write_count_d, s_master_write_count_q;
logic [NumTargets-1:0][CountWidth-1:0] s_target_read_count_d, s_target_read_count_q;
logic [NumTargets-1:0][CountWidth-1:0] s_target_write_count_d, s_target_write_count_q;
logic [NumMasters-1:0][SourceIds-1:0] s_read_id_busy_d, s_read_id_busy_q;
logic [NumMasters-1:0][SourceIds-1:0] s_write_id_busy_d, s_write_id_busy_q;
logic [NumTargets-1:0][NumMasters-1:0][AgeWidth-1:0] s_read_age_d, s_read_age_q;
@@ -262,6 +268,17 @@ module axi4_data_crossbar #(
return '0;
endfunction
function automatic logic [CountWidth-1:0] target_read_limit(input logic [TargetWidth-1:0] target);
if ((target == TargetSram) || (target == TargetSdram)) return CountWidth'(4);
return CountWidth'(1);
endfunction
function automatic logic [CountWidth-1:0] target_write_limit(
input logic [TargetWidth-1:0] target);
if ((target == TargetSram) || (target == TargetSdram)) return CountWidth'(2);
return CountWidth'(1);
endfunction
function automatic logic [3:0] master_priority(input int unsigned master,
input logic [3:0] request_qos, input logic aged,
input logic recovery);
@@ -418,6 +435,22 @@ module axi4_data_crossbar #(
.selected_o(s_write_selected[target]),
.valid_o (s_write_grant_valid[target])
);
fifo #(
.DATA_WIDTH (MasterWidth),
.BUFFER_DEPTH(RouteDepth)
) u_write_owner_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(1'b0),
.push_i (s_target_write_owner_push[target]),
.full_o (s_target_write_owner_full[target]),
.dat_i (s_write_selected[target]),
.pop_i (s_target_write_owner_pop[target]),
.empty_o(s_target_write_owner_empty[target]),
.dat_o (s_target_write_owner_data[target]),
.cnt_o (unused_target_write_owner_count[target])
);
end
@@ -477,20 +510,23 @@ module axi4_data_crossbar #(
s_read_decoded_target[master] : TargetError;
s_write_routed_target[master] = (s_write_fault_reason[master] == 4'd0) ?
s_write_decoded_target[master] : TargetError;
if (!block_new_i && m_arvalid[master] &&
!s_read_owner_valid_q[s_read_routed_target[master]] &&
(s_master_read_count_q[master] <
((s_read_fault_reason[master] != 4'd0) ? CountWidth'(1) :
master_read_limit(
if (!block_new_i && !master_block_i[master] && m_arvalid[master] &&
(s_target_read_count_q[s_read_routed_target[master]] <
target_read_limit(
s_read_routed_target[master]
)) && (s_master_read_count_q[master] <
((s_read_fault_reason[master] != 4'd0) ? CountWidth'(1) : master_read_limit(
master
))) && !s_read_id_busy_q[master][m_ar[master].id[2:0]]) begin
s_read_req[s_read_routed_target[master]][master] = 1'b1;
end
if (!block_new_i && m_awvalid[master] &&
!s_write_owner_valid_q[s_write_routed_target[master]] &&
(s_master_write_count_q[master] <
((s_write_fault_reason[master] != 4'd0) ? CountWidth'(1) :
master_write_limit(
if (!block_new_i && !master_block_i[master] && m_awvalid[master] &&
!s_target_write_owner_full[s_write_routed_target[master]] &&
(s_target_write_count_q[s_write_routed_target[master]] <
target_write_limit(
s_write_routed_target[master]
)) && (s_master_write_count_q[master] <
((s_write_fault_reason[master] != 4'd0) ? CountWidth'(1) : master_write_limit(
master
))) && (s_route_count_q[master] < 2'(RouteDepth)) &&
!s_write_id_busy_q[master][m_aw[master].id[2:0]]) begin
@@ -575,20 +611,22 @@ module axi4_data_crossbar #(
end
always_comb begin
m_awready = '0;
t_aw = '{default: '0};
t_awvalid = '0;
s_write_capture = '0;
s_write_accept = '0;
s_write_accept_target = '0;
s_write_accept_id = '0;
m_awready = '0;
t_aw = '{default: '0};
t_awvalid = '0;
s_write_capture = '0;
s_target_write_owner_push = '0;
s_write_accept = '0;
s_write_accept_target = '0;
s_write_accept_id = '0;
for (int target = 0; target < NumTargets; target++) begin
if (s_write_grant_valid[target]) begin
automatic logic [MasterWidth-1:0] owner = s_write_selected[target];
t_aw[target] = m_aw[owner];
t_awvalid[target] = m_awvalid[owner];
m_awready[owner] = t_awready[target];
s_write_capture[target] = t_awvalid[target] && t_awready[target];
t_aw[target] = m_aw[owner];
t_awvalid[target] = m_awvalid[owner];
m_awready[owner] = t_awready[target];
s_write_capture[target] = t_awvalid[target] && t_awready[target];
s_target_write_owner_push[target] = s_write_capture[target];
if (s_write_capture[target]) begin
s_write_accept[owner] = 1'b1;
s_write_accept_target[owner] = TargetWidth'(target);
@@ -599,21 +637,21 @@ module axi4_data_crossbar #(
end
always_comb begin
m_wready = '0;
t_w = '{default: '0};
t_wvalid = '0;
s_write_data_terminal = '0;
for (int master = 0; master < NumMasters; master++) begin
if (s_route_count_q[master] != 2'd0) begin
automatic
logic [TargetWidth-1:0]
target = s_write_route_q[master][s_route_read_ptr_q[master]];
if (s_write_owner_valid_q[target] &&
(s_write_owner_q[target] == MasterWidth'(master))) begin
t_w[target] = m_w[master];
t_wvalid[target] = m_wvalid[master];
m_wready[master] = t_wready[target];
m_wready = '0;
t_w = '{default: '0};
t_wvalid = '0;
s_write_data_terminal = '0;
s_target_write_owner_pop = '0;
for (int target = 0; target < NumTargets; target++) begin
if (!s_target_write_owner_empty[target]) begin
automatic logic [MasterWidth-1:0] master = s_target_write_owner_data[target];
if ((s_route_count_q[master] != 2'd0) &&
(s_write_route_q[master][s_route_read_ptr_q[master]] == TargetWidth'(target))) begin
t_w[target] = m_w[master];
t_wvalid[target] = m_wvalid[master];
m_wready[master] = t_wready[target];
s_write_data_terminal[master] = t_wvalid[target] && t_wready[target] && t_w[target].last;
s_target_write_owner_pop[target] = s_write_data_terminal[master];
end
end
end
@@ -623,11 +661,11 @@ module axi4_data_crossbar #(
s_read_resp_req = '0;
s_write_resp_req = '0;
for (int target = 0; target < NumTargets; target++) begin
if (s_read_owner_valid_q[target] && t_rvalid[target]) begin
s_read_resp_req[s_read_owner_q[target]][target] = 1'b1;
if (t_rvalid[target]) begin
s_read_resp_req[t_r[target].id[5:3]][target] = 1'b1;
end
if (s_write_owner_valid_q[target] && t_bvalid[target]) begin
s_write_resp_req[s_write_owner_q[target]][target] = 1'b1;
if (t_bvalid[target]) begin
s_write_resp_req[t_b[target].id[5:3]][target] = 1'b1;
end
end
end
@@ -664,35 +702,35 @@ module axi4_data_crossbar #(
end
always_comb begin
s_read_owner_valid_d = s_read_owner_valid_q;
s_write_owner_valid_d = s_write_owner_valid_q;
s_read_owner_d = s_read_owner_q;
s_write_owner_d = s_write_owner_q;
s_master_read_count_d = s_master_read_count_q;
s_master_write_count_d = s_master_write_count_q;
s_target_read_count_d = s_target_read_count_q;
s_target_write_count_d = s_target_write_count_q;
s_read_id_busy_d = s_read_id_busy_q;
s_write_id_busy_d = s_write_id_busy_q;
for (int master = 0; master < NumMasters; master++) begin
if (s_read_terminal[master]) begin
s_read_owner_valid_d[s_read_resp_selected[master]] = 1'b0;
s_master_read_count_d[master] = s_master_read_count_d[master] - 1'b1;
s_master_read_count_d[master] = s_master_read_count_d[master] - 1'b1;
s_target_read_count_d[s_read_resp_selected[master]] =
s_target_read_count_d[s_read_resp_selected[master]] - 1'b1;
s_read_id_busy_d[master][s_read_complete_id[master]] = 1'b0;
end
if (s_write_terminal[master]) begin
s_write_owner_valid_d[s_write_resp_selected[master]] = 1'b0;
s_master_write_count_d[master] = s_master_write_count_d[master] - 1'b1;
s_target_write_count_d[s_write_resp_selected[master]] =
s_target_write_count_d[s_write_resp_selected[master]] - 1'b1;
s_write_id_busy_d[master][s_write_complete_id[master]] = 1'b0;
end
if (s_read_accept[master]) begin
s_read_owner_valid_d[s_read_accept_target[master]] = 1'b1;
s_read_owner_d[s_read_accept_target[master]] = MasterWidth'(master);
s_master_read_count_d[master] = s_master_read_count_d[master] + 1'b1;
s_master_read_count_d[master] = s_master_read_count_d[master] + 1'b1;
s_target_read_count_d[s_read_accept_target[master]] =
s_target_read_count_d[s_read_accept_target[master]] + 1'b1;
s_read_id_busy_d[master][s_read_accept_id[master]] = 1'b1;
end
if (s_write_accept[master]) begin
s_write_owner_valid_d[s_write_accept_target[master]] = 1'b1;
s_write_owner_d[s_write_accept_target[master]] = MasterWidth'(master);
s_master_write_count_d[master] = s_master_write_count_d[master] + 1'b1;
s_target_write_count_d[s_write_accept_target[master]] =
s_target_write_count_d[s_write_accept_target[master]] + 1'b1;
s_write_id_busy_d[master][s_write_accept_id[master]] = 1'b1;
end
end
@@ -780,15 +818,18 @@ module axi4_data_crossbar #(
assign idle_o = (outstanding_read_o == 8'd0) && (outstanding_write_o == 8'd0) &&
!(|s_route_count_q);
for (genvar master = 0; master < NumMasters; master++) begin : gen_master_idle
assign master_idle_o[master] = (s_master_read_count_q[master] == '0) &&
(s_master_write_count_q[master] == '0) && (s_route_count_q[master] == '0) &&
!m_arvalid[master] && !m_awvalid[master] && !m_wvalid[master];
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_read_owner_valid_q <= '0;
s_write_owner_valid_q <= '0;
s_read_owner_q <= '0;
s_write_owner_q <= '0;
s_master_read_count_q <= '0;
s_master_write_count_q <= '0;
s_target_read_count_q <= '0;
s_target_write_count_q <= '0;
s_read_id_busy_q <= '0;
s_write_id_busy_q <= '0;
s_write_route_q <= '0;
@@ -804,12 +845,10 @@ module axi4_data_crossbar #(
fault_write_o <= 1'b0;
fault_reason_o <= '0;
end else begin
s_read_owner_valid_q <= s_read_owner_valid_d;
s_write_owner_valid_q <= s_write_owner_valid_d;
s_read_owner_q <= s_read_owner_d;
s_write_owner_q <= s_write_owner_d;
s_master_read_count_q <= s_master_read_count_d;
s_master_write_count_q <= s_master_write_count_d;
s_target_read_count_q <= s_target_read_count_d;
s_target_write_count_q <= s_target_write_count_d;
s_read_id_busy_q <= s_read_id_busy_d;
s_write_id_busy_q <= s_write_id_busy_d;
s_write_route_q <= s_write_route_d;
+20 -2
View File
@@ -9,6 +9,7 @@ module axi4_downsizer_64to32 #(
) (
input logic clk_i,
input logic rst_n_i,
input logic clear_i,
axi4_if.slave wide,
axi4_if.master narrow
);
@@ -19,6 +20,8 @@ module axi4_downsizer_64to32 #(
channel_state_e s_read_state_d, s_read_state_q;
channel_state_e s_write_state_d, s_write_state_q;
logic s_read_state_bits_q;
logic s_write_state_bits_q;
logic [WideIdWidth-1:0] s_read_id_d, s_read_id_q;
logic [WideIdWidth-1:0] s_write_id_d, s_write_id_q;
logic [2:0] s_read_size_d, s_read_size_q;
@@ -46,6 +49,8 @@ module axi4_downsizer_64to32 #(
logic [8:0] s_read_beats;
logic [8:0] s_write_beats;
assign s_read_state_q = channel_state_e'(s_read_state_bits_q);
assign s_write_state_q = channel_state_e'(s_write_state_bits_q);
assign s_read_beats = {1'b0, wide.arlen} + 9'd1;
assign s_write_beats = {1'b0, wide.awlen} + 9'd1;
@@ -115,6 +120,12 @@ module axi4_downsizer_64to32 #(
s_read_addr_d = s_read_next_addr;
if (narrow.rlast) s_read_state_d = Idle;
end
if (clear_i) begin
s_read_state_d = Idle;
s_read_upper_d = 1'b0;
s_read_resp_d = `AXI4_RESP_OKAY;
end
end
assign wide.awready = (s_write_state_q == Idle) && narrow.awready;
@@ -188,6 +199,13 @@ module axi4_downsizer_64to32 #(
s_b_pending_d = 1'b0;
s_write_state_d = Idle;
end
if (clear_i) begin
s_write_state_d = Idle;
s_write_upper_d = 1'b0;
s_b_pending_d = 1'b0;
s_b_resp_d = `AXI4_RESP_OKAY;
end
end
axi4_addr_gen #(
@@ -215,7 +233,7 @@ module axi4_downsizer_64to32 #(
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_state_d),
.dat_o (s_read_state_q)
.dat_o (s_read_state_bits_q)
);
dffr #(
@@ -224,7 +242,7 @@ module axi4_downsizer_64to32 #(
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_state_d),
.dat_o (s_write_state_q)
.dat_o (s_write_state_bits_q)
);
dffr #(
+2
View File
@@ -463,6 +463,8 @@ module axi4_interconnect #(
`SOC_ADDR_IS_APB4_EXT_L(addr)
&& !
`SOC_ADDR_IS_APB4_EXT_H(addr)
&& !
`SOC_ADDR_IS_APB4_RESOURCE_CTRL(addr)
);
`else
s_access_allowed[master] = (master != 1) || ((read_req &&
+476
View File
@@ -0,0 +1,476 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "axi4_define.svh"
module axi4_target_guard #(
parameter int unsigned AddrWidth = 32,
parameter int unsigned DataWidth = 64,
parameter int unsigned IdWidth = 6,
parameter int unsigned UserWidth = 1,
parameter int unsigned ReadDepth = 4,
parameter int unsigned WriteDepth = 2
) (
// verilog_format: off -- preserve the timeout/isolation boundary columns
input logic clk_i,
input logic rst_n_i,
input logic clear_i,
input logic [31:0] timeout_i,
output logic clear_busy_o,
output logic abort_o,
input logic abort_done_i,
output logic timeout_valid_o,
output logic timeout_write_o,
output logic [IdWidth-1:0] timeout_id_o,
output logic [AddrWidth-1:0] timeout_addr_o,
axi4_if.slave source,
axi4_if.master sink
// verilog_format: on
);
localparam int unsigned StrbWidth = DataWidth / 8;
localparam int unsigned AddrChannelWidth =
IdWidth + AddrWidth + 8 + 3 + 2 + 1 + 4 + 3 + 4 + 4 + UserWidth;
typedef enum logic [2:0] {
ReadIdle,
ReadSend,
ReadForward,
ReadAbort,
ReadSynthetic
} read_state_e;
typedef enum logic [2:0] {
WriteIdle,
WriteSend,
WriteStream,
WriteResponse,
WriteAbort,
WriteDrop,
WriteSynthetic
} write_state_e;
logic [AddrChannelWidth-1:0] s_ar_push_data;
logic [AddrChannelWidth-1:0] s_ar_head_data;
logic [AddrChannelWidth-1:0] s_aw_push_data;
logic [AddrChannelWidth-1:0] s_aw_head_data;
logic s_ar_full;
logic s_ar_empty;
logic s_ar_push;
logic s_ar_pop;
logic s_aw_full;
logic s_aw_empty;
logic s_aw_push;
logic s_aw_pop;
logic [ $clog2(ReadDepth):0] unused_ar_count;
logic [$clog2(WriteDepth):0] unused_aw_count;
logic [ IdWidth-1:0] s_ar_id;
logic [ AddrWidth-1:0] s_ar_addr;
logic [ 7:0] s_ar_len;
logic [ 2:0] s_ar_size;
logic [ 1:0] s_ar_burst;
logic s_ar_lock;
logic [ 3:0] s_ar_cache;
logic [ 2:0] s_ar_prot;
logic [ 3:0] s_ar_qos;
logic [ 3:0] s_ar_region;
logic [ UserWidth-1:0] s_ar_user;
logic [ IdWidth-1:0] s_aw_id;
logic [ AddrWidth-1:0] s_aw_addr;
logic [ 7:0] s_aw_len;
logic [ 2:0] s_aw_size;
logic [ 1:0] s_aw_burst;
logic s_aw_lock;
logic [ 3:0] s_aw_cache;
logic [ 2:0] s_aw_prot;
logic [ 3:0] s_aw_qos;
logic [ 3:0] s_aw_region;
logic [ UserWidth-1:0] s_aw_user;
read_state_e s_read_state_d, s_read_state_q;
write_state_e s_write_state_d, s_write_state_q;
logic [IdWidth-1:0] s_read_id_d, s_read_id_q;
logic [AddrWidth-1:0] s_read_addr_d, s_read_addr_q;
logic [8:0] s_read_remaining_d, s_read_remaining_q;
logic [31:0] s_read_timeout_d, s_read_timeout_q;
logic [IdWidth-1:0] s_write_id_d, s_write_id_q;
logic [AddrWidth-1:0] s_write_addr_d, s_write_addr_q;
logic [31:0] s_write_timeout_d, s_write_timeout_q;
logic s_write_last_seen_d, s_write_last_seen_q;
logic s_isolated_d, s_isolated_q;
logic s_timeout_read;
logic s_timeout_write;
logic s_read_progress;
logic s_write_progress;
logic s_read_timeout_enable;
logic s_write_timeout_enable;
assign s_ar_push_data = {
source.arid,
source.araddr,
source.arlen,
source.arsize,
source.arburst,
source.arlock,
source.arcache,
source.arprot,
source.arqos,
source.arregion,
source.aruser
};
assign {
s_ar_id,
s_ar_addr,
s_ar_len,
s_ar_size,
s_ar_burst,
s_ar_lock,
s_ar_cache,
s_ar_prot,
s_ar_qos,
s_ar_region,
s_ar_user
} = s_ar_head_data;
assign s_aw_push_data = {
source.awid,
source.awaddr,
source.awlen,
source.awsize,
source.awburst,
source.awlock,
source.awcache,
source.awprot,
source.awqos,
source.awregion,
source.awuser
};
assign {
s_aw_id,
s_aw_addr,
s_aw_len,
s_aw_size,
s_aw_burst,
s_aw_lock,
s_aw_cache,
s_aw_prot,
s_aw_qos,
s_aw_region,
s_aw_user
} = s_aw_head_data;
assign source.arready = !s_ar_full && !clear_i;
assign source.awready = !s_aw_full && !clear_i;
assign s_ar_push = source.arvalid && source.arready;
assign s_aw_push = source.awvalid && source.awready;
fifo #(
.DATA_WIDTH (AddrChannelWidth),
.BUFFER_DEPTH(ReadDepth)
) u_ar_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(clear_i),
.push_i (s_ar_push),
.full_o (s_ar_full),
.dat_i (s_ar_push_data),
.pop_i (s_ar_pop),
.empty_o(s_ar_empty),
.dat_o (s_ar_head_data),
.cnt_o (unused_ar_count)
);
fifo #(
.DATA_WIDTH (AddrChannelWidth),
.BUFFER_DEPTH(WriteDepth)
) u_aw_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(clear_i),
.push_i (s_aw_push),
.full_o (s_aw_full),
.dat_i (s_aw_push_data),
.pop_i (s_aw_pop),
.empty_o(s_aw_empty),
.dat_o (s_aw_head_data),
.cnt_o (unused_aw_count)
);
assign sink.arid = s_ar_id;
assign sink.araddr = s_ar_addr;
assign sink.arlen = s_ar_len;
assign sink.arsize = s_ar_size;
assign sink.arburst = s_ar_burst;
assign sink.arlock = s_ar_lock;
assign sink.arcache = s_ar_cache;
assign sink.arprot = s_ar_prot;
assign sink.arqos = s_ar_qos;
assign sink.arregion = s_ar_region;
assign sink.aruser = s_ar_user;
assign sink.arvalid = (s_read_state_q == ReadSend) && !s_isolated_q;
assign s_ar_pop = ((s_read_state_q == ReadSend) &&
(sink.arready || s_timeout_read || s_isolated_q)) ||
((s_read_state_q == ReadIdle) && s_isolated_q && !s_ar_empty);
assign source.rid = (s_read_state_q == ReadSynthetic) ? s_read_id_q : sink.rid;
assign source.rdata = (s_read_state_q == ReadSynthetic) ? '0 : sink.rdata;
assign source.rresp = (s_read_state_q == ReadSynthetic) ? `AXI4_RESP_SLAVE_ERROR : sink.rresp;
assign source.rlast = (s_read_state_q == ReadSynthetic) ?
(s_read_remaining_q == 9'd1) : sink.rlast;
assign source.ruser = (s_read_state_q == ReadSynthetic) ? '0 : sink.ruser;
assign source.rvalid = (s_read_state_q == ReadSynthetic) ||
((s_read_state_q == ReadForward) && sink.rvalid);
assign sink.rready = (s_read_state_q == ReadForward) ? source.rready :
((s_read_state_q == ReadAbort) ||
(s_read_state_q == ReadSynthetic));
assign s_read_progress = (sink.arvalid && sink.arready) ||
((s_read_state_q == ReadForward) && sink.rvalid && sink.rready);
assign s_read_timeout_enable = (timeout_i != 32'd0) &&
(((s_read_state_q == ReadSend) && !sink.arready) ||
((s_read_state_q == ReadForward) && source.rready &&
!sink.rvalid));
assign s_timeout_read = s_read_timeout_enable && (s_read_timeout_q == timeout_i - 1'b1);
always_comb begin
s_read_state_d = s_read_state_q;
s_read_id_d = s_read_id_q;
s_read_addr_d = s_read_addr_q;
s_read_remaining_d = s_read_remaining_q;
s_read_timeout_d = s_read_timeout_q;
if (s_read_state_q == ReadIdle) begin
s_read_timeout_d = '0;
if (!s_ar_empty && s_isolated_q) begin
s_read_id_d = s_ar_id;
s_read_addr_d = s_ar_addr;
s_read_remaining_d = {1'b0, s_ar_len} + 1'b1;
s_read_state_d = ReadSynthetic;
end else if (!s_ar_empty) begin
s_read_state_d = ReadSend;
end
end else if (s_isolated_q && (s_read_state_q == ReadSend)) begin
s_read_id_d = s_ar_id;
s_read_addr_d = s_ar_addr;
s_read_remaining_d = {1'b0, s_ar_len} + 1'b1;
s_read_state_d = ReadSynthetic;
end else if (s_isolated_q && (s_read_state_q == ReadForward)) begin
s_read_state_d = ReadAbort;
end else if (s_timeout_read) begin
s_read_timeout_d = '0;
if (s_read_state_q == ReadSend) begin
s_read_id_d = s_ar_id;
s_read_addr_d = s_ar_addr;
s_read_remaining_d = {1'b0, s_ar_len} + 1'b1;
s_read_state_d = ReadSynthetic;
end else begin
s_read_id_d = s_read_id_q;
s_read_addr_d = s_read_addr_q;
s_read_state_d = ReadAbort;
end
end else begin
s_read_timeout_d = s_read_progress ? 32'd0 :
s_read_timeout_enable ? s_read_timeout_q + 1'b1 : s_read_timeout_q;
unique case (s_read_state_q)
ReadSend: begin
if (sink.arvalid && sink.arready) begin
s_read_id_d = s_ar_id;
s_read_addr_d = s_ar_addr;
s_read_remaining_d = {1'b0, s_ar_len} + 1'b1;
s_read_state_d = ReadForward;
end
end
ReadForward: begin
if (sink.rvalid && sink.rready) begin
if (s_read_remaining_q != 9'd0) s_read_remaining_d = s_read_remaining_q - 1'b1;
if (sink.rlast || (s_read_remaining_q == 9'd1)) s_read_state_d = ReadIdle;
end
end
ReadAbort: if (abort_done_i) s_read_state_d = ReadSynthetic;
ReadSynthetic: begin
if (source.rvalid && source.rready) begin
if (s_read_remaining_q == 9'd1) begin
s_read_remaining_d = '0;
s_read_state_d = ReadIdle;
end else begin
s_read_remaining_d = s_read_remaining_q - 1'b1;
end
end
end
default: s_read_state_d = ReadIdle;
endcase
end
if (clear_i) s_read_state_d = ReadIdle;
end
assign sink.awid = s_aw_id;
assign sink.awaddr = s_aw_addr;
assign sink.awlen = s_aw_len;
assign sink.awsize = s_aw_size;
assign sink.awburst = s_aw_burst;
assign sink.awlock = s_aw_lock;
assign sink.awcache = s_aw_cache;
assign sink.awprot = s_aw_prot;
assign sink.awqos = s_aw_qos;
assign sink.awregion = s_aw_region;
assign sink.awuser = s_aw_user;
assign sink.awvalid = (s_write_state_q == WriteSend) && !s_isolated_q;
assign s_aw_pop = ((s_write_state_q == WriteSend) &&
(sink.awready || s_timeout_write || s_isolated_q)) ||
((s_write_state_q == WriteIdle) && s_isolated_q && !s_aw_empty);
assign sink.wdata = source.wdata;
assign sink.wstrb = source.wstrb;
assign sink.wlast = source.wlast;
assign sink.wuser = source.wuser;
assign sink.wvalid = (s_write_state_q == WriteStream) && source.wvalid;
assign source.wready = (s_write_state_q == WriteStream) ? sink.wready :
(s_write_state_q == WriteDrop);
assign source.bid = (s_write_state_q == WriteSynthetic) ? s_write_id_q : sink.bid;
assign source.bresp = (s_write_state_q == WriteSynthetic) ? `AXI4_RESP_SLAVE_ERROR : sink.bresp;
assign source.buser = (s_write_state_q == WriteSynthetic) ? '0 : sink.buser;
assign source.bvalid = (s_write_state_q == WriteSynthetic) ||
((s_write_state_q == WriteResponse) && sink.bvalid);
assign sink.bready = (s_write_state_q == WriteResponse) ? source.bready :
((s_write_state_q == WriteAbort) ||
(s_write_state_q == WriteSynthetic));
assign s_write_progress = (sink.awvalid && sink.awready) ||
(sink.wvalid && sink.wready) ||
((s_write_state_q == WriteResponse) && sink.bvalid && sink.bready);
assign s_write_timeout_enable = (timeout_i != 32'd0) &&
(((s_write_state_q == WriteSend) && !sink.awready) ||
((s_write_state_q == WriteStream) && source.wvalid && !sink.wready) ||
((s_write_state_q == WriteResponse) && source.bready && !sink.bvalid));
assign s_timeout_write = s_write_timeout_enable && (s_write_timeout_q == timeout_i - 1'b1);
always_comb begin
s_write_state_d = s_write_state_q;
s_write_id_d = s_write_id_q;
s_write_addr_d = s_write_addr_q;
s_write_timeout_d = s_write_timeout_q;
s_write_last_seen_d = s_write_last_seen_q;
if (s_write_state_q == WriteIdle) begin
s_write_timeout_d = '0;
s_write_last_seen_d = 1'b0;
if (!s_aw_empty && s_isolated_q) begin
s_write_id_d = s_aw_id;
s_write_addr_d = s_aw_addr;
s_write_state_d = WriteDrop;
end else if (!s_aw_empty) begin
s_write_state_d = WriteSend;
end
end else if (s_isolated_q && (s_write_state_q == WriteSend)) begin
s_write_id_d = s_aw_id;
s_write_addr_d = s_aw_addr;
s_write_state_d = WriteDrop;
end else if (s_isolated_q && ((s_write_state_q == WriteStream) ||
(s_write_state_q == WriteResponse))) begin
s_write_state_d = WriteAbort;
end else if (s_timeout_write) begin
s_write_timeout_d = '0;
if (s_write_state_q == WriteSend) begin
s_write_id_d = s_aw_id;
s_write_addr_d = s_aw_addr;
s_write_state_d = WriteDrop;
end else begin
s_write_state_d = WriteAbort;
end
end else begin
s_write_timeout_d = s_write_progress ? 32'd0 :
s_write_timeout_enable ? s_write_timeout_q + 1'b1 : s_write_timeout_q;
unique case (s_write_state_q)
WriteSend: begin
if (sink.awvalid && sink.awready) begin
s_write_id_d = s_aw_id;
s_write_addr_d = s_aw_addr;
s_write_state_d = WriteStream;
end
end
WriteStream: begin
if (sink.wvalid && sink.wready && sink.wlast) begin
s_write_last_seen_d = 1'b1;
s_write_state_d = WriteResponse;
end
end
WriteResponse: begin
if (sink.bvalid && sink.bready) s_write_state_d = WriteIdle;
end
WriteAbort: begin
if (abort_done_i) begin
s_write_state_d = s_write_last_seen_q ? WriteSynthetic : WriteDrop;
end
end
WriteDrop: begin
if (source.wvalid && source.wready && source.wlast) begin
s_write_last_seen_d = 1'b1;
s_write_state_d = WriteSynthetic;
end
end
WriteSynthetic: begin
if (source.bvalid && source.bready) s_write_state_d = WriteIdle;
end
default: s_write_state_d = WriteIdle;
endcase
end
if (clear_i) s_write_state_d = WriteIdle;
end
always_comb begin
s_isolated_d = s_isolated_q;
if ((s_timeout_read && (s_read_state_q == ReadForward)) ||
(s_timeout_write && ((s_write_state_q == WriteStream) ||
(s_write_state_q == WriteResponse)))) begin
s_isolated_d = 1'b1;
end
end
assign abort_o = (s_read_state_q == ReadAbort) || (s_write_state_q == WriteAbort);
assign clear_busy_o = clear_i || abort_o;
assign timeout_valid_o = s_timeout_read || s_timeout_write;
assign timeout_write_o = s_timeout_write;
assign timeout_id_o = s_timeout_write ?
((s_write_state_q == WriteSend) ? s_aw_id : s_write_id_q) :
((s_read_state_q == ReadSend) ? s_ar_id : s_read_id_q);
assign timeout_addr_o = s_timeout_write ?
((s_write_state_q == WriteSend) ? s_aw_addr : s_write_addr_q) :
((s_read_state_q == ReadSend) ? s_ar_addr : s_read_addr_q);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_read_state_q <= ReadIdle;
s_read_id_q <= '0;
s_read_addr_q <= '0;
s_read_remaining_q <= '0;
s_read_timeout_q <= '0;
s_write_state_q <= WriteIdle;
s_write_id_q <= '0;
s_write_addr_q <= '0;
s_write_timeout_q <= '0;
s_write_last_seen_q <= 1'b0;
s_isolated_q <= 1'b0;
end else begin
s_read_state_q <= s_read_state_d;
s_read_id_q <= s_read_id_d;
s_read_addr_q <= s_read_addr_d;
s_read_remaining_q <= s_read_remaining_d;
s_read_timeout_q <= s_read_timeout_d;
s_write_state_q <= s_write_state_d;
s_write_id_q <= s_write_id_d;
s_write_addr_q <= s_write_addr_d;
s_write_timeout_q <= s_write_timeout_d;
s_write_last_seen_q <= s_write_last_seen_d;
s_isolated_q <= s_isolated_d;
end
end
`ifndef SYNTHESIS
initial begin
if ((AddrWidth < 1) || (DataWidth < 8) || ((DataWidth % 8) != 0) ||
(IdWidth < 1) || (UserWidth < 1) || (ReadDepth < 2) || (WriteDepth < 2) ||
((ReadDepth & (ReadDepth - 1)) != 0) ||
((WriteDepth & (WriteDepth - 1)) != 0) || (StrbWidth < 1)) begin
$fatal(1, "axi4_target_guard: invalid interface or queue geometry");
end
end
`endif
endmodule
+4 -1
View File
@@ -16,6 +16,7 @@ module hp_axi4_mux3 (
} state_e;
state_e s_state_d, s_state_q;
logic s_state_bits_q;
logic [1:0] s_owner_d, s_owner_q;
logic s_write_d, s_write_q;
logic [1:0] s_selected;
@@ -23,6 +24,8 @@ module hp_axi4_mux3 (
logic s_addr_accept;
logic s_terminal;
assign s_state_q = state_e'(s_state_bits_q);
always_comb begin
s_selected = 2'd0;
s_selected_write = 1'b0;
@@ -177,7 +180,7 @@ module hp_axi4_mux3 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_q)
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(2)
+24 -4
View File
@@ -8,10 +8,12 @@ module hp_lifecycle_controller (
input logic release_req_i,
input logic hp_idle_i,
input logic flush_busy_i,
input logic cache_clean_i,
input logic [15:0] timeout_i,
output logic hp_release_o,
output logic block_new_o,
output logic flush_o,
output logic cache_request_o,
output logic draining_o,
output logic forced_fault_o
// verilog_format: on
@@ -19,6 +21,7 @@ module hp_lifecycle_controller (
typedef enum logic [2:0] {
Held,
Running,
CacheRequest,
Quiesce,
FlushRequest,
FlushWait
@@ -36,8 +39,9 @@ module hp_lifecycle_controller (
assign hp_release_o = s_hp_release_q;
assign block_new_o = s_block_new_q;
assign flush_o = s_flush_q;
assign draining_o = (s_state_q == Quiesce) || (s_state_q == FlushRequest) ||
(s_state_q == FlushWait);
assign cache_request_o = s_state_q == CacheRequest;
assign draining_o = (s_state_q == CacheRequest) || (s_state_q == Quiesce) ||
(s_state_q == FlushRequest) || (s_state_q == FlushWait);
assign forced_fault_o = s_forced_fault_q;
always_comb begin
@@ -65,8 +69,24 @@ module hp_lifecycle_controller (
s_flush_d = 1'b0;
s_timeout_cnt_d = '0;
if (!release_req_i) begin
s_block_new_d = 1'b1;
s_state_d = Quiesce;
s_state_d = CacheRequest;
end
end
CacheRequest: begin
s_hp_release_d = 1'b1;
s_block_new_d = 1'b0;
s_flush_d = 1'b0;
if (cache_clean_i) begin
s_block_new_d = 1'b1;
s_timeout_cnt_d = '0;
s_state_d = Quiesce;
end else if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_forced_fault_d = 1'b1;
s_block_new_d = 1'b1;
s_timeout_cnt_d = '0;
s_state_d = Quiesce;
end else begin
s_timeout_cnt_d = s_timeout_cnt_q + 1'b1;
end
end
Quiesce: begin
+134 -57
View File
@@ -13,7 +13,9 @@
module onchip_ram #(
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128
parameter int unsigned CapacityKiB = 128,
parameter int unsigned DataWidth = 32,
parameter int unsigned IdWidth = 1
) (
// verilog_format: off -- preserve the AXI, APB, and performance grouping
input logic clk_i,
@@ -31,8 +33,10 @@ module onchip_ram #(
localparam int unsigned TechnologyBankKiB = 4;
localparam int unsigned TechnologyAddrWidth = 10;
`endif
localparam int unsigned NumBanks = CapacityKiB / TechnologyBankKiB;
localparam int unsigned NumBanks = (CapacityKiB + TechnologyBankKiB - 1) / TechnologyBankKiB;
localparam int unsigned BankIndexWidth = (NumBanks > 1) ? $clog2(NumBanks) : 1;
localparam int unsigned DataBytes = DataWidth / 8;
localparam int unsigned BeatSize = $clog2(DataBytes);
localparam logic [31:0] MemoryEnd = `SOC_ADDR_SRAM_BASE + 32'(CapacityKiB * 1024) - 1'b1;
typedef enum logic [2:0] {
@@ -47,7 +51,7 @@ module onchip_ram #(
state_e s_state_d;
state_e s_state_q;
logic [2:0] s_state_bits_q;
logic s_id_d, s_id_q;
logic [IdWidth-1:0] s_id_d, s_id_q;
logic [7:0] s_len_d, s_len_q;
logic [7:0] s_beat_d, s_beat_q;
logic [2:0] s_size_d, s_size_q;
@@ -69,38 +73,38 @@ module onchip_ram #(
logic [31:0] s_perf_stall_cycles_d, s_perf_stall_cycles_q;
logic [31:0] s_perf_err_resps_d, s_perf_err_resps_q;
logic [32:0] s_ar_last_addr;
logic [32:0] s_aw_last_addr;
logic [31:0] s_ar_next_addr;
logic [31:0] s_read_next_addr;
logic [31:0] s_write_next_addr;
logic s_ar_protocol_legal;
logic s_aw_protocol_legal;
logic s_ar_bounds_legal;
logic s_aw_bounds_legal;
logic [ 1:0] s_ar_response;
logic [ 1:0] s_aw_response;
logic s_ar_accept;
logic s_aw_accept;
logic s_w_accept;
logic s_read_accept;
logic s_b_accept;
logic s_read_issue;
logic s_read_can_issue;
logic s_expected_wlast;
logic [ 3:0] s_write_lane_mask;
logic s_write_strobe_legal;
logic s_write_beat_legal;
logic s_write_terminal;
logic s_err_resp_event;
logic s_stall_event;
logic [ 32:0] s_ar_last_addr;
logic [ 32:0] s_aw_last_addr;
logic [ 31:0] s_ar_next_addr;
logic [ 31:0] s_read_next_addr;
logic [ 31:0] s_write_next_addr;
logic s_ar_protocol_legal;
logic s_aw_protocol_legal;
logic s_ar_bounds_legal;
logic s_aw_bounds_legal;
logic [ 1:0] s_ar_response;
logic [ 1:0] s_aw_response;
logic s_ar_accept;
logic s_aw_accept;
logic s_w_accept;
logic s_read_accept;
logic s_b_accept;
logic s_read_issue;
logic s_read_can_issue;
logic s_expected_wlast;
logic [DataBytes-1:0] s_write_lane_mask;
logic s_write_strobe_legal;
logic s_write_beat_legal;
logic s_write_terminal;
logic s_err_resp_event;
logic s_stall_event;
logic s_mem_read;
logic s_mem_write;
logic [14:0] s_mem_word_addr;
logic [31:0] s_mem_wdata;
logic [ 3:0] s_mem_wstrb;
logic [31:0] s_mem_rdata;
logic s_mem_read;
logic s_mem_write;
logic [ 14:0] s_mem_word_addr;
logic [DataWidth-1:0] s_mem_wdata;
logic [DataBytes-1:0] s_mem_wstrb;
logic [DataWidth-1:0] s_mem_rdata;
function automatic logic wrap_length_legal(input logic [7:0] length_i);
return (length_i == 8'd1) || (length_i == 8'd3) || (length_i == 8'd7) || (length_i == 8'd15);
@@ -124,13 +128,14 @@ module onchip_ram #(
end
endfunction
function automatic logic [3:0] transfer_lane_mask(input logic [2:0] size_i,
input logic [1:0] address_i);
unique case (size_i)
3'd0: return 4'b0001 << address_i;
3'd1: return 4'b0011 << address_i;
default: return 4'b1111;
endcase
function automatic logic [DataBytes-1:0] transfer_lane_mask(input logic [2:0] size_i,
input logic [2:0] address_i);
logic [DataBytes-1:0] base_mask;
begin
base_mask = (size_i >= 3'(BeatSize)) ?
{DataBytes{1'b1}} : {DataBytes{1'b1}} >> (DataBytes - (1 << size_i));
return base_mask << (address_i & 3'(DataBytes - 1));
end
endfunction
function automatic logic [31:0] saturating_increment(input logic [31:0] value_i);
@@ -151,7 +156,7 @@ module onchip_ram #(
(mem_axi4.arburst == `AXI4_BURST_TYPE_INCR) ||
((mem_axi4.arburst == `AXI4_BURST_TYPE_WRAP) && wrap_length_legal(
mem_axi4.arlen
))) && !mem_axi4.arlock && (mem_axi4.arsize <= `AXI4_BURST_SIZE_4BYTES) &&
))) && !mem_axi4.arlock && (mem_axi4.arsize <= 3'(BeatSize)) &&
((mem_axi4.araddr & ((32'd1 << mem_axi4.arsize) - 1'b1)) == 32'd0) && !s_ar_last_addr[32] &&
(mem_axi4.araddr[31:12] == s_ar_last_addr[31:12]);
assign s_aw_protocol_legal =
@@ -160,7 +165,7 @@ module onchip_ram #(
(mem_axi4.awburst == `AXI4_BURST_TYPE_INCR) ||
((mem_axi4.awburst == `AXI4_BURST_TYPE_WRAP) && wrap_length_legal(
mem_axi4.awlen
))) && !mem_axi4.awlock && (mem_axi4.awsize <= `AXI4_BURST_SIZE_4BYTES) &&
))) && !mem_axi4.awlock && (mem_axi4.awsize <= 3'(BeatSize)) &&
((mem_axi4.awaddr & ((32'd1 << mem_axi4.awsize) - 1'b1)) == 32'd0) && !s_aw_last_addr[32] &&
(mem_axi4.awaddr[31:12] == s_aw_last_addr[31:12]);
assign s_ar_bounds_legal = Present && (mem_axi4.araddr >= `SOC_ADDR_SRAM_BASE) &&
@@ -176,7 +181,7 @@ module onchip_ram #(
assign mem_axi4.awready = (s_state_q == Idle) && !mem_axi4.arvalid;
assign mem_axi4.wready = (s_state_q == WriteData) || (s_state_q == WriteDrain);
assign mem_axi4.rid = s_id_q;
assign mem_axi4.rdata = (s_read_rsp_q == `AXI4_RESP_OKAY) ? s_mem_rdata : 32'd0;
assign mem_axi4.rdata = (s_read_rsp_q == `AXI4_RESP_OKAY) ? s_mem_rdata : '0;
assign mem_axi4.rresp = s_read_rsp_q;
assign mem_axi4.rlast = s_read_rsp_last_q;
assign mem_axi4.ruser = '0;
@@ -194,8 +199,8 @@ module onchip_ram #(
assign s_read_can_issue = !s_read_pending_q || mem_axi4.rready;
assign s_read_issue = (s_state_q == ReadData) && s_read_issue_valid_q && s_read_can_issue;
assign s_expected_wlast = s_beat_q == s_len_q;
assign s_write_lane_mask = transfer_lane_mask(s_size_q, s_addr_q[1:0]);
assign s_write_strobe_legal = (mem_axi4.wstrb & ~s_write_lane_mask) == 4'd0;
assign s_write_lane_mask = transfer_lane_mask(s_size_q, s_addr_q[2:0]);
assign s_write_strobe_legal = (mem_axi4.wstrb & ~s_write_lane_mask) == '0;
assign s_write_beat_legal = s_write_strobe_legal && (mem_axi4.wlast == s_expected_wlast);
assign s_write_terminal = s_expected_wlast || mem_axi4.wlast;
@@ -203,10 +208,11 @@ module onchip_ram #(
assign s_mem_write = (s_state_q == WriteData) && s_w_accept && s_write_beat_legal &&
(|mem_axi4.wstrb);
assign s_mem_word_addr =
(s_ar_accept && (s_ar_response == `AXI4_RESP_OKAY)) ? mem_axi4.araddr[16:2] :
s_read_issue ? s_read_issue_addr_q[16:2] : s_addr_q[16:2];
(s_ar_accept && (s_ar_response == `AXI4_RESP_OKAY)) ?
15'(mem_axi4.araddr >> BeatSize) :
s_read_issue ? 15'(s_read_issue_addr_q >> BeatSize) : 15'(s_addr_q >> BeatSize);
assign s_mem_wdata = mem_axi4.wdata;
assign s_mem_wstrb = s_mem_write ? mem_axi4.wstrb : 4'd0;
assign s_mem_wstrb = s_mem_write ? mem_axi4.wstrb : '0;
assign s_err_resp_event =
(s_b_accept && (mem_axi4.bresp != `AXI4_RESP_OKAY)) ||
@@ -367,7 +373,7 @@ module onchip_ram #(
end
end
if (Present) begin : gen_memory
if (Present && (DataWidth == 32)) begin : gen_memory
logic [ NumBanks-1:0] s_bank_cs;
logic [ 31:0] s_bank_rdata [NumBanks];
logic [BankIndexWidth-1:0] s_read_bank_q;
@@ -410,17 +416,86 @@ module onchip_ram #(
assign s_mem_rdata = s_bank_rdata[s_read_bank_q];
`ifdef HAVE_SVA
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(s_bank_cs));
`endif
end else if (Present) begin : gen_memory64
localparam int unsigned NumBankPairs = (NumBanks + 1) / 2;
localparam int unsigned PairIndexWidth = (NumBankPairs > 1) ? $clog2(NumBankPairs) : 1;
logic [ NumBankPairs-1:0] s_bank_cs;
logic [ 31:0] s_bank_lo_rdata[NumBankPairs];
logic [ 31:0] s_bank_hi_rdata[NumBankPairs];
logic [PairIndexWidth-1:0] s_read_bank_q;
for (genvar bank = 0; bank < NumBankPairs; bank++) begin : gen_bank
assign s_bank_cs[bank] = (s_mem_read || s_mem_write) &&
(s_mem_word_addr[14:TechnologyAddrWidth] == (15 - TechnologyAddrWidth)'(bank));
`ifdef PDK_ICS55
tc_sram_4096x32 u_ram_lo (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[11:0]),
.data_i(s_mem_wdata[31:0]),
.mask_i(s_mem_wstrb[3:0]),
.wren_i(s_mem_write),
.data_o(s_bank_lo_rdata[bank])
);
tc_sram_4096x32 u_ram_hi (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[11:0]),
.data_i(s_mem_wdata[63:32]),
.mask_i(s_mem_wstrb[7:4]),
.wren_i(s_mem_write),
.data_o(s_bank_hi_rdata[bank])
);
`else
tc_sram_1024x32 u_ram_lo (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[9:0]),
.data_i(s_mem_wdata[31:0]),
.mask_i(s_mem_wstrb[3:0]),
.wren_i(s_mem_write),
.data_o(s_bank_lo_rdata[bank])
);
tc_sram_1024x32 u_ram_hi (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[9:0]),
.data_i(s_mem_wdata[63:32]),
.mask_i(s_mem_wstrb[7:4]),
.wren_i(s_mem_write),
.data_o(s_bank_hi_rdata[bank])
);
`endif
end
dffer #(
.DATA_WIDTH(PairIndexWidth)
) u_read_bank_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_mem_read),
.dat_i (s_mem_word_addr[PairIndexWidth+TechnologyAddrWidth-1:TechnologyAddrWidth]),
.dat_o (s_read_bank_q)
);
assign s_mem_rdata = {s_bank_hi_rdata[s_read_bank_q], s_bank_lo_rdata[s_read_bank_q]};
`ifdef HAVE_SVA
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(s_bank_cs));
`endif
end else begin : gen_no_memory
assign s_mem_rdata = {32{1'b0 & ^{s_mem_read, s_mem_write, s_mem_word_addr,
s_mem_wdata, s_mem_wstrb}}};
assign s_mem_rdata = {DataWidth{1'b0 & ^{s_mem_read, s_mem_write, s_mem_word_addr,
s_mem_wdata, s_mem_wstrb}}};
end
onchip_ram_reg #(
.Present (Present),
.CapacityKiB(CapacityKiB)
.CapacityKiB(CapacityKiB),
.DataBytes (DataBytes)
) u_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
@@ -442,7 +517,7 @@ module onchip_ram #(
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(1)
.DATA_WIDTH(IdWidth)
) u_id_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
@@ -619,9 +694,11 @@ module onchip_ram #(
`ifndef SYNTHESIS
initial begin
if ((CapacityKiB != 4) && (CapacityKiB != 16) && (CapacityKiB != 32) &&
(CapacityKiB != 64) && (CapacityKiB != 128)) begin
$fatal(1, "onchip_ram: CapacityKiB must be 4, 16, 32, 64, or 128");
if (((DataWidth != 32) && (DataWidth != 64)) || (IdWidth < 1) ||
((DataWidth % 8) != 0) ||
((CapacityKiB != 4) && (CapacityKiB != 16) && (CapacityKiB != 32) &&
(CapacityKiB != 64) && (CapacityKiB != 128))) begin
$fatal(1, "onchip_ram: invalid width, ID, or capacity geometry");
end
end
`endif
+3 -2
View File
@@ -12,7 +12,8 @@
module onchip_ram_reg #(
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128
parameter int unsigned CapacityKiB = 128,
parameter int unsigned DataBytes = 4
) (
// verilog_format: off -- preserve the register input grouping
input logic clk_i,
@@ -31,7 +32,7 @@ module onchip_ram_reg #(
localparam int unsigned BankBytes = 4096;
localparam int unsigned BankCount = CapacityKiB / 4;
localparam logic [31:0] Capability =
32'(4 << `APB4_ONCHIP_RAM__CAP_DATA_BYTES_LSB) |
32'(DataBytes << `APB4_ONCHIP_RAM__CAP_DATA_BYTES_LSB) |
32'(16 << `APB4_ONCHIP_RAM__CAP_MAX_BEATS_LSB) |
32'h0000_007E | 32'(Present);
+216
View File
@@ -0,0 +1,216 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module resource_controller #(
parameter int unsigned ResourceCount = 6
) (
// verilog_format: off -- preserve the resource ownership boundary columns
input logic clk_i,
input logic rst_n_i,
input logic [ResourceCount-1:0] idle_i,
input logic [ResourceCount-1:0] block_ack_i,
input logic [ResourceCount-1:0] irq_i,
input logic cache_request_i,
output logic cache_clean_o,
output logic [ResourceCount-1:0][1:0] owner_o,
output logic [ResourceCount-1:0] quiesce_o,
output logic [ResourceCount-1:0] reset_o,
output logic [ResourceCount-1:0] irq_lp_o,
output logic [ResourceCount-1:0] irq_hp_o,
output logic fault_irq_o,
apb4_if.slave apb4
// verilog_format: on
);
localparam logic [31:0] IpId = 32'h5253_4354;
localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam logic [11:0] IpIdOffset = 12'h000;
localparam logic [11:0] IpVersionOffset = 12'h004;
localparam logic [11:0] CapabilityOffset = 12'h008;
localparam logic [11:0] GlobalStatusOffset = 12'h00C;
localparam logic [11:0] CacheControlOffset = 12'h010;
localparam logic [11:0] ResourceBase = 12'h100;
localparam logic [11:0] ResourceStride = 12'h020;
localparam logic [11:0] OwnerOffset = 12'h000;
localparam logic [11:0] ControlOffset = 12'h004;
localparam logic [11:0] StatusOffset = 12'h008;
localparam logic [11:0] FaultOffset = 12'h00C;
localparam logic [11:0] HandoffCountOffset = 12'h010;
localparam logic [1:0] OwnerLp = 2'd0;
localparam logic [1:0] OwnerHp = 2'd1;
logic [ResourceCount-1:0][ 1:0] s_owner_q;
logic [ResourceCount-1:0] s_owner_lock_q;
logic [ResourceCount-1:0] s_quiesce_q;
logic [ResourceCount-1:0] s_reset_q;
logic [ResourceCount-1:0] s_fault_q;
logic [ResourceCount-1:0][15:0] s_handoff_count_q;
logic s_cache_clean_q;
logic s_ready_q;
logic s_resp_err_q;
logic [ 31:0] s_rdata_q;
logic s_request;
logic s_aligned;
logic s_read_valid;
logic s_write_valid;
logic s_write_error;
logic [ 31:0] s_read_data;
logic [ResourceCount-1:0] s_owner_write;
logic [ResourceCount-1:0] s_control_write;
logic [ResourceCount-1:0] s_fault_clear;
logic s_cache_write;
function automatic logic [15:0] saturating_increment(input logic [15:0] value_i);
return (&value_i) ? value_i : value_i + 1'b1;
endfunction
assign s_request = apb4.psel && apb4.penable && !s_ready_q;
assign s_aligned = apb4.paddr[1:0] == 2'b00;
assign apb4.pready = s_ready_q;
assign apb4.pslverr = s_resp_err_q;
assign apb4.prdata = s_rdata_q;
assign owner_o = s_owner_q;
assign quiesce_o = s_quiesce_q;
assign reset_o = s_reset_q;
assign cache_clean_o = s_cache_clean_q;
assign fault_irq_o = |s_fault_q;
always_comb begin
irq_lp_o = '0;
irq_hp_o = '0;
for (int resource = 0; resource < ResourceCount; resource++) begin
irq_lp_o[resource] = irq_i[resource] && (s_owner_q[resource] == OwnerLp) &&
!s_reset_q[resource];
irq_hp_o[resource] = irq_i[resource] && (s_owner_q[resource] == OwnerHp) &&
!s_reset_q[resource];
end
end
always_comb begin
s_read_valid = 1'b1;
s_write_valid = 1'b0;
s_write_error = 1'b0;
s_read_data = 32'd0;
s_owner_write = '0;
s_control_write = '0;
s_fault_clear = '0;
s_cache_write = 1'b0;
unique case (apb4.paddr[11:0])
IpIdOffset: s_read_data = IpId;
IpVersionOffset: s_read_data = IpVersion;
CapabilityOffset: s_read_data = {16'd0, 8'(ResourceCount), 6'd0, 2'd1};
GlobalStatusOffset:
s_read_data = {
{(25 - ResourceCount) {1'b0}}, s_fault_q, 5'd0, cache_request_i, s_cache_clean_q
};
CacheControlOffset: begin
s_read_data = {30'd0, cache_request_i, s_cache_clean_q};
s_write_valid = 1'b1;
s_cache_write = apb4.pwrite;
s_write_error = apb4.pwrite && ((apb4.pstrb != 4'hF) ||
apb4.pwdata[31:1] != 31'd0 ||
(apb4.pwdata[0] && !cache_request_i));
end
default: begin
s_read_valid = 1'b0;
for (int resource = 0; resource < ResourceCount; resource++) begin
if (apb4.paddr[11:0] == ResourceBase + 12'(resource) * ResourceStride + OwnerOffset) begin
s_read_valid = 1'b1;
s_read_data = {23'd0, s_owner_lock_q[resource], 6'd0, s_owner_q[resource]};
s_write_valid = 1'b1;
s_owner_write[resource] = apb4.pwrite;
s_write_error = apb4.pwrite &&
((apb4.pstrb != 4'hF) || (apb4.pwdata[31:9] != 23'd0) ||
(apb4.pwdata[7:2] != 6'd0) || (apb4.pwdata[1:0] > OwnerHp) ||
s_owner_lock_q[resource] || !s_quiesce_q[resource] ||
!block_ack_i[resource] || !idle_i[resource]);
end else if (apb4.paddr[11:0] ==
ResourceBase + 12'(resource) * ResourceStride + ControlOffset) begin
s_read_valid = 1'b1;
s_read_data = {30'd0, s_reset_q[resource], s_quiesce_q[resource]};
s_write_valid = 1'b1;
s_control_write[resource] = apb4.pwrite;
s_write_error = apb4.pwrite && ((apb4.pstrb != 4'hF) || (apb4.pwdata[31:2] != 30'd0));
end else if (apb4.paddr[11:0] ==
ResourceBase + 12'(resource) * ResourceStride + StatusOffset) begin
s_read_valid = 1'b1;
s_read_data = {
24'd0,
block_ack_i[resource],
irq_i[resource],
s_fault_q[resource],
idle_i[resource],
s_reset_q[resource],
s_quiesce_q[resource],
s_owner_q[resource]
};
end else if (apb4.paddr[11:0] ==
ResourceBase + 12'(resource) * ResourceStride + FaultOffset) begin
s_read_valid = 1'b1;
s_read_data = {31'd0, s_fault_q[resource]};
s_write_valid = 1'b1;
s_fault_clear[resource] = apb4.pwrite;
s_write_error = apb4.pwrite && ((apb4.pstrb != 4'hF) || (apb4.pwdata[31:1] != 31'd0));
end else if (apb4.paddr[11:0] ==
ResourceBase + 12'(resource) * ResourceStride + HandoffCountOffset) begin
s_read_valid = 1'b1;
s_read_data = {16'd0, s_handoff_count_q[resource]};
end
end
end
endcase
if (!s_aligned || (apb4.pwrite && !s_write_valid) || (!apb4.pwrite && !s_read_valid)) begin
s_write_error = 1'b1;
end
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_owner_q <= '0;
s_owner_lock_q <= '0;
s_quiesce_q <= '0;
s_reset_q <= '0;
s_fault_q <= '0;
s_handoff_count_q <= '0;
s_cache_clean_q <= 1'b0;
s_ready_q <= 1'b0;
s_resp_err_q <= 1'b0;
s_rdata_q <= '0;
end else begin
s_ready_q <= s_request;
s_resp_err_q <= s_request && s_write_error;
if (s_request && !apb4.pwrite && s_read_valid) s_rdata_q <= s_read_data;
if (!cache_request_i) s_cache_clean_q <= 1'b0;
if (s_request && s_cache_write && !s_write_error) begin
s_cache_clean_q <= apb4.pwdata[0];
end
for (int resource = 0; resource < ResourceCount; resource++) begin
if (s_request && s_owner_write[resource]) begin
if (s_write_error) begin
s_fault_q[resource] <= 1'b1;
end else begin
if (s_owner_q[resource] != apb4.pwdata[1:0]) begin
s_handoff_count_q[resource] <= saturating_increment(s_handoff_count_q[resource]);
end
s_owner_q[resource] <= apb4.pwdata[1:0];
s_owner_lock_q[resource] <= apb4.pwdata[8];
end
end
if (s_request && s_control_write[resource] && !s_write_error) begin
s_quiesce_q[resource] <= apb4.pwdata[0];
s_reset_q[resource] <= apb4.pwdata[1];
end
if (s_request && s_fault_clear[resource] && !s_write_error && apb4.pwdata[0]) begin
s_fault_q[resource] <= 1'b0;
end
end
end
end
`ifndef SYNTHESIS
initial begin
if ((ResourceCount < 1) || (ResourceCount > 16)) begin
$fatal(1, "resource_controller: ResourceCount must be in [1, 16]");
end
end
`endif
endmodule
+185 -91
View File
@@ -61,8 +61,8 @@ module retrosoc (
// verilog_format: off -- preserve reviewed column alignment
// Generated fabric links use the common 32-bit AXI4 contract.
`include "soc_fabric_interfaces.svh"
apb4_if u_sdram_cfg_if (.pclk(clk_lp_i), .presetn(rst_lp_n_i));
apb4_if u_sram_cfg_if (.pclk(clk_lp_i), .presetn(rst_lp_n_i));
apb4_if u_sdram_cfg_if (.pclk(clk_mem_i), .presetn(rst_mem_n_i));
apb4_if u_sram_cfg_if (.pclk(clk_hp_i), .presetn(rst_hp_n_i));
apb4_if u_sdram_cfg_pclk_if (.pclk(clk_pclk_i), .presetn(rst_pclk_n_i));
apb4_if u_sram_cfg_pclk_if (.pclk(clk_pclk_i), .presetn(rst_pclk_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
@@ -101,10 +101,14 @@ module retrosoc (
u_hp_mmio_gated_axi4_if (.aclk(clk_hp_i), .aresetn(rst_hp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_hp_mmio_lp_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(6), .USER_WIDTH(1))
u_data_sram_axi4_if (.aclk(clk_hp_i), .aresetn(rst_hp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_data_sram_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
u_retired_data_sram_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_data_sdram_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
u_data_sdram_axi4_if (.aclk(clk_mem_i), .aresetn(rst_mem_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_retired_data_sdram_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_data_qpi_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
@@ -202,6 +206,10 @@ module retrosoc (
logic s_hp_lifecycle_release_hp;
logic s_hp_lifecycle_block;
logic s_hp_lifecycle_flush;
logic s_hp_cache_req_aon;
logic s_hp_cache_req_pclk;
logic s_hp_cache_clean_pclk;
logic s_hp_cache_clean_aon;
logic s_hp_lifecycle_draining;
logic s_hp_lifecycle_fault;
logic s_hp_block_combined;
@@ -215,6 +223,16 @@ module retrosoc (
logic [31:0] s_ext_h_timeout;
logic s_ext_h_irq_raw;
logic [ 1:0] s_ext_h_owner;
logic [ 5:0][1:0] s_resource_owner;
logic [ 5:0] s_resource_quiesce;
logic [ 5:0] s_resource_reset;
logic [ 5:0] s_resource_idle_hp;
logic [ 5:0] s_resource_idle_pclk;
logic [ 5:0] s_resource_block_ack_hp;
logic [ 5:0] s_resource_block_ack_pclk;
logic [ 4:0] s_resource_irq_raw;
logic [ 4:0] s_resource_irq_lp;
logic [ 4:0] s_resource_irq_hp;
logic [31:0] s_fault_addr_mux;
logic [ 3:0] s_fault_wstrb_mux;
logic s_fault_reserved_mux;
@@ -400,6 +418,7 @@ core_wrapper u_core_wrapper (
axi4_downsizer_64to32 u_hp_mmio_downsizer (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.clear_i(1'b0),
.wide (u_hp_mmio_wide_axi4_if),
.narrow (u_hp_mmio_hp_axi4_if)
);
@@ -443,17 +462,37 @@ core_wrapper u_core_wrapper (
.dat_o({s_hp_release_req_aon, s_hp_debug_reset_req_aon, s_hp_idle_aon, s_hp_flush_busy_aon})
);
hp_lifecycle_controller u_hp_lifecycle_controller (
.clk_i (clk_aon_i),
.rst_n_i (rst_aon_n_i),
.release_req_i (s_hp_release_req_aon && !s_hp_debug_reset_req_aon),
.hp_idle_i (s_hp_idle_aon),
.flush_busy_i (s_hp_flush_busy_aon),
.timeout_i (16'd1024),
.hp_release_o (s_hp_lifecycle_release_aon),
.block_new_o (s_hp_lifecycle_block),
.flush_o (s_hp_lifecycle_flush),
.draining_o (s_hp_lifecycle_draining),
.forced_fault_o(s_hp_lifecycle_fault)
.clk_i (clk_aon_i),
.rst_n_i (rst_aon_n_i),
.release_req_i (s_hp_release_req_aon && !s_hp_debug_reset_req_aon),
.hp_idle_i (s_hp_idle_aon),
.flush_busy_i (s_hp_flush_busy_aon),
.cache_clean_i (s_hp_cache_clean_aon),
.timeout_i (16'd1024),
.hp_release_o (s_hp_lifecycle_release_aon),
.block_new_o (s_hp_lifecycle_block),
.flush_o (s_hp_lifecycle_flush),
.cache_request_o(s_hp_cache_req_aon),
.draining_o (s_hp_lifecycle_draining),
.forced_fault_o (s_hp_lifecycle_fault)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_hp_cache_req_sync (
.clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.dat_i (s_hp_cache_req_aon),
.dat_o (s_hp_cache_req_pclk)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_hp_cache_clean_sync (
.clk_i (clk_aon_i),
.rst_n_i(rst_aon_n_i),
.dat_i (s_hp_cache_clean_pclk),
.dat_o (s_hp_cache_clean_aon)
);
cdc_sync #(
.STAGE (2),
@@ -485,49 +524,75 @@ core_wrapper u_core_wrapper (
);
soc_data_plane u_data_plane (
.clk_lp_i (clk_lp_i),
.rst_lp_n_i (rst_lp_n_i),
.clk_io_i (clk_pclk_i),
.rst_io_n_i (rst_pclk_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.block_new_i (s_hp_block_combined),
.recovery_i (s_hp_lifecycle_block),
.flush_i (s_hp_lifecycle_flush),
.mem_pad_mode_i (s_mem_pad_mode_lp),
.ext_h_block_i (s_ext_h_block),
.ext_h_read_base_i (s_ext_h_read_base),
.ext_h_read_limit_i (s_ext_h_read_limit),
.ext_h_write_base_i (s_ext_h_write_base),
.ext_h_write_limit_i(s_ext_h_write_limit),
.hp_icache_axi4 (u_hp_icache_axi4_if),
.hp_dcache_axi4 (u_hp_dcache_axi4_if),
.dma_axi4 (u_dma_axi4_if),
.sdio0_axi4 (u_sdio0_axi4_if),
.sdio1_axi4 (u_sdio1_axi4_if),
.spisd_axi4 (u_spisd_axi4_if),
.usb2_axi4 (u_usb2_axi4_if),
.lp_data_axi4 (u_mgmt_data_axi4_if),
.ext_h_axi4 (u_ext_h_wide_axi4_if),
.sram_gateway_axi4 (u_data_sram_axi4_if),
.sdram_gateway_axi4 (u_data_sdram_axi4_if),
.qpi_gateway_axi4 (u_data_qpi_axi4_if),
.opi_gateway_axi4 (u_data_opi_axi4_if),
.xpi_gateway_axi4 (u_data_xpi_axi4_if),
.idle_o (s_data_plane_idle),
.flush_busy_o (s_data_plane_flush_busy),
.ext_h_idle_o (s_ext_h_data_idle),
.outstanding_read_o (s_data_plane_outstanding_read),
.outstanding_write_o(s_data_plane_outstanding_write),
.fault_valid_o (s_data_plane_fault_valid),
.fault_master_o (s_data_plane_fault_master),
.fault_target_o (s_data_plane_fault_target),
.fault_addr_o (s_data_plane_fault_addr),
.fault_write_o (s_data_plane_fault_write),
.fault_reason_o (s_data_plane_fault_reason)
.clk_lp_i (clk_lp_i),
.rst_lp_n_i (rst_lp_n_i),
.clk_io_i (clk_pclk_i),
.rst_io_n_i (rst_pclk_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.clk_mem_i (clk_mem_i),
.rst_mem_n_i (rst_mem_n_i),
.block_new_i (s_hp_block_combined),
.recovery_i (s_hp_lifecycle_block),
.flush_i (s_hp_lifecycle_flush),
.resource_block_i (s_resource_quiesce | s_resource_reset),
.mem_pad_mode_i (s_mem_pad_mode_lp),
.ext_h_block_i (s_ext_h_block),
.ext_h_read_base_i (s_ext_h_read_base),
.ext_h_read_limit_i (s_ext_h_read_limit),
.ext_h_write_base_i (s_ext_h_write_base),
.ext_h_write_limit_i (s_ext_h_write_limit),
.hp_icache_axi4 (u_hp_icache_axi4_if),
.hp_dcache_axi4 (u_hp_dcache_axi4_if),
.dma_axi4 (u_dma_axi4_if),
.sdio0_axi4 (u_sdio0_axi4_if),
.sdio1_axi4 (u_sdio1_axi4_if),
.spisd_axi4 (u_spisd_axi4_if),
.usb2_axi4 (u_usb2_axi4_if),
.lp_data_axi4 (u_mgmt_data_axi4_if),
.ext_h_axi4 (u_ext_h_wide_axi4_if),
.sram_gateway_axi4 (u_data_sram_axi4_if),
.sdram_gateway_axi4 (u_data_sdram_axi4_if),
.qpi_gateway_axi4 (u_data_qpi_axi4_if),
.opi_gateway_axi4 (u_data_opi_axi4_if),
.xpi_gateway_axi4 (u_data_xpi_axi4_if),
.idle_o (s_data_plane_idle),
.flush_busy_o (s_data_plane_flush_busy),
.ext_h_idle_o (s_ext_h_data_idle),
.resource_idle_o (s_resource_idle_hp),
.resource_block_ack_o(s_resource_block_ack_hp),
.outstanding_read_o (s_data_plane_outstanding_read),
.outstanding_write_o (s_data_plane_outstanding_write),
.fault_valid_o (s_data_plane_fault_valid),
.fault_master_o (s_data_plane_fault_master),
.fault_target_o (s_data_plane_fault_target),
.fault_addr_o (s_data_plane_fault_addr),
.fault_write_o (s_data_plane_fault_write),
.fault_reason_o (s_data_plane_fault_reason)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(6)
) u_resource_idle_sync (
.clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.dat_i (s_resource_idle_hp),
.dat_o (s_resource_idle_pclk)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(6)
) u_resource_block_ack_sync (
.clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.dat_i (s_resource_block_ack_hp),
.dat_o (s_resource_block_ack_pclk)
);
axi4_master_idle u_idle_master (.axi4(u_idle_axi4_if));
axi4_master_idle u_retired_sram_master (.axi4(u_retired_data_sram_axi4_if));
axi4_master_idle u_retired_sdram_master (.axi4(u_retired_data_sdram_axi4_if));
axi4_async_bridge #(
.DataWidth(32),
@@ -602,16 +667,16 @@ core_wrapper u_core_wrapper (
apb4_async_bridge u_sdram_cfg_cdc (
.src_clk_i (clk_pclk_i),
.src_rst_n_i(rst_pclk_n_i),
.dst_clk_i (clk_lp_i),
.dst_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i(rst_mem_n_i),
.src_apb4 (u_sdram_cfg_pclk_if),
.dst_apb4 (u_sdram_cfg_if)
);
apb4_async_bridge u_sram_cfg_cdc (
.src_clk_i (clk_pclk_i),
.src_rst_n_i(rst_pclk_n_i),
.dst_clk_i (clk_lp_i),
.dst_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i(rst_hp_n_i),
.src_apb4 (u_sram_cfg_pclk_if),
.dst_apb4 (u_sram_cfg_if)
);
@@ -646,22 +711,35 @@ core_wrapper u_core_wrapper (
onchip_ram #(
.Present (SramPresent),
.CapacityKiB(`SOC_ADDR_SRAM_SIZE >> 10)
.CapacityKiB(`SOC_ADDR_SRAM_SIZE >> 10),
.DataWidth (64),
.IdWidth (6)
) u_onchip_ram (
.clk_i (clk_lp_i),
.rst_n_i (rst_lp_n_i),
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.perf_enable_i(s_perf_en),
.perf_clear_i (s_perf_clear),
.mem_axi4 (u_sram_axi4_if),
.mem_axi4 (u_data_sram_axi4_if),
.cfg_apb4 (u_sram_cfg_if)
);
axi4_error_slave u_retired_sram_target (
.clk_i (clk_lp_i),
.rst_n_i(rst_lp_n_i),
.axi4 (u_sram_axi4_if)
);
axi4_error_slave u_retired_sdram_target (
.clk_i (clk_lp_i),
.rst_n_i(rst_lp_n_i),
.axi4 (u_sdram_axi4_if)
);
axi4_bus u_bus (
.clk_i (clk_lp_i),
.rst_n_i (rst_lp_n_i),
.mgmt_axi4 (u_mgmt_control_axi4_if),
.user_axi4 (u_data_sram_axi4_if),
.dma_axi4 (u_data_sdram_axi4_if),
.user_axi4 (u_retired_data_sram_axi4_if),
.dma_axi4 (u_retired_data_sdram_axi4_if),
.sdio0_axi4 (u_data_qpi_axi4_if),
.sdio1_axi4 (u_data_xpi_axi4_if),
.usb2_axi4 (u_hp_mmio_lp_axi4_if),
@@ -711,6 +789,8 @@ core_wrapper u_core_wrapper (
.debug_halted_i (s_mgmt_debug_halted),
.timebase_tick_i (timebase_tick_i),
.ext_h_hp_irq_i ((s_ext_h_owner == 2'd1) ? s_ext_h_irq_raw : 1'b0),
.resource_irq_lp_i (s_resource_irq_lp),
.resource_irq_hp_i (s_resource_irq_hp),
.cfg_axi4 (u_cfg_pclk_axi4_if),
.dma_axi4 (u_dma_axi4_if),
.sdio0_axi4 (u_sdio0_axi4_if),
@@ -750,44 +830,55 @@ core_wrapper u_core_wrapper (
.hp_software_irq_o (s_hp_software_irq),
.hp_machine_external_irq_o (s_hp_machine_external_irq),
.hp_supervisor_external_irq_o(s_hp_supervisor_external_irq),
.resource_irq_raw_o (s_resource_irq_raw),
.irq_o (s_apb4_periph_irq)
);
axi4_sdram u_axi4_sdram (
.clk_i (clk_lp_i),
.rst_n_i (rst_lp_n_i),
.axi4 (u_sdram_axi4_if),
.clk_i (clk_mem_i),
.rst_n_i (rst_mem_n_i),
.axi4 (u_data_sdram_axi4_if),
.cfg_apb4(u_sdram_cfg_if),
.sdram (sdram)
);
apb4_system u_apb4_system (
.clk_i (clk_pclk_i),
.rst_n_i (rst_pclk_n_i),
.clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i),
.debug_halted_i (s_mgmt_debug_halted),
.ext_h_data_idle_i (s_ext_h_data_idle),
.ext_h_axi4 (u_ext_h_wide_axi4_if),
.axi4 (u_system_pclk_axi4_if),
.pwm (u_pwm_if),
.ps2 (u_ps2_if),
.ip_sel_i (u_sysctrl_if.ip_sel_o),
.user_gpio (u_user_gpio_if),
.rtc_wake_o (s_rtc_wake),
.wdg_reset_req_o (wdg_reset_req_o),
.ext_h_block_o (s_ext_h_block),
.ext_h_read_base_o (s_ext_h_read_base),
.ext_h_read_limit_o (s_ext_h_read_limit),
.ext_h_write_base_o (s_ext_h_write_base),
.clk_i(clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.clk_aud_i(clk_aud_i),
.rst_aud_n_i(rst_aud_n_i),
.debug_halted_i(s_mgmt_debug_halted),
.ext_h_data_idle_i(s_ext_h_data_idle),
.resource_idle_i({s_ext_h_data_idle && s_resource_idle_pclk[5], s_resource_idle_pclk[4:0]}),
.resource_block_ack_i(s_resource_block_ack_pclk),
.resource_irq_i(s_resource_irq_raw),
.cache_request_i(s_hp_cache_req_pclk),
.ext_h_axi4(u_ext_h_wide_axi4_if),
.axi4(u_system_pclk_axi4_if),
.pwm(u_pwm_if),
.ps2(u_ps2_if),
.ip_sel_i(u_sysctrl_if.ip_sel_o),
.user_gpio(u_user_gpio_if),
.rtc_wake_o(s_rtc_wake),
.wdg_reset_req_o(wdg_reset_req_o),
.ext_h_block_o(s_ext_h_block),
.ext_h_read_base_o(s_ext_h_read_base),
.ext_h_read_limit_o(s_ext_h_read_limit),
.ext_h_write_base_o(s_ext_h_write_base),
.ext_h_write_limit_o(s_ext_h_write_limit),
.ext_h_timeout_o (s_ext_h_timeout),
.ext_h_irq_raw_o (s_ext_h_irq_raw),
.ext_h_owner_o (s_ext_h_owner),
.irq_o (s_apb4_system_irq)
.ext_h_timeout_o(s_ext_h_timeout),
.ext_h_irq_raw_o(s_ext_h_irq_raw),
.ext_h_owner_o(s_ext_h_owner),
.cache_clean_o(s_hp_cache_clean_pclk),
.resource_owner_o(s_resource_owner),
.resource_quiesce_o(s_resource_quiesce),
.resource_reset_o(s_resource_reset),
.resource_irq_lp_o(s_resource_irq_lp),
.resource_irq_hp_o(s_resource_irq_hp),
.irq_o(s_apb4_system_irq)
);
logic [31:0] s_unused_domain_inputs;
logic [34:0] s_unused_domain_inputs;
assign s_unused_domain_inputs = {
clk_pclk_i,
rst_pclk_n_i,
@@ -801,6 +892,9 @@ core_wrapper u_core_wrapper (
s_mgmt_router_idle,
s_hp_lifecycle_draining,
s_hp_lifecycle_fault,
^s_resource_owner,
^s_resource_quiesce,
^s_resource_reset,
^unused_cdc_clear_busy,
^unused_cdc_epoch,
^s_ext_h_timeout
+214 -50
View File
@@ -9,9 +9,12 @@ module soc_data_plane (
input logic rst_io_n_i,
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic clk_mem_i,
input logic rst_mem_n_i,
input logic block_new_i,
input logic recovery_i,
input logic flush_i,
input logic [5:0] resource_block_i,
input logic [1:0] mem_pad_mode_i,
input logic ext_h_block_i,
input logic [31:0] ext_h_read_base_i,
@@ -35,6 +38,8 @@ module soc_data_plane (
output logic idle_o,
output logic flush_busy_o,
output logic ext_h_idle_o,
output logic [5:0] resource_idle_o,
output logic [5:0] resource_block_ack_o,
output logic [7:0] outstanding_read_o,
output logic [7:0] outstanding_write_o,
output logic fault_valid_o,
@@ -47,18 +52,37 @@ module soc_data_plane (
);
localparam int unsigned NumIoMasters = 3;
localparam int unsigned NumMemoryTargets = 5;
localparam logic [3:0] FaultTimeout = 4'd5;
logic s_block_new_hp;
logic [ 1:0] s_mem_pad_mode_hp;
logic [ NumIoMasters-1:0] s_io_clear_busy;
logic [NumMemoryTargets-1:0] s_target_clear_busy;
logic s_lp_clear_busy;
logic s_ext_clear_busy;
logic [ NumIoMasters-1:0][7:0] unused_io_epoch;
logic [NumMemoryTargets-1:0][7:0] unused_target_epoch;
logic [ 7:0] unused_lp_epoch;
logic [ 7:0] unused_ext_epoch;
logic [ 2:0] s_unused_epoch;
logic s_block_new_hp;
logic [ 1:0] s_mem_pad_mode_hp;
logic [ NumIoMasters-1:0] s_io_clear_busy;
logic [NumMemoryTargets-1:0] s_target_clear_busy;
logic [NumMemoryTargets-1:0] s_guard_clear_busy;
logic [NumMemoryTargets-1:0] s_guard_abort;
logic [NumMemoryTargets-1:0] s_guard_abort_done;
logic [NumMemoryTargets-1:0] s_guard_abort_seen_q;
logic [NumMemoryTargets-1:0] s_guard_timeout_valid;
logic [NumMemoryTargets-1:0] s_guard_timeout_write;
logic [NumMemoryTargets-1:0][ 5:0] s_guard_timeout_id;
logic [NumMemoryTargets-1:0][31:0] s_guard_timeout_addr;
logic s_sdram_abort_mem;
logic s_crossbar_fault_valid;
logic [ 2:0] s_crossbar_fault_master;
logic [ 2:0] s_crossbar_fault_target;
logic [ 31:0] s_crossbar_fault_addr;
logic s_crossbar_fault_write;
logic [ 3:0] s_crossbar_fault_reason;
logic s_lp_clear_busy;
logic s_ext_clear_busy;
logic [ NumIoMasters-1:0][ 7:0] unused_io_epoch;
logic [NumMemoryTargets-1:0][ 7:0] unused_target_epoch;
logic [ 7:0] unused_lp_epoch;
logic [ 7:0] unused_ext_epoch;
logic [ 2:0] s_unused_epoch;
logic [ 5:0] s_resource_block_hp;
logic [ 7:0] s_master_idle;
logic [ 7:0] s_master_block;
axi4_if #(
.ADDR_WIDTH(32),
@@ -74,10 +98,28 @@ module soc_data_plane (
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) u_target_axi4[6] (
) u_crossbar_target_axi4[6] (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) u_target_axi4[NumMemoryTargets] (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) u_sdram_mem_axi4 (
.aclk (clk_mem_i),
.aresetn(rst_mem_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
@@ -196,6 +238,33 @@ module soc_data_plane (
.dat_i (mem_pad_mode_i),
.dat_o (s_mem_pad_mode_hp)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(6)
) u_resource_block_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (resource_block_i),
.dat_o (s_resource_block_hp)
);
assign s_master_block = {
s_resource_block_hp[5],
2'b00,
s_resource_block_hp[4] || s_resource_block_hp[3],
s_resource_block_hp[2] || s_resource_block_hp[1],
s_resource_block_hp[0],
2'b00
};
assign resource_idle_o = {
s_master_idle[7],
s_master_idle[4],
s_master_idle[4],
s_master_idle[3],
s_master_idle[3],
s_master_idle[2]
};
assign resource_block_ack_o = s_resource_block_hp;
axi4_id_prefix #(
.MasterIndex(3'd0)
@@ -327,6 +396,7 @@ module soc_data_plane (
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.block_new_i (s_block_new_hp),
.master_block_i (s_master_block),
.recovery_i (recovery_i),
.mem_pad_mode_i (s_mem_pad_mode_hp),
.ext_h_read_base_i (ext_h_read_base_i),
@@ -334,41 +404,106 @@ module soc_data_plane (
.ext_h_write_base_i (ext_h_write_base_i),
.ext_h_write_limit_i(ext_h_write_limit_i),
.masters (u_master_axi4),
.targets (u_target_axi4),
.targets (u_crossbar_target_axi4),
.idle_o (idle_o),
.master_idle_o (s_master_idle),
.outstanding_read_o (outstanding_read_o),
.outstanding_write_o(outstanding_write_o),
.fault_valid_o (fault_valid_o),
.fault_master_o (fault_master_o),
.fault_target_o (fault_target_o),
.fault_addr_o (fault_addr_o),
.fault_write_o (fault_write_o),
.fault_reason_o (fault_reason_o)
.fault_valid_o (s_crossbar_fault_valid),
.fault_master_o (s_crossbar_fault_master),
.fault_target_o (s_crossbar_fault_target),
.fault_addr_o (s_crossbar_fault_addr),
.fault_write_o (s_crossbar_fault_write),
.fault_reason_o (s_crossbar_fault_reason)
);
for (genvar target = 0; target < NumMemoryTargets; target++) begin : gen_memory_target
axi4_downsizer_64to32 #(
.WideIdWidth(6)
) u_target_downsizer (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.wide (u_target_axi4[target]),
.narrow (u_target_hp_axi4[target])
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_target_cdc (
.src_clk_i (clk_hp_i),
.src_rst_n_i (rst_hp_n_i),
.dst_clk_i (clk_lp_i),
.dst_rst_n_i (rst_lp_n_i),
.clear_i (flush_i),
.clear_busy_o(s_target_clear_busy[target]),
.epoch_o (unused_target_epoch[target]),
.src_axi4 (u_target_hp_axi4[target]),
.dst_axi4 (u_gateway_lp_axi4[target])
localparam logic [31:0] TargetTimeout = (target == 0) ? 32'd256 :
(target == 1) ? 32'd8192 : 32'd65535;
axi4_target_guard #(
.ReadDepth (target < 2 ? 4 : 2),
.WriteDepth(2)
) u_target_guard (
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.clear_i (flush_i),
.timeout_i (TargetTimeout),
.clear_busy_o (s_guard_clear_busy[target]),
.abort_o (s_guard_abort[target]),
.abort_done_i (s_guard_abort_done[target]),
.timeout_valid_o(s_guard_timeout_valid[target]),
.timeout_write_o(s_guard_timeout_write[target]),
.timeout_id_o (s_guard_timeout_id[target]),
.timeout_addr_o (s_guard_timeout_addr[target]),
.source (u_crossbar_target_axi4[target]),
.sink (u_target_axi4[target])
);
if (target == 0) begin : gen_native_sram
axi4_connector u_sram_gateway_connector (
.source(u_target_axi4[target]),
.sink (sram_gateway_axi4)
);
assign s_target_clear_busy[target] = 1'b0;
assign unused_target_epoch[target] = 8'd0;
end else if (target == 1) begin : gen_native_sdram
axi4_async_bridge #(
.DataWidth(64),
.IdWidth (6)
) u_target_cdc (
.src_clk_i (clk_hp_i),
.src_rst_n_i (rst_hp_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i (rst_mem_n_i),
.clear_i (flush_i || s_guard_abort[target]),
.clear_busy_o(s_target_clear_busy[target]),
.epoch_o (unused_target_epoch[target]),
.src_axi4 (u_target_axi4[target]),
.dst_axi4 (u_sdram_mem_axi4)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_abort_sync (
.clk_i (clk_mem_i),
.rst_n_i(rst_mem_n_i),
.dat_i (s_guard_abort[target]),
.dat_o (s_sdram_abort_mem)
);
axi4_downsizer_64to32 #(
.WideIdWidth(6)
) u_target_downsizer (
.clk_i (clk_mem_i),
.rst_n_i(rst_mem_n_i),
.clear_i(s_sdram_abort_mem),
.wide (u_sdram_mem_axi4),
.narrow (sdram_gateway_axi4)
);
end else begin : gen_legacy_memory
axi4_downsizer_64to32 #(
.WideIdWidth(6)
) u_target_downsizer (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.clear_i(s_guard_abort[target]),
.wide (u_target_axi4[target]),
.narrow (u_target_hp_axi4[target])
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_target_cdc (
.src_clk_i (clk_hp_i),
.src_rst_n_i (rst_hp_n_i),
.dst_clk_i (clk_lp_i),
.dst_rst_n_i (rst_lp_n_i),
.clear_i (flush_i || s_guard_abort[target]),
.clear_busy_o(s_target_clear_busy[target]),
.epoch_o (unused_target_epoch[target]),
.src_axi4 (u_target_hp_axi4[target]),
.dst_axi4 (u_gateway_lp_axi4[target])
);
end
end
axi4_error_slave #(
@@ -377,23 +512,52 @@ module soc_data_plane (
) u_data_error_slave (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.axi4 (u_target_axi4[5])
.axi4 (u_crossbar_target_axi4[5])
);
for (genvar target = 0; target < NumMemoryTargets; target++) begin : gen_abort_handshake
assign s_guard_abort_done[target] = (target == 0) ? s_guard_abort[target] :
s_guard_abort[target] && s_guard_abort_seen_q[target] &&
!s_target_clear_busy[target];
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_guard_abort_seen_q[target] <= 1'b0;
end else if (!s_guard_abort[target]) begin
s_guard_abort_seen_q[target] <= 1'b0;
end else if (s_target_clear_busy[target]) begin
s_guard_abort_seen_q[target] <= 1'b1;
end
end
end
always_comb begin
fault_valid_o = s_crossbar_fault_valid;
fault_master_o = s_crossbar_fault_master;
fault_target_o = s_crossbar_fault_target;
fault_addr_o = s_crossbar_fault_addr;
fault_write_o = s_crossbar_fault_write;
fault_reason_o = s_crossbar_fault_reason;
if (!s_crossbar_fault_valid) begin
for (int target = NumMemoryTargets - 1; target >= 0; target--) begin
if (s_guard_timeout_valid[target]) begin
fault_valid_o = 1'b1;
fault_master_o = s_guard_timeout_id[target][5:3];
fault_target_o = 3'(target);
fault_addr_o = s_guard_timeout_addr[target];
fault_write_o = s_guard_timeout_write[target];
fault_reason_o = FaultTimeout;
end
end
end
end
assign flush_busy_o = (|s_io_clear_busy) || (|s_target_clear_busy) ||
s_lp_clear_busy || s_ext_clear_busy;
(|s_guard_clear_busy) || s_lp_clear_busy || s_ext_clear_busy;
assign s_unused_epoch = {
^unused_io_epoch, ^unused_target_epoch, ^{unused_lp_epoch, unused_ext_epoch}
};
axi4_connector u_sram_gateway_connector (
.source(u_gateway_lp_axi4[0]),
.sink (sram_gateway_axi4)
);
axi4_connector u_sdram_gateway_connector (
.source(u_gateway_lp_axi4[1]),
.sink (sdram_gateway_axi4)
);
axi4_connector u_qpi_gateway_connector (
.source(u_gateway_lp_axi4[2]),
.sink (qpi_gateway_axi4)
+2
View File
@@ -285,6 +285,7 @@
"axi4_async_bridge.sv",
"axi4_connector.sv",
"axi4_data_crossbar.sv",
"axi4_target_guard.sv",
"axi4_downsizer_64to32.sv",
"axi4_error_slave.sv",
"axi4_id_prefix.sv",
@@ -314,6 +315,7 @@
"onchip_ram.sv",
"pll_rcu_controller.sv",
"rcu.sv",
"resource_controller.sv",
"retrosoc.sv",
"retrosoc_asic.sv",
"retrosoc_core.sv",
+1 -1
View File
@@ -142,7 +142,7 @@ def build(args: argparse.Namespace) -> None:
f"PLATFORM_DIR={external / 'opensbi'}",
"PLATFORM_RISCV_XLEN=32",
"PLATFORM_RISCV_ABI=ilp32d",
"PLATFORM_RISCV_ISA=rv32imafdc_zicsr_zifencei",
"PLATFORM_RISCV_ISA=rv32imafdc_zicbom_zicsr_zifencei",
"FW_TEXT_START=0x38000000",
"FW_JUMP=y",
"FW_JUMP_ADDR=0x38400000",
+1 -1
View File
@@ -18,7 +18,7 @@ def build(args: argparse.Namespace) -> None:
subprocess.run(
[
compiler,
"-march=rv32imafdc_zicsr_zifencei",
"-march=rv32imafdc_zicbom_zicsr_zifencei",
"-mabi=ilp32d",
"-nostdlib",
"-nostartfiles",
+1 -1
View File
@@ -86,7 +86,7 @@ def generate(args: argparse.Namespace) -> None:
manifest = {
"schema_version": 1,
"module": GENERATED_MODULE,
"configuration": "rv32imafdc_max",
"configuration": "rv32imafdc_zicbom_max",
"vexiiriscv_revision": actual,
"source_status": git_status(source),
"submodules": submodules,
+14 -5
View File
@@ -16,16 +16,25 @@ from scripts.check_c_warnings import self_owned_warnings # noqa: E402
CI_SMOKE_APP_VALUE = "APP=ci_smoke"
CI_SMOKE_SIM_VALUES = (
CI_SMOKE_APP_VALUE,
"LINK_TYPE=ld2_all_sram",
CI_SMOKE_VERILATOR_VALUES = (
"VERILATOR_SIM_ARGS=--fast-flash",
"SOC_SIM_TIME=360",
"SIMU=VERILATOR",
"HAVE_SVA=YES",
"firmware",
"sim",
)
CI_SMOKE_SIM_VALUES = (
CI_SMOKE_APP_VALUE,
"LINK_TYPE=ld2_all_sram",
"SOC_SIM_TIME=360",
*CI_SMOKE_VERILATOR_VALUES,
)
CI_SMOKE_SDRAM_SIM_VALUES = (
CI_SMOKE_APP_VALUE,
"LINK_TYPE=ld2_sdram",
"SOC_SIM_TIME=600",
*CI_SMOKE_VERILATOR_VALUES,
)
RTL_LINT_VALUES = ("SIMU=VERILATOR", "HAVE_SVA=YES", "rtl-lint")
RTL_LINT_OBSERVATION_VALUES = (
"SIMU=VERILATOR",
@@ -106,7 +115,7 @@ def pdk_pr_commands(profile: str) -> tuple[tuple[str, tuple[str, ...]], ...]:
verilator_values = (
CI_SMOKE_SIM_VALUES
if profile == "configs/ci/ihp130.mk"
else (CI_SMOKE_APP_VALUE, "SIMU=VERILATOR", "HAVE_SVA=YES", "firmware", "sim")
else CI_SMOKE_SDRAM_SIM_VALUES
)
return (
(profile, RTL_LINT_VALUES),
+1
View File
@@ -34,6 +34,7 @@ TEST_SOURCES = (
"crt/src/hal/dma_math.c",
"crt/src/hal/sysctrl.c",
"crt/src/hal/clock.c",
"crt/src/hal/resource.c",
"crt/src/hal/extension.c",
"crt/src/hal/user_ip.c",
"rtl/managed/clusterip/ps2/sw/src/ps2.c",
+1 -1
View File
@@ -20,7 +20,7 @@ object GenerateRetroSocHp extends App {
param.physicalWidth = 32
param.resetVector = 0x38000000L
param.asidWidth = 9
param.addISA("m", "a", "f", "d", "c", "s", "u", "zicntr", "zihpm")
param.addISA("m", "a", "f", "d", "c", "s", "u", "zicbom", "zicntr", "zihpm")
param.fixIsaParams()
param.decoders = 2
+18
View File
@@ -14,6 +14,7 @@
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/i2s.h>
#include <retrosoc/hal/psram.h>
#include <retrosoc/hal/resource.h>
#include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/sdio.h>
#include <retrosoc/hal/spisd.h>
@@ -846,6 +847,22 @@ static int test_extension_validation(void) {
return 0;
}
static int test_resource_validation(void) {
rs_resource_status_t status;
if ((rs_resource_get_status((rs_resource_t)RS_RESOURCE_COUNT, &status) != RS_EINVAL) ||
(rs_resource_get_status(RS_RESOURCE_DMA, NULL) != RS_EINVAL) ||
(rs_resource_set_owner((rs_resource_t)RS_RESOURCE_COUNT, RS_RESOURCE_OWNER_LP, false) !=
RS_EINVAL) ||
(rs_resource_set_owner(RS_RESOURCE_DMA, (rs_resource_owner_t)2, false) != RS_EINVAL) ||
(rs_resource_set_lifecycle((rs_resource_t)RS_RESOURCE_COUNT, false, false) != RS_EINVAL) ||
(rs_resource_clear_fault((rs_resource_t)RS_RESOURCE_COUNT) != RS_EINVAL) ||
(rs_resource_get_cache_status(NULL) != RS_EINVAL)) {
return 1;
}
return 0;
}
static int test_ps2_decoders(void) {
ps2_keyboard_decoder_t keyboard;
ps2_mouse_decoder_t mouse;
@@ -935,6 +952,7 @@ int main(void) {
test_opipsram_helpers(),
test_user_ip_validation(),
test_extension_validation(),
test_resource_validation(),
test_ps2_decoders(),
test_wav_parser(),
test_video_parser(),
+45
View File
@@ -7,6 +7,8 @@ module axi4_data_crossbar_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic idle_o;
logic [ 7:0] master_block_i = '0;
logic [ 7:0] master_idle_o;
logic [ 7:0] outstanding_read_o;
logic [ 7:0] outstanding_write_o;
logic fault_valid_o;
@@ -46,6 +48,7 @@ module axi4_data_crossbar_tb;
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.block_new_i (1'b0),
.master_block_i (master_block_i),
.recovery_i (1'b0),
.mem_pad_mode_i (2'd1),
.ext_h_read_base_i (32'h3000_0000),
@@ -55,6 +58,7 @@ module axi4_data_crossbar_tb;
.masters (masters),
.targets (targets),
.idle_o (idle_o),
.master_idle_o (master_idle_o),
.outstanding_read_o (outstanding_read_o),
.outstanding_write_o(outstanding_write_o),
.fault_valid_o (fault_valid_o),
@@ -172,6 +176,18 @@ module axi4_data_crossbar_tb;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
@(negedge clk_i);
master_block_i[1] = 1'b1;
masters[1].arid = 6'b001_111;
masters[1].araddr = 32'h3000_0000;
masters[1].arvalid = 1'b1;
#1;
if (masters[1].arready || master_idle_o[1]) begin
$fatal(1, "blocked master was accepted or reported idle with VALID held");
end
masters[1].arvalid = 1'b0;
master_block_i[1] = 1'b0;
issue_master1_read(6'b001_000, 32'h3000_0000);
issue_master1_read(6'b001_001, 32'h3800_0000);
if (outstanding_read_o != 8'd2 || idle_o) begin
@@ -201,6 +217,35 @@ module axi4_data_crossbar_tb;
$fatal(1, "read completion did not drain the crossbar");
end
issue_master1_read(6'b001_010, 32'h3000_0040);
issue_master1_read(6'b001_011, 32'h3000_0080);
if (outstanding_read_o != 8'd2) begin
$fatal(1, "same-target reads were not tracked concurrently");
end
fork
return_target0_read(6'b001_011, 64'h3333_3333_3333_3333);
begin
wait (masters[1].rvalid);
if ((masters[1].rid != 6'b001_011) || (masters[1].rdata != 64'h3333_3333_3333_3333)) begin
$fatal(1, "same-target out-of-order response was misrouted");
end
end
join
@(posedge clk_i);
fork
return_target0_read(6'b001_010, 64'h4444_4444_4444_4444);
begin
wait (masters[1].rvalid);
if ((masters[1].rid != 6'b001_010) || (masters[1].rdata != 64'h4444_4444_4444_4444)) begin
$fatal(1, "same-target older response was misrouted");
end
end
join
@(negedge clk_i);
if (!idle_o || (outstanding_read_o != 8'd0)) begin
$fatal(1, "same-target reads did not drain the crossbar");
end
@(negedge clk_i);
masters[6].arid = 6'b110_000;
masters[6].araddr = 32'h3000_0000;
+1
View File
@@ -30,6 +30,7 @@ module axi4_downsizer_64to32_tb;
axi4_downsizer_64to32 u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.clear_i(1'b0),
.wide (wide),
.narrow (narrow)
);
+173
View File
@@ -0,0 +1,173 @@
`timescale 1ns / 1ps
module axi4_target_guard_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic abort_o;
logic timeout_valid_o;
logic timeout_write_o;
logic [ 5:0] timeout_id_o;
logic [31:0] timeout_addr_o;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) source (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) sink (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
axi4_target_guard #(
.ReadDepth (4),
.WriteDepth(2)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clear_i (1'b0),
.timeout_i (32'd4),
.clear_busy_o (),
.abort_o (abort_o),
.abort_done_i (abort_o),
.timeout_valid_o(timeout_valid_o),
.timeout_write_o(timeout_write_o),
.timeout_id_o (timeout_id_o),
.timeout_addr_o (timeout_addr_o),
.source (source),
.sink (sink)
);
task automatic issue_read(input logic [5:0] id, input logic [7:0] len, input logic [31:0] addr);
begin
@(negedge clk_i);
source.arid = id;
source.arlen = len;
source.araddr = addr;
source.arvalid = 1'b1;
do @(posedge clk_i); while (!source.arready);
@(negedge clk_i);
source.arvalid = 1'b0;
end
endtask
task automatic expect_error_read(input logic [5:0] id, input int unsigned beats);
begin
for (int beat = 0; beat < beats; beat++) begin
do @(posedge clk_i); while (!source.rvalid);
if ((source.rid != id) || (source.rresp != 2'b10) ||
(source.rlast != (beat == beats - 1))) begin
$fatal(1, "target guard synthetic read response mismatch");
end
end
end
endtask
initial begin
source.awid = '0;
source.awaddr = '0;
source.awlen = '0;
source.awsize = 3'd3;
source.awburst = 2'b01;
source.awlock = 1'b0;
source.awcache = '0;
source.awprot = '0;
source.awqos = '0;
source.awregion = '0;
source.awuser = '0;
source.awvalid = 1'b0;
source.wdata = '0;
source.wstrb = '0;
source.wlast = 1'b1;
source.wuser = '0;
source.wvalid = 1'b0;
source.bready = 1'b1;
source.arid = '0;
source.araddr = '0;
source.arlen = '0;
source.arsize = 3'd3;
source.arburst = 2'b01;
source.arlock = 1'b0;
source.arcache = '0;
source.arprot = '0;
source.arqos = '0;
source.arregion = '0;
source.aruser = '0;
source.arvalid = 1'b0;
source.rready = 1'b1;
sink.awready = 1'b1;
sink.wready = 1'b1;
sink.bid = '0;
sink.bresp = '0;
sink.buser = '0;
sink.bvalid = 1'b0;
sink.arready = 1'b1;
sink.rid = '0;
sink.rdata = '0;
sink.rresp = '0;
sink.rlast = 1'b0;
sink.ruser = '0;
sink.rvalid = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
issue_read(6'h09, 8'd1, 32'h3800_0040);
wait (timeout_valid_o);
if (timeout_write_o || (timeout_id_o != 6'h09) || (timeout_addr_o != 32'h3800_0040)) begin
$fatal(1, "target guard read timeout attribution mismatch");
end
expect_error_read(6'h09, 2);
fork
begin
issue_read(6'h0A, 8'd0, 32'h3800_0080);
expect_error_read(6'h0A, 1);
end
begin
repeat (12) begin
@(posedge clk_i);
if (sink.arvalid) $fatal(1, "isolated target accepted a new read");
end
end
join
@(negedge clk_i);
source.awid = 6'h0B;
source.awaddr = 32'h3800_0100;
source.awvalid = 1'b1;
do @(posedge clk_i); while (!source.awready);
@(negedge clk_i);
source.awvalid = 1'b0;
source.wdata = 64'h0123_4567_89AB_CDEF;
source.wstrb = 8'hFF;
source.wvalid = 1'b1;
do @(posedge clk_i); while (!source.wready);
@(negedge clk_i);
source.wvalid = 1'b0;
do @(posedge clk_i); while (!source.bvalid);
if ((source.bid != 6'h0B) || (source.bresp != 2'b10) || sink.awvalid || sink.wvalid) begin
$fatal(1, "isolated target write termination mismatch");
end
$display("AXI4 target guard timeout and isolation test passed");
$finish;
end
initial begin
repeat (200) @(posedge clk_i);
$fatal(1, "AXI4 target guard test timed out");
end
endmodule
+23 -11
View File
@@ -6,26 +6,30 @@ module hp_lifecycle_controller_tb;
logic release_req_i = 1'b0;
logic hp_idle_i = 1'b0;
logic flush_busy_i = 1'b0;
logic cache_clean_i = 1'b0;
logic hp_release_o;
logic block_new_o;
logic flush_o;
logic cache_request_o;
logic draining_o;
logic forced_fault_o;
always #5 clk_i = ~clk_i;
hp_lifecycle_controller u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.release_req_i (release_req_i),
.hp_idle_i (hp_idle_i),
.flush_busy_i (flush_busy_i),
.timeout_i (16'd4),
.hp_release_o (hp_release_o),
.block_new_o (block_new_o),
.flush_o (flush_o),
.draining_o (draining_o),
.forced_fault_o(forced_fault_o)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.release_req_i (release_req_i),
.hp_idle_i (hp_idle_i),
.flush_busy_i (flush_busy_i),
.cache_clean_i (cache_clean_i),
.timeout_i (16'd4),
.hp_release_o (hp_release_o),
.block_new_o (block_new_o),
.flush_o (flush_o),
.cache_request_o(cache_request_o),
.draining_o (draining_o),
.forced_fault_o (forced_fault_o)
);
task automatic complete_flush;
@@ -50,6 +54,14 @@ module hp_lifecycle_controller_tb;
@(negedge clk_i);
release_req_i = 1'b0;
wait (cache_request_o);
if (block_new_o || !hp_release_o) begin
$fatal(1, "cache-maintenance phase blocked HP before acknowledgement");
end
cache_clean_i = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
cache_clean_i = 1'b0;
repeat (2) @(negedge clk_i);
if (!block_new_o || !draining_o || !hp_release_o) begin
$fatal(1, "normal shutdown did not enter drain while HP remained released");
+170
View File
@@ -0,0 +1,170 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "mmap_define.svh"
module onchip_ram64_tb #(
parameter int unsigned CapacityKiB = 32
);
localparam logic [31:0] SramBase = `SOC_ADDR_SRAM_BASE;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) mem_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
onchip_ram #(
.Present (1'b1),
.CapacityKiB(CapacityKiB),
.DataWidth (64),
.IdWidth (6)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(1'b1),
.perf_clear_i (1'b0),
.mem_axi4 (mem_axi4),
.cfg_apb4 (cfg_apb4)
);
task automatic issue_write(input logic [5:0] id, input logic [31:0] address,
input logic [7:0] length, input logic [2:0] size,
input logic [63:0] seed, input logic [7:0] strobe);
begin
@(negedge clk_i);
mem_axi4.awid = id;
mem_axi4.awaddr = address;
mem_axi4.awlen = length;
mem_axi4.awsize = size;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
for (int beat = 0; beat <= int'(length); beat++) begin
mem_axi4.wdata = seed + beat;
mem_axi4.wstrb = strobe;
mem_axi4.wlast = beat == int'(length);
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
end
mem_axi4.wvalid = 1'b0;
mem_axi4.wlast = 1'b0;
mem_axi4.bready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.bvalid);
if ((mem_axi4.bid != id) || (mem_axi4.bresp != `AXI4_RESP_OKAY)) begin
$fatal(1, "AXI64 SRAM write response mismatch");
end
@(negedge clk_i);
mem_axi4.bready = 1'b0;
end
endtask
task automatic expect_read(input logic [5:0] id, input logic [31:0] address,
input logic [7:0] length, input logic [63:0] seed);
integer previous_cycle;
integer cycle_count;
begin
@(negedge clk_i);
mem_axi4.arid = id;
mem_axi4.araddr = address;
mem_axi4.arlen = length;
mem_axi4.arsize = 3'd3;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b1;
previous_cycle = -1;
cycle_count = 0;
for (int beat = 0; beat <= int'(length); beat++) begin
do begin
@(posedge clk_i);
cycle_count = cycle_count + 1;
end while (!mem_axi4.rvalid);
if ((mem_axi4.rid != id) || (mem_axi4.rresp != `AXI4_RESP_OKAY) ||
(mem_axi4.rlast != (beat == int'(length))) ||
(mem_axi4.rdata != (seed + beat))) begin
$fatal(1, "AXI64 SRAM read response mismatch at beat %0d", beat);
end
if ((previous_cycle >= 0) && (cycle_count != previous_cycle + 1)) begin
$fatal(1, "AXI64 SRAM read burst inserted a bubble at beat %0d", beat);
end
previous_cycle = cycle_count;
end
@(negedge clk_i);
mem_axi4.rready = 1'b0;
end
endtask
initial begin
mem_axi4.awid = '0;
mem_axi4.awaddr = '0;
mem_axi4.awlen = '0;
mem_axi4.awsize = 3'd3;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awlock = `AXI4_LOCK_NORM;
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
mem_axi4.awprot = `AXI4_PROT_DATA;
mem_axi4.awqos = `AXI4_QOS_NORMAL;
mem_axi4.awregion = `AXI4_REGION_NORMAL;
mem_axi4.awuser = '0;
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = '0;
mem_axi4.wstrb = '0;
mem_axi4.wlast = 1'b0;
mem_axi4.wuser = '0;
mem_axi4.wvalid = 1'b0;
mem_axi4.bready = 1'b0;
mem_axi4.arid = '0;
mem_axi4.araddr = '0;
mem_axi4.arlen = '0;
mem_axi4.arsize = 3'd3;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arlock = `AXI4_LOCK_NORM;
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
mem_axi4.arprot = `AXI4_PROT_DATA;
mem_axi4.arqos = `AXI4_QOS_NORMAL;
mem_axi4.arregion = `AXI4_REGION_NORMAL;
mem_axi4.aruser = '0;
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b0;
cfg_apb4.paddr = '0;
cfg_apb4.pprot = '0;
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
issue_write(6'h2A, SramBase + 32'h100, 8'd3, 3'd3, 64'h1000_2000_3000_4000, 8'hFF);
expect_read(6'h2B, SramBase + 32'h100, 8'd3, 64'h1000_2000_3000_4000);
issue_write(6'h2C, SramBase + 32'h10C, 8'd0, 3'd2, 64'hDEAD_BEEF_0000_0000, 8'hF0);
expect_read(6'h2D, SramBase + 32'h108, 8'd0, 64'hDEAD_BEEF_3000_4001);
$display("native AXI64 on-chip SRAM test passed");
$finish;
end
initial begin
repeat (400) @(posedge clk_i);
$fatal(1, "native AXI64 on-chip SRAM test timed out");
end
endmodule
+146
View File
@@ -0,0 +1,146 @@
`timescale 1ns / 1ps
module resource_controller_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [ 5:0] idle_i = '0;
logic [ 5:0] block_ack_i = '0;
logic [ 5:0] irq_i = '0;
logic cache_request_i = 1'b0;
logic cache_clean_o;
logic [ 5:0][1:0] owner_o;
logic [ 5:0] quiesce_o;
logic [ 5:0] reset_o;
logic [ 5:0] irq_lp_o;
logic [ 5:0] irq_hp_o;
logic fault_irq_o;
logic [31:0] read_data;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
resource_controller u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.idle_i (idle_i),
.block_ack_i (block_ack_i),
.irq_i (irq_i),
.cache_request_i(cache_request_i),
.cache_clean_o (cache_clean_o),
.owner_o (owner_o),
.quiesce_o (quiesce_o),
.reset_o (reset_o),
.irq_lp_o (irq_lp_o),
.irq_hp_o (irq_hp_o),
.fault_irq_o (fault_irq_o),
.apb4 (apb4)
);
task automatic apb_write(input logic [11:0] offset, input logic [31:0] data,
input logic expected_error);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset};
apb4.pwrite = 1'b1;
apb4.pwdata = data;
apb4.pstrb = 4'hF;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
do @(posedge clk_i); while (!apb4.pready);
if (apb4.pslverr != expected_error) begin
$fatal(1, "resource controller write error mismatch at %h", offset);
end
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [11:0] offset, output logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset};
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
do @(posedge clk_i); while (!apb4.pready);
if (apb4.pslverr) $fatal(1, "resource controller read failed at %h", offset);
data = apb4.prdata;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
idle_i = 6'h3F;
apb_read(12'h000, read_data);
if (read_data != 32'h5253_4354) $fatal(1, "resource controller ID mismatch");
irq_i[1] = 1'b1;
#1;
if (!irq_lp_o[1] || irq_hp_o[1]) $fatal(1, "default LP IRQ ownership mismatch");
idle_i[0] = 1'b0;
block_ack_i[0] = 1'b1;
apb_write(12'h104, 32'h0000_0001, 1'b0);
apb_write(12'h100, 32'h0000_0001, 1'b1);
if (!fault_irq_o || (owner_o[0] != 2'd0)) begin
$fatal(1, "busy resource handoff did not fail closed");
end
apb_write(12'h10C, 32'h0000_0001, 1'b0);
if (fault_irq_o) $fatal(1, "resource fault did not clear");
idle_i[0] = 1'b1;
irq_i[0] = 1'b1;
apb_write(12'h100, 32'h0000_0001, 1'b0);
if ((owner_o[0] != 2'd1) || irq_lp_o[0] || !irq_hp_o[0]) begin
$fatal(1, "HP resource ownership or IRQ routing mismatch");
end
apb_read(12'h110, read_data);
if (read_data != 32'd1) $fatal(1, "resource handoff counter mismatch");
apb_write(12'h100, 32'h0000_0101, 1'b0);
apb_write(12'h100, 32'h0000_0000, 1'b1);
if (owner_o[0] != 2'd1) $fatal(1, "locked resource owner changed");
cache_request_i = 1'b1;
apb_write(12'h010, 32'h0000_0001, 1'b0);
if (!cache_clean_o) $fatal(1, "cache maintenance acknowledgement was not retained");
cache_request_i = 1'b0;
@(posedge clk_i);
#1;
if (cache_clean_o) $fatal(1, "cache acknowledgement did not clear with request");
apb_write(12'h124, 32'h0000_0003, 1'b0);
if (!quiesce_o[1] || !reset_o[1]) begin
$fatal(1, "resource lifecycle controls did not update");
end
$display("Resource Controller ownership, IRQ, and cache handshake test passed");
$finish;
end
initial begin
repeat (300) @(posedge clk_i);
$fatal(1, "Resource Controller test timed out");
end
endmodule
+48 -43
View File
@@ -20,54 +20,59 @@ module rib2apb_tb;
apb4_pure_if user_ip ();
apb4_pure_if ext_l ();
apb4_pure_if ext_h ();
apb4_pure_if resource_ctrl ();
always #5 clk_i = ~clk_i;
assign archinfo.pready = 1'b0;
assign archinfo.prdata = '0;
assign archinfo.pslverr = 1'b0;
assign rng.pready = rng_ready;
assign rng.prdata = rng_rdata;
assign rng.pslverr = rng_slverr;
assign pwm.pready = 1'b0;
assign pwm.prdata = '0;
assign pwm.pslverr = 1'b0;
assign ps2.pready = 1'b0;
assign ps2.prdata = '0;
assign ps2.pslverr = 1'b0;
assign rtc.pready = 1'b0;
assign rtc.prdata = '0;
assign rtc.pslverr = 1'b0;
assign wdg.pready = 1'b0;
assign wdg.prdata = '0;
assign wdg.pslverr = 1'b0;
assign crc.pready = 1'b0;
assign crc.prdata = '0;
assign crc.pslverr = 1'b0;
assign user_ip.pready = 1'b0;
assign user_ip.prdata = '0;
assign user_ip.pslverr = 1'b0;
assign ext_l.pready = 1'b0;
assign ext_l.prdata = '0;
assign ext_l.pslverr = 1'b0;
assign ext_h.pready = 1'b0;
assign ext_h.prdata = '0;
assign ext_h.pslverr = 1'b0;
assign archinfo.pready = 1'b0;
assign archinfo.prdata = '0;
assign archinfo.pslverr = 1'b0;
assign rng.pready = rng_ready;
assign rng.prdata = rng_rdata;
assign rng.pslverr = rng_slverr;
assign pwm.pready = 1'b0;
assign pwm.prdata = '0;
assign pwm.pslverr = 1'b0;
assign ps2.pready = 1'b0;
assign ps2.prdata = '0;
assign ps2.pslverr = 1'b0;
assign rtc.pready = 1'b0;
assign rtc.prdata = '0;
assign rtc.pslverr = 1'b0;
assign wdg.pready = 1'b0;
assign wdg.prdata = '0;
assign wdg.pslverr = 1'b0;
assign crc.pready = 1'b0;
assign crc.prdata = '0;
assign crc.pslverr = 1'b0;
assign user_ip.pready = 1'b0;
assign user_ip.prdata = '0;
assign user_ip.pslverr = 1'b0;
assign ext_l.pready = 1'b0;
assign ext_l.prdata = '0;
assign ext_l.pslverr = 1'b0;
assign ext_h.pready = 1'b0;
assign ext_h.prdata = '0;
assign ext_h.pslverr = 1'b0;
assign resource_ctrl.pready = 1'b0;
assign resource_ctrl.prdata = '0;
assign resource_ctrl.pslverr = 1'b0;
rib2apb u_rib2apb (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rib (rib),
.archinfo(archinfo),
.rng (rng),
.pwm (pwm),
.ps2 (ps2),
.rtc (rtc),
.wdg (wdg),
.crc (crc),
.user_ip (user_ip),
.ext_l (ext_l),
.ext_h (ext_h)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rib (rib),
.archinfo (archinfo),
.rng (rng),
.pwm (pwm),
.ps2 (ps2),
.rtc (rtc),
.wdg (wdg),
.crc (crc),
.user_ip (user_ip),
.ext_l (ext_l),
.ext_h (ext_h),
.resource_ctrl(resource_ctrl)
);
task automatic send_command(input logic [31:0] address, input logic write,
+2 -1
View File
@@ -58,6 +58,7 @@ module soc_irq_topology_tb;
4: expect_irq(32'd1 << 16);
5: expect_irq(32'd1 << 27);
6: expect_irq(32'd1 << 28);
7: expect_irq(32'd1 << 29);
default: $fatal(1, "unexpected APB IRQ bit %0d", bit_index);
endcase
end
@@ -68,7 +69,7 @@ module soc_irq_topology_tb;
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
s_irq[10], s_irq[21]);
end
if ((s_irq[31:29] !== '0) || (s_irq[19:18] !== '0)) begin
if ((s_irq[31:30] !== '0) || (s_irq[19:18] !== '0)) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
end
+41
View File
@@ -271,6 +271,7 @@ def test_axi4_data_crossbar_supports_cross_target_outstanding_and_acl(tmp_path:
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv"),
str(ROOT / "rtl/mini/top/axi4_data_crossbar.sv"),
str(ROOT / "tests/rtl/axi4_data_crossbar_tb.sv"),
"-Mdir",
@@ -289,3 +290,43 @@ def test_axi4_data_crossbar_supports_cross_target_outstanding_and_acl(tmp_path:
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "AXI4 data crossbar concurrency and ACL test passed" in result.stdout
def test_axi4_target_guard_terminates_and_isolates_timeouts(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "axi4_target_guard_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"axi4_target_guard_tb",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv"),
str(ROOT / "rtl/mini/top/axi4_target_guard.sv"),
str(ROOT / "tests/rtl/axi4_target_guard_tb.sv"),
"-Mdir",
str(tmp_path / "obj"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "AXI4 target guard timeout and isolation test passed" in result.stdout
+6 -2
View File
@@ -20,7 +20,7 @@ def test_hp_profile_and_locked_generator_contract() -> None:
profile = (ROOT / "configs/ci/ihp130-hp.mk").read_text(encoding="utf-8")
assert "HAVE_HP" in profile and "YES" in profile
assert "rv32imafdc_max" in profile
assert "rv32imafdc_zicbom_max" in profile
assert "APP" in profile and "hp_boot" in profile
assert "LINK_TYPE" in profile and "ld2_all_sram" in profile
@@ -28,7 +28,7 @@ def test_hp_profile_and_locked_generator_contract() -> None:
encoding="utf-8"
)
for requirement in (
'param.addISA("m", "a", "f", "d", "c", "s", "u", "zicntr", "zihpm")',
'param.addISA("m", "a", "f", "d", "c", "s", "u", "zicbom", "zicntr", "zihpm")',
"param.decoders = 2",
"param.lanes = 2",
"param.fetchL1Ways = 4",
@@ -74,6 +74,7 @@ def test_hp_address_and_sysctrl_contract() -> None:
assert regions["HP_PLIC"]["base"] == "0x0C000000"
assert regions["APB4_UART1"]["base"] == "0x10018000"
assert regions["APB4_HP_MAILBOX"]["base"] == "0x10019000"
assert regions["APB4_RESOURCE_CTRL"]["base"] == "0x2000A000"
registers = {register["symbol"]: register["offset"] for register in document["sysctrl_registers"]}
assert registers["HP_CTRL"] == "0xA4"
@@ -151,3 +152,6 @@ def test_hp_linux_device_tree_compiles_and_can_patch_initrd_end(tmp_path: Path)
).strip()
assert initrd_end == "39123456"
assert hart_id == "1"
dts = (ROOT / "app/ports/linux/linux/retrosoc_hp.dts").read_text(encoding="utf-8")
assert '"zicbom"' in dts
assert "riscv,cbom-block-size = <64>;" in dts
+8 -2
View File
@@ -129,8 +129,14 @@ def test_chip_config_includes_configured_onchip_sram_banks(tmp_path: Path) -> No
macro = config["MACROS"]["RM_IHPSG13_1P_1024x32_c2_bm_bist"]
assert len(macro["instances"]) == 8
assert "u_retrosoc.u_onchip_ram.gen_memory.gen_bank[0].u_ram.u_mem" in macro["instances"]
assert "u_retrosoc.u_onchip_ram.gen_memory.gen_bank[7].u_ram.u_mem" in macro["instances"]
assert (
"u_retrosoc.u_onchip_ram.gen_memory64.gen_bank[0].u_ram_lo.u_mem"
in macro["instances"]
)
assert (
"u_retrosoc.u_onchip_ram.gen_memory64.gen_bank[3].u_ram_hi.u_mem"
in macro["instances"]
)
assert any(
"gen_bank.*0.*\\.u_ram" in connection
for connection in config["PDN_MACRO_CONNECTIONS"]
+69
View File
@@ -84,6 +84,75 @@ def test_onchip_ram_axi4_capacity_protocol_and_performance(
assert f"on-chip SRAM AXI4 test passed capacity_kib={capacity_kib}" in result.stdout
@pytest.mark.parametrize("capacity_kib", (4, 32))
def test_onchip_ram_native_axi64_width_ids_and_lanes(
tmp_path: Path, capacity_kib: int
) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
generated = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(generated),
"--have-sram-if",
"YES",
"--sram-size-kib",
str(capacity_kib),
],
check=True,
)
output = tmp_path / f"onchip_ram64_{capacity_kib}"
ccache_tmp = tmp_path / "ccache64"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"onchip_ram64_tb",
f"-GCapacityKiB={capacity_kib}",
"+define+PDK_BEHAV",
"+define+SV_ASSRT_DISABLE",
"-I" + str(generated / "rtl"),
"-I" + str(ROOT / "rtl/mini/top"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
str(ROOT / "tests/rtl/onchip_ram64_tb.sv"),
"-Mdir",
str(tmp_path / "obj64"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "native AXI64 on-chip SRAM test passed" in result.stdout
def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
+49
View File
@@ -0,0 +1,49 @@
"""Directed verification for the Mini Resource Controller."""
from __future__ import annotations
import os
import shutil
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_resource_controller_ownership_irq_and_cache_handshake(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "resource_controller_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"resource_controller_tb",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/mini/top/resource_controller.sv"),
str(ROOT / "tests/rtl/resource_controller_tb.sv"),
"-Mdir",
str(tmp_path / "obj"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "Resource Controller ownership, IRQ, and cache handshake test passed" in result.stdout
+3 -2
View File
@@ -1162,8 +1162,9 @@ def test_pdk_pr_regressions_cover_firmware_rtl_and_selected_netlist_target() ->
assert "VERILATOR_SIM_ARGS=--fast-flash" in verilator_values
assert "SOC_SIM_TIME=360" in verilator_values
else:
assert "LINK_TYPE=ld2_all_sram" not in verilator_values
assert "VERILATOR_SIM_ARGS=--fast-flash" not in verilator_values
assert "LINK_TYPE=ld2_sdram" in verilator_values
assert "VERILATOR_SIM_ARGS=--fast-flash" in verilator_values
assert "SOC_SIM_TIME=600" in verilator_values
assert any("SIMU=IVERILOG" in values and "sim-asm" in values for values in command_values)
assert any("SYNTH=YOSYS" in values and "synth" in values for values in command_values)
assert not any(
+1
View File
@@ -170,5 +170,6 @@ def test_ci_software_covers_sdram_mapped_window() -> None:
assert "rs_ci_smoke_sdram_access" in smoke
assert "RS_SOC_SDRAM_BASE" in smoke
assert "RS_SOC_SDRAM_END - RS_CI_SMOKE_SDRAM_SPAN" in smoke
assert "RS_SOC_SDRAM_BASE" in hello
assert "WAIT_SDRAM_READY" in hello
+19 -13
View File
@@ -118,11 +118,12 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign u_opipsram_if.rwds_i = u_gpio_if.di_i[31];" in gpio
assert "assign u_gpio_if.alt0_do_i[31] = u_opipsram_if.rwds_o;" in gpio
assert "s_tmr_capch" not in gpio
assert apb_interfaces.count("apb4_if u_") == 10
assert apb_interfaces.count("apb4_if u_") == 11
assert "apb4_pure_if u_archinfo_apb4_pure_if ();" in apb_interfaces
assert "apb4_if u_resource_ctrl_apb4_if (clk_i, rst_n_i);" in apb_interfaces
assert "assign user_ip.paddr = s_addr_q;" in apb_routes
assert "({32{s_psel_q[7]}} & user_ip.prdata)" in apb_response
assert "localparam int NSLV = 10;" in apb_declarations
assert "localparam int NSLV = 11;" in apb_declarations
assert fabric.count("axi4_if #(") == 8
assert ".mgmt_axi4(u_mgmt_axi4_if)" in bus_fabric
assert ".user_axi4(u_user_axi4_if)" in bus_fabric
@@ -137,22 +138,23 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 22" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 7" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 8" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
assert "assign irq_o[11] = gpio.irq_o;" in rib_irq
assert "assign irq_o[12] = i2c1.irq_o;" in rib_irq
assert "assign irq_o[13] = s_dvp_irq;" in rib_irq
assert "assign irq_o[14] = s_dma_irq;" in rib_irq
assert "assign irq_o[14] = resource_irq_lp_i[0];" in rib_irq
assert "assign irq_o[15] = opipsram.irq_o;" in rib_irq
assert "assign irq_o[16] = sdio0.irq_o;" in rib_irq
assert "assign irq_o[17] = sdio1.irq_o;" in rib_irq
assert "assign irq_o[16] = resource_irq_lp_i[2];" in rib_irq
assert "assign irq_o[17] = resource_irq_lp_i[3];" in rib_irq
assert "assign irq_o[18] = s_crypto_irq;" in rib_irq
assert "assign irq_o[19] = s_usb2_irq;" in rib_irq
assert "assign irq_o[19] = resource_irq_lp_i[1];" in rib_irq
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
assert "assign irq_o[4] = s_rng_irq;" in apb_irq
assert "assign irq_o[5] = s_ext_l_irq;" in apb_irq
assert "assign irq_o[6] = s_ext_h_irq;" in apb_irq
assert "assign irq_o[7] = s_resource_fault_irq;" in apb_irq
assert "s_irq[10] = s_apb4_periph_irq[16];" in irq_wiring
assert "s_irq[15] = s_apb4_periph_irq[13];" in irq_wiring
assert "s_irq[16] = s_apb4_system_irq[4];" in irq_wiring
@@ -163,6 +165,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "s_irq[24] = s_apb4_periph_irq[19];" in irq_wiring
assert "s_irq[27] = s_apb4_system_irq[5];" in irq_wiring
assert "s_irq[28] = s_apb4_system_irq[6];" in irq_wiring
assert "s_irq[29] = s_apb4_system_irq[7];" in irq_wiring
assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva
assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva
assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva
@@ -172,6 +175,8 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "irq_i[24] == apb4_periph_irq_i[19]" in irq_sva
assert "irq_i[31] == 1'b0" in irq_sva
assert "bind retrosoc soc_irq_topology_sva" in irq_sva
assert ".clk_i(clk_lp_i)" in irq_sva
assert ".rst_n_i(rst_lp_n_i)" in irq_sva
assert filelist.startswith("+incdir+")
@@ -195,11 +200,11 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("timer0", "apb4_periph", 3, 3, "s_tim0_irq"),
("timer1", "apb4_periph", 4, 4, "s_tim1_irq"),
("psram", "apb4_periph", 5, 5, "psram.irq_o"),
("spisd", "apb4_periph", 6, 6, "spisd.irq_o"),
("spisd", "apb4_periph", 6, 6, "resource_irq_lp_i[4]"),
("i2c0", "apb4_periph", 7, 7, "i2c0.irq_o"),
("i2s", "apb4_periph", 8, 8, "i2s.irq_o"),
("xpi", "apb4_periph", 9, 9, "s_xpi_irq"),
("sdio0", "apb4_periph", 16, 10, "sdio0.irq_o"),
("sdio0", "apb4_periph", 16, 10, "resource_irq_lp_i[2]"),
("pwm", "apb4_system", 0, 11, "pwm.irq_o"),
("ps2", "apb4_system", 1, 12, "ps2.irq_o"),
("rtc", "apb4_system", 2, 13, "u_rtc_if.irq_o"),
@@ -209,15 +214,16 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("gpio", "apb4_periph", 11, 18, "gpio.irq_o"),
("i2c1", "apb4_periph", 12, 19, "i2c1.irq_o"),
("dvp", "apb4_periph", 13, 15, "s_dvp_irq"),
("dma", "apb4_periph", 14, 20, "s_dma_irq"),
("dma", "apb4_periph", 14, 20, "resource_irq_lp_i[0]"),
("opipsram", "apb4_periph", 15, 22, "opipsram.irq_o"),
("sdio1", "apb4_periph", 17, 21, "sdio1.irq_o"),
("sdio1", "apb4_periph", 17, 21, "resource_irq_lp_i[3]"),
("crypto", "apb4_periph", 18, 23, "s_crypto_irq"),
("usb2", "apb4_periph", 19, 24, "s_usb2_irq"),
("usb2", "apb4_periph", 19, 24, "resource_irq_lp_i[1]"),
("hp_mailbox_lp", "apb4_periph", 20, 25, "s_mailbox_lp_irq"),
("uart1", "apb4_periph", 21, 26, "uart1.irq_o"),
("extension_l", "apb4_system", 5, 27, "s_ext_l_irq"),
("extension_h", "apb4_system", 6, 28, "s_ext_h_irq"),
("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"),
]
@@ -233,7 +239,7 @@ def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None:
)
assert "apb4_if u_user_ip_apb4_if (clk_i, rst_n_i);" in interfaces
assert "localparam int NSLV = 10;" in declarations
assert "localparam int NSLV = 11;" in declarations
assert "s_psel_q[7]" in response
assert "apb4_pure_if user_ip ();" in formal_design
assert ".user_ip (user_ip)" in formal_design