feat: add comprehensive tests for AXI4 components and lifecycle controllers

- Implemented tests for AXI4 async bridge functionality, including transfer and warm flush scenarios.
- Added tests for AXI4 data crossbar to validate concurrent outstanding requests and ACL handling.
- Introduced clock frequency monitor tests to ensure detection of clock stop and restart events.
- Developed tests for extension DMA master to verify copy operations, tail strobe behavior, and timeout handling.
- Created tests for HP lifecycle controller to validate drain, flush, and forced reset functionalities.
- Updated existing test scripts to include new RTL components and ensure proper simulation configurations.
This commit is contained in:
maksyuki committed 2026-08-30 13:15:10 +08:00
1 parent 2b6819449c
commit 98eb8ebad6
67 files changed
+4165 -612

No files matched your search

+2
View File
@@ -81,3 +81,5 @@ socver.*
/rtl/managed/mpw/
/rtl/managed/third_party/
*result.json
*.pdf
/publications/datasheets/assets/
+4 -1
View File
@@ -337,7 +337,7 @@ include physical/ecc/Makefile
.PHONY: help config doctor setup setup-regression setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app setup-hp-linux hp-linux hp-bundle hp-linux-sim hp-smoke-bundle hp-smoke-sim \
clean-all purge-cache manifest check-warnings metrics check-metrics package commercial-package \
regress-smoke regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \
regress-smoke regress-rtl regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \
mk-format-check rtl-format rtl-format-check rtl-style-check rtl-migrate-connections rtl-migrate-names sw-policy-check sw-host-test \
benchmark-report coremark-report \
hp-performance-check \
@@ -671,6 +671,9 @@ commercial-package: $(MPW_VARIANT_DEP) $(FILELIST_STAMP) manifest
regress-smoke:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130
regress-rtl:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130
regress-pr:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
+11 -3
View File
@@ -99,12 +99,18 @@ CI Verilator firmware simulations explicitly select the `ci_smoke`
application, which checks UART, archinfo APB readback, macro-backed on-chip SRAM,
RNG fail-closed behavior, and test-status
completion without the verbose startup report. ARCHINFO checks include its
ABI and the build/configuration identifiers generated for that variant. The profiles retain `bringup`
ABI and the build/configuration identifiers generated for that variant. The
IHP130 PR run links this image into the 32 KiB SRAM, explicitly enables the
fast-flash backend, and allows 360 seconds; Icarus keeps the real serial XPI
boot-model check. The profiles retain `bringup`
as their default for manual diagnostics. To run the full report in Verilator,
use:
```sh
make CONFIG=configs/ci/ihp130.mk APP=bringup SIMU=VERILATOR SOC_SIM_TIME=300 firmware sim
# Firmware, RTL lint, debug, and behavioral simulation without synthesis/STA
make regress-rtl
```
## Prerequisites
@@ -198,8 +204,10 @@ variant identifier; `make help` lists all available targets. Netlist simulation
analysis consume the Yosys netlist, so run the synthesis command first. The CI-proven netlist
regression uses the assembly self-test image:
Verilator simulations run for 180 seconds by default. Set `SOC_SIM_TIME` explicitly only when
an exploratory local run needs a different limit.
Verilator simulations run for 180 seconds by default. The IHP130 `ci_smoke`
regression explicitly uses 360 seconds because its LP memory accesses traverse
the asynchronous data gateway. Set `SOC_SIM_TIME` explicitly only when an
exploratory local run needs a different limit.
VCS flow commands use `bsub -Is` by default: this includes parse-time Python helpers used to
calculate the variant and dependency-lock digest, generated-flow Python helpers, VCS, `simv`, and
+7
View File
@@ -52,6 +52,13 @@ 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/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
cache visibility, and the controller fault summary by composing the existing
per-controller HALs. It does not merge heterogeneous memories into one
allocator or provide hardware cache coherency.
`<retrosoc/hal/dma.h>` provides the channel-aware direct and linked-list DMA
API. UART0, I2C0, I2C1, bulk/media clients, and the HP boot loader have
deterministic channel assignments; see the [DMA V2 contract](../docs/ip/dma.md).
+9
View File
@@ -46,6 +46,12 @@ typedef struct {
uint32_t timeout_cycles;
} rs_extension_acl_t;
typedef struct {
bool busy;
bool done;
bool fault;
} rs_extension_dma_status_t;
rs_status_t rs_extension_probe(rs_extension_slot_t slot, rs_extension_capabilities_t *capabilities);
rs_status_t rs_extension_get_status(rs_extension_slot_t slot, rs_extension_status_t *status);
rs_status_t rs_extension_set_owner(rs_extension_slot_t slot, rs_extension_owner_t owner, bool lock);
@@ -54,5 +60,8 @@ rs_status_t rs_extension_set_lifecycle(rs_extension_slot_t slot, bool quiesce, b
rs_status_t rs_extension_configure_acl(rs_extension_slot_t slot,
const rs_extension_acl_t *configuration);
rs_status_t rs_extension_read(rs_extension_slot_t slot, uint32_t offset, uint32_t *value);
rs_status_t rs_extension_dma_start(uintptr_t source, uintptr_t destination, uint32_t byte_count);
rs_status_t rs_extension_dma_abort(void);
rs_status_t rs_extension_dma_get_status(rs_extension_dma_status_t *status);
#endif
+48
View File
@@ -0,0 +1,48 @@
#ifndef RETROSOC_HAL_MEMORY_H
#define RETROSOC_HAL_MEMORY_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
typedef enum {
RS_MEMORY_SRAM = 0,
RS_MEMORY_SDRAM = 1,
RS_MEMORY_QPI_PSRAM = 2,
RS_MEMORY_OPI_PSRAM = 3,
RS_MEMORY_XPI = 4,
} rs_memory_target_t;
typedef enum {
RS_MEMORY_PAD_NONE = 0,
RS_MEMORY_PAD_QPI = 1,
RS_MEMORY_PAD_OPI = 2,
} rs_memory_pad_mode_t;
typedef enum {
RS_MEMORY_LATENCY_ONCHIP = 0,
RS_MEMORY_LATENCY_PARALLEL = 1,
RS_MEMORY_LATENCY_SERIAL = 2,
} rs_memory_latency_t;
typedef struct {
uintptr_t base;
uint32_t size;
uint32_t fault_code;
rs_memory_target_t target;
rs_memory_pad_mode_t pad_mode;
rs_memory_latency_t latency;
bool controller_present;
bool device_present;
bool initialized;
bool ready;
bool active;
bool cacheable;
bool dma_visible;
bool fault;
} rs_memory_info_t;
rs_status_t rs_memory_get_info(rs_memory_target_t target, rs_memory_info_t *info);
#endif
+3
View File
@@ -54,6 +54,9 @@ typedef struct {
bool present;
bool released;
bool reset_asserted;
bool draining;
bool forced_fault;
bool actual_released;
} rs_sysctrl_hp_status_t;
rs_status_t rs_sysctrl_get_core_select(uint8_t *core_id);
+65 -16
View File
@@ -4,23 +4,33 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/extension.h>
#define RS_EXTENSION_IDENTIFICATION_OFFSET UINT32_C(0x000)
#define RS_EXTENSION_VERSION_OFFSET UINT32_C(0x004)
#define RS_EXTENSION_CAPABILITY_OFFSET UINT32_C(0x008)
#define RS_EXTENSION_OWNER_OFFSET UINT32_C(0x00C)
#define RS_EXTENSION_COMMAND_OFFSET UINT32_C(0x010)
#define RS_EXTENSION_STATUS_OFFSET UINT32_C(0x014)
#define RS_EXTENSION_READ_BASE_OFFSET UINT32_C(0x018)
#define RS_EXTENSION_READ_LIMIT_OFFSET UINT32_C(0x01C)
#define RS_EXTENSION_WRITE_BASE_OFFSET UINT32_C(0x020)
#define RS_EXTENSION_WRITE_LIMIT_OFFSET UINT32_C(0x024)
#define RS_EXTENSION_TIMEOUT_OFFSET UINT32_C(0x028)
#define RS_EXTENSION_IDENTIFICATION_OFFSET UINT32_C(0x000)
#define RS_EXTENSION_VERSION_OFFSET UINT32_C(0x004)
#define RS_EXTENSION_CAPABILITY_OFFSET UINT32_C(0x008)
#define RS_EXTENSION_OWNER_OFFSET UINT32_C(0x00C)
#define RS_EXTENSION_COMMAND_OFFSET UINT32_C(0x010)
#define RS_EXTENSION_STATUS_OFFSET UINT32_C(0x014)
#define RS_EXTENSION_READ_BASE_OFFSET UINT32_C(0x018)
#define RS_EXTENSION_READ_LIMIT_OFFSET UINT32_C(0x01C)
#define RS_EXTENSION_WRITE_BASE_OFFSET UINT32_C(0x020)
#define RS_EXTENSION_WRITE_LIMIT_OFFSET UINT32_C(0x024)
#define RS_EXTENSION_TIMEOUT_OFFSET UINT32_C(0x028)
#define RS_EXTENSION_DMA_SOURCE_OFFSET UINT32_C(0x100)
#define RS_EXTENSION_DMA_DESTINATION_OFFSET UINT32_C(0x104)
#define RS_EXTENSION_DMA_LENGTH_OFFSET UINT32_C(0x108)
#define RS_EXTENSION_DMA_COMMAND_OFFSET UINT32_C(0x10C)
#define RS_EXTENSION_DMA_STATUS_OFFSET UINT32_C(0x110)
#define RS_EXTENSION_REGISTER_SIZE UINT32_C(4)
#define RS_EXTENSION_OWNER_LOCK UINT32_C(0x00000100)
#define RS_EXTENSION_COMMAND_QUIESCE UINT32_C(0x00000001)
#define RS_EXTENSION_COMMAND_RESET UINT32_C(0x00000002)
#define RS_EXTENSION_COMMAND_CLEAR_FAULT UINT32_C(0x00000004)
#define RS_EXTENSION_REGISTER_SIZE UINT32_C(4)
#define RS_EXTENSION_OWNER_LOCK UINT32_C(0x00000100)
#define RS_EXTENSION_COMMAND_QUIESCE UINT32_C(0x00000001)
#define RS_EXTENSION_COMMAND_RESET UINT32_C(0x00000002)
#define RS_EXTENSION_COMMAND_CLEAR_FAULT UINT32_C(0x00000004)
#define RS_EXTENSION_DMA_START UINT32_C(0x00000001)
#define RS_EXTENSION_DMA_ABORT UINT32_C(0x00000002)
#define RS_EXTENSION_DMA_STATUS_BUSY UINT32_C(0x00000001)
#define RS_EXTENSION_DMA_STATUS_DONE UINT32_C(0x00000002)
#define RS_EXTENSION_DMA_STATUS_FAULT UINT32_C(0x00000004)
static bool rs_extension_slot_valid(rs_extension_slot_t slot) {
return (slot == RS_EXTENSION_SLOT_L) || (slot == RS_EXTENSION_SLOT_H);
@@ -152,3 +162,42 @@ rs_status_t rs_extension_configure_acl(rs_extension_slot_t slot,
}
return RS_OK;
}
rs_status_t rs_extension_dma_start(uintptr_t source, uintptr_t destination, uint32_t byte_count) {
if (((source | destination) & (uintptr_t)UINT32_C(0x7)) != (uintptr_t)0U ||
(byte_count == 0U) || (source > (uintptr_t)UINT32_MAX) ||
(destination > (uintptr_t)UINT32_MAX)) {
return RS_EINVAL;
}
if ((rs_extension_write(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_SOURCE_OFFSET,
(uint32_t)source) != RS_OK) ||
(rs_extension_write(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_DESTINATION_OFFSET,
(uint32_t)destination) != RS_OK) ||
(rs_extension_write(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_LENGTH_OFFSET, byte_count) !=
RS_OK) ||
(rs_extension_write(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_COMMAND_OFFSET,
RS_EXTENSION_DMA_START) != RS_OK)) {
return RS_EIO;
}
return RS_OK;
}
rs_status_t rs_extension_dma_abort(void) {
return rs_extension_write(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_COMMAND_OFFSET,
RS_EXTENSION_DMA_ABORT);
}
rs_status_t rs_extension_dma_get_status(rs_extension_dma_status_t *status) {
uint32_t value;
if (status == NULL) {
return RS_EINVAL;
}
if (rs_extension_read(RS_EXTENSION_SLOT_H, RS_EXTENSION_DMA_STATUS_OFFSET, &value) != RS_OK) {
return RS_EIO;
}
status->busy = (value & RS_EXTENSION_DMA_STATUS_BUSY) != 0U;
status->done = (value & RS_EXTENSION_DMA_STATUS_DONE) != 0U;
status->fault = (value & RS_EXTENSION_DMA_STATUS_FAULT) != 0U;
return RS_OK;
}
+136
View File
@@ -0,0 +1,136 @@
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/memory.h>
#include <retrosoc/hal/onchip_sram.h>
#include <retrosoc/hal/opipsram.h>
#include <retrosoc/hal/psram.h>
#include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/xpi.h>
#define RS_MEMORY_PAD_MODE_MASK UINT32_C(0x00000003)
static volatile uint32_t *rs_memory_sysctrl_register(uint32_t offset) {
return (volatile uint32_t *)(RS_SOC_APB4_SYSCTRL_BASE + (uintptr_t)offset);
}
static rs_memory_pad_mode_t rs_memory_pad_mode(void) {
const uint32_t value =
*rs_memory_sysctrl_register(RS_SOC_SYSCTRL_MEM_PAD_STATUS_OFFSET) & RS_MEMORY_PAD_MODE_MASK;
return (rs_memory_pad_mode_t)value;
}
static void rs_memory_static_info(rs_memory_target_t target, rs_memory_info_t *info) {
info->target = target;
info->controller_present = true;
info->cacheable = true;
info->dma_visible = true;
switch (target) {
case RS_MEMORY_SRAM:
info->base = (uintptr_t)RS_SOC_SRAM_BASE;
info->size = RS_SOC_SRAM_END - RS_SOC_SRAM_BASE + UINT32_C(1);
info->latency = RS_MEMORY_LATENCY_ONCHIP;
break;
case RS_MEMORY_SDRAM:
info->base = (uintptr_t)RS_SOC_SDRAM_BASE;
info->size = RS_SOC_SDRAM_END - RS_SOC_SDRAM_BASE + UINT32_C(1);
info->latency = RS_MEMORY_LATENCY_PARALLEL;
break;
case RS_MEMORY_QPI_PSRAM:
info->base = (uintptr_t)RS_SOC_PSRAM_BASE;
info->size = RS_SOC_PSRAM_END - RS_SOC_PSRAM_BASE + UINT32_C(1);
info->latency = RS_MEMORY_LATENCY_SERIAL;
break;
case RS_MEMORY_OPI_PSRAM:
info->base = (uintptr_t)RS_SOC_OPIPSRAM_BASE;
info->size = RS_SOC_OPIPSRAM_END - RS_SOC_OPIPSRAM_BASE + UINT32_C(1);
info->latency = RS_MEMORY_LATENCY_SERIAL;
break;
case RS_MEMORY_XPI:
info->base = (uintptr_t)RS_SOC_XPI_BASE;
info->size = RS_SOC_XPI_END - RS_SOC_XPI_BASE + UINT32_C(1);
info->latency = RS_MEMORY_LATENCY_SERIAL;
break;
default:
info->controller_present = false;
break;
}
}
rs_status_t rs_memory_get_info(rs_memory_target_t target, rs_memory_info_t *info) {
rs_status_t result = RS_OK;
if ((info == NULL) || (target > RS_MEMORY_XPI)) {
return RS_EINVAL;
}
*info = (rs_memory_info_t){0};
rs_memory_static_info(target, info);
info->pad_mode = rs_memory_pad_mode();
switch (target) {
case RS_MEMORY_SRAM: {
rs_onchip_sram_info_t status;
result = rs_onchip_sram_probe(&status);
if (result == RS_OK) {
info->controller_present = status.present;
info->device_present = status.present;
info->initialized = status.present;
info->ready = status.present;
info->active = status.present;
info->size = status.memory_bytes;
}
break;
}
case RS_MEMORY_SDRAM: {
rs_sdram_status_t status;
result = rs_sdram_get_status(&status);
if (result == RS_OK) {
info->device_present = status.ready;
info->initialized = status.ready;
info->ready = status.ready;
info->active = status.ready;
info->fault = status.error;
info->fault_code = (uint32_t)status.last_error;
}
break;
}
case RS_MEMORY_QPI_PSRAM: {
rs_psram_status_t status;
result = rs_psram_get_status(&status);
if (result == RS_OK) {
info->device_present = status.chip_present != UINT8_C(0);
info->initialized = status.chip_ready != UINT8_C(0);
info->ready = status.ready;
info->active = status.ready && (info->pad_mode == RS_MEMORY_PAD_QPI);
info->fault = status.chip_error != UINT8_C(0);
info->fault_code = (uint32_t)status.last_error;
}
break;
}
case RS_MEMORY_OPI_PSRAM: {
rs_opipsram_status_t status;
result = rs_opipsram_get_status(&status);
if (result == RS_OK) {
info->device_present = status.initialized;
info->initialized = status.initialized;
info->ready = status.ready;
info->active = status.ready && (info->pad_mode == RS_MEMORY_PAD_OPI);
info->fault = status.error;
info->fault_code = status.last_error;
}
break;
}
case RS_MEMORY_XPI:
result = rs_xpi_probe();
info->device_present = result == RS_OK;
info->initialized = result == RS_OK;
info->ready = result == RS_OK;
info->active = result == RS_OK;
info->fault = result != RS_OK;
break;
default:
result = RS_EINVAL;
break;
}
return result;
}
+6
View File
@@ -72,6 +72,9 @@
#define RS_SYSCTRL_HP_PRESENT UINT32_C(0x00000001)
#define RS_SYSCTRL_HP_RELEASED UINT32_C(0x00000002)
#define RS_SYSCTRL_HP_RESET_ASSERTED UINT32_C(0x00000004)
#define RS_SYSCTRL_HP_DRAINING UINT32_C(0x00000008)
#define RS_SYSCTRL_HP_FORCED_FAULT UINT32_C(0x00000010)
#define RS_SYSCTRL_HP_ACTUAL_RELEASED UINT32_C(0x00000020)
static volatile uint32_t *rs_sysctrl_register(uint32_t offset) {
return (volatile uint32_t *)(RS_SOC_APB4_SYSCTRL_BASE + (uintptr_t)offset);
@@ -301,6 +304,9 @@ rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) {
status->present = (value & RS_SYSCTRL_HP_PRESENT) != 0U;
status->released = (value & RS_SYSCTRL_HP_RELEASED) != 0U;
status->reset_asserted = (value & RS_SYSCTRL_HP_RESET_ASSERTED) != 0U;
status->draining = (value & RS_SYSCTRL_HP_DRAINING) != 0U;
status->forced_fault = (value & RS_SYSCTRL_HP_FORCED_FAULT) != 0U;
status->actual_released = (value & RS_SYSCTRL_HP_ACTUAL_RELEASED) != 0U;
return RS_OK;
}
+4
View File
@@ -18,6 +18,10 @@ the root README and subsystem guides.
Debug Module integration, reset boundary, and Verilator/OpenOCD/GDB flow.
- [lp-hp-architecture.md](lp-hp-architecture.md) defines the current Mini
product AON/LP/HP/PCLK, AXI64 data-plane, memory-pad, and extension contract.
- [commercial-soc-alignment.md](commercial-soc-alignment.md) records the
selected commercial architecture patterns and their reuse boundaries.
- [mini-high-performance-traceability.md](mini-high-performance-traceability.md)
maps Mini architecture requirements to implementation and evidence status.
- [coremark.md](coremark.md) defines the SRAM-resident Hazard3 CoreMark quick
measurement, optional standard hardware run, and structured result format.
- [soc-family-positioning.md](soc-family-positioning.md) defines the planned
+14 -12
View File
@@ -33,16 +33,17 @@ IDs. It has eight masters:
| 0 | Vexii I-cache | native AXI64, source ID preserved |
| 1 | Vexii D-cache | native AXI64, source ID preserved |
| 2 | central DMA | PCLK-to-HP CDC, AXI32-to-64 |
| 3 | SDIO0 | PCLK-to-HP CDC, AXI32-to-64 |
| 4 | SDIO1 | PCLK-to-HP CDC, AXI32-to-64 |
| 5 | SPI-SD | PCLK-to-HP CDC, AXI32-to-64 |
| 6 | USB2 | PCLK-to-HP CDC, AXI32-to-64 |
| 3 | I/O gateway A | USB2 and SDIO0, PCLK-to-HP CDC, AXI32-to-64 |
| 4 | I/O gateway B | SDIO1 and SPI-SD, PCLK-to-HP CDC, AXI32-to-64 |
| 5 | LP data gateway | Hazard3 memory traffic, LP-to-HP CDC, AXI32-to-64 |
| 6 | reserved | permanently idle and denied |
| 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. It accepts one outstanding transaction in each
direction per master; it does not reorder responses within a source.
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.
| Target | Current backend |
| --- | --- |
@@ -63,18 +64,19 @@ fail-closed according to the synchronized AON pad mode.
- `axi4_downsizer_64to32` splits 64-bit beats for current memory frontends and
recombines read responses.
- `axi4_async_bridge` carries AW, W, B, AR, and R through independent Common
`cdc_fifo` channels with a two-sided reset barrier.
coordinated warm-flush FIFOs and reports clear-busy and epoch state.
- `apb4_async_bridge` converts one APB request into a request/response CDC
transaction and guarantees a finite APB completion when the destination runs.
Async bridges flush protocol state when either side resets. Clock-stop timeout
and full hot-reset recovery are not yet claimed; callers must quiesce before
reset or DFS.
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.
## 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 multi-outstanding reordering, cache coherency,
formal crossbar proof, CDC signoff, or physical bandwidth closure.
implementation does not claim same-target multi-outstanding reordering, cache
coherency, formal crossbar proof, CDC signoff, or physical bandwidth closure.
+40
View File
@@ -0,0 +1,40 @@
# Mini Commercial SoC Alignment
## Purpose
This document records the commercial architecture references used for the
Mini high-performance implementation. It is a design input, not a claim that
retroSoC has the qualification, safety case, analogue IP, or software
ecosystem of the referenced devices.
## Selected references
| Reference | Problem addressed | Architecture reused by Mini | Boundary |
| --- | --- | --- | --- |
| [NXP i.MX RT1180](https://www.nxp.com/products/i.MX-RT1180) | Heterogeneous lifecycle control, external-memory boot, and DVFS | Management-owned lifecycle, stable peripheral clocks while changing a CPU PLL, and resource-domain access control | Mini does not implement voltage scaling or the NXP security lifecycle |
| [ST STM32H7R/S RM0477](https://www.st.com/resource/en/reference_manual/rm0477-stm32h7rx7sx-armbased-32bit-mcus-stmicroelectronics.pdf) | Concurrent CPU, DMA, graphics, and external-memory traffic | Layered AXI/AHB/APB planes, independent memory targets, programmable QoS, and functional clock roots | Mini adds starvation aging instead of relying on unbounded static priority |
| [TI AM263x](https://www.ti.com/product/AM2632) | Freedom from interference and recoverable interconnect faults | Initiator/target firewalls, initiator identity, first-fault attribution, clock monitoring, and directed fault injection | Mini does not claim lockstep, ASIL, SIL, or redundant-bus coverage |
| [Arm CoreLink NIC-400](https://developer.arm.com/community/arm-community-blogs/b/soc-design-and-simulation-blog/posts/corelink-nic-400-a-great-interconnect-for-wearables-and-entry-level-smartphones) | Scalable AXI integration across widths and clock domains | Target-aware outstanding credits, independent channel buffering, register slices, and hierarchical I/O gateways | Mini remains a small crossbar and does not use a packetized NoC |
Primary sources are the NXP i.MX RT1180 product documentation, ST RM0477,
the [TI AM263x TRM](https://www.ti.com/lit/ug/spruj17h/spruj17h.pdf), and Arm
CoreLink Network Interconnect documentation. Web references were reviewed on
2026-08-30. The referenced products and documentation were active at review
time; this date is retained so a later architecture review can detect changes.
## Mini selection
Mini uses a performance-first, non-coherent architecture. Hazard3 retains
root lifecycle control. VexiiRiscv caches, DMA, I/O gateways, the LP data
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.
No release may describe the implementation as timing closed, CDC signed off,
silicon qualified, functionally safe, or cache coherent until the separate
physical and product evidence exists.
+5 -1
View File
@@ -14,7 +14,11 @@
| Cluster | `configs/cluster/ics55.mk` | compatibility profile for site-specific ICS55 runs |
Regression Verilator firmware simulations override the profiles' manual
`bringup` default with `APP=ci_smoke`. This application verifies UART output,
`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,
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;
+14 -3
View File
@@ -25,12 +25,23 @@ generating register definitions.
| `0x02C` | `FAULT` | RO | sticky fault state |
| `0x030` | `FAULT_ADDRESS` | RO | first rejected control address |
| `0x034` | `REQUEST_COUNT` | RO | saturating accepted APB request count |
| `0x100` | `DMA_SOURCE` | EXT-H RW | aligned memory-copy source |
| `0x104` | `DMA_DESTINATION` | EXT-H RW | aligned memory-copy destination |
| `0x108` | `DMA_LENGTH` | EXT-H RW | nonzero byte count |
| `0x10C` | `DMA_COMMAND` | EXT-H WO | start or abort |
| `0x110` | `DMA_STATUS` | EXT-H RO | busy, done pulse, and fault |
EXT-L rejects ACL and timeout writes. Invalid or read-only writes complete with
APB `PSLVERR`, set the slot fault, and record the first offending address.
The reference EXT-H master is idle; connecting a real accelerator must retain
the control-plane deny policy and implement the programmed ranges before it can
issue payload traffic.
The reference EXT-H master performs sequential 64-bit memory copies. It
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
does not report idle until the DMA and address gate have drained.
`<retrosoc/hal/extension.h>` provides discovery, status, ownership, lifecycle,
and EXT-H ACL operations. Register constants remain independently handwritten
+1 -1
View File
@@ -65,7 +65,7 @@ offset macros.
| `0x084` | `TEST_STATUS` | RW-once/RO | Full-word write with bit 31 set records sticky done, bit 0 pass, and bits `[15:8]` result code. |
| `0x088` | `RTC_WAKE_STATUS` | RW1C/RO | Bit 0 synchronized live wake; bit 1 sticky wake, cleared by writing one to bit 1. |
| `0x0A4` | `HP_CTRL` | RW | Bit 0 releases HP reset when the HP profile is present. Reset value 0. |
| `0x0A8` | `HP_STATUS` | RO | Bits 0/1/2 report present, released, and reset asserted. |
| `0x0A8` | `HP_STATUS` | RO | Bits 0/1/2 preserve present and requested release/reset; bits 3/4/5 report draining, forced fault, and actual release. |
| `0x0AC` | `DEBUG_SELECT` | RW | Bit 0 selects HP on shared JTAG only while HP remains in reset. Reset value 0 selects LP. |
| `0x0B0` | `CLK_HP_REQ` | RW/WO command | HP P-state; bits 31/30 apply PLL/force safe. |
| `0x0B4` | `CLK_HP_CURRENT` | RO | Current HP source/P-state and LP/PCLK/gate configuration. |
+29 -23
View File
@@ -66,37 +66,40 @@ HP uncached MMIO is downsized to AXI32 and crosses HP to LP through
watchdog, and GPIO administration windows with `SLVERR`; Hazard3 retains full
management access.
The LP control fabric remains the compatibility path for Hazard3 boot and
external-memory accesses. HP, DMA, and accelerator payload traffic uses the
native data plane described below. Moving the remaining controller protocol
engines from LP/PCLK to the exported memory root is an explicit follow-up; the
present RTL does not claim that migration or its physical CDC signoff.
`axi4_mgmt_router` keeps APB/control addresses on this path and routes every
memory window through an LP-to-HP data gateway. Hazard3 therefore shares the
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. Each direction permits one active
transaction per master and per target; global IDs contain a fixed master
prefix plus the source ID.
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.
| Master | Entry path |
| --- | --- |
| HP I-cache | native AXI64, ID prefix 0 |
| HP D-cache | native AXI64, ID prefix 1 |
| central DMA | PCLK-to-HP async bridge, AXI32-to-64 upsizer |
| SDIO0 / SDIO1 | PCLK-to-HP async bridge, upsizer |
| SPI-SD / USB2 | PCLK-to-HP async bridge, upsizer |
| I/O gateway A | USB2 and SDIO0, then PCLK-to-HP CDC and upsizer |
| I/O gateway B | SDIO1 and SPI-SD, then PCLK-to-HP CDC and upsizer |
| LP data gateway | Hazard3 memory traffic, LP-to-HP CDC and upsizer |
| 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 followed by HP-to-LP async bridges. Inactive QPI/OPI windows route
to `SLVERR`. HP I-cache, D-cache, and MMIO no longer share the retired
`hp_axi4_mux3` path.
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`.
All async AXI channels use Common `cdc_fifo` storage and a reset barrier. A
single-ended reset aborts the link epoch rather than returning a stale response.
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
it, then waits for bridge recovery before resetting the core.
## Memory and shared pads
@@ -123,9 +126,10 @@ Product Mini has fixed control windows:
Each slot exposes identification, version, capability, owner/lock, lifecycle,
status, timeout, first fault, fault address, and request count. EXT-H adds read
and write ACL ranges. The current reference slot is idle and issues no data
traffic; its interface and isolation contract are live. Software discovers it
through `<retrosoc/hal/extension.h>`.
and write ACL ranges that are enforced at data-plane admission. The reference
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 old `APB4_USER_IP` window is a read-only compatibility/capability window.
Product writes to `CORESEL`, `IPSEL`, `USER_CORE_RESET`, or
@@ -143,8 +147,10 @@ so absolute commercial paths are never tracked.
## Evidence boundary
Behavioral RTL, firmware, directed PLL/SYSCTRL tests, manifest parity, and
quality checks are the evidence for this implementation. It must not be called
cache coherent, power isolated, hot-reset safe, timing closed, CDC/RDC signed
off, or silicon-qualified. Synthesis, netlist simulation, STA, MMMC, clock-tree,
DFT, and analogue PLL qualification are separate gates.
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
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.
@@ -0,0 +1,59 @@
# Mini High-Performance Traceability
## Status vocabulary
- `implemented`: RTL and software-visible behavior exist and directed tests pass.
- `partial`: a bounded subset is implemented and the missing boundary is stated.
- `verified`: the required simulation and formal evidence pass.
- `physical-pending`: digital behavior exists but synthesis or later evidence is required.
- `planned`: the implementation is not complete.
## Requirement matrix
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
The architecture report under `/nfs/share/home/miaoyuchi/plan` is the design
input. This matrix and executable configuration are the repository status
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.
- 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.
The strict IHP130 warning-baseline comparison remains an observation: the
baseline predates the fixed HP/product topology and does not contain many
generated VexiiRiscv, newly enabled peripheral, and PDK signatures. New warning
signatures introduced directly by the data-plane, lifecycle, extension, and
CDC integration were removed; the baseline was intentionally not regenerated
or hand-edited in this change.
Synthesis, netlist simulation, STA, CDC/RDC signoff, gate-level power intent,
and PVT characterization are intentionally outside this delivery, as required
by the implementation scope.
+8 -1
View File
@@ -21,7 +21,8 @@ Applications use `rs_clock_set_frequency()` and `rs_clock_get_status()` from
1. Run the transition routine from SRAM, disable interrupts, and quiesce DMA,
serial transfers, and other frequency-sensitive transactions.
2. Call `rs_clock_set_frequency()` and wait with a bounded timeout.
2. Call `rs_clock_set_frequency()` and wait with a bounded timeout. The
programmed `CLK_TIMEOUT` is also the AON quiesce and PLL-lock budget.
3. On success, reprogram UART, SPI, I2C, timer, and other divider-dependent
peripherals for the new frequency before restoring normal operation.
4. `RS_ENOTSUP` means the current PDK has no integrated dynamic PLL or
@@ -32,3 +33,9 @@ Applications use `rs_clock_set_frequency()` and `rs_clock_get_status()` from
must integrate a characterized PLL macro, lock timing, and glitch-free clock
switch before setting capability to 1. The ICS55 hard wrapper advertises only
the qualified selector-0 configuration; requests for other selectors fail safe.
AON Gray-counter monitors track EXT72, HP root, PCLK, and the stable memory
root without sampling their pulses directly at 24 MHz. A stopped clock sets a
sticky clock fault. `CLK_GATE[0]` drives the HP-core leaf ICG; the AXI64 fabric
and memory root remain active for management recovery and DMA. Technology
timing, scan force-on, and analogue PLL qualification remain physical gates.
@@ -0,0 +1,376 @@
#set document(
title: "retroSoC Mini Gen2 Datasheet",
author: "Yuchi Miao",
keywords: "retroSoC",
)
#set page(
header: align(center)[
https://github.com/retroSoC
],
numbering: "1/1",
columns: 1,
)
#set par(justify: true)
#set text(font: "Inter", size: 11pt)
#set heading(numbering: "1.")
#show title: set text(size: 17pt)
#show link: underline
#title[
retroSoC Mini Series Gen2/Gen2+
]
#place(
top + right,
dx: 0pt,
// square(size: 36pt, fill: none, stroke: black),
rect(height: 66pt, width: 140pt, fill: none, stroke: black),
)
Datasheet (rev.3 – 2026.02.15) \
*Author:* Yuchi Miao (#link("miaoyuchi@ict.ac.cn")) \
*Maintained by* _ECOS Team, ICT, CAS_
#line(length: 100%)
A *Lightweight* RV32I/EMC *MCU Framework* with *128KiB* OCM, 1xXPI, 2xSPISD/SDIO, 2xPSRAM(SDR/DTR), 1xSDRAM, 24xGPIO, 1xDMA, 3xTIMER, 1xRTC, 1xWDG, 4xPWM, 2xUART, 2xI2C, 1xONEWIRE, 1xPS2, 1xARCHINFO, 1xSYSCTRL, 1xRCU, 1xRNG, 1xCRC, 1xI2S, 1xDVP, 1xAPU\*, 1xGA\*, 1xCRC, 1xRNG
#line(length: 100%)
= Product Brief
#place(
top + right,
dx: 0pt,
dy: 168pt,
grid(
columns: auto,
align: center,
gutter: 3pt,
// stroke: black,
rect(height: 27pt, width: 250pt, fill: white, stroke: black, radius: 6pt)[
#grid(
columns: (1fr, 1fr, 1fr, 1fr),
align: center,
gutter: 3pt,
// stroke: black,
text("Available", size: 8pt, weight: "medium"),
text("Functional", size: 8pt, weight: "medium"),
text("Reproduced", size: 8pt, weight: "medium"),
text("Tapedout", size: 8pt, weight: "medium"),
circle(radius: 4pt, stroke: black, fill: yellow.lighten(60%)),
circle(radius: 4pt, stroke: black, fill: white),
circle(radius: 4pt, stroke: black, fill: white),
circle(radius: 4pt, stroke: black, fill: white),
)
],
text("Artifact Evaluation", size: 7pt, weight: "medium")
),
)
== Feature
#columns(2)[
- Core – PicoRV32/Hazard3
- *196MHz* maximum frequency
- 2-pipeline RV32I/E\[MAC\], 16xIRQ
- Hardware multiplication and division
- Memories
- *0\~128KiB* On-Chip-RAM *(OCM)*
- *32MiB* XPI NOR Flash Controller
- *8/32MiB* PSRAM Controller
- *64MiB* 16bit SDRAM Controller
- Bus – Async. *No-burst* Protocol
- Native Memory Interface *(NMI)*
- AXI4-Lite master, APB3 slave
- NMI, AXI4L, APB3 Interconnect
- Reset Clock Unit *(RCU)*
- *4\~50MHz* Active Crystal Oscillator
- PLL for core clock *(24\~192MHz)*
- PLL or Bypass mode
- XPI *(SDR mode only)*
- Half-duplex, SPI/DPI/QSPI/QPI mode
- 32x32b TX/RX dual FIFO Integrated
- Register/Memory-mapped/DMA
- SPISD/SDIO (SDHC, 4\~32GB)
- *1GiB* Memory-mapped access
- 1/4-wire SPI or SDIO mode *(SDv2.0)*
- 512B cache, 50MHz maximum freq.
- I2S (HQ, Hi-Res audio)
- Stereo input/output mode
- max *24b/96KHz* format support
- Full-duplex, I2S Phillips only
#colbreak()
- DVP (Camera interface)
- 8-bit parallel input mode
- max *2M* image sensor support
- RGB565 format support only
- APU/GA\* *(Experimental integrate)*
- Audio Keyword Wake-Up (CNN)
- Simple 2D/3D graphics render
- High performance DMA engine
- Other Communication Interfaces
- 2xUARTs (FIFO/No FIFO)
- 3xTIMER, 1xRTC, 1xWDG, 4xPWM
- 1xRNG, 1xARCHINFO, 1xSYSCTRL
- 1xONEWIRE, 1xCRC, 1xI2C, 1xPS2
- *GDS Merge Template*
- *0\~32* master designs (Core)
- *0\~256* slave designs (digital module)
- 16xGPIO, max 5000 cells per design
- Development
- *Open source* and full synthesizable
- Static synchronous design
- *ICS55*, IHP130 PDK support
- RV32I/E\[MAC\] C Running Time *(CRT)*
- Tools & Flows
- VCS, Verilator, iVerilog (TBD)
- Yosys, ECC, Pristine (TBD)
- *QFN128* (12.3x12.3mm) Package
- Application
- *Wearables*;, IoT services, E-readers
- Education, ASIC Prototype
]
#figure(
image(
"retroSoC-mini-block-diagram.svg",
width: 100%,
),
caption: [
*_retroSoC Mini Gen2/Gen2+_* block diagram
],
)<block-diagram>
== Overview
_*retroSoC*_ is a fully open-source and customizable ASIC framework for _*“retro-style”*_ applications(handheld game consoles, system). And of ECOS Team.\
(story, target, my thinking, be dedicated to retro multimedia area, full open source, focus on funny)
(insert a retro poster/diagram to descripte the motivation[why])
- *Open source*: It is both straightforward to use and highly compatible with EDA toolchains — specifically targeting open-source EDA *(iEDA, OpenROAD, etc)*.
- *HW-SW co*:
- *Time to market*: focus on the funcitonality, seprate into 4 series
- *Lower the price*: Additionally, retroSoC can also serve as a flexible, reconfigurable *SoC template* for end customers or students who want to integrate their own design into a shared fabrication wafer *(shuttle)*.
- *Well documented*: balba
The _*retroSoC Mini Series*_ is a family of highly integrated, lightweight MCU designed for Internet of Things *(IoT)* scenarios. _*retroSoC Mini Gen2/Gen2+*_ adopts the PicoRV32/Hazard3 core and integrates DMA, SPISD/SDIO, SDRAM, I2S and XPI IPs. It can be used for a wide variety of applications as wearables, education or ASIC prototype verification.
#lorem(380)
== Product Series
#lorem(100)
#figure(
image(
"retroSoC-Product-Series.svg",
width: 100%,
),
caption: [
*_retroSoC_* Product series
],
)<product-series>
== Roadmap
#lorem(100)
#figure(
image(
"retroSoC-Roadmap.svg",
width: 100%,
),
caption: [
*_retroSoC_* Roadmap
],
)<product-rodamap>
== Revision History
#set table(
stroke: black,
gutter: 0em,
fill: (x, y) => if y == 0 { yellow.lighten(60%) },
)
#show table.cell: it => {
if it.y == 0 {
set text(black)
strong(it)
} else if it.body == [] {
pad(..it.inset)[_N/A_]
} else {
it
}
}
#table(
columns: (1fr, 1fr, 1fr, 1fr),
[Date], [Version], [Release Note], [Author],
[2025-09-28], [v0.1], [Create document], [Yuchi Miao],
[2025-12-02], [v0.2], [Initial draft], [Yuchi Miao],
[2026-02-15], [v0.3], [Initial release], [Yuchi Miao],
)
== License
#lorem(60)
#pagebreak()
#outline()
= System Architecture
== Introduction
=== SoC Architecture
=== Management Processor
==== PicoRV32
==== Hazard3
== Interconnect
=== Native Memory Interface *(NMI)*
=== Interconnect Matrix
=== Address Mapping
== Clock and Reset
=== Architecture
=== Reset and Logic
== Interrupt System
== Packaging
=== Pin Mapping
=== Package Dimensions
=== Graded Reflow Soldering
=== Ordering Information
== Peripherals
=== Memory Interface
==== Non-Volatile Memory *(NVM)*
===== XPI Universal Controller *(XPI)*
===== SPI SD Card Controller *(SPISD)*
===== SD Card Controller *(SDIO0)*
==== Volatile Memory *(VM)*
===== On Chip Memory *(OCM)*
===== PSRAM Controller *(PSRAM)*
===== SDRAM Controller *(SDRAM)*
=== General-Purpose Input Output *(GPIO)*
==== System IO *(GPIO0)*
==== User Custom IO *(GPIO1)*
=== Direct Memory Access *(DMA)*
==== Architecture
==== Hardware Trigger Channels
=== Timers
==== General-Purpose Timer *(TIM0, TIM1)*
==== Advanced Timer *(TIM2)*
==== Real Time Clock *(RTC)*
==== Watchdog *(WDG)*
==== Pulse Width Modulation *(PWM)*
=== IO Interface
==== Universal Async. Receiver/Transmitter *(UART)*
===== Simple No-FIFO Version *(UART0)*
===== Advanced FIFO Version *(UART1)*
==== Inter-Integrated Circuit *(I2C)*
==== One Wire Serial Bus *(ONEWIRE)*
==== Personal System/2 *(PS2)*
==== SDIO Controller *(SDIO1)*
==== Architecture Information *(ARCHINFO)*
==== System Controller *(SYSCTRL)*
==== Reset Clock Unit *(RCU)*
=== Multimedia
==== Inter-Integrated Circuit Sound *(I2S)*
==== Extended Peripheral Interface *(XPI)*
==== Digital Video Port *(DVP)*
==== Audio Processing Unit\* *(APU)*
==== Graphic Accelerator\* *(GA)*
=== Encryption
==== Cyclic Redundancy Check *(CRC)*
==== Random Number Generator *(RNG)*
== Software
=== Boot Sequence
=== TinySDK and TinyShell
=== Application
== SoC Template
=== Architecture
=== User Guide
== Implementation
=== SoC Integration
=== RTL Simulation
=== FPGA Verification
=== Physical Design
=== PCB Hardware
= Technical Report (ongoing)
== First Shuttle *(2026.06.01, ICS55nm)*
Overview(written in SystemVerilog)
1. Introduction (arch info first)
1.1 core(info, wavedorm)
1.2 bus(nmi intro, matrix, addr mmap)
1.3 clock&reset (block diagram, wavedorm)
1.4 IO/Package(voltage, IO pad speed, IO pos)
2. Peripherals
2.1 Memory
2.2 DMA
2.3 Timer
2.4 IO perip
2.5 Encryption
3. Software
3.1 boot sequ.
3.2 application
4. SoC Template
4.1 intro four mode(bakcend GDS)
4.2 how to integrate 'core' or 'IP design'
5. Implemention
5.1 FPGA
5.2 Backend(SDC, ICS55 example)
5.3 PCB+
6. Resource &
6.1
+7 -1
View File
@@ -683,7 +683,13 @@ module sysctrl_core (
HpCtrl: read_data_o = {31'd0, s_hp_release_q};
HpStatus:
read_data_o = {
29'd0, sysctrl.hp_present_i && !s_hp_release_q, s_hp_release_q, sysctrl.hp_present_i
26'd0,
sysctrl.hp_actual_released_i,
sysctrl.hp_forced_fault_i,
sysctrl.hp_draining_i,
sysctrl.hp_present_i && !s_hp_release_q,
s_hp_release_q,
sysctrl.hp_present_i
};
DebugSelect: read_data_o = {31'd0, s_debug_hp_sel_q};
ClockHpReq: read_data_o = {29'd0, s_pll_cfg_q};
+6
View File
@@ -37,6 +37,9 @@ interface sysctrl_if ();
logic [ 7:0] test_code_o;
logic [ `USER_IPSEL_WIDTH-1:0] ip_sel_o;
logic hp_present_i;
logic hp_actual_released_i;
logic hp_draining_i;
logic hp_forced_fault_i;
logic hp_release_o;
logic debug_hp_select_o;
@@ -58,6 +61,9 @@ interface sysctrl_if ();
input perf_flash_wait_i,
input rtc_wake_i,
input hp_present_i,
input hp_actual_released_i,
input hp_draining_i,
input hp_forced_fault_i,
output core_sel_o,
output core_reset_o,
output user_bus_enable_o,
+11 -10
View File
@@ -95,24 +95,25 @@ bind rib2apb rib2apb_sva #(
module soc_pll_rcu_sva (
input logic clk_i,
input logic rst_n_i,
input logic [2:0] state_i,
input logic [3:0] state_i,
input logic pll_capable_i,
input logic pll_apply_i,
input logic select_ext_clk_i
);
localparam logic [2:0] PLL_SAFE = 3'd1;
localparam logic [2:0] PLL_APPLY = 3'd2;
localparam logic [2:0] PLL_WAIT_LOCK = 3'd3;
localparam logic [2:0] PLL_RESPOND = 3'd5;
localparam logic [3:0] PLL_VALIDATE = 4'd1;
localparam logic [3:0] PLL_APPLY = 4'd4;
localparam logic [3:0] PLL_WAIT_LOCK_LOW = 4'd5;
localparam logic [3:0] PLL_QUALIFY_LOCK = 4'd6;
localparam logic [3:0] PLL_FAIL_SAFE = 4'd10;
// The output remains on the external clock until the PLL switch stage completes.
assert property (@(posedge clk_i) disable iff (!rst_n_i)
state_i == PLL_SAFE |-> select_ext_clk_i);
((state_i == PLL_APPLY) || (state_i == PLL_WAIT_LOCK_LOW) ||
(state_i == PLL_QUALIFY_LOCK)) |-> select_ext_clk_i);
assert property (@(posedge clk_i) disable iff (!rst_n_i) pll_apply_i |-> state_i == PLL_APPLY);
assert property (@(posedge clk_i) disable iff (!rst_n_i)
pll_apply_i |-> state_i inside {PLL_APPLY, PLL_WAIT_LOCK});
assert property (@(posedge clk_i) disable iff (!rst_n_i)
state_i == PLL_SAFE && !pll_capable_i |=> state_i == PLL_RESPOND);
state_i == PLL_VALIDATE && !pll_capable_i |=> state_i == PLL_FAIL_SAFE);
endmodule
@@ -122,7 +123,7 @@ bind pll_rcu_controller soc_pll_rcu_sva u_soc_pll_rcu_sva (
.state_i (s_state_q),
.pll_capable_i (pll_capable_i),
.pll_apply_i (pll_apply_o),
.select_ext_clk_i(select_ext_clk_o)
.select_ext_clk_i(sel_ext_clk_o)
);
module soc_gpio_user_handoff_sva #(
+13
View File
@@ -250,6 +250,19 @@ module retrosoc_tb;
if ($value$plusargs("sim_timeout=%d", sim_runtime) && sim_runtime > 0) begin
$display("Simulation timeout set to: %0dns", sim_runtime);
#sim_runtime;
$display("CLOCK_RESET_DIAG aon=%0b lp=%0b hp=%0b pclk=%0b mem=%0b",
u_retrosoc_asic.s_aon_rst_n, u_retrosoc_asic.s_sys_rst_n, u_retrosoc_asic.s_hp_rst_n,
u_retrosoc_asic.s_pclk_rst_n, u_retrosoc_asic.s_mem_rst_n);
$display("DATA_PLANE_DIAG rcu_block=%0b lifecycle_block=%0b flush=%0b busy=%0b idle=%0b",
u_retrosoc_asic.s_hp_block, u_retrosoc_asic.u_retrosoc.s_hp_lifecycle_block,
u_retrosoc_asic.u_retrosoc.s_hp_lifecycle_flush,
u_retrosoc_asic.u_retrosoc.s_data_plane_flush_busy,
u_retrosoc_asic.u_retrosoc.s_data_plane_idle);
$display("LP_GATEWAY_DIAG src_arvalid=%0b src_arready=%0b dst_arvalid=%0b dst_arready=%0b",
u_retrosoc_asic.u_retrosoc.u_mgmt_data_axi4_if.arvalid,
u_retrosoc_asic.u_retrosoc.u_mgmt_data_axi4_if.arready,
u_retrosoc_asic.u_retrosoc.u_data_plane.u_lp_data_hp_axi4.arvalid,
u_retrosoc_asic.u_retrosoc.u_data_plane.u_lp_data_hp_axi4.arready);
$fatal(1, "SIM_TEST_TIMEOUT");
end
$display("Simulation timeout disabled; waiting for terminal software status");
+2 -1
View File
@@ -85,7 +85,7 @@ module retrosoc_top (
wire s_jtag_trst_n;
wire s_jtag_tdo;
wire s_uart1_tx;
tri1 s_uart1_rx;
wire s_uart1_rx;
wire s_usb2_ulpi_clk;
tri0 s_usb2_ulpi_dir;
tri0 s_usb2_ulpi_nxt;
@@ -103,6 +103,7 @@ module retrosoc_top (
assign s_jtag_tms = jtag_tms_i;
assign s_jtag_tdi = jtag_tdi_i;
assign s_jtag_trst_n = jtag_trst_n_i;
assign s_uart1_rx = s_uart1_tx;
assign jtag_tdo_o = s_jtag_tdo;
assign test_done_o = u_retrosoc_asic.s_test_done;
assign test_pass_o = u_retrosoc_asic.s_test_pass;
+5
View File
@@ -7,6 +7,7 @@
/top/rib2apb.sv
/top/axi4_connector.sv
/top/axi4_master_idle.sv
/top/axi4_mgmt_router.sv
/top/axi4_async_bridge.sv
/top/axi4_address_gate.sv
/top/axi4_upsizer_32to64.sv
@@ -38,8 +39,11 @@
/top/rcu.sv
/top/pll_rcu_controller.sv
/top/clock_config_controller.sv
/top/clock_frequency_monitor.sv
/top/soc_clock_gate.sv
/top/soc_clock_reset_subsystem.sv
/top/mgmt_debug_reset.sv
/top/hp_lifecycle_controller.sv
/top/mgmt_debug_wrapper.sv
/top/mgmt_core_wrapper.sv
/top/hp_core_wrapper.sv
@@ -50,6 +54,7 @@
/top/user_ip_top.sv
/top/user_ip_wrapper.sv
/top/extension_slot.sv
/top/extension_dma_master.sv
/top/extension_compat.sv
/top/extension_subsystem.sv
/sva/rib_protocol_sva.sv
@@ -28,6 +28,8 @@ TOP = ROOT / "rtl/mini/top"
def target_defines(target: str) -> list[str]:
if target in {"onchip_ram", "opipsram"}:
return ["+define+PDK_BEHAV"]
if target == "sysctrl":
return ["+define+SV_ASSRT_DISABLE", "+define+MINI_PRODUCT"]
return ["+define+SV_ASSRT_DISABLE"]
@@ -72,6 +74,7 @@ def source_files(target: str) -> list[Path]:
*common,
COMMON_RTL / "cdc/cdc_sync.sv",
PERIPHERAL / "pll_ctrl_if.sv",
PERIPHERAL / "clock_ctrl_if.sv",
PERIPHERAL / "sysctrl_if.sv",
PERIPHERAL / "sysctrl_define.svh",
PERIPHERAL / "sysctrl_reg.sv",
@@ -89,6 +92,7 @@ def source_files(target: str) -> list[Path]:
COMMON_RTL / "clkrst/counter.sv",
COMMON_RTL / "utils/edge_det.sv",
PERIPHERAL / "pll_ctrl_if.sv",
PERIPHERAL / "clock_ctrl_if.sv",
PERIPHERAL / "clint_timebase.sv",
TOP / "rcu.sv",
TOP / "pll_rcu_controller.sv",
+20 -16
View File
@@ -19,13 +19,12 @@ module pll_rcu_formal_design (
output logic rsp_ready,
output logic pll_lock,
output logic pll_capable,
output logic [2:0] state,
output logic [3:0] state,
output logic pll_apply,
output logic select_ext_clk,
output logic active_valid,
output logic safe_clk,
output logic active_lock,
output logic lock_seen_low,
output logic [1:0] error
);
@@ -47,13 +46,10 @@ module pll_rcu_formal_design (
assign rsp_ready = pll_ctrl.rsp_ready_o;
assign pll_lock = f_pll_lock;
assign pll_capable = f_pll_capable;
assign state = u_dut.s_state_q;
assign pll_apply = u_dut.s_pll_apply_q;
assign select_ext_clk = u_dut.s_sel_ext_clk_q;
assign state = u_dut.s_state_bits_q;
assign active_valid = u_dut.s_active_valid_q;
assign safe_clk = u_dut.s_safe_clk_q;
assign active_lock = u_dut.s_lock_q;
assign lock_seen_low = u_dut.s_lock_seen_low_q;
assign error = u_dut.s_err_q;
// Both controller domains share the formal clock. This proves the control
@@ -61,16 +57,24 @@ module pll_rcu_formal_design (
pll_rcu_controller #(
.LockTimeout(4)
) u_dut (
.sys_clk_i (clk_i),
.sys_rst_n_i (rst_n_i),
.ext_clk_i (clk_i),
.ext_rst_n_i (rst_n_i),
.pll_lock_i (f_pll_lock),
.pll_capable_i (f_pll_capable),
.pll_sel_o (),
.pll_apply_o (),
.select_ext_clk_o(),
.pll_ctrl (pll_ctrl)
.sys_clk_i (clk_i),
.sys_rst_n_i (rst_n_i),
.ext_clk_i (clk_i),
.ext_rst_n_i (rst_n_i),
.pll_lock_i (f_pll_lock),
.pll_capable_i (f_pll_capable),
.hp_idle_i (1'b1),
.pclk_idle_i (1'b1),
.timeout_i (16'd16),
.force_safe_i (1'b0),
.clear_fault_i (1'b0),
.pll_sel_o (),
.pll_apply_o (pll_apply),
.sel_ext_clk_o (select_ext_clk),
.hp_block_o (),
.lp_force_ref_o(),
.pll_fault_o (),
.pll_ctrl (pll_ctrl)
);
initial begin
+26 -22
View File
@@ -10,12 +10,13 @@
module pll_rcu_formal;
localparam [2:0] PLL_IDLE = 3'd0;
localparam [2:0] PLL_SAFE = 3'd1;
localparam [2:0] PLL_APPLY = 3'd2;
localparam [2:0] PLL_WAIT_LOCK = 3'd3;
localparam [2:0] PLL_SWITCH = 3'd4;
localparam [2:0] PLL_RESPOND = 3'd5;
localparam [3:0] PLL_APPLY = 4'd4;
localparam [3:0] PLL_WAIT_LOCK_LOW = 4'd5;
localparam [3:0] PLL_QUALIFY_LOCK = 4'd6;
localparam [3:0] PLL_SWITCH = 4'd7;
localparam [3:0] PLL_RESTORE_LP = 4'd8;
localparam [3:0] PLL_RESPOND = 4'd9;
localparam [3:0] PLL_FAIL_SAFE = 4'd10;
(* anyseq *) (* gclk *)reg clk_i;
wire rst_n_i;
@@ -27,13 +28,12 @@ module pll_rcu_formal;
wire rsp_ready;
wire pll_lock;
wire pll_capable;
wire [2:0] state;
wire [3:0] state;
wire pll_apply;
wire select_ext_clk;
wire active_valid;
wire safe_clk;
wire active_lock;
wire lock_seen_low;
wire [1:0] error;
pll_rcu_formal_design u_design (
@@ -53,7 +53,6 @@ module pll_rcu_formal;
.active_valid (active_valid),
.safe_clk (safe_clk),
.active_lock (active_lock),
.lock_seen_low (lock_seen_low),
.error (error)
);
@@ -64,25 +63,30 @@ module pll_rcu_formal;
end
if (rst_n_i) begin
assert(!(state == PLL_SAFE || state == PLL_APPLY || state == PLL_WAIT_LOCK) ||
select_ext_clk);
assert (!pll_apply || state != PLL_IDLE && state != PLL_SAFE);
if (f_past_valid && $past(state == PLL_SAFE && !pll_capable)) begin
assert (state == PLL_RESPOND);
assert (safe_clk);
assert (!active_valid);
assert (error == 2'd1);
end
if (f_past_valid && $past(state == PLL_WAIT_LOCK && pll_lock && lock_seen_low)) begin
assert (state == PLL_SWITCH);
assert (!(state == PLL_APPLY || state == PLL_WAIT_LOCK_LOW ||
state == PLL_QUALIFY_LOCK) || (select_ext_clk && safe_clk));
assert (!(state == PLL_APPLY || state == PLL_WAIT_LOCK_LOW ||
state == PLL_QUALIFY_LOCK) ||
(error == 2'd0 && !active_valid && !active_lock));
assert (!pll_apply || state == PLL_WAIT_LOCK_LOW);
if (state == PLL_SWITCH) begin
assert (active_valid);
assert (!safe_clk);
assert (select_ext_clk);
assert (safe_clk);
assert (active_lock);
assert (error == 2'd0);
end
if (f_past_valid && $past(state == PLL_SWITCH)) begin
assert (state == PLL_RESPOND);
assert (state == PLL_RESTORE_LP);
assert (!select_ext_clk);
assert (!safe_clk);
end
if (f_past_valid && $past(state == PLL_FAIL_SAFE)) begin
assert (state == PLL_RESPOND);
assert (select_ext_clk);
assert (safe_clk);
assert (!active_valid);
assert (!active_lock);
end
cover (state == PLL_SWITCH && active_valid && active_lock);
cover (state == PLL_RESPOND && error == 2'd1);
+24 -3
View File
@@ -53,6 +53,7 @@ module sysctrl_formal_design (
);
sysctrl_if sysctrl ();
pll_ctrl_if pll_ctrl ();
clock_ctrl_if clock_ctrl ();
(* anyseq *)logic f_apb_sel;
(* anyseq *)logic [31:0] f_apb_addr;
@@ -70,6 +71,12 @@ module sysctrl_formal_design (
(* anyseq *)logic [31:0] f_fault_addr;
(* anyseq *)logic [ 3:0] f_fault_wstrb;
(* anyseq *)logic f_fault_reserved;
(* anyseq *)logic f_clock_req_ready;
(* anyseq *)logic f_clock_rsp_valid;
(* anyseq *)logic [31:0] f_clock_rsp_data;
(* anyseq *)logic [31:0] f_clock_current;
(* anyseq *)logic [31:0] f_clock_fault;
(* anyseq *)logic [31:0] f_clock_memory;
assign apb4.psel = f_apb_sel;
assign apb4.penable = f_apb_sel;
@@ -95,6 +102,9 @@ module sysctrl_formal_design (
assign sysctrl.perf_flash_wait_i = '0;
assign sysctrl.rtc_wake_i = 1'b0;
assign sysctrl.hp_present_i = 1'b1;
assign sysctrl.hp_actual_released_i = 1'b0;
assign sysctrl.hp_draining_i = 1'b0;
assign sysctrl.hp_forced_fault_i = 1'b0;
assign pll_ctrl.req_ready_i = f_pll_req_ready;
assign pll_ctrl.rsp_active_sel_i = f_pll_active_sel;
assign pll_ctrl.rsp_active_valid_i = f_pll_active_valid;
@@ -103,6 +113,12 @@ module sysctrl_formal_design (
assign pll_ctrl.rsp_error_i = f_pll_rsp_error;
assign pll_ctrl.rsp_valid_i = f_pll_rsp_valid;
assign pll_ctrl.capable_i = f_pll_capable;
assign clock_ctrl.req_ready_i = f_clock_req_ready;
assign clock_ctrl.rsp_valid_i = f_clock_rsp_valid;
assign clock_ctrl.rsp_data_i = f_clock_rsp_data;
assign clock_ctrl.current_i = f_clock_current;
assign clock_ctrl.fault_i = f_clock_fault;
assign clock_ctrl.memory_i = f_clock_memory;
assign rib_valid = apb4.psel;
assign rib_addr = apb4.paddr;
@@ -111,8 +127,12 @@ module sysctrl_formal_design (
assign rib_ready = apb4.pready;
assign ip_sel = sysctrl.ip_sel_o;
assign core_sel = u_dut.u_sysctrl_core.s_sysctrl_coresel_q;
assign user_reset = {{(32 - `USER_CORE_COUNT) {1'b0}}, u_dut.u_sysctrl_core.s_user_reset_q};
assign user_reset_mask = {{(32 - `USER_CORE_COUNT) {1'b0}}, {`USER_CORE_COUNT{1'b1}}};
assign user_reset = {
{(32 - `USER_CORE_STORAGE_COUNT) {1'b0}}, u_dut.u_sysctrl_core.s_user_reset_q
};
assign user_reset_mask = {
{(32 - `USER_CORE_STORAGE_COUNT) {1'b0}}, {`USER_CORE_STORAGE_COUNT{1'b1}}
};
assign user_core_count = `USER_CORE_COUNT;
assign user_bus_enable = u_dut.u_sysctrl_core.s_user_running_q;
assign user_config_error = u_dut.u_sysctrl_core.s_user_config_err_q;
@@ -147,7 +167,8 @@ module sysctrl_formal_design (
.fault_reserved_i(f_fault_reserved),
.apb4 (apb4),
.sysctrl (sysctrl),
.pll_ctrl (pll_ctrl)
.pll_ctrl (pll_ctrl),
.clock_ctrl (clock_ctrl)
);
initial begin
+7 -1
View File
@@ -107,6 +107,11 @@ module sysctrl_formal;
end
if (rst_n_i && f_past_valid) begin
if (user_core_count == 0) begin
assert (user_reset == user_reset_mask);
assert (!user_bus_enable);
assert (ip_sel == 0);
end
if ($past(
rst_n_i && rib_valid && !rib_ready && rib_wstrb[0] &&
rib_addr[7:0] == 8'h00 && rib_wdata[4:0] < user_core_count &&
@@ -123,6 +128,7 @@ module sysctrl_formal;
end
if ($past(
rst_n_i && rib_valid && !rib_ready && rib_wstrb[0] &&
user_core_count != 0 &&
rib_addr[7:0] == SYSCTRL_USER_CORE_RESET_OFFSET &&
(rib_wdata & user_reset_mask) == user_reset_mask
)) begin
@@ -130,7 +136,7 @@ module sysctrl_formal;
assert (!user_bus_enable);
end
if ($past(
rst_n_i && rib_valid && !rib_ready && |rib_wstrb &&
rst_n_i && user_core_count != 0 && rib_valid && !rib_ready && |rib_wstrb &&
rib_addr[7:0] == SYSCTRL_IPSEL_OFFSET
)) begin
assert (ip_sel == $past(rib_wdata[7:0]));
+5 -3
View File
@@ -5,6 +5,7 @@ FORMAL_SOLVER := bitwuzla
FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
FORMAL_DEPTH ?= 20
FORMAL_PLL_RCU_DEPTH ?= 32
FORMAL_WS2812_DEPTH ?= 120
FORMAL_I2C_DEPTH ?= 80
FORMAL_CLINT_DEPTH ?= 32
@@ -76,6 +77,7 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/peripheral/gpio_reg.sv \
$(ROOT_PATH)/rtl/ip/peripheral/apb4_gpio.sv \
$(ROOT_PATH)/rtl/ip/peripheral/pll_ctrl_if.sv \
$(ROOT_PATH)/rtl/ip/peripheral/clock_ctrl_if.sv \
$(ROOT_PATH)/rtl/ip/peripheral/sysctrl_if.sv \
$(ROOT_PATH)/rtl/ip/peripheral/sysctrl_define.svh \
$(ROOT_PATH)/rtl/ip/peripheral/sysctrl_reg.sv \
@@ -175,7 +177,7 @@ $(FORMAL_DIR)/%/formal.fl: $(FORMAL_FILELIST_GENERATOR) $(FORMAL_SOURCE_FILES) \
--user-extensions-dir $(USER_EXTENSIONS_DIR)
$(FORMAL_DIR)/%/design.v: $(FORMAL_DIR)/%/formal.fl $(RTL_PATH)/script/convt_sv2v.py \
$(RTL_PATH)/script/filelist.py
$(RTL_PATH)/script/filelist.py $(FORMAL_SOURCE_FILES)
python3 $(ROOT_PATH)/scripts/run_flow.py --tool formal-sv2v \
--log $(@D)/sv2v.log --result $(@D)/result-sv2v.json \
-- python3 $(RTL_PATH)/script/convt_sv2v.py -f $< --output $@
@@ -184,14 +186,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode $(if $(filter dma sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH)))))))) --output $@
--mode $(if $(filter dma sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode cover --depth $(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH)))))))) \
--mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))) \
$(if $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
+1
View File
@@ -105,6 +105,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/perf.c \
$(ROOT_PATH)/crt/src/hal/lcd.c \
$(ROOT_PATH)/crt/src/hal/onchip_sram.c \
$(ROOT_PATH)/crt/src/hal/memory.c \
$(ROOT_PATH)/crt/src/hal/psram_math.c \
$(ROOT_PATH)/crt/src/hal/psram.c \
$(ROOT_PATH)/crt/src/hal/opipsram_math.c \
+111 -19
View File
@@ -18,8 +18,11 @@ module apb4_periph (
input logic clk_aud_i,
input logic rst_aud_n_i,
input logic clk_ulpi_i,
input logic clk_mem_i,
input logic rst_mem_n_i,
input logic debug_halted_i,
input logic timebase_tick_i,
input logic ext_h_hp_irq_i,
axi4_if.slave cfg_axi4,
axi4_if.slave psram_axi4,
axi4_if.slave xpi_axi4,
@@ -116,6 +119,20 @@ axi4_stream_if #(
clint_if u_clint_if ();
clint_if #(.HartNum(2)) u_hp_clint_if ();
dma_req_if u_dma_req_if ();
apb4_if u_psram_mem_apb4_if (
.pclk (clk_mem_i),
.presetn(rst_mem_n_i)
);
apb4_if u_xpi_mem_apb4_if (
.pclk (clk_mem_i),
.presetn(rst_mem_n_i)
);
apb4_if u_opipsram_mem_apb4_if (
.pclk (clk_mem_i),
.presetn(rst_mem_n_i)
);
psram_if u_psram_mem_if ();
opipsram_if u_opipsram_mem_if ();
logic s_dma_i2s_tx_stall, s_dma_i2s_rx_stall;
logic s_dma_xpi_tx_stall, s_dma_xpi_rx_stall;
@@ -131,9 +148,16 @@ axi4_stream_if #(
logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq;
logic s_xpi_irq;
logic s_xpi_irq_mem;
logic s_psram_irq_mem;
logic s_opipsram_irq_mem;
logic s_dma_xfer_done_mem;
logic s_dma_xpi_tx_stall_mem;
logic s_dma_xpi_rx_stall_mem;
logic s_mailbox_lp_irq, s_mailbox_hp_irq;
logic [31:0] s_hp_plic_source;
logic [ 1:0] s_hp_plic_context_irq;
logic [ 3:0] s_unused_optional_status;
`ifdef PDK_GF180
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
@@ -178,10 +202,78 @@ axi4_stream_if #(
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1];
apb4_async_bridge u_psram_cfg_mem_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i(rst_mem_n_i),
.src_apb4 (u_psram_apb4_if),
.dst_apb4 (u_psram_mem_apb4_if)
);
apb4_async_bridge u_xpi_cfg_mem_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i(rst_mem_n_i),
.src_apb4 (u_xpi_apb4_if),
.dst_apb4 (u_xpi_mem_apb4_if)
);
apb4_async_bridge u_opipsram_cfg_mem_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i(rst_mem_n_i),
.src_apb4 (u_opipsram_apb4_if),
.dst_apb4 (u_opipsram_mem_apb4_if)
);
cdc_event_bridge u_xpi_dma_done_cdc (
.src_clk_i (clk_i),
.src_rst_n_i (rst_n_i),
.event_i (s_dma_xfer_done),
.source_ready_o(s_unused_optional_status[0]),
.source_busy_o (s_unused_optional_status[1]),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i (rst_mem_n_i),
.event_o (s_dma_xfer_done_mem)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(5)
) u_memory_status_sync (
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.dat_i({
s_xpi_irq_mem,
s_psram_irq_mem,
s_opipsram_irq_mem,
s_dma_xpi_tx_stall_mem,
s_dma_xpi_rx_stall_mem
}),
.dat_o({s_xpi_irq, psram.irq_o, opipsram.irq_o, s_dma_xpi_tx_stall, s_dma_xpi_rx_stall})
);
assign psram.sck_o = u_psram_mem_if.sck_o;
assign psram.nss_o = u_psram_mem_if.nss_o;
assign psram.io_oe_o = u_psram_mem_if.io_oe_o;
assign psram.io_do_o = u_psram_mem_if.io_do_o;
assign u_psram_mem_if.io_di_i = psram.io_di_i;
assign s_psram_irq_mem = u_psram_mem_if.irq_o;
assign opipsram.ck_o = u_opipsram_mem_if.ck_o;
assign opipsram.cs_n_o = u_opipsram_mem_if.cs_n_o;
assign opipsram.dq_oe_o = u_opipsram_mem_if.dq_oe_o;
assign opipsram.dq_o = u_opipsram_mem_if.dq_o;
assign opipsram.rwds_oe_o = u_opipsram_mem_if.rwds_oe_o;
assign opipsram.rwds_o = u_opipsram_mem_if.rwds_o;
assign u_opipsram_mem_if.dq_i = opipsram.dq_i;
assign u_opipsram_mem_if.rwds_i = opipsram.rwds_i;
assign s_opipsram_irq_mem = u_opipsram_mem_if.irq_o;
always_comb begin
s_hp_plic_source = '0;
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;
end
`include "apb4_periph_irq_bindings.svh"
@@ -215,8 +307,8 @@ axi4_stream_if #(
apb4_uart u_apb4_uart1 (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(),
.dma_rx_stall_o(),
.dma_tx_stall_o(s_unused_optional_status[2]),
.dma_rx_stall_o(s_unused_optional_status[3]),
.apb4 (u_uart1_apb4_if),
.uart (uart1)
);
@@ -264,11 +356,11 @@ axi4_stream_if #(
);
apb4_psram u_apb4_psram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.cfg_apb4(u_psram_apb4_if),
.clk_i (clk_mem_i),
.rst_n_i (rst_mem_n_i),
.cfg_apb4(u_psram_mem_apb4_if),
.mem_axi4(psram_axi4),
.psram (psram)
.psram (u_psram_mem_if)
);
apb4_spisd u_apb4_spisd (
@@ -309,13 +401,13 @@ axi4_stream_if #(
);
apb4_xpi u_apb4_xpi (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_xfer_done_i(s_dma_xfer_done),
.dma_tx_stall_o (s_dma_xpi_tx_stall),
.dma_rx_stall_o (s_dma_xpi_rx_stall),
.irq_o (s_xpi_irq),
.apb4 (u_xpi_apb4_if),
.clk_i (clk_mem_i),
.rst_n_i (rst_mem_n_i),
.dma_xfer_done_i(s_dma_xfer_done_mem),
.dma_tx_stall_o (s_dma_xpi_tx_stall_mem),
.dma_rx_stall_o (s_dma_xpi_rx_stall_mem),
.irq_o (s_xpi_irq_mem),
.apb4 (u_xpi_mem_apb4_if),
.mem_axi4 (xpi_axi4),
.xpi (xpi)
);
@@ -406,13 +498,13 @@ axi4_stream_if #(
);
apb4_opipsram u_apb4_opipsram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_phy_i (clk_i),
.rst_phy_n_i(rst_n_i),
.cfg_apb4 (u_opipsram_apb4_if),
.clk_i (clk_mem_i),
.rst_n_i (rst_mem_n_i),
.clk_phy_i (clk_mem_i),
.rst_phy_n_i(rst_mem_n_i),
.cfg_apb4 (u_opipsram_mem_apb4_if),
.mem_axi4 (opipsram_axi4),
.psram (opipsram)
.psram (u_opipsram_mem_if)
);
apb4_i2c u_apb4_i2c1 (
+30 -7
View File
@@ -22,6 +22,7 @@ module apb4_system (
input logic clk_aud_i,
input logic rst_aud_n_i,
input logic debug_halted_i,
input logic ext_h_data_idle_i,
axi4_if.slave axi4,
axi4_if.master ext_h_axi4,
pwm_if.dut pwm,
@@ -30,6 +31,14 @@ module apb4_system (
user_gpio_if.user_ip user_gpio,
output logic rtc_wake_o,
output logic wdg_reset_req_o,
output logic ext_h_block_o,
output logic [31:0] ext_h_read_base_o,
output logic [31:0] ext_h_read_limit_o,
output logic [31:0] ext_h_write_base_o,
output logic [31:0] ext_h_write_limit_o,
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 [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o
// verilog_format: on
);
@@ -75,10 +84,16 @@ module apb4_system (
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;
`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;
`ifdef PDK_IHP130
localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082;
`elsif PDK_GF180
@@ -219,13 +234,21 @@ axi42apb4_system u_axi42apb4_system (
`endif
extension_subsystem u_extension_subsystem (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ext_l_apb4 (u_ext_l_apb4_if),
.ext_h_apb4 (u_ext_h_apb4_if),
.ext_h_axi4 (ext_h_axi4),
.ext_l_irq_o(s_ext_l_irq),
.ext_h_irq_o(s_ext_h_irq)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ext_h_data_idle_i (ext_h_data_idle_i),
.ext_l_apb4 (u_ext_l_apb4_if),
.ext_h_apb4 (u_ext_h_apb4_if),
.ext_h_axi4 (ext_h_axi4),
.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_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),
.ext_h_write_limit_o(ext_h_write_limit_o),
.ext_h_timeout_o (ext_h_timeout_o)
);
endmodule
+133 -55
View File
@@ -13,6 +13,9 @@ module axi4_async_bridge #(
input logic src_rst_n_i,
input logic dst_clk_i,
input logic dst_rst_n_i,
input logic clear_i,
output logic clear_busy_o,
output logic [7:0] epoch_o,
axi4_if.slave src_axi4,
axi4_if.master dst_axi4
// verilog_format: on
@@ -34,6 +37,40 @@ module axi4_async_bridge #(
logic [ArWidth-1:0] s_ar_dst_data;
logic [ RWidth-1:0] s_r_dst_data;
logic [ RWidth-1:0] s_r_src_data;
logic s_clear_src;
logic s_clear_dst;
logic s_clear_q;
logic s_clear_dst_q;
logic s_clear_src_pulse;
logic s_clear_dst_pulse;
logic [7:0] s_epoch_d, s_epoch_q;
logic s_aw_src_clear_busy, s_aw_dst_clear_busy;
logic s_w_src_clear_busy, s_w_dst_clear_busy;
logic s_b_src_clear_busy, s_b_dst_clear_busy;
logic s_ar_src_clear_busy, s_ar_dst_clear_busy;
logic s_r_src_clear_busy, s_r_dst_clear_busy;
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_clear_src_sync (
.clk_i (src_clk_i),
.rst_n_i(src_rst_n_i),
.dat_i (clear_i),
.dat_o (s_clear_src)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_clear_sync (
.clk_i (dst_clk_i),
.rst_n_i(dst_rst_n_i),
.dat_i (clear_i),
.dat_o (s_clear_dst)
);
assign s_clear_src_pulse = s_clear_src && !s_clear_q;
assign s_clear_dst_pulse = s_clear_dst && !s_clear_dst_q;
assign s_aw_src_data = {
src_axi4.awid,
@@ -62,58 +99,67 @@ module axi4_async_bridge #(
dst_axi4.awuser
} = s_aw_dst_data;
cdc_fifo #(
cdc_fifo_warm_flush #(
.DATA_WIDTH (AwWidth),
.BUFFER_DEPTH(FifoDepth)
) u_aw_fifo (
.src_clk_i (src_clk_i),
.src_rst_n_i(src_rst_n_i),
.src_data_i (s_aw_src_data),
.src_valid_i(src_axi4.awvalid),
.src_ready_o(src_axi4.awready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i(dst_rst_n_i),
.dst_data_o (s_aw_dst_data),
.dst_valid_o(dst_axi4.awvalid),
.dst_ready_i(dst_axi4.awready)
.src_clk_i (src_clk_i),
.src_rst_n_i (src_rst_n_i),
.src_clear_i (s_clear_src_pulse),
.src_clear_busy_o(s_aw_src_clear_busy),
.src_data_i (s_aw_src_data),
.src_valid_i (src_axi4.awvalid),
.src_ready_o (src_axi4.awready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i (dst_rst_n_i),
.dst_clear_busy_o(s_aw_dst_clear_busy),
.dst_data_o (s_aw_dst_data),
.dst_valid_o (dst_axi4.awvalid),
.dst_ready_i (dst_axi4.awready)
);
assign s_w_src_data = {src_axi4.wdata, src_axi4.wstrb, src_axi4.wlast, src_axi4.wuser};
assign {dst_axi4.wdata, dst_axi4.wstrb, dst_axi4.wlast, dst_axi4.wuser} = s_w_dst_data;
cdc_fifo #(
cdc_fifo_warm_flush #(
.DATA_WIDTH (WWidth),
.BUFFER_DEPTH(FifoDepth)
) u_w_fifo (
.src_clk_i (src_clk_i),
.src_rst_n_i(src_rst_n_i),
.src_data_i (s_w_src_data),
.src_valid_i(src_axi4.wvalid),
.src_ready_o(src_axi4.wready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i(dst_rst_n_i),
.dst_data_o (s_w_dst_data),
.dst_valid_o(dst_axi4.wvalid),
.dst_ready_i(dst_axi4.wready)
.src_clk_i (src_clk_i),
.src_rst_n_i (src_rst_n_i),
.src_clear_i (s_clear_src_pulse),
.src_clear_busy_o(s_w_src_clear_busy),
.src_data_i (s_w_src_data),
.src_valid_i (src_axi4.wvalid),
.src_ready_o (src_axi4.wready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i (dst_rst_n_i),
.dst_clear_busy_o(s_w_dst_clear_busy),
.dst_data_o (s_w_dst_data),
.dst_valid_o (dst_axi4.wvalid),
.dst_ready_i (dst_axi4.wready)
);
assign s_b_dst_data = {dst_axi4.bid, dst_axi4.bresp, dst_axi4.buser};
assign {src_axi4.bid, src_axi4.bresp, src_axi4.buser} = s_b_src_data;
cdc_fifo #(
cdc_fifo_warm_flush #(
.DATA_WIDTH (BWidth),
.BUFFER_DEPTH(FifoDepth)
) u_b_fifo (
.src_clk_i (dst_clk_i),
.src_rst_n_i(dst_rst_n_i),
.src_data_i (s_b_dst_data),
.src_valid_i(dst_axi4.bvalid),
.src_ready_o(dst_axi4.bready),
.dst_clk_i (src_clk_i),
.dst_rst_n_i(src_rst_n_i),
.dst_data_o (s_b_src_data),
.dst_valid_o(src_axi4.bvalid),
.dst_ready_i(src_axi4.bready)
.src_clk_i (dst_clk_i),
.src_rst_n_i (dst_rst_n_i),
.src_clear_i (s_clear_dst_pulse),
.src_clear_busy_o(s_b_dst_clear_busy),
.src_data_i (s_b_dst_data),
.src_valid_i (dst_axi4.bvalid),
.src_ready_o (dst_axi4.bready),
.dst_clk_i (src_clk_i),
.dst_rst_n_i (src_rst_n_i),
.dst_clear_busy_o(s_b_src_clear_busy),
.dst_data_o (s_b_src_data),
.dst_valid_o (src_axi4.bvalid),
.dst_ready_i (src_axi4.bready)
);
assign s_ar_src_data = {
@@ -143,20 +189,23 @@ module axi4_async_bridge #(
dst_axi4.aruser
} = s_ar_dst_data;
cdc_fifo #(
cdc_fifo_warm_flush #(
.DATA_WIDTH (ArWidth),
.BUFFER_DEPTH(FifoDepth)
) u_ar_fifo (
.src_clk_i (src_clk_i),
.src_rst_n_i(src_rst_n_i),
.src_data_i (s_ar_src_data),
.src_valid_i(src_axi4.arvalid),
.src_ready_o(src_axi4.arready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i(dst_rst_n_i),
.dst_data_o (s_ar_dst_data),
.dst_valid_o(dst_axi4.arvalid),
.dst_ready_i(dst_axi4.arready)
.src_clk_i (src_clk_i),
.src_rst_n_i (src_rst_n_i),
.src_clear_i (s_clear_src_pulse),
.src_clear_busy_o(s_ar_src_clear_busy),
.src_data_i (s_ar_src_data),
.src_valid_i (src_axi4.arvalid),
.src_ready_o (src_axi4.arready),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i (dst_rst_n_i),
.dst_clear_busy_o(s_ar_dst_clear_busy),
.dst_data_o (s_ar_dst_data),
.dst_valid_o (dst_axi4.arvalid),
.dst_ready_i (dst_axi4.arready)
);
assign s_r_dst_data = {
@@ -165,22 +214,51 @@ module axi4_async_bridge #(
assign {src_axi4.rid, src_axi4.rdata, src_axi4.rresp, src_axi4.rlast,
src_axi4.ruser} = s_r_src_data;
cdc_fifo #(
cdc_fifo_warm_flush #(
.DATA_WIDTH (RWidth),
.BUFFER_DEPTH(FifoDepth)
) u_r_fifo (
.src_clk_i (dst_clk_i),
.src_rst_n_i(dst_rst_n_i),
.src_data_i (s_r_dst_data),
.src_valid_i(dst_axi4.rvalid),
.src_ready_o(dst_axi4.rready),
.dst_clk_i (src_clk_i),
.dst_rst_n_i(src_rst_n_i),
.dst_data_o (s_r_src_data),
.dst_valid_o(src_axi4.rvalid),
.dst_ready_i(src_axi4.rready)
.src_clk_i (dst_clk_i),
.src_rst_n_i (dst_rst_n_i),
.src_clear_i (s_clear_dst_pulse),
.src_clear_busy_o(s_r_dst_clear_busy),
.src_data_i (s_r_dst_data),
.src_valid_i (dst_axi4.rvalid),
.src_ready_o (dst_axi4.rready),
.dst_clk_i (src_clk_i),
.dst_rst_n_i (src_rst_n_i),
.dst_clear_busy_o(s_r_src_clear_busy),
.dst_data_o (s_r_src_data),
.dst_valid_o (src_axi4.rvalid),
.dst_ready_i (src_axi4.rready)
);
assign clear_busy_o = s_aw_src_clear_busy || s_aw_dst_clear_busy ||
s_w_src_clear_busy || s_w_dst_clear_busy ||
s_b_src_clear_busy || s_b_dst_clear_busy ||
s_ar_src_clear_busy || s_ar_dst_clear_busy ||
s_r_src_clear_busy || s_r_dst_clear_busy;
assign s_epoch_d = (s_clear_src_pulse && !clear_busy_o) ? s_epoch_q + 1'b1 : s_epoch_q;
assign epoch_o = s_epoch_q;
always_ff @(posedge src_clk_i or negedge src_rst_n_i) begin
if (!src_rst_n_i) begin
s_clear_q <= 1'b0;
s_epoch_q <= '0;
end else begin
s_clear_q <= s_clear_src;
s_epoch_q <= s_epoch_d;
end
end
always_ff @(posedge dst_clk_i or negedge dst_rst_n_i) begin
if (!dst_rst_n_i) begin
s_clear_dst_q <= 1'b0;
end else begin
s_clear_dst_q <= s_clear_dst;
end
end
`ifndef SYNTHESIS
initial begin
if ((AddrWidth < 1) || (DataWidth < 8) || ((DataWidth % 8) != 0) ||
+589 -154
View File
@@ -4,19 +4,40 @@
`include "mmap_define.svh"
module axi4_data_crossbar #(
parameter int unsigned NumMasters = 8,
parameter int unsigned NumTargets = 6
parameter int unsigned NumMasters = 8,
parameter int unsigned NumTargets = 6,
parameter int unsigned StarvationCycles = 256
) (
// verilog_format: off -- preserve the data-plane contract columns
input logic clk_i,
input logic rst_n_i,
input logic block_new_i,
input logic recovery_i,
input logic [1:0] mem_pad_mode_i,
axi4_if.slave masters [NumMasters],
axi4_if.master targets [NumTargets],
output logic idle_o
input logic [31:0] ext_h_read_base_i,
input logic [31:0] ext_h_read_limit_i,
input logic [31:0] ext_h_write_base_i,
input logic [31:0] ext_h_write_limit_i,
axi4_if.slave masters [NumMasters],
axi4_if.master targets [NumTargets],
output logic idle_o,
output logic [7:0] outstanding_read_o,
output logic [7:0] outstanding_write_o,
output logic fault_valid_o,
output logic [2:0] fault_master_o,
output logic [2:0] fault_target_o,
output logic [31:0] fault_addr_o,
output logic fault_write_o,
output logic [3:0] fault_reason_o
// verilog_format: on
);
localparam int unsigned MasterWidth = $clog2(NumMasters);
localparam int unsigned TargetWidth = $clog2(NumTargets);
localparam int unsigned SourceIds = 8;
localparam int unsigned RouteDepth = 2;
localparam int unsigned CountWidth = 3;
localparam int unsigned AgeWidth = $clog2(StarvationCycles + 1);
localparam logic [TargetWidth-1:0] TargetSram = TargetWidth'(0);
localparam logic [TargetWidth-1:0] TargetSdram = TargetWidth'(1);
localparam logic [TargetWidth-1:0] TargetQpi = TargetWidth'(2);
@@ -24,6 +45,11 @@ module axi4_data_crossbar #(
localparam logic [TargetWidth-1:0] TargetXpi = TargetWidth'(4);
localparam logic [TargetWidth-1:0] TargetError = TargetWidth'(5);
localparam logic [3:0] FaultUnmapped = 4'd1;
localparam logic [3:0] FaultInactivePad = 4'd2;
localparam logic [3:0] FaultAccess = 4'd3;
localparam logic [3:0] FaultProtocol = 4'd4;
typedef struct packed {
logic [5:0] id;
logic [31:0] addr;
@@ -59,11 +85,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;
@@ -75,11 +101,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;
@@ -93,31 +119,70 @@ module axi4_data_crossbar #(
logic [NumTargets-1:0][ NumMasters-1:0] s_read_req;
logic [NumTargets-1:0][ NumMasters-1:0] s_write_req;
logic [NumTargets-1:0][ NumMasters-1:0] s_read_grant;
logic [NumTargets-1:0][ NumMasters-1:0] s_write_grant;
logic s_unused_grant;
logic [NumTargets-1:0][ NumMasters-1:0] s_read_priority_req;
logic [NumTargets-1:0][ NumMasters-1:0] s_write_priority_req;
logic [NumTargets-1:0][ NumMasters-1:0] unused_read_grant;
logic [NumTargets-1:0][ NumMasters-1:0] unused_write_grant;
logic [NumTargets-1:0][MasterWidth-1:0] s_read_selected;
logic [NumTargets-1:0][MasterWidth-1:0] s_write_selected;
logic [NumTargets-1:0] s_read_grant_valid;
logic [NumTargets-1:0] s_write_grant_valid;
logic [NumTargets-1:0] s_read_capture;
logic [NumTargets-1:0] s_write_capture;
logic [NumTargets-1:0] s_read_terminal;
logic [NumTargets-1:0] s_write_terminal;
logic [NumTargets-1:0] s_read_owner_valid_d;
logic [NumTargets-1:0] s_read_owner_valid_q;
logic [NumTargets-1:0] s_write_owner_valid_d;
logic [NumTargets-1:0] s_write_owner_valid_q;
logic [NumTargets-1:0][MasterWidth-1:0] s_read_owner_d;
logic [NumTargets-1:0][MasterWidth-1:0] s_read_owner_q;
logic [NumTargets-1:0][MasterWidth-1:0] s_write_owner_d;
logic [NumTargets-1:0][MasterWidth-1:0] s_write_owner_q;
logic [NumMasters-1:0] s_master_read_busy_d;
logic [NumMasters-1:0] s_master_read_busy_q;
logic [NumMasters-1:0] s_master_write_busy_d;
logic [NumMasters-1:0] s_master_write_busy_q;
logic [NumMasters-1:0][TargetWidth-1:0] s_master_read_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_master_write_target;
logic [NumMasters-1:0][ NumTargets-1:0] s_read_resp_req;
logic [NumMasters-1:0][ NumTargets-1:0] s_write_resp_req;
logic [NumMasters-1:0][ NumTargets-1:0] unused_read_resp_grant;
logic [NumMasters-1:0][ NumTargets-1:0] unused_write_resp_grant;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_resp_selected;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_resp_selected;
logic [NumMasters-1:0] s_read_resp_valid;
logic [NumMasters-1:0] s_write_resp_valid;
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 [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 [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;
logic [NumTargets-1:0][NumMasters-1:0][AgeWidth-1:0] s_write_age_d, s_write_age_q;
logic [NumMasters-1:0][RouteDepth-1:0][TargetWidth-1:0] s_write_route_d;
logic [NumMasters-1:0][RouteDepth-1:0][TargetWidth-1:0] s_write_route_q;
logic [NumMasters-1:0] s_route_read_ptr_d, s_route_read_ptr_q;
logic [NumMasters-1:0] s_route_write_ptr_d, s_route_write_ptr_q;
logic [NumMasters-1:0][1:0] s_route_count_d, s_route_count_q;
logic [NumMasters-1:0] s_write_data_terminal;
logic [NumMasters-1:0] s_read_accept;
logic [NumMasters-1:0] s_write_accept;
logic [NumMasters-1:0][TargetWidth-1:0] s_read_accept_target;
logic [NumMasters-1:0][TargetWidth-1:0] s_write_accept_target;
logic [NumMasters-1:0][ 2:0] s_read_accept_id;
logic [NumMasters-1:0][ 2:0] s_write_accept_id;
logic [NumMasters-1:0][ 2:0] s_read_complete_id;
logic [NumMasters-1:0][ 2:0] s_write_complete_id;
logic [NumTargets-1:0][ 3:0] s_read_max_priority;
logic [NumTargets-1:0][ 3:0] s_write_max_priority;
logic s_fault_valid_d;
logic [ 2:0] s_fault_master_d;
logic [ 2:0] s_fault_target_d;
logic [ 31:0] s_fault_addr_d;
logic s_fault_write_d;
logic [ 3:0] s_fault_reason_d;
function automatic logic [TargetWidth-1:0] decode_target(input logic [31:0] addr,
input logic [1:0] pad_mode);
@@ -129,6 +194,89 @@ module axi4_data_crossbar #(
return TargetError;
endfunction
function automatic logic [3:0] decode_fault_reason(input logic [31:0] addr,
input logic [1:0] pad_mode);
if (`SOC_ADDR_IS_PSRAM(addr) && (pad_mode != 2'd1)) return FaultInactivePad;
if (`SOC_ADDR_IS_OPIPSRAM(addr) && (pad_mode != 2'd2)) return FaultInactivePad;
if (!(
`SOC_ADDR_IS_SRAM(addr)
||
`SOC_ADDR_IS_SDRAM(addr)
||
`SOC_ADDR_IS_PSRAM(addr)
||
`SOC_ADDR_IS_OPIPSRAM(addr)
||
`SOC_ADDR_IS_FLASH(addr)
||
`SOC_ADDR_IS_XPI(addr)
)) begin
return FaultUnmapped;
end
return 4'd0;
endfunction
function automatic logic access_allowed(
input int unsigned master, input logic [31:0] addr, input logic write_access,
input logic [31:0] ext_read_base, input logic [31:0] ext_read_limit,
input logic [31:0] ext_write_base, input logic [31:0] ext_write_limit);
if (master == 6) return 1'b0;
if ((master == 0) && write_access) return 1'b0;
if ((master == 7) && write_access && ((addr < ext_write_base) || (addr > ext_write_limit)))
return 1'b0;
if ((master == 7) && !write_access && ((addr < ext_read_base) || (addr > ext_read_limit)))
return 1'b0;
return 1'b1;
endfunction
function automatic logic burst_legal(input logic [31:0] addr, input logic [7:0] len,
input logic [2:0] size, input logic [1:0] burst,
input logic lock);
logic [32:0] beat_bytes;
logic [32:0] burst_bytes;
logic [32:0] last_addr;
logic [32:0] window_limit;
beat_bytes = 33'd1 << size;
burst_bytes = beat_bytes * ({25'd0, len} + 1'b1);
last_addr = (burst == 2'b00) ?
({1'b0, addr} + beat_bytes - 1'b1) :
({1'b0, addr} + burst_bytes - 1'b1);
window_limit = {1'b0, addr[31:12], 12'd0} + 33'd4096;
return !lock && (size <= 3'd3) &&
((burst == 2'b00) || (burst == 2'b01) ||
((burst == 2'b10) &&
((len == 8'd1) || (len == 8'd3) || (len == 8'd7) || (len == 8'd15)))) &&
(last_addr < window_limit);
endfunction
function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master);
if ((master <= 2) || (master == 7)) return CountWidth'(4);
if ((master == 3) || (master == 4)) return CountWidth'(2);
if (master == 5) return CountWidth'(1);
return '0;
endfunction
function automatic logic [CountWidth-1:0] master_write_limit(input int unsigned master);
if ((master == 1) || (master == 2) || (master == 7)) return CountWidth'(2);
if ((master >= 3) && (master <= 5)) return CountWidth'(1);
return '0;
endfunction
function automatic logic [3:0] master_priority(input int unsigned master,
input logic [3:0] request_qos, input logic aged,
input logic recovery);
logic [3:0] base_priority;
unique case (master)
0, 1: base_priority = 4'd12;
3, 4: base_priority = 4'd10;
2, 7: base_priority = 4'd8;
5: base_priority = recovery ? 4'd15 : 4'd2;
default: base_priority = 4'd0;
endcase
if (aged) return 4'd15;
return (request_qos > base_priority) ? request_qos : base_priority;
endfunction
for (genvar master = 0; master < NumMasters; master++) begin : gen_master_ports
assign m_aw[master] = {
masters[master].awid,
@@ -177,6 +325,29 @@ module axi4_data_crossbar #(
assign masters[master].ruser = m_r[master].user;
assign masters[master].rvalid = m_rvalid[master];
assign m_rready[master] = masters[master].rready;
round_robin_arbiter #(
.CLIENTS(NumTargets)
) u_read_response_arbiter (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.advance_i (s_read_terminal[master]),
.request_i (s_read_resp_req[master]),
.grant_o (unused_read_resp_grant[master]),
.selected_o(s_read_resp_selected[master]),
.valid_o (s_read_resp_valid[master])
);
round_robin_arbiter #(
.CLIENTS(NumTargets)
) u_write_response_arbiter (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.advance_i (s_write_terminal[master]),
.request_i (s_write_resp_req[master]),
.grant_o (unused_write_resp_grant[master]),
.selected_o(s_write_resp_selected[master]),
.valid_o (s_write_resp_valid[master])
);
end
for (genvar target = 0; target < NumTargets; target++) begin : gen_target_ports
@@ -227,80 +398,177 @@ module axi4_data_crossbar #(
round_robin_arbiter #(
.CLIENTS(NumMasters)
) u_read_arbiter (
) u_read_address_arbiter (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.advance_i (s_read_capture[target]),
.request_i (s_read_req[target]),
.grant_o (s_read_grant[target]),
.request_i (s_read_priority_req[target]),
.grant_o (unused_read_grant[target]),
.selected_o(s_read_selected[target]),
.valid_o (s_read_grant_valid[target])
);
round_robin_arbiter #(
.CLIENTS(NumMasters)
) u_write_arbiter (
) u_write_address_arbiter (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.advance_i (s_write_capture[target]),
.request_i (s_write_req[target]),
.grant_o (s_write_grant[target]),
.request_i (s_write_priority_req[target]),
.grant_o (unused_write_grant[target]),
.selected_o(s_write_selected[target]),
.valid_o (s_write_grant_valid[target])
);
end
always_comb begin
s_read_req = '0;
s_write_req = '0;
s_master_read_target = '0;
s_master_write_target = '0;
always_comb begin
s_read_req = '0;
s_write_req = '0;
s_read_decoded_target = '0;
s_write_decoded_target = '0;
s_read_routed_target = '0;
s_write_routed_target = '0;
s_read_fault_reason = '0;
s_write_fault_reason = '0;
for (int master = 0; master < NumMasters; master++) begin
s_master_read_target[master] = decode_target(m_ar[master].addr, mem_pad_mode_i);
s_master_write_target[master] = decode_target(m_aw[master].addr, mem_pad_mode_i);
if (!block_new_i && !s_master_read_busy_q[master] && m_arvalid[master]) begin
s_read_req[s_master_read_target[master]][master] = 1'b1;
s_read_decoded_target[master] = decode_target(m_ar[master].addr, mem_pad_mode_i);
s_write_decoded_target[master] = decode_target(m_aw[master].addr, mem_pad_mode_i);
s_read_fault_reason[master] = decode_fault_reason(m_ar[master].addr, mem_pad_mode_i);
s_write_fault_reason[master] = decode_fault_reason(m_aw[master].addr, mem_pad_mode_i);
if (!access_allowed(
master,
m_ar[master].addr,
1'b0,
ext_h_read_base_i,
ext_h_read_limit_i,
ext_h_write_base_i,
ext_h_write_limit_i
)) begin
s_read_fault_reason[master] = FaultAccess;
end else if (!burst_legal(
m_ar[master].addr,
m_ar[master].len,
m_ar[master].size,
m_ar[master].burst,
m_ar[master].lock
)) begin
s_read_fault_reason[master] = FaultProtocol;
end
if (!block_new_i && !s_master_write_busy_q[master] && m_awvalid[master]) begin
s_write_req[s_master_write_target[master]][master] = 1'b1;
if (!access_allowed(
master,
m_aw[master].addr,
1'b1,
ext_h_read_base_i,
ext_h_read_limit_i,
ext_h_write_base_i,
ext_h_write_limit_i
)) begin
s_write_fault_reason[master] = FaultAccess;
end else if (!burst_legal(
m_aw[master].addr,
m_aw[master].len,
m_aw[master].size,
m_aw[master].burst,
m_aw[master].lock
)) begin
s_write_fault_reason[master] = FaultProtocol;
end
s_read_routed_target[master] = (s_read_fault_reason[master] == 4'd0) ?
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(
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(
master
))) && (s_route_count_q[master] < 2'(RouteDepth)) &&
!s_write_id_busy_q[master][m_aw[master].id[2:0]]) begin
s_write_req[s_write_routed_target[master]][master] = 1'b1;
end
end
end
always_comb begin
s_read_priority_req = '0;
s_write_priority_req = '0;
s_read_max_priority = '0;
s_write_max_priority = '0;
for (int target = 0; target < NumTargets; target++) begin
for (int master = 0; master < NumMasters; master++) begin
if (s_read_req[target][master] && (master_priority(
master,
m_ar[master].qos,
s_read_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
) > s_read_max_priority[target])) begin
s_read_max_priority[target] = master_priority(
master,
m_ar[master].qos,
s_read_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
);
end
if (s_write_req[target][master] && (master_priority(
master,
m_aw[master].qos,
s_write_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
) > s_write_max_priority[target])) begin
s_write_max_priority[target] = master_priority(
master,
m_aw[master].qos,
s_write_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
);
end
end
for (int master = 0; master < NumMasters; master++) begin
s_read_priority_req[target][master] = s_read_req[target][master] && (master_priority(
master,
m_ar[master].qos,
s_read_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
) == s_read_max_priority[target]);
s_write_priority_req[target][master] = s_write_req[target][master] && (master_priority(
master,
m_aw[master].qos,
s_write_age_q[target][master] == AgeWidth'(StarvationCycles),
recovery_i
) == s_write_max_priority[target]);
end
end
end
always_comb begin
m_arready = '0;
m_r = '{default: '0};
m_rvalid = '0;
t_ar = '{default: '0};
t_arvalid = '0;
t_rready = '0;
s_read_capture = '0;
s_read_terminal = '0;
s_read_owner_valid_d = s_read_owner_valid_q;
s_read_owner_d = s_read_owner_q;
s_master_read_busy_d = s_master_read_busy_q;
s_read_accept = '0;
s_read_accept_target = '0;
s_read_accept_id = '0;
for (int target = 0; target < NumTargets; target++) begin
if (!s_read_owner_valid_q[target] && s_read_grant_valid[target]) begin
if (s_read_grant_valid[target]) begin
automatic logic [MasterWidth-1:0] owner = s_read_selected[target];
t_ar[target] = m_ar[owner];
t_arvalid[target] = m_arvalid[owner];
m_arready[owner] = t_arready[target];
s_read_capture[target] = t_arvalid[target] && t_arready[target];
if (s_read_capture[target]) begin
s_read_owner_valid_d[target] = 1'b1;
s_read_owner_d[target] = owner;
s_master_read_busy_d[owner] = 1'b1;
end
end else if (s_read_owner_valid_q[target]) begin
automatic logic [MasterWidth-1:0] owner = s_read_owner_q[target];
m_r[owner] = t_r[target];
m_rvalid[owner] = t_rvalid[target];
t_rready[target] = m_rready[owner];
s_read_terminal[target] = t_rvalid[target] && t_rready[target] && t_r[target].last;
if (s_read_terminal[target]) begin
s_read_owner_valid_d[target] = 1'b0;
s_master_read_busy_d[owner] = 1'b0;
s_read_accept[owner] = 1'b1;
s_read_accept_target[owner] = TargetWidth'(target);
s_read_accept_id[owner] = m_ar[owner].id[2:0];
end
end
end
@@ -308,106 +576,273 @@ module axi4_data_crossbar #(
always_comb begin
m_awready = '0;
m_wready = '0;
m_b = '{default: '0};
m_bvalid = '0;
t_aw = '{default: '0};
t_awvalid = '0;
t_w = '{default: '0};
t_wvalid = '0;
t_bready = '0;
s_write_capture = '0;
s_write_terminal = '0;
s_write_owner_valid_d = s_write_owner_valid_q;
s_write_owner_d = s_write_owner_q;
s_master_write_busy_d = s_master_write_busy_q;
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_owner_valid_q[target] && s_write_grant_valid[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];
if (s_write_capture[target]) begin
s_write_owner_valid_d[target] = 1'b1;
s_write_owner_d[target] = owner;
s_master_write_busy_d[owner] = 1'b1;
end
end else if (s_write_owner_valid_q[target]) begin
automatic logic [MasterWidth-1:0] owner = s_write_owner_q[target];
t_w[target] = m_w[owner];
t_wvalid[target] = m_wvalid[owner];
m_wready[owner] = t_wready[target];
m_b[owner] = t_b[target];
m_bvalid[owner] = t_bvalid[target];
t_bready[target] = m_bready[owner];
s_write_terminal[target] = t_bvalid[target] && t_bready[target];
if (s_write_terminal[target]) begin
s_write_owner_valid_d[target] = 1'b0;
s_master_write_busy_d[owner] = 1'b0;
s_write_accept[owner] = 1'b1;
s_write_accept_target[owner] = TargetWidth'(target);
s_write_accept_id[owner] = m_aw[owner].id[2:0];
end
end
end
end
assign idle_o = !(|s_master_read_busy_q) && !(|s_master_write_busy_q) &&
!(|s_read_owner_valid_q) && !(|s_write_owner_valid_q);
assign s_unused_grant = ^{s_read_grant, s_write_grant};
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];
s_write_data_terminal[master] = t_wvalid[target] && t_wready[target] && t_w[target].last;
end
end
end
end
dffr #(
.DATA_WIDTH(NumTargets)
) u_read_owner_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_owner_valid_d),
.dat_o (s_read_owner_valid_q)
);
dffr #(
.DATA_WIDTH(NumTargets)
) u_write_owner_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_owner_valid_d),
.dat_o (s_write_owner_valid_q)
);
dffr #(
.DATA_WIDTH(NumTargets * MasterWidth)
) u_read_owner_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_owner_d),
.dat_o (s_read_owner_q)
);
dffr #(
.DATA_WIDTH(NumTargets * MasterWidth)
) u_write_owner_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_owner_d),
.dat_o (s_write_owner_q)
);
dffr #(
.DATA_WIDTH(NumMasters)
) u_master_read_busy_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_master_read_busy_d),
.dat_o (s_master_read_busy_q)
);
dffr #(
.DATA_WIDTH(NumMasters)
) u_master_write_busy_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_master_write_busy_d),
.dat_o (s_master_write_busy_q)
);
always_comb begin
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;
end
if (s_write_owner_valid_q[target] && t_bvalid[target]) begin
s_write_resp_req[s_write_owner_q[target]][target] = 1'b1;
end
end
end
always_comb begin
m_r = '{default: '0};
m_rvalid = '0;
t_rready = '0;
s_read_terminal = '0;
s_read_complete_id = '0;
m_b = '{default: '0};
m_bvalid = '0;
t_bready = '0;
s_write_terminal = '0;
s_write_complete_id = '0;
for (int master = 0; master < NumMasters; master++) begin
if (s_read_resp_valid[master]) begin
automatic logic [TargetWidth-1:0] target = s_read_resp_selected[master];
m_r[master] = t_r[target];
m_rvalid[master] = t_rvalid[target];
t_rready[target] = m_rready[master];
s_read_terminal[master] = m_rvalid[master] && m_rready[master] && m_r[master].last;
s_read_complete_id[master] = m_r[master].id[2:0];
end
if (s_write_resp_valid[master]) begin
automatic logic [TargetWidth-1:0] target = s_write_resp_selected[master];
m_b[master] = t_b[target];
m_bvalid[master] = t_bvalid[target];
t_bready[target] = m_bready[master];
s_write_terminal[master] = m_bvalid[master] && m_bready[master];
s_write_complete_id[master] = m_b[master].id[2:0];
end
end
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_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_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_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_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_write_id_busy_d[master][s_write_accept_id[master]] = 1'b1;
end
end
end
always_comb begin
s_write_route_d = s_write_route_q;
s_route_read_ptr_d = s_route_read_ptr_q;
s_route_write_ptr_d = s_route_write_ptr_q;
s_route_count_d = s_route_count_q;
for (int master = 0; master < NumMasters; master++) begin
unique case ({
s_write_accept[master], s_write_data_terminal[master]
})
2'b10: s_route_count_d[master] = s_route_count_q[master] + 1'b1;
2'b01: s_route_count_d[master] = s_route_count_q[master] - 1'b1;
default: s_route_count_d[master] = s_route_count_q[master];
endcase
if (s_write_accept[master]) begin
s_write_route_d[master][s_route_write_ptr_q[master]] = s_write_accept_target[master];
s_route_write_ptr_d[master] = ~s_route_write_ptr_q[master];
end
if (s_write_data_terminal[master]) begin
s_route_read_ptr_d[master] = ~s_route_read_ptr_q[master];
end
end
end
always_comb begin
s_read_age_d = s_read_age_q;
s_write_age_d = s_write_age_q;
for (int target = 0; target < NumTargets; target++) begin
for (int master = 0; master < NumMasters; master++) begin
if (!s_read_req[target][master] ||
(s_read_capture[target] && (s_read_selected[target] == MasterWidth'(master)))) begin
s_read_age_d[target][master] = '0;
end else if (s_read_age_q[target][master] < AgeWidth'(StarvationCycles)) begin
s_read_age_d[target][master] = s_read_age_q[target][master] + 1'b1;
end
if (!s_write_req[target][master] ||
(s_write_capture[target] && (s_write_selected[target] == MasterWidth'(master)))) begin
s_write_age_d[target][master] = '0;
end else if (s_write_age_q[target][master] < AgeWidth'(StarvationCycles)) begin
s_write_age_d[target][master] = s_write_age_q[target][master] + 1'b1;
end
end
end
end
always_comb begin
s_fault_valid_d = 1'b0;
s_fault_master_d = '0;
s_fault_target_d = '0;
s_fault_addr_d = '0;
s_fault_write_d = 1'b0;
s_fault_reason_d = '0;
for (int master = 0; master < NumMasters; master++) begin
if (!s_fault_valid_d && s_read_accept[master] && (s_read_fault_reason[master] != 4'd0)) begin
s_fault_valid_d = 1'b1;
s_fault_master_d = 3'(master);
s_fault_target_d = 3'(s_read_decoded_target[master]);
s_fault_addr_d = m_ar[master].addr;
s_fault_reason_d = s_read_fault_reason[master];
end
if (!s_fault_valid_d && s_write_accept[master] &&
(s_write_fault_reason[master] != 4'd0)) begin
s_fault_valid_d = 1'b1;
s_fault_master_d = 3'(master);
s_fault_target_d = 3'(s_write_decoded_target[master]);
s_fault_addr_d = m_aw[master].addr;
s_fault_write_d = 1'b1;
s_fault_reason_d = s_write_fault_reason[master];
end
end
end
always_comb begin
outstanding_read_o = '0;
outstanding_write_o = '0;
for (int master = 0; master < NumMasters; master++) begin
outstanding_read_o = outstanding_read_o + 8'(s_master_read_count_q[master]);
outstanding_write_o = outstanding_write_o + 8'(s_master_write_count_q[master]);
end
end
assign idle_o = (outstanding_read_o == 8'd0) && (outstanding_write_o == 8'd0) &&
!(|s_route_count_q);
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_read_id_busy_q <= '0;
s_write_id_busy_q <= '0;
s_write_route_q <= '0;
s_route_read_ptr_q <= '0;
s_route_write_ptr_q <= '0;
s_route_count_q <= '0;
s_read_age_q <= '0;
s_write_age_q <= '0;
fault_valid_o <= 1'b0;
fault_master_o <= '0;
fault_target_o <= '0;
fault_addr_o <= '0;
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_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;
s_route_read_ptr_q <= s_route_read_ptr_d;
s_route_write_ptr_q <= s_route_write_ptr_d;
s_route_count_q <= s_route_count_d;
s_read_age_q <= s_read_age_d;
s_write_age_q <= s_write_age_d;
fault_valid_o <= s_fault_valid_d;
fault_master_o <= s_fault_master_d;
fault_target_o <= s_fault_target_d;
fault_addr_o <= s_fault_addr_d;
fault_write_o <= s_fault_write_d;
fault_reason_o <= s_fault_reason_d;
end
end
`ifndef SYNTHESIS
always_ff @(posedge clk_i) begin
if (rst_n_i && $test$plusargs("trace_mgmt")) begin
if (m_arvalid[5] && m_arready[5]) begin
$display("DATA_M5_AR addr=%08x target=%0d", m_ar[5].addr, s_read_routed_target[5]);
end
if (m_rvalid[5] && m_rready[5] && m_r[5].last) begin
$display("DATA_M5_R resp=%0d data=%016x", m_r[5].resp, m_r[5].data);
end
end
end
initial begin
if ((NumMasters != 8) || (NumTargets != 6)) begin
$fatal(1, "axi4_data_crossbar: product topology must be 8x6");
if ((NumMasters != 8) || (NumTargets != 6) || (StarvationCycles < 2) ||
((StarvationCycles & (StarvationCycles - 1)) != 0)) begin
$fatal(1, "axi4_data_crossbar: invalid product topology or aging interval");
end
end
`endif
+4
View File
@@ -459,6 +459,10 @@ module axi4_interconnect #(
`SOC_ADDR_IS_APB4_WDG(addr)
&& !
`SOC_ADDR_IS_APB4_GPIO_ADMIN(addr)
&& !
`SOC_ADDR_IS_APB4_EXT_L(addr)
&& !
`SOC_ADDR_IS_APB4_EXT_H(addr)
);
`else
s_access_allowed[master] = (master != 1) || ((read_req &&
+181
View File
@@ -0,0 +1,181 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "mmap_define.svh"
module axi4_mgmt_router (
// verilog_format: off -- preserve the control/data route columns
input logic clk_i,
input logic rst_n_i,
axi4_if.slave source,
axi4_if.master control,
axi4_if.master data,
output logic idle_o
// verilog_format: on
);
logic s_read_active_d, s_read_active_q;
logic s_read_data_d, s_read_data_q;
logic s_write_active_d, s_write_active_q;
logic s_write_data_d, s_write_data_q;
logic s_read_data_sel;
logic s_write_data_sel;
logic s_read_accept;
logic s_write_accept;
logic s_read_terminal;
logic s_write_terminal;
function automatic logic is_data_addr(input logic [31:0] addr);
return
`SOC_ADDR_IS_FLASH(addr)
||
`SOC_ADDR_IS_SRAM(addr)
||
`SOC_ADDR_IS_SDRAM(addr)
||
`SOC_ADDR_IS_PSRAM(addr)
||
`SOC_ADDR_IS_OPIPSRAM(addr)
||
`SOC_ADDR_IS_XPI(addr)
;
endfunction
assign s_read_data_sel = is_data_addr(source.araddr);
assign s_write_data_sel = is_data_addr(source.awaddr);
assign control.arid = source.arid;
assign data.arid = source.arid;
assign control.araddr = source.araddr;
assign data.araddr = source.araddr;
assign control.arlen = source.arlen;
assign data.arlen = source.arlen;
assign control.arsize = source.arsize;
assign data.arsize = source.arsize;
assign control.arburst = source.arburst;
assign data.arburst = source.arburst;
assign control.arlock = source.arlock;
assign data.arlock = source.arlock;
assign control.arcache = source.arcache;
assign data.arcache = source.arcache;
assign control.arprot = source.arprot;
assign data.arprot = source.arprot;
assign control.arqos = source.arqos;
assign data.arqos = source.arqos;
assign control.arregion = source.arregion;
assign data.arregion = source.arregion;
assign control.aruser = source.aruser;
assign data.aruser = source.aruser;
assign control.arvalid = !s_read_active_q && !s_read_data_sel && source.arvalid;
assign data.arvalid = !s_read_active_q && s_read_data_sel && source.arvalid;
assign source.arready = !s_read_active_q && (s_read_data_sel ? data.arready : control.arready);
assign source.rid = s_read_data_q ? data.rid : control.rid;
assign source.rdata = s_read_data_q ? data.rdata : control.rdata;
assign source.rresp = s_read_data_q ? data.rresp : control.rresp;
assign source.rlast = s_read_data_q ? data.rlast : control.rlast;
assign source.ruser = s_read_data_q ? data.ruser : control.ruser;
assign source.rvalid = s_read_active_q && (s_read_data_q ? data.rvalid : control.rvalid);
assign control.rready = s_read_active_q && !s_read_data_q && source.rready;
assign data.rready = s_read_active_q && s_read_data_q && source.rready;
assign control.awid = source.awid;
assign data.awid = source.awid;
assign control.awaddr = source.awaddr;
assign data.awaddr = source.awaddr;
assign control.awlen = source.awlen;
assign data.awlen = source.awlen;
assign control.awsize = source.awsize;
assign data.awsize = source.awsize;
assign control.awburst = source.awburst;
assign data.awburst = source.awburst;
assign control.awlock = source.awlock;
assign data.awlock = source.awlock;
assign control.awcache = source.awcache;
assign data.awcache = source.awcache;
assign control.awprot = source.awprot;
assign data.awprot = source.awprot;
assign control.awqos = source.awqos;
assign data.awqos = source.awqos;
assign control.awregion = source.awregion;
assign data.awregion = source.awregion;
assign control.awuser = source.awuser;
assign data.awuser = source.awuser;
assign control.awvalid = !s_write_active_q && !s_write_data_sel && source.awvalid;
assign data.awvalid = !s_write_active_q && s_write_data_sel && source.awvalid;
assign source.awready = !s_write_active_q && (s_write_data_sel ? data.awready : control.awready);
assign control.wdata = source.wdata;
assign data.wdata = source.wdata;
assign control.wstrb = source.wstrb;
assign data.wstrb = source.wstrb;
assign control.wlast = source.wlast;
assign data.wlast = source.wlast;
assign control.wuser = source.wuser;
assign data.wuser = source.wuser;
assign control.wvalid = s_write_active_q && !s_write_data_q && source.wvalid;
assign data.wvalid = s_write_active_q && s_write_data_q && source.wvalid;
assign source.wready = s_write_active_q && (s_write_data_q ? data.wready : control.wready);
assign source.bid = s_write_data_q ? data.bid : control.bid;
assign source.bresp = s_write_data_q ? data.bresp : control.bresp;
assign source.buser = s_write_data_q ? data.buser : control.buser;
assign source.bvalid = s_write_active_q && (s_write_data_q ? data.bvalid : control.bvalid);
assign control.bready = s_write_active_q && !s_write_data_q && source.bready;
assign data.bready = s_write_active_q && s_write_data_q && source.bready;
assign s_read_accept = source.arvalid && source.arready;
assign s_write_accept = source.awvalid && source.awready;
assign s_read_terminal = source.rvalid && source.rready && source.rlast;
assign s_write_terminal = source.bvalid && source.bready;
assign idle_o = !s_read_active_q && !s_write_active_q;
always_comb begin
s_read_active_d = s_read_active_q;
s_read_data_d = s_read_data_q;
s_write_active_d = s_write_active_q;
s_write_data_d = s_write_data_q;
if (s_read_terminal) s_read_active_d = 1'b0;
if (s_read_accept) begin
s_read_active_d = 1'b1;
s_read_data_d = s_read_data_sel;
end
if (s_write_terminal) s_write_active_d = 1'b0;
if (s_write_accept) begin
s_write_active_d = 1'b1;
s_write_data_d = s_write_data_sel;
end
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_read_active_q <= 1'b0;
s_read_data_q <= 1'b0;
s_write_active_q <= 1'b0;
s_write_data_q <= 1'b0;
end else begin
s_read_active_q <= s_read_active_d;
s_read_data_q <= s_read_data_d;
s_write_active_q <= s_write_active_d;
s_write_data_q <= s_write_data_d;
end
end
`ifndef SYNTHESIS
always_ff @(posedge clk_i) begin
if (rst_n_i && $test$plusargs("trace_mgmt")) begin
if (source.arvalid && source.arready) begin
$display("MGMT_AR addr=%08x data=%0d", source.araddr, s_read_data_sel);
end
if (source.rvalid && source.rready && source.rlast) begin
$display("MGMT_R resp=%0d data=%08x", source.rresp, source.rdata);
end
if (source.awvalid && source.awready) begin
$display("MGMT_AW addr=%08x data=%0d", source.awaddr, s_write_data_sel);
end
if (source.bvalid && source.bready) begin
$display("MGMT_B resp=%0d", source.bresp);
end
end
end
`endif
endmodule
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module clock_frequency_monitor #(
parameter int unsigned CounterWidth = 16,
parameter int unsigned WindowCycles = 256
) (
// verilog_format: off -- preserve the monitored/reference domain columns
input logic ref_clk_i,
input logic ref_rst_n_i,
input logic monitored_clk_i,
input logic monitored_rst_n_i,
input logic clear_fault_i,
output logic alive_o,
output logic fault_o,
output logic [CounterWidth-1:0] edge_delta_o
// verilog_format: on
);
localparam int unsigned WindowWidth = $clog2(WindowCycles);
logic [CounterWidth-1:0] s_monitored_count_q;
logic [CounterWidth-1:0] s_monitored_gray;
logic [CounterWidth-1:0] s_monitored_gray_ref;
logic [CounterWidth-1:0] s_monitored_count_ref;
logic [CounterWidth-1:0] s_last_count_q;
logic [ WindowWidth-1:0] s_window_count_q;
logic s_window_valid_q;
function automatic logic [CounterWidth-1:0] gray_to_bin(input logic [CounterWidth-1:0] value_i);
logic [CounterWidth-1:0] result;
result[CounterWidth-1] = value_i[CounterWidth-1];
for (int bit_index = CounterWidth - 2; bit_index >= 0; bit_index--) begin
result[bit_index] = result[bit_index+1] ^ value_i[bit_index];
end
return result;
endfunction
assign s_monitored_gray = s_monitored_count_q ^ (s_monitored_count_q >> 1);
assign s_monitored_count_ref = gray_to_bin(s_monitored_gray_ref);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(CounterWidth)
) u_count_sync (
.clk_i (ref_clk_i),
.rst_n_i(ref_rst_n_i),
.dat_i (s_monitored_gray),
.dat_o (s_monitored_gray_ref)
);
always_ff @(posedge monitored_clk_i or negedge monitored_rst_n_i) begin
if (!monitored_rst_n_i) begin
s_monitored_count_q <= '0;
end else begin
s_monitored_count_q <= s_monitored_count_q + 1'b1;
end
end
always_ff @(posedge ref_clk_i or negedge ref_rst_n_i) begin
if (!ref_rst_n_i) begin
s_last_count_q <= '0;
s_window_count_q <= '0;
s_window_valid_q <= 1'b0;
edge_delta_o <= '0;
alive_o <= 1'b0;
fault_o <= 1'b0;
end else begin
if (clear_fault_i) fault_o <= 1'b0;
if (s_window_count_q == WindowWidth'(WindowCycles - 1)) begin
edge_delta_o <= s_monitored_count_ref - s_last_count_q;
alive_o <= s_monitored_count_ref != s_last_count_q;
if (!clear_fault_i && s_window_valid_q && (s_monitored_count_ref == s_last_count_q)) begin
fault_o <= 1'b1;
end
s_last_count_q <= s_monitored_count_ref;
s_window_count_q <= '0;
s_window_valid_q <= 1'b1;
end else begin
s_window_count_q <= s_window_count_q + 1'b1;
end
end
end
`ifndef SYNTHESIS
initial begin
if ((CounterWidth < 4) || (WindowCycles < 4) ||
((WindowCycles & (WindowCycles - 1)) != 0)) begin
$fatal(1, "clock_frequency_monitor: invalid counter or window geometry");
end
end
`endif
endmodule
+198
View File
@@ -0,0 +1,198 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "axi4_define.svh"
module extension_dma_master (
// verilog_format: off -- preserve the extension command/status columns
input logic clk_i,
input logic rst_n_i,
input logic start_i,
input logic abort_i,
input logic quiesce_i,
input logic [31:0] src_addr_i,
input logic [31:0] dst_addr_i,
input logic [31:0] len_i,
input logic [31:0] timeout_i,
output logic busy_o,
output logic done_o,
output logic err_o,
output logic [31:0] fault_addr_o,
axi4_if.master axi4
// verilog_format: on
);
typedef enum logic [2:0] {
Idle,
ReadAddr,
ReadData,
WriteAddr,
WriteData,
WriteResp
} state_e;
state_e s_state_d, s_state_q;
logic [31:0] s_src_addr_d, s_src_addr_q;
logic [31:0] s_dst_addr_d, s_dst_addr_q;
logic [31:0] s_remaining_d, s_remaining_q;
logic [63:0] s_data_d, s_data_q;
logic [31:0] s_timeout_cnt_d, s_timeout_cnt_q;
logic s_done_d, s_done_q;
logic s_err_d, s_err_q;
logic [31:0] s_fault_addr_d, s_fault_addr_q;
logic [7:0] s_tail_strobe;
logic s_progress;
always_comb begin
if (s_remaining_q >= 32'd8) begin
s_tail_strobe = 8'hFF;
end else begin
s_tail_strobe = 8'((9'd1 << s_remaining_q[3:0]) - 1'b1);
end
end
assign axi4.awid = 3'd0;
assign axi4.awaddr = s_dst_addr_q;
assign axi4.awlen = 8'd0;
assign axi4.awsize = 3'd3;
assign axi4.awburst = `AXI4_BURST_TYPE_INCR;
assign axi4.awlock = 1'b0;
assign axi4.awcache = 4'd0;
assign axi4.awprot = 3'd0;
assign axi4.awqos = 4'd8;
assign axi4.awregion = 4'd0;
assign axi4.awuser = 1'b0;
assign axi4.awvalid = s_state_q == WriteAddr;
assign axi4.wdata = s_data_q;
assign axi4.wstrb = s_tail_strobe;
assign axi4.wlast = 1'b1;
assign axi4.wuser = 1'b0;
assign axi4.wvalid = s_state_q == WriteData;
assign axi4.bready = s_state_q == WriteResp;
assign axi4.arid = 3'd0;
assign axi4.araddr = s_src_addr_q;
assign axi4.arlen = 8'd0;
assign axi4.arsize = 3'd3;
assign axi4.arburst = `AXI4_BURST_TYPE_INCR;
assign axi4.arlock = 1'b0;
assign axi4.arcache = 4'd0;
assign axi4.arprot = 3'd0;
assign axi4.arqos = 4'd8;
assign axi4.arregion = 4'd0;
assign axi4.aruser = 1'b0;
assign axi4.arvalid = s_state_q == ReadAddr;
assign axi4.rready = s_state_q == ReadData;
assign busy_o = s_state_q != Idle;
assign done_o = s_done_q;
assign err_o = s_err_q;
assign fault_addr_o = s_fault_addr_q;
assign s_progress = (axi4.arvalid && axi4.arready) ||
(axi4.rvalid && axi4.rready) ||
(axi4.awvalid && axi4.awready) ||
(axi4.wvalid && axi4.wready) ||
(axi4.bvalid && axi4.bready);
always_comb begin
s_state_d = s_state_q;
s_src_addr_d = s_src_addr_q;
s_dst_addr_d = s_dst_addr_q;
s_remaining_d = s_remaining_q;
s_data_d = s_data_q;
s_timeout_cnt_d = s_timeout_cnt_q;
s_done_d = 1'b0;
s_err_d = 1'b0;
s_fault_addr_d = s_fault_addr_q;
if (s_state_q == Idle) begin
s_timeout_cnt_d = '0;
if (start_i && !quiesce_i) begin
if ((len_i == 32'd0) || (src_addr_i[2:0] != 3'd0) || (dst_addr_i[2:0] != 3'd0)) begin
s_done_d = 1'b1;
s_err_d = 1'b1;
s_fault_addr_d = (src_addr_i[2:0] != 3'd0) ? src_addr_i : dst_addr_i;
end else begin
s_src_addr_d = src_addr_i;
s_dst_addr_d = dst_addr_i;
s_remaining_d = len_i;
s_state_d = ReadAddr;
end
end
end else if (abort_i) begin
s_state_d = Idle;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_fault_addr_d = (s_state_q == ReadAddr || s_state_q == ReadData) ?
s_src_addr_q : s_dst_addr_q;
end else if (!s_progress && (timeout_i != 32'd0) && (s_timeout_cnt_q == timeout_i - 1'b1)) begin
s_state_d = Idle;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_fault_addr_d = (s_state_q == ReadAddr || s_state_q == ReadData) ?
s_src_addr_q : s_dst_addr_q;
s_timeout_cnt_d = '0;
end else begin
s_timeout_cnt_d = s_progress ? 32'd0 : s_timeout_cnt_q + 1'b1;
unique case (s_state_q)
ReadAddr: if (axi4.arvalid && axi4.arready) s_state_d = ReadData;
ReadData: begin
if (axi4.rvalid && axi4.rready) begin
if ((axi4.rresp != `AXI4_RESP_OKAY) || !axi4.rlast) begin
s_state_d = Idle;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_fault_addr_d = s_src_addr_q;
end else begin
s_data_d = axi4.rdata;
s_state_d = WriteAddr;
end
end
end
WriteAddr: if (axi4.awvalid && axi4.awready) s_state_d = WriteData;
WriteData: if (axi4.wvalid && axi4.wready) s_state_d = WriteResp;
WriteResp: begin
if (axi4.bvalid && axi4.bready) begin
if (axi4.bresp != `AXI4_RESP_OKAY) begin
s_state_d = Idle;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_fault_addr_d = s_dst_addr_q;
end else if (s_remaining_q <= 32'd8) begin
s_state_d = Idle;
s_done_d = 1'b1;
end else begin
s_src_addr_d = s_src_addr_q + 32'd8;
s_dst_addr_d = s_dst_addr_q + 32'd8;
s_remaining_d = s_remaining_q - 32'd8;
s_state_d = ReadAddr;
end
end
end
default: s_state_d = Idle;
endcase
end
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= Idle;
s_src_addr_q <= '0;
s_dst_addr_q <= '0;
s_remaining_q <= '0;
s_data_q <= '0;
s_timeout_cnt_q <= '0;
s_done_q <= 1'b0;
s_err_q <= 1'b0;
s_fault_addr_q <= '0;
end else begin
s_state_q <= s_state_d;
s_src_addr_q <= s_src_addr_d;
s_dst_addr_q <= s_dst_addr_d;
s_remaining_q <= s_remaining_d;
s_data_q <= s_data_d;
s_timeout_cnt_q <= s_timeout_cnt_d;
s_done_q <= s_done_d;
s_err_q <= s_err_d;
s_fault_addr_q <= s_fault_addr_d;
end
end
endmodule
+137 -18
View File
@@ -7,13 +7,31 @@ module extension_slot #(
parameter int unsigned IrqCount = 1,
parameter int unsigned ResetTimeoutCycles = 1024
) (
// verilog_format: off -- preserve the extension lifecycle/data contract columns
input logic clk_i,
input logic rst_n_i,
input logic data_idle_i,
input logic dma_busy_i,
input logic dma_done_i,
input logic dma_err_i,
input logic [31:0] dma_fault_addr_i,
apb4_if.slave cfg_apb4,
output logic irq_o,
output logic idle_o,
output logic quiesce_o,
output logic reset_o
output logic reset_o,
output logic [1:0] owner_o,
output logic [31:0] read_base_o,
output logic [31:0] read_limit_o,
output logic [31:0] write_base_o,
output logic [31:0] write_limit_o,
output logic [31:0] timeout_o,
output logic dma_start_o,
output logic dma_abort_o,
output logic [31:0] dma_src_addr_o,
output logic [31:0] dma_dst_addr_o,
output logic [31:0] dma_len_o
// verilog_format: on
);
localparam logic [11:0] Identification = 12'h000;
localparam logic [11:0] Version = 12'h004;
@@ -29,6 +47,11 @@ module extension_slot #(
localparam logic [11:0] Fault = 12'h02C;
localparam logic [11:0] FaultAddress = 12'h030;
localparam logic [11:0] RequestCount = 12'h034;
localparam logic [11:0] DmaSource = 12'h100;
localparam logic [11:0] DmaDestination = 12'h104;
localparam logic [11:0] DmaLength = 12'h108;
localparam logic [11:0] DmaCommand = 12'h10C;
localparam logic [11:0] DmaStatus = 12'h110;
localparam logic [31:0] Identifier = KindExtH ? 32'h4558_5448 : 32'h4558_544C;
localparam logic [31:0] Capabilities = {
@@ -65,16 +88,32 @@ module extension_slot #(
logic [31:0] s_fault_addr_q;
logic [31:0] s_req_count_d;
logic [31:0] s_req_count_q;
logic s_req_accept;
logic s_write;
logic s_cfg_write_legal;
logic [31:0] s_dma_src_addr_d, s_dma_src_addr_q;
logic [31:0] s_dma_dst_addr_d, s_dma_dst_addr_q;
logic [31:0] s_dma_len_d, s_dma_len_q;
logic s_dma_start_d, s_dma_start_q;
logic s_dma_abort_d, s_dma_abort_q;
logic s_req_accept;
logic s_write;
logic s_cfg_write_legal;
assign s_req_accept = cfg_apb4.psel && cfg_apb4.penable && !s_apb4_ready_q;
assign s_write = s_req_accept && (|cfg_apb4.pstrb);
assign idle_o = 1'b1;
assign idle_o = KindExtH ? (data_idle_i && !dma_busy_i) : 1'b1;
assign quiesce_o = s_quiesce_q;
assign reset_o = s_reset_q;
assign irq_o = s_fault_sticky_q;
assign owner_o = s_owner_q;
assign read_base_o = s_read_base_q;
assign read_limit_o = s_read_limit_q;
assign write_base_o = s_write_base_q;
assign write_limit_o = s_write_limit_q;
assign timeout_o = s_timeout_q;
assign dma_start_o = s_dma_start_q;
assign dma_abort_o = s_dma_abort_q;
assign dma_src_addr_o = s_dma_src_addr_q;
assign dma_dst_addr_o = s_dma_dst_addr_q;
assign dma_len_o = s_dma_len_q;
assign irq_o = s_fault_sticky_q || dma_done_i;
assign cfg_apb4.pready = s_apb4_ready_q;
assign cfg_apb4.prdata = s_apb4_rdata_q;
assign cfg_apb4.pslverr = s_apb4_resp_err_q;
@@ -89,6 +128,11 @@ module extension_slot #(
s_write_base_d = s_write_base_q;
s_write_limit_d = s_write_limit_q;
s_timeout_d = s_timeout_q;
s_dma_src_addr_d = s_dma_src_addr_q;
s_dma_dst_addr_d = s_dma_dst_addr_q;
s_dma_len_d = s_dma_len_q;
s_dma_start_d = 1'b0;
s_dma_abort_d = 1'b0;
s_fault_sticky_d = s_fault_sticky_q;
s_fault_addr_d = s_fault_addr_q;
s_req_count_d = s_req_count_q;
@@ -135,6 +179,26 @@ module extension_slot #(
s_cfg_write_legal = KindExtH && (cfg_apb4.pwdata != 32'd0);
if (s_cfg_write_legal) s_timeout_d = cfg_apb4.pwdata;
end
DmaSource: begin
s_cfg_write_legal = KindExtH && !dma_busy_i;
if (s_cfg_write_legal) s_dma_src_addr_d = cfg_apb4.pwdata;
end
DmaDestination: begin
s_cfg_write_legal = KindExtH && !dma_busy_i;
if (s_cfg_write_legal) s_dma_dst_addr_d = cfg_apb4.pwdata;
end
DmaLength: begin
s_cfg_write_legal = KindExtH && !dma_busy_i && (cfg_apb4.pwdata != 32'd0);
if (s_cfg_write_legal) s_dma_len_d = cfg_apb4.pwdata;
end
DmaCommand: begin
s_cfg_write_legal = KindExtH && ((cfg_apb4.pwdata & 32'hFFFF_FFFC) == 32'd0) &&
!(cfg_apb4.pwdata[0] && dma_busy_i);
if (s_cfg_write_legal) begin
s_dma_start_d = cfg_apb4.pwdata[0];
s_dma_abort_d = cfg_apb4.pwdata[1];
end
end
default: s_cfg_write_legal = 1'b0;
endcase
if (!s_cfg_write_legal) begin
@@ -142,6 +206,10 @@ module extension_slot #(
s_fault_addr_d = cfg_apb4.paddr;
end
end
if (dma_err_i && !s_fault_sticky_q) begin
s_fault_sticky_d = 1'b1;
s_fault_addr_d = dma_fault_addr_i;
end
end
always_comb begin
@@ -161,6 +229,13 @@ module extension_slot #(
Fault: s_apb4_rdata_d = {31'd0, s_fault_sticky_q};
FaultAddress: s_apb4_rdata_d = s_fault_addr_q;
RequestCount: s_apb4_rdata_d = s_req_count_q;
DmaSource: s_apb4_rdata_d = KindExtH ? s_dma_src_addr_q : 32'd0;
DmaDestination: s_apb4_rdata_d = KindExtH ? s_dma_dst_addr_q : 32'd0;
DmaLength: s_apb4_rdata_d = KindExtH ? s_dma_len_q : 32'd0;
DmaCommand: s_apb4_rdata_d = 32'd0;
DmaStatus: begin
s_apb4_rdata_d = KindExtH ? {29'd0, s_fault_sticky_q, dma_done_i, dma_busy_i} : 32'd0;
end
default: s_apb4_rdata_d = '0;
endcase
end
@@ -225,33 +300,37 @@ module extension_slot #(
.dat_i (s_reset_d),
.dat_o (s_reset_q)
);
dffr #(
.DATA_WIDTH(32)
) u_read_base_dffr (
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (32'h3000_0000)
) u_read_base_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_base_d),
.dat_o (s_read_base_q)
);
dffr #(
.DATA_WIDTH(32)
) u_read_limit_dffr (
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (32'h4FFF_FFFF)
) u_read_limit_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_limit_d),
.dat_o (s_read_limit_q)
);
dffr #(
.DATA_WIDTH(32)
) u_write_base_dffr (
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (32'h3000_0000)
) u_write_base_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_base_d),
.dat_o (s_write_base_q)
);
dffr #(
.DATA_WIDTH(32)
) u_write_limit_dffr (
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (32'h4FFF_FFFF)
) u_write_limit_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_limit_d),
@@ -290,6 +369,46 @@ module extension_slot #(
.dat_i (s_req_count_d),
.dat_o (s_req_count_q)
);
dffr #(
.DATA_WIDTH(32)
) u_dma_src_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_src_addr_d),
.dat_o (s_dma_src_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_dma_dst_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_dst_addr_d),
.dat_o (s_dma_dst_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_dma_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_len_d),
.dat_o (s_dma_len_q)
);
dffr #(
.DATA_WIDTH(1)
) u_dma_start_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_start_d),
.dat_o (s_dma_start_q)
);
dffr #(
.DATA_WIDTH(1)
) u_dma_abort_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_abort_d),
.dat_o (s_dma_abort_q)
);
`ifndef SYNTHESIS
initial begin
+119 -54
View File
@@ -2,33 +2,79 @@
// SPDX-License-Identifier: MulanPSL-2.0
module extension_subsystem (
// verilog_format: off -- preserve the slot control/data contract columns
input logic clk_i,
input logic rst_n_i,
input logic ext_h_data_idle_i,
apb4_if.slave ext_l_apb4,
apb4_if.slave ext_h_apb4,
axi4_if.master ext_h_axi4,
output logic ext_l_irq_o,
output logic ext_h_irq_o
output logic ext_h_irq_o,
output logic ext_h_block_o,
output logic [1:0] ext_h_owner_o,
output logic [31:0] ext_h_read_base_o,
output logic [31:0] ext_h_read_limit_o,
output logic [31:0] ext_h_write_base_o,
output logic [31:0] ext_h_write_limit_o,
output logic [31:0] ext_h_timeout_o
// verilog_format: on
);
logic s_ext_l_idle;
logic s_ext_l_quiesce;
logic s_ext_l_reset;
logic s_ext_h_idle;
logic s_ext_h_quiesce;
logic s_ext_h_reset;
logic s_ext_l_idle;
logic s_ext_l_quiesce;
logic s_ext_l_reset;
logic s_ext_h_idle;
logic s_ext_h_quiesce;
logic s_ext_h_reset;
logic s_ext_h_dma_busy;
logic s_ext_h_dma_done;
logic s_ext_h_dma_err;
logic [31:0] s_ext_h_dma_fault_addr;
logic s_ext_h_dma_start;
logic s_ext_h_dma_abort;
logic [31:0] s_ext_h_dma_src_addr;
logic [31:0] s_ext_h_dma_dst_addr;
logic [31:0] s_ext_h_dma_len;
logic [ 1:0] unused_ext_l_owner;
logic [31:0] unused_ext_l_read_base;
logic [31:0] unused_ext_l_read_limit;
logic [31:0] unused_ext_l_write_base;
logic [31:0] unused_ext_l_write_limit;
logic [31:0] unused_ext_l_timeout;
logic unused_ext_l_dma_start;
logic unused_ext_l_dma_abort;
logic [31:0] unused_ext_l_dma_src_addr;
logic [31:0] unused_ext_l_dma_dst_addr;
logic [31:0] unused_ext_l_dma_len;
extension_slot #(
.SlotId (0),
.KindExtH(1'b0),
.IrqCount(1)
) u_ext_l_slot (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.cfg_apb4 (ext_l_apb4),
.irq_o (ext_l_irq_o),
.idle_o (s_ext_l_idle),
.quiesce_o(s_ext_l_quiesce),
.reset_o (s_ext_l_reset)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.data_idle_i (1'b1),
.dma_busy_i (1'b0),
.dma_done_i (1'b0),
.dma_err_i (1'b0),
.dma_fault_addr_i(32'd0),
.cfg_apb4 (ext_l_apb4),
.irq_o (ext_l_irq_o),
.idle_o (s_ext_l_idle),
.quiesce_o (s_ext_l_quiesce),
.reset_o (s_ext_l_reset),
.owner_o (unused_ext_l_owner),
.read_base_o (unused_ext_l_read_base),
.read_limit_o (unused_ext_l_read_limit),
.write_base_o (unused_ext_l_write_base),
.write_limit_o (unused_ext_l_write_limit),
.timeout_o (unused_ext_l_timeout),
.dma_start_o (unused_ext_l_dma_start),
.dma_abort_o (unused_ext_l_dma_abort),
.dma_src_addr_o (unused_ext_l_dma_src_addr),
.dma_dst_addr_o (unused_ext_l_dma_dst_addr),
.dma_len_o (unused_ext_l_dma_len)
);
extension_slot #(
@@ -36,49 +82,68 @@ module extension_subsystem (
.KindExtH(1'b1),
.IrqCount(1)
) u_ext_h_slot (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.cfg_apb4 (ext_h_apb4),
.irq_o (ext_h_irq_o),
.idle_o (s_ext_h_idle),
.quiesce_o(s_ext_h_quiesce),
.reset_o (s_ext_h_reset)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.data_idle_i (ext_h_data_idle_i),
.dma_busy_i (s_ext_h_dma_busy),
.dma_done_i (s_ext_h_dma_done),
.dma_err_i (s_ext_h_dma_err),
.dma_fault_addr_i(s_ext_h_dma_fault_addr),
.cfg_apb4 (ext_h_apb4),
.irq_o (ext_h_irq_o),
.idle_o (s_ext_h_idle),
.quiesce_o (s_ext_h_quiesce),
.reset_o (s_ext_h_reset),
.owner_o (ext_h_owner_o),
.read_base_o (ext_h_read_base_o),
.read_limit_o (ext_h_read_limit_o),
.write_base_o (ext_h_write_base_o),
.write_limit_o (ext_h_write_limit_o),
.timeout_o (ext_h_timeout_o),
.dma_start_o (s_ext_h_dma_start),
.dma_abort_o (s_ext_h_dma_abort),
.dma_src_addr_o (s_ext_h_dma_src_addr),
.dma_dst_addr_o (s_ext_h_dma_dst_addr),
.dma_len_o (s_ext_h_dma_len)
);
assign ext_h_axi4.awid = '0;
assign ext_h_axi4.awaddr = '0;
assign ext_h_axi4.awlen = '0;
assign ext_h_axi4.awsize = '0;
assign ext_h_axi4.awburst = '0;
assign ext_h_axi4.awlock = 1'b0;
assign ext_h_axi4.awcache = '0;
assign ext_h_axi4.awprot = '0;
assign ext_h_axi4.awqos = '0;
assign ext_h_axi4.awregion = '0;
assign ext_h_axi4.awuser = '0;
assign ext_h_axi4.awvalid = 1'b0;
assign ext_h_axi4.wdata = '0;
assign ext_h_axi4.wstrb = '0;
assign ext_h_axi4.wlast = 1'b0;
assign ext_h_axi4.wuser = '0;
assign ext_h_axi4.wvalid = 1'b0;
assign ext_h_axi4.bready = 1'b1;
assign ext_h_axi4.arid = '0;
assign ext_h_axi4.araddr = '0;
assign ext_h_axi4.arlen = '0;
assign ext_h_axi4.arsize = '0;
assign ext_h_axi4.arburst = '0;
assign ext_h_axi4.arlock = 1'b0;
assign ext_h_axi4.arcache = '0;
assign ext_h_axi4.arprot = '0;
assign ext_h_axi4.arqos = '0;
assign ext_h_axi4.arregion = '0;
assign ext_h_axi4.aruser = '0;
assign ext_h_axi4.arvalid = 1'b0;
assign ext_h_axi4.rready = 1'b1;
assign ext_h_block_o = s_ext_h_quiesce || s_ext_h_reset;
logic [5:0] s_unused_status;
extension_dma_master u_ext_h_dma (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_ext_h_dma_start),
.abort_i (s_ext_h_dma_abort || s_ext_h_reset),
.quiesce_i (s_ext_h_quiesce),
.src_addr_i (s_ext_h_dma_src_addr),
.dst_addr_i (s_ext_h_dma_dst_addr),
.len_i (s_ext_h_dma_len),
.timeout_i (ext_h_timeout_o),
.busy_o (s_ext_h_dma_busy),
.done_o (s_ext_h_dma_done),
.err_o (s_ext_h_dma_err),
.fault_addr_o(s_ext_h_dma_fault_addr),
.axi4 (ext_h_axi4)
);
logic [16:0] s_unused_status;
assign s_unused_status = {
s_ext_l_idle, s_ext_l_quiesce, s_ext_l_reset, s_ext_h_idle, s_ext_h_quiesce, s_ext_h_reset
s_ext_l_idle,
s_ext_l_quiesce,
s_ext_l_reset,
s_ext_h_idle,
s_ext_h_quiesce,
s_ext_h_reset,
^unused_ext_l_owner,
^unused_ext_l_read_base,
^unused_ext_l_read_limit,
^unused_ext_l_write_base,
^unused_ext_l_write_limit,
^unused_ext_l_timeout,
unused_ext_l_dma_start,
unused_ext_l_dma_abort,
^unused_ext_l_dma_src_addr,
^unused_ext_l_dma_dst_addr,
^unused_ext_l_dma_len
};
endmodule
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module hp_lifecycle_controller (
// verilog_format: off -- preserve the AON lifecycle contract columns
input logic clk_i,
input logic rst_n_i,
input logic release_req_i,
input logic hp_idle_i,
input logic flush_busy_i,
input logic [15:0] timeout_i,
output logic hp_release_o,
output logic block_new_o,
output logic flush_o,
output logic draining_o,
output logic forced_fault_o
// verilog_format: on
);
typedef enum logic [2:0] {
Held,
Running,
Quiesce,
FlushRequest,
FlushWait
} state_e;
state_e s_state_d, s_state_q;
logic s_hp_release_d, s_hp_release_q;
logic s_block_new_d, s_block_new_q;
logic s_flush_d, s_flush_q;
logic s_forced_fault_d, s_forced_fault_q;
logic [15:0] s_timeout_cnt_d, s_timeout_cnt_q;
logic [15:0] s_timeout_limit;
assign s_timeout_limit = (timeout_i < 16'd2) ? 16'd2 : timeout_i;
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 forced_fault_o = s_forced_fault_q;
always_comb begin
s_state_d = s_state_q;
s_hp_release_d = s_hp_release_q;
s_block_new_d = s_block_new_q;
s_flush_d = s_flush_q;
s_forced_fault_d = s_forced_fault_q;
s_timeout_cnt_d = s_timeout_cnt_q;
unique case (s_state_q)
Held: begin
s_hp_release_d = 1'b0;
s_block_new_d = 1'b0;
s_flush_d = 1'b0;
s_timeout_cnt_d = '0;
if (release_req_i) begin
s_hp_release_d = 1'b1;
s_forced_fault_d = 1'b0;
s_state_d = Running;
end
end
Running: begin
s_hp_release_d = 1'b1;
s_block_new_d = 1'b0;
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;
end
end
Quiesce: begin
s_block_new_d = 1'b1;
if (hp_idle_i) begin
s_flush_d = 1'b1;
s_timeout_cnt_d = '0;
s_state_d = FlushRequest;
end else if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_forced_fault_d = 1'b1;
s_flush_d = 1'b1;
s_timeout_cnt_d = '0;
s_state_d = FlushRequest;
end else begin
s_timeout_cnt_d = s_timeout_cnt_q + 1'b1;
end
end
FlushRequest: begin
s_block_new_d = 1'b1;
s_flush_d = 1'b1;
if (flush_busy_i) begin
s_flush_d = 1'b0;
s_timeout_cnt_d = '0;
s_state_d = FlushWait;
end else if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_forced_fault_d = 1'b1;
s_flush_d = 1'b0;
s_timeout_cnt_d = '0;
s_state_d = FlushWait;
end else begin
s_timeout_cnt_d = s_timeout_cnt_q + 1'b1;
end
end
FlushWait: begin
s_block_new_d = 1'b1;
s_flush_d = 1'b0;
if (!flush_busy_i || (s_timeout_cnt_q == s_timeout_limit - 1'b1)) begin
if (flush_busy_i) s_forced_fault_d = 1'b1;
s_hp_release_d = 1'b0;
s_block_new_d = 1'b0;
s_timeout_cnt_d = '0;
s_state_d = Held;
end else begin
s_timeout_cnt_d = s_timeout_cnt_q + 1'b1;
end
end
default: s_state_d = Held;
endcase
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= Held;
s_hp_release_q <= 1'b0;
s_block_new_q <= 1'b0;
s_flush_q <= 1'b0;
s_forced_fault_q <= 1'b0;
s_timeout_cnt_q <= '0;
end else begin
s_state_q <= s_state_d;
s_hp_release_q <= s_hp_release_d;
s_block_new_q <= s_block_new_d;
s_flush_q <= s_flush_d;
s_forced_fault_q <= s_forced_fault_d;
s_timeout_cnt_q <= s_timeout_cnt_d;
end
end
endmodule
+6 -3
View File
@@ -15,6 +15,7 @@ module pll_rcu_controller #(
input logic pll_capable_i,
input logic hp_idle_i,
input logic pclk_idle_i,
input logic [15:0] timeout_i,
input logic force_safe_i,
input logic clear_fault_i,
output logic [2:0] pll_sel_o,
@@ -79,6 +80,7 @@ module pll_rcu_controller #(
logic s_fault_sticky_q;
logic s_rsp_valid_d;
logic s_rsp_valid_q;
logic [15:0] s_timeout_limit;
assign s_state_q = state_e'(s_state_bits_q);
@@ -127,6 +129,7 @@ module pll_rcu_controller #(
assign hp_block_o = s_hp_block_q;
assign lp_force_ref_o = s_lp_force_ref_q;
assign pll_fault_o = s_fault_sticky_q;
assign s_timeout_limit = (timeout_i < 16'd2) ? 16'd2 : timeout_i;
always_comb begin
s_state_d = s_state_q;
@@ -182,7 +185,7 @@ module pll_rcu_controller #(
if (hp_idle_i && pclk_idle_i) begin
s_timeout_cnt_d = '0;
s_state_d = ParkLp;
end else if (s_timeout_cnt_q == 16'(QuiesceTimeout - 1)) begin
end else if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_err_d = 2'd2;
s_fault_sticky_d = 1'b1;
s_state_d = FailSafe;
@@ -208,7 +211,7 @@ module pll_rcu_controller #(
if (!pll_lock_i) begin
s_timeout_cnt_d = '0;
s_state_d = QualifyLock;
end else if (s_timeout_cnt_q == 16'(LockTimeout - 1)) begin
end else if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_err_d = 2'd2;
s_fault_sticky_d = 1'b1;
s_state_d = FailSafe;
@@ -227,7 +230,7 @@ module pll_rcu_controller #(
end else begin
s_lock_qual_cnt_d = s_lock_qual_cnt_q + 1'b1;
end
if (s_timeout_cnt_q == 16'(LockTimeout - 1)) begin
if (s_timeout_cnt_q == s_timeout_limit - 1'b1) begin
s_err_d = 2'd2;
s_fault_sticky_d = 1'b1;
s_state_d = FailSafe;
+2
View File
@@ -22,6 +22,7 @@ module rcu #(
output logic sys_clk_o,
output logic sys_rst_n_o,
output logic hp_clk_o,
output logic hp_core_clk_o,
output logic hp_rst_n_o,
output logic pclk_o,
output logic pclk_rst_n_o,
@@ -55,6 +56,7 @@ module rcu #(
.lp_clk_o (sys_clk_o),
.lp_rst_n_o (sys_rst_n_o),
.hp_clk_o (hp_clk_o),
.hp_core_clk_o (hp_core_clk_o),
.hp_rst_n_o (hp_rst_n_o),
.pclk_o (pclk_o),
.pclk_rst_n_o (pclk_rst_n_o),
+292 -121
View File
@@ -14,9 +14,12 @@
module retrosoc (
// verilog_format: off -- preserve reviewed column alignment
input logic clk_aon_i,
input logic rst_aon_n_i,
input logic clk_lp_i,
input logic rst_lp_n_i,
input logic clk_hp_i,
input logic clk_hp_core_i,
input logic rst_hp_n_i,
input logic clk_pclk_i,
input logic rst_pclk_n_i,
@@ -79,11 +82,11 @@ module retrosoc (
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_opipsram_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_psram_pclk_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i));
u_psram_pclk_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_xpi_pclk_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i));
u_xpi_pclk_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_opipsram_pclk_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i));
u_opipsram_pclk_axi4_if (.aclk(clk_mem_i), .aresetn(rst_mem_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1))
u_ext_h_wide_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1))
@@ -110,6 +113,10 @@ module retrosoc (
u_data_xpi_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_idle_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_mgmt_control_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_mgmt_data_axi4_if (.aclk(clk_lp_i), .aresetn(rst_lp_n_i));
user_gpio_if u_user_gpio_if ();
// ip interface
gpio_if u_gpio_if ();
@@ -161,22 +168,58 @@ module retrosoc (
`else
logic s_unused_product_legacy;
`endif
logic s_rtc_wake;
logic s_hp_jtag_tdo;
logic s_hp_debug_reset_req;
logic s_lp_jtag_tdo;
logic s_debug_hp_sel_jtag;
logic [63:0] s_hp_time;
logic s_hp_timer_irq;
logic s_hp_software_irq;
logic s_hp_machine_external_irq;
logic s_hp_supervisor_external_irq;
logic s_data_plane_idle;
logic s_hp_mmio_idle;
logic [ 3:0] s_pclk_pending_d;
logic [ 3:0] s_pclk_pending_q;
logic [ 1:0] s_mem_pad_mode_lp;
logic s_mem_pad_lock_lp;
logic s_rtc_wake;
logic s_hp_jtag_tdo;
logic s_hp_debug_reset_req;
logic s_lp_jtag_tdo;
logic s_debug_hp_sel_jtag;
logic [63:0] s_hp_time;
logic s_hp_timer_irq;
logic s_hp_software_irq;
logic s_hp_machine_external_irq;
logic s_hp_supervisor_external_irq;
logic s_data_plane_idle;
logic s_data_plane_flush_busy;
logic s_mgmt_router_idle;
logic [ 7:0] s_data_plane_outstanding_read;
logic [ 7:0] s_data_plane_outstanding_write;
logic s_data_plane_fault_valid;
logic [ 2:0] s_data_plane_fault_master;
logic [ 2:0] s_data_plane_fault_target;
logic [31:0] s_data_plane_fault_addr;
logic s_data_plane_fault_write;
logic [ 3:0] s_data_plane_fault_reason;
logic s_hp_mmio_idle;
logic [ 3:0] s_pclk_pending_d;
logic [ 3:0] s_pclk_pending_q;
logic [ 1:0] s_mem_pad_mode_lp;
logic s_mem_pad_lock_lp;
logic s_hp_release_req_aon;
logic s_hp_debug_reset_req_aon;
logic s_hp_idle_aon;
logic s_hp_flush_busy_aon;
logic s_hp_lifecycle_release_aon;
logic s_hp_lifecycle_release_hp;
logic s_hp_lifecycle_block;
logic s_hp_lifecycle_flush;
logic s_hp_lifecycle_draining;
logic s_hp_lifecycle_fault;
logic s_hp_block_combined;
logic [ 2:0] s_hp_lifecycle_stat_pclk;
logic s_ext_h_data_idle;
logic s_ext_h_block;
logic [31:0] s_ext_h_read_base;
logic [31:0] s_ext_h_read_limit;
logic [31:0] s_ext_h_write_base;
logic [31:0] s_ext_h_write_limit;
logic [31:0] s_ext_h_timeout;
logic s_ext_h_irq_raw;
logic [ 1:0] s_ext_h_owner;
logic [31:0] s_fault_addr_mux;
logic [ 3:0] s_fault_wstrb_mux;
logic s_fault_reserved_mux;
logic [ 5:0] unused_cdc_clear_busy;
logic [ 5:0][7:0] unused_cdc_epoch;
`ifdef HAVE_SRAM_IF
localparam bit SramPresent = 1'b1;
@@ -184,36 +227,46 @@ module retrosoc (
localparam bit SramPresent = 1'b0;
`endif
assign u_sysctrl_if.fault_access_i = s_bus_fault_access;
assign u_sysctrl_if.fault_master_i = s_bus_fault_master;
assign u_sysctrl_if.fault_code_i = s_bus_fault_code;
assign s_perf_en = u_sysctrl_if.perf_enable_o;
assign s_perf_clear = u_sysctrl_if.perf_clear_o;
assign test_done_o = u_sysctrl_if.test_done_o;
assign test_pass_o = u_sysctrl_if.test_pass_o;
assign test_code_o = u_sysctrl_if.test_code_o;
assign u_sysctrl_if.perf_mgmt_wait_i = s_perf_mgmt_wait;
assign u_sysctrl_if.perf_user_wait_i = s_perf_user_wait;
assign u_sysctrl_if.perf_dma_wait_i = s_perf_dma_wait;
assign u_sysctrl_if.perf_sdio0_wait_i = s_perf_sdio0_wait;
assign u_sysctrl_if.perf_sdio1_wait_i = s_perf_sdio1_wait;
assign u_sysctrl_if.perf_usb2_wait_i = s_perf_usb2_wait;
assign u_sysctrl_if.fault_access_i = s_data_plane_fault_valid ?
s_data_plane_fault_write : s_bus_fault_access;
assign u_sysctrl_if.fault_master_i = s_data_plane_fault_valid ?
s_data_plane_fault_master : s_bus_fault_master;
assign u_sysctrl_if.fault_code_i = s_data_plane_fault_valid ?
s_data_plane_fault_reason[2:0] : s_bus_fault_code;
assign s_perf_en = u_sysctrl_if.perf_enable_o;
assign s_perf_clear = u_sysctrl_if.perf_clear_o;
assign test_done_o = u_sysctrl_if.test_done_o;
assign test_pass_o = u_sysctrl_if.test_pass_o;
assign test_code_o = u_sysctrl_if.test_code_o;
assign u_sysctrl_if.perf_mgmt_wait_i = s_perf_mgmt_wait;
assign u_sysctrl_if.perf_user_wait_i = s_perf_user_wait;
assign u_sysctrl_if.perf_dma_wait_i = s_perf_dma_wait;
assign u_sysctrl_if.perf_sdio0_wait_i = s_perf_sdio0_wait;
assign u_sysctrl_if.perf_sdio1_wait_i = s_perf_sdio1_wait;
assign u_sysctrl_if.perf_usb2_wait_i = s_perf_usb2_wait;
assign u_sysctrl_if.perf_apb4_periph_wait_i = s_perf_apb4_periph_wait;
assign u_sysctrl_if.perf_apb4_system_wait_i = s_perf_apb4_system_wait;
assign u_sysctrl_if.perf_sdram_wait_i = s_perf_sdram_wait;
assign u_sysctrl_if.perf_sdram_wait_i = s_perf_sdram_wait;
// The SYSCTRL PSRAM statistic aggregates the legacy and OPI PSRAM windows.
assign u_sysctrl_if.perf_psram_wait_i = s_perf_psram_wait + s_perf_opipsram_wait;
assign u_sysctrl_if.perf_flash_wait_i = s_perf_flash_wait;
assign u_sysctrl_if.rtc_wake_i = s_rtc_wake;
assign u_sysctrl_if.perf_psram_wait_i = s_perf_psram_wait + s_perf_opipsram_wait;
assign u_sysctrl_if.perf_flash_wait_i = s_perf_flash_wait;
assign u_sysctrl_if.rtc_wake_i = s_rtc_wake;
`ifdef HAVE_HP
assign u_sysctrl_if.hp_present_i = 1'b1;
`else
assign u_sysctrl_if.hp_present_i = 1'b0;
`endif
assign u_sysctrl_if.hp_actual_released_i = s_hp_lifecycle_stat_pclk[2];
assign u_sysctrl_if.hp_draining_i = s_hp_lifecycle_stat_pclk[1];
assign u_sysctrl_if.hp_forced_fault_i = s_hp_lifecycle_stat_pclk[0];
assign s_fault_addr_mux = s_data_plane_fault_valid ? s_data_plane_fault_addr : s_bus_fault_addr;
assign s_fault_wstrb_mux = s_data_plane_fault_valid && s_data_plane_fault_write ?
4'hF : s_bus_fault_wstrb;
assign s_fault_reserved_mux = s_data_plane_fault_valid ? 1'b0 : s_bus_fault_reserved;
assign u_uart0_if.rx_i = uart_rx_i;
assign uart_tx_o = u_uart0_if.tx_o;
assign uart_tx_o = u_uart0_if.tx_o;
assign u_uart1_if.rx_i = uart1_rx_i;
assign uart1_tx_o = u_uart1_if.tx_o;
assign uart1_tx_o = u_uart1_if.tx_o;
cdc_sync #(
.STAGE (2),
@@ -318,9 +371,9 @@ core_wrapper u_core_wrapper (
`ifdef HAVE_HP
hp_subsystem u_hp_subsystem (
.clk_i (clk_hp_i),
.clk_i (clk_hp_core_i),
.rst_n_i (rst_hp_n_i),
.core_reset_i (!u_sysctrl_if.hp_release_o || s_hp_debug_reset_req),
.core_reset_i (!s_hp_lifecycle_release_hp),
.time_i (s_hp_time),
.timer_irq_i (s_hp_timer_irq),
.software_irq_i (s_hp_software_irq),
@@ -355,46 +408,123 @@ core_wrapper u_core_wrapper (
.DataWidth(32),
.IdWidth (1)
) u_hp_mmio_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),
.src_axi4 (u_hp_mmio_gated_axi4_if),
.dst_axi4 (u_hp_mmio_lp_axi4_if)
.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 (1'b0),
.clear_busy_o(unused_cdc_clear_busy[0]),
.epoch_o (unused_cdc_epoch[0]),
.src_axi4 (u_hp_mmio_gated_axi4_if),
.dst_axi4 (u_hp_mmio_lp_axi4_if)
);
axi4_address_gate u_hp_mmio_gate (
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.block_new_i(hp_block_i),
.block_new_i(s_hp_block_combined),
.source (u_hp_mmio_hp_axi4_if),
.sink (u_hp_mmio_gated_axi4_if),
.idle_o (s_hp_mmio_idle)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(4)
) u_hp_lifecycle_input_sync (
.clk_i(clk_aon_i),
.rst_n_i(rst_aon_n_i),
.dat_i({
u_sysctrl_if.hp_release_o,
s_hp_debug_reset_req,
s_data_plane_idle && s_hp_mmio_idle,
s_data_plane_flush_busy
}),
.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)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_hp_release_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_hp_lifecycle_release_aon),
.dat_o (s_hp_lifecycle_release_hp)
);
assign s_hp_block_combined = hp_block_i || s_hp_lifecycle_block;
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(3)
) u_hp_lifecycle_status_sync (
.clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.dat_i ({s_hp_lifecycle_release_aon, s_hp_lifecycle_draining, s_hp_lifecycle_fault}),
.dat_o (s_hp_lifecycle_stat_pclk)
);
axi4_mgmt_router u_mgmt_router (
.clk_i (clk_lp_i),
.rst_n_i(rst_lp_n_i),
.source (u_mgmt_axi4_if),
.control(u_mgmt_control_axi4_if),
.data (u_mgmt_data_axi4_if),
.idle_o (s_mgmt_router_idle)
);
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 (hp_block_i),
.mem_pad_mode_i (s_mem_pad_mode_lp),
.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),
.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)
.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)
);
axi4_master_idle u_idle_master (.axi4(u_idle_axi4_if));
@@ -403,56 +533,71 @@ core_wrapper u_core_wrapper (
.DataWidth(32),
.IdWidth (1)
) u_cfg_pclk_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i(rst_pclk_n_i),
.src_axi4 (u_cfg_axi4_if),
.dst_axi4 (u_cfg_pclk_axi4_if)
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i (rst_pclk_n_i),
.clear_i (1'b0),
.clear_busy_o(unused_cdc_clear_busy[1]),
.epoch_o (unused_cdc_epoch[1]),
.src_axi4 (u_cfg_axi4_if),
.dst_axi4 (u_cfg_pclk_axi4_if)
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_system_pclk_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i(rst_pclk_n_i),
.src_axi4 (u_system_axi4_if),
.dst_axi4 (u_system_pclk_axi4_if)
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i (rst_pclk_n_i),
.clear_i (1'b0),
.clear_busy_o(unused_cdc_clear_busy[2]),
.epoch_o (unused_cdc_epoch[2]),
.src_axi4 (u_system_axi4_if),
.dst_axi4 (u_system_pclk_axi4_if)
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_psram_target_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i(rst_pclk_n_i),
.src_axi4 (u_psram_axi4_if),
.dst_axi4 (u_psram_pclk_axi4_if)
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i (rst_mem_n_i),
.clear_i (1'b0),
.clear_busy_o(unused_cdc_clear_busy[3]),
.epoch_o (unused_cdc_epoch[3]),
.src_axi4 (u_psram_axi4_if),
.dst_axi4 (u_psram_pclk_axi4_if)
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_xpi_target_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i(rst_pclk_n_i),
.src_axi4 (u_xpi_axi4_if),
.dst_axi4 (u_xpi_pclk_axi4_if)
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i (rst_mem_n_i),
.clear_i (1'b0),
.clear_busy_o(unused_cdc_clear_busy[4]),
.epoch_o (unused_cdc_epoch[4]),
.src_axi4 (u_xpi_axi4_if),
.dst_axi4 (u_xpi_pclk_axi4_if)
);
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_opi_target_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i(rst_lp_n_i),
.dst_clk_i (clk_pclk_i),
.dst_rst_n_i(rst_pclk_n_i),
.src_axi4 (u_opipsram_axi4_if),
.dst_axi4 (u_opipsram_pclk_axi4_if)
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_mem_i),
.dst_rst_n_i (rst_mem_n_i),
.clear_i (1'b0),
.clear_busy_o(unused_cdc_clear_busy[5]),
.epoch_o (unused_cdc_epoch[5]),
.src_axi4 (u_opipsram_axi4_if),
.dst_axi4 (u_opipsram_pclk_axi4_if)
);
apb4_async_bridge u_sdram_cfg_cdc (
.src_clk_i (clk_pclk_i),
@@ -514,7 +659,7 @@ core_wrapper u_core_wrapper (
axi4_bus u_bus (
.clk_i (clk_lp_i),
.rst_n_i (rst_lp_n_i),
.mgmt_axi4 (u_mgmt_axi4_if),
.mgmt_axi4 (u_mgmt_control_axi4_if),
.user_axi4 (u_data_sram_axi4_if),
.dma_axi4 (u_data_sdram_axi4_if),
.sdio0_axi4 (u_data_qpi_axi4_if),
@@ -561,8 +706,11 @@ core_wrapper u_core_wrapper (
.clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i),
.clk_ulpi_i (clk_ulpi_i),
.clk_mem_i (clk_mem_i),
.rst_mem_n_i (rst_mem_n_i),
.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),
.cfg_axi4 (u_cfg_pclk_axi4_if),
.dma_axi4 (u_dma_axi4_if),
.sdio0_axi4 (u_sdio0_axi4_if),
@@ -593,10 +741,10 @@ core_wrapper u_core_wrapper (
.usb2 (usb2),
.opipsram (u_opipsram_raw_if),
.i2c1 (u_i2c1_if),
.fault_valid_i (s_bus_fault_valid),
.fault_addr_i (s_bus_fault_addr),
.fault_wstrb_i (s_bus_fault_wstrb),
.fault_reserved_i (s_bus_fault_reserved),
.fault_valid_i (s_data_plane_fault_valid || s_bus_fault_valid),
.fault_addr_i (s_fault_addr_mux),
.fault_wstrb_i (s_fault_wstrb_mux),
.fault_reserved_i (s_fault_reserved_mux),
.hp_time_o (s_hp_time),
.hp_timer_irq_o (s_hp_timer_irq),
.hp_software_irq_o (s_hp_software_irq),
@@ -614,25 +762,48 @@ core_wrapper u_core_wrapper (
);
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_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),
.irq_o (s_apb4_system_irq)
.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),
.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)
);
logic [3:0] s_unused_domain_inputs;
logic [31:0] s_unused_domain_inputs;
assign s_unused_domain_inputs = {
clk_pclk_i, rst_pclk_n_i, clk_mem_i, rst_mem_n_i ^ s_mem_pad_lock_lp
clk_pclk_i,
rst_pclk_n_i,
clk_mem_i,
rst_mem_n_i ^ s_mem_pad_lock_lp,
s_data_plane_outstanding_read,
s_data_plane_outstanding_write,
s_data_plane_fault_target,
s_data_plane_fault_addr[1:0],
s_data_plane_fault_reason[3],
s_mgmt_router_idle,
s_hp_lifecycle_draining,
s_hp_lifecycle_fault,
^unused_cdc_clear_busy,
^unused_cdc_epoch,
^s_ext_h_timeout
};
endmodule
+5
View File
@@ -67,6 +67,7 @@ module retrosoc_asic (
logic s_aon_rst_n;
logic s_sys_clk;
logic s_hp_clk;
logic s_hp_core_clk;
logic s_hp_rst_n;
logic s_pclk;
logic s_pclk_rst_n;
@@ -124,6 +125,7 @@ rcu #(
.sys_clk_o (s_sys_clk),
.sys_rst_n_o (s_sys_rst_n),
.hp_clk_o (s_hp_clk),
.hp_core_clk_o (s_hp_core_clk),
.hp_rst_n_o (s_hp_rst_n),
.pclk_o (s_pclk),
.pclk_rst_n_o (s_pclk_rst_n),
@@ -140,9 +142,12 @@ rcu #(
);
retrosoc u_retrosoc (
.clk_aon_i (s_aon_clk),
.rst_aon_n_i (s_aon_rst_n),
.clk_lp_i (s_sys_clk),
.rst_lp_n_i (s_sys_rst_n),
.clk_hp_i (s_hp_clk),
.clk_hp_core_i (s_hp_core_clk),
.rst_hp_n_i (s_hp_rst_n),
.clk_pclk_i (s_pclk),
.rst_pclk_n_i (s_pclk_rst_n),
+5
View File
@@ -26,6 +26,7 @@ module retrosoc_core (
logic s_aon_rst_n;
logic s_sys_clk;
logic s_hp_clk;
logic s_hp_core_clk;
logic s_hp_rst_n;
logic s_pclk;
logic s_pclk_rst_n;
@@ -83,6 +84,7 @@ rcu #(
.sys_clk_o (s_sys_clk),
.sys_rst_n_o (s_sys_rst_n),
.hp_clk_o (s_hp_clk),
.hp_core_clk_o (s_hp_core_clk),
.hp_rst_n_o (s_hp_rst_n),
.pclk_o (s_pclk),
.pclk_rst_n_o (s_pclk_rst_n),
@@ -99,9 +101,12 @@ rcu #(
);
retrosoc u_retrosoc (
.clk_aon_i (s_aon_clk),
.rst_aon_n_i (s_aon_rst_n),
.clk_lp_i (s_sys_clk),
.rst_lp_n_i (s_sys_rst_n),
.clk_hp_i (s_hp_clk),
.clk_hp_core_i (s_hp_core_clk),
.rst_hp_n_i (s_hp_rst_n),
.clk_pclk_i (s_pclk),
.rst_pclk_n_i (s_pclk_rst_n),
+22
View File
@@ -0,0 +1,22 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module soc_clock_gate (
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
logic s_en_q;
always_latch begin
if (!clk_i) begin
// Intentional low-phase latch for glitch-free clock gating.
// verilator lint_off COMBDLY
s_en_q <= en_i || test_en_i;
// verilator lint_on COMBDLY
end
end
assign clk_o = clk_i && s_en_q;
endmodule
+70 -13
View File
@@ -20,6 +20,7 @@ module soc_clock_reset_subsystem #(
output logic lp_clk_o,
output logic lp_rst_n_o,
output logic hp_clk_o,
output logic hp_core_clk_o,
output logic hp_rst_n_o,
output logic pclk_o,
output logic pclk_rst_n_o,
@@ -50,6 +51,7 @@ module soc_clock_reset_subsystem #(
logic s_pll_force_lp_ref;
logic s_lp_force_ref;
logic s_hp_root_clk;
logic s_hp_core_clk;
logic s_hp_div2_clk;
logic s_hp_div4_clk;
logic s_lp_perf_div2_or_more_clk;
@@ -79,6 +81,10 @@ module soc_clock_reset_subsystem #(
logic s_pclk_second_trigger;
logic s_hp_idle_aon;
logic s_pclk_idle_aon;
logic s_pll_fault;
logic [ 3:0] s_clock_alive;
logic [ 3:0] s_clock_fault;
logic [63:0] s_clock_edge_delta;
tc_clk_buf u_ref24_clk_buf (
.clk_i(ref24_clk_i),
@@ -149,6 +155,13 @@ module soc_clock_reset_subsystem #(
.clk_o (s_hp_root_clk)
);
soc_clock_gate u_hp_core_clock_gate (
.clk_i (s_hp_root_clk),
.en_i (!s_gate_mask[0]),
.test_en_i(1'b0),
.clk_o (s_hp_core_clk)
);
clk_int_even_div_static #(
.DIV_VALUE_WIDTH(1)
) u_hp_div2 (
@@ -266,6 +279,47 @@ module soc_clock_reset_subsystem #(
.clk_o (s_lp_clk_div4)
);
clock_frequency_monitor u_ext72_monitor (
.ref_clk_i (s_ref24_clk_buf),
.ref_rst_n_i (s_aon_rst_n),
.monitored_clk_i (s_ext72_clk_buf),
.monitored_rst_n_i(s_aon_rst_n),
.clear_fault_i (s_clear_fault),
.alive_o (s_clock_alive[0]),
.fault_o (s_clock_fault[0]),
.edge_delta_o (s_clock_edge_delta[15:0])
);
clock_frequency_monitor u_hp_monitor (
.ref_clk_i (s_ref24_clk_buf),
.ref_rst_n_i (s_aon_rst_n),
.monitored_clk_i (s_hp_root_clk),
.monitored_rst_n_i(s_aon_rst_n),
.clear_fault_i (s_clear_fault),
.alive_o (s_clock_alive[1]),
.fault_o (s_clock_fault[1]),
.edge_delta_o (s_clock_edge_delta[31:16])
);
clock_frequency_monitor u_pclk_monitor (
.ref_clk_i (s_ref24_clk_buf),
.ref_rst_n_i (s_aon_rst_n),
.monitored_clk_i (s_pclk),
.monitored_rst_n_i(s_aon_rst_n),
.clear_fault_i (s_clear_fault),
.alive_o (s_clock_alive[2]),
.fault_o (s_clock_fault[2]),
.edge_delta_o (s_clock_edge_delta[47:32])
);
clock_frequency_monitor u_memory_monitor (
.ref_clk_i (s_ref24_clk_buf),
.ref_rst_n_i (s_aon_rst_n),
.monitored_clk_i (s_mem_clk_div2),
.monitored_rst_n_i(s_aon_rst_n),
.clear_fault_i (s_clear_fault),
.alive_o (s_clock_alive[3]),
.fault_o (s_clock_fault[3]),
.edge_delta_o (s_clock_edge_delta[63:48])
);
pll_rcu_controller u_pll_rcu_controller (
.sys_clk_i (s_pclk),
.sys_rst_n_i (pclk_rst_n_o),
@@ -275,6 +329,7 @@ module soc_clock_reset_subsystem #(
.pll_capable_i (s_pll_capable),
.hp_idle_i (s_hp_idle_aon),
.pclk_idle_i (s_pclk_idle_aon),
.timeout_i (s_timeout),
.force_safe_i (s_force_safe),
.clear_fault_i (s_clear_fault),
.pll_sel_o (s_pll_sel),
@@ -282,7 +337,7 @@ module soc_clock_reset_subsystem #(
.sel_ext_clk_o (s_sel_ext_clk),
.hp_block_o (hp_block_o),
.lp_force_ref_o(s_pll_force_lp_ref),
.pll_fault_o (pll_fault_o),
.pll_fault_o (s_pll_fault),
.pll_ctrl (pll_ctrl)
);
@@ -293,7 +348,7 @@ module soc_clock_reset_subsystem #(
.aon_rst_n_i (s_aon_rst_n),
.hp_pstate_i (s_pll_sel),
.hp_pll_sel_i (!s_sel_ext_clk),
.pll_fault_i (pll_fault_o),
.pll_fault_i (s_pll_fault || (|s_clock_fault)),
.pclk_idle_i (s_pclk_idle_aon),
.clock_ctrl (clock_ctrl),
.lp_mode_o (s_lp_mode),
@@ -357,23 +412,25 @@ module soc_clock_reset_subsystem #(
.tick_o (timebase_tick_o)
);
assign aon_clk_o = s_ref24_clk_buf;
assign aon_rst_n_o = s_aon_rst_n;
assign lp_clk_o = s_lp_root_clk;
assign hp_clk_o = s_hp_root_clk;
assign pclk_o = s_pclk;
assign mem_clk_o = s_mem_clk_div2;
assign aud_clk_o = s_aud_clk_buf;
assign lp_clkdiv4_o = s_lp_clk_div4;
assign aon_clk_o = s_ref24_clk_buf;
assign aon_rst_n_o = s_aon_rst_n;
assign lp_clk_o = s_lp_root_clk;
assign hp_clk_o = s_hp_root_clk;
assign hp_core_clk_o = s_hp_core_clk;
assign pclk_o = s_pclk;
assign mem_clk_o = s_mem_clk_div2;
assign aud_clk_o = s_aud_clk_buf;
assign lp_clkdiv4_o = s_lp_clk_div4;
assign pll_fault_o = s_pll_fault || (|s_clock_fault);
logic [31:0] s_unused_clock_config;
logic [16:0] s_unused_clock_config;
assign s_unused_clock_config = {
2'd0,
s_gate_mask,
s_timeout,
s_pclk_count,
^{s_pclk_div_done, s_pclk_first_trigger, s_pclk_second_trigger, s_pclk_cfg_src_ready,
s_pll_apply}
s_pll_apply},
^{s_clock_alive, s_clock_edge_delta}
};
`ifndef SYNTHESIS
+1
View File
@@ -17,5 +17,6 @@
`include "cdc_fifo.sv"
`include "async_reqack.sv"
`include "cdc_warm_flush.sv"
`include "cdc_advanced.sv"
/* verilator lint_on SYNCASYNCNET */
/* verilator lint_on DECLFILENAME */
+159 -43
View File
@@ -10,7 +10,14 @@ module soc_data_plane (
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic block_new_i,
input logic recovery_i,
input logic flush_i,
input logic [1:0] mem_pad_mode_i,
input logic ext_h_block_i,
input logic [31:0] ext_h_read_base_i,
input logic [31:0] ext_h_read_limit_i,
input logic [31:0] ext_h_write_base_i,
input logic [31:0] ext_h_write_limit_i,
axi4_if.slave hp_icache_axi4,
axi4_if.slave hp_dcache_axi4,
axi4_if.slave dma_axi4,
@@ -18,20 +25,40 @@ module soc_data_plane (
axi4_if.slave sdio1_axi4,
axi4_if.slave spisd_axi4,
axi4_if.slave usb2_axi4,
axi4_if.slave lp_data_axi4,
axi4_if.slave ext_h_axi4,
axi4_if.master sram_gateway_axi4,
axi4_if.master sdram_gateway_axi4,
axi4_if.master qpi_gateway_axi4,
axi4_if.master opi_gateway_axi4,
axi4_if.master xpi_gateway_axi4,
output logic idle_o
output logic idle_o,
output logic flush_busy_o,
output logic ext_h_idle_o,
output logic [7:0] outstanding_read_o,
output logic [7:0] outstanding_write_o,
output logic fault_valid_o,
output logic [2:0] fault_master_o,
output logic [2:0] fault_target_o,
output logic [31:0] fault_addr_o,
output logic fault_write_o,
output logic [3:0] fault_reason_o
// verilog_format: on
);
localparam int unsigned NumIoMasters = 5;
localparam int unsigned NumIoMasters = 3;
localparam int unsigned NumMemoryTargets = 5;
logic s_block_new_hp;
logic [1:0] s_mem_pad_mode_hp;
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;
axi4_if #(
.ADDR_WIDTH(32),
@@ -87,6 +114,24 @@ module soc_data_plane (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_io_idle_axi4[2] (
.aclk (clk_io_i),
.aresetn(rst_io_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_lp_data_hp_axi4 (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
@@ -96,6 +141,15 @@ module soc_data_plane (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) u_ext_h_gated_axi4 (
.aclk (clk_io_i),
.aresetn(rst_io_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
@@ -168,34 +222,39 @@ module soc_data_plane (
.source(dma_axi4),
.sink (u_io_lp_axi4[0])
);
axi4_connector u_sdio0_source_connector (
.source(sdio0_axi4),
.sink (u_io_lp_axi4[1])
hp_axi4_mux3 u_io_gateway_a (
.clk_i (clk_io_i),
.rst_n_i(rst_io_n_i),
.icache (u_io_idle_axi4[0]),
.dcache (sdio0_axi4),
.mmio (usb2_axi4),
.axi4 (u_io_lp_axi4[1])
);
axi4_connector u_sdio1_source_connector (
.source(sdio1_axi4),
.sink (u_io_lp_axi4[2])
);
axi4_connector u_spisd_source_connector (
.source(spisd_axi4),
.sink (u_io_lp_axi4[3])
);
axi4_connector u_usb2_source_connector (
.source(usb2_axi4),
.sink (u_io_lp_axi4[4])
hp_axi4_mux3 u_io_gateway_b (
.clk_i (clk_io_i),
.rst_n_i(rst_io_n_i),
.icache (u_io_idle_axi4[1]),
.dcache (spisd_axi4),
.mmio (sdio1_axi4),
.axi4 (u_io_lp_axi4[2])
);
axi4_master_idle u_io_gateway_a_idle (.axi4(u_io_idle_axi4[0]));
axi4_master_idle u_io_gateway_b_idle (.axi4(u_io_idle_axi4[1]));
for (genvar master = 0; master < NumIoMasters; master++) begin : gen_io_master
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_io_cdc (
.src_clk_i (clk_io_i),
.src_rst_n_i(rst_io_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i(rst_hp_n_i),
.src_axi4 (u_io_lp_axi4[master]),
.dst_axi4 (u_io_hp_axi4[master])
.src_clk_i (clk_io_i),
.src_rst_n_i (rst_io_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i (rst_hp_n_i),
.clear_i (flush_i),
.clear_busy_o(s_io_clear_busy[master]),
.epoch_o (unused_io_epoch[master]),
.src_axi4 (u_io_lp_axi4[master]),
.dst_axi4 (u_io_hp_axi4[master])
);
axi4_upsizer_32to64 #(
.MasterIndex(3'(master + 2))
@@ -207,16 +266,51 @@ module soc_data_plane (
);
end
axi4_async_bridge #(
.DataWidth(32),
.IdWidth (1)
) u_lp_data_cdc (
.src_clk_i (clk_lp_i),
.src_rst_n_i (rst_lp_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i (rst_hp_n_i),
.clear_i (flush_i),
.clear_busy_o(s_lp_clear_busy),
.epoch_o (unused_lp_epoch),
.src_axi4 (lp_data_axi4),
.dst_axi4 (u_lp_data_hp_axi4)
);
axi4_upsizer_32to64 #(
.MasterIndex(3'd5)
) u_lp_data_upsizer (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.narrow (u_lp_data_hp_axi4),
.wide (u_master_axi4[5])
);
axi4_master_idle u_reserved_master_idle (.axi4(u_master_axi4[6]));
axi4_async_bridge #(
.DataWidth(64),
.IdWidth (3)
) u_ext_h_cdc (
.src_clk_i (clk_io_i),
.src_rst_n_i(rst_io_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i(rst_hp_n_i),
.src_axi4 (ext_h_axi4),
.dst_axi4 (u_ext_h_hp_axi4)
.src_clk_i (clk_io_i),
.src_rst_n_i (rst_io_n_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i (rst_hp_n_i),
.clear_i (flush_i),
.clear_busy_o(s_ext_clear_busy),
.epoch_o (unused_ext_epoch),
.src_axi4 (u_ext_h_gated_axi4),
.dst_axi4 (u_ext_h_hp_axi4)
);
axi4_address_gate u_ext_h_address_gate (
.clk_i (clk_io_i),
.rst_n_i (rst_io_n_i),
.block_new_i(ext_h_block_i),
.source (ext_h_axi4),
.sink (u_ext_h_gated_axi4),
.idle_o (ext_h_idle_o)
);
axi4_id_prefix #(
.MasterIndex(3'd7)
@@ -230,13 +324,26 @@ module soc_data_plane (
);
axi4_data_crossbar u_data_crossbar (
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.block_new_i (s_block_new_hp),
.mem_pad_mode_i(s_mem_pad_mode_hp),
.masters (u_master_axi4),
.targets (u_target_axi4),
.idle_o (idle_o)
.clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i),
.block_new_i (s_block_new_hp),
.recovery_i (recovery_i),
.mem_pad_mode_i (s_mem_pad_mode_hp),
.ext_h_read_base_i (ext_h_read_base_i),
.ext_h_read_limit_i (ext_h_read_limit_i),
.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),
.idle_o (idle_o),
.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)
);
for (genvar target = 0; target < NumMemoryTargets; target++) begin : gen_memory_target
@@ -252,12 +359,15 @@ module soc_data_plane (
.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),
.src_axi4 (u_target_hp_axi4[target]),
.dst_axi4 (u_gateway_lp_axi4[target])
.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])
);
end
@@ -270,6 +380,12 @@ module soc_data_plane (
.axi4 (u_target_axi4[5])
);
assign flush_busy_o = (|s_io_clear_busy) || (|s_target_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)
+5
View File
@@ -290,17 +290,21 @@
"axi4_id_prefix.sv",
"axi4_interconnect.sv",
"axi4_master_idle.sv",
"axi4_mgmt_router.sv",
"axi4_upsizer_32to64.sv",
"axi4_word_bridge.sv",
"core_wrapper.sv",
"clock_config_controller.sv",
"clock_frequency_monitor.sv",
"extension_compat.sv",
"extension_dma_master.sv",
"extension_slot.sv",
"extension_subsystem.sv",
"gpio_pad_bridge.sv",
"hp_axi4_mux3.sv",
"hp_core_wrapper.sv",
"hp_subsystem.sv",
"hp_lifecycle_controller.sv",
"mgmt_core_wrapper.sv",
"mgmt_debug_reset.sv",
"mgmt_debug_wrapper.sv",
@@ -325,6 +329,7 @@
"ribp2rib.sv",
"ribp_arbiter2.sv",
"soc_clock_config.svh",
"soc_clock_gate.sv",
"soc_clock_reset_subsystem.sv",
"soc_common_cdc.sv",
"soc_common_fifo.sv",
+25 -6
View File
@@ -16,6 +16,16 @@ 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",
"VERILATOR_SIM_ARGS=--fast-flash",
"SOC_SIM_TIME=360",
"SIMU=VERILATOR",
"HAVE_SVA=YES",
"firmware",
"sim",
)
RTL_LINT_VALUES = ("SIMU=VERILATOR", "HAVE_SVA=YES", "rtl-lint")
RTL_LINT_OBSERVATION_VALUES = (
"SIMU=VERILATOR",
@@ -31,7 +41,7 @@ PR_COMMANDS = (
("configs/ci/ihp130-shell.mk", ("firmware",)),
(
"configs/ci/ihp130.mk",
(CI_SMOKE_APP_VALUE, "SIMU=VERILATOR", "HAVE_SVA=YES", "firmware", "sim"),
CI_SMOKE_SIM_VALUES,
),
("configs/ci/ihp130-debug.mk", ("SIMU=VERILATOR", "debug-sim")),
("configs/ci/ihp130.mk", ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
@@ -47,6 +57,7 @@ PR_COMMANDS = (
),
("configs/ci/ihp130.mk", ("STA=OPENSTA", "sta")),
)
RTL_COMMANDS = PR_COMMANDS[:6]
SMOKE_COMMANDS = (
("configs/ci/ihp130.mk", RTL_LINT_VALUES),
("configs/ci/ihp130.mk", (CI_SMOKE_APP_VALUE, "firmware")),
@@ -92,13 +103,15 @@ NETSIM_BOOT_PROFILES = frozenset(
def pdk_pr_commands(profile: str) -> tuple[tuple[str, tuple[str, ...]], ...]:
netsim_target = "netsim-boot" if profile in NETSIM_BOOT_PROFILES else "netsim"
verilator_values = (
CI_SMOKE_SIM_VALUES
if profile == "configs/ci/ihp130.mk"
else (CI_SMOKE_APP_VALUE, "SIMU=VERILATOR", "HAVE_SVA=YES", "firmware", "sim")
)
return (
(profile, RTL_LINT_VALUES),
(profile, (CI_SMOKE_APP_VALUE, "firmware")),
(
profile,
(CI_SMOKE_APP_VALUE, "SIMU=VERILATOR", "HAVE_SVA=YES", "firmware", "sim"),
),
(profile, verilator_values),
(profile, ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
(profile, ("SYNTH=YOSYS", "synth")),
(profile, ("STA=OPENSTA", "sta")),
@@ -121,6 +134,10 @@ def select_regression(
if pdk is not None and pdk != "IHP130":
raise ValueError("smoke regression supports only --pdk IHP130")
return SMOKE_COMMANDS, SMOKE_PROFILES
if suite == "rtl":
if pdk is not None and pdk != "IHP130":
raise ValueError("rtl regression supports only --pdk IHP130")
return RTL_COMMANDS, PR_PROFILES
if suite == "nightly-extra":
if pdk is not None and pdk != "IHP130":
raise ValueError("nightly-extra regression supports only --pdk IHP130")
@@ -207,7 +224,9 @@ def run_observation(command: list[str], root: Path, environment: dict[str, str])
def main() -> int:
parser = argparse.ArgumentParser(description="Run a supported retroSoC regression suite")
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--suite", choices=("smoke", "pr", "nightly", "nightly-extra"), required=True)
parser.add_argument(
"--suite", choices=("smoke", "rtl", "pr", "nightly", "nightly-extra"), required=True
)
parser.add_argument("--pdk", choices=tuple(PDK_PR_PROFILES), help="run one PDK matrix")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument(
+7
View File
@@ -836,6 +836,13 @@ static int test_extension_validation(void) {
if (rs_extension_configure_acl(RS_EXTENSION_SLOT_H, &invalid_acl) != RS_EINVAL) {
return 5;
}
if ((rs_extension_dma_start((uintptr_t)UINT32_C(0x30000001), (uintptr_t)UINT32_C(0x30000008),
UINT32_C(8)) != RS_EINVAL) ||
(rs_extension_dma_start((uintptr_t)UINT32_C(0x30000000), (uintptr_t)UINT32_C(0x30000008),
0U) != RS_EINVAL) ||
(rs_extension_dma_get_status(NULL) != RS_EINVAL)) {
return 6;
}
return 0;
}
+129
View File
@@ -0,0 +1,129 @@
`timescale 1ns / 1ps
module axi4_async_bridge_tb;
logic src_clk_i = 1'b0;
logic dst_clk_i = 1'b0;
logic src_rst_n_i = 1'b0;
logic dst_rst_n_i = 1'b0;
logic clear_i = 1'b0;
logic clear_busy_o;
logic [7:0] epoch_o;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) source (
.aclk (src_clk_i),
.aresetn(src_rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) sink (
.aclk (dst_clk_i),
.aresetn(dst_rst_n_i)
);
always #5 src_clk_i = ~src_clk_i;
always #7 dst_clk_i = ~dst_clk_i;
axi4_async_bridge u_dut (
.src_clk_i (src_clk_i),
.src_rst_n_i (src_rst_n_i),
.dst_clk_i (dst_clk_i),
.dst_rst_n_i (dst_rst_n_i),
.clear_i (clear_i),
.clear_busy_o(clear_busy_o),
.epoch_o (epoch_o),
.src_axi4 (source),
.dst_axi4 (sink)
);
initial begin
source.awid = 0;
source.awaddr = 0;
source.awlen = 0;
source.awsize = 2;
source.awburst = 1;
source.awlock = 0;
source.awcache = 0;
source.awprot = 0;
source.awqos = 0;
source.awregion = 0;
source.awuser = 0;
source.awvalid = 0;
source.wdata = 0;
source.wstrb = 0;
source.wlast = 1;
source.wuser = 0;
source.wvalid = 0;
source.bready = 1;
source.arid = 0;
source.araddr = 0;
source.arlen = 0;
source.arsize = 2;
source.arburst = 1;
source.arlock = 0;
source.arcache = 0;
source.arprot = 0;
source.arqos = 0;
source.arregion = 0;
source.aruser = 0;
source.arvalid = 0;
source.rready = 1;
sink.awready = 1;
sink.wready = 1;
sink.bid = 0;
sink.bresp = 0;
sink.buser = 0;
sink.bvalid = 0;
sink.arready = 1;
sink.rid = 0;
sink.rdata = 32'hCAFE_BABE;
sink.rresp = 0;
sink.rlast = 1;
sink.ruser = 0;
sink.rvalid = 0;
repeat (3) @(posedge src_clk_i);
src_rst_n_i = 1;
dst_rst_n_i = 1;
repeat (8) @(posedge src_clk_i);
@(negedge src_clk_i);
source.araddr = 32'h5000_0040;
source.arvalid = 1;
do @(posedge src_clk_i); while (!source.arready);
@(negedge src_clk_i);
source.arvalid = 0;
wait (sink.arvalid);
if (sink.araddr != 32'h5000_0040) $fatal(1, "async bridge address mismatch");
@(posedge dst_clk_i);
@(negedge dst_clk_i);
sink.rvalid = 1;
do @(posedge dst_clk_i); while (!sink.rready);
@(negedge dst_clk_i);
sink.rvalid = 0;
wait (source.rvalid);
if (source.rdata != 32'hCAFE_BABE) $fatal(1, "async bridge response mismatch");
@(posedge src_clk_i);
@(negedge src_clk_i);
clear_i = 1;
wait (clear_busy_o);
clear_i = 0;
wait (!clear_busy_o);
if (epoch_o != 8'd1) $fatal(1, "async bridge epoch did not advance");
$display("AXI4 async bridge transfer and warm flush test passed");
$finish;
end
initial begin
#20000;
$fatal(1, "AXI4 async bridge test timed out");
end
endmodule
+230
View File
@@ -0,0 +1,230 @@
`timescale 1ns / 1ps
module axi4_data_crossbar_tb;
localparam int NumMasters = 8;
localparam int NumTargets = 6;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic idle_o;
logic [ 7:0] outstanding_read_o;
logic [ 7:0] outstanding_write_o;
logic fault_valid_o;
logic [ 2:0] fault_master_o;
logic [ 2:0] fault_target_o;
logic [31:0] fault_addr_o;
logic fault_write_o;
logic [ 3:0] fault_reason_o;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) masters[NumMasters] (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (6),
.USER_WIDTH(1)
) targets[NumTargets] (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
initial begin
repeat (500) @(posedge clk_i);
$fatal(1, "AXI4 data crossbar test timed out");
end
axi4_data_crossbar u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.block_new_i (1'b0),
.recovery_i (1'b0),
.mem_pad_mode_i (2'd1),
.ext_h_read_base_i (32'h3000_0000),
.ext_h_read_limit_i (32'h4FFF_FFFF),
.ext_h_write_base_i (32'h3000_0000),
.ext_h_write_limit_i(32'h4FFF_FFFF),
.masters (masters),
.targets (targets),
.idle_o (idle_o),
.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)
);
task automatic issue_master1_read(input logic [5:0] id, input logic [31:0] addr);
begin
@(negedge clk_i);
masters[1].arid = id;
masters[1].araddr = addr;
masters[1].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[1].arready);
@(negedge clk_i);
masters[1].arvalid = 1'b0;
end
endtask
task automatic return_target0_read(input logic [5:0] id, input logic [63:0] data);
begin
@(negedge clk_i);
targets[0].rid = id;
targets[0].rdata = data;
targets[0].rresp = 2'b00;
targets[0].rlast = 1'b1;
targets[0].rvalid = 1'b1;
do @(posedge clk_i); while (!targets[0].rready);
@(negedge clk_i);
targets[0].rvalid = 1'b0;
end
endtask
task automatic return_target1_read(input logic [5:0] id, input logic [63:0] data);
begin
@(negedge clk_i);
targets[1].rid = id;
targets[1].rdata = data;
targets[1].rresp = 2'b00;
targets[1].rlast = 1'b1;
targets[1].rvalid = 1'b1;
do @(posedge clk_i); while (!targets[1].rready);
@(negedge clk_i);
targets[1].rvalid = 1'b0;
end
endtask
`define INIT_MASTER(index) \
masters[index].awid = '0; \
masters[index].awaddr = '0; \
masters[index].awlen = '0; \
masters[index].awsize = 3'd3; \
masters[index].awburst = 2'b01; \
masters[index].awlock = 1'b0; \
masters[index].awcache = '0; \
masters[index].awprot = '0; \
masters[index].awqos = '0; \
masters[index].awregion = '0; \
masters[index].awuser = '0; \
masters[index].awvalid = 1'b0; \
masters[index].wdata = '0; \
masters[index].wstrb = '0; \
masters[index].wlast = 1'b1; \
masters[index].wuser = '0; \
masters[index].wvalid = 1'b0; \
masters[index].bready = 1'b1; \
masters[index].arid = '0; \
masters[index].araddr = '0; \
masters[index].arlen = '0; \
masters[index].arsize = 3'd3; \
masters[index].arburst = 2'b01; \
masters[index].arlock = 1'b0; \
masters[index].arcache = '0; \
masters[index].arprot = '0; \
masters[index].arqos = '0; \
masters[index].arregion = '0; \
masters[index].aruser = '0; \
masters[index].arvalid = 1'b0; \
masters[index].rready = 1'b1;
`define INIT_TARGET(index) \
targets[index].awready = 1'b1; \
targets[index].wready = 1'b1; \
targets[index].bid = '0; \
targets[index].bresp = '0; \
targets[index].buser = '0; \
targets[index].bvalid = 1'b0; \
targets[index].arready = 1'b1; \
targets[index].rid = '0; \
targets[index].rdata = '0; \
targets[index].rresp = '0; \
targets[index].rlast = 1'b1; \
targets[index].ruser = '0; \
targets[index].rvalid = 1'b0;
initial begin
`INIT_MASTER(0)
`INIT_MASTER(1)
`INIT_MASTER(2)
`INIT_MASTER(3)
`INIT_MASTER(4)
`INIT_MASTER(5)
`INIT_MASTER(6)
`INIT_MASTER(7)
`INIT_TARGET(0)
`INIT_TARGET(1)
`INIT_TARGET(2)
`INIT_TARGET(3)
`INIT_TARGET(4)
`INIT_TARGET(5)
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
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
$fatal(1, "different-target reads were not tracked concurrently");
end
@(negedge clk_i);
masters[1].arid = 6'b001_000;
masters[1].araddr = 32'h4000_0000;
masters[1].arvalid = 1'b1;
#1;
if (masters[1].arready) $fatal(1, "same source ID was accepted before completion");
masters[1].arvalid = 1'b0;
fork
return_target1_read(6'b001_001, 64'h2222_2222_2222_2222);
begin
wait (masters[1].rvalid);
if ((masters[1].rid != 6'b001_001) || (masters[1].rdata != 64'h2222_2222_2222_2222)) begin
$fatal(1, "out-of-order different-ID response was misrouted");
end
end
join
return_target0_read(6'b001_000, 64'h1111_1111_1111_1111);
@(negedge clk_i);
if (!idle_o || (outstanding_read_o != 8'd0)) begin
$fatal(1, "read completion did not drain the crossbar");
end
@(negedge clk_i);
masters[6].arid = 6'b110_000;
masters[6].araddr = 32'h3000_0000;
masters[6].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[6].arready);
#1;
if (!fault_valid_o || (fault_master_o != 3'd6) ||
(fault_reason_o != 4'd3) || fault_write_o) begin
$fatal(1, "reserved-master ACL fault attribution mismatch");
end
@(negedge clk_i);
masters[6].arvalid = 1'b0;
targets[5].rid = 6'b110_000;
targets[5].rresp = 2'b10;
targets[5].rlast = 1'b1;
targets[5].rvalid = 1'b1;
do @(posedge clk_i); while (!targets[5].rready);
@(negedge clk_i);
targets[5].rvalid = 1'b0;
$display("AXI4 data crossbar concurrency and ACL test passed");
$finish;
end
`undef INIT_MASTER
`undef INIT_TARGET
endmodule
+58
View File
@@ -0,0 +1,58 @@
`timescale 1ns / 1ps
module clock_frequency_monitor_tb;
logic ref_clk_i = 1'b0;
logic monitored_clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic clear_fault_i = 1'b0;
logic run_monitored = 1'b1;
logic alive_o;
logic fault_o;
logic [7:0] edge_delta_o;
always #5 ref_clk_i = ~ref_clk_i;
always begin
#2;
if (run_monitored) monitored_clk_i = ~monitored_clk_i;
else monitored_clk_i = 1'b0;
end
clock_frequency_monitor #(
.CounterWidth(8),
.WindowCycles(8)
) u_dut (
.ref_clk_i (ref_clk_i),
.ref_rst_n_i (rst_n_i),
.monitored_clk_i (monitored_clk_i),
.monitored_rst_n_i(rst_n_i),
.clear_fault_i (clear_fault_i),
.alive_o (alive_o),
.fault_o (fault_o),
.edge_delta_o (edge_delta_o)
);
initial begin
repeat (2) @(posedge ref_clk_i);
rst_n_i = 1'b1;
repeat (20) @(posedge ref_clk_i);
if (!alive_o || fault_o || (edge_delta_o == 8'd0)) begin
$fatal(1, "running clock was not measured as alive");
end
run_monitored = 1'b0;
wait (fault_o);
if (alive_o) $fatal(1, "stopped clock remained alive");
@(negedge ref_clk_i);
clear_fault_i = 1'b1;
@(negedge ref_clk_i);
clear_fault_i = 1'b0;
@(negedge ref_clk_i);
if (fault_o) $fatal(1, "clock fault did not clear");
run_monitored = 1'b1;
repeat (12) @(posedge ref_clk_i);
if (!alive_o) $fatal(1, "restarted clock did not recover activity status");
$display("Clock frequency activity and sticky fault test passed");
$finish;
end
endmodule
+125
View File
@@ -0,0 +1,125 @@
`timescale 1ns / 1ps
module extension_dma_master_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic start_i = 1'b0;
logic abort_i = 1'b0;
logic quiesce_i = 1'b0;
logic [31:0] src_addr_i = 32'h3000_0000;
logic [31:0] dst_addr_i = 32'h3000_0100;
logic [31:0] len_i = 32'd12;
logic [31:0] timeout_i = 32'd16;
logic busy_o;
logic done_o;
logic err_o;
logic [31:0] fault_addr_o;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
extension_dma_master u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (start_i),
.abort_i (abort_i),
.quiesce_i (quiesce_i),
.src_addr_i (src_addr_i),
.dst_addr_i (dst_addr_i),
.len_i (len_i),
.timeout_i (timeout_i),
.busy_o (busy_o),
.done_o (done_o),
.err_o (err_o),
.fault_addr_o(fault_addr_o),
.axi4 (axi4)
);
task automatic start_dma;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic serve_beat(input logic [31:0] expected_src, input logic [31:0] expected_dst,
input logic [63:0] data, input logic [7:0] expected_strobe);
begin
wait (axi4.arvalid);
if (axi4.araddr != expected_src) $fatal(1, "DMA read address mismatch");
@(posedge clk_i);
@(negedge clk_i);
axi4.rid = 3'd0;
axi4.rdata = data;
axi4.rresp = 2'b00;
axi4.rlast = 1'b1;
axi4.rvalid = 1'b1;
do @(posedge clk_i); while (!axi4.rready);
@(negedge clk_i);
axi4.rvalid = 1'b0;
wait (axi4.awvalid);
if (axi4.awaddr != expected_dst) $fatal(1, "DMA write address mismatch");
@(posedge clk_i);
wait (axi4.wvalid);
if ((axi4.wdata != data) || (axi4.wstrb != expected_strobe) || !axi4.wlast) begin
$fatal(1, "DMA write data or tail strobe mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
axi4.bid = 3'd0;
axi4.bresp = 2'b00;
axi4.bvalid = 1'b1;
do @(posedge clk_i); while (!axi4.bready);
@(negedge clk_i);
axi4.bvalid = 1'b0;
end
endtask
initial begin
axi4.awready = 1'b1;
axi4.wready = 1'b1;
axi4.bid = '0;
axi4.bresp = '0;
axi4.buser = '0;
axi4.bvalid = 1'b0;
axi4.arready = 1'b1;
axi4.rid = '0;
axi4.rdata = '0;
axi4.rresp = '0;
axi4.rlast = 1'b1;
axi4.ruser = '0;
axi4.rvalid = 1'b0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
start_dma();
serve_beat(32'h3000_0000, 32'h3000_0100, 64'h1122_3344_5566_7788, 8'hFF);
serve_beat(32'h3000_0008, 32'h3000_0108, 64'hAABB_CCDD_EEFF_0011, 8'h0F);
wait (done_o);
if (err_o || busy_o) $fatal(1, "successful DMA copy reported an error");
@(negedge clk_i);
timeout_i = 32'd4;
len_i = 32'd8;
axi4.arready = 1'b0;
start_dma();
wait (done_o);
if (!err_o || (fault_addr_o != src_addr_i)) begin
$fatal(1, "DMA no-progress timeout was not attributed to the source address");
end
$display("Extension DMA copy, tail strobe, and timeout test passed");
$finish;
end
endmodule
+74
View File
@@ -0,0 +1,74 @@
`timescale 1ns / 1ps
module hp_lifecycle_controller_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic release_req_i = 1'b0;
logic hp_idle_i = 1'b0;
logic flush_busy_i = 1'b0;
logic hp_release_o;
logic block_new_o;
logic flush_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)
);
task automatic complete_flush;
begin
wait (flush_o);
@(negedge clk_i);
flush_busy_i = 1'b1;
wait (!flush_o);
repeat (2) @(negedge clk_i);
flush_busy_i = 1'b0;
wait (!hp_release_o);
end
endtask
initial begin
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
@(negedge clk_i);
release_req_i = 1'b1;
wait (hp_release_o);
if (block_new_o || draining_o) $fatal(1, "released HP was unexpectedly blocked");
@(negedge clk_i);
release_req_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");
end
hp_idle_i = 1'b1;
complete_flush();
if (forced_fault_o || block_new_o) $fatal(1, "normal shutdown reported a forced fault");
@(negedge clk_i);
hp_idle_i = 1'b0;
release_req_i = 1'b1;
wait (hp_release_o);
@(negedge clk_i);
release_req_i = 1'b0;
wait (flush_o);
if (!forced_fault_o) $fatal(1, "quiesce timeout did not record a forced fault");
complete_flush();
$display("HP lifecycle drain, flush, and forced reset test passed");
$finish;
end
endmodule
+3
View File
@@ -164,6 +164,9 @@ module pll_ctrl_tb;
sysctrl.perf_flash_wait_i = '0;
sysctrl.rtc_wake_i = 1'b0;
sysctrl.hp_present_i = 1'b0;
sysctrl.hp_actual_released_i = 1'b0;
sysctrl.hp_draining_i = 1'b0;
sysctrl.hp_forced_fault_i = 1'b0;
rst_n_i = 1'b1;
#10;
rst_n_i = 1'b0;
+3
View File
@@ -118,6 +118,9 @@ module sysctrl_tb;
sysctrl.perf_flash_wait_i = 64'd18;
sysctrl.rtc_wake_i = 1'b0;
sysctrl.hp_present_i = 1'b0;
sysctrl.hp_actual_released_i = 1'b0;
sysctrl.hp_draining_i = 1'b0;
sysctrl.hp_forced_fault_i = 1'b0;
pll_ctrl.req_ready_i = 1'b1;
pll_ctrl.rsp_active_sel_i = '0;
pll_ctrl.rsp_active_valid_i = 1'b0;
+98
View File
@@ -32,6 +32,8 @@ def test_axi4_address_gate_blocks_new_and_drains_accepted_requests(tmp_path: Pat
"-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/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/xchecker.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/mini/top/axi4_address_gate.sv"),
str(ROOT / "tests/rtl/axi4_address_gate_tb.sv"),
"-Mdir",
@@ -48,6 +50,44 @@ def test_axi4_address_gate_blocks_new_and_drains_accepted_requests(tmp_path: Pat
assert "AXI4 address gate quiesce test passed" in result.stdout
def test_axi4_async_bridge_transfers_and_warm_flushes(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "axi4_async_bridge_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator, "--binary", "--timing", "-Wno-fatal", "--top-module",
"axi4_async_bridge_tb",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/cdc"),
"-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/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/xchecker.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/bin2gray.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/gray2bin.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_rst_ctrlr.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_2phase.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/clkrst/rst_sync.sv"),
str(ROOT / "rtl/mini/top/soc_common_cdc.sv"),
str(ROOT / "rtl/mini/top/axi4_async_bridge.sv"),
str(ROOT / "tests/rtl/axi4_async_bridge_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 async bridge transfer and warm flush test passed" in result.stdout
def test_axi4_downsizer_preserves_bursts_lanes_and_backpressure(
tmp_path: Path,
) -> None:
@@ -191,3 +231,61 @@ def test_axi4_interconnect_classifies_decode_protocol_and_access_errors(
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "AXI4 interconnect fault classification test passed" in result.stdout
def test_axi4_data_crossbar_supports_cross_target_outstanding_and_acl(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
memory_map = 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(memory_map),
"--have-sram-if",
"YES",
"--sram-size-kib",
"32",
],
check=True,
)
output = tmp_path / "axi4_data_crossbar_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"axi4_data_crossbar_tb",
"-I" + str(memory_map / "rtl"),
"-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/stream/round_robin_arbiter.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/mini/top/axi4_data_crossbar.sv"),
str(ROOT / "tests/rtl/axi4_data_crossbar_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 data crossbar concurrency and ACL test passed" in result.stdout
+54
View File
@@ -265,6 +265,7 @@ def test_pll_controller_reconfigures_and_falls_back_to_the_safe_clock(tmp_path:
str(ROOT / "rtl/managed/clusterip/common/rtl/clkrst/counter.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/clkrst/clk_int_div.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/clock/safe_clock_mux.sv"),
str(ROOT / "rtl/mini/top/soc_clock_gate.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/edge_det.sv"),
str(ROOT / "rtl/ip/peripheral/pll_ctrl_if.sv"),
str(ROOT / "rtl/ip/peripheral/clock_ctrl_if.sv"),
@@ -279,6 +280,7 @@ def test_pll_controller_reconfigures_and_falls_back_to_the_safe_clock(tmp_path:
str(ROOT / "rtl/mini/top/rcu.sv"),
str(ROOT / "rtl/mini/top/pll_rcu_controller.sv"),
str(ROOT / "rtl/mini/top/clock_config_controller.sv"),
str(ROOT / "rtl/mini/top/clock_frequency_monitor.sv"),
str(ROOT / "rtl/mini/top/soc_clock_reset_subsystem.sv"),
str(ROOT / "tests/rtl/pll_ctrl_tb.sv"),
"",
@@ -360,3 +362,55 @@ def test_sysctrl_does_not_expose_unused_i2c_or_qspi_select_registers() -> None:
):
assert symbol not in rtl
assert "reg_sysctrl_" not in header
def test_hp_lifecycle_controller_drains_and_forces_bounded_reset(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
simulation = tmp_path / "hp_lifecycle_controller_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"hp_lifecycle_controller_tb",
"-o",
str(simulation),
str(ROOT / "rtl/mini/top/hp_lifecycle_controller.sv"),
str(ROOT / "tests/rtl/hp_lifecycle_controller_tb.sv"),
],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "HP lifecycle drain, flush, and forced reset test passed" in result.stdout
def test_clock_frequency_monitor_detects_stop_and_restart(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
simulation = tmp_path / "clock_frequency_monitor_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"clock_frequency_monitor_tb",
"-I",
str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-o",
str(simulation),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv"),
str(ROOT / "rtl/mini/top/clock_frequency_monitor.sv"),
str(ROOT / "tests/rtl/clock_frequency_monitor_tb.sv"),
],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "Clock frequency activity and sticky fault test passed" in result.stdout
+47 -1
View File
@@ -50,6 +50,7 @@ from scripts.regress import ( # noqa: E402
NIGHTLY_EXTRA_COMMANDS,
PDK_PR_PROFILES,
PR_COMMANDS,
RTL_COMMANDS,
RTL_LINT_VALUES,
SMOKE_COMMANDS,
pdk_pr_commands,
@@ -955,7 +956,13 @@ def test_verilator_simulations_use_uniform_timeout() -> None:
regression_commands = (*SMOKE_COMMANDS, *PR_COMMANDS, *NIGHTLY_COMMANDS)
for _, values in regression_commands:
if "SIMU=VERILATOR" in values:
assert not any(value.startswith("SOC_SIM_TIME=") for value in values)
simulation_timeout = [value for value in values if value.startswith("SOC_SIM_TIME=")]
if "APP=ci_smoke" in values and "sim" in values:
assert simulation_timeout == ["SOC_SIM_TIME=360"]
assert "LINK_TYPE=ld2_all_sram" in values
assert "VERILATOR_SIM_ARGS=--fast-flash" in values
else:
assert not simulation_timeout
if "debug-sim" not in values:
assert "HAVE_SVA=YES" in values
@@ -1107,6 +1114,37 @@ def test_smoke_regression_uses_ihp130_behavioral_coverage_only() -> None:
assert "smoke regression supports only --pdk IHP130" in invalid.stderr
def test_rtl_regression_excludes_synthesis_and_timing() -> None:
commands, profiles = select_regression("rtl", "IHP130")
assert commands == RTL_COMMANDS
assert profiles == ("configs/ci/ihp130.mk",)
values = [value for _, command_values in commands for value in command_values]
assert "sim" in values
assert "sim-asm" in values
assert "debug-sim" in values
assert "synth" not in values
assert "netsim" not in values
assert "sta" not in values
dry_run = run(
sys.executable,
str(ROOT / "scripts/regress.py"),
"--root",
str(ROOT),
"--suite",
"rtl",
"--pdk",
"IHP130",
"--dry-run",
)
assert "SIMU=VERILATOR" in dry_run.stdout
assert "SIMU=IVERILOG" in dry_run.stdout
assert "synth" not in dry_run.stdout
assert "netsim" not in dry_run.stdout
assert "STA=OPENSTA" not in dry_run.stdout
def test_pdk_pr_regressions_cover_firmware_rtl_and_selected_netlist_target() -> None:
assert set(PDK_PR_PROFILES) == {"GF180", "IHP130", "ICS55", "SKY130"}
for pdk, profile in PDK_PR_PROFILES.items():
@@ -1118,6 +1156,14 @@ def test_pdk_pr_regressions_cover_firmware_rtl_and_selected_netlist_target() ->
CI_SMOKE_APP_VALUE in values and "SIMU=VERILATOR" in values and "firmware" in values
for values in command_values
)
verilator_values = next(values for values in command_values if "sim" in values)
if pdk == "IHP130":
assert "LINK_TYPE=ld2_all_sram" in verilator_values
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 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(
+42
View File
@@ -3,6 +3,8 @@
from __future__ import annotations
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
@@ -142,3 +144,43 @@ def test_extensions_reject_duplicate_regions_and_irqs(tmp_path: Path) -> None:
result = validate(write_invalid_extensions(tmp_path, document))
assert result.returncode != 0
assert "overlapping or out-of-range IRQs" in result.stderr
def test_extension_dma_executes_copy_tail_and_timeout(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "extension_dma_master_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"extension_dma_master_tb",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
"-I" + str(ROOT / "rtl/mini/top"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/mini/top/extension_dma_master.sv"),
str(ROOT / "tests/rtl/extension_dma_master_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 "Extension DMA copy, tail strobe, and timeout test passed" in result.stdout