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
+2881
-151
No files matched your search
@@ -39,6 +39,7 @@ PDK ?= IHP130
|
|||||||
HAVE_PLL ?= NO
|
HAVE_PLL ?= NO
|
||||||
HAVE_SRAM_IF ?= NO
|
HAVE_SRAM_IF ?= NO
|
||||||
HAVE_SRAM_MACRO ?= NO
|
HAVE_SRAM_MACRO ?= NO
|
||||||
|
SRAM_SIZE_KIB ?= 128
|
||||||
PDK_BEHAV ?= NO
|
PDK_BEHAV ?= NO
|
||||||
HAVE_SVA ?= NO
|
HAVE_SVA ?= NO
|
||||||
BUILD_RELEASE ?= 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'); \
|
JOBS ?= $(shell count=$$(nproc 2>/dev/null || printf '1'); \
|
||||||
if [ "$$count" -gt "$(MAX_JOBS)" ]; then printf '%s' '$(MAX_JOBS)'; \
|
if [ "$$count" -gt "$(MAX_JOBS)" ]; then printf '%s' '$(MAX_JOBS)'; \
|
||||||
else printf '%s' "$$count"; fi)
|
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 \
|
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
|
COREMARK_MODE RTL_TOP FIRMWARE_NAME
|
||||||
VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \
|
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_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_LINK_TYPE := xip jtag_sram ld2_all_sram ld2_sram ld2_psram ld2_sdram
|
||||||
VALID_COREMARK_MODE := quick standard
|
VALID_COREMARK_MODE := quick standard
|
||||||
|
VALID_SRAM_SIZE_KIB := 4 16 32 64 128
|
||||||
|
|
||||||
define validate_value
|
define validate_value
|
||||||
$(if $(filter $($(1)),$(2)),,$(error Invalid $(1)='$($(1))'; expected one of: $(2)))
|
$(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_PLL,$(VALID_BOOL))
|
||||||
$(call validate_value,HAVE_SRAM_IF,$(VALID_BOOL))
|
$(call validate_value,HAVE_SRAM_IF,$(VALID_BOOL))
|
||||||
$(call validate_value,HAVE_SRAM_MACRO,$(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,PDK_BEHAV,$(VALID_BOOL))
|
||||||
$(call validate_value,HAVE_SVA,$(VALID_BOOL))
|
$(call validate_value,HAVE_SVA,$(VALID_BOOL))
|
||||||
$(call validate_value,BUILD_RELEASE,$(VALID_BOOL))
|
$(call validate_value,BUILD_RELEASE,$(VALID_BOOL))
|
||||||
@@ -276,7 +279,7 @@ include physical/librelane/Makefile
|
|||||||
benchmark-report coremark-report \
|
benchmark-report coremark-report \
|
||||||
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
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 \
|
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
|
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all
|
||||||
.NOTPARALLEL: setup
|
.NOTPARALLEL: setup
|
||||||
|
|
||||||
@@ -310,7 +313,7 @@ help:
|
|||||||
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
|
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
|
||||||
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
|
' 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 | 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' \
|
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
|
||||||
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
|
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
|
||||||
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
|
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
|
||||||
@@ -350,7 +353,8 @@ config:
|
|||||||
STA '$(STA)' FORMAL '$(FORMAL)' \
|
STA '$(STA)' FORMAL '$(FORMAL)' \
|
||||||
VCS_USE_LSF '$(VCS_USE_LSF)' PDK '$(PDK)' \
|
VCS_USE_LSF '$(VCS_USE_LSF)' PDK '$(PDK)' \
|
||||||
HAVE_PLL '$(HAVE_PLL)' HAVE_SRAM_IF '$(HAVE_SRAM_IF)' \
|
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)' \
|
BUILD_RELEASE '$(BUILD_RELEASE)' \
|
||||||
JTAG_IDCODE '$(JTAG_IDCODE)' EXT_CLK_HZ '$(EXT_CLK_HZ)' AUD_CLK_HZ '$(AUD_CLK_HZ)' \
|
JTAG_IDCODE '$(JTAG_IDCODE)' EXT_CLK_HZ '$(EXT_CLK_HZ)' AUD_CLK_HZ '$(AUD_CLK_HZ)' \
|
||||||
CLINT_TIMEBASE_HZ '$(CLINT_TIMEBASE_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
|
PDK, application, linker layout, and optional features as one reproducible
|
||||||
configuration. The management core is fixed to Hazard3. The user-extension
|
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.
|
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 |
|
| Profile | ISA | Application | Coverage |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <retrosoc/hal/clint.h>
|
#include <retrosoc/hal/clint.h>
|
||||||
#include <retrosoc/hal/crypto.h>
|
#include <retrosoc/hal/crypto.h>
|
||||||
#include <retrosoc/hal/gpio.h>
|
#include <retrosoc/hal/gpio.h>
|
||||||
|
#include <retrosoc/hal/onchip_sram.h>
|
||||||
#include <retrosoc/hal/rng.h>
|
#include <retrosoc/hal/rng.h>
|
||||||
#include <retrosoc/hal/rtc.h>
|
#include <retrosoc/hal/rtc.h>
|
||||||
#include <retrosoc/hal/sdram.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);
|
(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) {
|
static bool rs_ci_smoke_user_ip(void) {
|
||||||
rs_status_t status;
|
rs_status_t status;
|
||||||
uint32_t identifier = 0U;
|
uint32_t identifier = 0U;
|
||||||
@@ -289,6 +306,10 @@ int main(void) {
|
|||||||
rs_test_finish(RS_TEST_FAILED, 1U);
|
rs_test_finish(RS_TEST_FAILED, 1U);
|
||||||
}
|
}
|
||||||
printf("ci_smoke: archinfo passed\n");
|
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()) {
|
if (!rs_ci_smoke_user_ip()) {
|
||||||
rs_test_finish(RS_TEST_FAILED, 11U);
|
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
|
Start builds from a committed profile rather than setting an unreviewed mix of
|
||||||
variables on the command line.
|
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
|
`PDK_BEHAV=YES` selects technology-wrapper functional models for behavioral
|
||||||
simulation. It is a simulation-only setting, participates in the build variant
|
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
|
key, and is invalid with Yosys synthesis. Profiles that execute from an SRAM
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
PDK_BEHAV := YES
|
PDK_BEHAV := YES
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
PDK_BEHAV := YES
|
PDK_BEHAV := YES
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
PDK_BEHAV := YES
|
PDK_BEHAV := YES
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := GF180
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
AUD_CLK_HZ := 18432000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := ICS55
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
AUD_CLK_HZ := 18432000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
PDK_BEHAV := YES
|
PDK_BEHAV := YES
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
AUD_CLK_HZ := 18432000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
PDK_BEHAV := YES
|
PDK_BEHAV := YES
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
AUD_CLK_HZ := 18432000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := SKY130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
AUD_CLK_HZ := 18432000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ PDK := ICS55
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_SRAM_IF := NO
|
HAVE_SRAM_IF := NO
|
||||||
HAVE_SRAM_MACRO := NO
|
HAVE_SRAM_MACRO := NO
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
HAVE_SVA := NO
|
HAVE_SVA := NO
|
||||||
EXT_CLK_HZ := 72000000
|
EXT_CLK_HZ := 72000000
|
||||||
AUD_CLK_HZ := 18432000
|
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.
|
counting modes, interrupt, debug-freeze, HAL, and verification contracts.
|
||||||
- [ip/sysctrl.md](ip/sysctrl.md) defines the SystemCtrl register ABI, user-core
|
- [ip/sysctrl.md](ip/sysctrl.md) defines the SystemCtrl register ABI, user-core
|
||||||
lifecycle, PLL/fault/performance/RTC/test contracts, HAL, and verification.
|
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,
|
- [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.
|
generic HAL, extension-owned register ABI, and verification contract.
|
||||||
- [ip/clint.md](ip/clint.md) defines the standard CLINT register map, fixed
|
- [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
|
The fixed targets are the APB4 configuration plane (`apb4_periph`), APB
|
||||||
(`apb4_system`), SRAM, SDRAM, 4-bit PSRAM, OPI PSRAM, XPI/Flash, SPI-SD,
|
(`apb4_system`), SRAM, SDRAM, 4-bit PSRAM, OPI PSRAM, XPI/Flash, SPI-SD,
|
||||||
`DECERR`, and `SLVERR`. APB4 and APB accept only single-beat transactions.
|
`DECERR`, and `SLVERR`. APB4 and APB accept only single-beat transactions.
|
||||||
SRAM uses a direct AXI4 target with pipelined synchronous reads and response
|
SRAM uses a direct AXI4 target with pipelined synchronous reads, a one-cycle
|
||||||
buffering. External-memory data windows accept up to sixteen beats; their
|
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
|
controller front ends validate and serialize or coalesce the physical
|
||||||
transactions while preserving the documented AXI response contract. Register
|
transactions while preserving the documented AXI response contract. Register
|
||||||
configuration remains on APB4.
|
configuration remains on APB4.
|
||||||
@@ -63,12 +66,16 @@ access-control rejection to `PROTERR`.
|
|||||||
|
|
||||||
## Verification and Performance Gates
|
## Verification and Performance Gates
|
||||||
|
|
||||||
`tests/test_axi4.py` covers a four-beat write/read, response backpressure, and
|
`tests/test_axi4.py` covers fabric classification. `tests/test_onchip_ram.py`
|
||||||
rejection of a seventeen-beat request without touching the APB4 target. The
|
covers all five capacities, sixteen-beat reads/writes, response backpressure,
|
||||||
bridge unit test also covers Common address generation for FIXED and WRAP
|
partial writes, technology mapping, and error termination. The bridge unit
|
||||||
sequencing. The IHP130 `ci_smoke` regression is the end-to-end boot and
|
test also covers Common address generation for FIXED and WRAP sequencing. The
|
||||||
configuration check.
|
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
|
CoreMark uses SRAM and must not regress more than five percent from the native
|
||||||
7,620,324-cycle baseline. A controller may be promoted to a native burst target
|
4,662,868-cycle baseline; the retired compatibility path required 7,620,324
|
||||||
only after aligned sixteen-word tests show at least twenty percent improvement
|
cycles. A native SRAM target must return an aligned sixteen-word burst in
|
||||||
and cover partial writes, backpressure, device timing, and error termination.
|
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
|
testbench retains that Micron timing model, so it is the reference for SDRAM
|
||||||
command timing while Verilator provides fast functional coverage.
|
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
|
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.
|
separate APB4 management plane across four independently isolated 8 MiB chips.
|
||||||
Its frozen architecture, register ABI, timing limits, model, formal target, and
|
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_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_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_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_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
|
/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)
|
$(MEMORY_MAP_STAMP): $(MEMORY_MAP_SOURCE) $(MEMORY_MAP_SCRIPT)
|
||||||
$(FLOW_PYTHON) $(MEMORY_MAP_SCRIPT) --map $(MEMORY_MAP_SOURCE) \
|
$(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 $@
|
@touch $@
|
||||||
|
|
||||||
memory-map: $(MEMORY_MAP_STAMP)
|
memory-map: $(MEMORY_MAP_STAMP)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ VALID_ROUTES = {"apb4_periph", "apb4_system", "axi4", "ram", "reserved"}
|
|||||||
VALID_KINDS = {"active", "reserved"}
|
VALID_KINDS = {"active", "reserved"}
|
||||||
VALID_USER_ACCESS = {"none", "ro", "rw"}
|
VALID_USER_ACCESS = {"none", "ro", "rw"}
|
||||||
VALID_BURST = {"incr1", "incr4", "incr16"}
|
VALID_BURST = {"incr1", "incr4", "incr16"}
|
||||||
|
VALID_SRAM_SIZE_KIB = (4, 16, 32, 64, 128)
|
||||||
|
|
||||||
|
|
||||||
def parse_integer(value: Any, field: str) -> int:
|
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")
|
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:
|
with path.open(encoding="utf-8") as source:
|
||||||
document = json.load(source)
|
document = json.load(source)
|
||||||
if document.get("schema_version") != 2:
|
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")
|
raise ValueError(f"regions[{index}] reserved regions cannot support bursts")
|
||||||
base = parse_integer(region.get("base"), f"regions[{index}].base")
|
base = parse_integer(region.get("base"), f"regions[{index}].base")
|
||||||
size = parse_integer(region.get("size"), f"regions[{index}].size")
|
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:
|
if base < 0 or size <= 0 or base + size > MAX_ADDRESS:
|
||||||
raise ValueError(f"regions[{index}] exceeds the 32-bit address space")
|
raise ValueError(f"regions[{index}] exceeds the 32-bit address space")
|
||||||
if base % 4 != 0 or size % 4 != 0:
|
if base % 4 != 0 or size % 4 != 0:
|
||||||
@@ -155,6 +171,7 @@ def render_rtl(
|
|||||||
symbol = region["symbol"]
|
symbol = region["symbol"]
|
||||||
lines += [
|
lines += [
|
||||||
f"`define SOC_ADDR_{symbol}_BASE {hex32(region['base'])}",
|
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_{symbol}_END {hex32(region['end'])}",
|
||||||
f"`define SOC_ADDR_IS_{symbol}(addr) "
|
f"`define SOC_ADDR_IS_{symbol}(addr) "
|
||||||
f"{range_expression(symbol, region['base'], region['end'])}",
|
f"{range_expression(symbol, region['base'], region['end'])}",
|
||||||
@@ -172,9 +189,7 @@ def render_rtl(
|
|||||||
for region in regions
|
for region in regions
|
||||||
if region["route"] in {"apb4_periph", "axi4"}
|
if region["route"] in {"apb4_periph", "axi4"}
|
||||||
]
|
]
|
||||||
lines.append(
|
lines.append(f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})")
|
||||||
f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})"
|
|
||||||
)
|
|
||||||
user_readable = [
|
user_readable = [
|
||||||
f"`SOC_ADDR_IS_{region['symbol']}(addr)"
|
f"`SOC_ADDR_IS_{region['symbol']}(addr)"
|
||||||
for region in regions
|
for region in regions
|
||||||
@@ -282,8 +297,8 @@ def atomic_write(path: Path, content: str) -> None:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def generate(map_path: Path, output_dir: Path, have_sram_if: str) -> None:
|
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)
|
cpu_reset, regions, sysctrl_registers = read_map(map_path, sram_size_kib)
|
||||||
atomic_write(
|
atomic_write(
|
||||||
output_dir / "rtl" / "mmap_define.svh",
|
output_dir / "rtl" / "mmap_define.svh",
|
||||||
render_rtl(cpu_reset, regions, sysctrl_registers),
|
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("--map", required=True, type=Path)
|
||||||
parser.add_argument("--output-dir", type=Path)
|
parser.add_argument("--output-dir", type=Path)
|
||||||
parser.add_argument("--have-sram-if", choices=("YES", "NO"), default="NO")
|
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")
|
parser.add_argument("--check", action="store_true", help="validate only; write nothing")
|
||||||
arguments = parser.parse_args()
|
arguments = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
if arguments.check:
|
if arguments.check:
|
||||||
read_map(arguments.map)
|
read_map(arguments.map, arguments.sram_size_kib)
|
||||||
else:
|
else:
|
||||||
if arguments.output_dir is None:
|
if arguments.output_dir is None:
|
||||||
parser.error("--output-dir is required unless --check is used")
|
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:
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
parser.error(str(error))
|
parser.error(str(error))
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -379,6 +379,15 @@
|
|||||||
"public": true,
|
"public": true,
|
||||||
"user_access": "none"
|
"user_access": "none"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"symbol": "APB4_SRAM",
|
||||||
|
"base": "0x10017000",
|
||||||
|
"size": "0x00001000",
|
||||||
|
"route": "apb4_periph",
|
||||||
|
"kind": "active",
|
||||||
|
"public": true,
|
||||||
|
"user_access": "none"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"symbol": "APB4_ARCHINFO",
|
"symbol": "APB4_ARCHINFO",
|
||||||
"base": "0x20000000",
|
"base": "0x20000000",
|
||||||
@@ -455,6 +464,13 @@
|
|||||||
"symbol": "SRAM",
|
"symbol": "SRAM",
|
||||||
"base": "0x30000000",
|
"base": "0x30000000",
|
||||||
"size": "0x00020000",
|
"size": "0x00020000",
|
||||||
|
"size_options": [
|
||||||
|
"0x00001000",
|
||||||
|
"0x00004000",
|
||||||
|
"0x00008000",
|
||||||
|
"0x00010000",
|
||||||
|
"0x00020000"
|
||||||
|
],
|
||||||
"route": "ram",
|
"route": "ram",
|
||||||
"kind": "active",
|
"kind": "active",
|
||||||
"public": true,
|
"public": true,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
/top/core_wrapper.sv
|
/top/core_wrapper.sv
|
||||||
/top/retrosoc.sv
|
/top/retrosoc.sv
|
||||||
/top/retrosoc_asic.sv
|
/top/retrosoc_asic.sv
|
||||||
|
/top/retrosoc_core.sv
|
||||||
|
/top/onchip_ram_reg.sv
|
||||||
/top/onchip_ram.sv
|
/top/onchip_ram.sv
|
||||||
/top/apb4_periph.sv
|
/top/apb4_periph.sv
|
||||||
/top/apb4_system.sv
|
/top/apb4_system.sv
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ TOP = ROOT / "rtl/mini/top"
|
|||||||
|
|
||||||
|
|
||||||
def target_defines(target: str) -> list[str]:
|
def target_defines(target: str) -> list[str]:
|
||||||
if target == "opipsram":
|
if target in {"onchip_ram", "opipsram"}:
|
||||||
return ["+define+PDK_BEHAV"]
|
return ["+define+PDK_BEHAV"]
|
||||||
return ["+define+SV_ASSRT_DISABLE"]
|
return ["+define+SV_ASSRT_DISABLE"]
|
||||||
|
|
||||||
@@ -197,6 +197,18 @@ def source_files(target: str) -> list[Path]:
|
|||||||
MEMORY / "psram_phy.sv",
|
MEMORY / "psram_phy.sv",
|
||||||
SCRIPT_DIR / "psram_formal.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":
|
if target == "opipsram":
|
||||||
return [
|
return [
|
||||||
COMMON_RTL / "interface/axi4_if.sv",
|
COMMON_RTL / "interface/axi4_if.sv",
|
||||||
@@ -292,6 +304,7 @@ def parse_args() -> argparse.Namespace:
|
|||||||
"dvp",
|
"dvp",
|
||||||
"i2s",
|
"i2s",
|
||||||
"psram",
|
"psram",
|
||||||
|
"onchip_ram",
|
||||||
"opipsram",
|
"opipsram",
|
||||||
"dma",
|
"dma",
|
||||||
"sdio",
|
"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",
|
"timed_interface": "u_usb2_apb4_if",
|
||||||
"pure_interface": "u_usb2_apb4_pure_if",
|
"pure_interface": "u_usb2_apb4_pure_if",
|
||||||
"region": "APB4_USB2"
|
"region": "APB4_USB2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slot": 21,
|
||||||
|
"name": "sram",
|
||||||
|
"timed_interface": "sram_cfg",
|
||||||
|
"pure_interface": "sram_cfg_pure",
|
||||||
|
"region": "APB4_SRAM",
|
||||||
|
"external": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"apb4_system_targets": [
|
"apb4_system_targets": [
|
||||||
|
|||||||
+10
-3
@@ -22,7 +22,7 @@ FORMAL_OPIPSRAM_TIMEOUT ?= 300
|
|||||||
FORMAL_DMA_TIMEOUT ?= 120
|
FORMAL_DMA_TIMEOUT ?= 120
|
||||||
FORMAL_SDIO_TIMEOUT ?= 120
|
FORMAL_SDIO_TIMEOUT ?= 120
|
||||||
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma
|
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_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
|
||||||
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
||||||
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.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/i2s_formal_props.sv \
|
||||||
$(RTL_PATH)/formal/psram_formal.sv \
|
$(RTL_PATH)/formal/psram_formal.sv \
|
||||||
$(RTL_PATH)/formal/psram_formal_props.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.sv \
|
||||||
$(RTL_PATH)/formal/opipsram_formal_props.sv \
|
$(RTL_PATH)/formal/opipsram_formal_props.sv \
|
||||||
$(RTL_PATH)/formal/dma_formal.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_pkg.sv \
|
||||||
$(ROOT_PATH)/rtl/ip/memory/psram_axi4.sv \
|
$(ROOT_PATH)/rtl/ip/memory/psram_axi4.sv \
|
||||||
$(ROOT_PATH)/rtl/ip/memory/psram_phy.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/register.sv \
|
||||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/xchecker.sv \
|
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/xchecker.sv \
|
||||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/gray2bin.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)
|
$(FORMAL_SBY_GENERATOR)
|
||||||
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
||||||
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
|
--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
|
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||||
|
|
||||||
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
|
$(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-psram: $(FORMAL_DIR)/psram/.stamp | manifest
|
||||||
|
|
||||||
|
formal-onchip-ram: $(FORMAL_DIR)/onchip_ram/.stamp | manifest
|
||||||
|
|
||||||
formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
|
formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
|
||||||
|
|
||||||
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
|
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
|
||||||
@@ -289,4 +296,4 @@ formal-doctor:
|
|||||||
formal-clean:
|
formal-clean:
|
||||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
|
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/dvp.c \
|
||||||
$(ROOT_PATH)/crt/src/hal/perf.c \
|
$(ROOT_PATH)/crt/src/hal/perf.c \
|
||||||
$(ROOT_PATH)/crt/src/hal/lcd.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_math.c \
|
||||||
$(ROOT_PATH)/crt/src/hal/psram.c \
|
$(ROOT_PATH)/crt/src/hal/psram.c \
|
||||||
$(ROOT_PATH)/crt/src/hal/opipsram_math.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"
|
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:
|
def render_pad_instance(pad: Pad) -> str | None:
|
||||||
instance = f"u_{pad.name}"
|
instance = f"u_{pad.name}"
|
||||||
if pad.kind == "tri_input":
|
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",
|
f" for (genvar i = 0; i < {count}; i++) begin : {kind}_pads",
|
||||||
' (* keep *) (* dont_touch = "true" *)',
|
' (* keep *) (* dont_touch = "true" *)',
|
||||||
f" sg13g2_IOPad{cell_suffixes[kind]} {kind}_pad (",
|
f" sg13g2_IOPad{cell_suffixes[kind]} {kind}_pad (",
|
||||||
"`ifdef USE_POWER_PINS",
|
|
||||||
" .iovdd(IOVDD), .iovss(IOVSS), .vdd(VDD), .vss(VSS)",
|
|
||||||
"`endif",
|
|
||||||
" );",
|
" );",
|
||||||
" end",
|
" 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:
|
for pad in pads:
|
||||||
line = render_pad_instance(pad)
|
line = render_pad_instance(pad)
|
||||||
if line is not None:
|
if line is not None:
|
||||||
@@ -324,6 +337,72 @@ def render_pad_bindings(pads: list[Pad]) -> str:
|
|||||||
return "\n".join(lines) + "\n"
|
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:
|
def ihp130_pad_cell(pad: Pad) -> str | None:
|
||||||
if pad.kind in {"tri_input", "schmitt_input"}:
|
if pad.kind in {"tri_input", "schmitt_input"}:
|
||||||
return "sg13g2_IOPadIn"
|
return "sg13g2_IOPadIn"
|
||||||
@@ -359,6 +438,8 @@ def generate(map_path: Path, output_dir: Path) -> None:
|
|||||||
rtl_dir = output_dir / "rtl"
|
rtl_dir = output_dir / "rtl"
|
||||||
atomic_write(rtl_dir / "retrosoc_asic_ports.svh", render_ports(pads))
|
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_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():
|
for profile, bindings in profiles.items():
|
||||||
atomic_write(
|
atomic_write(
|
||||||
rtl_dir / f"retrosoc_asic_{profile}_bindings.svh",
|
rtl_dir / f"retrosoc_asic_{profile}_bindings.svh",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ module apb4_periph (
|
|||||||
sysctrl_if.dut sysctrl,
|
sysctrl_if.dut sysctrl,
|
||||||
pll_ctrl_if.sysctrl pll_ctrl,
|
pll_ctrl_if.sysctrl pll_ctrl,
|
||||||
apb4_if.master sdram_cfg,
|
apb4_if.master sdram_cfg,
|
||||||
|
apb4_if.master sram_cfg,
|
||||||
dvp_if.dut dvp,
|
dvp_if.dut dvp,
|
||||||
sdio_if.dut sdio0,
|
sdio_if.dut sdio0,
|
||||||
sdio_if.dut sdio1,
|
sdio_if.dut sdio1,
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ module axi4_bus (
|
|||||||
axi4_if.slave spisd_axi4,
|
axi4_if.slave spisd_axi4,
|
||||||
axi4_if.master cfg_axi4,
|
axi4_if.master cfg_axi4,
|
||||||
axi4_if.master system_axi4,
|
axi4_if.master system_axi4,
|
||||||
`ifdef HAVE_SRAM_IF
|
axi4_if.master sram_axi4,
|
||||||
ram_if.master ram,
|
|
||||||
`endif
|
|
||||||
axi4_if.master sdram_axi4,
|
axi4_if.master sdram_axi4,
|
||||||
axi4_if.master psram_axi4,
|
axi4_if.master psram_axi4,
|
||||||
axi4_if.master xpi_axi4,
|
axi4_if.master xpi_axi4,
|
||||||
@@ -76,16 +74,6 @@ module axi4_bus (
|
|||||||
.aresetn(rst_n_i)
|
.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 (
|
axi4_connector u_mgmt_connector (
|
||||||
.source(mgmt_axi4),
|
.source(mgmt_axi4),
|
||||||
.sink (u_master_axi4_if[0])
|
.sink (u_master_axi4_if[0])
|
||||||
@@ -133,7 +121,7 @@ module axi4_bus (
|
|||||||
|
|
||||||
axi4_connector u_ram_connector (
|
axi4_connector u_ram_connector (
|
||||||
.source(u_target_axi4_if[2]),
|
.source(u_target_axi4_if[2]),
|
||||||
.sink (u_ram_axi4_if)
|
.sink (sram_axi4)
|
||||||
);
|
);
|
||||||
|
|
||||||
axi4_connector u_sdram_connector (
|
axi4_connector u_sdram_connector (
|
||||||
@@ -189,23 +177,6 @@ module axi4_bus (
|
|||||||
.perf_opipsram_wait_o (perf_opipsram_wait_o)
|
.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 #(
|
axi4_error_slave #(
|
||||||
.Response(2'b11)
|
.Response(2'b11)
|
||||||
) u_decode_error_slave (
|
) u_decode_error_slave (
|
||||||
|
|||||||
+589
-52
@@ -8,64 +8,601 @@
|
|||||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
// See the Mulan PSL v2 for more details.
|
// See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
// 32x4KB=128KB
|
`include "axi4_define.svh"
|
||||||
module onchip_ram (
|
`include "mmap_define.svh"
|
||||||
// verilog_format: off -- preserve aligned port declarations
|
|
||||||
input logic clk_i,
|
module onchip_ram #(
|
||||||
ram_if.slave ram
|
parameter bit Present = 1'b1,
|
||||||
|
parameter int unsigned CapacityKiB = 128
|
||||||
|
) (
|
||||||
|
// verilog_format: off -- preserve the AXI, APB, and performance grouping
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_n_i,
|
||||||
|
input logic perf_enable_i,
|
||||||
|
input logic perf_clear_i,
|
||||||
|
axi4_if.slave mem_axi4,
|
||||||
|
apb4_if.slave cfg_apb4
|
||||||
// verilog_format: on
|
// 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];
|
typedef enum logic [2:0] {
|
||||||
logic [31:0] s_rdata[0:31];
|
Idle,
|
||||||
|
ReadData,
|
||||||
|
ReadError,
|
||||||
|
WriteData,
|
||||||
|
WriteDrain,
|
||||||
|
WriteResponse
|
||||||
|
} state_e;
|
||||||
|
|
||||||
// verilog_format: off -- preserve aligned bank decode
|
state_e s_state_d;
|
||||||
assign s_cs[0] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
|
state_e s_state_q;
|
||||||
assign s_cs[1] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
|
logic [2:0] s_state_bits_q;
|
||||||
assign s_cs[2] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
|
logic s_id_d, s_id_q;
|
||||||
assign s_cs[3] = ~ram.addr[14] && ~ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
|
logic [7:0] s_len_d, s_len_q;
|
||||||
assign s_cs[4] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
|
logic [7:0] s_beat_d, s_beat_q;
|
||||||
assign s_cs[5] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
|
logic [2:0] s_size_d, s_size_q;
|
||||||
assign s_cs[6] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
|
logic [1:0] s_burst_d, s_burst_q;
|
||||||
assign s_cs[7] = ~ram.addr[14] && ~ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
|
logic [31:0] s_addr_d, s_addr_q;
|
||||||
assign s_cs[8] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
|
logic [1:0] s_write_resp_d, s_write_resp_q;
|
||||||
assign s_cs[9] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ~ram.addr[11] && ram.addr[10];
|
logic s_read_pending_d, s_read_pending_q;
|
||||||
assign s_cs[10] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ~ram.addr[10];
|
logic [7:0] s_read_rsp_beat_d, s_read_rsp_beat_q;
|
||||||
assign s_cs[11] = ~ram.addr[14] && ram.addr[13] && ~ram.addr[12] && ram.addr[11] && ram.addr[10];
|
logic s_read_rsp_last_d, s_read_rsp_last_q;
|
||||||
assign s_cs[12] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ~ram.addr[10];
|
logic [1:0] s_read_rsp_d, s_read_rsp_q;
|
||||||
assign s_cs[13] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ~ram.addr[11] && ram.addr[10];
|
logic s_read_issue_valid_d, s_read_issue_valid_q;
|
||||||
assign s_cs[14] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ~ram.addr[10];
|
logic [31:0] s_read_issue_addr_d, s_read_issue_addr_q;
|
||||||
assign s_cs[15] = ~ram.addr[14] && ram.addr[13] && ram.addr[12] && ram.addr[11] && ram.addr[10];
|
logic [7:0] s_read_issue_beat_d, s_read_issue_beat_q;
|
||||||
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];
|
|
||||||
|
|
||||||
assign ram.rdata = s_rdata[ram.addr[14:10]];
|
logic [31:0] s_perf_read_requests_d, s_perf_read_requests_q;
|
||||||
// verilog_format: on
|
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;
|
||||||
tc_sram_1024x32 u_ram (
|
logic [32:0] s_aw_last_addr;
|
||||||
.clk_i (clk_i),
|
logic [31:0] s_ar_next_addr;
|
||||||
.cs_i (s_cs[i]),
|
logic [31:0] s_read_next_addr;
|
||||||
.addr_i(ram.addr[9:0]),
|
logic [31:0] s_write_next_addr;
|
||||||
.data_i(ram.wdata),
|
logic s_ar_protocol_legal;
|
||||||
.mask_i(ram.wstrb),
|
logic s_aw_protocol_legal;
|
||||||
.wren_i(|ram.wstrb),
|
logic s_ar_bounds_legal;
|
||||||
.data_o(s_rdata[i])
|
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
|
end
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
s_perf_read_requests_d = s_perf_read_requests_q;
|
||||||
|
s_perf_write_requests_d = s_perf_write_requests_q;
|
||||||
|
s_perf_read_beats_d = s_perf_read_beats_q;
|
||||||
|
s_perf_write_beats_d = s_perf_write_beats_q;
|
||||||
|
s_perf_stall_cycles_d = s_perf_stall_cycles_q;
|
||||||
|
s_perf_err_resps_d = s_perf_err_resps_q;
|
||||||
|
if (perf_clear_i) begin
|
||||||
|
s_perf_read_requests_d = '0;
|
||||||
|
s_perf_write_requests_d = '0;
|
||||||
|
s_perf_read_beats_d = '0;
|
||||||
|
s_perf_write_beats_d = '0;
|
||||||
|
s_perf_stall_cycles_d = '0;
|
||||||
|
s_perf_err_resps_d = '0;
|
||||||
|
end else if (perf_enable_i) begin
|
||||||
|
if (s_ar_accept) begin
|
||||||
|
s_perf_read_requests_d = saturating_increment(s_perf_read_requests_q);
|
||||||
|
end
|
||||||
|
if (s_aw_accept) begin
|
||||||
|
s_perf_write_requests_d = saturating_increment(s_perf_write_requests_q);
|
||||||
|
end
|
||||||
|
if (s_read_accept) begin
|
||||||
|
s_perf_read_beats_d = saturating_increment(s_perf_read_beats_q);
|
||||||
|
end
|
||||||
|
if (s_w_accept) begin
|
||||||
|
s_perf_write_beats_d = saturating_increment(s_perf_write_beats_q);
|
||||||
|
end
|
||||||
|
if (s_stall_event) begin
|
||||||
|
s_perf_stall_cycles_d = saturating_increment(s_perf_stall_cycles_q);
|
||||||
|
end
|
||||||
|
if (s_err_resp_event) begin
|
||||||
|
s_perf_err_resps_d = saturating_increment(s_perf_err_resps_q);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (Present) begin : gen_memory
|
||||||
|
logic [ NumBanks-1:0] s_bank_cs;
|
||||||
|
logic [ 31:0] s_bank_rdata [NumBanks];
|
||||||
|
logic [BankIndexWidth-1:0] s_read_bank_q;
|
||||||
|
|
||||||
|
for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_bank
|
||||||
|
assign s_bank_cs[bank] = (s_mem_read || s_mem_write) && (s_mem_word_addr[14:10] == 5'(bank));
|
||||||
|
tc_sram_1024x32 u_ram (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.cs_i (s_bank_cs[bank]),
|
||||||
|
.addr_i(s_mem_word_addr[9:0]),
|
||||||
|
.data_i(s_mem_wdata),
|
||||||
|
.mask_i(s_mem_wstrb),
|
||||||
|
.wren_i(s_mem_write),
|
||||||
|
.data_o(s_bank_rdata[bank])
|
||||||
|
);
|
||||||
|
end
|
||||||
|
|
||||||
|
dffer #(
|
||||||
|
.DATA_WIDTH(BankIndexWidth)
|
||||||
|
) u_read_bank_dffer (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.en_i (s_mem_read),
|
||||||
|
.dat_i (s_mem_word_addr[BankIndexWidth+9:10]),
|
||||||
|
.dat_o (s_read_bank_q)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign s_mem_rdata = s_bank_rdata[s_read_bank_q];
|
||||||
|
|
||||||
|
`ifdef HAVE_SVA
|
||||||
|
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(s_bank_cs));
|
||||||
|
`endif
|
||||||
|
end else begin : gen_no_memory
|
||||||
|
assign s_mem_rdata = {32{1'b0 & ^{s_mem_read, s_mem_write, s_mem_word_addr,
|
||||||
|
s_mem_wdata, s_mem_wstrb}}};
|
||||||
|
end
|
||||||
|
|
||||||
|
onchip_ram_reg #(
|
||||||
|
.Present (Present),
|
||||||
|
.CapacityKiB(CapacityKiB)
|
||||||
|
) u_reg (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i (rst_n_i),
|
||||||
|
.apb4 (cfg_apb4),
|
||||||
|
.perf_read_requests_i (s_perf_read_requests_q),
|
||||||
|
.perf_write_requests_i (s_perf_write_requests_q),
|
||||||
|
.perf_read_beats_i (s_perf_read_beats_q),
|
||||||
|
.perf_write_beats_i (s_perf_write_beats_q),
|
||||||
|
.perf_stall_cycles_i (s_perf_stall_cycles_q),
|
||||||
|
.perf_error_responses_i(s_perf_err_resps_q)
|
||||||
|
);
|
||||||
|
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(3)
|
||||||
|
) u_state_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_state_d),
|
||||||
|
.dat_o (s_state_bits_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(1)
|
||||||
|
) u_id_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_id_d),
|
||||||
|
.dat_o (s_id_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(8)
|
||||||
|
) u_len_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_len_d),
|
||||||
|
.dat_o (s_len_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(8)
|
||||||
|
) u_beat_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_beat_d),
|
||||||
|
.dat_o (s_beat_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(3)
|
||||||
|
) u_size_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_size_d),
|
||||||
|
.dat_o (s_size_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(2)
|
||||||
|
) u_burst_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_burst_d),
|
||||||
|
.dat_o (s_burst_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_addr_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_addr_d),
|
||||||
|
.dat_o (s_addr_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(2)
|
||||||
|
) u_write_resp_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_write_resp_d),
|
||||||
|
.dat_o (s_write_resp_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(1)
|
||||||
|
) u_read_pending_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_pending_d),
|
||||||
|
.dat_o (s_read_pending_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(8)
|
||||||
|
) u_read_rsp_beat_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_rsp_beat_d),
|
||||||
|
.dat_o (s_read_rsp_beat_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(1)
|
||||||
|
) u_read_rsp_last_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_rsp_last_d),
|
||||||
|
.dat_o (s_read_rsp_last_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(2)
|
||||||
|
) u_read_rsp_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_rsp_d),
|
||||||
|
.dat_o (s_read_rsp_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(1)
|
||||||
|
) u_read_issue_valid_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_issue_valid_d),
|
||||||
|
.dat_o (s_read_issue_valid_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_read_issue_addr_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_issue_addr_d),
|
||||||
|
.dat_o (s_read_issue_addr_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(8)
|
||||||
|
) u_read_issue_beat_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_read_issue_beat_d),
|
||||||
|
.dat_o (s_read_issue_beat_q)
|
||||||
|
);
|
||||||
|
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_read_requests_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_read_requests_d),
|
||||||
|
.dat_o (s_perf_read_requests_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_write_requests_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_write_requests_d),
|
||||||
|
.dat_o (s_perf_write_requests_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_read_beats_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_read_beats_d),
|
||||||
|
.dat_o (s_perf_read_beats_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_write_beats_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_write_beats_d),
|
||||||
|
.dat_o (s_perf_write_beats_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_stall_cycles_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_stall_cycles_d),
|
||||||
|
.dat_o (s_perf_stall_cycles_q)
|
||||||
|
);
|
||||||
|
dffr #(
|
||||||
|
.DATA_WIDTH(32)
|
||||||
|
) u_perf_err_resps_dffr (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.dat_i (s_perf_err_resps_d),
|
||||||
|
.dat_o (s_perf_err_resps_q)
|
||||||
|
);
|
||||||
|
|
||||||
|
`ifdef HAVE_SVA
|
||||||
|
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||||
|
mem_axi4.rvalid && !mem_axi4.rready |=>
|
||||||
|
$stable(
|
||||||
|
{mem_axi4.rid, mem_axi4.rdata, mem_axi4.rresp, mem_axi4.rlast}
|
||||||
|
));
|
||||||
|
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||||
|
mem_axi4.bvalid && !mem_axi4.bready |=>
|
||||||
|
$stable(
|
||||||
|
{mem_axi4.bid, mem_axi4.bresp}
|
||||||
|
));
|
||||||
|
assert property (@(posedge clk_i) disable iff (!rst_n_i) !(s_mem_read && s_mem_write));
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef SYNTHESIS
|
||||||
|
initial begin
|
||||||
|
if ((CapacityKiB != 4) && (CapacityKiB != 16) && (CapacityKiB != 32) &&
|
||||||
|
(CapacityKiB != 64) && (CapacityKiB != 128)) begin
|
||||||
|
$fatal(1, "onchip_ram: CapacityKiB must be 4, 16, 32, 64, or 128");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`endif
|
||||||
endmodule
|
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 clkdiv4_i,
|
||||||
input logic timebase_tick_i,
|
input logic timebase_tick_i,
|
||||||
pll_ctrl_if.sysctrl pll_ctrl,
|
pll_ctrl_if.sysctrl pll_ctrl,
|
||||||
`ifdef HAVE_SRAM_IF
|
|
||||||
ram_if.master ram,
|
|
||||||
`endif
|
|
||||||
gpio_if.soc_pad gpio,
|
gpio_if.soc_pad gpio,
|
||||||
input logic uart_rx_i,
|
input logic uart_rx_i,
|
||||||
output logic uart_tx_o,
|
output logic uart_tx_o,
|
||||||
@@ -48,6 +45,9 @@ module retrosoc (
|
|||||||
// Generated fabric links use the common 32-bit AXI4 contract.
|
// Generated fabric links use the common 32-bit AXI4 contract.
|
||||||
`include "soc_fabric_interfaces.svh"
|
`include "soc_fabric_interfaces.svh"
|
||||||
apb4_if u_sdram_cfg_if (.pclk(clk_i), .presetn(rst_n_i));
|
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))
|
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));
|
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))
|
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 [ `SOC_IRQ_VECTOR_WIDTH-1:0] s_user_irq;
|
||||||
logic s_rtc_wake;
|
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_access_i = s_bus_fault_access;
|
||||||
assign u_sysctrl_if.fault_master_i = s_bus_fault_master;
|
assign u_sysctrl_if.fault_master_i = s_bus_fault_master;
|
||||||
assign u_sysctrl_if.fault_code_i = s_bus_fault_code;
|
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)
|
.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 (
|
axi4_bus u_bus (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (rst_n_i),
|
.rst_n_i (rst_n_i),
|
||||||
`ifdef HAVE_SRAM_IF
|
.sram_axi4 (u_sram_axi4_if),
|
||||||
.ram (ram),
|
|
||||||
`endif
|
|
||||||
.user_bus_enable_i (u_sysctrl_if.user_bus_enable_o),
|
.user_bus_enable_i (u_sysctrl_if.user_bus_enable_o),
|
||||||
.user_bus_idle_o (u_sysctrl_if.user_bus_idle_i),
|
.user_bus_idle_o (u_sysctrl_if.user_bus_idle_i),
|
||||||
`include "soc_bus_fabric.svh"
|
`include "soc_bus_fabric.svh"
|
||||||
@@ -224,6 +240,7 @@ core_wrapper u_core_wrapper (
|
|||||||
.sysctrl (u_sysctrl_if),
|
.sysctrl (u_sysctrl_if),
|
||||||
.pll_ctrl (pll_ctrl),
|
.pll_ctrl (pll_ctrl),
|
||||||
.sdram_cfg (u_sdram_cfg_if),
|
.sdram_cfg (u_sdram_cfg_if),
|
||||||
|
.sram_cfg (u_sram_cfg_if),
|
||||||
.dvp (u_dvp_if),
|
.dvp (u_dvp_if),
|
||||||
.sdio0 (u_sdio0_if),
|
.sdio0 (u_sdio0_if),
|
||||||
.sdio1 (sdio1),
|
.sdio1 (sdio1),
|
||||||
|
|||||||
@@ -82,10 +82,6 @@ module retrosoc_asic (
|
|||||||
(* keep = "true" *)logic s_test_pass;
|
(* keep = "true" *)logic s_test_pass;
|
||||||
(* keep = "true" *)logic [7:0] s_test_code;
|
(* keep = "true" *)logic [7:0] s_test_code;
|
||||||
|
|
||||||
`ifdef HAVE_SRAM_IF
|
|
||||||
ram_if u_ram_if ();
|
|
||||||
`endif
|
|
||||||
|
|
||||||
gpio_if u_gpio_if ();
|
gpio_if u_gpio_if ();
|
||||||
xpi_if u_xpi_if ();
|
xpi_if u_xpi_if ();
|
||||||
sdram_if u_sdram_if ();
|
sdram_if u_sdram_if ();
|
||||||
@@ -115,13 +111,6 @@ rcu #(
|
|||||||
.timebase_tick_o(s_timebase_tick)
|
.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 (
|
retrosoc u_retrosoc (
|
||||||
.clk_i (s_sys_clk),
|
.clk_i (s_sys_clk),
|
||||||
.rst_n_i (s_sys_rst_n),
|
.rst_n_i (s_sys_rst_n),
|
||||||
@@ -131,9 +120,6 @@ rcu #(
|
|||||||
.clkdiv4_i (s_sys_clkdiv4),
|
.clkdiv4_i (s_sys_clkdiv4),
|
||||||
.timebase_tick_i(s_timebase_tick),
|
.timebase_tick_i(s_timebase_tick),
|
||||||
.pll_ctrl (u_pll_ctrl_if),
|
.pll_ctrl (u_pll_ctrl_if),
|
||||||
`ifdef HAVE_SRAM_IF
|
|
||||||
.ram (u_ram_if),
|
|
||||||
`endif
|
|
||||||
.gpio (u_gpio_if),
|
.gpio (u_gpio_if),
|
||||||
.uart_rx_i (s_uart0_rx),
|
.uart_rx_i (s_uart0_rx),
|
||||||
.uart_tx_o (s_uart0_tx),
|
.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_core_wrapper.sv",
|
||||||
"mgmt_debug_reset.sv",
|
"mgmt_debug_reset.sv",
|
||||||
"mgmt_debug_wrapper.sv",
|
"mgmt_debug_wrapper.sv",
|
||||||
|
"onchip_ram_define.svh",
|
||||||
|
"onchip_ram_reg.sv",
|
||||||
"onchip_ram.sv",
|
"onchip_ram.sv",
|
||||||
"pll_rcu_controller.sv",
|
"pll_rcu_controller.sv",
|
||||||
"rcu.sv",
|
"rcu.sv",
|
||||||
"retrosoc.sv",
|
"retrosoc.sv",
|
||||||
"retrosoc_asic.sv",
|
"retrosoc_asic.sv",
|
||||||
|
"retrosoc_core.sv",
|
||||||
"rib2apb.sv",
|
"rib2apb.sv",
|
||||||
"rib2axi4.sv",
|
"rib2axi4.sv",
|
||||||
"rib2ram.sv",
|
"rib2ram.sv",
|
||||||
@@ -360,10 +363,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
"rtl/mini/top/onchip_ram.sv"
|
"rtl/mini/top/onchip_ram.sv",
|
||||||
|
"rtl/mini/top/onchip_ram_reg.sv"
|
||||||
],
|
],
|
||||||
"result": "technology boundary retained",
|
"result": "reviewed AXI and technology-memory boundary",
|
||||||
"rationale": "PDK SRAM cell and pin names remain compatible with the technology library."
|
"rationale": "AXI and APB state uses Common registers; the synchronous bank array retains the stable technology wrapper and PDK SRAM pin boundary."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
|
|||||||
+4
-6
@@ -38,17 +38,15 @@ module tc_sram_1024x32 (
|
|||||||
end
|
end
|
||||||
`elsif PDK_IHP130
|
`elsif PDK_IHP130
|
||||||
`ifdef HAVE_SRAM_MACRO
|
`ifdef HAVE_SRAM_MACRO
|
||||||
logic [63:0] s_rd_data_mem;
|
RM_IHPSG13_1P_1024x32_c2_bm_bist u_mem (
|
||||||
assign data_o = s_rd_data_mem[31:0];
|
|
||||||
RM_IHPSG13_1P_1024x64_c2_bm_bist u_mem (
|
|
||||||
.A_CLK (clk_i),
|
.A_CLK (clk_i),
|
||||||
.A_ADDR (addr_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_MEN (cs_i),
|
||||||
.A_WEN (wren_i),
|
.A_WEN (wren_i),
|
||||||
.A_REN (~wren_i),
|
.A_REN (~wren_i),
|
||||||
.A_DIN ({32'h0, data_i[31:0]}),
|
.A_DIN (data_i),
|
||||||
.A_DOUT (s_rd_data_mem),
|
.A_DOUT (data_o),
|
||||||
.A_DLY (1'b1),
|
.A_DLY (1'b1),
|
||||||
.A_BIST_CLK ('0),
|
.A_BIST_CLK ('0),
|
||||||
.A_BIST_EN ('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"
|
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(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
sys.executable,
|
sys.executable,
|
||||||
@@ -27,6 +27,8 @@ def generate(output_dir: Path, *, sram: str = "NO") -> None:
|
|||||||
str(output_dir),
|
str(output_dir),
|
||||||
"--have-sram-if",
|
"--have-sram-if",
|
||||||
sram,
|
sram,
|
||||||
|
"--sram-size-kib",
|
||||||
|
str(sram_size_kib),
|
||||||
],
|
],
|
||||||
check=True,
|
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_SDIO0_BASE UINT32_C(0x1000F000)" in header
|
||||||
assert "RS_SOC_APB4_SDIO1_BASE UINT32_C(0x10015000)" 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_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_NMI_" not in header
|
||||||
assert "RS_SOC_OPIPSRAM_BASE UINT32_C(0x48000000)" in header
|
assert "RS_SOC_OPIPSRAM_BASE UINT32_C(0x48000000)" in header
|
||||||
assert "RS_SOC_HAS_SRAM 1U" 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
|
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:
|
def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> None:
|
||||||
generate(tmp_path)
|
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 "Other: Zifencei_Zilsd_Xh3BextM_Xh3IRQ" in booter
|
||||||
assert "Not present: U-mode PMP Zcb_Zclsd_Zcmp_Xh3PMPM_Xh3Power" 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 "IRQ: 30 external + software + timer; Xh3IRQ, 4 priority levels" in booter
|
||||||
assert "one shared instruction/data AHB5 manager port" in booter
|
assert "AHB5 manager -> AHB-Lite/AXI4 fabric" in booter
|
||||||
assert "single-beat AXI4 normal access" in booter
|
assert "Single-beat access; no burst or exclusive transactions" in booter
|
||||||
assert "no burst, lock, or global-exclusive forwarding; no system A atomics" in booter
|
|
||||||
assert "RS_SOC_MGMT_JTAG_IDCODE" in booter
|
assert "RS_SOC_MGMT_JTAG_IDCODE" in booter
|
||||||
assert "halted-hart memory commands; system bus access disabled" in booter
|
assert "RISC-V JTAG DTM/DM, IDCODE 0x%08x" in booter
|
||||||
assert "2 exact execute-address hardware breakpoints; no watchpoints" 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 (
|
for parameter in (
|
||||||
".EXTENSION_A (1)",
|
".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