feat: improve the performance of the onchip sram ctrl

This commit is contained in:
maksyuki committed 2026-08-24 20:10:22 +08:00
1 parent 2e06aaf721
commit 46a3a90be6
53 files changed
+2881 -151

No files matched your search

+8 -4
View File
@@ -39,6 +39,7 @@ PDK ?= IHP130
HAVE_PLL ?= NO
HAVE_SRAM_IF ?= NO
HAVE_SRAM_MACRO ?= NO
SRAM_SIZE_KIB ?= 128
PDK_BEHAV ?= NO
HAVE_SVA ?= NO
BUILD_RELEASE ?= NO
@@ -85,7 +86,7 @@ VCS_SHELL_PYTHON := $(if $(filter VCS,$(SIMU)),$(if $(filter $(VCS_SHELL_GOALS
JOBS ?= $(shell count=$$(nproc 2>/dev/null || printf '1'); \
if [ "$$count" -gt "$(MAX_JOBS)" ]; then printf '%s' '$(MAX_JOBS)'; \
else printf '%s' "$$count"; fi)
CONFIG_KEY_VARS := SOC PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO PDK_BEHAV HAVE_SVA \
CONFIG_KEY_VARS := SOC PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO SRAM_SIZE_KIB PDK_BEHAV HAVE_SVA \
BUILD_RELEASE JTAG_IDCODE EXT_CLK_HZ AUD_CLK_HZ CLINT_TIMEBASE_HZ ISA HAVE_CSR APP LINK_TYPE \
COREMARK_MODE RTL_TOP FIRMWARE_NAME
VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \
@@ -131,6 +132,7 @@ VALID_ISA := RV32E RV32I RV32IM
VALID_APP := benchmark bringup ci_smoke coremark debug shell xpi_flash_loader
VALID_LINK_TYPE := xip jtag_sram ld2_all_sram ld2_sram ld2_psram ld2_sdram
VALID_COREMARK_MODE := quick standard
VALID_SRAM_SIZE_KIB := 4 16 32 64 128
define validate_value
$(if $(filter $($(1)),$(2)),,$(error Invalid $(1)='$($(1))'; expected one of: $(2)))
@@ -145,6 +147,7 @@ $(call validate_value,PDK,$(VALID_PDK))
$(call validate_value,HAVE_PLL,$(VALID_BOOL))
$(call validate_value,HAVE_SRAM_IF,$(VALID_BOOL))
$(call validate_value,HAVE_SRAM_MACRO,$(VALID_BOOL))
$(call validate_value,SRAM_SIZE_KIB,$(VALID_SRAM_SIZE_KIB))
$(call validate_value,PDK_BEHAV,$(VALID_BOOL))
$(call validate_value,HAVE_SVA,$(VALID_BOOL))
$(call validate_value,BUILD_RELEASE,$(VALID_BOOL))
@@ -276,7 +279,7 @@ include physical/librelane/Makefile
benchmark-report coremark-report \
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-opipsram formal-dma formal-sdio formal-clean formal-doctor \
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-sdio formal-clean formal-doctor \
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all
.NOTPARALLEL: setup
@@ -310,7 +313,7 @@ help:
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-opipsram | formal-dma | formal-sdio run peripheral proofs' \
'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-onchip-ram | formal-opipsram | formal-dma | formal-sdio run peripheral proofs' \
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
@@ -350,7 +353,8 @@ config:
STA '$(STA)' FORMAL '$(FORMAL)' \
VCS_USE_LSF '$(VCS_USE_LSF)' PDK '$(PDK)' \
HAVE_PLL '$(HAVE_PLL)' HAVE_SRAM_IF '$(HAVE_SRAM_IF)' \
HAVE_SRAM_MACRO '$(HAVE_SRAM_MACRO)' PDK_BEHAV '$(PDK_BEHAV)' HAVE_SVA '$(HAVE_SVA)' \
HAVE_SRAM_MACRO '$(HAVE_SRAM_MACRO)' SRAM_SIZE_KIB '$(SRAM_SIZE_KIB)' \
PDK_BEHAV '$(PDK_BEHAV)' HAVE_SVA '$(HAVE_SVA)' \
BUILD_RELEASE '$(BUILD_RELEASE)' \
JTAG_IDCODE '$(JTAG_IDCODE)' EXT_CLK_HZ '$(EXT_CLK_HZ)' AUD_CLK_HZ '$(AUD_CLK_HZ)' \
CLINT_TIMEBASE_HZ '$(CLINT_TIMEBASE_HZ)' \
+2
View File
@@ -69,6 +69,8 @@ The committed profiles are the supported starting points. They select an ISA,
PDK, application, linker layout, and optional features as one reproducible
configuration. The management core is fixed to Hazard3. The user-extension
fabric exposes C0-C3 user-core slots and software selects one active user core.
`SRAM_SIZE_KIB` selects a generated 4/16/32/64/128 KiB native-AXI4 on-chip
SRAM window; committed profiles explicitly retain the 128 KiB default.
| Profile | ISA | Application | Coverage |
| --- | --- | --- | --- |
+21
View File
@@ -4,6 +4,7 @@
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/crypto.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/onchip_sram.h>
#include <retrosoc/hal/rng.h>
#include <retrosoc/hal/rtc.h>
#include <retrosoc/hal/sdram.h>
@@ -22,6 +23,22 @@ static bool rs_ci_smoke_archinfo_v2(void) {
(rs_archinfo_read_device_id(device_id) == RS_ENOTSUP) && (rs_rtc_probe() == RS_OK);
}
static bool rs_ci_smoke_onchip_sram(void) {
rs_onchip_sram_info_t info;
rs_onchip_sram_perf_t performance;
if ((rs_onchip_sram_probe(&info) != RS_OK) ||
(rs_onchip_sram_read_performance(&performance) != RS_OK) ||
(info.present != (RS_SOC_HAS_SRAM != 0U))) {
return false;
}
if (info.present) {
return (info.memory_bytes == RS_SOC_SRAM_SIZE) &&
(info.bank_count == (RS_SOC_SRAM_SIZE / RS_ONCHIP_SRAM_BANK_BYTES_VALUE));
}
return (info.memory_bytes == 0U) && (info.bank_count == 0U);
}
static bool rs_ci_smoke_user_ip(void) {
rs_status_t status;
uint32_t identifier = 0U;
@@ -289,6 +306,10 @@ int main(void) {
rs_test_finish(RS_TEST_FAILED, 1U);
}
printf("ci_smoke: archinfo passed\n");
if (!rs_ci_smoke_onchip_sram()) {
rs_test_finish(RS_TEST_FAILED, 12U);
}
printf("ci_smoke: on-chip SRAM passed\n");
if (!rs_ci_smoke_user_ip()) {
rs_test_finish(RS_TEST_FAILED, 11U);
}
+5
View File
@@ -17,6 +17,11 @@ by GDB/OpenOCD; it is a programming utility, not a normal boot application.
Start builds from a committed profile rather than setting an unreviewed mix of
variables on the command line.
`SRAM_SIZE_KIB` selects 4, 16, 32, 64, or 128 KiB of on-chip SRAM and is part
of the build variant key. Every committed profile sets 128 explicitly;
profiles with `HAVE_SRAM_IF=NO` keep a reproducible capacity selection while
the hardware capability reports the memory absent.
`PDK_BEHAV=YES` selects technology-wrapper functional models for behavioral
simulation. It is a simulation-only setting, participates in the build variant
key, and is invalid with Yosys synthesis. Profiles that execute from an SRAM
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1
View File
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1
View File
@@ -3,6 +3,7 @@ PDK := GF180
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1
View File
@@ -3,6 +3,7 @@ PDK := ICS55
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1
View File
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1
View File
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1
View File
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+1
View File
@@ -3,6 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1
View File
@@ -3,6 +3,7 @@ PDK := SKY130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1
View File
@@ -3,6 +3,7 @@ PDK := ICS55
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+32
View File
@@ -0,0 +1,32 @@
#ifndef RETROSOC_HAL_ONCHIP_SRAM_H
#define RETROSOC_HAL_ONCHIP_SRAM_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#include <retrosoc/hal/onchip_sram_regs.h>
typedef struct {
uint32_t capability;
uint32_t memory_bytes;
uint32_t bank_count;
uint32_t bank_bytes;
uint8_t max_burst_beats;
uint8_t data_bytes;
bool present;
} rs_onchip_sram_info_t;
typedef struct {
uint32_t read_requests;
uint32_t write_requests;
uint32_t read_beats;
uint32_t write_beats;
uint32_t stall_cycles;
uint32_t error_responses;
} rs_onchip_sram_perf_t;
rs_status_t rs_onchip_sram_probe(rs_onchip_sram_info_t *info);
rs_status_t rs_onchip_sram_read_performance(rs_onchip_sram_perf_t *performance);
#endif
@@ -0,0 +1,41 @@
#ifndef RETROSOC_HAL_ONCHIP_SRAM_REGS_H
#define RETROSOC_HAL_ONCHIP_SRAM_REGS_H
#include <stdint.h>
#include <retrosoc/generated/memory_map.h>
/* Keep this handwritten ABI synchronized with onchip_ram_define.svh. */
#define RS_ONCHIP_SRAM_REG_IP_ID UINT32_C(0x000)
#define RS_ONCHIP_SRAM_REG_IP_VERSION UINT32_C(0x004)
#define RS_ONCHIP_SRAM_REG_CAPABILITY UINT32_C(0x008)
#define RS_ONCHIP_SRAM_REG_MEMORY_BYTES UINT32_C(0x00C)
#define RS_ONCHIP_SRAM_REG_BANK_COUNT UINT32_C(0x010)
#define RS_ONCHIP_SRAM_REG_BANK_BYTES UINT32_C(0x014)
#define RS_ONCHIP_SRAM_REG_PERF_READ_REQUESTS UINT32_C(0x020)
#define RS_ONCHIP_SRAM_REG_PERF_WRITE_REQUESTS UINT32_C(0x024)
#define RS_ONCHIP_SRAM_REG_PERF_READ_BEATS UINT32_C(0x028)
#define RS_ONCHIP_SRAM_REG_PERF_WRITE_BEATS UINT32_C(0x02C)
#define RS_ONCHIP_SRAM_REG_PERF_STALL_CYCLES UINT32_C(0x030)
#define RS_ONCHIP_SRAM_REG_PERF_ERROR_RESPONSES UINT32_C(0x034)
#define RS_ONCHIP_SRAM_IP_ID_VALUE UINT32_C(0x5352414D)
#define RS_ONCHIP_SRAM_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_ONCHIP_SRAM_BANK_BYTES_VALUE UINT32_C(4096)
#define RS_ONCHIP_SRAM_CAP_PRESENT UINT32_C(0x00000001)
#define RS_ONCHIP_SRAM_CAP_NATIVE_AXI4 UINT32_C(0x00000002)
#define RS_ONCHIP_SRAM_CAP_BYTE_WRITE UINT32_C(0x00000004)
#define RS_ONCHIP_SRAM_CAP_FIXED UINT32_C(0x00000008)
#define RS_ONCHIP_SRAM_CAP_INCR UINT32_C(0x00000010)
#define RS_ONCHIP_SRAM_CAP_WRAP UINT32_C(0x00000020)
#define RS_ONCHIP_SRAM_CAP_PERFORMANCE UINT32_C(0x00000040)
#define RS_ONCHIP_SRAM_CAP_MAX_BEATS_SHIFT UINT32_C(8)
#define RS_ONCHIP_SRAM_CAP_MAX_BEATS_MASK UINT32_C(0x0000FF00)
#define RS_ONCHIP_SRAM_CAP_DATA_BYTES_SHIFT UINT32_C(16)
#define RS_ONCHIP_SRAM_CAP_DATA_BYTES_MASK UINT32_C(0x00FF0000)
#define RS_ONCHIP_SRAM_REG(offset) \
(*(volatile uint32_t *)(uintptr_t)(RS_SOC_APB4_SRAM_BASE + (offset)))
#endif
+66
View File
@@ -0,0 +1,66 @@
#include <stddef.h>
#include <retrosoc/hal/onchip_sram.h>
rs_status_t rs_onchip_sram_probe(rs_onchip_sram_info_t *info) {
uint32_t capability;
uint32_t memory_bytes;
uint32_t bank_count;
uint32_t bank_bytes;
if (info == NULL) {
return RS_EINVAL;
}
if ((RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_IP_ID) != RS_ONCHIP_SRAM_IP_ID_VALUE) ||
(RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_IP_VERSION) != RS_ONCHIP_SRAM_IP_VERSION_VALUE)) {
return RS_EIO;
}
capability = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_CAPABILITY);
memory_bytes = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_MEMORY_BYTES);
bank_count = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_BANK_COUNT);
bank_bytes = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_BANK_BYTES);
info->capability = capability;
info->memory_bytes = memory_bytes;
info->bank_count = bank_count;
info->bank_bytes = bank_bytes;
info->max_burst_beats = (uint8_t)((capability & RS_ONCHIP_SRAM_CAP_MAX_BEATS_MASK) >>
RS_ONCHIP_SRAM_CAP_MAX_BEATS_SHIFT);
info->data_bytes = (uint8_t)((capability & RS_ONCHIP_SRAM_CAP_DATA_BYTES_MASK) >>
RS_ONCHIP_SRAM_CAP_DATA_BYTES_SHIFT);
info->present = (capability & RS_ONCHIP_SRAM_CAP_PRESENT) != 0U;
if ((bank_bytes != RS_ONCHIP_SRAM_BANK_BYTES_VALUE) || (info->max_burst_beats != 16U) ||
(info->data_bytes != 4U) ||
((capability & (RS_ONCHIP_SRAM_CAP_NATIVE_AXI4 | RS_ONCHIP_SRAM_CAP_BYTE_WRITE |
RS_ONCHIP_SRAM_CAP_FIXED | RS_ONCHIP_SRAM_CAP_INCR |
RS_ONCHIP_SRAM_CAP_WRAP | RS_ONCHIP_SRAM_CAP_PERFORMANCE)) !=
(RS_ONCHIP_SRAM_CAP_NATIVE_AXI4 | RS_ONCHIP_SRAM_CAP_BYTE_WRITE |
RS_ONCHIP_SRAM_CAP_FIXED | RS_ONCHIP_SRAM_CAP_INCR | RS_ONCHIP_SRAM_CAP_WRAP |
RS_ONCHIP_SRAM_CAP_PERFORMANCE))) {
return RS_EIO;
}
if (info->present) {
if ((memory_bytes != RS_SOC_SRAM_SIZE) ||
(bank_count != (memory_bytes / RS_ONCHIP_SRAM_BANK_BYTES_VALUE)) ||
(bank_count == 0U)) {
return RS_EIO;
}
} else if ((memory_bytes != 0U) || (bank_count != 0U)) {
return RS_EIO;
}
return RS_OK;
}
rs_status_t rs_onchip_sram_read_performance(rs_onchip_sram_perf_t *performance) {
if (performance == NULL) {
return RS_EINVAL;
}
performance->read_requests = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_READ_REQUESTS);
performance->write_requests = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_WRITE_REQUESTS);
performance->read_beats = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_READ_BEATS);
performance->write_beats = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_WRITE_BEATS);
performance->stall_cycles = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_STALL_CYCLES);
performance->error_responses = RS_ONCHIP_SRAM_REG(RS_ONCHIP_SRAM_REG_PERF_ERROR_RESPONSES);
return RS_OK;
}
+3
View File
@@ -46,6 +46,9 @@ the root README and subsystem guides.
counting modes, interrupt, debug-freeze, HAL, and verification contracts.
- [ip/sysctrl.md](ip/sysctrl.md) defines the SystemCtrl register ABI, user-core
lifecycle, PLL/fault/performance/RTC/test contracts, HAL, and verification.
- [ip/onchip-sram.md](ip/onchip-sram.md) defines the configurable native-AXI4
SRAM architecture, APB capability ABI, technology mapping, verification, and
reliability roadmap.
- [ip/user-ip.md](ip/user-ip.md) defines the selectable 4 KiB user-IP window,
generic HAL, extension-owned register ABI, and verification contract.
- [ip/clint.md](ip/clint.md) defines the standard CLINT register map, fixed
+18 -11
View File
@@ -34,8 +34,11 @@ Fixed-address and APB4/MMIO endpoints are always single-beat. See the
The fixed targets are the APB4 configuration plane (`apb4_periph`), APB
(`apb4_system`), SRAM, SDRAM, 4-bit PSRAM, OPI PSRAM, XPI/Flash, SPI-SD,
`DECERR`, and `SLVERR`. APB4 and APB accept only single-beat transactions.
SRAM uses a direct AXI4 target with pipelined synchronous reads and response
buffering. External-memory data windows accept up to sixteen beats; their
SRAM uses a direct AXI4 target with pipelined synchronous reads, a one-cycle
first response, backpressure, and one beat per cycle sustained bursts. Its
effective window is generated from `SRAM_SIZE_KIB`; see the
[on-chip SRAM contract](ip/onchip-sram.md). External-memory data windows accept
up to sixteen beats; their
controller front ends validate and serialize or coalesce the physical
transactions while preserving the documented AXI response contract. Register
configuration remains on APB4.
@@ -63,12 +66,16 @@ access-control rejection to `PROTERR`.
## Verification and Performance Gates
`tests/test_axi4.py` covers a four-beat write/read, response backpressure, and
rejection of a seventeen-beat request without touching the APB4 target. The
bridge unit test also covers Common address generation for FIXED and WRAP
sequencing. The IHP130 `ci_smoke` regression is the end-to-end boot and
configuration check.
CoreMark uses SRAM and must not regress more than five percent from the recorded
7,620,324-cycle baseline. A controller may be promoted to a native burst target
only after aligned sixteen-word tests show at least twenty percent improvement
and cover partial writes, backpressure, device timing, and error termination.
`tests/test_axi4.py` covers fabric classification. `tests/test_onchip_ram.py`
covers all five capacities, sixteen-beat reads/writes, response backpressure,
partial writes, technology mapping, and error termination. The bridge unit
test also covers Common address generation for FIXED and WRAP sequencing. The
IHP130 `ci_smoke` regression is the end-to-end boot and configuration check.
CoreMark uses SRAM and must not regress more than five percent from the native
4,662,868-cycle baseline; the retired compatibility path required 7,620,324
cycles. A native SRAM target must return an aligned sixteen-word burst in
sixteen response cycles, report first-response and total latency against the
compatibility bridge, and cover partial writes, backpressure, device timing,
and error termination. The measured compatibility bridge needs 18 response
cycles; the native target removes both overhead cycles and reaches the 32-bit
AXI payload limit.
+161
View File
@@ -0,0 +1,161 @@
# Configurable Native-AXI4 On-chip SRAM
## Scope and maturity
The Mini SoC on-chip SRAM is a synthesis-time configurable, 32-bit native-AXI4
target backed by fixed `1024 x 32` single-port technology wrappers. Supported
capacities are 4, 16, 32, 64, and 128 KiB. The data aperture starts at
`0x3000_0000`; its generated end address and linker length match the selected
physical capacity. A management-only APB4 window at `0x1001_7000` reports the
configuration and saturating performance counters.
This release is a protocol and performance MVP. It is not a safety-certified,
silicon-proven, or tapeout-ready memory subsystem. ECC, autonomous scrubbing,
MBIST, redundancy repair, power gating, retention, and independently licensed
AXI verification IP signoff remain outside the delivered scope.
## Commercial reference review
The reference review was refreshed on 2026-08-24 from vendor-owned material.
These products guide architecture and delivery expectations; they are not RTL
dependencies and do not transfer a vendor's qualification to retroSoC.
| Reference | Problem and architecture | Status and decision |
| --- | --- | --- |
| [Raspberry Pi RP2350](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) | Main SRAM is split into two four-way word-striped groups. Two independent 4 KiB banks provide low-contention storage for stacks or hot data. | The official data sheet remains maintained. Reuse explicit bank ownership. Do not add low-bit striping while the Mini interconnect grants one transaction per target; it would increase switching without increasing target throughput. |
| [STM32H7 embedded SRAM and RAMECC](https://www.st.com/resource/en/reference_manual/rm0455-stm32h7a37b3-and-stm32h7b0-value-line-_advanced-armbased-32bit-mcus-stmicroelectronics.pdf) | AXI SRAM, AHB SRAM, TCM, backup SRAM, and power domains separate bandwidth, deterministic latency, and retention. SECDED reports failing address/data, and partial writes require read-modify-write. | STM32H7 products remain active. Reuse capability discovery, error localization, and a separate future ECC layer. Avoid claiming unchanged latency before ECC/RMW timing is verified. |
| [NXP i.MX RT FlexRAM](https://www.nxp.com/docs/en/application-note/AN12077.pdf) and [RT1170 ECC guidance](https://www.nxp.com/docs/en/application-note/AN13204.pdf) | Fixed banks can be assigned to OCRAM, ITCM, or DTCM. This depends on boot configuration, legal TCM sizes, linker agreement, and complete ECC initialization. | NXP continues to update RT1170 material. Reuse bank-based product configuration and generated linker consistency. Avoid runtime repartitioning and fuse/boot-order coupling for Mini. |
| [Infineon AURIX TC3xx MTU/SSH](https://www.infineon.com/assets/row/public/documents/10/44/infineon-aurix-tc3xx-part1-usermanual-en.pdf) | SRAM support hardware surrounds memories with ECC/EDC, address monitoring, error tracking, initialization, and MBIST. A central MTU exposes a uniform control plane. | AURIX documentation and products remain active. Reuse separation between functional access and reliability support. Avoid destructive runtime MBIST without a quiesce contract. |
| [CAST SRAM-CTRL and ECC-SRAM](https://www.cast-inc.com/peripherals/memory-controllers/sram-ctrl) | Commercial IP emphasizes native AXI/AHB/APB, byte accesses, low latency, lint/scan readiness, SECDED, testbenches, scripts, and integration documentation. | The products remain offered. Use the features and deliverables as an IP-readiness checklist, not as evidence for this implementation. |
| [Synopsys STAR Memory System](https://www.synopsys.com/articles/embedded-memory-test.html) | Hierarchical test, diagnosis, redundancy repair, tester patterns, and physical failed-bit mapping address manufacturing yield and field diagnosis. | The product family remains published. Keep DFT/repair as a physical-delivery phase instead of embedding an unqualified March engine in the AXI datapath. |
The selected architecture is static capacity configuration plus a native AXI
datapath. Dynamic bank reassignment, caches, multiple outstanding IDs, and
multi-master bank scheduling require changes above this target and are not
cost-effective in the current one-owner-per-target fabric.
## Configuration and address contract
`SRAM_SIZE_KIB` is part of the reproducible configuration digest and accepts
only `4`, `16`, `32`, `64`, or `128`. Every committed profile sets `128`
explicitly to preserve the prior address map. The address-map generator uses
the selected value for RTL decode macros, SDK base/size/end constants, and the
linker `LENGTH(SRAM)`.
`HAVE_SRAM_IF=YES` marks the memory present and elaborates the bank array. When
it is `NO`, the AXI target returns `DECERR`; the APB window remains readable
with `PRESENT=0`, zero memory bytes, and zero banks.
| `SRAM_SIZE_KIB` | 4 KiB data banks | Address range |
| ---: | ---: | --- |
| 4 | 1 | `0x3000_0000..0x3000_0fff` |
| 16 | 4 | `0x3000_0000..0x3000_3fff` |
| 32 | 8 | `0x3000_0000..0x3000_7fff` |
| 64 | 16 | `0x3000_0000..0x3000_ffff` |
| 128 | 32 | `0x3000_0000..0x3001_ffff` |
## AXI4 datapath
The target uses the project-wide 32-bit address/data and 1-bit ID/USER
geometry. It accepts one active read or write transaction, gives AR priority
when AR and AW are simultaneously valid, and preserves the ID to the terminal
response.
Supported requests are naturally aligned 1-, 2-, or 4-byte transfers using
`FIXED`, `INCR`, or legal 2/4/8/16-beat `WRAP` bursts. Bursts are limited to 16
beats and one 4 KiB page. Because a bank is exactly one 4 KiB page, a legal
transaction selects one bank for its lifetime.
The macro is issued directly on AR acceptance. The first response is available
one cycle later. With `RREADY` asserted, following beats issue on preceding
response cycles, giving one word per cycle without bubbles. Backpressure stops
new reads and holds data, ID, response, and `RLAST` stable.
After AW acceptance the target consumes one W beat per cycle. Strobes outside
the lanes selected by `AWSIZE` and address are illegal. A zero strobe is a
no-op and does not enable a bank. `WLAST` must agree with `AWLEN`; a mismatching
beat is not written and terminates with `SLVERR`. Earlier accepted beats are
not rolled back.
Malformed protocol requests return `SLVERR`. An absent memory or address
outside the parameterized capacity returns `DECERR`. Protocol-invalid requests
are capped to one error response beat at the IP boundary. The SoC interconnect
normally rejects them before they reach SRAM.
## APB4 management ABI
All registers are read-only. Writes, unaligned accesses, and unknown offsets
complete with `PSLVERR`. Counters are 32-bit saturating values controlled by
the existing SYSCTRL `PERF_CTRL` enable and clear signals; no second control
ABI or interrupt is introduced.
| Offset | Name | Description |
| ---: | --- | --- |
| `0x000` | `IP_ID` | `0x5352414d` (`SRAM`) |
| `0x004` | `IP_VERSION` | ABI version `0x00010000` |
| `0x008` | `CAPABILITY` | Present, native AXI4, byte-write, burst, and performance flags; max beats in 15:8 and data bytes in 23:16 |
| `0x00c` | `MEMORY_BYTES` | Implemented capacity, or zero when absent |
| `0x010` | `BANK_COUNT` | Implemented data banks, or zero when absent |
| `0x014` | `BANK_BYTES` | Fixed value 4096 |
| `0x020` | `PERF_READ_REQUESTS` | Accepted AR requests |
| `0x024` | `PERF_WRITE_REQUESTS` | Accepted AW requests |
| `0x028` | `PERF_READ_BEATS` | Accepted R beats |
| `0x02c` | `PERF_WRITE_BEATS` | Accepted W beats, including drained error traffic |
| `0x030` | `PERF_STALL_CYCLES` | Cycles with any AXI VALID blocked by READY |
| `0x034` | `PERF_ERROR_RESPONSES` | Terminal non-OKAY responses |
The SDK exposes `rs_onchip_sram_probe()` and
`rs_onchip_sram_read_performance()`. RTL and C constants remain handwritten;
the register parity test prevents drift without adding a generator.
## Technology mapping
`tc_sram_1024x32` is the stable technology boundary. Behavioral simulation
uses an inferred 1024-by-32 array. IHP130 maps to
`RM_IHPSG13_1P_1024x32_c2_bm_bist`; the former 1024-by-64 mapping and discarded
upper half are removed. ICS55 continues to map to its 1024-by-32 wrapper. Bank
count, rather than technology depth or width, implements every product size.
The wrapper preserves byte masks and one-cycle synchronous reads. PDK timing,
LEF/GDS placement, power hookup, BIST pins, and foundry signoff remain
physical-flow responsibilities.
## Verification and performance evidence
`tests/test_onchip_ram.py` elaborates all five capacities and covers bank
boundaries, first/last words, byte/half/word masks, `FIXED`, `INCR`, `WRAP`,
16-beat continuous traffic, backpressure, AR/AW priority, absent-memory and
malformed accesses, counters, and IHP130/ICS55 x32 wrapper polarity and masks.
`make CONFIG=configs/ci/ihp130.mk formal-onchip-ram` runs a 20-cycle bounded
proof and cover set for AXI stability, channel exclusion, response bounds,
physical-write qualification, and reachable read/write/error completions.
The directed performance comparison measures 16 response cycles for a 16-word
native read and 18 through the compatibility bridge. This is an 11.1 percent
total-latency reduction and a complete removal of the bridge's two non-payload
cycles; 16 cycles is the minimum possible on the 32-bit response channel.
The SRAM-resident four-iteration CoreMark run completes in 4,662,868 cycles
against the recorded 7,620,324-cycle compatibility baseline, a 38.8 percent
cycle reduction. It remains an unqualified regression workload rather than a
public CoreMark score.
## Reliability and performance roadmap
The next phase should add SECDED outside the stable data macro. For every four
data banks, one `1024 x 32` bank can store four independent ECC bytes per word
address. A full write computes one ECC byte and writes its byte lane; a partial
write reads and corrects the old word, merges bytes, then writes data and ECC.
The management ABI would add first-error address/syndrome, corrected and
uncorrectable counters, injection, sticky status, and an uncorrectable-error
interrupt. All locations must be initialized with valid data/ECC before reads.
MBIST follows ECC. A future support wrapper must quiesce AXI, exclude CPUs,
DMA, and debuggers, run a reviewed startup March test or qualified
nondestructive runtime test, record the failing bank/address, and hand
technology BIST/redundancy pins to the physical flow. That phase requires DFT
and tester-ready evidence, not only RTL simulation.
Further throughput work is conditional on the fabric. Independent read/write
target ownership, more IDs, or per-bank queues are useful only after concurrent
transactions can reach SRAM. RP2350-style striping, bank-aware arbitration,
QoS, and power gating should then be evaluated from measured contention.
+6
View File
@@ -68,6 +68,12 @@ does not elaborate the tri-state delays in the Micron model. The Icarus
testbench retains that Micron timing model, so it is the reference for SDRAM
command timing while Verilator provides fast functional coverage.
The Mini SoC on-chip SRAM is a synthesis-time selectable 4/16/32/64/128 KiB
native 32-bit AXI4 target built from fixed 4 KiB technology banks. Its read-only
APB capability/performance ABI, technology mapping, verification evidence, and
ECC/MBIST roadmap are documented in
[Configurable Native-AXI4 On-chip SRAM](../docs/ip/onchip-sram.md).
The self-owned ESP-PSRAM64H controller exposes a 32-bit AXI4 data window and a
separate APB4 management plane across four independently isolated 8 MiB chips.
Its frozen architecture, register ABI, timing limits, model, formal target, and
+1 -1
View File
@@ -2,6 +2,6 @@
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_udp.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_core_behavioral_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x32_c2_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x16_c3_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x8_c3_bm_bist.v
+2 -1
View File
@@ -64,7 +64,8 @@ gen_mpw_code: $(MPW_VARIANT_STAMP)
$(MEMORY_MAP_STAMP): $(MEMORY_MAP_SOURCE) $(MEMORY_MAP_SCRIPT)
$(FLOW_PYTHON) $(MEMORY_MAP_SCRIPT) --map $(MEMORY_MAP_SOURCE) \
--output-dir $(MEMORY_MAP_DIR) --have-sram-if $(HAVE_SRAM_IF)
--output-dir $(MEMORY_MAP_DIR) --have-sram-if $(HAVE_SRAM_IF) \
--sram-size-kib $(SRAM_SIZE_KIB)
@touch $@
memory-map: $(MEMORY_MAP_STAMP)
+29 -8
View File
@@ -16,6 +16,7 @@ VALID_ROUTES = {"apb4_periph", "apb4_system", "axi4", "ram", "reserved"}
VALID_KINDS = {"active", "reserved"}
VALID_USER_ACCESS = {"none", "ro", "rw"}
VALID_BURST = {"incr1", "incr4", "incr16"}
VALID_SRAM_SIZE_KIB = (4, 16, 32, 64, 128)
def parse_integer(value: Any, field: str) -> int:
@@ -29,7 +30,11 @@ def parse_integer(value: Any, field: str) -> int:
raise ValueError(f"{field} must be an integer")
def read_map(path: Path) -> tuple[int, list[dict[str, Any]], list[dict[str, Any]]]:
def read_map(
path: Path, sram_size_kib: int = 128
) -> tuple[int, list[dict[str, Any]], list[dict[str, Any]]]:
if sram_size_kib not in VALID_SRAM_SIZE_KIB:
raise ValueError(f"sram_size_kib must be one of {VALID_SRAM_SIZE_KIB}")
with path.open(encoding="utf-8") as source:
document = json.load(source)
if document.get("schema_version") != 2:
@@ -89,6 +94,17 @@ def read_map(path: Path) -> tuple[int, list[dict[str, Any]], list[dict[str, Any]
raise ValueError(f"regions[{index}] reserved regions cannot support bursts")
base = parse_integer(region.get("base"), f"regions[{index}].base")
size = parse_integer(region.get("size"), f"regions[{index}].size")
if symbol == "SRAM":
size_options = region.get("size_options")
if not isinstance(size_options, list):
raise ValueError(f"regions[{index}].size_options must be a list")
parsed_options = tuple(
parse_integer(value, f"regions[{index}].size_options") for value in size_options
)
expected_options = tuple(value * 1024 for value in VALID_SRAM_SIZE_KIB)
if parsed_options != expected_options:
raise ValueError(f"regions[{index}].size_options must be {expected_options}")
size = sram_size_kib * 1024
if base < 0 or size <= 0 or base + size > MAX_ADDRESS:
raise ValueError(f"regions[{index}] exceeds the 32-bit address space")
if base % 4 != 0 or size % 4 != 0:
@@ -155,6 +171,7 @@ def render_rtl(
symbol = region["symbol"]
lines += [
f"`define SOC_ADDR_{symbol}_BASE {hex32(region['base'])}",
f"`define SOC_ADDR_{symbol}_SIZE {hex32(region['size'])}",
f"`define SOC_ADDR_{symbol}_END {hex32(region['end'])}",
f"`define SOC_ADDR_IS_{symbol}(addr) "
f"{range_expression(symbol, region['base'], region['end'])}",
@@ -172,9 +189,7 @@ def render_rtl(
for region in regions
if region["route"] in {"apb4_periph", "axi4"}
]
lines.append(
f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})"
)
lines.append(f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})")
user_readable = [
f"`SOC_ADDR_IS_{region['symbol']}(addr)"
for region in regions
@@ -282,8 +297,8 @@ def atomic_write(path: Path, content: str) -> None:
raise
def generate(map_path: Path, output_dir: Path, have_sram_if: str) -> None:
cpu_reset, regions, sysctrl_registers = read_map(map_path)
def generate(map_path: Path, output_dir: Path, have_sram_if: str, sram_size_kib: int) -> None:
cpu_reset, regions, sysctrl_registers = read_map(map_path, sram_size_kib)
atomic_write(
output_dir / "rtl" / "mmap_define.svh",
render_rtl(cpu_reset, regions, sysctrl_registers),
@@ -301,15 +316,21 @@ def main() -> int:
parser.add_argument("--map", required=True, type=Path)
parser.add_argument("--output-dir", type=Path)
parser.add_argument("--have-sram-if", choices=("YES", "NO"), default="NO")
parser.add_argument("--sram-size-kib", type=int, choices=VALID_SRAM_SIZE_KIB, default=128)
parser.add_argument("--check", action="store_true", help="validate only; write nothing")
arguments = parser.parse_args()
try:
if arguments.check:
read_map(arguments.map)
read_map(arguments.map, arguments.sram_size_kib)
else:
if arguments.output_dir is None:
parser.error("--output-dir is required unless --check is used")
generate(arguments.map, arguments.output_dir, arguments.have_sram_if)
generate(
arguments.map,
arguments.output_dir,
arguments.have_sram_if,
arguments.sram_size_kib,
)
except (OSError, ValueError, json.JSONDecodeError) as error:
parser.error(str(error))
return 0
+16
View File
@@ -379,6 +379,15 @@
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_SRAM",
"base": "0x10017000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_ARCHINFO",
"base": "0x20000000",
@@ -455,6 +464,13 @@
"symbol": "SRAM",
"base": "0x30000000",
"size": "0x00020000",
"size_options": [
"0x00001000",
"0x00004000",
"0x00008000",
"0x00010000",
"0x00020000"
],
"route": "ram",
"kind": "active",
"public": true,
+2
View File
@@ -17,6 +17,8 @@
/top/core_wrapper.sv
/top/retrosoc.sv
/top/retrosoc_asic.sv
/top/retrosoc_core.sv
/top/onchip_ram_reg.sv
/top/onchip_ram.sv
/top/apb4_periph.sv
/top/apb4_system.sv
+14 -1
View File
@@ -26,7 +26,7 @@ TOP = ROOT / "rtl/mini/top"
def target_defines(target: str) -> list[str]:
if target == "opipsram":
if target in {"onchip_ram", "opipsram"}:
return ["+define+PDK_BEHAV"]
return ["+define+SV_ASSRT_DISABLE"]
@@ -197,6 +197,18 @@ def source_files(target: str) -> list[Path]:
MEMORY / "psram_phy.sv",
SCRIPT_DIR / "psram_formal.sv",
]
if target == "onchip_ram":
return [
COMMON_RTL / "interface/axi4_if.sv",
COMMON_RTL / "interface/apb4_if.sv",
COMMON_RTL / "interface/axi4_addr_gen.sv",
COMMON_RTL / "utils/register.sv",
COMMON_RTL / "utils/xchecker.sv",
ROOT / "rtl/tech/tc_sram.sv",
TOP / "onchip_ram_reg.sv",
TOP / "onchip_ram.sv",
SCRIPT_DIR / "onchip_ram_formal.sv",
]
if target == "opipsram":
return [
COMMON_RTL / "interface/axi4_if.sv",
@@ -292,6 +304,7 @@ def parse_args() -> argparse.Namespace:
"dvp",
"i2s",
"psram",
"onchip_ram",
"opipsram",
"dma",
"sdio",
+172
View File
@@ -0,0 +1,172 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module onchip_ram_formal_design (
// verilog_format: off -- preserve AXI channel grouping
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic awvalid,
output logic awready,
output logic [31:0] awaddr,
output logic [7:0] awlen,
output logic [2:0] awsize,
output logic [1:0] awburst,
output logic wvalid,
output logic wready,
output logic [31:0] wdata,
output logic [3:0] wstrb,
output logic wlast,
output logic bvalid,
output logic bready,
output logic [1:0] bresp,
output logic arvalid,
output logic arready,
output logic [31:0] araddr,
output logic [7:0] arlen,
output logic [2:0] arsize,
output logic [1:0] arburst,
output logic rvalid,
output logic rready,
output logic [31:0] rdata,
output logic [1:0] rresp,
output logic rlast,
output logic memory_read,
output logic memory_write,
output logic [14:0] memory_word_addr,
output logic [3:0] memory_write_strobe
// verilog_format: on
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) mem_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
(* anyseq *)logic f_awvalid;
(* anyseq *)logic [31:0] f_awaddr;
(* anyseq *)logic [ 7:0] f_awlen;
(* anyseq *)logic [ 2:0] f_awsize;
(* anyseq *)logic [ 1:0] f_awburst;
(* anyseq *)logic f_awlock;
(* anyseq *)logic f_wvalid;
(* anyseq *)logic [31:0] f_wdata;
(* anyseq *)logic [ 3:0] f_wstrb;
(* anyseq *)logic f_wlast;
(* anyseq *)logic f_bready;
(* anyseq *)logic f_arvalid;
(* anyseq *)logic [31:0] f_araddr;
(* anyseq *)logic [ 7:0] f_arlen;
(* anyseq *)logic [ 2:0] f_arsize;
(* anyseq *)logic [ 1:0] f_arburst;
(* anyseq *)logic f_arlock;
(* anyseq *)logic f_rready;
assign mem_axi4.awid = 1'b0;
assign mem_axi4.awaddr = f_awaddr;
assign mem_axi4.awlen = f_awlen;
assign mem_axi4.awsize = f_awsize;
assign mem_axi4.awburst = f_awburst;
assign mem_axi4.awlock = f_awlock;
assign mem_axi4.awcache = '0;
assign mem_axi4.awprot = '0;
assign mem_axi4.awqos = '0;
assign mem_axi4.awregion = '0;
assign mem_axi4.awuser = '0;
assign mem_axi4.awvalid = f_awvalid;
assign mem_axi4.wdata = f_wdata;
assign mem_axi4.wstrb = f_wstrb;
assign mem_axi4.wlast = f_wlast;
assign mem_axi4.wuser = '0;
assign mem_axi4.wvalid = f_wvalid;
assign mem_axi4.bready = f_bready;
assign mem_axi4.arid = 1'b0;
assign mem_axi4.araddr = f_araddr;
assign mem_axi4.arlen = f_arlen;
assign mem_axi4.arsize = f_arsize;
assign mem_axi4.arburst = f_arburst;
assign mem_axi4.arlock = f_arlock;
assign mem_axi4.arcache = '0;
assign mem_axi4.arprot = '0;
assign mem_axi4.arqos = '0;
assign mem_axi4.arregion = '0;
assign mem_axi4.aruser = '0;
assign mem_axi4.arvalid = f_arvalid;
assign mem_axi4.rready = f_rready;
assign cfg_apb4.paddr = '0;
assign cfg_apb4.pprot = '0;
assign cfg_apb4.psel = 1'b0;
assign cfg_apb4.penable = 1'b0;
assign cfg_apb4.pwrite = 1'b0;
assign cfg_apb4.pwdata = '0;
assign cfg_apb4.pstrb = '0;
assign awvalid = mem_axi4.awvalid;
assign awready = mem_axi4.awready;
assign awaddr = mem_axi4.awaddr;
assign awlen = mem_axi4.awlen;
assign awsize = mem_axi4.awsize;
assign awburst = mem_axi4.awburst;
assign wvalid = mem_axi4.wvalid;
assign wready = mem_axi4.wready;
assign wdata = mem_axi4.wdata;
assign wstrb = mem_axi4.wstrb;
assign wlast = mem_axi4.wlast;
assign bvalid = mem_axi4.bvalid;
assign bready = mem_axi4.bready;
assign bresp = mem_axi4.bresp;
assign arvalid = mem_axi4.arvalid;
assign arready = mem_axi4.arready;
assign araddr = mem_axi4.araddr;
assign arlen = mem_axi4.arlen;
assign arsize = mem_axi4.arsize;
assign arburst = mem_axi4.arburst;
assign rvalid = mem_axi4.rvalid;
assign rready = mem_axi4.rready;
assign rdata = mem_axi4.rdata;
assign rresp = mem_axi4.rresp;
assign rlast = mem_axi4.rlast;
onchip_ram #(
.Present (1'b1),
.CapacityKiB(4)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(1'b1),
.perf_clear_i (1'b0),
.mem_axi4 (mem_axi4),
.cfg_apb4 (cfg_apb4)
);
assign memory_read = u_dut.s_mem_read;
assign memory_write = u_dut.s_mem_write;
assign memory_word_addr = u_dut.s_mem_word_addr;
assign memory_write_strobe = u_dut.s_mem_wstrb;
initial begin
rst_n_i = 1'b0;
f_past_valid = 1'b0;
end
always_ff @(posedge clk_i) begin
rst_n_i <= 1'b1;
f_past_valid <= 1'b1;
end
endmodule
+113
View File
@@ -0,0 +1,113 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module onchip_ram_formal;
(* anyseq *) (* gclk *)reg clk_i;
logic rst_n_i;
logic f_past_valid;
logic awvalid;
logic awready;
logic [31:0] awaddr;
logic [ 7:0] awlen;
logic [ 2:0] awsize;
logic [ 1:0] awburst;
logic wvalid;
logic wready;
logic [31:0] wdata;
logic [ 3:0] wstrb;
logic wlast;
logic bvalid;
logic bready;
logic [ 1:0] bresp;
logic arvalid;
logic arready;
logic [31:0] araddr;
logic [ 7:0] arlen;
logic [ 2:0] arsize;
logic [ 1:0] arburst;
logic rvalid;
logic rready;
logic [31:0] rdata;
logic [ 1:0] rresp;
logic rlast;
logic memory_read;
logic memory_write;
logic [14:0] memory_word_addr;
logic [ 3:0] memory_write_strobe;
logic [ 4:0] s_read_beat_count_q;
logic [ 4:0] s_write_beat_count_q;
onchip_ram_formal_design u_design (.*);
always @(posedge clk_i) begin
if (!rst_n_i) begin
s_read_beat_count_q <= '0;
s_write_beat_count_q <= '0;
end else begin
if (arvalid && arready) s_read_beat_count_q <= '0;
else if (rvalid && rready) s_read_beat_count_q <= s_read_beat_count_q + 1'b1;
if (awvalid && awready) s_write_beat_count_q <= '0;
else if (wvalid && wready) s_write_beat_count_q <= s_write_beat_count_q + 1'b1;
end
if (f_past_valid && $past(rst_n_i)) begin
if ($past(awvalid && !awready)) begin
assume (awvalid);
assume (awaddr == $past(awaddr));
assume (awlen == $past(awlen));
assume (awsize == $past(awsize));
assume (awburst == $past(awburst));
end
if ($past(wvalid && !wready)) begin
assume (wvalid);
assume (wdata == $past(wdata));
assume (wstrb == $past(wstrb));
assume (wlast == $past(wlast));
end
if ($past(arvalid && !arready)) begin
assume (arvalid);
assume (araddr == $past(araddr));
assume (arlen == $past(arlen));
assume (arsize == $past(arsize));
assume (arburst == $past(arburst));
end
if ($past(bvalid && !bready)) begin
assert (bvalid);
assert (bresp == $past(bresp));
end
if ($past(rvalid && !rready)) begin
assert (rvalid);
assert (rdata == $past(rdata));
assert (rresp == $past(rresp));
assert (rlast == $past(rlast));
end
end
if (rst_n_i) begin
assert (!((awvalid && awready) && (arvalid && arready)));
assert (!(memory_read && memory_write));
assert (s_read_beat_count_q <= 5'd16);
assert (s_write_beat_count_q <= 5'd16);
if (memory_write) begin
assert (wvalid && wready);
assert (memory_write_strobe != 4'd0);
end
if (rlast && rvalid) assert (s_read_beat_count_q <= 5'd15);
if (bvalid) assert (s_write_beat_count_q <= 5'd16);
cover (arvalid && arready && (arlen == 8'd3));
cover (rvalid && rready && rlast && (rresp == 2'b00));
cover (awvalid && awready && (awlen == 8'd3));
cover (memory_write && (memory_write_strobe == 4'hF));
cover (bvalid && bready && (bresp == 2'b00));
cover (rvalid && rready && (rresp == 2'b11));
end
end
endmodule
+8
View File
@@ -429,6 +429,14 @@
"timed_interface": "u_usb2_apb4_if",
"pure_interface": "u_usb2_apb4_pure_if",
"region": "APB4_USB2"
},
{
"slot": 21,
"name": "sram",
"timed_interface": "sram_cfg",
"pure_interface": "sram_cfg_pure",
"region": "APB4_SRAM",
"external": true
}
],
"apb4_system_targets": [
+10 -3
View File
@@ -22,7 +22,7 @@ FORMAL_OPIPSRAM_TIMEOUT ?= 300
FORMAL_DMA_TIMEOUT ?= 120
FORMAL_SDIO_TIMEOUT ?= 120
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram opipsram dma sdio
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma sdio
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
@@ -54,6 +54,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/i2s_formal_props.sv \
$(RTL_PATH)/formal/psram_formal.sv \
$(RTL_PATH)/formal/psram_formal_props.sv \
$(RTL_PATH)/formal/onchip_ram_formal.sv \
$(RTL_PATH)/formal/onchip_ram_formal_props.sv \
$(RTL_PATH)/formal/opipsram_formal.sv \
$(RTL_PATH)/formal/opipsram_formal_props.sv \
$(RTL_PATH)/formal/dma_formal.sv \
@@ -115,6 +117,9 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/memory/psram_pkg.sv \
$(ROOT_PATH)/rtl/ip/memory/psram_axi4.sv \
$(ROOT_PATH)/rtl/ip/memory/psram_phy.sv \
$(RTL_PATH)/top/onchip_ram_reg.sv \
$(RTL_PATH)/top/onchip_ram.sv \
$(ROOT_PATH)/rtl/tech/tc_sram.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/register.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/xchecker.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/gray2bin.sv \
@@ -179,7 +184,7 @@ $(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,$*),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 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 \
@@ -277,6 +282,8 @@ formal-i2s: $(FORMAL_DIR)/i2s/.stamp | manifest
formal-psram: $(FORMAL_DIR)/psram/.stamp | manifest
formal-onchip-ram: $(FORMAL_DIR)/onchip_ram/.stamp | manifest
formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
@@ -289,4 +296,4 @@ formal-doctor:
formal-clean:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-opipsram formal-dma formal-sdio formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-sdio formal-doctor formal-clean
+1
View File
@@ -103,6 +103,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/dvp.c \
$(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/psram_math.c \
$(ROOT_PATH)/crt/src/hal/psram.c \
$(ROOT_PATH)/crt/src/hal/opipsram_math.c \
+85 -4
View File
@@ -237,6 +237,15 @@ def render_ports(pads: list[Pad]) -> str:
return "\n".join(lines) + "\n"
def render_core_ports(pads: list[Pad]) -> str:
"""Render the logical-chip port list without technology power pins."""
lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."]
for index, pad in enumerate(pads):
suffix = "," if index != len(pads) - 1 else ""
lines.extend(wrap_feature(pad, f" {pad.direction} {pad.name}{suffix}"))
return "\n".join(lines) + "\n"
def render_pad_instance(pad: Pad) -> str | None:
instance = f"u_{pad.name}"
if pad.kind == "tri_input":
@@ -308,14 +317,18 @@ def render_pad_bindings(pads: list[Pad]) -> str:
f" for (genvar i = 0; i < {count}; i++) begin : {kind}_pads",
' (* keep *) (* dont_touch = "true" *)',
f" sg13g2_IOPad{cell_suffixes[kind]} {kind}_pad (",
"`ifdef USE_POWER_PINS",
" .iovdd(IOVDD), .iovss(IOVSS), .vdd(VDD), .vss(VSS)",
"`endif",
" );",
" end",
]
)
lines.extend([" endgenerate", "`else", "`define RETROSOC_PAD_POWER_CONNECTIONS", "`endif"])
lines.extend(
[
" endgenerate",
"`else",
"`define RETROSOC_PAD_POWER_CONNECTIONS",
"`endif",
]
)
for pad in pads:
line = render_pad_instance(pad)
if line is not None:
@@ -324,6 +337,72 @@ def render_pad_bindings(pads: list[Pad]) -> str:
return "\n".join(lines) + "\n"
def render_core_binding(pad: Pad) -> str | None:
"""Bind a package-facing core port directly to the logical SoC interface."""
if pad.kind in {"tri_input", "schmitt_input"}:
assert pad.signal is not None
return f" assign {pad.signal} = {pad.name};"
if pad.kind in {"tri_output", "peripheral_output"}:
assert pad.signal is not None
return f" assign {pad.name} = {pad.signal};"
if pad.kind == "peripheral_bidir":
assert pad.input_signal is not None
assert pad.output_signal is not None
assert pad.output_enable_signal is not None
return "\n".join(
[
f" assign {pad.name} = {pad.output_enable_signal} ? {pad.output_signal} : 1'bz;",
f" assign {pad.input_signal} = {pad.name};",
]
)
if pad.kind == "full":
assert pad.interface is not None
assert pad.index is not None
return "\n".join(
[
f" assign {pad.name} = {pad.interface}.oe_o[{pad.index}] ? "
f"{pad.interface}.do_o[{pad.index}] : 1'bz;",
f" assign {pad.interface}.di_i[{pad.index}] = {pad.name};",
]
)
if pad.kind == "xpi_data":
assert pad.index is not None
return "\n".join(
[
f" assign {pad.name} = u_xpi_if.io_oe_o[{pad.index}] ? "
f"u_xpi_if.io_do_o[{pad.index}] : 1'bz;",
f" assign u_xpi_if.io_di_i[{pad.index}] = {pad.name};",
]
)
if pad.kind == "sdram_dq":
assert pad.index is not None
return "\n".join(
[
f" assign {pad.name} = u_sdram_if.oe_o ? u_sdram_if.dq_o[{pad.index}] : 1'bz;",
f" assign u_sdram_if.dq_i[{pad.index}] = {pad.name};",
]
)
if pad.kind == "xtal" and pad.bind:
assert pad.signal is not None
assert pad.peer is not None
return "\n".join(
[
f" assign {pad.signal} = {pad.name};",
f" assign {pad.peer} = 1'b0;",
]
)
return None
def render_core_bindings(pads: list[Pad]) -> str:
lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."]
for pad in pads:
binding = render_core_binding(pad)
if binding is not None:
lines.extend(wrap_feature(pad, binding))
return "\n".join(lines) + "\n"
def ihp130_pad_cell(pad: Pad) -> str | None:
if pad.kind in {"tri_input", "schmitt_input"}:
return "sg13g2_IOPadIn"
@@ -359,6 +438,8 @@ def generate(map_path: Path, output_dir: Path) -> None:
rtl_dir = output_dir / "rtl"
atomic_write(rtl_dir / "retrosoc_asic_ports.svh", render_ports(pads))
atomic_write(rtl_dir / "retrosoc_asic_pad_bindings.svh", render_pad_bindings(pads))
atomic_write(rtl_dir / "retrosoc_core_ports.svh", render_core_ports(pads))
atomic_write(rtl_dir / "retrosoc_core_bindings.svh", render_core_bindings(pads))
for profile, bindings in profiles.items():
atomic_write(
rtl_dir / f"retrosoc_asic_{profile}_bindings.svh",
+1
View File
@@ -41,6 +41,7 @@ module apb4_periph (
sysctrl_if.dut sysctrl,
pll_ctrl_if.sysctrl pll_ctrl,
apb4_if.master sdram_cfg,
apb4_if.master sram_cfg,
dvp_if.dut dvp,
sdio_if.dut sdio0,
sdio_if.dut sdio1,
+2 -31
View File
@@ -21,9 +21,7 @@ module axi4_bus (
axi4_if.slave spisd_axi4,
axi4_if.master cfg_axi4,
axi4_if.master system_axi4,
`ifdef HAVE_SRAM_IF
ram_if.master ram,
`endif
axi4_if.master sram_axi4,
axi4_if.master sdram_axi4,
axi4_if.master psram_axi4,
axi4_if.master xpi_axi4,
@@ -76,16 +74,6 @@ module axi4_bus (
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_ram_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_connector u_mgmt_connector (
.source(mgmt_axi4),
.sink (u_master_axi4_if[0])
@@ -133,7 +121,7 @@ module axi4_bus (
axi4_connector u_ram_connector (
.source(u_target_axi4_if[2]),
.sink (u_ram_axi4_if)
.sink (sram_axi4)
);
axi4_connector u_sdram_connector (
@@ -189,23 +177,6 @@ module axi4_bus (
.perf_opipsram_wait_o (perf_opipsram_wait_o)
);
`ifdef HAVE_SRAM_IF
axi42ram u_axi42ram (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (u_ram_axi4_if),
.ram (ram)
);
`else
axi4_error_slave #(
.Response(2'b11)
) u_ram_error_slave (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (u_ram_axi4_if)
);
`endif
axi4_error_slave #(
.Response(2'b11)
) u_decode_error_slave (
+589 -52
View File
@@ -8,64 +8,601 @@
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
// 32x4KB=128KB
module onchip_ram (
// verilog_format: off -- preserve aligned port declarations
input logic clk_i,
ram_if.slave ram
`include "axi4_define.svh"
`include "mmap_define.svh"
module onchip_ram #(
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128
) (
// verilog_format: off -- preserve the AXI, APB, and performance grouping
input logic clk_i,
input logic rst_n_i,
input logic perf_enable_i,
input logic perf_clear_i,
axi4_if.slave mem_axi4,
apb4_if.slave cfg_apb4
// verilog_format: on
);
localparam int unsigned NumBanks = CapacityKiB / 4;
localparam int unsigned BankIndexWidth = (NumBanks > 1) ? $clog2(NumBanks) : 1;
localparam logic [31:0] MemoryEnd = `SOC_ADDR_SRAM_BASE + 32'(CapacityKiB * 1024) - 1'b1;
logic s_cs [31:0];
logic [31:0] s_rdata[0:31];
typedef enum logic [2:0] {
Idle,
ReadData,
ReadError,
WriteData,
WriteDrain,
WriteResponse
} state_e;
// verilog_format: off -- preserve aligned bank decode
assign s_cs[0] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[1] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[2] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[3] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[4] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[5] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[6] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[7] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[8] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[9] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[10] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[11] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[12] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[13] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[14] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[15] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[16] = ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[17] = ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[18] = ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[19] = ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[20] = ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[21] = ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[22] = ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[23] = ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[24] = ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[25] = ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[26] = ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[27] = ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
assign s_cs[28] = ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
assign s_cs[29] = ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
assign s_cs[30] = ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
assign s_cs[31] = ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
state_e s_state_d;
state_e s_state_q;
logic [2:0] s_state_bits_q;
logic s_id_d, s_id_q;
logic [7:0] s_len_d, s_len_q;
logic [7:0] s_beat_d, s_beat_q;
logic [2:0] s_size_d, s_size_q;
logic [1:0] s_burst_d, s_burst_q;
logic [31:0] s_addr_d, s_addr_q;
logic [1:0] s_write_resp_d, s_write_resp_q;
logic s_read_pending_d, s_read_pending_q;
logic [7:0] s_read_rsp_beat_d, s_read_rsp_beat_q;
logic s_read_rsp_last_d, s_read_rsp_last_q;
logic [1:0] s_read_rsp_d, s_read_rsp_q;
logic s_read_issue_valid_d, s_read_issue_valid_q;
logic [31:0] s_read_issue_addr_d, s_read_issue_addr_q;
logic [7:0] s_read_issue_beat_d, s_read_issue_beat_q;
assign ram.rdata = s_rdata[ram.addr[14:10]];
// verilog_format: on
logic [31:0] s_perf_read_requests_d, s_perf_read_requests_q;
logic [31:0] s_perf_write_requests_d, s_perf_write_requests_q;
logic [31:0] s_perf_read_beats_d, s_perf_read_beats_q;
logic [31:0] s_perf_write_beats_d, s_perf_write_beats_q;
logic [31:0] s_perf_stall_cycles_d, s_perf_stall_cycles_q;
logic [31:0] s_perf_err_resps_d, s_perf_err_resps_q;
for (genvar i = 0; i < 32; ++i) begin : gen_sram_block
tc_sram_1024x32 u_ram (
.clk_i (clk_i),
.cs_i (s_cs[i]),
.addr_i(ram.addr[9:0]),
.data_i(ram.wdata),
.mask_i(ram.wstrb),
.wren_i(|ram.wstrb),
.data_o(s_rdata[i])
);
logic [32:0] s_ar_last_addr;
logic [32:0] s_aw_last_addr;
logic [31:0] s_ar_next_addr;
logic [31:0] s_read_next_addr;
logic [31:0] s_write_next_addr;
logic s_ar_protocol_legal;
logic s_aw_protocol_legal;
logic s_ar_bounds_legal;
logic s_aw_bounds_legal;
logic [ 1:0] s_ar_response;
logic [ 1:0] s_aw_response;
logic s_ar_accept;
logic s_aw_accept;
logic s_w_accept;
logic s_read_accept;
logic s_b_accept;
logic s_read_issue;
logic s_read_can_issue;
logic s_expected_wlast;
logic [ 3:0] s_write_lane_mask;
logic s_write_strobe_legal;
logic s_write_beat_legal;
logic s_write_terminal;
logic s_err_resp_event;
logic s_stall_event;
logic s_mem_read;
logic s_mem_write;
logic [14:0] s_mem_word_addr;
logic [31:0] s_mem_wdata;
logic [ 3:0] s_mem_wstrb;
logic [31:0] s_mem_rdata;
function automatic logic wrap_length_legal(input logic [7:0] length_i);
return (length_i == 8'd1) || (length_i == 8'd3) || (length_i == 8'd7) || (length_i == 8'd15);
endfunction
function automatic logic [32:0] burst_last_addr(
input logic [31:0] addr_i, input logic [7:0] length_i, input logic [2:0] size_i,
input logic [1:0] burst_i);
logic [32:0] beat_bytes;
logic [32:0] burst_bytes;
logic [32:0] wrap_base;
begin
beat_bytes = 33'd1 << size_i;
burst_bytes = beat_bytes * ({25'd0, length_i} + 1'b1);
wrap_base = {1'b0, addr_i} & ~(burst_bytes - 1'b1);
unique case (burst_i)
`AXI4_BURST_TYPE_FIXED: burst_last_addr = {1'b0, addr_i} + beat_bytes - 1'b1;
`AXI4_BURST_TYPE_WRAP: burst_last_addr = wrap_base + burst_bytes - 1'b1;
default: burst_last_addr = {1'b0, addr_i} + burst_bytes - 1'b1;
endcase
end
endfunction
function automatic logic [3:0] transfer_lane_mask(input logic [2:0] size_i,
input logic [1:0] address_i);
unique case (size_i)
3'd0: return 4'b0001 << address_i;
3'd1: return 4'b0011 << address_i;
default: return 4'b1111;
endcase
endfunction
function automatic logic [31:0] saturating_increment(input logic [31:0] value_i);
return (&value_i) ? value_i : value_i + 1'b1;
endfunction
assign s_state_q = state_e'(s_state_bits_q);
assign s_ar_last_addr = burst_last_addr(
mem_axi4.araddr, mem_axi4.arlen, mem_axi4.arsize, mem_axi4.arburst
);
assign s_aw_last_addr = burst_last_addr(
mem_axi4.awaddr, mem_axi4.awlen, mem_axi4.awsize, mem_axi4.awburst
);
assign s_ar_protocol_legal =
(mem_axi4.arlen <= 8'd15) &&
((mem_axi4.arburst == `AXI4_BURST_TYPE_FIXED) ||
(mem_axi4.arburst == `AXI4_BURST_TYPE_INCR) ||
((mem_axi4.arburst == `AXI4_BURST_TYPE_WRAP) && wrap_length_legal(
mem_axi4.arlen
))) && !mem_axi4.arlock && (mem_axi4.arsize <= `AXI4_BURST_SIZE_4BYTES) &&
((mem_axi4.araddr & ((32'd1 << mem_axi4.arsize) - 1'b1)) == 32'd0) && !s_ar_last_addr[32] &&
(mem_axi4.araddr[31:12] == s_ar_last_addr[31:12]);
assign s_aw_protocol_legal =
(mem_axi4.awlen <= 8'd15) &&
((mem_axi4.awburst == `AXI4_BURST_TYPE_FIXED) ||
(mem_axi4.awburst == `AXI4_BURST_TYPE_INCR) ||
((mem_axi4.awburst == `AXI4_BURST_TYPE_WRAP) && wrap_length_legal(
mem_axi4.awlen
))) && !mem_axi4.awlock && (mem_axi4.awsize <= `AXI4_BURST_SIZE_4BYTES) &&
((mem_axi4.awaddr & ((32'd1 << mem_axi4.awsize) - 1'b1)) == 32'd0) && !s_aw_last_addr[32] &&
(mem_axi4.awaddr[31:12] == s_aw_last_addr[31:12]);
assign s_ar_bounds_legal = Present && (mem_axi4.araddr >= `SOC_ADDR_SRAM_BASE) &&
(s_ar_last_addr[31:0] <= MemoryEnd);
assign s_aw_bounds_legal = Present && (mem_axi4.awaddr >= `SOC_ADDR_SRAM_BASE) &&
(s_aw_last_addr[31:0] <= MemoryEnd);
assign s_ar_response = !s_ar_protocol_legal ? `AXI4_RESP_SLAVE_ERROR :
!s_ar_bounds_legal ? `AXI4_RESP_DECODE_ERROR : `AXI4_RESP_OKAY;
assign s_aw_response = !s_aw_protocol_legal ? `AXI4_RESP_SLAVE_ERROR :
!s_aw_bounds_legal ? `AXI4_RESP_DECODE_ERROR : `AXI4_RESP_OKAY;
assign mem_axi4.arready = s_state_q == Idle;
assign mem_axi4.awready = (s_state_q == Idle) && !mem_axi4.arvalid;
assign mem_axi4.wready = (s_state_q == WriteData) || (s_state_q == WriteDrain);
assign mem_axi4.rid = s_id_q;
assign mem_axi4.rdata = (s_read_rsp_q == `AXI4_RESP_OKAY) ? s_mem_rdata : 32'd0;
assign mem_axi4.rresp = s_read_rsp_q;
assign mem_axi4.rlast = s_read_rsp_last_q;
assign mem_axi4.ruser = '0;
assign mem_axi4.rvalid = s_read_pending_q;
assign mem_axi4.bid = s_id_q;
assign mem_axi4.bresp = s_write_resp_q;
assign mem_axi4.buser = '0;
assign mem_axi4.bvalid = s_state_q == WriteResponse;
assign s_ar_accept = mem_axi4.arvalid && mem_axi4.arready;
assign s_aw_accept = mem_axi4.awvalid && mem_axi4.awready;
assign s_w_accept = mem_axi4.wvalid && mem_axi4.wready;
assign s_read_accept = mem_axi4.rvalid && mem_axi4.rready;
assign s_b_accept = mem_axi4.bvalid && mem_axi4.bready;
assign s_read_can_issue = !s_read_pending_q || mem_axi4.rready;
assign s_read_issue = (s_state_q == ReadData) && s_read_issue_valid_q && s_read_can_issue;
assign s_expected_wlast = s_beat_q == s_len_q;
assign s_write_lane_mask = transfer_lane_mask(s_size_q, s_addr_q[1:0]);
assign s_write_strobe_legal = (mem_axi4.wstrb & ~s_write_lane_mask) == 4'd0;
assign s_write_beat_legal = s_write_strobe_legal && (mem_axi4.wlast == s_expected_wlast);
assign s_write_terminal = s_expected_wlast || mem_axi4.wlast;
assign s_mem_read = (s_ar_accept && (s_ar_response == `AXI4_RESP_OKAY)) || s_read_issue;
assign s_mem_write = (s_state_q == WriteData) && s_w_accept && s_write_beat_legal &&
(|mem_axi4.wstrb);
assign s_mem_word_addr =
(s_ar_accept && (s_ar_response == `AXI4_RESP_OKAY)) ? mem_axi4.araddr[16:2] :
s_read_issue ? s_read_issue_addr_q[16:2] : s_addr_q[16:2];
assign s_mem_wdata = mem_axi4.wdata;
assign s_mem_wstrb = s_mem_write ? mem_axi4.wstrb : 4'd0;
assign s_err_resp_event =
(s_b_accept && (mem_axi4.bresp != `AXI4_RESP_OKAY)) ||
(s_read_accept && mem_axi4.rlast && (mem_axi4.rresp != `AXI4_RESP_OKAY));
assign s_stall_event = (mem_axi4.arvalid && !mem_axi4.arready) ||
(mem_axi4.awvalid && !mem_axi4.awready) ||
(mem_axi4.wvalid && !mem_axi4.wready) ||
(mem_axi4.rvalid && !mem_axi4.rready) ||
(mem_axi4.bvalid && !mem_axi4.bready);
axi4_addr_gen #(
.ADDR_WIDTH(32)
) u_ar_addr_gen (
.alen_i (mem_axi4.arlen),
.asize_i (mem_axi4.arsize),
.aburst_i(mem_axi4.arburst),
.addr_i (mem_axi4.araddr),
.addr_o (s_ar_next_addr)
);
axi4_addr_gen #(
.ADDR_WIDTH(32)
) u_read_addr_gen (
.alen_i (s_len_q),
.asize_i (s_size_q),
.aburst_i(s_burst_q),
.addr_i (s_read_issue_addr_q),
.addr_o (s_read_next_addr)
);
axi4_addr_gen #(
.ADDR_WIDTH(32)
) u_write_addr_gen (
.alen_i (s_len_q),
.asize_i (s_size_q),
.aburst_i(s_burst_q),
.addr_i (s_addr_q),
.addr_o (s_write_next_addr)
);
always_comb begin
s_state_d = s_state_q;
s_id_d = s_id_q;
s_len_d = s_len_q;
s_beat_d = s_beat_q;
s_size_d = s_size_q;
s_burst_d = s_burst_q;
s_addr_d = s_addr_q;
s_write_resp_d = s_write_resp_q;
s_read_pending_d = s_read_pending_q;
s_read_rsp_beat_d = s_read_rsp_beat_q;
s_read_rsp_last_d = s_read_rsp_last_q;
s_read_rsp_d = s_read_rsp_q;
s_read_issue_valid_d = s_read_issue_valid_q;
s_read_issue_addr_d = s_read_issue_addr_q;
s_read_issue_beat_d = s_read_issue_beat_q;
if (s_read_accept) begin
s_read_pending_d = 1'b0;
if (s_read_rsp_last_q) begin
s_state_d = Idle;
end
end
if (s_ar_accept) begin
s_state_d = (s_ar_response == `AXI4_RESP_OKAY) ? ReadData : ReadError;
s_id_d = mem_axi4.arid;
s_len_d = s_ar_protocol_legal ? mem_axi4.arlen : 8'd0;
s_size_d = mem_axi4.arsize;
s_burst_d = mem_axi4.arburst;
s_read_pending_d = 1'b1;
s_read_rsp_beat_d = 8'd0;
s_read_rsp_last_d = !s_ar_protocol_legal || (mem_axi4.arlen == 8'd0);
s_read_rsp_d = s_ar_response;
s_read_issue_valid_d = (s_ar_response == `AXI4_RESP_OKAY) && (mem_axi4.arlen != 8'd0);
s_read_issue_addr_d = s_ar_next_addr;
s_read_issue_beat_d = 8'd1;
end else if (s_aw_accept) begin
s_state_d = (s_aw_response == `AXI4_RESP_OKAY) ? WriteData : WriteDrain;
s_id_d = mem_axi4.awid;
s_len_d = s_aw_protocol_legal ? mem_axi4.awlen : 8'd0;
s_beat_d = 8'd0;
s_size_d = mem_axi4.awsize;
s_burst_d = mem_axi4.awburst;
s_addr_d = mem_axi4.awaddr;
s_write_resp_d = s_aw_response;
end
if (s_read_issue) begin
s_read_pending_d = 1'b1;
s_read_rsp_beat_d = s_read_issue_beat_q;
s_read_rsp_last_d = s_read_issue_beat_q == s_len_q;
s_read_rsp_d = `AXI4_RESP_OKAY;
if (s_read_issue_beat_q == s_len_q) begin
s_read_issue_valid_d = 1'b0;
end else begin
s_read_issue_addr_d = s_read_next_addr;
s_read_issue_beat_d = s_read_issue_beat_q + 1'b1;
end
end
if ((s_state_q == ReadError) && s_read_accept && !s_read_rsp_last_q) begin
s_read_pending_d = 1'b1;
s_read_rsp_beat_d = s_read_rsp_beat_q + 1'b1;
s_read_rsp_last_d = (s_read_rsp_beat_q + 1'b1) == s_len_q;
end
if (s_w_accept) begin
if ((s_state_q == WriteData) && !s_write_beat_legal) begin
s_write_resp_d = `AXI4_RESP_SLAVE_ERROR;
end
if (s_write_terminal) begin
s_state_d = WriteResponse;
end else begin
s_beat_d = s_beat_q + 1'b1;
s_addr_d = s_write_next_addr;
end
end
if (s_b_accept) begin
s_state_d = Idle;
end
end
always_comb begin
s_perf_read_requests_d = s_perf_read_requests_q;
s_perf_write_requests_d = s_perf_write_requests_q;
s_perf_read_beats_d = s_perf_read_beats_q;
s_perf_write_beats_d = s_perf_write_beats_q;
s_perf_stall_cycles_d = s_perf_stall_cycles_q;
s_perf_err_resps_d = s_perf_err_resps_q;
if (perf_clear_i) begin
s_perf_read_requests_d = '0;
s_perf_write_requests_d = '0;
s_perf_read_beats_d = '0;
s_perf_write_beats_d = '0;
s_perf_stall_cycles_d = '0;
s_perf_err_resps_d = '0;
end else if (perf_enable_i) begin
if (s_ar_accept) begin
s_perf_read_requests_d = saturating_increment(s_perf_read_requests_q);
end
if (s_aw_accept) begin
s_perf_write_requests_d = saturating_increment(s_perf_write_requests_q);
end
if (s_read_accept) begin
s_perf_read_beats_d = saturating_increment(s_perf_read_beats_q);
end
if (s_w_accept) begin
s_perf_write_beats_d = saturating_increment(s_perf_write_beats_q);
end
if (s_stall_event) begin
s_perf_stall_cycles_d = saturating_increment(s_perf_stall_cycles_q);
end
if (s_err_resp_event) begin
s_perf_err_resps_d = saturating_increment(s_perf_err_resps_q);
end
end
end
if (Present) begin : gen_memory
logic [ NumBanks-1:0] s_bank_cs;
logic [ 31:0] s_bank_rdata [NumBanks];
logic [BankIndexWidth-1:0] s_read_bank_q;
for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_bank
assign s_bank_cs[bank] = (s_mem_read || s_mem_write) && (s_mem_word_addr[14:10] == 5'(bank));
tc_sram_1024x32 u_ram (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[9:0]),
.data_i(s_mem_wdata),
.mask_i(s_mem_wstrb),
.wren_i(s_mem_write),
.data_o(s_bank_rdata[bank])
);
end
dffer #(
.DATA_WIDTH(BankIndexWidth)
) u_read_bank_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_mem_read),
.dat_i (s_mem_word_addr[BankIndexWidth+9:10]),
.dat_o (s_read_bank_q)
);
assign s_mem_rdata = s_bank_rdata[s_read_bank_q];
`ifdef HAVE_SVA
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(s_bank_cs));
`endif
end else begin : gen_no_memory
assign s_mem_rdata = {32{1'b0 & ^{s_mem_read, s_mem_write, s_mem_word_addr,
s_mem_wdata, s_mem_wstrb}}};
end
onchip_ram_reg #(
.Present (Present),
.CapacityKiB(CapacityKiB)
) u_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.apb4 (cfg_apb4),
.perf_read_requests_i (s_perf_read_requests_q),
.perf_write_requests_i (s_perf_write_requests_q),
.perf_read_beats_i (s_perf_read_beats_q),
.perf_write_beats_i (s_perf_write_beats_q),
.perf_stall_cycles_i (s_perf_stall_cycles_q),
.perf_error_responses_i(s_perf_err_resps_q)
);
dffr #(
.DATA_WIDTH(3)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(1)
) u_id_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_id_d),
.dat_o (s_id_q)
);
dffr #(
.DATA_WIDTH(8)
) u_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_len_d),
.dat_o (s_len_q)
);
dffr #(
.DATA_WIDTH(8)
) u_beat_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_beat_d),
.dat_o (s_beat_q)
);
dffr #(
.DATA_WIDTH(3)
) u_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_size_d),
.dat_o (s_size_q)
);
dffr #(
.DATA_WIDTH(2)
) u_burst_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_burst_d),
.dat_o (s_burst_q)
);
dffr #(
.DATA_WIDTH(32)
) u_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_addr_d),
.dat_o (s_addr_q)
);
dffr #(
.DATA_WIDTH(2)
) u_write_resp_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_resp_d),
.dat_o (s_write_resp_q)
);
dffr #(
.DATA_WIDTH(1)
) u_read_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_pending_d),
.dat_o (s_read_pending_q)
);
dffr #(
.DATA_WIDTH(8)
) u_read_rsp_beat_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_rsp_beat_d),
.dat_o (s_read_rsp_beat_q)
);
dffr #(
.DATA_WIDTH(1)
) u_read_rsp_last_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_rsp_last_d),
.dat_o (s_read_rsp_last_q)
);
dffr #(
.DATA_WIDTH(2)
) u_read_rsp_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_rsp_d),
.dat_o (s_read_rsp_q)
);
dffr #(
.DATA_WIDTH(1)
) u_read_issue_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_issue_valid_d),
.dat_o (s_read_issue_valid_q)
);
dffr #(
.DATA_WIDTH(32)
) u_read_issue_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_issue_addr_d),
.dat_o (s_read_issue_addr_q)
);
dffr #(
.DATA_WIDTH(8)
) u_read_issue_beat_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_issue_beat_d),
.dat_o (s_read_issue_beat_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_read_requests_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_read_requests_d),
.dat_o (s_perf_read_requests_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_write_requests_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_write_requests_d),
.dat_o (s_perf_write_requests_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_read_beats_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_read_beats_d),
.dat_o (s_perf_read_beats_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_write_beats_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_write_beats_d),
.dat_o (s_perf_write_beats_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_stall_cycles_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_stall_cycles_d),
.dat_o (s_perf_stall_cycles_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_err_resps_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_err_resps_d),
.dat_o (s_perf_err_resps_q)
);
`ifdef HAVE_SVA
assert property (@(posedge clk_i) disable iff (!rst_n_i)
mem_axi4.rvalid && !mem_axi4.rready |=>
$stable(
{mem_axi4.rid, mem_axi4.rdata, mem_axi4.rresp, mem_axi4.rlast}
));
assert property (@(posedge clk_i) disable iff (!rst_n_i)
mem_axi4.bvalid && !mem_axi4.bready |=>
$stable(
{mem_axi4.bid, mem_axi4.bresp}
));
assert property (@(posedge clk_i) disable iff (!rst_n_i) !(s_mem_read && s_mem_write));
`endif
`ifndef SYNTHESIS
initial begin
if ((CapacityKiB != 4) && (CapacityKiB != 16) && (CapacityKiB != 32) &&
(CapacityKiB != 64) && (CapacityKiB != 128)) begin
$fatal(1, "onchip_ram: CapacityKiB must be 4, 16, 32, 64, or 128");
end
end
`endif
endmodule
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`ifndef RETROSOC_ONCHIP_RAM_DEFINE_SVH
`define RETROSOC_ONCHIP_RAM_DEFINE_SVH
// verilog_format: off -- preserve the handwritten register ABI alignment
`define APB4_ONCHIP_RAM__IP_ID 12'h000
`define APB4_ONCHIP_RAM__IP_VERSION 12'h004
`define APB4_ONCHIP_RAM__CAPABILITY 12'h008
`define APB4_ONCHIP_RAM__MEMORY_BYTES 12'h00C
`define APB4_ONCHIP_RAM__BANK_COUNT 12'h010
`define APB4_ONCHIP_RAM__BANK_BYTES 12'h014
`define APB4_ONCHIP_RAM__PERF_READ_REQUESTS 12'h020
`define APB4_ONCHIP_RAM__PERF_WRITE_REQUESTS 12'h024
`define APB4_ONCHIP_RAM__PERF_READ_BEATS 12'h028
`define APB4_ONCHIP_RAM__PERF_WRITE_BEATS 12'h02C
`define APB4_ONCHIP_RAM__PERF_STALL_CYCLES 12'h030
`define APB4_ONCHIP_RAM__PERF_ERROR_RESPONSES 12'h034
`define APB4_ONCHIP_RAM__CAP_PRESENT 0
`define APB4_ONCHIP_RAM__CAP_NATIVE_AXI4 1
`define APB4_ONCHIP_RAM__CAP_BYTE_WRITE 2
`define APB4_ONCHIP_RAM__CAP_FIXED 3
`define APB4_ONCHIP_RAM__CAP_INCR 4
`define APB4_ONCHIP_RAM__CAP_WRAP 5
`define APB4_ONCHIP_RAM__CAP_PERFORMANCE 6
`define APB4_ONCHIP_RAM__CAP_MAX_BEATS_LSB 8
`define APB4_ONCHIP_RAM__CAP_DATA_BYTES_LSB 16
// verilog_format: on
`endif
+108
View File
@@ -0,0 +1,108 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`include "onchip_ram_define.svh"
module onchip_ram_reg #(
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128
) (
// verilog_format: off -- preserve the register input grouping
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
input logic [31:0] perf_read_requests_i,
input logic [31:0] perf_write_requests_i,
input logic [31:0] perf_read_beats_i,
input logic [31:0] perf_write_beats_i,
input logic [31:0] perf_stall_cycles_i,
input logic [31:0] perf_error_responses_i
// verilog_format: on
);
localparam logic [31:0] IpId = 32'h5352_414D;
localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam int unsigned BankBytes = 4096;
localparam int unsigned BankCount = CapacityKiB / 4;
localparam logic [31:0] Capability =
32'(4 << `APB4_ONCHIP_RAM__CAP_DATA_BYTES_LSB) |
32'(16 << `APB4_ONCHIP_RAM__CAP_MAX_BEATS_LSB) |
32'h0000_007E | 32'(Present);
logic s_req;
logic s_aligned;
logic s_offset_valid;
logic s_access_err;
logic s_ready_d;
logic s_ready_q;
logic s_resp_err_d;
logic s_resp_err_q;
logic [31:0] s_rdata_d;
logic [31:0] s_rdata_q;
assign s_req = apb4.psel && apb4.penable && !s_ready_q;
assign s_aligned = apb4.paddr[1:0] == 2'b00;
assign s_access_err = !s_aligned || apb4.pwrite || !s_offset_valid;
assign s_ready_d = s_req;
assign s_resp_err_d = s_req && s_access_err;
assign apb4.pready = s_ready_q;
assign apb4.prdata = s_rdata_q;
assign apb4.pslverr = s_resp_err_q;
always_comb begin
s_offset_valid = 1'b1;
s_rdata_d = 32'd0;
unique case (apb4.paddr[11:0])
`APB4_ONCHIP_RAM__IP_ID: s_rdata_d = IpId;
`APB4_ONCHIP_RAM__IP_VERSION: s_rdata_d = IpVersion;
`APB4_ONCHIP_RAM__CAPABILITY: s_rdata_d = Capability;
`APB4_ONCHIP_RAM__MEMORY_BYTES: s_rdata_d = Present ? 32'(CapacityKiB * 1024) : 32'd0;
`APB4_ONCHIP_RAM__BANK_COUNT: s_rdata_d = Present ? 32'(BankCount) : 32'd0;
`APB4_ONCHIP_RAM__BANK_BYTES: s_rdata_d = 32'(BankBytes);
`APB4_ONCHIP_RAM__PERF_READ_REQUESTS: s_rdata_d = perf_read_requests_i;
`APB4_ONCHIP_RAM__PERF_WRITE_REQUESTS: s_rdata_d = perf_write_requests_i;
`APB4_ONCHIP_RAM__PERF_READ_BEATS: s_rdata_d = perf_read_beats_i;
`APB4_ONCHIP_RAM__PERF_WRITE_BEATS: s_rdata_d = perf_write_beats_i;
`APB4_ONCHIP_RAM__PERF_STALL_CYCLES: s_rdata_d = perf_stall_cycles_i;
`APB4_ONCHIP_RAM__PERF_ERROR_RESPONSES: s_rdata_d = perf_error_responses_i;
default: begin
s_offset_valid = 1'b0;
s_rdata_d = 32'd0;
end
endcase
end
dffr #(
.DATA_WIDTH(1)
) u_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ready_d),
.dat_o (s_ready_q)
);
dffr #(
.DATA_WIDTH(1)
) u_resp_err_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_resp_err_d),
.dat_o (s_resp_err_q)
);
dffer #(
.DATA_WIDTH(32)
) u_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_req),
.dat_i (s_rdata_d),
.dat_o (s_rdata_q)
);
endmodule
+23 -6
View File
@@ -22,9 +22,6 @@ module retrosoc (
input logic clkdiv4_i,
input logic timebase_tick_i,
pll_ctrl_if.sysctrl pll_ctrl,
`ifdef HAVE_SRAM_IF
ram_if.master ram,
`endif
gpio_if.soc_pad gpio,
input logic uart_rx_i,
output logic uart_tx_o,
@@ -48,6 +45,9 @@ module retrosoc (
// Generated fabric links use the common 32-bit AXI4 contract.
`include "soc_fabric_interfaces.svh"
apb4_if u_sdram_cfg_if (.pclk(clk_i), .presetn(rst_n_i));
apb4_if u_sram_cfg_if (.pclk(clk_i), .presetn(rst_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_sram_axi4_if (.aclk(clk_i), .aresetn(rst_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_sdram_axi4_if (.aclk(clk_i), .aresetn(rst_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
@@ -104,6 +104,12 @@ module retrosoc (
logic [ `SOC_IRQ_VECTOR_WIDTH-1:0] s_user_irq;
logic s_rtc_wake;
`ifdef HAVE_SRAM_IF
localparam bit SramPresent = 1'b1;
`else
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;
@@ -162,12 +168,22 @@ core_wrapper u_core_wrapper (
.core_reset_i(u_sysctrl_if.core_reset_o)
);
onchip_ram #(
.Present (SramPresent),
.CapacityKiB(`SOC_ADDR_SRAM_SIZE >> 10)
) u_onchip_ram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(s_perf_en),
.perf_clear_i (s_perf_clear),
.mem_axi4 (u_sram_axi4_if),
.cfg_apb4 (u_sram_cfg_if)
);
axi4_bus u_bus (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
`ifdef HAVE_SRAM_IF
.ram (ram),
`endif
.sram_axi4 (u_sram_axi4_if),
.user_bus_enable_i (u_sysctrl_if.user_bus_enable_o),
.user_bus_idle_o (u_sysctrl_if.user_bus_idle_i),
`include "soc_bus_fabric.svh"
@@ -224,6 +240,7 @@ core_wrapper u_core_wrapper (
.sysctrl (u_sysctrl_if),
.pll_ctrl (pll_ctrl),
.sdram_cfg (u_sdram_cfg_if),
.sram_cfg (u_sram_cfg_if),
.dvp (u_dvp_if),
.sdio0 (u_sdio0_if),
.sdio1 (sdio1),
-14
View File
@@ -82,10 +82,6 @@ module retrosoc_asic (
(* keep = "true" *)logic s_test_pass;
(* keep = "true" *)logic [7:0] s_test_code;
`ifdef HAVE_SRAM_IF
ram_if u_ram_if ();
`endif
gpio_if u_gpio_if ();
xpi_if u_xpi_if ();
sdram_if u_sdram_if ();
@@ -115,13 +111,6 @@ rcu #(
.timebase_tick_o(s_timebase_tick)
);
`ifdef HAVE_SRAM_IF
onchip_ram u_onchip_ram (
.clk_i(s_sys_clk),
.ram (u_ram_if)
);
`endif
retrosoc u_retrosoc (
.clk_i (s_sys_clk),
.rst_n_i (s_sys_rst_n),
@@ -131,9 +120,6 @@ rcu #(
.clkdiv4_i (s_sys_clkdiv4),
.timebase_tick_i(s_timebase_tick),
.pll_ctrl (u_pll_ctrl_if),
`ifdef HAVE_SRAM_IF
.ram (u_ram_if),
`endif
.gpio (u_gpio_if),
.uart_rx_i (s_uart0_rx),
.uart_tx_o (s_uart0_tx),
+100
View File
@@ -0,0 +1,100 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
`include "gpio_define.svh"
`include "soc_clock_config.svh"
`include "user_extensions.svh"
// Technology-independent hardening top. The generated bindings retain the
// exact logical pin contract of retrosoc_asic but intentionally omit pad cells.
module retrosoc_core (
`include "retrosoc_core_ports.svh"
);
logic s_ext_clk;
logic s_aud_clk;
logic s_aud_clk_buf;
logic s_sys_clkdiv4;
logic s_timebase_tick;
`ifdef HAVE_PLL
logic s_xtal_io;
`endif
logic s_sys_clk;
logic s_ext_rst_n;
logic s_sys_rst_n;
logic s_aud_rst_n;
logic s_wdg_reset_req;
logic s_jtag_tck;
logic s_jtag_tms;
logic s_jtag_tdi;
logic s_jtag_trst_n;
logic s_jtag_tdo;
logic s_uart0_rx;
logic s_uart0_tx;
logic s_usb2_ulpi_clk;
(* keep = "true" *)logic s_test_done;
(* keep = "true" *)logic s_test_pass;
(* keep = "true" *)logic [7:0] s_test_code;
gpio_if u_gpio_if ();
xpi_if u_xpi_if ();
sdram_if u_sdram_if ();
sdio_if u_sdio1_if ();
usb2_ulpi_if u_usb2_ulpi_if ();
pll_ctrl_if u_pll_ctrl_if ();
`include "retrosoc_core_bindings.svh"
rcu #(
.ExtClkHz (`SOC_EXT_CLK_HZ),
.ClintTimebaseHz(`SOC_CLINT_TIMEBASE_HZ)
) u_rcu (
.ext_clk_i (s_ext_clk),
.aud_clk_i (s_aud_clk),
.ext_rst_n_i (s_ext_rst_n),
.wdg_reset_req_i(s_wdg_reset_req),
`ifdef HAVE_PLL
.xtal_clk_i (s_xtal_io),
`endif
.pll_ctrl (u_pll_ctrl_if),
.sys_clk_o (s_sys_clk),
.sys_rst_n_o (s_sys_rst_n),
.aud_clk_o (s_aud_clk_buf),
.aud_rst_n_o (s_aud_rst_n),
.sys_clkdiv4_o (s_sys_clkdiv4),
.timebase_tick_o(s_timebase_tick)
);
retrosoc u_retrosoc (
.clk_i (s_sys_clk),
.rst_n_i (s_sys_rst_n),
.clk_aud_i (s_aud_clk_buf),
.rst_aud_n_i (s_aud_rst_n),
.clk_ulpi_i (s_usb2_ulpi_clk),
.clkdiv4_i (s_sys_clkdiv4),
.timebase_tick_i(s_timebase_tick),
.pll_ctrl (u_pll_ctrl_if),
.gpio (u_gpio_if),
.uart_rx_i (s_uart0_rx),
.uart_tx_o (s_uart0_tx),
.xpi (u_xpi_if),
.sdram (u_sdram_if),
.sdio1 (u_sdio1_if),
.usb2 (u_usb2_ulpi_if),
.jtag_tck_i (s_jtag_tck),
.jtag_tms_i (s_jtag_tms),
.jtag_tdi_i (s_jtag_tdi),
.jtag_trst_n_i (s_jtag_trst_n),
.jtag_tdo_o (s_jtag_tdo),
.wdg_reset_req_o(s_wdg_reset_req),
.test_done_o (s_test_done),
.test_pass_o (s_test_pass),
.test_code_o (s_test_code)
);
endmodule
+7 -3
View File
@@ -285,11 +285,14 @@
"mgmt_core_wrapper.sv",
"mgmt_debug_reset.sv",
"mgmt_debug_wrapper.sv",
"onchip_ram_define.svh",
"onchip_ram_reg.sv",
"onchip_ram.sv",
"pll_rcu_controller.sv",
"rcu.sv",
"retrosoc.sv",
"retrosoc_asic.sv",
"retrosoc_core.sv",
"rib2apb.sv",
"rib2axi4.sv",
"rib2ram.sv",
@@ -360,10 +363,11 @@
},
{
"paths": [
"rtl/mini/top/onchip_ram.sv"
"rtl/mini/top/onchip_ram.sv",
"rtl/mini/top/onchip_ram_reg.sv"
],
"result": "technology boundary retained",
"rationale": "PDK SRAM cell and pin names remain compatible with the technology library."
"result": "reviewed AXI and technology-memory boundary",
"rationale": "AXI and APB state uses Common registers; the synchronous bank array retains the stable technology wrapper and PDK SRAM pin boundary."
},
{
"paths": [
+4 -6
View File
@@ -38,17 +38,15 @@ module tc_sram_1024x32 (
end
`elsif PDK_IHP130
`ifdef HAVE_SRAM_MACRO
logic [63:0] s_rd_data_mem;
assign data_o = s_rd_data_mem[31:0];
RM_IHPSG13_1P_1024x64_c2_bm_bist u_mem (
RM_IHPSG13_1P_1024x32_c2_bm_bist u_mem (
.A_CLK (clk_i),
.A_ADDR (addr_i),
.A_BM ({32'h0, {8{mask_i[3]}}, {8{mask_i[2]}}, {8{mask_i[1]}}, {8{mask_i[0]}}}),
.A_BM ({{8{mask_i[3]}}, {8{mask_i[2]}}, {8{mask_i[1]}}, {8{mask_i[0]}}}),
.A_MEN (cs_i),
.A_WEN (wren_i),
.A_REN (~wren_i),
.A_DIN ({32'h0, data_i[31:0]}),
.A_DOUT (s_rd_data_mem),
.A_DIN (data_i),
.A_DOUT (data_o),
.A_DLY (1'b1),
.A_BIST_CLK ('0),
.A_BIST_EN ('0),
+26
View File
@@ -0,0 +1,26 @@
// Functional test model for the site-supplied ICS55 1024 x 32 SRAM macro.
module S55NLLG1PH_X256Y4D32_BW (
input logic [31:0] D,
input logic CLK,
input logic CEN,
input logic WEN,
input logic [31:0] BWEN,
input logic [ 9:0] A,
output logic [31:0] Q
);
logic [31:0] s_memory[0:1023];
always_ff @(posedge CLK) begin
if (!CEN) begin
if (!WEN) begin
for (int bit_index = 0; bit_index < 32; bit_index++) begin
if (!BWEN[bit_index]) begin
s_memory[A][bit_index] <= D[bit_index];
end
end
end else begin
Q <= s_memory[A];
end
end
end
endmodule
+146
View File
@@ -0,0 +1,146 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "onchip_ram_define.svh"
module onchip_ram_absent_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) mem_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
onchip_ram #(
.Present (1'b0),
.CapacityKiB(128)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(1'b1),
.perf_clear_i (1'b0),
.mem_axi4 (mem_axi4),
.cfg_apb4 (cfg_apb4)
);
task automatic init_interfaces;
begin
mem_axi4.awid = '0;
mem_axi4.awaddr = '0;
mem_axi4.awlen = '0;
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awlock = `AXI4_LOCK_NORM;
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
mem_axi4.awprot = `AXI4_PROT_DATA;
mem_axi4.awqos = `AXI4_QOS_NORMAL;
mem_axi4.awregion = `AXI4_REGION_NORMAL;
mem_axi4.awuser = '0;
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = '0;
mem_axi4.wstrb = '0;
mem_axi4.wlast = 1'b0;
mem_axi4.wuser = '0;
mem_axi4.wvalid = 1'b0;
mem_axi4.bready = 1'b0;
mem_axi4.arid = '0;
mem_axi4.araddr = '0;
mem_axi4.arlen = '0;
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arlock = `AXI4_LOCK_NORM;
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
mem_axi4.arprot = `AXI4_PROT_DATA;
mem_axi4.arqos = `AXI4_QOS_NORMAL;
mem_axi4.arregion = `AXI4_REGION_NORMAL;
mem_axi4.aruser = '0;
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b0;
cfg_apb4.paddr = '0;
cfg_apb4.pprot = '0;
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [11:0] offset, input logic [31:0] expected);
begin
@(negedge clk_i);
cfg_apb4.paddr = {20'd0, offset};
cfg_apb4.psel = 1'b1;
cfg_apb4.penable = 1'b0;
@(negedge clk_i);
cfg_apb4.penable = 1'b1;
do @(posedge clk_i); while (!cfg_apb4.pready);
if (cfg_apb4.pslverr || (cfg_apb4.prdata !== expected)) begin
$fatal(1, "absent SRAM APB read mismatch offset=%03x", offset);
end
@(negedge clk_i);
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
end
endtask
initial begin
init_interfaces();
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
apb_read(`APB4_ONCHIP_RAM__CAPABILITY, 32'h0004_107E);
apb_read(`APB4_ONCHIP_RAM__MEMORY_BYTES, 32'd0);
apb_read(`APB4_ONCHIP_RAM__BANK_COUNT, 32'd0);
@(negedge clk_i);
mem_axi4.araddr = `SOC_ADDR_SRAM_BASE;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.rvalid);
if ((mem_axi4.rresp != `AXI4_RESP_DECODE_ERROR) || !mem_axi4.rlast) begin
$fatal(1, "absent SRAM read did not return DECERR");
end
@(negedge clk_i);
mem_axi4.rready = 1'b0;
@(negedge clk_i);
mem_axi4.awaddr = `SOC_ADDR_SRAM_BASE;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = 32'hDEAD_BEEF;
mem_axi4.wstrb = 4'hF;
mem_axi4.wlast = 1'b1;
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
mem_axi4.wvalid = 1'b0;
mem_axi4.wlast = 1'b0;
mem_axi4.bready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != `AXI4_RESP_DECODE_ERROR) begin
$fatal(1, "absent SRAM write did not return DECERR");
end
$display("absent on-chip SRAM test passed");
$finish;
end
endmodule
+192
View File
@@ -0,0 +1,192 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
module onchip_ram_perf_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [31:0] s_legacy_memory [0:1023];
integer s_native_cycles;
integer s_legacy_cycles;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) native_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) legacy_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
ram_if legacy_ram ();
always #5 clk_i = ~clk_i;
always_ff @(posedge clk_i) begin
legacy_ram.rdata <= s_legacy_memory[legacy_ram.addr[9:0]];
for (int lane = 0; lane < 4; lane++) begin
if (legacy_ram.wstrb[lane]) begin
s_legacy_memory[legacy_ram.addr[9:0]][lane*8+:8] <= legacy_ram.wdata[lane*8+:8];
end
end
end
onchip_ram #(
.Present (1'b1),
.CapacityKiB(4)
) u_native (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(1'b0),
.perf_clear_i (1'b0),
.mem_axi4 (native_axi4),
.cfg_apb4 (cfg_apb4)
);
axi42ram u_legacy (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (legacy_axi4),
.ram (legacy_ram)
);
task automatic init_axi4;
begin
native_axi4.awid = '0;
native_axi4.awaddr = '0;
native_axi4.awlen = '0;
native_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
native_axi4.awburst = `AXI4_BURST_TYPE_INCR;
native_axi4.awlock = 1'b0;
native_axi4.awcache = '0;
native_axi4.awprot = '0;
native_axi4.awqos = '0;
native_axi4.awregion = '0;
native_axi4.awuser = '0;
native_axi4.awvalid = 1'b0;
native_axi4.wdata = '0;
native_axi4.wstrb = '0;
native_axi4.wlast = 1'b0;
native_axi4.wuser = '0;
native_axi4.wvalid = 1'b0;
native_axi4.bready = 1'b0;
native_axi4.arid = '0;
native_axi4.araddr = '0;
native_axi4.arlen = '0;
native_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
native_axi4.arburst = `AXI4_BURST_TYPE_INCR;
native_axi4.arlock = 1'b0;
native_axi4.arcache = '0;
native_axi4.arprot = '0;
native_axi4.arqos = '0;
native_axi4.arregion = '0;
native_axi4.aruser = '0;
native_axi4.arvalid = 1'b0;
native_axi4.rready = 1'b1;
legacy_axi4.awid = '0;
legacy_axi4.awaddr = '0;
legacy_axi4.awlen = '0;
legacy_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
legacy_axi4.awburst = `AXI4_BURST_TYPE_INCR;
legacy_axi4.awlock = 1'b0;
legacy_axi4.awcache = '0;
legacy_axi4.awprot = '0;
legacy_axi4.awqos = '0;
legacy_axi4.awregion = '0;
legacy_axi4.awuser = '0;
legacy_axi4.awvalid = 1'b0;
legacy_axi4.wdata = '0;
legacy_axi4.wstrb = '0;
legacy_axi4.wlast = 1'b0;
legacy_axi4.wuser = '0;
legacy_axi4.wvalid = 1'b0;
legacy_axi4.bready = 1'b0;
legacy_axi4.arid = '0;
legacy_axi4.araddr = '0;
legacy_axi4.arlen = '0;
legacy_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
legacy_axi4.arburst = `AXI4_BURST_TYPE_INCR;
legacy_axi4.arlock = 1'b0;
legacy_axi4.arcache = '0;
legacy_axi4.arprot = '0;
legacy_axi4.arqos = '0;
legacy_axi4.arregion = '0;
legacy_axi4.aruser = '0;
legacy_axi4.arvalid = 1'b0;
legacy_axi4.rready = 1'b1;
cfg_apb4.paddr = '0;
cfg_apb4.pprot = '0;
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
end
endtask
task automatic measure_native;
integer beats;
begin
@(negedge clk_i);
native_axi4.araddr = `SOC_ADDR_SRAM_BASE;
native_axi4.arlen = 8'd15;
native_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!native_axi4.arready);
@(negedge clk_i);
native_axi4.arvalid = 1'b0;
s_native_cycles = 0;
beats = 0;
while (beats < 16) begin
@(posedge clk_i);
s_native_cycles = s_native_cycles + 1;
if (native_axi4.rvalid) beats = beats + 1;
end
end
endtask
task automatic measure_legacy;
integer beats;
begin
@(negedge clk_i);
legacy_axi4.araddr = `SOC_ADDR_SRAM_BASE;
legacy_axi4.arlen = 8'd15;
legacy_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!legacy_axi4.arready);
@(negedge clk_i);
legacy_axi4.arvalid = 1'b0;
s_legacy_cycles = 0;
beats = 0;
while (beats < 16) begin
@(posedge clk_i);
s_legacy_cycles = s_legacy_cycles + 1;
if (legacy_axi4.rvalid) beats = beats + 1;
end
end
endtask
initial begin
init_axi4();
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
measure_native();
measure_legacy();
$display("on-chip SRAM burst cycles native=%0d legacy=%0d", s_native_cycles, s_legacy_cycles);
if ((s_native_cycles != 16) || (s_legacy_cycles < (s_native_cycles + 2))) begin
$fatal(1, "native SRAM did not reach one beat per cycle or remove bridge overhead");
end
$finish;
end
endmodule
+387
View File
@@ -0,0 +1,387 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "mmap_define.svh"
`include "onchip_ram_define.svh"
module onchip_ram_tb #(
parameter int unsigned CapacityKiB = 128
);
localparam logic [31:0] SramBase = `SOC_ADDR_SRAM_BASE;
localparam logic [31:0] SramEnd = `SOC_ADDR_SRAM_END;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic perf_enable_i = 1'b0;
logic perf_clear_i = 1'b0;
logic [31:0] s_apb_read_data;
logic [31:0] s_expected;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) mem_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
apb4_if cfg_apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
onchip_ram #(
.Present (1'b1),
.CapacityKiB(CapacityKiB)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.perf_enable_i(perf_enable_i),
.perf_clear_i (perf_clear_i),
.mem_axi4 (mem_axi4),
.cfg_apb4 (cfg_apb4)
);
task automatic init_interfaces;
begin
mem_axi4.awid = '0;
mem_axi4.awaddr = '0;
mem_axi4.awlen = '0;
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awlock = `AXI4_LOCK_NORM;
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
mem_axi4.awprot = `AXI4_PROT_DATA;
mem_axi4.awqos = `AXI4_QOS_NORMAL;
mem_axi4.awregion = `AXI4_REGION_NORMAL;
mem_axi4.awuser = '0;
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = '0;
mem_axi4.wstrb = '0;
mem_axi4.wlast = 1'b0;
mem_axi4.wuser = '0;
mem_axi4.wvalid = 1'b0;
mem_axi4.bready = 1'b0;
mem_axi4.arid = '0;
mem_axi4.araddr = '0;
mem_axi4.arlen = '0;
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arlock = `AXI4_LOCK_NORM;
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
mem_axi4.arprot = `AXI4_PROT_DATA;
mem_axi4.arqos = `AXI4_QOS_NORMAL;
mem_axi4.arregion = `AXI4_REGION_NORMAL;
mem_axi4.aruser = '0;
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b0;
cfg_apb4.paddr = '0;
cfg_apb4.pprot = '0;
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
end
endtask
task automatic write_burst(input logic [31:0] address, input logic [7:0] length,
input logic [2:0] size, input logic [1:0] burst,
input logic [31:0] seed, input logic [1:0] response);
integer previous_cycle;
integer cycle_count;
begin
@(negedge clk_i);
mem_axi4.awaddr = address;
mem_axi4.awlen = length;
mem_axi4.awsize = size;
mem_axi4.awburst = burst;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
previous_cycle = -1;
cycle_count = 0;
for (int beat = 0; beat <= int'(length); beat++) begin
mem_axi4.wdata = seed + beat;
mem_axi4.wstrb = 4'hF;
mem_axi4.wlast = beat == int'(length);
mem_axi4.wvalid = 1'b1;
do begin
@(posedge clk_i);
cycle_count = cycle_count + 1;
end while (!mem_axi4.wready);
if ((previous_cycle >= 0) && (cycle_count != previous_cycle + 1)) begin
$fatal(1, "write burst inserted a bubble at beat %0d", beat);
end
previous_cycle = cycle_count;
@(negedge clk_i);
end
mem_axi4.wvalid = 1'b0;
mem_axi4.wlast = 1'b0;
mem_axi4.bready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != response) begin
$fatal(1, "write response=%0d expected=%0d", mem_axi4.bresp, response);
end
@(negedge clk_i);
mem_axi4.bready = 1'b0;
end
endtask
task automatic read_burst(input logic [31:0] address, input logic [7:0] length,
input logic [2:0] size, input logic [1:0] burst,
input logic [31:0] seed, input logic [1:0] response,
input logic check_data, input logic add_backpressure);
logic [31:0] held_data;
logic [ 1:0] held_resp;
logic held_last;
integer previous_cycle;
integer cycle_count;
begin
@(negedge clk_i);
mem_axi4.araddr = address;
mem_axi4.arlen = length;
mem_axi4.arsize = size;
mem_axi4.arburst = burst;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b1;
previous_cycle = -1;
cycle_count = 0;
for (int beat = 0; beat <= int'(length); beat++) begin
if (add_backpressure && (beat == 1)) begin
@(negedge clk_i);
mem_axi4.rready = 1'b0;
while (!mem_axi4.rvalid) @(negedge clk_i);
held_data = mem_axi4.rdata;
held_resp = mem_axi4.rresp;
held_last = mem_axi4.rlast;
repeat (3) begin
@(negedge clk_i);
if (!mem_axi4.rvalid || (mem_axi4.rdata !== held_data) ||
(mem_axi4.rresp !== held_resp) || (mem_axi4.rlast !== held_last)) begin
$fatal(1, "read response changed under backpressure");
end
end
mem_axi4.rready = 1'b1;
previous_cycle = -1;
end
do begin
@(posedge clk_i);
cycle_count = cycle_count + 1;
end while (!mem_axi4.rvalid);
if (mem_axi4.rresp != response || mem_axi4.rlast != (beat == int'(length))) begin
$fatal(1, "read response mismatch at beat %0d", beat);
end
s_expected = seed + beat;
if (check_data && (mem_axi4.rdata !== s_expected)) begin
$fatal(1, "read data=%08x expected=%08x at beat %0d", mem_axi4.rdata, s_expected, beat);
end
if ((previous_cycle >= 0) && (cycle_count != previous_cycle + 1)) begin
$fatal(1, "read burst inserted a bubble at beat %0d", beat);
end
previous_cycle = cycle_count;
end
@(negedge clk_i);
mem_axi4.rready = 1'b0;
end
endtask
task automatic write_single(input logic [31:0] address, input logic [2:0] size,
input logic [31:0] data, input logic [3:0] strobe,
input logic [1:0] response);
begin
@(negedge clk_i);
mem_axi4.awaddr = address;
mem_axi4.awlen = 8'd0;
mem_axi4.awsize = size;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = data;
mem_axi4.wstrb = strobe;
mem_axi4.wlast = 1'b1;
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
mem_axi4.wvalid = 1'b0;
mem_axi4.wlast = 1'b0;
mem_axi4.bready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != response) begin
$fatal(1, "single write response=%0d expected=%0d", mem_axi4.bresp, response);
end
@(negedge clk_i);
mem_axi4.bready = 1'b0;
end
endtask
task automatic write_missing_last(input logic [31:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
mem_axi4.awaddr = address;
mem_axi4.awlen = 8'd0;
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = data;
mem_axi4.wstrb = 4'hF;
mem_axi4.wlast = 1'b0;
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
mem_axi4.wvalid = 1'b0;
mem_axi4.bready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != `AXI4_RESP_SLAVE_ERROR) begin
$fatal(1, "missing WLAST did not return SLVERR");
end
@(negedge clk_i);
mem_axi4.bready = 1'b0;
end
endtask
task automatic read_single(input logic [31:0] address, input logic [2:0] size,
input logic [31:0] expected, input logic [1:0] response);
begin
read_burst(address, 8'd0, size, `AXI4_BURST_TYPE_INCR, expected, response,
response == `AXI4_RESP_OKAY, 1'b0);
end
endtask
task automatic apb_read(input logic [11:0] offset, input logic [31:0] expected);
begin
@(negedge clk_i);
cfg_apb4.paddr = {20'd0, offset};
cfg_apb4.psel = 1'b1;
cfg_apb4.penable = 1'b0;
@(negedge clk_i);
cfg_apb4.penable = 1'b1;
do @(posedge clk_i); while (!cfg_apb4.pready);
s_apb_read_data = cfg_apb4.prdata;
if (cfg_apb4.pslverr || (s_apb_read_data !== expected)) begin
$fatal(1, "APB offset=%03x data=%08x expected=%08x error=%0b", offset, s_apb_read_data,
expected, cfg_apb4.pslverr);
end
@(negedge clk_i);
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
end
endtask
task automatic apb_error(input logic [11:0] offset, input logic write_access);
begin
@(negedge clk_i);
cfg_apb4.paddr = {20'd0, offset};
cfg_apb4.psel = 1'b1;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = write_access;
cfg_apb4.pwdata = 32'hFFFF_FFFF;
cfg_apb4.pstrb = 4'hF;
@(negedge clk_i);
cfg_apb4.penable = 1'b1;
do @(posedge clk_i); while (!cfg_apb4.pready);
if (!cfg_apb4.pslverr) begin
$fatal(1, "APB illegal access offset=%03x write=%0b did not return PSLVERR", offset,
write_access);
end
@(negedge clk_i);
cfg_apb4.psel = 1'b0;
cfg_apb4.penable = 1'b0;
cfg_apb4.pwrite = 1'b0;
cfg_apb4.pwdata = '0;
cfg_apb4.pstrb = '0;
end
endtask
initial begin
init_interfaces();
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
perf_enable_i = 1'b1;
apb_read(`APB4_ONCHIP_RAM__IP_ID, 32'h5352_414D);
apb_read(`APB4_ONCHIP_RAM__IP_VERSION, 32'h0001_0000);
apb_read(`APB4_ONCHIP_RAM__MEMORY_BYTES, 32'(CapacityKiB * 1024));
apb_read(`APB4_ONCHIP_RAM__BANK_COUNT, 32'(CapacityKiB / 4));
apb_read(`APB4_ONCHIP_RAM__BANK_BYTES, 32'd4096);
apb_error(`APB4_ONCHIP_RAM__IP_ID, 1'b1);
apb_error(12'h018, 1'b0);
apb_error(12'h001, 1'b0);
write_burst(SramEnd - 32'd63, 8'd15, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_INCR,
32'hA500_0000, `AXI4_RESP_OKAY);
read_burst(SramEnd - 32'd63, 8'd15, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_INCR,
32'hA500_0000, `AXI4_RESP_OKAY, 1'b1, 1'b1);
for (int bank = 0; bank < (CapacityKiB / 4); bank++) begin
write_single(SramBase + 32'(bank * 4096), `AXI4_BURST_SIZE_4BYTES, 32'hB000_0000 + bank, 4'hF,
`AXI4_RESP_OKAY);
read_single(SramBase + 32'(bank * 4096), `AXI4_BURST_SIZE_4BYTES, 32'hB000_0000 + bank,
`AXI4_RESP_OKAY);
end
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'h1122_3344, 4'hF, `AXI4_RESP_OKAY);
write_single(SramBase + 32'h101, `AXI4_BURST_SIZE_1BYTE, 32'h0000_AA00, 4'b0010,
`AXI4_RESP_OKAY);
write_single(SramBase + 32'h102, `AXI4_BURST_SIZE_2BYTES, 32'hBEEF_0000, 4'b1100,
`AXI4_RESP_OKAY);
read_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hBEEF_AA44, `AXI4_RESP_OKAY);
write_burst(SramBase + 32'h200, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_FIXED,
32'hCAFE_0000, `AXI4_RESP_OKAY);
read_single(SramBase + 32'h200, `AXI4_BURST_SIZE_4BYTES, 32'hCAFE_0003, `AXI4_RESP_OKAY);
write_burst(SramBase + 32'h248, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_WRAP,
32'hFACE_0000, `AXI4_RESP_OKAY);
read_burst(SramBase + 32'h248, 8'd3, `AXI4_BURST_SIZE_4BYTES, `AXI4_BURST_TYPE_WRAP,
32'hFACE_0000, `AXI4_RESP_OKAY, 1'b1, 1'b0);
read_single(SramEnd + 1'b1, `AXI4_BURST_SIZE_4BYTES, 32'd0, `AXI4_RESP_DECODE_ERROR);
read_single(SramBase + 32'd2, `AXI4_BURST_SIZE_4BYTES, 32'd0, `AXI4_RESP_SLAVE_ERROR);
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hFFFF_FFFF, 4'b0000,
`AXI4_RESP_OKAY);
write_missing_last(SramBase + 32'h100, 32'hDEAD_BEEF);
write_single(SramBase + 32'h100, `AXI4_BURST_SIZE_1BYTE, 32'h0000_5500, 4'b0010,
`AXI4_RESP_SLAVE_ERROR);
read_single(SramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hBEEF_AA44, `AXI4_RESP_OKAY);
@(negedge clk_i);
mem_axi4.araddr = SramBase;
mem_axi4.awaddr = SramBase + 32'd4;
mem_axi4.arvalid = 1'b1;
mem_axi4.awvalid = 1'b1;
@(posedge clk_i);
if (!mem_axi4.arready || mem_axi4.awready) begin
$fatal(1, "simultaneous AR/AW did not select the read channel");
end
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
mem_axi4.awvalid = 1'b0;
mem_axi4.rready = 1'b1;
do @(posedge clk_i); while (!mem_axi4.rvalid);
@(negedge clk_i);
mem_axi4.rready = 1'b0;
apb_read(`APB4_ONCHIP_RAM__PERF_READ_REQUESTS, 32'(CapacityKiB / 4 + 8));
if (u_dut.s_perf_write_requests_q == 32'd0 || u_dut.s_perf_read_beats_q == 32'd0 ||
u_dut.s_perf_write_beats_q == 32'd0 || u_dut.s_perf_err_resps_q != 32'd4) begin
$fatal(1, "performance counters did not record the directed traffic");
end
$display("on-chip SRAM AXI4 test passed capacity_kib=%0d", CapacityKiB);
$finish;
end
endmodule
+63
View File
@@ -0,0 +1,63 @@
`timescale 1ns / 1ps
module tc_sram_1024x32_tb;
logic clk_i = 1'b0;
logic cs_i = 1'b0;
logic [ 9:0] addr_i = '0;
logic [31:0] data_i = '0;
logic [ 3:0] mask_i = '0;
logic wren_i = 1'b0;
logic [31:0] data_o;
always #5 clk_i = ~clk_i;
tc_sram_1024x32 u_dut (
.clk_i (clk_i),
.cs_i (cs_i),
.addr_i(addr_i),
.data_i(data_i),
.mask_i(mask_i),
.wren_i(wren_i),
.data_o(data_o)
);
task automatic write_word(input logic [9:0] address, input logic [31:0] data,
input logic [3:0] mask);
begin
@(negedge clk_i);
cs_i = 1'b1;
addr_i = address;
data_i = data;
mask_i = mask;
wren_i = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
cs_i = 1'b0;
wren_i = 1'b0;
mask_i = '0;
end
endtask
task automatic read_word(input logic [9:0] address, input logic [31:0] expected);
begin
@(negedge clk_i);
cs_i = 1'b1;
addr_i = address;
wren_i = 1'b0;
@(posedge clk_i);
@(negedge clk_i);
if (data_o !== expected) begin
$fatal(1, "1024x32 SRAM data=%08x expected=%08x", data_o, expected);
end
cs_i = 1'b0;
end
endtask
initial begin
write_word(10'h155, 32'h1122_3344, 4'hF);
write_word(10'h155, 32'hAABB_CCDD, 4'b0101);
read_word(10'h155, 32'h11BB_33DD);
$display("1024x32 SRAM wrapper test passed");
$finish;
end
endmodule
+20 -1
View File
@@ -16,7 +16,7 @@ USER_EXTENSIONS = ROOT / "rtl/mini/integration/user_extensions.json"
USER_GENERATOR = ROOT / "rtl/mini/integration/generate_user_extensions.py"
def generate(output_dir: Path, *, sram: str = "NO") -> None:
def generate(output_dir: Path, *, sram: str = "NO", sram_size_kib: int = 128) -> None:
subprocess.run(
[
sys.executable,
@@ -27,6 +27,8 @@ def generate(output_dir: Path, *, sram: str = "NO") -> None:
str(output_dir),
"--have-sram-if",
sram,
"--sram-size-kib",
str(sram_size_kib),
],
check=True,
)
@@ -66,6 +68,7 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "RS_SOC_APB4_SDIO0_BASE UINT32_C(0x1000F000)" in header
assert "RS_SOC_APB4_SDIO1_BASE UINT32_C(0x10015000)" in header
assert "RS_SOC_APB4_USB2_BASE UINT32_C(0x10016000)" in header
assert "RS_SOC_APB4_SRAM_BASE UINT32_C(0x10017000)" in header
assert "RS_SOC_NMI_" not in header
assert "RS_SOC_OPIPSRAM_BASE UINT32_C(0x48000000)" in header
assert "RS_SOC_HAS_SRAM 1U" in header
@@ -101,6 +104,22 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "OPIPSRAM (wxa!ri) : ORIGIN = 0x48000000, LENGTH = 0x08000000" in linker
def test_sram_capacity_selects_one_consistent_hardware_software_window(tmp_path: Path) -> None:
for size_kib in (4, 16, 32, 64, 128):
output = tmp_path / str(size_kib)
generate(output, sram="YES", sram_size_kib=size_kib)
size = size_kib * 1024
end = 0x30000000 + size - 1
rtl = (output / "rtl/mmap_define.svh").read_text(encoding="utf-8")
header = (output / "include/retrosoc/generated/memory_map.h").read_text(encoding="utf-8")
linker = (output / "linker/memory_regions.ld").read_text(encoding="utf-8")
assert f"`define SOC_ADDR_SRAM_SIZE 32'h{size:08X}" in rtl
assert f"`define SOC_ADDR_SRAM_END 32'h{end:08X}" in rtl
assert f"#define RS_SOC_SRAM_SIZE UINT32_C(0x{size:08X})" in header
assert f"SRAM (wxa!ri) : ORIGIN = 0x30000000, LENGTH = 0x{size:08X}" in linker
def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> None:
generate(tmp_path)
+9 -5
View File
@@ -28,12 +28,16 @@ def test_booter_prints_the_fixed_hazard3_specification() -> None:
assert "Other: Zifencei_Zilsd_Xh3BextM_Xh3IRQ" in booter
assert "Not present: U-mode PMP Zcb_Zclsd_Zcmp_Xh3PMPM_Xh3Power" in booter
assert "IRQ: 30 external + software + timer; Xh3IRQ, 4 priority levels" in booter
assert "one shared instruction/data AHB5 manager port" in booter
assert "single-beat AXI4 normal access" in booter
assert "no burst, lock, or global-exclusive forwarding; no system A atomics" in booter
assert "AHB5 manager -> AHB-Lite/AXI4 fabric" in booter
assert "Single-beat access; no burst or exclusive transactions" in booter
assert "RS_SOC_MGMT_JTAG_IDCODE" in booter
assert "halted-hart memory commands; system bus access disabled" in booter
assert "2 exact execute-address hardware breakpoints; no watchpoints" in booter
assert "RISC-V JTAG DTM/DM, IDCODE 0x%08x" in booter
assert "One hart: halt/resume, 2 breakpoints; no system-bus access" in booter
assert "AHB-Lite NSEQ subset" not in booter
assert "external cache/coherency handshake" not in booter
assert "TCK/TMS/TDI/TRST_n/TDO" not in booter
assert "instruction injection" not in booter
assert "exact execute-address hardware breakpoints" not in booter
for parameter in (
".EXTENSION_A (1)",
+303
View File
@@ -0,0 +1,303 @@
"""Directed verification for the configurable native-AXI4 on-chip SRAM."""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
CAPACITIES_KIB = (4, 16, 32, 64, 128)
@pytest.mark.parametrize("capacity_kib", CAPACITIES_KIB)
def test_onchip_ram_axi4_capacity_protocol_and_performance(
tmp_path: Path, capacity_kib: int
) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
generated = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(generated),
"--have-sram-if",
"YES",
"--sram-size-kib",
str(capacity_kib),
],
check=True,
)
output = tmp_path / f"onchip_ram_{capacity_kib}"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"onchip_ram_tb",
f"-GCapacityKiB={capacity_kib}",
"+define+PDK_BEHAV",
"+define+SV_ASSRT_DISABLE",
"-I" + str(generated / "rtl"),
"-I" + str(ROOT / "rtl/mini/top"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
str(ROOT / "tests/rtl/onchip_ram_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 f"on-chip SRAM AXI4 test passed capacity_kib={capacity_kib}" in result.stdout
def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
generated = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(generated),
"--have-sram-if",
"NO",
],
check=True,
)
output = tmp_path / "onchip_ram_absent"
ccache_tmp = tmp_path / "ccache-absent"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"onchip_ram_absent_tb",
"+define+SV_ASSRT_DISABLE",
"-I" + str(generated / "rtl"),
"-I" + str(ROOT / "rtl/mini/top"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
str(ROOT / "tests/rtl/onchip_ram_absent_tb.sv"),
"-Mdir",
str(tmp_path / "obj-absent"),
"-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 "absent on-chip SRAM test passed" in result.stdout
def test_ihp130_wrapper_uses_functional_1024x32_macro(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "tc_sram_ihp130"
ccache_tmp = tmp_path / "ccache-ihp130"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"tc_sram_1024x32_tb",
"+define+PDK_IHP130",
"+define+HAVE_SRAM_MACRO",
"+define+FUNCTIONAL",
"+define+SYNTHESIS",
str(
ROOT / "physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/"
"RM_IHPSG13_1P_core_behavioral_bm_bist.v"
),
str(
ROOT / "physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/"
"RM_IHPSG13_1P_1024x32_c2_bm_bist.v"
),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "tests/rtl/tc_sram_1024x32_tb.sv"),
"-Mdir",
str(tmp_path / "obj-ihp130"),
"-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 "1024x32 SRAM wrapper test passed" in result.stdout
wrapper = (ROOT / "rtl/tech/tc_sram.sv").read_text(encoding="utf-8")
assert "RM_IHPSG13_1P_1024x32_c2_bm_bist" in wrapper
assert "RM_IHPSG13_1P_1024x64_c2_bm_bist" not in wrapper
def test_ics55_wrapper_preserves_active_low_controls_and_byte_masks(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "tc_sram_ics55"
ccache_tmp = tmp_path / "ccache-ics55"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"tc_sram_1024x32_tb",
"+define+PDK_ICS55",
"+define+HAVE_SRAM_MACRO",
str(ROOT / "tests/rtl/ics55_onchip_sram_stub.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "tests/rtl/tc_sram_1024x32_tb.sv"),
"-Mdir",
str(tmp_path / "obj-ics55"),
"-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 "1024x32 SRAM wrapper test passed" in result.stdout
def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
generated = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(generated),
"--have-sram-if",
"YES",
"--sram-size-kib",
"4",
],
check=True,
)
output = tmp_path / "onchip_ram_perf"
ccache_tmp = tmp_path / "ccache-perf"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"onchip_ram_perf_tb",
"+define+PDK_BEHAV",
"+define+SV_ASSRT_DISABLE",
"-I" + str(generated / "rtl"),
"-I" + str(ROOT / "rtl/mini/top"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ram_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/xchecker.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/axi42ram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
str(ROOT / "tests/rtl/onchip_ram_perf_tb.sv"),
"-Mdir",
str(tmp_path / "obj-perf"),
"-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 "on-chip SRAM burst cycles native=16 legacy=18" in result.stdout
+35
View File
@@ -0,0 +1,35 @@
"""Keep the handwritten on-chip SRAM RTL and SDK register ABI synchronized."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RTL_DEFINE = ROOT / "rtl/mini/top/onchip_ram_define.svh"
C_HEADER = ROOT / "crt/include/retrosoc/hal/onchip_sram_regs.h"
def _rtl_offsets() -> dict[str, int]:
return {
name: int(value, 16)
for name, value in re.findall(
r"`define\s+APB4_ONCHIP_RAM__([A-Z0-9_]+)\s+12'h([0-9A-Fa-f]+)",
RTL_DEFINE.read_text(encoding="utf-8"),
)
}
def _c_offsets() -> dict[str, int]:
return {
name: int(value, 16)
for name, value in re.findall(
r"#define\s+RS_ONCHIP_SRAM_REG_([A-Z0-9_]+)\s+UINT32_C\(0x([0-9A-Fa-f]+)\)",
C_HEADER.read_text(encoding="utf-8"),
)
}
def test_onchip_sram_register_offsets_match_rtl() -> None:
assert _c_offsets() == _rtl_offsets()