mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: improve the performance of the onchip sram ctrl
This commit is contained in:
53 files changed
+2877
-147
No files matched your search
@@ -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)' \
|
||||
|
||||
@@ -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 |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 := 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
SRAM_SIZE_KIB := 128
|
||||
HAVE_SVA := NO
|
||||
EXT_CLK_HZ := 72000000
|
||||
AUD_CLK_HZ := 18432000
|
||||
|
||||
@@ -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 := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
SRAM_SIZE_KIB := 128
|
||||
HAVE_SVA := NO
|
||||
EXT_CLK_HZ := 72000000
|
||||
AUD_CLK_HZ := 18432000
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 (
|
||||
|
||||
+585
-48
@@ -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
|
||||
`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,
|
||||
ram_if.slave ram
|
||||
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
|
||||
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_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])
|
||||
.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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user