feat: add HP mailbox and PLIC tests; enhance existing test coverage

- Updated axi4_interconnect_tb to include an additional master and issue high-priority access tests.
- Introduced hp_mailbox_tb for testing the HP mailbox functionality, including register and doorbell operations.
- Added plic_tb to validate PLIC priority, claim, and completion mechanisms.
- Enhanced pll_ctrl_tb to initialize additional system control signals.
- Updated soc_irq_topology_tb to account for new IRQ sources and validate IRQ signal behavior.
- Improved sysctrl_tb to include tests for HP lifecycle control and status registers.
- Added tests for HP boot bundle layout, CRC, and payload validation.
- Introduced test_hp_mailbox.py for RTL regression testing of the HP mailbox.
- Added test_hp_platform.py to validate HP platform configuration and generated-source boundaries.
- Enhanced test_librelane_flow.py to ensure correct signal placement in chip configuration.
- Added performance log tests to validate LP and HP coremark ratios.
- Updated test_soc_topology.py to reflect changes in IRQ and peripheral mappings.
This commit is contained in:
maksyuki committed 2026-08-27 19:26:32 +08:00
1 parent f953ef7137
commit 8ba8a0ad47
101 files changed
+4880 -111

No files matched your search

+137 -17
View File
@@ -42,6 +42,8 @@ HAVE_SRAM_MACRO ?= $(if $(filter ICS55,$(PDK)),NO,YES)
SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32) SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32)
PDK_BEHAV ?= NO PDK_BEHAV ?= NO
HAVE_SVA ?= NO HAVE_SVA ?= NO
HAVE_HP ?= NO
HP_CONFIG ?= rv32imafdc_max
BUILD_RELEASE ?= NO BUILD_RELEASE ?= NO
JTAG_IDCODE ?= DEADBEEF JTAG_IDCODE ?= DEADBEEF
EXT_CLK_HZ ?= 72000000 EXT_CLK_HZ ?= 72000000
@@ -61,16 +63,22 @@ RTL_PATH := $(ROOT_PATH)/rtl/mini
RTL_TOP ?= retrosoc_tb RTL_TOP ?= retrosoc_tb
# SW # SW
ISA ?= RV32IM ISA ?= RV32IM
HAVE_CSR ?= NO HAVE_CSR ?= NO
FIRMWARE_NAME ?= retrosoc_fw FIRMWARE_NAME ?= retrosoc_fw
APP ?= shell APP ?= shell
LINK_TYPE ?= ld2_sram LINK_TYPE ?= ld2_sram
COREMARK_MODE ?= quick COREMARK_MODE ?= quick
HP_PERF_MIN_RATIO ?= 2.5
LP_COREMARK_REPORT ?=
HP_COREMARK_REPORT ?=
BUILD_ROOT ?= $(ROOT_PATH)/build BUILD_ROOT ?= $(ROOT_PATH)/build
CACHE_ROOT ?= $(ROOT_PATH)/.cache/retrosoc CACHE_ROOT ?= $(ROOT_PATH)/.cache/retrosoc
VEXIIRISCV_ROOT ?= $(abspath $(ROOT_PATH)/../VexiiRiscv)
SBT ?= sbt
BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%d-%H-%M') BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%d-%H-%M')
BUILD_TIMESTAMP := $(BUILD_TIMESTAMP)
MAX_JOBS ?= 16 MAX_JOBS ?= 16
HOST_CC ?= cc HOST_CC ?= cc
PYTHON ?= python3 PYTHON ?= python3
@@ -87,7 +95,7 @@ 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 SRAM_SIZE_KIB 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 \ HAVE_HP HP_CONFIG 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 \
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \ --lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
@@ -99,11 +107,30 @@ LOCK_DIGEST := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/depend
--lock $(LOCK_FILE) --digest | tail -n 1)) --lock $(LOCK_FILE) --digest | tail -n 1))
CONFIG_DIGEST := $(lastword $(subst -, ,$(VARIANT_ID))) CONFIG_DIGEST := $(lastword $(subst -, ,$(VARIANT_ID)))
export BUILD_TIMESTAMP export BUILD_TIMESTAMP
VARIANT_ROOT := $(abspath $(BUILD_ROOT))/$(VARIANT_ID) VARIANT_ROOT := $(abspath $(BUILD_ROOT))/$(VARIANT_ID)
SW_BUILD_DIR := $(VARIANT_ROOT)/sw SW_BUILD_DIR := $(VARIANT_ROOT)/sw
SIM_TOOL_NAME := $(shell printf '%s' '$(SIMU)' | tr '[:upper:]' '[:lower:]') SIM_TOOL_NAME := $(shell printf '%s' '$(SIMU)' | tr '[:upper:]' '[:lower:]')
SIM_BUILD_ROOT := $(VARIANT_ROOT)/sim/$(SIM_TOOL_NAME) SIM_BUILD_ROOT := $(VARIANT_ROOT)/sim/$(SIM_TOOL_NAME)
META_DIR := $(VARIANT_ROOT)/meta META_DIR := $(VARIANT_ROOT)/meta
HP_GENERATED_DIR := $(VARIANT_ROOT)/generated/vexiiriscv
HP_GENERATED_RTL := $(HP_GENERATED_DIR)/vexii_riscv_hp_generated.v
HP_GENERATED_MANIFEST := $(HP_GENERATED_DIR)/manifest.json
HP_GENERATED_STAMP := $(HP_GENERATED_DIR)/.stamp
HP_LINUX_BUILD_DIR := $(VARIANT_ROOT)/hp-linux
HP_LINUX_STAMP := $(HP_LINUX_BUILD_DIR)/images/.stamp
HP_BOOT_BUNDLE_NAME ?= retrosoc_hp_linux
HP_BOOT_BUNDLE_BIN := $(SW_BUILD_DIR)/$(HP_BOOT_BUNDLE_NAME).bin
HP_BOOT_BUNDLE_HEX := $(SW_BUILD_DIR)/$(HP_BOOT_BUNDLE_NAME).hex
HP_BOOT_BUNDLE_MANIFEST := $(SW_BUILD_DIR)/$(HP_BOOT_BUNDLE_NAME).json
HP_SMOKE_BUILD_DIR := $(VARIANT_ROOT)/hp-smoke
HP_SMOKE_STAMP := $(HP_SMOKE_BUILD_DIR)/images/.stamp
HP_SMOKE_BUNDLE_NAME ?= retrosoc_hp_smoke
HP_SMOKE_BUNDLE_BIN := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).bin
HP_SMOKE_BUNDLE_HEX := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).hex
HP_SMOKE_MANIFEST := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).json
HP_BUILDRT_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/buildroot-hp
HP_LINUX_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/linux-hp
HP_OPENSBI_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/opensbi-hp
ifeq ($(SYNTH_RECIPE),balanced) ifeq ($(SYNTH_RECIPE),balanced)
SYN_BUILD_ROOT := $(VARIANT_ROOT)/syn/yosys SYN_BUILD_ROOT := $(VARIANT_ROOT)/syn/yosys
STA_BUILD_ROOT := $(VARIANT_ROOT)/sta/opensta STA_BUILD_ROOT := $(VARIANT_ROOT)/sta/opensta
@@ -128,8 +155,9 @@ VALID_SYNTH_RECIPE := balanced area speed
VALID_STA := NONE OPENSTA VALID_STA := NONE OPENSTA
VALID_PDK := ICS55 IHP130 SKY130 GF180 VALID_PDK := ICS55 IHP130 SKY130 GF180
VALID_BOOL := YES NO VALID_BOOL := YES NO
VALID_HP_CONFIG := rv32imafdc_max
VALID_ISA := RV32E RV32I RV32IM 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 hp_boot 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 VALID_SRAM_SIZE_KIB := 4 16 32 64 128
@@ -150,6 +178,8 @@ $(call validate_value,HAVE_SRAM_MACRO,$(VALID_BOOL))
$(call validate_value,SRAM_SIZE_KIB,$(VALID_SRAM_SIZE_KIB)) $(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,HAVE_HP,$(VALID_BOOL))
$(call validate_value,HP_CONFIG,$(VALID_HP_CONFIG))
$(call validate_value,BUILD_RELEASE,$(VALID_BOOL)) $(call validate_value,BUILD_RELEASE,$(VALID_BOOL))
$(call validate_value,WAVE,$(VALID_BOOL)) $(call validate_value,WAVE,$(VALID_BOOL))
$(call validate_value,FORMAL,$(VALID_BOOL)) $(call validate_value,FORMAL,$(VALID_BOOL))
@@ -255,6 +285,10 @@ ifeq ($(HAVE_SVA), NO)
DEF_LIST += +define+SV_ASSRT_DISABLE DEF_LIST += +define+SV_ASSRT_DISABLE
endif endif
ifeq ($(HAVE_HP), YES)
DEF_LIST += +define+HAVE_HP
endif
ifeq ($(SYNTH), YOSYS) ifeq ($(SYNTH), YOSYS)
DEF_LIST += +define+SYNTHESIS DEF_LIST += +define+SYNTHESIS
endif endif
@@ -279,15 +313,16 @@ endif
include physical/librelane/Makefile include physical/librelane/Makefile
include physical/ecc/Makefile include physical/ecc/Makefile
.PHONY: help config doctor setup setup-regression setup-mpw setup-clusterip setup-ip setup-pdk setup-app \ .PHONY: help config doctor setup setup-regression setup-mpw setup-clusterip setup-ip setup-pdk setup-app setup-hp-linux hp-linux hp-bundle hp-smoke-bundle hp-smoke-sim \
clean-all purge-cache manifest check-warnings metrics check-metrics package commercial-package \ clean-all purge-cache manifest check-warnings metrics check-metrics package commercial-package \
regress-smoke regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \ regress-smoke regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \
mk-format-check rtl-format rtl-format-check rtl-style-check rtl-migrate-connections rtl-migrate-names sw-policy-check sw-host-test \ mk-format-check rtl-format rtl-format-check rtl-style-check rtl-migrate-connections rtl-migrate-names sw-policy-check sw-host-test \
benchmark-report coremark-report \ benchmark-report coremark-report \
hp-performance-check \
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-onchip-ram 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 vexii-generate
.NOTPARALLEL: setup .NOTPARALLEL: setup
help: help:
@@ -311,6 +346,10 @@ help:
' ecc-package package ECC core views and evidence' \ ' ecc-package package ECC core views and evidence' \
' setup install pinned external dependencies' \ ' setup install pinned external dependencies' \
' setup-regression install pinned dependencies for all PR PDK profiles' \ ' setup-regression install pinned dependencies for all PR PDK profiles' \
' setup-hp-linux install pinned Buildroot, Linux, and OpenSBI sources' \
' hp-linux build the pinned RV32 HP Linux image set' \
' hp-bundle package LP firmware and HP Linux images for flash' \
' hp-smoke-sim run LP release, HP MMIO, and mailbox RTL smoke test' \
' doctor check tools, paths, and selected configuration' \ ' doctor check tools, paths, and selected configuration' \
' config | manifest print/write the effective configuration' \ ' config | manifest print/write the effective configuration' \
' memory-map generate the selected address-map artifacts' \ ' memory-map generate the selected address-map artifacts' \
@@ -320,6 +359,7 @@ help:
' soc-topology generate the selected internal SoC integration artifacts' \ ' soc-topology generate the selected internal SoC integration artifacts' \
' check-soc-topology validate the canonical internal SoC integration map' \ ' check-soc-topology validate the canonical internal SoC integration map' \
' user-extensions generate the selected scalar user-extension bindings' \ ' user-extensions generate the selected scalar user-extension bindings' \
' vexii-generate generate the locked HP VexiiRiscv RTL below build/' \
' check-user-extensions validate the canonical user-extension map' \ ' check-user-extensions validate the canonical user-extension map' \
' 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' \
@@ -328,6 +368,7 @@ help:
' 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' \
' hp-performance-check require measured HP CoreMark/MHz >= 2.5x LP' \
' tech-cell-test test open-PDK technology IO and clock wrappers' \ ' tech-cell-test test open-PDK technology IO and clock wrappers' \
' check-warnings | metrics analyze flow logs and reports' \ ' check-warnings | metrics analyze flow logs and reports' \
' check-metrics apply the committed metrics policy' \ ' check-metrics apply the committed metrics policy' \
@@ -366,7 +407,7 @@ config:
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)' SRAM_SIZE_KIB '$(SRAM_SIZE_KIB)' \ HAVE_SRAM_MACRO '$(HAVE_SRAM_MACRO)' SRAM_SIZE_KIB '$(SRAM_SIZE_KIB)' \
PDK_BEHAV '$(PDK_BEHAV)' HAVE_SVA '$(HAVE_SVA)' \ PDK_BEHAV '$(PDK_BEHAV)' HAVE_SVA '$(HAVE_SVA)' \
BUILD_RELEASE '$(BUILD_RELEASE)' \ HAVE_HP '$(HAVE_HP)' HP_CONFIG '$(HP_CONFIG)' 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)' \
ISA '$(ISA)' HAVE_CSR '$(HAVE_CSR)' APP '$(APP)' \ ISA '$(ISA)' HAVE_CSR '$(HAVE_CSR)' APP '$(APP)' \
@@ -390,6 +431,13 @@ coremark-report: firmware
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/parse_coremark_log.py --log $(PERF_LOG) \ $(FLOW_PYTHON) $(ROOT_PATH)/scripts/parse_coremark_log.py --log $(PERF_LOG) \
--output $(META_DIR)/coremark.json --output $(META_DIR)/coremark.json
hp-performance-check:
@test -n '$(LP_COREMARK_REPORT)' -a -n '$(HP_COREMARK_REPORT)' || { \
echo 'LP_COREMARK_REPORT and HP_COREMARK_REPORT are required' >&2; exit 1; }
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/check_lp_hp_performance.py \
--lp $(LP_COREMARK_REPORT) --hp $(HP_COREMARK_REPORT) \
--minimum-ratio $(HP_PERF_MIN_RATIO) --output $(META_DIR)/lp-hp-performance.json
setup: setup-mpw setup-clusterip setup-ip setup-pdk setup-app setup: setup-mpw setup-clusterip setup-ip setup-pdk setup-app
setup-regression: setup-regression:
@@ -417,6 +465,78 @@ setup-pdk:
setup-app: setup-app:
python3 $(ROOT_PATH)/app/setup.py python3 $(ROOT_PATH)/app/setup.py
setup-hp-linux:
python3 $(ROOT_PATH)/scripts/setup_hp_linux.py
$(HP_LINUX_STAMP): $(ROOT_PATH)/scripts/build_hp_linux.py \
$(ROOT_PATH)/app/ports/linux/configs/retrosoc_hp_defconfig \
$(ROOT_PATH)/app/ports/linux/busybox/retrosoc_hp.config \
$(ROOT_PATH)/app/ports/linux/linux/retrosoc_hp.config \
$(ROOT_PATH)/app/ports/linux/linux/retrosoc_hp.dts \
$(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/Kconfig \
$(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/configs/defconfig \
$(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/objects.mk \
$(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/platform.c \
$(ROOT_PATH)/app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp
@test '$(HAVE_HP)' = YES
python3 $(ROOT_PATH)/scripts/build_hp_linux.py --root $(ROOT_PATH) \
--buildroot $(HP_BUILDRT_ROOT) --linux $(HP_LINUX_ROOT) --opensbi $(HP_OPENSBI_ROOT) \
--output $(HP_LINUX_BUILD_DIR) --jobs $(JOBS)
@touch $@
hp-linux: $(HP_LINUX_STAMP)
$(HP_BOOT_BUNDLE_BIN): $(FIRMWARE_ELF) $(HP_LINUX_STAMP) \
$(ROOT_PATH)/scripts/package_hp_boot.py
python3 $(ROOT_PATH)/scripts/package_hp_boot.py \
--firmware $(SW_BUILD_DIR)/$(FIRMWARE_NAME).bin \
--images $(HP_LINUX_BUILD_DIR)/images --output $@ \
--manifest $(HP_BOOT_BUNDLE_MANIFEST)
$(HP_BOOT_BUNDLE_HEX): $(HP_BOOT_BUNDLE_BIN)
$(OBJC) -I binary -O verilog $< $@
hp-bundle: $(HP_BOOT_BUNDLE_BIN) $(HP_BOOT_BUNDLE_HEX)
$(HP_SMOKE_STAMP): $(ROOT_PATH)/scripts/build_hp_smoke.py \
$(ROOT_PATH)/app/ports/linux/smoke/start.S \
$(ROOT_PATH)/app/ports/linux/smoke/linker.ld
python3 $(ROOT_PATH)/scripts/build_hp_smoke.py \
--source $(ROOT_PATH)/app/ports/linux/smoke/start.S \
--linker $(ROOT_PATH)/app/ports/linux/smoke/linker.ld \
--output $(HP_SMOKE_BUILD_DIR) --cross $(CROSS)
@touch $@
$(HP_SMOKE_BUNDLE_BIN): $(FIRMWARE_ELF) $(HP_SMOKE_STAMP) \
$(ROOT_PATH)/scripts/package_hp_boot.py
python3 $(ROOT_PATH)/scripts/package_hp_boot.py \
--firmware $(SW_BUILD_DIR)/$(FIRMWARE_NAME).bin \
--images $(HP_SMOKE_BUILD_DIR)/images --output $@ \
--manifest $(HP_SMOKE_MANIFEST)
$(HP_SMOKE_BUNDLE_HEX): $(HP_SMOKE_BUNDLE_BIN)
$(OBJC) -I binary -O verilog $< $@
hp-smoke-bundle: $(HP_SMOKE_BUNDLE_BIN) $(HP_SMOKE_BUNDLE_HEX)
hp-smoke-sim: hp-smoke-bundle comp
@test '$(SIMU)' = VERILATOR
$(MAKE) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) SIM_FIRMWARE_NAME=$(HP_SMOKE_BUNDLE_NAME) sim
ifeq ($(HAVE_HP),YES)
$(HP_GENERATED_STAMP): $(ROOT_PATH)/scripts/generate_vexiiriscv.py \
$(ROOT_PATH)/scripts/vexiiriscv/GenerateRetroSocHp.scala $(LOCK_FILE)
python3 $(ROOT_PATH)/scripts/generate_vexiiriscv.py \
--root $(ROOT_PATH) --source $(VEXIIRISCV_ROOT) --output $(HP_GENERATED_DIR) \
--manifest $(HP_GENERATED_MANIFEST) --lock $(LOCK_FILE) --sbt $(SBT)
@touch $@
vexii-generate: $(HP_GENERATED_STAMP)
else
vexii-generate:
@printf '%s\n' 'HAVE_HP=NO; no VexiiRiscv RTL is required'
endif
clean-all: clean-all:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(abspath $(BUILD_ROOT)) python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(abspath $(BUILD_ROOT))
@@ -523,4 +643,4 @@ regress-nightly:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly
sim-asm: asm sim-asm: asm
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
+8
View File
@@ -21,6 +21,11 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
- A fixed Hazard3 management core with a permanent JTAG Debug Module, plus - A fixed Hazard3 management core with a permanent JTAG Debug Module, plus
software-selected user-core and user-IP extension slots. The active user software-selected user-core and user-IP extension slots. The active user
cores are KianV RV32I, SERV, FemtoRV32, and DarkRISCV at slots C0-C3. cores are KianV RV32I, SERV, FemtoRV32, and DarkRISCV at slots C0-C3.
- An experimental IHP130 [LP/HP profile](docs/lp-hp-architecture.md) that keeps
Hazard3 as LP and adds a generated dual-issue RV32IMAFDC VexiiRiscv HP core,
HP CLINT/PLIC, UART1, mailbox, locked OpenSBI/Linux/Buildroot inputs, and a
CRC-checked direct-Linux boot bundle. Linux boot and performance evidence are
release gates, not yet baseline CI claims.
- A documented [Tiny/Mini/Std/Pro product direction](docs/soc-family-positioning.md) - A documented [Tiny/Mini/Std/Pro product direction](docs/soc-family-positioning.md)
anchored by Mini and a trusted Hazard3 management model. The higher Linux, anchored by Mini and a trusted Hazard3 management model. The higher Linux,
graphics, AI, and RV64 configurations are roadmap targets, not supported graphics, AI, and RV64 configurations are roadmap targets, not supported
@@ -82,6 +87,7 @@ because their fixed data placement and SRAM-resident image require the larger ca
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB OpenRAM SRAM. | | [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB OpenRAM SRAM. |
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. | | [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
| [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. | | [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. |
| [`configs/ci/ihp130-hp.mk`](configs/ci/ihp130-hp.mk) | LP RV32IM / HP RV32IMAFDC | `hp_boot` | Experimental fixed-72-MHz LP/HP integration, generated VexiiRiscv RTL, Linux image/bundle flow, and HP RTL validation. |
| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | RV32IM | `coremark` | Fixed four-iteration SRAM CoreMark quick measurement, recorded by nightly IHP130 regression. | | [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | RV32IM | `coremark` | Fixed four-iteration SRAM CoreMark quick measurement, recorded by nightly IHP130 regression. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Compatibility profile for site-specific ICS55 runs. | | [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Compatibility profile for site-specific ICS55 runs. |
@@ -220,6 +226,8 @@ make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG \
| OpenSTA core timing analysis after synthesis | `make CONFIG=configs/ci/ihp130.mk STA=OPENSTA sta` | | OpenSTA core timing analysis after synthesis | `make CONFIG=configs/ci/ihp130.mk STA=OPENSTA sta` |
| Strict Verilator RTL lint | `make CONFIG=configs/ci/ihp130.mk SIMU=VERILATOR HAVE_SVA=YES check-rtl-lint` | | Strict Verilator RTL lint | `make CONFIG=configs/ci/ihp130.mk SIMU=VERILATOR HAVE_SVA=YES check-rtl-lint` |
| Hazard3 CoreMark quick report | `make CONFIG=configs/benchmark/ihp130-hazard3-coremark.mk SIMU=VERILATOR coremark-report` | | Hazard3 CoreMark quick report | `make CONFIG=configs/benchmark/ihp130-hazard3-coremark.mk SIMU=VERILATOR coremark-report` |
| Generate the untracked HP core RTL | `make CONFIG=configs/ci/ihp130-hp.mk vexii-generate` |
| Build and package the HP Linux flash image | `make setup-hp-linux && make CONFIG=configs/ci/ihp130-hp.mk hp-bundle` |
| IHP130 fast smoke suite | `make regress-smoke` | | IHP130 fast smoke suite | `make regress-smoke` |
| Pull-request regression suite | `make regress-pr` | | Pull-request regression suite | `make regress-pr` |
| Nightly regression suite | `make regress-nightly` | | Nightly regression suite | `make regress-nightly` |
+6 -1
View File
@@ -13,7 +13,7 @@ application policy separate from common runtime and HAL code.
| `media/` | WAV, video, and visual-demo components with public media headers. | | `media/` | WAV, video, and visual-demo components with public media headers. |
| `middleware/` | Application middleware configuration and integration, including FatFs. | | `middleware/` | Application middleware configuration and integration, including FatFs. |
| `network/` | Optional network or user-IP integration. | | `network/` | Optional network or user-IP integration. |
| `ports/` | Ports and configuration for external libraries, such as LVGL. | | `ports/` | Ports and configuration for external libraries, including LVGL and the HP Linux/OpenSBI platform. |
| `benchmark/` | Project integration for benchmark workloads, including CoreMark. | | `benchmark/` | Project integration for benchmark workloads, including CoreMark. |
| `asm/` | Standalone assembly firmware used by low-level simulation self-tests. | | `asm/` | Standalone assembly firmware used by low-level simulation self-tests. |
@@ -32,6 +32,7 @@ The build selects an application with `APP=<name>`. The supported profiles are:
| `ci_smoke` | Fast deterministic CI smoke test for UART, ARCHINFO V2, RNG, crypto AES/SHA, SDRAM mapped access, and test-status completion. | | `ci_smoke` | Fast deterministic CI smoke test for UART, ARCHINFO V2, RNG, crypto AES/SHA, SDRAM mapped access, and test-status completion. |
| `coremark` | SRAM-resident Hazard3 CoreMark measurement; use the committed quick or standard profile. | | `coremark` | SRAM-resident Hazard3 CoreMark measurement; use the committed quick or standard profile. |
| `debug` | Minimal SRAM image used only by the Hazard3 OpenOCD/GDB acceptance flow. | | `debug` | Minimal SRAM image used only by the Hazard3 OpenOCD/GDB acceptance flow. |
| `hp_boot` | SRAM-resident LP loader that validates the HP Linux flash bundle, loads SDRAM, releases HP, and monitors the mailbox verdict. |
| `shell` | Interactive application that adds shell services, board drivers, media, FatFs, CoreMark, and UserIP integration. | | `shell` | Interactive application that adds shell services, board drivers, media, FatFs, CoreMark, and UserIP integration. |
| `xpi_flash_loader` | SRAM-resident, GDB-called service image for sector-preserving JTAG programming of the qualified NSS0 NOR. | | `xpi_flash_loader` | SRAM-resident, GDB-called service image for sector-preserving JTAG programming of the qualified NSS0 NOR. |
@@ -60,6 +61,10 @@ make CONFIG=configs/benchmark/ihp130-hazard3-coremark.mk SIMU=VERILATOR coremark
# Hazard3 remote-bitbang, OpenOCD, and GDB acceptance flow # Hazard3 remote-bitbang, OpenOCD, and GDB acceptance flow
make CONFIG=configs/ci/ihp130-debug.mk SIMU=VERILATOR debug-sim make CONFIG=configs/ci/ihp130-debug.mk SIMU=VERILATOR debug-sim
# Build the locked OpenSBI/Linux/initramfs set and LP/HP flash bundle
make setup-hp-linux
make CONFIG=configs/ci/ihp130-hp.mk hp-bundle
# Build the SRAM-resident XPI NOR loader used by the host JTAG tool # Build the SRAM-resident XPI NOR loader used by the host JTAG tool
make CONFIG=configs/ci/ihp130-xpi-flash-loader.mk firmware make CONFIG=configs/ci/ihp130-xpi-flash-loader.mk firmware
``` ```
+2
View File
@@ -0,0 +1,2 @@
APP_SRCS += $(ROOT_PATH)/app/apps/hp_boot/main.c
APP_INC_DIRS += $(ROOT_PATH)/app/apps/hp_boot
+47
View File
@@ -0,0 +1,47 @@
#ifndef RETROSOC_HP_BOOT_BUNDLE_H
#define RETROSOC_HP_BOOT_BUNDLE_H
#include <stdint.h>
#define RS_HP_BOOT_BUNDLE_OFFSET UINT32_C(0x00100000)
#define RS_HP_BOOT_BUNDLE_MAGIC UINT32_C(0x50485352)
#define RS_HP_BOOT_BUNDLE_VERSION UINT32_C(1)
#define RS_HP_BOOT_BUNDLE_ENTRY_COUNT UINT32_C(4)
#define RS_HP_BOOT_BUNDLE_REQUIRED UINT32_C(1)
#define RS_HP_BOOT_TYPE_OPENSBI UINT32_C(1)
#define RS_HP_BOOT_TYPE_DTB UINT32_C(2)
#define RS_HP_BOOT_TYPE_LINUX UINT32_C(3)
#define RS_HP_BOOT_TYPE_INITRAMFS UINT32_C(4)
#define RS_HP_BOOT_OPENSBI_ADDRESS UINT32_C(0x38000000)
#define RS_HP_BOOT_OPENSBI_MAX_SIZE UINT32_C(0x00080000)
#define RS_HP_BOOT_DTB_ADDRESS UINT32_C(0x38080000)
#define RS_HP_BOOT_DTB_MAX_SIZE UINT32_C(0x00010000)
#define RS_HP_BOOT_LINUX_ADDRESS UINT32_C(0x38400000)
#define RS_HP_BOOT_LINUX_MAX_SIZE UINT32_C(0x00C00000)
#define RS_HP_BOOT_INITRAMFS_ADDRESS UINT32_C(0x39000000)
#define RS_HP_BOOT_INITRAMFS_MAX_SIZE UINT32_C(0x00800000)
typedef struct {
uint32_t type;
uint32_t flash_offset;
uint32_t load_address;
uint32_t size;
uint32_t crc32;
uint32_t flags;
} rs_hp_boot_entry_t;
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t header_size;
uint32_t entry_count;
uint32_t total_size;
uint32_t header_crc32;
uint32_t flags;
uint32_t reserved;
rs_hp_boot_entry_t entries[RS_HP_BOOT_BUNDLE_ENTRY_COUNT];
} rs_hp_boot_header_t;
#endif
+210
View File
@@ -0,0 +1,210 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/hp_mailbox.h>
#include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/sysctrl.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/service/test.h>
#include "hp_boot_bundle.h"
#define RS_HP_BOOT_HEADER_SIZE UINT32_C(128)
#define RS_HP_BOOT_READY_EVENT UINT32_C(1)
#define RS_HP_BOOT_READY_ARG UINT32_C(0x4C4E5801)
#define RS_HP_BOOT_READBACK_STRIDE_BYTES UINT32_C(1024)
_Static_assert(sizeof(rs_hp_boot_header_t) == RS_HP_BOOT_HEADER_SIZE,
"HP boot bundle header ABI mismatch");
static uint32_t rs_hp_boot_crc32_byte(uint32_t crc, uint8_t value) {
crc ^= value;
for (uint32_t bit = 0U; bit < 8U; ++bit) {
uint32_t mask = UINT32_C(0) - (crc & UINT32_C(1));
crc = (crc >> 1U) ^ (UINT32_C(0xEDB88320) & mask);
}
return crc;
}
static uint32_t rs_hp_boot_crc32(const uint8_t *data, uint32_t size) {
uint32_t crc = UINT32_C(0xFFFFFFFF);
for (uint32_t index = 0U; index < size; ++index) {
crc = rs_hp_boot_crc32_byte(crc, data[index]);
}
return ~crc;
}
static bool rs_hp_boot_range_valid(uint32_t address, uint32_t size, uint32_t base,
uint32_t capacity) {
return (size != 0U) && (address >= base) && (address < (base + capacity)) &&
((size - 1U) <= ((base + capacity - 1U) - address));
}
static void rs_hp_boot_read_header(rs_hp_boot_header_t *header) {
volatile const uint8_t *source =
(volatile const uint8_t *)(uintptr_t)(RS_SOC_FLASH_BASE + RS_HP_BOOT_BUNDLE_OFFSET);
uint8_t *destination = (uint8_t *)header;
for (uint32_t index = 0U; index < sizeof(*header); ++index) {
destination[index] = source[index];
}
}
static bool rs_hp_boot_entry_expected(const rs_hp_boot_entry_t *entry, uint32_t index,
uint32_t bundle_size) {
static const uint32_t types[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = {
RS_HP_BOOT_TYPE_OPENSBI,
RS_HP_BOOT_TYPE_DTB,
RS_HP_BOOT_TYPE_LINUX,
RS_HP_BOOT_TYPE_INITRAMFS,
};
static const uint32_t addresses[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = {
RS_HP_BOOT_OPENSBI_ADDRESS,
RS_HP_BOOT_DTB_ADDRESS,
RS_HP_BOOT_LINUX_ADDRESS,
RS_HP_BOOT_INITRAMFS_ADDRESS,
};
static const uint32_t maximum_sizes[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = {
RS_HP_BOOT_OPENSBI_MAX_SIZE,
RS_HP_BOOT_DTB_MAX_SIZE,
RS_HP_BOOT_LINUX_MAX_SIZE,
RS_HP_BOOT_INITRAMFS_MAX_SIZE,
};
return (entry->type == types[index]) && (entry->load_address == addresses[index]) &&
(entry->size <= maximum_sizes[index]) &&
((entry->flags & RS_HP_BOOT_BUNDLE_REQUIRED) != 0U) &&
((entry->flash_offset & UINT32_C(3)) == 0U) &&
((entry->load_address & UINT32_C(3)) == 0U) &&
rs_hp_boot_range_valid(entry->flash_offset, entry->size, RS_HP_BOOT_BUNDLE_OFFSET,
bundle_size) &&
rs_hp_boot_range_valid(entry->load_address, entry->size, RS_SOC_SDRAM_BASE,
RS_SOC_SDRAM_SIZE);
}
static bool rs_hp_boot_header_valid(rs_hp_boot_header_t *header) {
uint32_t expected_crc = header->header_crc32;
header->header_crc32 = 0U;
if ((header->magic != RS_HP_BOOT_BUNDLE_MAGIC) ||
(header->version != RS_HP_BOOT_BUNDLE_VERSION) ||
(header->header_size != sizeof(*header)) ||
(header->entry_count != RS_HP_BOOT_BUNDLE_ENTRY_COUNT) ||
((header->flags & RS_HP_BOOT_BUNDLE_REQUIRED) == 0U) ||
(header->total_size < sizeof(*header)) ||
(header->total_size > (RS_SOC_FLASH_SIZE - RS_HP_BOOT_BUNDLE_OFFSET)) ||
(rs_hp_boot_crc32((const uint8_t *)header, sizeof(*header)) != expected_crc)) {
return false;
}
for (uint32_t index = 0U; index < RS_HP_BOOT_BUNDLE_ENTRY_COUNT; ++index) {
if (!rs_hp_boot_entry_expected(&header->entries[index], index, header->total_size)) {
return false;
}
}
return true;
}
static bool rs_hp_boot_copy_entry(const rs_hp_boot_entry_t *entry) {
volatile const uint32_t *source_words =
(volatile const uint32_t *)(uintptr_t)(RS_SOC_FLASH_BASE + entry->flash_offset);
volatile uint32_t *destination_words = (volatile uint32_t *)(uintptr_t)entry->load_address;
uint32_t word_count = entry->size / (uint32_t)sizeof(uint32_t);
uint32_t crc = UINT32_C(0xFFFFFFFF);
bool verify_full = entry->size <= RS_HP_BOOT_READBACK_STRIDE_BYTES;
for (uint32_t index = 0U; index < word_count; ++index) {
uint32_t value = source_words[index];
destination_words[index] = value;
if (verify_full && (destination_words[index] != value)) {
return false;
}
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)value);
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 8U));
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 16U));
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 24U));
}
for (uint32_t index = word_count * (uint32_t)sizeof(uint32_t); index < entry->size; ++index) {
volatile const uint8_t *source_bytes =
(volatile const uint8_t *)(uintptr_t)(RS_SOC_FLASH_BASE + entry->flash_offset);
volatile uint8_t *destination_bytes = (volatile uint8_t *)(uintptr_t)entry->load_address;
uint8_t value = source_bytes[index];
destination_bytes[index] = value;
if (verify_full && (destination_bytes[index] != value)) {
return false;
}
crc = rs_hp_boot_crc32_byte(crc, value);
}
return (~crc) == entry->crc32;
}
static bool rs_hp_boot_wait_sdram(void) {
rs_sdram_status_t status;
for (uint32_t timeout = 0U; timeout < RS_TIMEOUT_DEFAULT; ++timeout) {
if ((rs_sdram_get_status(&status) == RS_OK) && status.ready && !status.init_busy &&
!status.error) {
return true;
}
}
return false;
}
static void rs_hp_boot_fail(uint8_t code) {
(void)rs_sysctrl_set_hp_release(false);
printf("HP_BOOT_FAILED:%u\n", (unsigned int)code);
rs_test_finish(RS_TEST_FAILED, code);
}
int main(void) {
rs_hp_boot_header_t header;
rs_sysctrl_hp_status_t hp_status;
rs_hp_mailbox_message_t message;
if (rs_uart_init(CPU_FREQ * UINT32_C(1000000), UART_BPS) != RS_OK) {
rs_test_finish(RS_TEST_FAILED, UINT8_C(1));
}
if ((rs_sysctrl_set_hp_release(false) != RS_OK) ||
(rs_sysctrl_select_hp_debug(false) != RS_OK) ||
(rs_sysctrl_get_hp_status(&hp_status) != RS_OK) || !hp_status.present ||
!hp_status.reset_asserted) {
rs_hp_boot_fail(UINT8_C(2));
}
if (!rs_hp_boot_wait_sdram()) {
rs_hp_boot_fail(UINT8_C(3));
}
rs_hp_boot_read_header(&header);
if (!rs_hp_boot_header_valid(&header)) {
rs_hp_boot_fail(UINT8_C(4));
}
for (uint32_t index = 0U; index < RS_HP_BOOT_BUNDLE_ENTRY_COUNT; ++index) {
printf("HP_BOOT_LOAD:%u:%u\n", (unsigned int)header.entries[index].type,
(unsigned int)header.entries[index].size);
if (!rs_hp_boot_copy_entry(&header.entries[index])) {
rs_hp_boot_fail((uint8_t)(UINT8_C(5) + (uint8_t)index));
}
}
__asm__ volatile("fence rw, rw" ::: "memory");
if ((rs_hp_mailbox_clear_lp_interrupt() != RS_OK) ||
(rs_sysctrl_set_hp_release(true) != RS_OK) ||
(rs_sysctrl_get_hp_status(&hp_status) != RS_OK) || !hp_status.released) {
rs_hp_boot_fail(UINT8_C(9));
}
printf("HP_BOOT_RELEASED\n");
for (;;) {
if ((rs_hp_mailbox_receive_from_hp(&message) == RS_OK) && (message.sequence != 0U)) {
if ((message.code == RS_HP_BOOT_READY_EVENT) &&
(message.argument == RS_HP_BOOT_READY_ARG)) {
printf("HP_LINUX_READY\n");
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
}
rs_hp_boot_fail(UINT8_C(10));
}
}
}
+1
View File
@@ -0,0 +1 @@
+21
View File
@@ -0,0 +1,21 @@
# HP Linux Port
This directory owns the Buildroot external tree, device tree, kernel config,
and integration metadata for the fixed RV32 VexiiRiscv HP core. Linux,
OpenSBI, Buildroot, toolchains, and all generated images remain below `.cache/`
or `build/` and are not tracked.
The MVP uses OpenSBI `FW_JUMP` without U-Boot. Its fixed SDRAM layout is:
| Artifact | Address | Maximum size |
| --- | --- | --- |
| OpenSBI | `0x38000000` | 512 KiB |
| DTB | `0x38080000` | 64 KiB |
| Linux Image | `0x38400000` | 12 MiB |
| initramfs | `0x39000000` | 8 MiB |
Run `make setup-hp-linux` to install the locked sources, then use the
`hp-linux` target from an `HAVE_HP=YES` committed profile. The initial Linux
console uses SBI; the UART1 and mailbox nodes reserve their stable platform
ABI for the native drivers.
@@ -0,0 +1 @@
CONFIG_DEVMEM=y
@@ -0,0 +1,18 @@
BR2_riscv=y
BR2_RISCV_32=y
BR2_RISCV_ISA_RVM=y
BR2_RISCV_ISA_RVA=y
BR2_RISCV_ISA_RVF=y
BR2_RISCV_ISA_RVD=y
BR2_RISCV_ISA_RVC=y
BR2_RISCV_ABI_ILP32D=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_TARGET_GENERIC_HOSTNAME="retrosoc-hp"
BR2_TARGET_GENERIC_ISSUE="retroSoC HP Linux"
BR2_INIT_BUSYBOX=y
BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_RETROSOC_HP_PATH)/busybox/retrosoc_hp.config"
BR2_TARGET_GENERIC_GETTY=y
BR2_TARGET_GENERIC_GETTY_PORT="hvc0"
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_GZIP=y
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RETROSOC_HP_PATH)/rootfs-overlay"
+3
View File
@@ -0,0 +1,3 @@
name: RETROSOC_HP
desc: retroSoC RV32 HP Linux system
+1
View File
@@ -0,0 +1 @@
+17
View File
@@ -0,0 +1,17 @@
CONFIG_ARCH_RV32I=y
CONFIG_NONPORTABLE=y
CONFIG_RISCV_ISA_M=y
CONFIG_RISCV_ISA_A=y
CONFIG_RISCV_ISA_C=y
CONFIG_FPU=y
CONFIG_SMP=n
CONFIG_HVC_RISCV_SBI=y
CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
CONFIG_SIFIVE_PLIC=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_RD_GZIP=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DEVMEM=y
CONFIG_TMPFS=y
CONFIG_PRINTK_TIME=y
+83
View File
@@ -0,0 +1,83 @@
/dts-v1/;
/ {
compatible = "retrosoc,mini-hp";
#address-cells = <1>;
#size-cells = <1>;
chosen {
bootargs = "console=hvc0 earlycon=sbi rdinit=/sbin/init";
linux,initrd-start = <0x39000000>;
/* build_hp_linux.py replaces this with the actual rootfs end. */
linux,initrd-end = <0x39000000>;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <1000000>;
cpu@1 {
compatible = "spinal,vexiiriscv", "riscv";
device_type = "cpu";
reg = <1>;
clock-frequency = <72000000>;
mmu-type = "riscv,sv32";
riscv,isa-base = "rv32i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr",
"zifencei";
hp_cpu_intc: interrupt-controller {
compatible = "riscv,cpu-intc";
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
};
memory@38000000 {
device_type = "memory";
reg = <0x38000000 0x04000000>;
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
hp_aclint: timer@2000000 {
compatible = "sifive,clint0";
reg = <0x02000000 0x00010000>;
interrupts-extended = <&hp_cpu_intc 3>, <&hp_cpu_intc 7>;
};
hp_plic: interrupt-controller@c000000 {
compatible = "sifive,plic-1.0.0";
reg = <0x0c000000 0x04000000>;
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
riscv,ndev = <31>;
interrupts-extended = <&hp_cpu_intc 11>, <&hp_cpu_intc 9>;
};
uart1: serial@10018000 {
compatible = "retrosoc,uart-v3";
reg = <0x10018000 0x1000>;
interrupt-parent = <&hp_plic>;
interrupts = <1>;
clock-frequency = <72000000>;
status = "okay";
};
hp_mailbox: mailbox@10019000 {
compatible = "retrosoc,hp-mailbox-v1";
reg = <0x10019000 0x1000>;
interrupt-parent = <&hp_plic>;
interrupts = <2>;
#mbox-cells = <1>;
};
};
};
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: BSD-2-Clause
config PLATFORM_RETROSOC_HP
bool
select IPI_MSWI
select TIMER_MTIMER
default y
Whitespace-only changes.
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: BSD-2-Clause
platform-objs-y += platform.o
@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (c) 2026 Yuchi Miao */
#include <sbi/riscv_io.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_platform.h>
#include <sbi_utils/ipi/aclint_mswi.h>
#include <sbi_utils/timer/aclint_mtimer.h>
#define RETROSOC_HP_ACLINT_BASE 0x02000000UL
#define RETROSOC_HP_UART_BASE 0x10018000UL
#define RETROSOC_HP_UART_BAUD_INT 78U
#define RETROSOC_HP_UART_BAUD_FRAC 32U
#define RETROSOC_HP_UART_BAUD_INT_REG 0x00UL
#define RETROSOC_HP_UART_BAUD_FRAC_REG 0x04UL
#define RETROSOC_HP_UART_LINE_CTRL_REG 0x08UL
#define RETROSOC_HP_UART_CTRL_REG 0x0CUL
#define RETROSOC_HP_UART_TXDATA_REG 0x10UL
#define RETROSOC_HP_UART_RXDATA_REG 0x14UL
#define RETROSOC_HP_UART_STATUS_REG 0x18UL
#define RETROSOC_HP_UART_FIFO_CTRL_REG 0x20UL
#define RETROSOC_HP_UART_ERROR_REG 0x30UL
#define RETROSOC_HP_UART_INTR_ENABLE_REG 0x38UL
#define RETROSOC_HP_UART_TX_FULL 0x20U
#define RETROSOC_HP_UART_RX_EMPTY 0x40U
#define RETROSOC_HP_UART_ENABLE 0x03U
#define RETROSOC_HP_UART_FIFO_FLUSH 0x03U
#define RETROSOC_HP_UART_ERROR_ALL 0x7FU
static const u32 s_hart_index_to_id[] = {1U};
static struct aclint_mswi_data s_mswi = {
.addr = RETROSOC_HP_ACLINT_BASE,
.size = ACLINT_MSWI_SIZE,
.first_hartid = 0U,
.hart_count = 2U,
};
static struct aclint_mtimer_data s_mtimer = {
.mtime_freq = 1000000UL,
.mtime_addr = RETROSOC_HP_ACLINT_BASE + 0xBFF8UL,
.mtime_size = ACLINT_DEFAULT_MTIME_SIZE,
.mtimecmp_addr = RETROSOC_HP_ACLINT_BASE + 0x4000UL,
.mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE,
.first_hartid = 0U,
.hart_count = 2U,
.has_64bit_mmio = false,
};
static void retrosoc_hp_uart_write(u32 offset, u32 value) {
writel(value, (void *)(RETROSOC_HP_UART_BASE + offset));
}
static u32 retrosoc_hp_uart_read(u32 offset) {
return readl((const void *)(RETROSOC_HP_UART_BASE + offset));
}
static void retrosoc_hp_console_putc(char value) {
while ((retrosoc_hp_uart_read(RETROSOC_HP_UART_STATUS_REG) & RETROSOC_HP_UART_TX_FULL) != 0U) {
}
retrosoc_hp_uart_write(RETROSOC_HP_UART_TXDATA_REG, (u32)(unsigned char)value);
}
static int retrosoc_hp_console_getc(void) {
if ((retrosoc_hp_uart_read(RETROSOC_HP_UART_STATUS_REG) & RETROSOC_HP_UART_RX_EMPTY) != 0U) {
return -1;
}
return (int)(retrosoc_hp_uart_read(RETROSOC_HP_UART_RXDATA_REG) & 0xFFU);
}
static struct sbi_console_device s_console = {
.name = "retrosoc-uart1",
.console_putc = retrosoc_hp_console_putc,
.console_getc = retrosoc_hp_console_getc,
};
static int retrosoc_hp_early_init(bool cold_boot) {
if (!cold_boot) {
return 0;
}
retrosoc_hp_uart_write(RETROSOC_HP_UART_CTRL_REG, 0U);
retrosoc_hp_uart_write(RETROSOC_HP_UART_FIFO_CTRL_REG, RETROSOC_HP_UART_FIFO_FLUSH);
retrosoc_hp_uart_write(RETROSOC_HP_UART_ERROR_REG, RETROSOC_HP_UART_ERROR_ALL);
retrosoc_hp_uart_write(RETROSOC_HP_UART_INTR_ENABLE_REG, 0U);
retrosoc_hp_uart_write(RETROSOC_HP_UART_BAUD_INT_REG, RETROSOC_HP_UART_BAUD_INT);
retrosoc_hp_uart_write(RETROSOC_HP_UART_BAUD_FRAC_REG, RETROSOC_HP_UART_BAUD_FRAC);
retrosoc_hp_uart_write(RETROSOC_HP_UART_LINE_CTRL_REG, 3U);
retrosoc_hp_uart_write(RETROSOC_HP_UART_CTRL_REG, RETROSOC_HP_UART_ENABLE);
sbi_console_set_device(&s_console);
return aclint_mswi_cold_init(&s_mswi);
}
static int retrosoc_hp_timer_init(void) {
return aclint_mtimer_cold_init(&s_mtimer, NULL);
}
static const struct sbi_platform_operations s_platform_operations = {
.early_init = retrosoc_hp_early_init,
.timer_init = retrosoc_hp_timer_init,
};
const struct sbi_platform platform = {
.opensbi_version = OPENSBI_VERSION,
.platform_version = SBI_PLATFORM_VERSION(1U, 0U),
.name = "retroSoC RV32 HP",
.features = SBI_PLATFORM_DEFAULT_FEATURES,
.hart_count = 1U,
.hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
.heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(1U),
.platform_ops_addr = (unsigned long)&s_platform_operations,
.hart_index2id = s_hart_index_to_id,
};
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
case "$1" in
start)
echo "retroSoC HP Linux ready"
devmem 0x10019020 32 0x00000001
devmem 0x10019024 32 0x4C4E5801
devmem 0x10019028 32 0x00000001
devmem 0x1001902C 32 0x00000001
;;
esac
+12
View File
@@ -0,0 +1,12 @@
ENTRY(_start)
SECTIONS
{
. = 0x38000000;
.text :
{
KEEP(*(.text.start))
*(.text*)
*(.rodata*)
}
}
+68
View File
@@ -0,0 +1,68 @@
.section .text.start
.global _start
.type _start, @function
.equ UART1_BASE, 0x10018000
.equ UART_BAUD_INT, 0x00
.equ UART_BAUD_FRAC, 0x04
.equ UART_LINE_CTRL, 0x08
.equ UART_CTRL, 0x0c
.equ UART_TXDATA, 0x10
.equ UART_STATUS, 0x18
.equ UART_FIFO_CTRL, 0x20
.equ UART_ERROR, 0x30
.equ UART_INTR_ENABLE, 0x38
.equ UART_TX_FULL, 0x20
.equ MAILBOX_BASE, 0x10019000
.equ HP_EVENT, 0x20
.equ HP_ARG0, 0x24
.equ HP_SEQUENCE, 0x28
.equ HP_DOORBELL, 0x2c
.equ HP_READY_ARG, 0x4c4e5801
_start:
li t0, UART1_BASE
sw zero, UART_CTRL(t0)
li t1, 3
sw t1, UART_FIFO_CTRL(t0)
li t1, 0x7f
sw t1, UART_ERROR(t0)
sw zero, UART_INTR_ENABLE(t0)
li t1, 78
sw t1, UART_BAUD_INT(t0)
li t1, 32
sw t1, UART_BAUD_FRAC(t0)
li t1, 3
sw t1, UART_LINE_CTRL(t0)
sw t1, UART_CTRL(t0)
la a0, hp_message
write_character:
lbu a1, 0(a0)
beqz a1, publish_ready
wait_tx_space:
lw a2, UART_STATUS(t0)
andi a2, a2, UART_TX_FULL
bnez a2, wait_tx_space
sw a1, UART_TXDATA(t0)
addi a0, a0, 1
j write_character
publish_ready:
li t0, MAILBOX_BASE
li t1, 1
sw t1, HP_EVENT(t0)
li t1, HP_READY_ARG
sw t1, HP_ARG0(t0)
li t1, 1
sw t1, HP_SEQUENCE(t0)
fence w, w
sw t1, HP_DOORBELL(t0)
halt:
j halt
.section .rodata
hp_message:
.asciz "HP_SMOKE_READY\n"
+5
View File
@@ -12,6 +12,11 @@ profiles. The `ihp130-hazard3-coremark.mk` profile is the automated SRAM
CoreMark quick measurement; its `-standard` counterpart is reserved for a CoreMark quick measurement; its `-standard` counterpart is reserved for a
10-second hardware run. The nightly workflow reuses the IHP130 CI profile and 10-second hardware run. The nightly workflow reuses the IHP130 CI profile and
runs the quick CoreMark profile. runs the quick CoreMark profile.
`ci/ihp130-hp.mk` is the experimental asymmetric Linux profile. It fixes both
LP and HP at 72 MHz, runs `hp_boot` entirely from 32 KiB on-chip SRAM, and
generates VexiiRiscv RTL only below the selected build variant. It is not part
of the supported PR matrix until Linux boot, HP performance, synthesis, and
timing evidence are qualified.
`ci/ihp130-xpi-flash-loader.mk` builds the SRAM-only XPI NOR service image used `ci/ihp130-xpi-flash-loader.mk` builds the SRAM-only XPI NOR service image used
by GDB/OpenOCD; it is a programming utility, not a normal boot application. 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
+20
View File
@@ -0,0 +1,20 @@
SOC := MINI
PDK := IHP130
HAVE_PLL := NO
HAVE_HP := YES
HP_CONFIG := rv32imafdc_max
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
PDK_BEHAV := YES
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
CLINT_TIMEBASE_HZ := 1000000
ISA := RV32IM
HAVE_CSR := YES
APP := hp_boot
LINK_TYPE := ld2_all_sram
SIM_SUCCESS_MARKER := SIM_TEST_PASS
RTL_SIM_TIMEOUT := 20000000
FIRMWARE_NAME := retrosoc_hp_boot
SIM_FIRMWARE_NAME := retrosoc_hp_boot
+22
View File
@@ -0,0 +1,22 @@
#ifndef RETROSOC_HAL_HP_MAILBOX_H
#define RETROSOC_HAL_HP_MAILBOX_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
typedef struct {
uint32_t code;
uint32_t argument;
uint32_t sequence;
} rs_hp_mailbox_message_t;
rs_status_t rs_hp_mailbox_probe(void);
rs_status_t rs_hp_mailbox_send_to_hp(const rs_hp_mailbox_message_t *message);
rs_status_t rs_hp_mailbox_receive_from_hp(rs_hp_mailbox_message_t *message);
rs_status_t rs_hp_mailbox_enable_lp_interrupt(bool enable);
rs_status_t rs_hp_mailbox_lp_interrupt_pending(bool *pending);
rs_status_t rs_hp_mailbox_clear_lp_interrupt(void);
#endif
+9
View File
@@ -50,6 +50,12 @@ typedef struct {
bool capable; bool capable;
} rs_sysctrl_pll_status_t; } rs_sysctrl_pll_status_t;
typedef struct {
bool present;
bool released;
bool reset_asserted;
} rs_sysctrl_hp_status_t;
rs_status_t rs_sysctrl_get_core_select(uint8_t *core_id); rs_status_t rs_sysctrl_get_core_select(uint8_t *core_id);
rs_status_t rs_sysctrl_set_core_select(uint8_t core_id); rs_status_t rs_sysctrl_set_core_select(uint8_t core_id);
rs_status_t rs_sysctrl_get_ip_select(uint8_t *ip_id); rs_status_t rs_sysctrl_get_ip_select(uint8_t *ip_id);
@@ -70,5 +76,8 @@ void rs_sysctrl_write_test_status(bool pass, uint8_t code);
rs_status_t rs_sysctrl_get_test_status(bool *done, bool *pass, uint8_t *code); rs_status_t rs_sysctrl_get_test_status(bool *done, bool *pass, uint8_t *code);
rs_status_t rs_sysctrl_get_rtc_wake_status(bool *live, bool *seen); rs_status_t rs_sysctrl_get_rtc_wake_status(bool *live, bool *seen);
rs_status_t rs_sysctrl_clear_rtc_wake(void); rs_status_t rs_sysctrl_clear_rtc_wake(void);
rs_status_t rs_sysctrl_set_hp_release(bool release);
rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status);
rs_status_t rs_sysctrl_select_hp_debug(bool select_hp);
#endif #endif
+77
View File
@@ -0,0 +1,77 @@
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/hp_mailbox.h>
#define RS_HP_MAILBOX_IP_VERSION_OFFSET UINT32_C(0x000)
#define RS_HP_MAILBOX_CAPABILITY_OFFSET UINT32_C(0x004)
#define RS_HP_MAILBOX_LP_COMMAND_OFFSET UINT32_C(0x010)
#define RS_HP_MAILBOX_LP_ARG0_OFFSET UINT32_C(0x014)
#define RS_HP_MAILBOX_LP_SEQUENCE_OFFSET UINT32_C(0x018)
#define RS_HP_MAILBOX_LP_DOORBELL_OFFSET UINT32_C(0x01C)
#define RS_HP_MAILBOX_HP_EVENT_OFFSET UINT32_C(0x020)
#define RS_HP_MAILBOX_HP_ARG0_OFFSET UINT32_C(0x024)
#define RS_HP_MAILBOX_HP_SEQUENCE_OFFSET UINT32_C(0x028)
#define RS_HP_MAILBOX_LP_INTR_STATE_OFFSET UINT32_C(0x030)
#define RS_HP_MAILBOX_LP_INTR_ENABLE_OFFSET UINT32_C(0x034)
#define RS_HP_MAILBOX_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_HP_MAILBOX_CAPABILITY_VALUE UINT32_C(0x00000007)
static volatile uint32_t *rs_hp_mailbox_register(uint32_t offset) {
return (volatile uint32_t *)(uintptr_t)(RS_SOC_APB4_HP_MAILBOX_BASE + offset);
}
rs_status_t rs_hp_mailbox_probe(void) {
return ((*rs_hp_mailbox_register(RS_HP_MAILBOX_IP_VERSION_OFFSET) ==
RS_HP_MAILBOX_IP_VERSION_VALUE) &&
(*rs_hp_mailbox_register(RS_HP_MAILBOX_CAPABILITY_OFFSET) ==
RS_HP_MAILBOX_CAPABILITY_VALUE))
? RS_OK
: RS_ENOTSUP;
}
rs_status_t rs_hp_mailbox_send_to_hp(const rs_hp_mailbox_message_t *message) {
if ((message == NULL) || (message->sequence == 0U)) {
return RS_EINVAL;
}
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_COMMAND_OFFSET) = message->code;
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_ARG0_OFFSET) = message->argument;
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_SEQUENCE_OFFSET) = message->sequence;
__asm__ volatile("fence w, w" ::: "memory");
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_DOORBELL_OFFSET) = UINT32_C(1);
return RS_OK;
}
rs_status_t rs_hp_mailbox_receive_from_hp(rs_hp_mailbox_message_t *message) {
uint32_t sequence;
if (message == NULL) {
return RS_EINVAL;
}
sequence = *rs_hp_mailbox_register(RS_HP_MAILBOX_HP_SEQUENCE_OFFSET);
__asm__ volatile("fence r, r" ::: "memory");
message->code = *rs_hp_mailbox_register(RS_HP_MAILBOX_HP_EVENT_OFFSET);
message->argument = *rs_hp_mailbox_register(RS_HP_MAILBOX_HP_ARG0_OFFSET);
message->sequence = sequence;
return RS_OK;
}
rs_status_t rs_hp_mailbox_enable_lp_interrupt(bool enable) {
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_INTR_ENABLE_OFFSET) =
enable ? UINT32_C(1) : UINT32_C(0);
return RS_OK;
}
rs_status_t rs_hp_mailbox_lp_interrupt_pending(bool *pending) {
if (pending == NULL) {
return RS_EINVAL;
}
*pending = (*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_INTR_STATE_OFFSET) & UINT32_C(1)) != 0U;
return RS_OK;
}
rs_status_t rs_hp_mailbox_clear_lp_interrupt(void) {
*rs_hp_mailbox_register(RS_HP_MAILBOX_LP_INTR_STATE_OFFSET) = UINT32_C(1);
return RS_OK;
}
+47
View File
@@ -40,6 +40,9 @@
#define RS_SYSCTRL_PERF_FLASH_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_FLASH_WAIT_HI_OFFSET #define RS_SYSCTRL_PERF_FLASH_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_FLASH_WAIT_HI_OFFSET
#define RS_SYSCTRL_TEST_STATUS_OFFSET RS_SOC_SYSCTRL_TEST_STATUS_OFFSET #define RS_SYSCTRL_TEST_STATUS_OFFSET RS_SOC_SYSCTRL_TEST_STATUS_OFFSET
#define RS_SYSCTRL_RTC_WAKE_STATUS_OFFSET RS_SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET #define RS_SYSCTRL_RTC_WAKE_STATUS_OFFSET RS_SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET
#define RS_SYSCTRL_HP_CTRL_OFFSET RS_SOC_SYSCTRL_HP_CTRL_OFFSET
#define RS_SYSCTRL_HP_STATUS_OFFSET RS_SOC_SYSCTRL_HP_STATUS_OFFSET
#define RS_SYSCTRL_DEBUG_SELECT_OFFSET RS_SOC_SYSCTRL_DEBUG_SELECT_OFFSET
#define RS_SYSCTRL_PLL_CMD_APPLY UINT32_C(0x00000001) #define RS_SYSCTRL_PLL_CMD_APPLY UINT32_C(0x00000001)
#define RS_SYSCTRL_PLL_CMD_CLEAR_ERROR UINT32_C(0x00000002) #define RS_SYSCTRL_PLL_CMD_CLEAR_ERROR UINT32_C(0x00000002)
@@ -66,6 +69,9 @@
#define RS_SYSCTRL_PLL_CAPABLE UINT32_C(0x00000400) #define RS_SYSCTRL_PLL_CAPABLE UINT32_C(0x00000400)
#define RS_SYSCTRL_RTC_WAKE_LIVE UINT32_C(0x00000001) #define RS_SYSCTRL_RTC_WAKE_LIVE UINT32_C(0x00000001)
#define RS_SYSCTRL_RTC_WAKE_SEEN UINT32_C(0x00000002) #define RS_SYSCTRL_RTC_WAKE_SEEN UINT32_C(0x00000002)
#define RS_SYSCTRL_HP_PRESENT UINT32_C(0x00000001)
#define RS_SYSCTRL_HP_RELEASED UINT32_C(0x00000002)
#define RS_SYSCTRL_HP_RESET_ASSERTED UINT32_C(0x00000004)
static volatile uint32_t *rs_sysctrl_register(uint32_t offset) { static volatile uint32_t *rs_sysctrl_register(uint32_t offset) {
return (volatile uint32_t *)(RS_SOC_APB4_SYSCTRL_BASE + (uintptr_t)offset); return (volatile uint32_t *)(RS_SOC_APB4_SYSCTRL_BASE + (uintptr_t)offset);
@@ -253,6 +259,47 @@ rs_status_t rs_sysctrl_clear_fault(void) {
return RS_OK; return RS_OK;
} }
rs_status_t rs_sysctrl_set_hp_release(bool release) {
rs_sysctrl_hp_status_t status;
if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) {
return RS_ENOTSUP;
}
*rs_sysctrl_register(RS_SYSCTRL_HP_CTRL_OFFSET) = release ? UINT32_C(1) : UINT32_C(0);
if (rs_sysctrl_get_hp_status(&status) != RS_OK) {
return RS_EIO;
}
return (status.released == release) && (status.reset_asserted != release) ? RS_OK : RS_EIO;
}
rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) {
uint32_t value;
if (status == NULL) {
return RS_EINVAL;
}
value = *rs_sysctrl_register(RS_SYSCTRL_HP_STATUS_OFFSET);
status->present = (value & RS_SYSCTRL_HP_PRESENT) != 0U;
status->released = (value & RS_SYSCTRL_HP_RELEASED) != 0U;
status->reset_asserted = (value & RS_SYSCTRL_HP_RESET_ASSERTED) != 0U;
return RS_OK;
}
rs_status_t rs_sysctrl_select_hp_debug(bool select_hp) {
rs_sysctrl_hp_status_t status;
uint32_t value;
if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) {
return RS_ENOTSUP;
}
if (status.released) {
return RS_EIO;
}
*rs_sysctrl_register(RS_SYSCTRL_DEBUG_SELECT_OFFSET) = select_hp ? UINT32_C(1) : UINT32_C(0);
value = *rs_sysctrl_register(RS_SYSCTRL_DEBUG_SELECT_OFFSET) & UINT32_C(1);
return (value == (select_hp ? UINT32_C(1) : UINT32_C(0))) ? RS_OK : RS_EIO;
}
rs_status_t rs_sysctrl_set_perf_control(bool enable, bool clear, bool snapshot) { rs_status_t rs_sysctrl_set_perf_control(bool enable, bool clear, bool snapshot) {
uint32_t control = 0U; uint32_t control = 0U;
+6
View File
@@ -20,3 +20,9 @@ The locked SKY130 OpenRAM SRAM archive is generated and published by the
manifest, geometry, source revisions, generated-file hashes, and TT/SS views manifest, geometry, source revisions, generated-file hashes, and TT/SS views
before materializing it below `.cache/retrosoc/pdk/sky130/openram/`. Generated before materializing it below `.cache/retrosoc/pdk/sky130/openram/`. Generated
Verilog, Liberty, LEF, GDS, and SPICE views are never committed here. Verilog, Liberty, LEF, GDS, and SPICE views are never committed here.
The HP profile additionally locks VexiiRiscv, OpenSBI, Linux stable, and
Buildroot source revisions. `make setup-hp-linux` installs the software sources
below `.cache/retrosoc/sources/`; VexiiRiscv may be supplied through
`VEXIIRISCV_ROOT`, but its revision is still checked before generated RTL is
accepted. No generated CPU RTL or Linux build output belongs in Git.
+26 -1
View File
@@ -13,6 +13,31 @@
"destination": "rtl/managed/hazard3", "destination": "rtl/managed/hazard3",
"license": "NOASSERTION" "license": "NOASSERTION"
}, },
"vexiiriscv": {
"url": "https://github.com/SpinalHDL/VexiiRiscv.git",
"revision": "ef7da4134ff11ec6a681d0632123b232b853b046",
"destination": ".cache/retrosoc/sources/vexiiriscv",
"recursive": true,
"license": "MIT"
},
"opensbi_hp": {
"url": "https://github.com/riscv-software-src/opensbi.git",
"revision": "cbf9f6734dd85a982c63e3cb5db7ffe09da839ca",
"destination": ".cache/retrosoc/sources/opensbi-hp",
"license": "BSD-2-Clause"
},
"linux_hp": {
"url": "https://github.com/gregkh/linux.git",
"revision": "14c37ff05f22da2fa7076d10f6a07c7ede330c83",
"destination": ".cache/retrosoc/sources/linux-hp",
"license": "GPL-2.0-only"
},
"buildroot_hp": {
"url": "https://gitlab.com/buildroot.org/buildroot.git",
"revision": "cb857ba4c87a93e5265a9e4a3f32071abf39e14a",
"destination": ".cache/retrosoc/sources/buildroot-hp",
"license": "GPL-2.0-or-later"
},
"ibex": { "ibex": {
"url": "https://github.com/lowRISC/ibex.git", "url": "https://github.com/lowRISC/ibex.git",
"revision": "0c233f54361d769f370889223acc456f2ac19d46", "revision": "0c233f54361d769f370889223acc456f2ac19d46",
@@ -61,7 +86,7 @@
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"cluster_archinfo": {"url": "https://github.com/retroSoC/archinfo.git", "revision": "78505969a0afdbc887cdd9b36eda228b4afb616e", "destination": "rtl/managed/clusterip/archinfo", "license": "MulanPSL-2.0"}, "cluster_archinfo": {"url": "https://github.com/retroSoC/archinfo.git", "revision": "78505969a0afdbc887cdd9b36eda228b4afb616e", "destination": "rtl/managed/clusterip/archinfo", "license": "MulanPSL-2.0"},
"cluster_common": {"url": "https://github.com/retroSoC/common.git", "revision": "5604bd5899bcc3a937cef55db4bac3b5102e562c", "destination": "rtl/managed/clusterip/common", "license": "NOASSERTION"}, "cluster_common": {"url": "https://github.com/retroSoC/common.git", "revision": "964a54d70cb78e394ba9a25c05f2aa2b02941fb7", "destination": "rtl/managed/clusterip/common", "license": "NOASSERTION"},
"cluster_crc": {"url": "https://github.com/retroSoC/crc.git", "revision": "8c1a5664c4d23d005c1afad432372a13ca967cc8", "destination": "rtl/managed/clusterip/crc", "license": "MulanPSL-2.0"}, "cluster_crc": {"url": "https://github.com/retroSoC/crc.git", "revision": "8c1a5664c4d23d005c1afad432372a13ca967cc8", "destination": "rtl/managed/clusterip/crc", "license": "MulanPSL-2.0"},
"cluster_ps2": {"url": "https://github.com/retroSoC/ps2.git", "revision": "2097a0d41b07573ff0453d65ba9a1670b3df7b9c", "destination": "rtl/managed/clusterip/ps2", "license": "MulanPSL-2.0"}, "cluster_ps2": {"url": "https://github.com/retroSoC/ps2.git", "revision": "2097a0d41b07573ff0453d65ba9a1670b3df7b9c", "destination": "rtl/managed/clusterip/ps2", "license": "MulanPSL-2.0"},
"cluster_pwm": {"url": "https://github.com/retroSoC/pwm.git", "revision": "79ae32f725e848a1e7f6134847b3fb032ed64006", "destination": "rtl/managed/clusterip/pwm", "license": "MulanPSL-2.0"}, "cluster_pwm": {"url": "https://github.com/retroSoC/pwm.git", "revision": "79ae32f725e848a1e7f6134847b3fb032ed64006", "destination": "rtl/managed/clusterip/pwm", "license": "MulanPSL-2.0"},
+5
View File
@@ -16,6 +16,9 @@ the root README and subsystem guides.
register protocol and software quiesce contract. register protocol and software quiesce contract.
- [hazard3-debug.md](hazard3-debug.md) defines the management Hazard3 JTAG - [hazard3-debug.md](hazard3-debug.md) defines the management Hazard3 JTAG
Debug Module integration, reset boundary, and Verilator/OpenOCD/GDB flow. Debug Module integration, reset boundary, and Verilator/OpenOCD/GDB flow.
- [lp-hp-architecture.md](lp-hp-architecture.md) defines the implemented
Hazard3 LP/VexiiRiscv HP topology, Linux boot ABI, MVP evidence, and
commercial-alignment roadmap.
- [coremark.md](coremark.md) defines the SRAM-resident Hazard3 CoreMark quick - [coremark.md](coremark.md) defines the SRAM-resident Hazard3 CoreMark quick
measurement, optional standard hardware run, and structured result format. measurement, optional standard hardware run, and structured result format.
- [soc-family-positioning.md](soc-family-positioning.md) defines the planned - [soc-family-positioning.md](soc-family-positioning.md) defines the planned
@@ -46,6 +49,8 @@ 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/hp-platform.md](ip/hp-platform.md) defines the experimental HP PLIC and
LP/HP mailbox register, interrupt, software, and verification contracts.
- [ip/onchip-sram.md](ip/onchip-sram.md) defines the configurable native-AXI4 - [ip/onchip-sram.md](ip/onchip-sram.md) defines the configurable native-AXI4
SRAM architecture, APB capability ABI, technology mapping, verification, and SRAM architecture, APB capability ABI, technology mapping, verification, and
reliability roadmap. reliability roadmap.
+13 -4
View File
@@ -21,14 +21,21 @@ Read and write ordering is therefore program order at each master. Different
masters may use different targets concurrently; requests to one target use a masters may use different targets concurrently; requests to one target use a
Common round-robin arbiter and retain ownership through `B` or `RLAST`. Common round-robin arbiter and retain ownership through `B` or `RLAST`.
The three masters are the Hazard3 management core, the selected user core, and The integrated fabric has eight master slots: Hazard3 management (0), selected
DMA. Hazard3 AHB-Lite and the current user-core RIBP ABI issue single-beat AXI4 user core (1), central DMA (2), SDIO0 (3), SDIO1 (4), the retained SPI-SD slot
transactions through adapters. DMA is a native AXI4 master: its production (5), USB2 (6), and optional HP (7). Hazard3 AHB-Lite and the current user-core
RIBP ABI issue single-beat AXI4 transactions through adapters. DMA is a native AXI4 master: its production
six-channel engine uses fixed ID zero, independently schedules one read and six-channel engine uses fixed ID zero, independently schedules one read and
one write transaction, and issues aligned `INCR` bursts up to sixteen beats. one write transaction, and issues aligned `INCR` bursts up to sixteen beats.
Fixed-address and APB4/MMIO endpoints are always single-beat. See the Fixed-address and APB4/MMIO endpoints are always single-beat. See the
[DMA MVP](ip/dma.md) for its ownership, abort, and FIFO-credit contract. [DMA MVP](ip/dma.md) for its ownership, abort, and FIFO-credit contract.
HP presents separate 64-bit instruction-cache, data-cache, and uncached-MMIO
ports. Self-owned downsizers and a transaction mux serialize them onto master
7. A maximum eight-beat 64-bit transfer becomes the fabric maximum sixteen
32-bit beats. This is a compatibility plane without coherency; see the
[LP/HP contract](lp-hp-architecture.md).
## Targets and Access Control ## Targets and Access Control
The fixed targets are the APB4 configuration plane (`apb4_periph`), APB The fixed targets are the APB4 configuration plane (`apb4_periph`), APB
@@ -66,7 +73,9 @@ access-control rejection to `PROTERR`.
## Verification and Performance Gates ## Verification and Performance Gates
`tests/test_axi4.py` covers fabric classification. `tests/test_onchip_ram.py` `tests/test_axi4.py` covers fabric classification, HP master 7 attribution,
64-to-32 burst/lane conversion, response identity, and backpressure.
`tests/test_onchip_ram.py`
covers all five capacities, sixteen-beat reads/writes, response backpressure, covers all five capacities, sixteen-beat reads/writes, response backpressure,
partial writes, technology mapping, and error termination. The bridge unit partial writes, technology mapping, and error termination. The bridge unit
test also covers Common address generation for FIXED and WRAP sequencing. The test also covers Common address generation for FIXED and WRAP sequencing. The
+8 -2
View File
@@ -1,7 +1,9 @@
# Core-Local Interruptor # Core-Local Interruptor
The Mini SoC CLINT provides machine software and machine timer interrupts for The baseline Mini SoC CLINT provides machine software and machine timer
the management hart. Its register layout follows the conventional SiFive interrupts for the management hart. The HP profile adds an independent
two-slot instance at `0x02000000` and connects slot 1 to HP hart 1. Its register
layout follows the conventional SiFive
CLINT and RISC-V ACLINT-compatible offsets, while its bus-facing logic uses the CLINT and RISC-V ACLINT-compatible offsets, while its bus-facing logic uses the
retroSoC APB4 target protocol. retroSoC APB4 target protocol.
@@ -18,6 +20,10 @@ retroSoC APB4 target protocol.
| User-core access | denied by the AXI4 interconnect access policy | | User-core access | denied by the AXI4 interconnect access policy |
| User-core interrupt visibility | masked | | User-core interrupt visibility | masked |
The HP instance uses the same 1 MHz reference timebase. OpenSBI programs hart
1 `MSIP` at `0x02000004`, `MTIMECMP` at `0x02004008`, and shared `MTIME` at
`0x0200BFF8`. The baseline management CLINT remains at `0x10020000`.
The timebase divider runs from the buffered external reference clock, not the The timebase divider runs from the buffered external reference clock, not the
PLL-selected system clock. A toggle crosses into the system domain through the PLL-selected system clock. A toggle crosses into the system domain through the
Common `edge_det` synchronizer and produces one system-clock pulse per Common `edge_det` synchronizer and produces one system-clock pulse per
+62
View File
@@ -0,0 +1,62 @@
# HP Interrupt and Mailbox Platform
The experimental LP/HP profile adds a 32-source, two-context PLIC at
`0x0C000000` and a bidirectional mailbox at `0x10019000`. Both are self-owned
APB4 peripherals. Their role in boot and lifecycle control is defined by
[LP/HP Architecture](../lp-hp-architecture.md).
## PLIC
Source 0 is permanently reserved. Source 1 is UART1 and source 2 is the
HP-side mailbox doorbell; sources 3 through 31 are reserved and tied low. Each
source has a three-bit priority. Context 0 drives HP machine external interrupt
and context 1 drives HP supervisor external interrupt.
| Address offset | Register | Access |
| --- | --- | --- |
| `0x000000 + 4 * source` | source priority | RW |
| `0x001000` | pending bits 31:0 | RO |
| `0x002000 + 0x80 * context` | enable bits 31:0 | RW |
| `0x200000 + 0x1000 * context` | threshold | RW |
| `0x200004 + 0x1000 * context` | claim/complete | RO/RW |
Claim returns the lowest source ID at the greatest priority strictly above the
context threshold, clears its pending bit, and marks it claimed. Writing that
nonzero ID to the same context's claim/complete register completes it. A level
source that remains asserted becomes pending again after completion. Priority
zero disables delivery. The MVP implements one 32-bit pending/enable word and
does not implement MSI, AIA, virtualization, or affinity routing.
## Mailbox
LP writes the `LP_*` bank and rings `LP_DOORBELL`, which sets the HP interrupt
state. HP writes the `HP_*` bank and rings `HP_DOORBELL`, which sets the LP
interrupt state. State is sticky W1C and is qualified by a separate enable for
each destination.
| Offset | Register | Access | Purpose |
| --- | --- | --- | --- |
| `0x000` | `IP_VERSION` | RO | `0x00010000` |
| `0x004` | `CAPABILITY` | RO | `0x00000007` |
| `0x010` | `LP_COMMAND` | RW | LP-to-HP message code |
| `0x014` | `LP_ARG0` | RW | LP-to-HP argument |
| `0x018` | `LP_SEQUENCE` | RW | LP publication sequence; zero means none |
| `0x01C` | `LP_DOORBELL` | WO | Set HP interrupt state |
| `0x020` | `HP_EVENT` | RW | HP-to-LP event code |
| `0x024` | `HP_ARG0` | RW | HP-to-LP argument |
| `0x028` | `HP_SEQUENCE` | RW | HP publication sequence; zero means none |
| `0x02C` | `HP_DOORBELL` | WO | Set LP interrupt state |
| `0x030`/`0x034`/`0x038`/`0x03C` | LP interrupt state/enable/status/test | mixed | LP destination IRQ control |
| `0x040`/`0x044`/`0x048`/`0x04C` | HP interrupt state/enable/status/test | mixed | HP destination IRQ control |
The publication order is code, argument, sequence, memory fence, then
doorbell. `<retrosoc/hal/hp_mailbox.h>` supplies the LP-side API. The initial
Linux userspace acceptance script reports event 1, argument `0x4C4E5801`, and
sequence 1 after init; this is a bring-up verdict, not a general Linux mailbox
driver ABI.
`tests/rtl/plic_tb.sv` and `tests/rtl/hp_mailbox_tb.sv` cover register and
interrupt behavior. `tests/test_hp_boot_bundle.py` enforces the handwritten
mailbox RTL/C offset parity. Production work still requires a Linux mailbox
driver, concurrent sequence/wrap policy, malformed-message tests, lifecycle
timeouts, and fault-injection coverage.
+6 -2
View File
@@ -3,7 +3,8 @@
SystemCtrl is the Mini SoC control-plane peripheral at SystemCtrl is the Mini SoC control-plane peripheral at
`0x1000B000`. It owns user-core/IP selection, the PLL request protocol, `0x1000B000`. It owns user-core/IP selection, the PLL request protocol,
bus-fault retention, performance-counter snapshots, RTC wake observation, and bus-fault retention, performance-counter snapshots, RTC wake observation, and
the terminal simulation result. The integration wrapper is `apb4_sysctrl`; the terminal simulation result. In the HP profile it also owns HP reset release
and shared-JTAG selection. The integration wrapper is `apb4_sysctrl`;
the implementation is separated into `sysctrl_reg`, `sysctrl_core`, and the implementation is separated into `sysctrl_reg`, `sysctrl_core`, and
`sysctrl_if`. `sysctrl_if`.
@@ -52,7 +53,7 @@ offset macros.
| `0x01C` | `PLL_STATUS` | RO | Active profile, valid, busy, error/error reason, safe clock, lock, and capability. | | `0x01C` | `PLL_STATUS` | RO | Active profile, valid, busy, error/error reason, safe clock, lock, and capability. |
| `0x020` | `USER_CORE_RESET` | RW | Per-user-core active-high reset mask. All ones stops the user bus; a one-hot deassertion starts the selected core. | | `0x020` | `USER_CORE_RESET` | RW | Per-user-core active-high reset mask. All ones stops the user bus; a one-hot deassertion starts the selected core. |
| `0x024` | `USER_CORE_STATUS` | RW1C/RO | Selected core, user-bus enabled/idle/draining, and bit 11 configuration-error W1C. | | `0x024` | `USER_CORE_STATUS` | RW1C/RO | Selected core, user-bus enabled/idle/draining, and bit 11 configuration-error W1C. |
| `0x028` | `FAULT_MASTER` | RO | First fault master, a three-bit AXI master ID (0..4). | | `0x028` | `FAULT_MASTER` | RO | First fault master, a three-bit AXI master ID (0..7); HP is 7. |
| `0x02C` | `FAULT_DETAIL` | RO | First raw RIB response code. | | `0x02C` | `FAULT_DETAIL` | RO | First raw RIB response code. |
| `0x040` | `PERF_CTRL` | RW | Bit 0 enable, bit 1 clear pulse, bit 2 snapshot pulse. | | `0x040` | `PERF_CTRL` | RW | Bit 0 enable, bit 1 clear pulse, bit 2 snapshot pulse. |
| `0x044`-`0x098` | `PERF_*_WAIT_{LO,HI}` | RO | Snapshot of management, user, central DMA, SDIO0, SDIO1, APB4, SDRAM, PSRAM, and flash wait counters. | | `0x044`-`0x098` | `PERF_*_WAIT_{LO,HI}` | RO | Snapshot of management, user, central DMA, SDIO0, SDIO1, APB4, SDRAM, PSRAM, and flash wait counters. |
@@ -60,6 +61,9 @@ offset macros.
| `0x094`/`0x098` | `PERF_SDIO1_WAIT_LO/HI` | RO | Independent SDIO1 AXI master wait snapshot. | | `0x094`/`0x098` | `PERF_SDIO1_WAIT_LO/HI` | RO | Independent SDIO1 AXI master wait snapshot. |
| `0x084` | `TEST_STATUS` | RW-once/RO | Full-word write with bit 31 set records sticky done, bit 0 pass, and bits `[15:8]` result code. | | `0x084` | `TEST_STATUS` | RW-once/RO | Full-word write with bit 31 set records sticky done, bit 0 pass, and bits `[15:8]` result code. |
| `0x088` | `RTC_WAKE_STATUS` | RW1C/RO | Bit 0 synchronized live wake; bit 1 sticky wake, cleared by writing one to bit 1. | | `0x088` | `RTC_WAKE_STATUS` | RW1C/RO | Bit 0 synchronized live wake; bit 1 sticky wake, cleared by writing one to bit 1. |
| `0x0A4` | `HP_CTRL` | RW | Bit 0 releases HP reset when the HP profile is present. Reset value 0. |
| `0x0A8` | `HP_STATUS` | RO | Bits 0/1/2 report present, released, and reset asserted. |
| `0x0AC` | `DEBUG_SELECT` | RW | Bit 0 selects HP on shared JTAG only while HP remains in reset. Reset value 0 selects LP. |
The first fault remains visible until software clears pending; later faults The first fault remains visible until software clears pending; later faults
increase only the saturating count. A hardware fault in the clear cycle increase only the saturating count. A hardware fault in the clear cycle
+6
View File
@@ -6,6 +6,12 @@ fractional baud-rate generation, configurable framing, receive diagnostics,
watermark and timeout interrupts, internal loopback, break generation, watermark and timeout interrupts, internal loopback, break generation,
generic-DMA request pacing, and automatic active-low RTS/CTS flow control. generic-DMA request pacing, and automatic active-low RTS/CTS flow control.
The HP profile instantiates the same UART v3 register ABI as UART1 at
`0x10018000`, with dedicated `uart1_tx`/`uart1_rx` pads and PLIC source 1.
UART1 has no DMA or RTS/CTS connection in the MVP. The external OpenSBI platform
uses UART1 as the SBI debug console, and Linux initially consumes it through
`hvc0`; a native Linux UART driver is a later delivery item.
## Integration ## Integration
| Property | Value | | Property | Value |
+199
View File
@@ -0,0 +1,199 @@
# LP/HP Architecture and Delivery Contract
## Status
The committed `configs/ci/ihp130-hp.mk` profile is the executable MVP for the
retroSoC asymmetric LP/HP architecture. Hazard3 is the low-performance control
core (LP, hart 0) and a generated RV32 VexiiRiscv instance is the
high-performance Linux core (HP, hart 1). Both run from the fixed 72 MHz system
clock in this profile.
The profile proves integration, build, and lint readiness. A repeatable Linux
boot, the 2.5x performance gate, physical timing closure, and hardware
qualification remain release gates until their evidence is recorded. This MVP
must not be described as cache coherent, hot-reset safe, secure boot capable,
or power isolated.
The commercial reference analysis and product positioning are in
[SoC Family Product Positioning](soc-family-positioning.md). The implementation
reuses the most relevant patterns from heterogeneous Linux/MCU devices:
- an always-available management core owns application-core release;
- Linux receives standard CLINT/PLIC privilege interrupts and a dedicated
console;
- a mailbox is the versioned cross-core control path;
- boot artifacts and generated CPU RTL have locked, reviewable provenance;
- performance claims are machine checked from measurements, not inferred from
CPU configuration.
The MVP deliberately avoids presenting the HP core as an ordinary C0-C3 user
core. Those legacy slots remain available and software selected. HP has a
fixed hart ID, reset vector, bus master, interrupt topology, and Linux ABI.
## Compute and Memory Topology
| Property | LP | HP |
| --- | --- | --- |
| Core | Hazard3 | generated VexiiRiscv |
| Hart ID | 0 | 1 |
| ISA | RV32IM plus existing CSR/debug profile | RV32IMAFDC, S/U mode, Sv32 |
| Clock | 72 MHz | 72 MHz |
| Primary role | boot, lifecycle, diagnostics, recovery | OpenSBI and Linux |
| Reset | root reset controller | SYSCTRL release plus HP debug reset request |
| JTAG | shared pads, reset default owner | selectable only while HP is held reset |
| Main-memory view | 32-bit AXI4 | three AXI64 planes converted to one 32-bit AXI4 master |
HP instruction-cache, data-cache, and uncached MMIO ports each pass through a
64-to-32 adapter. The adapters preserve IDs and responses, split or combine
64-bit beats, honor backpressure, and track lanes across AXI burst types. The
transaction mux prioritizes uncached MMIO, then data cache, then instruction
cache and permits one transaction at a time. A Common `axi4_regslice` separates
the HP compatibility plane from the legacy system fabric.
The current system interconnect accepts up to sixteen 32-bit beats. Therefore
an HP 64-bit burst is limited to eight source beats. The generated 16 KiB,
four-way instruction and data caches use eight-beat refill/writeback geometry.
This is a compatibility architecture for the current 64 MiB SDRAM, not the
native 64-bit memory plane required for a later performance product.
There is no hardware cache coherency between LP, HP, DMA, or user cores. LP
loads HP memory before releasing HP, when HP caches are cold. Any later shared
buffer protocol must define ownership transfer and explicit cache maintenance.
Linux must not map device registers as cacheable.
## Platform ABI
| Block | Address | Purpose |
| --- | --- | --- |
| HP ACLINT | `0x02000000` | hart 1 machine software/timer interrupts, 1 MHz timebase |
| HP PLIC | `0x0C000000` | 31 usable sources and M/S contexts |
| UART1 | `0x10018000` | dedicated HP/SBI console pads; no DMA in MVP |
| HP mailbox | `0x10019000` | command/event, argument, sequence, and dual doorbells |
| SDRAM | `0x38000000`-`0x3BFFFFFF` | shared 64 MiB Linux main memory |
PLIC source 1 is UART1 and source 2 is the HP mailbox. The LP interrupt map
retains existing assignments and adds mailbox IRQ 25 and UART1 IRQ 26. The
PLIC implements the priority, enable, pending, threshold, claim, and complete
registers needed by the Linux SiFive PLIC driver; it is not an AIA, MSI, or
virtualization interrupt controller.
SYSCTRL adds `HP_CTRL` at `0xA4`, `HP_STATUS` at `0xA8`, and `DEBUG_SELECT` at
`0xAC`. Reset defaults keep HP asserted and JTAG connected to LP. Selecting HP
debug while HP is running is rejected by hardware. The MVP release bit does
not drain transactions before reasserting reset, so software must not use it
as a safe hot-reset interface.
The mailbox RTL and `<retrosoc/hal/hp_mailbox.h>` are handwritten definitions.
`tests/test_hp_boot_bundle.py` checks their register offsets against each other.
Sequence zero means no message. Writers publish code and argument before the
nonzero sequence and doorbell; readers use the sequence as the validity token.
## Generated Core Contract
`make CONFIG=configs/ci/ihp130-hp.mk vexii-generate` runs the locked Scala
configuration in `scripts/vexiiriscv/GenerateRetroSocHp.scala`. It verifies the
source revision from `dependencies/dependencies.lock.json` and writes RTL,
filelist, source status, and SHA-256 provenance below the selected
`build/<variant>/generated/vexiiriscv/` directory. Generated VexiiRiscv RTL is
never Git tracked.
The fixed configuration enables dual issue, late ALU, full bypassing, branch
prediction, PMP, performance counters, debug triggers, RV32IMAFDC, Sv32, and
separate 16 KiB instruction/data caches. Changing any of these is a new
`HP_CONFIG` and requires a separate profile, software ABI review, synthesis,
timing, and benchmark evidence.
## Boot Flow
The `hp_boot` LP application is linked wholly into the 32 KiB on-chip SRAM so
loading OpenSBI at the SDRAM base cannot overwrite running management code.
The `hp-bundle` target combines LP firmware and the four HP artifacts in the
16 MiB boot flash:
| Artifact | Load address | Maximum |
| --- | --- | --- |
| OpenSBI `fw_jump.bin` | `0x38000000` | 512 KiB |
| `retrosoc_hp.dtb` | `0x38080000` | 64 KiB |
| Linux `Image` | `0x38400000` | 12 MiB |
| `rootfs.cpio.gz` | `0x39000000` | 8 MiB |
The bundle starts at flash offset `0x00100000`. Its 128-byte v1 header contains
four whitelisted entries with type, flash offset, load address, byte count,
CRC32, and required flags. LP validates the header, flash and SDRAM bounds,
fixed destinations, sizes, alignment, and every payload CRC. It copies with
32-bit writes and full readback for control payloads; large payloads use full
flash CRC validation without destination rereads. It issues a memory fence
before releasing HP.
CRC detects accidental corruption; it is not authentication.
OpenSBI is built from an external repo-owned platform directory without
patching its locked source. The platform maps OpenSBI hart index 0 to hardware
hart ID 1, registers the HP ACLINT, and provides the SBI debug console through
UART1. OpenSBI starts at `0x38000000` and directly enters Linux at
`0x38400000`, passing the DTB at `0x38080000`; U-Boot is not part of the MVP.
Buildroot creates the RV32 glibc toolchain and a small initramfs. Linux starts
from `tinyconfig` and merges `app/ports/linux/linux/retrosoc_hp.config` so the
Image stays within the fixed flash budget. Its final init script prints
`retroSoC HP Linux ready` and posts mailbox event 1 with argument `0x4C4E5801`.
LP then writes the normal sticky `TEST_STATUS` pass result, giving simulation a
deterministic terminal verdict.
```sh
make setup-hp-linux
make CONFIG=configs/ci/ihp130-hp.mk hp-linux
make CONFIG=configs/ci/ihp130-hp.mk hp-bundle
make CONFIG=configs/ci/ihp130-hp.mk SIMU=VERILATOR comp sim
```
The setup flow locks VexiiRiscv, OpenSBI, Linux, and Buildroot by full Git
revision. Interrupted HP source fetches are repaired in place on retry.
## Performance and Verification
LP and HP claims use the same 72 MHz frequency and CoreMark mode. After both
machine-readable reports exist, enforce the selected requirement with:
```sh
make hp-performance-check \
LP_COREMARK_REPORT=<lp-coremark.json> \
HP_COREMARK_REPORT=<hp-coremark.json>
```
The gate writes `meta/lp-hp-performance.json` and requires HP CoreMark/MHz to
be at least 2.5 times LP. An HP report must come from executed Linux, RTL
simulation, FPGA, or silicon with its provenance retained. A predicted score
is not acceptable evidence.
Current directed verification covers the AXI downsizer, 8-master interconnect,
mailbox, PLIC, topology generation, pad ring, generated-source boundary,
bundle CRC/layout, HAL/RTL offset parity, HP firmware build, RTL style, and
full-design Verilator lint. Release qualification additionally requires a
successful Linux boot log, both CoreMark reports, IHP130 synthesis/STA, and the
normal PR regression.
## Commercial Alignment Roadmap
The next phases are ordered by risk and dependency:
1. Complete repeatable Linux boot and native UART/mailbox kernel drivers;
retain boot-time and error-injection evidence.
2. Replace the serialized compatibility plane with a native burst-capable
memory path, add HP QoS/wait counters, and close the 2.5x performance gate.
3. Add lifecycle handshakes that block new HP requests, drain accepted traffic,
request Linux shutdown, apply cache maintenance, and only then reset.
4. Add clock gating, power isolation/retention controls, CDC/RDC signoff, and
measured state-transition latency and energy.
5. Add immutable boot ROM, signature verification, rollback protection, OTP
key/lifecycle state, debug authentication/disable, firewall regions, and a
documented threat model. CRC32 is then retained only for transport errors.
6. Add RAS evidence: ECC/parity coverage, watchdog escalation, bus timeout,
machine-check logging, fault injection, and recovery/reset campaigns.
7. Add production DFT, MBIST, trace, PMU event definitions, coverage closure,
software update/recovery, long-duration stress, and PVT/package signoff.
Avoid claiming a commercial big.LITTLE-style coherent cluster: this design is
asymmetric multiprocessing with independent software roles. Also avoid direct
LP reset of a running HP, shared cacheable buffers without an ownership
protocol, Linux control over root lifecycle registers, and per-PDK frequency
claims derived only from RTL simulation.
+26 -17
View File
@@ -98,11 +98,13 @@ feature reference when Wi-Fi is included.
### Status and Position ### Status and Position
retroSoC Mini is currently an open-source RISC-V microcontroller-class SoC retroSoC Mini is an open-source RISC-V microcontroller-class SoC with a fixed
with a fixed Hazard3 management core and one software-selected user core. The Hazard3 management core and one software-selected user core. The experimental
planned Linux-capable configuration adds a 32-bit VexiiRiscv user core. The `configs/ci/ihp130-hp.mk` profile now adds a separate 32-bit VexiiRiscv Linux
VexiiRiscv integration and Linux boot flow are product targets, not supported core without consuming the C0-C3 slots. RTL integration, a direct-Linux image
repository configurations yet. flow, and LP boot firmware are implemented; repeatable Linux boot,
performance, synthesis, timing, and hardware evidence are still qualification
gates rather than supported product claims.
Mini is the price, complexity, and Linux-capability anchor for the family. It Mini is the price, complexity, and Linux-capability anchor for the family. It
targets industrial control, protocol gateways, compact human-machine targets industrial control, protocol gateways, compact human-machine
@@ -137,7 +139,7 @@ capabilities:
- SYSCTRL controls user-core selection, reset, interrupt admission, and bus - SYSCTRL controls user-core selection, reset, interrupt admission, and bus
admission. A stop request blocks new user transactions, drains an accepted admission. A stop request blocks new user transactions, drains an accepted
transaction, and then asserts reset. transaction, and then asserts reset.
- The canonical address map exposes 128 KiB of on-chip SRAM, a 32 MiB SDRAM - The canonical address map exposes configurable on-chip SRAM, a 64 MiB SDRAM
window, and an 8 MiB PSRAM window. Address-window capacity does not guarantee window, and an 8 MiB PSRAM window. Address-window capacity does not guarantee
that every implementation includes the corresponding physical memory. that every implementation includes the corresponding physical memory.
- AXI4 is the active 32-bit interconnect. It supports linear one- through - AXI4 is the active 32-bit interconnect. It supports linear one- through
@@ -147,12 +149,16 @@ capabilities:
scalar engine accesses. They do not yet combine a burst into a native SDRAM, scalar engine accesses. They do not yet combine a burst into a native SDRAM,
PSRAM, flash, or SPI-SD physical transaction. PSRAM, flash, or SPI-SD physical transaction.
The experimental HP profile adds the fixed hart 1 core, a compatibility AXI
plane, HP ACLINT/PLIC, UART1, mailbox, reset release, and shared-JTAG selection.
Its detailed executable contract is [LP/HP Architecture](lp-hp-architecture.md).
The generated address map, user-extension map, The generated address map, user-extension map,
[AXI4 contract](axi4-interconnect.md), and retained [AXI4 contract](axi4-interconnect.md), and retained
[RIB protocol contract](rib-interconnect.md) remain the integration references [RIB protocol contract](rib-interconnect.md) remain the integration references
for the implemented baseline. for the implemented baseline.
### Planned Linux Configuration ### Experimental Linux Configuration
The Linux configuration keeps Hazard3 as the always-available trusted The Linux configuration keeps Hazard3 as the always-available trusted
management core and adds VexiiRiscv as the application processor. Hazard3 is management core and adds VexiiRiscv as the application processor. Hazard3 is
@@ -162,7 +168,7 @@ able to reconfigure the management-core lifecycle controls.
The initial VexiiRiscv configuration should provide: The initial VexiiRiscv configuration should provide:
- RV32IMAC with machine, supervisor, and user modes. - RV32IMAFDC with machine, supervisor, and user modes.
- Sv32 virtual memory, hardware page-table walking, and the privileged CSRs - Sv32 virtual memory, hardware page-table walking, and the privileged CSRs
required by the supported Linux kernel. required by the supported Linux kernel.
- LR/SC atomics, separate 16 KiB instruction and data caches, and uncached MMIO - LR/SC atomics, separate 16 KiB instruction and data caches, and uncached MMIO
@@ -172,13 +178,15 @@ The initial VexiiRiscv configuration should provide:
- A documented DMA cache-maintenance contract unless the memory system supplies - A documented DMA cache-maintenance contract unless the memory system supplies
hardware coherency. hardware coherency.
The management firmware initializes external memory, verifies and places the The implemented management firmware waits for external memory, validates and
OpenSBI firmware, device tree, kernel, and initial filesystem, programs the places the OpenSBI firmware, device tree, kernel, and initial filesystem at
VexiiRiscv entry point, and then enables its bus access and releases reset. A fixed addresses, and then releases the fixed VexiiRiscv reset vector. The
normal stop first requests Linux shutdown through a mailbox. After Linux bundle uses CRC32 for accidental-corruption detection, not secure boot. A
acknowledges and flushes persistent data, the management core disables new bus future normal stop must first request Linux shutdown through the mailbox. After
transactions, drains the final accepted transaction, and asserts reset. A Linux acknowledges and flushes persistent data, the management core must
bounded timeout permits fault recovery through forced isolation and reset. disable new bus transactions, drain the final accepted transaction, and assert
reset. A bounded timeout permits fault recovery through forced isolation and
reset.
### Product Targets ### Product Targets
@@ -194,8 +202,9 @@ implemented or cross-PDK guarantee:
| Boot storage | SPI NAND or qualified SDIO path | SDIO/eMMC plus recovery image | | Boot storage | SPI NAND or qualified SDIO path | SDIO/eMMC plus recovery image |
| Linux platform devices | UART, timer, interrupt controller, storage | Ethernet or USB selected by the product profile | | Linux platform devices | UART, timer, interrupt controller, storage | Ethernet or USB selected by the product profile |
Meeting these targets requires enlarging the current 32 MiB SDRAM address The current 64 MiB SDRAM window meets the minimum address-capacity target, but
window and replacing the scalar external-memory bottleneck. A dedicated memory meeting the bandwidth target requires replacing the scalar external-memory
bottleneck. A dedicated memory
fabric may evolve independently while APB4 and APB remain the stable peripheral fabric may evolve independently while APB4 and APB remain the stable peripheral
register interfaces. Frequency claims must be qualified separately for each register interfaces. Frequency claims must be qualified separately for each
PDK and physical implementation. PDK and physical implementation.
+4
View File
@@ -13,6 +13,10 @@ package locations remain in the board `.xdc` file.
The dedicated SDIO1 pads are intentionally left unbound by the FPGA profile The dedicated SDIO1 pads are intentionally left unbound by the FPGA profile
until a board package location and 3.3 V I/O-bank assignment are approved; until a board package location and 3.3 V I/O-bank assignment are approved;
the existing LVCMOS18 constraints do not establish SDIO electrical support. the existing LVCMOS18 constraints do not establish SDIO electrical support.
The new UART1 HP-console ports are likewise left unbound until two StarrySky
package pins and their I/O voltage are reviewed. The ASIC profile still owns
dedicated UART1 pads; this FPGA constraint gap is intentional and must be
resolved before generating an HP board bitstream.
FPGA constraints are board-specific implementation inputs, not generic ASIC FPGA constraints are board-specific implementation inputs, not generic ASIC
RTL. Validate changes with the relevant FPGA tool flow and hardware target; do RTL. Validate changes with the relevant FPGA tool flow and hardware target; do
+2
View File
@@ -27,6 +27,8 @@ module retrosoc_top (
inout gpio_io30, inout gpio_io30,
output uart0_tx_o, output uart0_tx_o,
inout uart0_rx_i, inout uart0_rx_i,
output uart1_tx_o,
inout uart1_rx_i,
output xpi_sck_o, output xpi_sck_o,
output xpi_nss0_o, output xpi_nss0_o,
inout xpi_dat0_io, inout xpi_dat0_io,
+1 -1
View File
@@ -30,7 +30,7 @@ make CONFIG=configs/ci/ihp130.mk librelane-core-package
make CONFIG=configs/ci/ihp130.mk librelane-package make CONFIG=configs/ci/ihp130.mk librelane-package
``` ```
The chip target places all 109 signal pads, 80 core/IO power and ground pads, The chip target places all 111 signal pads, 80 core/IO power and ground pads,
four corners, I/O fillers, and 70 um bondpads. It is a bare-die plan, not a four corners, I/O fillers, and 70 um bondpads. It is a bare-die plan, not a
QFN/BGA package definition. The SDC constrains the external clock at 72 MHz; QFN/BGA package definition. The SDC constrains the external clock at 72 MHz;
mid-PnR timing report passes are skipped because LibreLane 3.0.5 emits 1000 mid-PnR timing report passes are skipped because LibreLane 3.0.5 emits 1000
+1 -1
View File
@@ -20,7 +20,7 @@ from scripts.setup_helpers import atomic_write # noqa: E402
EXPECTED_LIBRELANE_VERSION = "LibreLane v3.0.5" EXPECTED_LIBRELANE_VERSION = "LibreLane v3.0.5"
EXPECTED_OPENSTA_VERSION = "3.0.0" EXPECTED_OPENSTA_VERSION = "3.0.0"
EXPECTED_SIGNAL_PADS = 109 EXPECTED_SIGNAL_PADS = 111
def command_output(command: list[str]) -> str: def command_output(command: list[str]) -> str:
@@ -23,7 +23,7 @@ from scripts.setup_helpers import atomic_write # noqa: E402
SIDE_ORDER = ("south", "east", "north", "west") SIDE_ORDER = ("south", "east", "north", "west")
EXPECTED_SIGNAL_PADS = 109 EXPECTED_SIGNAL_PADS = 111
CLOCK_PORTS = ( CLOCK_PORTS = (
"extclk_i_pad", "extclk_i_pad",
"audclk_i_pad", "audclk_i_pad",
+6
View File
@@ -11,6 +11,12 @@ notices. `filelist/` selects PDK-specific RTL sources; `tech/` contains
technology wrappers. Respect managed upstream boundaries and use setup helpers technology wrappers. Respect managed upstream boundaries and use setup helpers
rather than editing generated MPW output. rather than editing generated MPW output.
The optional IHP130 LP/HP profile integrates VexiiRiscv through self-owned
wrappers, a 64-to-32 compatibility plane, HP ACLINT/PLIC, mailbox, UART1, and
SYSCTRL lifecycle signals. The generated CPU Verilog lives only below
`build/<variant>/generated/vexiiriscv/` and is an external lint boundary. See
[LP/HP Architecture](../docs/lp-hp-architecture.md).
RTL changes require an affected firmware build and simulation. Use RTL changes require an affected firmware build and simulation. Use
`make regress-pr` or `make regress-nightly` for supported regression coverage; `make regress-pr` or `make regress-nightly` for supported regression coverage;
see [Engineering Workflow](../docs/engineering.md) for results and artifacts. see [Engineering Workflow](../docs/engineering.md) for results and artifacts.
+1
View File
@@ -55,5 +55,6 @@ module apb4_clint #(
assign clint.software_irq_o = s_msip; assign clint.software_irq_o = s_msip;
assign clint.timer_irq_o = s_timer_irq; assign clint.timer_irq_o = s_timer_irq;
assign clint.mtime_o = s_mtime;
endmodule endmodule
+232
View File
@@ -0,0 +1,232 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "hp_mailbox_define.svh"
module apb4_hp_mailbox (
// verilog_format: off -- preserve APB and interrupt alignment
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
output logic lp_irq_o,
output logic hp_irq_o
// verilog_format: on
);
localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam logic [31:0] Capability = 32'h0000_0007;
logic s_req_accept;
logic s_write;
logic [11:0] s_offset;
logic s_access_err;
logic s_ready_d, s_ready_q;
logic s_resp_err_d, s_resp_err_q;
logic [31:0] s_rdata_d, s_rdata_q;
logic [31:0] s_lp_cmd_d, s_lp_cmd_q;
logic [31:0] s_lp_arg0_d, s_lp_arg0_q;
logic [31:0] s_lp_sequence_d, s_lp_sequence_q;
logic [31:0] s_hp_event_d, s_hp_event_q;
logic [31:0] s_hp_arg0_d, s_hp_arg0_q;
logic [31:0] s_hp_sequence_d, s_hp_sequence_q;
logic s_lp_intr_state_d, s_lp_intr_state_q;
logic s_lp_intr_en_d, s_lp_intr_en_q;
logic s_hp_intr_state_d, s_hp_intr_state_q;
logic s_hp_intr_en_d, s_hp_intr_en_q;
function automatic logic [31:0] merge_wstrb(input logic [31:0] current, input logic [31:0] value,
input logic [3:0] strobe);
logic [31:0] merged;
begin
merged = current;
for (int byte_index = 0; byte_index < 4; byte_index++) begin
if (strobe[byte_index]) merged[byte_index*8+:8] = value[byte_index*8+:8];
end
return merged;
end
endfunction
assign s_req_accept = apb4.psel && apb4.penable && !s_ready_q;
assign s_write = apb4.pwrite;
assign s_offset = apb4.paddr[11:0];
assign apb4.pready = s_ready_q;
assign apb4.pslverr = s_resp_err_q;
assign apb4.prdata = s_rdata_q;
assign lp_irq_o = s_lp_intr_state_q && s_lp_intr_en_q;
assign hp_irq_o = s_hp_intr_state_q && s_hp_intr_en_q;
always_comb begin
s_access_err = 1'b0;
s_rdata_d = '0;
unique case (s_offset)
`APB4_HP_MAILBOX__IP_VERSION: s_rdata_d = IpVersion;
`APB4_HP_MAILBOX__CAPABILITY: s_rdata_d = Capability;
`APB4_HP_MAILBOX__LP_COMMAND: s_rdata_d = s_lp_cmd_q;
`APB4_HP_MAILBOX__LP_ARG0: s_rdata_d = s_lp_arg0_q;
`APB4_HP_MAILBOX__LP_SEQUENCE: s_rdata_d = s_lp_sequence_q;
`APB4_HP_MAILBOX__HP_EVENT: s_rdata_d = s_hp_event_q;
`APB4_HP_MAILBOX__HP_ARG0: s_rdata_d = s_hp_arg0_q;
`APB4_HP_MAILBOX__HP_SEQUENCE: s_rdata_d = s_hp_sequence_q;
`APB4_HP_MAILBOX__LP_INTR_STATE: s_rdata_d = {31'd0, s_lp_intr_state_q};
`APB4_HP_MAILBOX__LP_INTR_ENABLE: s_rdata_d = {31'd0, s_lp_intr_en_q};
`APB4_HP_MAILBOX__LP_INTR_STATUS: s_rdata_d = {31'd0, lp_irq_o};
`APB4_HP_MAILBOX__HP_INTR_STATE: s_rdata_d = {31'd0, s_hp_intr_state_q};
`APB4_HP_MAILBOX__HP_INTR_ENABLE: s_rdata_d = {31'd0, s_hp_intr_en_q};
`APB4_HP_MAILBOX__HP_INTR_STATUS: s_rdata_d = {31'd0, hp_irq_o};
`APB4_HP_MAILBOX__LP_DOORBELL, `APB4_HP_MAILBOX__HP_DOORBELL,
`APB4_HP_MAILBOX__LP_INTR_TEST, `APB4_HP_MAILBOX__HP_INTR_TEST: begin
s_access_err = !s_write;
end
default: s_access_err = 1'b1;
endcase
end
always_comb begin
s_lp_cmd_d = s_lp_cmd_q;
s_lp_arg0_d = s_lp_arg0_q;
s_lp_sequence_d = s_lp_sequence_q;
s_hp_event_d = s_hp_event_q;
s_hp_arg0_d = s_hp_arg0_q;
s_hp_sequence_d = s_hp_sequence_q;
s_lp_intr_state_d = s_lp_intr_state_q;
s_lp_intr_en_d = s_lp_intr_en_q;
s_hp_intr_state_d = s_hp_intr_state_q;
s_hp_intr_en_d = s_hp_intr_en_q;
if (s_req_accept && s_write && !s_access_err) begin
unique case (s_offset)
`APB4_HP_MAILBOX__LP_COMMAND: s_lp_cmd_d = merge_wstrb(s_lp_cmd_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__LP_ARG0: s_lp_arg0_d = merge_wstrb(s_lp_arg0_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__LP_SEQUENCE:
s_lp_sequence_d = merge_wstrb(s_lp_sequence_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__LP_DOORBELL: s_hp_intr_state_d = 1'b1;
`APB4_HP_MAILBOX__HP_EVENT:
s_hp_event_d = merge_wstrb(s_hp_event_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__HP_ARG0: s_hp_arg0_d = merge_wstrb(s_hp_arg0_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__HP_SEQUENCE:
s_hp_sequence_d = merge_wstrb(s_hp_sequence_q, apb4.pwdata, apb4.pstrb);
`APB4_HP_MAILBOX__HP_DOORBELL: s_lp_intr_state_d = 1'b1;
`APB4_HP_MAILBOX__LP_INTR_STATE:
if (apb4.pstrb[0] && apb4.pwdata[0]) s_lp_intr_state_d = 1'b0;
`APB4_HP_MAILBOX__LP_INTR_ENABLE: if (apb4.pstrb[0]) s_lp_intr_en_d = apb4.pwdata[0];
`APB4_HP_MAILBOX__LP_INTR_TEST:
if (apb4.pstrb[0] && apb4.pwdata[0]) s_lp_intr_state_d = 1'b1;
`APB4_HP_MAILBOX__HP_INTR_STATE:
if (apb4.pstrb[0] && apb4.pwdata[0]) s_hp_intr_state_d = 1'b0;
`APB4_HP_MAILBOX__HP_INTR_ENABLE: if (apb4.pstrb[0]) s_hp_intr_en_d = apb4.pwdata[0];
`APB4_HP_MAILBOX__HP_INTR_TEST:
if (apb4.pstrb[0] && apb4.pwdata[0]) s_hp_intr_state_d = 1'b1;
default: begin
end
endcase
end
end
assign s_ready_d = s_req_accept;
assign s_resp_err_d = s_req_accept && s_access_err;
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_accept && !s_write),
.dat_i (s_rdata_d),
.dat_o (s_rdata_q)
);
dffr #(
.DATA_WIDTH(32)
) u_lp_command_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lp_cmd_d),
.dat_o (s_lp_cmd_q)
);
dffr #(
.DATA_WIDTH(32)
) u_lp_arg0_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lp_arg0_d),
.dat_o (s_lp_arg0_q)
);
dffr #(
.DATA_WIDTH(32)
) u_lp_sequence_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lp_sequence_d),
.dat_o (s_lp_sequence_q)
);
dffr #(
.DATA_WIDTH(32)
) u_hp_event_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_event_d),
.dat_o (s_hp_event_q)
);
dffr #(
.DATA_WIDTH(32)
) u_hp_arg0_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_arg0_d),
.dat_o (s_hp_arg0_q)
);
dffr #(
.DATA_WIDTH(32)
) u_hp_sequence_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_sequence_d),
.dat_o (s_hp_sequence_q)
);
dffr #(
.DATA_WIDTH(1)
) u_lp_intr_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lp_intr_state_d),
.dat_o (s_lp_intr_state_q)
);
dffr #(
.DATA_WIDTH(1)
) u_lp_intr_enable_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lp_intr_en_d),
.dat_o (s_lp_intr_en_q)
);
dffr #(
.DATA_WIDTH(1)
) u_hp_intr_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_intr_state_d),
.dat_o (s_hp_intr_state_q)
);
dffr #(
.DATA_WIDTH(1)
) u_hp_intr_enable_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_intr_en_d),
.dat_o (s_hp_intr_en_q)
);
endmodule
+226
View File
@@ -0,0 +1,226 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apb4_plic #(
parameter int NumSources = 32,
parameter int NumContexts = 2
) (
// verilog_format: off -- preserve APB and interrupt alignment
input logic clk_i,
input logic rst_n_i,
input logic [NumSources-1:0] source_i,
apb4_if.slave apb4,
output logic [NumContexts-1:0] context_irq_o
// verilog_format: on
);
localparam int SourceWidth = $clog2(NumSources);
logic s_req_accept;
logic s_write;
logic [25:0] s_offset;
logic s_ready_d, s_ready_q;
logic s_resp_err_d, s_resp_err_q;
logic [31:0] s_rdata_d, s_rdata_q;
logic [2:0] s_priority_d[NumSources], s_priority_q[NumSources];
logic [NumSources-1:0] s_pending_d, s_pending_q;
logic [NumSources-1:0] s_claimed_d, s_claimed_q;
logic [NumSources-1:0] s_en_d[NumContexts], s_en_q[NumContexts];
logic [2:0] s_threshold_d[NumContexts], s_threshold_q[NumContexts];
logic [SourceWidth-1:0] s_claim_id [NumContexts];
logic [ 2:0] s_claim_priority[NumContexts];
logic s_priority_sel;
logic s_pending_sel;
logic s_en_sel [NumContexts];
logic s_threshold_sel [NumContexts];
logic s_claim_sel [NumContexts];
logic [SourceWidth-1:0] s_priority_src;
assign s_req_accept = apb4.psel && apb4.penable && !s_ready_q;
assign s_write = apb4.pwrite;
assign s_offset = apb4.paddr[25:0];
assign s_priority_sel = (s_offset < 26'h0001000) && (s_offset[1:0] == 2'b00);
assign s_priority_src = s_offset[SourceWidth+1:2];
assign s_pending_sel = s_offset == 26'h0001000;
for (genvar ctx = 0; ctx < NumContexts; ctx++) begin : gen_select
assign s_en_sel[ctx] = s_offset == (26'h0002000 + 26'(ctx * 128));
assign s_threshold_sel[ctx] = s_offset == (26'h0200000 + 26'(ctx * 4096));
assign s_claim_sel[ctx] = s_offset == (26'h0200004 + 26'(ctx * 4096));
end
always_comb begin
for (int ctx = 0; ctx < NumContexts; ctx++) begin
s_claim_id[ctx] = '0;
s_claim_priority[ctx] = '0;
for (int source = 1; source < NumSources; source++) begin
if (s_pending_q[source] && s_en_q[ctx][source] &&
(s_priority_q[source] > s_threshold_q[ctx]) &&
((s_priority_q[source] > s_claim_priority[ctx]) ||
((s_priority_q[source] == s_claim_priority[ctx]) &&
((s_claim_id[ctx] == '0) || (source < s_claim_id[ctx]))))) begin
s_claim_id[ctx] = SourceWidth'(source);
s_claim_priority[ctx] = s_priority_q[source];
end
end
context_irq_o[ctx] = s_claim_id[ctx] != '0;
end
end
always_comb begin
s_rdata_d = '0;
s_resp_err_d = 1'b0;
if (s_priority_sel && ({1'b0, s_priority_src} < (SourceWidth + 1)'(NumSources))) begin
s_rdata_d = {29'd0, s_priority_q[s_priority_src]};
end else if (s_pending_sel) begin
s_rdata_d = 32'(s_pending_q);
end else begin
s_resp_err_d = 1'b1;
for (int ctx = 0; ctx < NumContexts; ctx++) begin
if (s_en_sel[ctx]) begin
s_rdata_d = 32'(s_en_q[ctx]);
s_resp_err_d = 1'b0;
end
if (s_threshold_sel[ctx]) begin
s_rdata_d = {29'd0, s_threshold_q[ctx]};
s_resp_err_d = 1'b0;
end
if (s_claim_sel[ctx]) begin
s_rdata_d = 32'(s_claim_id[ctx]);
s_resp_err_d = 1'b0;
end
end
end
if (s_write && s_pending_sel) s_resp_err_d = 1'b1;
end
always_comb begin
for (int source = 0; source < NumSources; source++) begin
s_priority_d[source] = s_priority_q[source];
end
for (int ctx = 0; ctx < NumContexts; ctx++) begin
s_en_d[ctx] = s_en_q[ctx];
s_threshold_d[ctx] = s_threshold_q[ctx];
end
s_pending_d = s_pending_q;
s_claimed_d = s_claimed_q;
s_pending_d[0] = 1'b0;
s_claimed_d[0] = 1'b0;
for (int source = 1; source < NumSources; source++) begin
if (source_i[source] && !s_claimed_q[source]) s_pending_d[source] = 1'b1;
end
if (s_req_accept && s_write && !s_resp_err_d) begin
if (s_priority_sel && (s_priority_src != '0) && apb4.pstrb[0]) begin
s_priority_d[s_priority_src] = apb4.pwdata[2:0];
end
for (int ctx = 0; ctx < NumContexts; ctx++) begin
if (s_en_sel[ctx]) begin
for (int byte_index = 0; byte_index < 4; byte_index++) begin
if (apb4.pstrb[byte_index]) begin
s_en_d[ctx][byte_index*8+:8] = apb4.pwdata[byte_index*8+:8];
end
end
s_en_d[ctx][0] = 1'b0;
end
if (s_threshold_sel[ctx] && apb4.pstrb[0]) begin
s_threshold_d[ctx] = apb4.pwdata[2:0];
end
if (s_claim_sel[ctx] && (apb4.pwdata[SourceWidth-1:0] != '0) &&
({1'b0, apb4.pwdata[SourceWidth-1:0]} < (SourceWidth + 1)'(NumSources))) begin
s_claimed_d[apb4.pwdata[SourceWidth-1:0]] = 1'b0;
end
end
end
if (s_req_accept && !s_write) begin
for (int ctx = 0; ctx < NumContexts; ctx++) begin
if (s_claim_sel[ctx] && (s_claim_id[ctx] != '0)) begin
s_pending_d[s_claim_id[ctx]] = 1'b0;
s_claimed_d[s_claim_id[ctx]] = 1'b1;
end
end
end
end
assign s_ready_d = s_req_accept;
assign apb4.pready = s_ready_q;
assign apb4.pslverr = s_resp_err_q;
assign apb4.prdata = s_rdata_q;
for (genvar source = 0; source < NumSources; source++) begin : gen_priority
dffr #(
.DATA_WIDTH(3)
) u_priority_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_priority_d[source]),
.dat_o (s_priority_q[source])
);
end
for (genvar ctx = 0; ctx < NumContexts; ctx++) begin : gen_context
dffr #(
.DATA_WIDTH(NumSources)
) u_enable_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_en_d[ctx]),
.dat_o (s_en_q[ctx])
);
dffr #(
.DATA_WIDTH(3)
) u_threshold_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_threshold_d[ctx]),
.dat_o (s_threshold_q[ctx])
);
end
dffr #(
.DATA_WIDTH(NumSources)
) u_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pending_d),
.dat_o (s_pending_q)
);
dffr #(
.DATA_WIDTH(NumSources)
) u_claimed_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_claimed_d),
.dat_o (s_claimed_q)
);
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_req_accept && 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_accept && !s_write),
.dat_i (s_rdata_d),
.dat_o (s_rdata_q)
);
`ifndef SYNTHESIS
initial begin
if ((NumSources != 32) || (NumContexts != 2)) begin
$fatal(1, "apb4_plic: Mini HP profile requires 32 sources and two contexts");
end
end
`endif
endmodule
+2 -1
View File
@@ -13,6 +13,7 @@ interface clint_if #(
) (); ) ();
logic [HartNum-1:0] timer_irq_o; logic [HartNum-1:0] timer_irq_o;
logic [HartNum-1:0] software_irq_o; logic [HartNum-1:0] software_irq_o;
logic [ 63:0] mtime_o;
modport dut(output timer_irq_o, output software_irq_o); modport dut(output timer_irq_o, output software_irq_o, output mtime_o);
endinterface endinterface
+26
View File
@@ -0,0 +1,26 @@
`ifndef RETROSOC_HP_MAILBOX_DEFINE_SVH
`define RETROSOC_HP_MAILBOX_DEFINE_SVH
// verilog_format: off -- preserve software-visible register alignment
`define APB4_HP_MAILBOX__IP_VERSION 12'h000
`define APB4_HP_MAILBOX__CAPABILITY 12'h004
`define APB4_HP_MAILBOX__LP_COMMAND 12'h010
`define APB4_HP_MAILBOX__LP_ARG0 12'h014
`define APB4_HP_MAILBOX__LP_SEQUENCE 12'h018
`define APB4_HP_MAILBOX__LP_DOORBELL 12'h01C
`define APB4_HP_MAILBOX__HP_EVENT 12'h020
`define APB4_HP_MAILBOX__HP_ARG0 12'h024
`define APB4_HP_MAILBOX__HP_SEQUENCE 12'h028
`define APB4_HP_MAILBOX__HP_DOORBELL 12'h02C
`define APB4_HP_MAILBOX__LP_INTR_STATE 12'h030
`define APB4_HP_MAILBOX__LP_INTR_ENABLE 12'h034
`define APB4_HP_MAILBOX__LP_INTR_STATUS 12'h038
`define APB4_HP_MAILBOX__LP_INTR_TEST 12'h03C
`define APB4_HP_MAILBOX__HP_INTR_STATE 12'h040
`define APB4_HP_MAILBOX__HP_INTR_ENABLE 12'h044
`define APB4_HP_MAILBOX__HP_INTR_STATUS 12'h048
`define APB4_HP_MAILBOX__HP_INTR_TEST 12'h04C
// verilog_format: on
`endif
+42
View File
@@ -85,6 +85,9 @@ module sysctrl_core (
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_FLASH_WAIT_HI); sysctrl_offset_t'(`APB4_SYSCTRL__PERF_FLASH_WAIT_HI);
localparam sysctrl_offset_t TestStatus = sysctrl_offset_t'(`APB4_SYSCTRL__TEST_STATUS); localparam sysctrl_offset_t TestStatus = sysctrl_offset_t'(`APB4_SYSCTRL__TEST_STATUS);
localparam sysctrl_offset_t RtcWakeStatus = sysctrl_offset_t'(`APB4_SYSCTRL__RTC_WAKE_STATUS); localparam sysctrl_offset_t RtcWakeStatus = sysctrl_offset_t'(`APB4_SYSCTRL__RTC_WAKE_STATUS);
localparam sysctrl_offset_t HpCtrl = sysctrl_offset_t'(`APB4_SYSCTRL__HP_CTRL);
localparam sysctrl_offset_t HpStatus = sysctrl_offset_t'(`APB4_SYSCTRL__HP_STATUS);
localparam sysctrl_offset_t DebugSelect = sysctrl_offset_t'(`APB4_SYSCTRL__DEBUG_SELECT);
logic [`USER_CORESEL_WIDTH-1:0] s_sysctrl_coresel_d, s_sysctrl_coresel_q; logic [`USER_CORESEL_WIDTH-1:0] s_sysctrl_coresel_d, s_sysctrl_coresel_q;
logic [`USER_CORE_COUNT-1:0] s_user_reset_d, s_user_reset_q; logic [`USER_CORE_COUNT-1:0] s_user_reset_d, s_user_reset_q;
@@ -124,6 +127,8 @@ module sysctrl_core (
logic s_test_pass_d, s_test_pass_q; logic s_test_pass_d, s_test_pass_q;
logic [7:0] s_test_code_d, s_test_code_q; logic [7:0] s_test_code_d, s_test_code_q;
logic s_rtc_wake_seen_d, s_rtc_wake_seen_q; logic s_rtc_wake_seen_d, s_rtc_wake_seen_q;
logic s_hp_release_d, s_hp_release_q;
logic s_debug_hp_sel_d, s_debug_hp_sel_q;
logic s_rtc_wake_sync; logic s_rtc_wake_sync;
logic s_core_sel_write_valid; logic s_core_sel_write_valid;
logic s_core_sel_write_en; logic s_core_sel_write_en;
@@ -154,6 +159,8 @@ module sysctrl_core (
assign sysctrl.test_done_o = s_test_done_q; assign sysctrl.test_done_o = s_test_done_q;
assign sysctrl.test_pass_o = s_test_pass_q; assign sysctrl.test_pass_o = s_test_pass_q;
assign sysctrl.test_code_o = s_test_code_q; assign sysctrl.test_code_o = s_test_code_q;
assign sysctrl.hp_release_o = s_hp_release_q;
assign sysctrl.debug_hp_select_o = s_debug_hp_sel_q;
assign pll_ctrl.req_sel_o = s_pll_cfg_q; assign pll_ctrl.req_sel_o = s_pll_cfg_q;
assign pll_ctrl.req_valid_o = s_pll_req_valid_q; assign pll_ctrl.req_valid_o = s_pll_req_valid_q;
assign pll_ctrl.rsp_ready_o = 1'b1; assign pll_ctrl.rsp_ready_o = 1'b1;
@@ -221,6 +228,35 @@ module sysctrl_core (
end end
end end
always_comb begin
s_hp_release_d = s_hp_release_q;
s_debug_hp_sel_d = s_debug_hp_sel_q;
if (write_valid_i && (write_offset_i == HpCtrl) && write_strobe_i[0]) begin
s_hp_release_d = write_data_i[`APB4_SYSCTRL__HP_CTRL_RELEASE] && sysctrl.hp_present_i;
end
if (write_valid_i && (write_offset_i == DebugSelect) && write_strobe_i[0] &&
!s_hp_release_q) begin
s_debug_hp_sel_d = write_data_i[`APB4_SYSCTRL__DEBUG_SELECT_HP] && sysctrl.hp_present_i;
end
end
dffr #(
.DATA_WIDTH(1)
) u_hp_release_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hp_release_d),
.dat_o (s_hp_release_q)
);
dffr #(
.DATA_WIDTH(1)
) u_debug_hp_select_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_debug_hp_sel_d),
.dat_o (s_debug_hp_sel_q)
);
assign s_pll_cfg_write_en = write_valid_i && (write_offset_i == PllCfg) && write_strobe_i[0]; assign s_pll_cfg_write_en = write_valid_i && (write_offset_i == PllCfg) && write_strobe_i[0];
assign s_pll_apply = write_valid_i && (write_offset_i == PllCmd) && write_strobe_i[0] && assign s_pll_apply = write_valid_i && (write_offset_i == PllCmd) && write_strobe_i[0] &&
write_data_i[`APB4_SYSCTRL__PLL_CMD_APPLY]; write_data_i[`APB4_SYSCTRL__PLL_CMD_APPLY];
@@ -510,6 +546,12 @@ module sysctrl_core (
PerfFlashWaitHi: read_data_o = s_perf_flash_wait_q[63:32]; PerfFlashWaitHi: read_data_o = s_perf_flash_wait_q[63:32];
TestStatus: read_data_o = {s_test_done_q, 15'd0, s_test_code_q, 7'd0, s_test_pass_q}; TestStatus: read_data_o = {s_test_done_q, 15'd0, s_test_code_q, 7'd0, s_test_pass_q};
RtcWakeStatus: read_data_o = {30'd0, s_rtc_wake_seen_q, s_rtc_wake_sync}; RtcWakeStatus: read_data_o = {30'd0, s_rtc_wake_seen_q, s_rtc_wake_sync};
HpCtrl: read_data_o = {31'd0, s_hp_release_q};
HpStatus:
read_data_o = {
29'd0, sysctrl.hp_present_i && !s_hp_release_q, s_hp_release_q, sysctrl.hp_present_i
};
DebugSelect: read_data_o = {31'd0, s_debug_hp_sel_q};
default: begin default: begin
read_data_valid_o = 1'b0; read_data_valid_o = 1'b0;
end end
+5
View File
@@ -51,6 +51,9 @@
`define APB4_SYSCTRL__RTC_WAKE_STATUS `SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET `define APB4_SYSCTRL__RTC_WAKE_STATUS `SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET
`define APB4_SYSCTRL__PERF_USB2_WAIT_LO `SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET `define APB4_SYSCTRL__PERF_USB2_WAIT_LO `SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET
`define APB4_SYSCTRL__PERF_USB2_WAIT_HI `SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET `define APB4_SYSCTRL__PERF_USB2_WAIT_HI `SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET
`define APB4_SYSCTRL__HP_CTRL `SOC_SYSCTRL_HP_CTRL_OFFSET
`define APB4_SYSCTRL__HP_STATUS `SOC_SYSCTRL_HP_STATUS_OFFSET
`define APB4_SYSCTRL__DEBUG_SELECT `SOC_SYSCTRL_DEBUG_SELECT_OFFSET
`define APB4_SYSCTRL__PLL_CMD_APPLY 0 `define APB4_SYSCTRL__PLL_CMD_APPLY 0
`define APB4_SYSCTRL__PLL_CMD_CLEAR_ERROR 1 `define APB4_SYSCTRL__PLL_CMD_CLEAR_ERROR 1
@@ -63,6 +66,8 @@
`define APB4_SYSCTRL__TEST_STATUS_VALID 31 `define APB4_SYSCTRL__TEST_STATUS_VALID 31
`define APB4_SYSCTRL__RTC_WAKE_STATUS_LIVE 0 `define APB4_SYSCTRL__RTC_WAKE_STATUS_LIVE 0
`define APB4_SYSCTRL__RTC_WAKE_STATUS_SEEN 1 `define APB4_SYSCTRL__RTC_WAKE_STATUS_SEEN 1
`define APB4_SYSCTRL__HP_CTRL_RELEASE 0
`define APB4_SYSCTRL__DEBUG_SELECT_HP 0
// verilog_format: on // verilog_format: on
`endif `endif
+7 -1
View File
@@ -36,6 +36,9 @@ interface sysctrl_if ();
logic test_pass_o; logic test_pass_o;
logic [ 7:0] test_code_o; logic [ 7:0] test_code_o;
logic [ `USER_IPSEL_WIDTH-1:0] ip_sel_o; logic [ `USER_IPSEL_WIDTH-1:0] ip_sel_o;
logic hp_present_i;
logic hp_release_o;
logic debug_hp_select_o;
modport dut( modport dut(
input user_bus_idle_i, input user_bus_idle_i,
@@ -54,6 +57,7 @@ interface sysctrl_if ();
input perf_psram_wait_i, input perf_psram_wait_i,
input perf_flash_wait_i, input perf_flash_wait_i,
input rtc_wake_i, input rtc_wake_i,
input hp_present_i,
output core_sel_o, output core_sel_o,
output core_reset_o, output core_reset_o,
output user_bus_enable_o, output user_bus_enable_o,
@@ -62,6 +66,8 @@ interface sysctrl_if ();
output perf_clear_o, output perf_clear_o,
output test_done_o, output test_done_o,
output test_pass_o, output test_pass_o,
output test_code_o output test_code_o,
output hp_release_o,
output debug_hp_select_o
); );
endinterface endinterface
+2 -1
View File
@@ -122,7 +122,7 @@ check-clock-reset-domains:
$(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \ $(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \
$(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) \ $(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) \
$(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(if $(filter YES,$(HAVE_HP)),$(HP_GENERATED_STAMP))
$(FLOW_PYTHON) $(RTL_PATH)/script/generate_filelist.py \ $(FLOW_PYTHON) $(RTL_PATH)/script/generate_filelist.py \
--output-dir $(GENERATED_FL_DIR) \ --output-dir $(GENERATED_FL_DIR) \
--incdir $(ARCHINFO_METADATA_DIR) \ --incdir $(ARCHINFO_METADATA_DIR) \
@@ -167,6 +167,7 @@ RTL_FILELISTS := $(MEMORY_MAP_FILELIST) $(SOC_TOPOLOGY_FILELIST) $(USER_EXTENSIO
$(GENERATED_FL_DIR)/core_hazard3.fl \ $(GENERATED_FL_DIR)/core_hazard3.fl \
$(MPW_OUTPUT_DIR)/core/core.fl \ $(MPW_OUTPUT_DIR)/core/core.fl \
$(MPW_OUTPUT_DIR)/ip/ip.fl \ $(MPW_OUTPUT_DIR)/ip/ip.fl \
$(if $(filter YES,$(HAVE_HP)),$(HP_GENERATED_DIR)/vexiiriscv.fl) \
$(GENERATED_FL_DIR)/top.fl $(GENERATED_FL_DIR)/top.fl
RTL_FLIST := $(foreach filelist,$(RTL_FILELISTS),-f $(filelist)) RTL_FLIST := $(foreach filelist,$(RTL_FILELISTS),-f $(filelist))
#====================== end ====================== #====================== end ======================
+48
View File
@@ -149,6 +149,18 @@
{ {
"symbol": "PERF_USB2_WAIT_HI", "symbol": "PERF_USB2_WAIT_HI",
"offset": "0xA0" "offset": "0xA0"
},
{
"symbol": "HP_CTRL",
"offset": "0xA4"
},
{
"symbol": "HP_STATUS",
"offset": "0xA8"
},
{
"symbol": "DEBUG_SELECT",
"offset": "0xAC"
} }
], ],
"regions": [ "regions": [
@@ -163,6 +175,24 @@
"user_access": "ro", "user_access": "ro",
"burst": "incr16" "burst": "incr16"
}, },
{
"symbol": "HP_ACLINT",
"base": "0x02000000",
"size": "0x00010000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{
"symbol": "HP_PLIC",
"base": "0x0C000000",
"size": "0x04000000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{ {
"symbol": "APB4_GPIO", "symbol": "APB4_GPIO",
"base": "0x10000000", "base": "0x10000000",
@@ -388,6 +418,24 @@
"public": true, "public": true,
"user_access": "none" "user_access": "none"
}, },
{
"symbol": "APB4_UART1",
"base": "0x10018000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{
"symbol": "APB4_HP_MAILBOX",
"base": "0x10019000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{ {
"symbol": "APB4_ARCHINFO", "symbol": "APB4_ARCHINFO",
"base": "0x20000000", "base": "0x20000000",
+2
View File
@@ -35,6 +35,8 @@ module retrosoc_tb;
wire s_jtag_trst_n; wire s_jtag_trst_n;
wire s_uart0_tx; wire s_uart0_tx;
wire s_uart0_rx; wire s_uart0_rx;
wire s_uart1_tx;
tri1 s_uart1_rx;
tri1 s_gpio_0_io; tri1 s_gpio_0_io;
tri1 s_gpio_1_io; tri1 s_gpio_1_io;
wire s_psram_sck; wire s_psram_sck;
@@ -71,6 +71,8 @@ module retrosoc_top (
wire s_jtag_tdi; wire s_jtag_tdi;
wire s_jtag_trst_n; wire s_jtag_trst_n;
wire s_jtag_tdo; wire s_jtag_tdo;
wire s_uart1_tx;
tri1 s_uart1_rx;
wire s_usb2_ulpi_clk; wire s_usb2_ulpi_clk;
tri0 s_usb2_ulpi_dir; tri0 s_usb2_ulpi_dir;
tri0 s_usb2_ulpi_nxt; tri0 s_usb2_ulpi_nxt;
+1
View File
@@ -3,6 +3,7 @@
/clusterip/common/rtl/interface/ahbl_if.sv /clusterip/common/rtl/interface/ahbl_if.sv
/clusterip/common/rtl/interface/axi4_if.sv /clusterip/common/rtl/interface/axi4_if.sv
/clusterip/common/rtl/interface/axi4_addr_gen.sv /clusterip/common/rtl/interface/axi4_addr_gen.sv
/clusterip/common/rtl/bus/axi4_regslice.sv
/clusterip/common/rtl/interface/axi4_stream_if.sv /clusterip/common/rtl/interface/axi4_stream_if.sv
/clusterip/common/rtl/interface/ribp_if.sv /clusterip/common/rtl/interface/ribp_if.sv
/clusterip/common/rtl/interface/ram_if.sv /clusterip/common/rtl/interface/ram_if.sv
+3
View File
@@ -111,6 +111,9 @@
/ip/peripheral/clint_reg.sv /ip/peripheral/clint_reg.sv
/ip/peripheral/clint_core.sv /ip/peripheral/clint_core.sv
/ip/peripheral/apb4_clint.sv /ip/peripheral/apb4_clint.sv
/ip/peripheral/hp_mailbox_define.svh
/ip/peripheral/apb4_hp_mailbox.sv
/ip/peripheral/apb4_plic.sv
/ip/memory/sdram_pkg.sv /ip/memory/sdram_pkg.sv
/ip/memory/sdram_clkgen.sv /ip/memory/sdram_clkgen.sv
/ip/memory/sdram_reg.sv /ip/memory/sdram_reg.sv
+4
View File
@@ -11,6 +11,8 @@
/top/axi42apb4_system.sv /top/axi42apb4_system.sv
/top/axi42apb4_periph.sv /top/axi42apb4_periph.sv
/top/axi4_word_bridge.sv /top/axi4_word_bridge.sv
/top/axi4_downsizer_64to32.sv
/top/hp_axi4_mux3.sv
/top/axi42ram.sv /top/axi42ram.sv
/top/rib_error_slave.sv /top/rib_error_slave.sv
/top/rib2ram.sv /top/rib2ram.sv
@@ -29,6 +31,8 @@
/top/mgmt_debug_reset.sv /top/mgmt_debug_reset.sv
/top/mgmt_debug_wrapper.sv /top/mgmt_debug_wrapper.sv
/top/mgmt_core_wrapper.sv /top/mgmt_core_wrapper.sv
/top/hp_core_wrapper.sv
/top/hp_subsystem.sv
/top/user_core_design.sv /top/user_core_design.sv
/top/user_core_top.sv /top/user_core_top.sv
/top/user_ip_design_template.sv /top/user_ip_design_template.sv
+7 -1
View File
@@ -42,7 +42,9 @@ module sysctrl_formal_design (
output logic [31:0] fault_count, output logic [31:0] fault_count,
output logic test_done, output logic test_done,
output logic test_pass, output logic test_pass,
output logic [ 7:0] test_code output logic [ 7:0] test_code,
output logic hp_release,
output logic debug_hp_select
); );
apb4_if apb4 ( apb4_if apb4 (
@@ -85,12 +87,14 @@ module sysctrl_formal_design (
assign sysctrl.perf_dma_wait_i = '0; assign sysctrl.perf_dma_wait_i = '0;
assign sysctrl.perf_sdio0_wait_i = '0; assign sysctrl.perf_sdio0_wait_i = '0;
assign sysctrl.perf_sdio1_wait_i = '0; assign sysctrl.perf_sdio1_wait_i = '0;
assign sysctrl.perf_usb2_wait_i = '0;
assign sysctrl.perf_apb4_periph_wait_i = '0; assign sysctrl.perf_apb4_periph_wait_i = '0;
assign sysctrl.perf_apb4_system_wait_i = '0; assign sysctrl.perf_apb4_system_wait_i = '0;
assign sysctrl.perf_sdram_wait_i = '0; assign sysctrl.perf_sdram_wait_i = '0;
assign sysctrl.perf_psram_wait_i = '0; assign sysctrl.perf_psram_wait_i = '0;
assign sysctrl.perf_flash_wait_i = '0; assign sysctrl.perf_flash_wait_i = '0;
assign sysctrl.rtc_wake_i = 1'b0; assign sysctrl.rtc_wake_i = 1'b0;
assign sysctrl.hp_present_i = 1'b1;
assign pll_ctrl.req_ready_i = f_pll_req_ready; assign pll_ctrl.req_ready_i = f_pll_req_ready;
assign pll_ctrl.rsp_active_sel_i = f_pll_active_sel; assign pll_ctrl.rsp_active_sel_i = f_pll_active_sel;
assign pll_ctrl.rsp_active_valid_i = f_pll_active_valid; assign pll_ctrl.rsp_active_valid_i = f_pll_active_valid;
@@ -131,6 +135,8 @@ module sysctrl_formal_design (
assign test_done = sysctrl.test_done_o; assign test_done = sysctrl.test_done_o;
assign test_pass = sysctrl.test_pass_o; assign test_pass = sysctrl.test_pass_o;
assign test_code = sysctrl.test_code_o; assign test_code = sysctrl.test_code_o;
assign hp_release = sysctrl.hp_release_o;
assign debug_hp_select = sysctrl.debug_hp_select_o;
apb4_sysctrl u_dut ( apb4_sysctrl u_dut (
.clk_i (clk_i), .clk_i (clk_i),
+23 -1
View File
@@ -16,6 +16,8 @@ module sysctrl_formal;
localparam [7:0] SYSCTRL_FAULT_STATUS_OFFSET = 8'h10; localparam [7:0] SYSCTRL_FAULT_STATUS_OFFSET = 8'h10;
localparam [7:0] SYSCTRL_USER_CORE_RESET_OFFSET = 8'h20; localparam [7:0] SYSCTRL_USER_CORE_RESET_OFFSET = 8'h20;
localparam [7:0] SYSCTRL_TEST_STATUS_OFFSET = 8'h84; localparam [7:0] SYSCTRL_TEST_STATUS_OFFSET = 8'h84;
localparam [7:0] SYSCTRL_HP_CTRL_OFFSET = 8'hA4;
localparam [7:0] SYSCTRL_DEBUG_SELECT_OFFSET = 8'hAC;
(* anyseq *) (* gclk *)reg clk_i; (* anyseq *) (* gclk *)reg clk_i;
wire rst_n_i; wire rst_n_i;
@@ -51,6 +53,8 @@ module sysctrl_formal;
wire test_done; wire test_done;
wire test_pass; wire test_pass;
wire [ 7:0] test_code; wire [ 7:0] test_code;
wire hp_release;
wire debug_hp_select;
sysctrl_formal_design u_design ( sysctrl_formal_design u_design (
.clk_i (clk_i), .clk_i (clk_i),
@@ -86,7 +90,9 @@ module sysctrl_formal;
.fault_count (fault_count), .fault_count (fault_count),
.test_done (test_done), .test_done (test_done),
.test_pass (test_pass), .test_pass (test_pass),
.test_code (test_code) .test_code (test_code),
.hp_release (hp_release),
.debug_hp_select (debug_hp_select)
); );
always @(posedge clk_i) begin always @(posedge clk_i) begin
@@ -187,12 +193,28 @@ module sysctrl_formal;
assert (test_pass == $past(test_pass)); assert (test_pass == $past(test_pass));
assert (test_code == $past(test_code)); assert (test_code == $past(test_code));
end end
if ($past(
rst_n_i && rib_valid && !rib_ready && rib_wstrb[0] &&
rib_addr[7:0] == SYSCTRL_HP_CTRL_OFFSET
)) begin
assert (hp_release == $past(rib_wdata[0]));
end
if ($past(
rst_n_i && rib_valid && !rib_ready && rib_wstrb[0] &&
rib_addr[7:0] == SYSCTRL_DEBUG_SELECT_OFFSET && !hp_release
)) begin
assert (debug_hp_select == $past(rib_wdata[0]));
end
if ($past(rst_n_i && hp_release)) begin
assert (debug_hp_select == $past(debug_hp_select));
end
end end
if (rst_n_i) begin if (rst_n_i) begin
cover (pll_busy && pll_req_valid); cover (pll_busy && pll_req_valid);
cover (pll_error && pll_error_reason == 2'd3); cover (pll_error && pll_error_reason == 2'd3);
cover (fault_pending && fault_reason == 2'd2); cover (fault_pending && fault_reason == 2'd2);
cover (hp_release);
end end
end end
@@ -198,6 +198,14 @@
"path": "rtl/managed/hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v", "path": "rtl/managed/hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v",
"instance": "inst_hazard3_apb_async_bridge" "instance": "inst_hazard3_apb_async_bridge"
}, },
{
"name": "hp_debug_select",
"source": "system",
"destination": "jtag",
"primitive": "cdc_sync",
"path": "rtl/mini/top/retrosoc.sv",
"instance": "u_debug_select_sync"
},
{ {
"name": "wdg_command", "name": "wdg_command",
"source": "system", "source": "system",
+47 -1
View File
@@ -5,7 +5,7 @@
"irq_groups": [ "irq_groups": [
{ {
"name": "apb4_periph", "name": "apb4_periph",
"width": 20, "width": 22,
"top_signal": "s_apb4_periph_irq" "top_signal": "s_apb4_periph_irq"
}, },
{ {
@@ -222,6 +222,24 @@
"core_bit": 24, "core_bit": 24,
"signal": "s_usb2_irq", "signal": "s_usb2_irq",
"user_visible": false "user_visible": false
},
{
"name": "hp_mailbox_lp",
"description": "HP-to-LP mailbox doorbell",
"group": "apb4_periph",
"group_bit": 20,
"core_bit": 25,
"signal": "s_mailbox_lp_irq",
"user_visible": false
},
{
"name": "uart1",
"description": "HP Linux UART interrupt",
"group": "apb4_periph",
"group_bit": 21,
"core_bit": 26,
"signal": "uart1.irq_o",
"user_visible": false
} }
], ],
"wake_sources": [ "wake_sources": [
@@ -437,6 +455,34 @@
"pure_interface": "sram_cfg_pure", "pure_interface": "sram_cfg_pure",
"region": "APB4_SRAM", "region": "APB4_SRAM",
"external": true "external": true
},
{
"slot": 22,
"name": "uart1",
"timed_interface": "u_uart1_apb4_if",
"pure_interface": "u_uart1_apb4_pure_if",
"region": "APB4_UART1"
},
{
"slot": 23,
"name": "hp_mailbox",
"timed_interface": "u_hp_mailbox_apb4_if",
"pure_interface": "u_hp_mailbox_apb4_pure_if",
"region": "APB4_HP_MAILBOX"
},
{
"slot": 24,
"name": "hp_aclint",
"timed_interface": "u_hp_aclint_apb4_if",
"pure_interface": "u_hp_aclint_apb4_pure_if",
"region": "HP_ACLINT"
},
{
"slot": 25,
"name": "hp_plic",
"timed_interface": "u_hp_plic_apb4_if",
"pure_interface": "u_hp_plic_apb4_pure_if",
"region": "HP_PLIC"
} }
], ],
"apb4_system_targets": [ "apb4_system_targets": [
+1
View File
@@ -83,6 +83,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/uart.c \ $(ROOT_PATH)/crt/src/hal/uart.c \
$(ROOT_PATH)/crt/src/hal/gpio_math.c \ $(ROOT_PATH)/crt/src/hal/gpio_math.c \
$(ROOT_PATH)/crt/src/hal/gpio.c \ $(ROOT_PATH)/crt/src/hal/gpio.c \
$(ROOT_PATH)/crt/src/hal/hp_mailbox.c \
$(ROOT_PATH)/crt/src/hal/timer_math.c \ $(ROOT_PATH)/crt/src/hal/timer_math.c \
$(ROOT_PATH)/crt/src/hal/timer.c \ $(ROOT_PATH)/crt/src/hal/timer.c \
$(ROOT_PATH)/crt/src/hal/pwm.c \ $(ROOT_PATH)/crt/src/hal/pwm.c \
+20
View File
@@ -139,6 +139,20 @@
{"name": "uart0_rx_i_pad", "signal": "s_uart0_rx"} {"name": "uart0_rx_i_pad", "signal": "s_uart0_rx"}
] ]
}, },
{
"kind": "tri_output",
"direction": "output",
"ports": [
{"name": "uart1_tx_o_pad", "signal": "s_uart1_tx"}
]
},
{
"kind": "tri_input",
"direction": "inout",
"ports": [
{"name": "uart1_rx_i_pad", "signal": "s_uart1_rx"}
]
},
{ {
"kind": "tri_output", "kind": "tri_output",
"direction": "output", "direction": "output",
@@ -260,6 +274,8 @@
"usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n", "usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n",
"uart0_tx_o_pad": "uart0_tx_o", "uart0_tx_o_pad": "uart0_tx_o",
"uart0_rx_i_pad": "uart0_rx_i", "uart0_rx_i_pad": "uart0_rx_i",
"uart1_tx_o_pad": "uart1_tx_o",
"uart1_rx_i_pad": "uart1_rx_i",
"xpi_sck_o_pad": "xpi_sck_o", "xpi_sck_o_pad": "xpi_sck_o",
"xpi_nss0_o_pad": "xpi_nss0_o", "xpi_nss0_o_pad": "xpi_nss0_o",
"xpi_dat0_io_pad": "xpi_dat0_io", "xpi_dat0_io_pad": "xpi_dat0_io",
@@ -316,6 +332,8 @@
"jtag_tdi_i_pad": "s_jtag_tdi", "jtag_tdi_i_pad": "s_jtag_tdi",
"jtag_trst_n_i_pad": "s_jtag_trst_n", "jtag_trst_n_i_pad": "s_jtag_trst_n",
"jtag_tdo_o_pad": "s_jtag_tdo", "jtag_tdo_o_pad": "s_jtag_tdo",
"uart1_tx_o_pad": "s_uart1_tx",
"uart1_rx_i_pad": "s_uart1_rx",
"gpio_21_io_pad": "s_psram_sck", "gpio_21_io_pad": "s_psram_sck",
"gpio_22_io_pad": "s_psram_nss0", "gpio_22_io_pad": "s_psram_nss0",
"gpio_23_io_pad": "s_psram_dat0", "gpio_23_io_pad": "s_psram_dat0",
@@ -436,6 +454,8 @@
"usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n", "usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n",
"uart0_tx_o_pad": "s_uart0_tx", "uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx", "uart0_rx_i_pad": "s_uart0_rx",
"uart1_tx_o_pad": "s_uart1_tx",
"uart1_rx_i_pad": "s_uart1_rx",
"xpi_sck_o_pad": "s_xpi_sck_o", "xpi_sck_o_pad": "s_xpi_sck_o",
"xpi_nss0_o_pad": "s_xpi_nss0_o", "xpi_nss0_o_pad": "s_xpi_nss0_o",
"xpi_nss1_o_pad": "s_xpi_nss1_o", "xpi_nss1_o_pad": "s_xpi_nss1_o",
+57
View File
@@ -28,6 +28,7 @@ module apb4_periph (
gpio_if.dut gpio, gpio_if.dut gpio,
user_gpio_if.padctrl user_gpio, user_gpio_if.padctrl user_gpio,
uart_if.dut uart, uart_if.dut uart,
uart_if.dut uart1,
psram_if.dut psram, psram_if.dut psram,
spi_if.dut spisd, spi_if.dut spisd,
i2c_if.dut i2c0, i2c_if.dut i2c0,
@@ -52,6 +53,11 @@ module apb4_periph (
input logic [31:0] fault_addr_i, input logic [31:0] fault_addr_i,
input logic [3:0] fault_wstrb_i, input logic [3:0] fault_wstrb_i,
input logic fault_reserved_i, input logic fault_reserved_i,
output logic [63:0] hp_time_o,
output logic hp_timer_irq_o,
output logic hp_software_irq_o,
output logic hp_machine_external_irq_o,
output logic hp_supervisor_external_irq_o,
output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o
// verilog_format: on // verilog_format: on
); );
@@ -107,6 +113,7 @@ axi4_stream_if #(
); );
clint_if u_clint_if (); clint_if u_clint_if ();
clint_if #(.HartNum(2)) u_hp_clint_if ();
dma_req_if u_dma_req_if (); dma_req_if u_dma_req_if ();
logic s_dma_i2s_tx_stall, s_dma_i2s_rx_stall; logic s_dma_i2s_tx_stall, s_dma_i2s_rx_stall;
@@ -123,6 +130,9 @@ axi4_stream_if #(
logic s_tim0_irq, s_tim1_irq; logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq; logic s_dvp_irq;
logic s_xpi_irq; logic s_xpi_irq;
logic s_mailbox_lp_irq, s_mailbox_hp_irq;
logic [31:0] s_hp_plic_source;
logic [ 1:0] s_hp_plic_context_irq;
`ifdef PDK_GF180 `ifdef PDK_GF180
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1; localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
@@ -161,6 +171,17 @@ axi4_stream_if #(
assign u_dma_req_if.i2c1_rx_proc = ~s_dma_i2c1_rx_stall; assign u_dma_req_if.i2c1_rx_proc = ~s_dma_i2c1_rx_stall;
assign u_dma_req_if.crypto_in_proc = s_dma_crypto_in_proc; assign u_dma_req_if.crypto_in_proc = s_dma_crypto_in_proc;
assign u_dma_req_if.crypto_out_proc = s_dma_crypto_out_proc; assign u_dma_req_if.crypto_out_proc = s_dma_crypto_out_proc;
assign hp_time_o = u_hp_clint_if.mtime_o;
assign hp_timer_irq_o = u_hp_clint_if.timer_irq_o[1];
assign hp_software_irq_o = u_hp_clint_if.software_irq_o[1];
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1];
always_comb begin
s_hp_plic_source = '0;
s_hp_plic_source[1] = uart1.irq_o;
s_hp_plic_source[2] = s_mailbox_hp_irq;
end
`include "apb4_periph_irq_bindings.svh" `include "apb4_periph_irq_bindings.svh"
@@ -189,6 +210,42 @@ axi4_stream_if #(
.uart (uart) .uart (uart)
); );
// UART1 is dedicated to HP Linux and intentionally has no central-DMA route in the MVP.
apb4_uart u_apb4_uart1 (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(),
.dma_rx_stall_o(),
.apb4 (u_uart1_apb4_if),
.uart (uart1)
);
apb4_hp_mailbox u_apb4_hp_mailbox (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.apb4 (u_hp_mailbox_apb4_if),
.lp_irq_o(s_mailbox_lp_irq),
.hp_irq_o(s_mailbox_hp_irq)
);
apb4_clint #(
.HartNum(2)
) u_apb4_hp_aclint (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.timebase_tick_i(timebase_tick_i),
.apb4 (u_hp_aclint_apb4_if),
.clint (u_hp_clint_if)
);
apb4_plic u_apb4_hp_plic (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.source_i (s_hp_plic_source),
.apb4 (u_hp_plic_apb4_if),
.context_irq_o(s_hp_plic_context_irq)
);
apb4_timer u_apb4_timer0 ( apb4_timer u_apb4_timer0 (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
+1
View File
@@ -32,6 +32,7 @@ always_comb begin
s_resp_d = `AXI4_RESP_OKAY; s_resp_d = `AXI4_RESP_OKAY;
if (axi4.arvalid && axi4.arready) begin if (axi4.arvalid && axi4.arready) begin
s_addr_d = axi4.araddr; s_addr_d = axi4.araddr;
s_wstrb_d = '0;
s_write_d = 1'b0; s_write_d = 1'b0;
s_id_d = axi4.arid; s_id_d = axi4.arid;
s_len_d = axi4.arlen; s_len_d = axi4.arlen;
+7 -1
View File
@@ -19,6 +19,7 @@ module axi4_bus (
axi4_if.slave sdio1_axi4, axi4_if.slave sdio1_axi4,
axi4_if.slave usb2_axi4, axi4_if.slave usb2_axi4,
axi4_if.slave spisd_axi4, axi4_if.slave spisd_axi4,
axi4_if.slave hp_axi4,
axi4_if.master cfg_axi4, axi4_if.master cfg_axi4,
axi4_if.master system_axi4, axi4_if.master system_axi4,
axi4_if.master sram_axi4, axi4_if.master sram_axi4,
@@ -51,7 +52,7 @@ module axi4_bus (
output logic [63:0] perf_opipsram_wait_o output logic [63:0] perf_opipsram_wait_o
// verilog_format: on // verilog_format: on
); );
localparam int NumMasters = 7; localparam int NumMasters = 8;
localparam int NumTargets = 10; localparam int NumTargets = 10;
axi4_if #( axi4_if #(
@@ -109,6 +110,11 @@ module axi4_bus (
.sink (u_master_axi4_if[6]) .sink (u_master_axi4_if[6])
); );
axi4_connector u_hp_connector (
.source(hp_axi4),
.sink (u_master_axi4_if[7])
);
axi4_connector u_cfg_connector ( axi4_connector u_cfg_connector (
.source(u_target_axi4_if[0]), .source(u_target_axi4_if[0]),
.sink (cfg_axi4) .sink (cfg_axi4)
+372
View File
@@ -0,0 +1,372 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "axi4_define.svh"
// Converts one outstanding 64-bit AXI4 transaction into a 32-bit transaction.
module axi4_downsizer_64to32 (
input logic clk_i,
input logic rst_n_i,
axi4_if.slave wide,
axi4_if.master narrow
);
typedef enum logic {
Idle,
Active
} channel_state_e;
channel_state_e s_read_state_d, s_read_state_q;
channel_state_e s_write_state_d, s_write_state_q;
logic [2:0] s_read_id_d, s_read_id_q;
logic [2:0] s_write_id_d, s_write_id_q;
logic [2:0] s_read_size_d, s_read_size_q;
logic [2:0] s_write_size_d, s_write_size_q;
logic [7:0] s_read_len_d, s_read_len_q;
logic [7:0] s_write_len_d, s_write_len_q;
logic [1:0] s_read_burst_d, s_read_burst_q;
logic [1:0] s_write_burst_d, s_write_burst_q;
logic [31:0] s_read_addr_d, s_read_addr_q;
logic [31:0] s_write_addr_d, s_write_addr_q;
logic [31:0] s_read_next_addr;
logic [31:0] s_write_next_addr;
logic s_read_upper_d, s_read_upper_q;
logic s_write_upper_d, s_write_upper_q;
logic [31:0] s_read_lower_d, s_read_lower_q;
logic [1:0] s_read_resp_d, s_read_resp_q;
logic s_read_accept;
logic s_write_addr_accept;
logic s_narrow_read_accept;
logic s_narrow_write_accept;
logic s_wide_read_accept;
logic s_wide_write_accept;
logic s_b_pending_d, s_b_pending_q;
logic [1:0] s_b_resp_d, s_b_resp_q;
logic [8:0] s_read_beats;
logic [8:0] s_write_beats;
assign s_read_beats = {1'b0, wide.arlen} + 9'd1;
assign s_write_beats = {1'b0, wide.awlen} + 9'd1;
assign wide.arready = (s_read_state_q == Idle) && narrow.arready;
assign narrow.arvalid = (s_read_state_q == Idle) && wide.arvalid;
assign narrow.arid = '0;
assign narrow.araddr = wide.araddr;
assign narrow.arlen = (wide.arsize == 3'd3) ? 8'((s_read_beats << 1) - 9'd1) : wide.arlen;
assign narrow.arsize = (wide.arsize == 3'd3) ? 3'd2 : wide.arsize;
assign narrow.arburst = wide.arburst;
assign narrow.arlock = wide.arlock;
assign narrow.arcache = wide.arcache;
assign narrow.arprot = wide.arprot;
assign narrow.arqos = wide.arqos;
assign narrow.arregion = wide.arregion;
assign narrow.aruser = '0;
assign s_read_accept = wide.arvalid && wide.arready;
assign wide.rid = s_read_id_q;
assign wide.rdata = (s_read_size_q == 3'd3) ? {narrow.rdata, s_read_lower_q} :
s_read_addr_q[2] ? {narrow.rdata, 32'd0} : {32'd0, narrow.rdata};
assign wide.rresp = (s_read_size_q == 3'd3) && (s_read_resp_q != `AXI4_RESP_OKAY) ?
s_read_resp_q : narrow.rresp;
assign wide.rlast = narrow.rlast;
assign wide.ruser = '0;
assign wide.rvalid = (s_read_state_q == Active) && narrow.rvalid &&
((s_read_size_q != 3'd3) || s_read_upper_q);
assign narrow.rready = (s_read_state_q == Active) &&
(((s_read_size_q == 3'd3) && !s_read_upper_q) || wide.rready);
assign s_narrow_read_accept = narrow.rvalid && narrow.rready;
assign s_wide_read_accept = wide.rvalid && wide.rready;
always_comb begin
s_read_state_d = s_read_state_q;
s_read_id_d = s_read_id_q;
s_read_size_d = s_read_size_q;
s_read_len_d = s_read_len_q;
s_read_burst_d = s_read_burst_q;
s_read_addr_d = s_read_addr_q;
s_read_upper_d = s_read_upper_q;
s_read_lower_d = s_read_lower_q;
s_read_resp_d = s_read_resp_q;
if (s_read_accept) begin
s_read_state_d = Active;
s_read_id_d = wide.arid;
s_read_size_d = wide.arsize;
s_read_len_d = wide.arlen;
s_read_burst_d = wide.arburst;
s_read_addr_d = wide.araddr;
s_read_upper_d = 1'b0;
s_read_resp_d = `AXI4_RESP_OKAY;
end
if (s_narrow_read_accept && (s_read_size_q == 3'd3)) begin
if (!s_read_upper_q) begin
s_read_lower_d = narrow.rdata;
s_read_resp_d = narrow.rresp;
s_read_upper_d = 1'b1;
end else begin
s_read_upper_d = 1'b0;
s_read_resp_d = `AXI4_RESP_OKAY;
end
end
if (s_wide_read_accept) begin
s_read_addr_d = s_read_next_addr;
if (narrow.rlast) s_read_state_d = Idle;
end
end
assign wide.awready = (s_write_state_q == Idle) && narrow.awready;
assign narrow.awvalid = (s_write_state_q == Idle) && wide.awvalid;
assign narrow.awid = '0;
assign narrow.awaddr = wide.awaddr;
assign narrow.awlen = (wide.awsize == 3'd3) ? 8'((s_write_beats << 1) - 9'd1) : wide.awlen;
assign narrow.awsize = (wide.awsize == 3'd3) ? 3'd2 : wide.awsize;
assign narrow.awburst = wide.awburst;
assign narrow.awlock = wide.awlock;
assign narrow.awcache = wide.awcache;
assign narrow.awprot = wide.awprot;
assign narrow.awqos = wide.awqos;
assign narrow.awregion = wide.awregion;
assign narrow.awuser = '0;
assign s_write_addr_accept = wide.awvalid && wide.awready;
assign narrow.wdata = (s_write_size_q == 3'd3) ?
(s_write_upper_q ? wide.wdata[63:32] : wide.wdata[31:0]) :
(s_write_addr_q[2] ? wide.wdata[63:32] : wide.wdata[31:0]);
assign narrow.wstrb = (s_write_size_q == 3'd3) ?
(s_write_upper_q ? wide.wstrb[7:4] : wide.wstrb[3:0]) :
(s_write_addr_q[2] ? wide.wstrb[7:4] : wide.wstrb[3:0]);
assign narrow.wlast = wide.wlast && ((s_write_size_q != 3'd3) || s_write_upper_q);
assign narrow.wuser = '0;
assign narrow.wvalid = (s_write_state_q == Active) && wide.wvalid;
assign wide.wready = (s_write_state_q == Active) && narrow.wready &&
((s_write_size_q != 3'd3) || s_write_upper_q);
assign s_narrow_write_accept = narrow.wvalid && narrow.wready;
assign s_wide_write_accept = wide.wvalid && wide.wready;
assign wide.bid = s_write_id_q;
assign wide.bresp = s_b_resp_q;
assign wide.buser = '0;
assign wide.bvalid = s_b_pending_q;
assign narrow.bready = (s_write_state_q == Active) && !s_b_pending_q;
always_comb begin
s_write_state_d = s_write_state_q;
s_write_id_d = s_write_id_q;
s_write_size_d = s_write_size_q;
s_write_len_d = s_write_len_q;
s_write_burst_d = s_write_burst_q;
s_write_addr_d = s_write_addr_q;
s_write_upper_d = s_write_upper_q;
s_b_pending_d = s_b_pending_q;
s_b_resp_d = s_b_resp_q;
if (s_write_addr_accept) begin
s_write_state_d = Active;
s_write_id_d = wide.awid;
s_write_size_d = wide.awsize;
s_write_len_d = wide.awlen;
s_write_burst_d = wide.awburst;
s_write_addr_d = wide.awaddr;
s_write_upper_d = 1'b0;
s_b_pending_d = 1'b0;
end
if (s_narrow_write_accept && (s_write_size_q == 3'd3)) begin
s_write_upper_d = !s_write_upper_q;
end
if (s_wide_write_accept) s_write_addr_d = s_write_next_addr;
if (narrow.bvalid && narrow.bready) begin
s_b_pending_d = 1'b1;
s_b_resp_d = narrow.bresp;
end
if (wide.bvalid && wide.bready) begin
s_b_pending_d = 1'b0;
s_write_state_d = Idle;
end
end
axi4_addr_gen #(
.ADDR_WIDTH(32)
) u_read_addr_gen (
.alen_i (s_read_len_q),
.asize_i (s_read_size_q),
.aburst_i(s_read_burst_q),
.addr_i (s_read_addr_q),
.addr_o (s_read_next_addr)
);
axi4_addr_gen #(
.ADDR_WIDTH(32)
) u_write_addr_gen (
.alen_i (s_write_len_q),
.asize_i (s_write_size_q),
.aburst_i(s_write_burst_q),
.addr_i (s_write_addr_q),
.addr_o (s_write_next_addr)
);
dffr #(
.DATA_WIDTH(1)
) u_read_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_state_d),
.dat_o (s_read_state_q)
);
dffr #(
.DATA_WIDTH(1)
) u_write_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_state_d),
.dat_o (s_write_state_q)
);
dffr #(
.DATA_WIDTH(3)
) u_read_id_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_id_d),
.dat_o (s_read_id_q)
);
dffr #(
.DATA_WIDTH(3)
) u_write_id_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_id_d),
.dat_o (s_write_id_q)
);
dffr #(
.DATA_WIDTH(3)
) u_read_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_size_d),
.dat_o (s_read_size_q)
);
dffr #(
.DATA_WIDTH(3)
) u_write_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_size_d),
.dat_o (s_write_size_q)
);
dffr #(
.DATA_WIDTH(8)
) u_read_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_len_d),
.dat_o (s_read_len_q)
);
dffr #(
.DATA_WIDTH(8)
) u_write_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_len_d),
.dat_o (s_write_len_q)
);
dffr #(
.DATA_WIDTH(2)
) u_read_burst_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_burst_d),
.dat_o (s_read_burst_q)
);
dffr #(
.DATA_WIDTH(2)
) u_write_burst_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_burst_d),
.dat_o (s_write_burst_q)
);
dffr #(
.DATA_WIDTH(32)
) u_read_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_addr_d),
.dat_o (s_read_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_write_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_addr_d),
.dat_o (s_write_addr_q)
);
dffr #(
.DATA_WIDTH(1)
) u_read_upper_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_upper_d),
.dat_o (s_read_upper_q)
);
dffr #(
.DATA_WIDTH(1)
) u_write_upper_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_upper_d),
.dat_o (s_write_upper_q)
);
dffr #(
.DATA_WIDTH(32)
) u_read_lower_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_lower_d),
.dat_o (s_read_lower_q)
);
dffr #(
.DATA_WIDTH(2)
) u_read_resp_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_resp_d),
.dat_o (s_read_resp_q)
);
dffr #(
.DATA_WIDTH(1)
) u_b_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_b_pending_d),
.dat_o (s_b_pending_q)
);
dffr #(
.DATA_WIDTH(2)
) u_b_resp_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_b_resp_d),
.dat_o (s_b_resp_q)
);
`ifndef SYNTHESIS
property p_wide_read_geometry;
@(posedge clk_i) disable iff (!rst_n_i) wide.arvalid |->
((wide.arsize <= 3'd3) &&
((wide.arsize != 3'd3) || ((wide.arlen <= 8'd7) && (wide.araddr[2:0] == 3'd0))));
endproperty
assert property (p_wide_read_geometry);
property p_wide_write_geometry;
@(posedge clk_i) disable iff (!rst_n_i) wide.awvalid |->
((wide.awsize <= 3'd3) &&
((wide.awsize != 3'd3) || ((wide.awlen <= 8'd7) && (wide.awaddr[2:0] == 3'd0))));
endproperty
assert property (p_wide_write_geometry);
`endif
endmodule
+1 -1
View File
@@ -606,7 +606,7 @@ module axi4_interconnect #(
`ifndef SYNTHESIS `ifndef SYNTHESIS
initial begin initial begin
if (NumMasters != 7 || ((NumTargets != 9) && (NumTargets != 10))) begin if (((NumMasters != 7) && (NumMasters != 8)) || ((NumTargets != 9) && (NumTargets != 10))) begin
$fatal(1, "axi4_interconnect: invalid topology dimensions"); $fatal(1, "axi4_interconnect: invalid topology dimensions");
end end
end end
+198
View File
@@ -0,0 +1,198 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// Serializes the three HP compatibility ports onto one legacy AXI4 master.
module hp_axi4_mux3 (
input logic clk_i,
input logic rst_n_i,
axi4_if.slave icache,
axi4_if.slave dcache,
axi4_if.slave mmio,
axi4_if.master axi4
);
typedef enum logic {
Idle,
Active
} state_e;
state_e s_state_d, s_state_q;
logic [1:0] s_owner_d, s_owner_q;
logic s_write_d, s_write_q;
logic [1:0] s_selected;
logic s_selected_write;
logic s_addr_accept;
logic s_terminal;
always_comb begin
s_selected = 2'd0;
s_selected_write = 1'b0;
if (mmio.arvalid || mmio.awvalid) begin
s_selected = 2'd2;
s_selected_write = mmio.awvalid && !mmio.arvalid;
end else if (dcache.arvalid || dcache.awvalid) begin
s_selected = 2'd1;
s_selected_write = dcache.awvalid && !dcache.arvalid;
end else begin
s_selected = 2'd0;
s_selected_write = icache.awvalid && !icache.arvalid;
end
end
assign axi4.awid = (s_state_q == Idle) ?
((s_selected == 2'd2) ? mmio.awid :
(s_selected == 2'd1) ? dcache.awid : icache.awid) : '0;
assign axi4.awaddr = (s_selected == 2'd2) ? mmio.awaddr :
(s_selected == 2'd1) ? dcache.awaddr : icache.awaddr;
assign axi4.awlen = (s_selected == 2'd2) ? mmio.awlen :
(s_selected == 2'd1) ? dcache.awlen : icache.awlen;
assign axi4.awsize = (s_selected == 2'd2) ? mmio.awsize :
(s_selected == 2'd1) ? dcache.awsize : icache.awsize;
assign axi4.awburst = (s_selected == 2'd2) ? mmio.awburst :
(s_selected == 2'd1) ? dcache.awburst : icache.awburst;
assign axi4.awlock = (s_selected == 2'd2) ? mmio.awlock :
(s_selected == 2'd1) ? dcache.awlock : icache.awlock;
assign axi4.awcache = (s_selected == 2'd2) ? mmio.awcache :
(s_selected == 2'd1) ? dcache.awcache : icache.awcache;
assign axi4.awprot = (s_selected == 2'd2) ? mmio.awprot :
(s_selected == 2'd1) ? dcache.awprot : icache.awprot;
assign axi4.awqos = (s_selected == 2'd2) ? mmio.awqos :
(s_selected == 2'd1) ? dcache.awqos : icache.awqos;
assign axi4.awregion = (s_selected == 2'd2) ? mmio.awregion :
(s_selected == 2'd1) ? dcache.awregion : icache.awregion;
assign axi4.awuser = '0;
assign axi4.awvalid = (s_state_q == Idle) && s_selected_write &&
((s_selected == 2'd2) ? mmio.awvalid :
(s_selected == 2'd1) ? dcache.awvalid : icache.awvalid);
assign mmio.awready = (s_state_q == Idle) && (s_selected == 2'd2) &&
s_selected_write && axi4.awready;
assign dcache.awready = (s_state_q == Idle) && (s_selected == 2'd1) &&
s_selected_write && axi4.awready;
assign icache.awready = (s_state_q == Idle) && (s_selected == 2'd0) &&
s_selected_write && axi4.awready;
assign axi4.arid = (s_selected == 2'd2) ? mmio.arid :
(s_selected == 2'd1) ? dcache.arid : icache.arid;
assign axi4.araddr = (s_selected == 2'd2) ? mmio.araddr :
(s_selected == 2'd1) ? dcache.araddr : icache.araddr;
assign axi4.arlen = (s_selected == 2'd2) ? mmio.arlen :
(s_selected == 2'd1) ? dcache.arlen : icache.arlen;
assign axi4.arsize = (s_selected == 2'd2) ? mmio.arsize :
(s_selected == 2'd1) ? dcache.arsize : icache.arsize;
assign axi4.arburst = (s_selected == 2'd2) ? mmio.arburst :
(s_selected == 2'd1) ? dcache.arburst : icache.arburst;
assign axi4.arlock = (s_selected == 2'd2) ? mmio.arlock :
(s_selected == 2'd1) ? dcache.arlock : icache.arlock;
assign axi4.arcache = (s_selected == 2'd2) ? mmio.arcache :
(s_selected == 2'd1) ? dcache.arcache : icache.arcache;
assign axi4.arprot = (s_selected == 2'd2) ? mmio.arprot :
(s_selected == 2'd1) ? dcache.arprot : icache.arprot;
assign axi4.arqos = (s_selected == 2'd2) ? mmio.arqos :
(s_selected == 2'd1) ? dcache.arqos : icache.arqos;
assign axi4.arregion = (s_selected == 2'd2) ? mmio.arregion :
(s_selected == 2'd1) ? dcache.arregion : icache.arregion;
assign axi4.aruser = '0;
assign axi4.arvalid = (s_state_q == Idle) && !s_selected_write &&
((s_selected == 2'd2) ? mmio.arvalid :
(s_selected == 2'd1) ? dcache.arvalid : icache.arvalid);
assign mmio.arready = (s_state_q == Idle) && (s_selected == 2'd2) &&
!s_selected_write && axi4.arready;
assign dcache.arready = (s_state_q == Idle) && (s_selected == 2'd1) &&
!s_selected_write && axi4.arready;
assign icache.arready = (s_state_q == Idle) && (s_selected == 2'd0) &&
!s_selected_write && axi4.arready;
assign axi4.wdata = (s_owner_q == 2'd2) ? mmio.wdata :
(s_owner_q == 2'd1) ? dcache.wdata : icache.wdata;
assign axi4.wstrb = (s_owner_q == 2'd2) ? mmio.wstrb :
(s_owner_q == 2'd1) ? dcache.wstrb : icache.wstrb;
assign axi4.wlast = (s_owner_q == 2'd2) ? mmio.wlast :
(s_owner_q == 2'd1) ? dcache.wlast : icache.wlast;
assign axi4.wuser = '0;
assign axi4.wvalid = (s_state_q == Active) && s_write_q &&
((s_owner_q == 2'd2) ? mmio.wvalid :
(s_owner_q == 2'd1) ? dcache.wvalid : icache.wvalid);
assign mmio.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd2) && axi4.wready;
assign dcache.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd1) && axi4.wready;
assign icache.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd0) && axi4.wready;
assign mmio.bid = axi4.bid;
assign dcache.bid = axi4.bid;
assign icache.bid = axi4.bid;
assign mmio.bresp = axi4.bresp;
assign dcache.bresp = axi4.bresp;
assign icache.bresp = axi4.bresp;
assign mmio.buser = '0;
assign dcache.buser = '0;
assign icache.buser = '0;
assign mmio.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd2) && axi4.bvalid;
assign dcache.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd1) && axi4.bvalid;
assign icache.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd0) && axi4.bvalid;
assign axi4.bready = ((s_state_q == Active) && s_write_q) ?
((s_owner_q == 2'd2) ? mmio.bready :
(s_owner_q == 2'd1) ? dcache.bready : icache.bready) : 1'b0;
assign mmio.rid = axi4.rid;
assign dcache.rid = axi4.rid;
assign icache.rid = axi4.rid;
assign mmio.rdata = axi4.rdata;
assign dcache.rdata = axi4.rdata;
assign icache.rdata = axi4.rdata;
assign mmio.rresp = axi4.rresp;
assign dcache.rresp = axi4.rresp;
assign icache.rresp = axi4.rresp;
assign mmio.rlast = axi4.rlast;
assign dcache.rlast = axi4.rlast;
assign icache.rlast = axi4.rlast;
assign mmio.ruser = '0;
assign dcache.ruser = '0;
assign icache.ruser = '0;
assign mmio.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd2) && axi4.rvalid;
assign dcache.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd1) && axi4.rvalid;
assign icache.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd0) && axi4.rvalid;
assign axi4.rready = ((s_state_q == Active) && !s_write_q) ?
((s_owner_q == 2'd2) ? mmio.rready :
(s_owner_q == 2'd1) ? dcache.rready : icache.rready) : 1'b0;
assign s_addr_accept = (axi4.awvalid && axi4.awready) || (axi4.arvalid && axi4.arready);
assign s_terminal = (s_write_q && axi4.bvalid && axi4.bready) ||
(!s_write_q && axi4.rvalid && axi4.rready && axi4.rlast);
always_comb begin
s_state_d = s_state_q;
s_owner_d = s_owner_q;
s_write_d = s_write_q;
if ((s_state_q == Idle) && s_addr_accept) begin
s_state_d = Active;
s_owner_d = s_selected;
s_write_d = axi4.awvalid && axi4.awready;
end
if ((s_state_q == Active) && s_terminal) s_state_d = Idle;
end
dffr #(
.DATA_WIDTH(1)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_q)
);
dffr #(
.DATA_WIDTH(2)
) u_owner_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_owner_d),
.dat_o (s_owner_q)
);
dffr #(
.DATA_WIDTH(1)
) u_write_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_write_d),
.dat_o (s_write_q)
);
endmodule
+169
View File
@@ -0,0 +1,169 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "axi4_define.svh"
module hp_core_wrapper (
// verilog_format: off -- preserve generated-core boundary alignment
input logic clk_i,
input logic rst_n_i,
input logic core_reset_i,
input logic [63:0] time_i,
input logic timer_irq_i,
input logic software_irq_i,
input logic machine_external_irq_i,
input logic supervisor_external_irq_i,
input logic jtag_tck_i,
input logic jtag_tms_i,
input logic jtag_tdi_i,
input logic jtag_trst_n_i,
output logic jtag_tdo_o,
output logic debug_reset_req_o,
axi4_if.master icache_axi4,
axi4_if.master dcache_axi4,
axi4_if.master mmio_axi4
// verilog_format: on
);
logic s_unused_debug_stoptime;
assign icache_axi4.awid = '0;
assign icache_axi4.awaddr = '0;
assign icache_axi4.awlen = '0;
assign icache_axi4.awsize = '0;
assign icache_axi4.awburst = '0;
assign icache_axi4.awlock = '0;
assign icache_axi4.awcache = '0;
assign icache_axi4.awprot = '0;
assign icache_axi4.awqos = '0;
assign icache_axi4.awregion = '0;
assign icache_axi4.awuser = '0;
assign icache_axi4.awvalid = 1'b0;
assign icache_axi4.wdata = '0;
assign icache_axi4.wstrb = '0;
assign icache_axi4.wlast = 1'b0;
assign icache_axi4.wuser = '0;
assign icache_axi4.wvalid = 1'b0;
assign icache_axi4.bready = 1'b1;
// verilog_format: off -- generated VexiiRiscv ports are upstream ABI
vexii_riscv_hp_generated u_vexii_riscv_hp_generated (
.EmbeddedRiscvJtag_logic_jtag_tms (jtag_tms_i),
.EmbeddedRiscvJtag_logic_jtag_tdi (jtag_tdi_i),
.EmbeddedRiscvJtag_logic_jtag_tdo (jtag_tdo_o),
.EmbeddedRiscvJtag_logic_jtag_tck (jtag_tck_i),
.EmbeddedRiscvJtag_logic_ndmreset (debug_reset_req_o),
.LsuL1Axi4Plugin_logic_axi_aw_valid (dcache_axi4.awvalid),
.LsuL1Axi4Plugin_logic_axi_aw_ready (dcache_axi4.awready),
.LsuL1Axi4Plugin_logic_axi_aw_payload_addr (dcache_axi4.awaddr),
.LsuL1Axi4Plugin_logic_axi_aw_payload_id (dcache_axi4.awid),
.LsuL1Axi4Plugin_logic_axi_aw_payload_len (dcache_axi4.awlen),
.LsuL1Axi4Plugin_logic_axi_aw_payload_size (dcache_axi4.awsize),
.LsuL1Axi4Plugin_logic_axi_aw_payload_burst (dcache_axi4.awburst),
.LsuL1Axi4Plugin_logic_axi_aw_payload_cache (dcache_axi4.awcache),
.LsuL1Axi4Plugin_logic_axi_aw_payload_prot (dcache_axi4.awprot),
.LsuL1Axi4Plugin_logic_axi_w_valid (dcache_axi4.wvalid),
.LsuL1Axi4Plugin_logic_axi_w_ready (dcache_axi4.wready),
.LsuL1Axi4Plugin_logic_axi_w_payload_data (dcache_axi4.wdata),
.LsuL1Axi4Plugin_logic_axi_w_payload_strb (dcache_axi4.wstrb),
.LsuL1Axi4Plugin_logic_axi_w_payload_last (dcache_axi4.wlast),
.LsuL1Axi4Plugin_logic_axi_b_valid (dcache_axi4.bvalid),
.LsuL1Axi4Plugin_logic_axi_b_ready (dcache_axi4.bready),
.LsuL1Axi4Plugin_logic_axi_b_payload_id (dcache_axi4.bid),
.LsuL1Axi4Plugin_logic_axi_b_payload_resp (dcache_axi4.bresp),
.LsuL1Axi4Plugin_logic_axi_ar_valid (dcache_axi4.arvalid),
.LsuL1Axi4Plugin_logic_axi_ar_ready (dcache_axi4.arready),
.LsuL1Axi4Plugin_logic_axi_ar_payload_addr (dcache_axi4.araddr),
.LsuL1Axi4Plugin_logic_axi_ar_payload_id (dcache_axi4.arid),
.LsuL1Axi4Plugin_logic_axi_ar_payload_len (dcache_axi4.arlen),
.LsuL1Axi4Plugin_logic_axi_ar_payload_size (dcache_axi4.arsize),
.LsuL1Axi4Plugin_logic_axi_ar_payload_burst (dcache_axi4.arburst),
.LsuL1Axi4Plugin_logic_axi_ar_payload_cache (dcache_axi4.arcache),
.LsuL1Axi4Plugin_logic_axi_ar_payload_prot (dcache_axi4.arprot),
.LsuL1Axi4Plugin_logic_axi_r_valid (dcache_axi4.rvalid),
.LsuL1Axi4Plugin_logic_axi_r_ready (dcache_axi4.rready),
.LsuL1Axi4Plugin_logic_axi_r_payload_data (dcache_axi4.rdata),
.LsuL1Axi4Plugin_logic_axi_r_payload_id (dcache_axi4.rid),
.LsuL1Axi4Plugin_logic_axi_r_payload_resp (dcache_axi4.rresp),
.LsuL1Axi4Plugin_logic_axi_r_payload_last (dcache_axi4.rlast),
.PrivilegedPlugin_logic_rdtime (time_i),
.PrivilegedPlugin_logic_harts_0_int_m_timer (timer_irq_i),
.PrivilegedPlugin_logic_harts_0_int_m_software (software_irq_i),
.PrivilegedPlugin_logic_harts_0_int_m_external (machine_external_irq_i),
.PrivilegedPlugin_logic_harts_0_int_s_external (supervisor_external_irq_i),
.FetchL1Axi4Plugin_logic_axi_ar_valid (icache_axi4.arvalid),
.FetchL1Axi4Plugin_logic_axi_ar_ready (icache_axi4.arready),
.FetchL1Axi4Plugin_logic_axi_ar_payload_addr (icache_axi4.araddr),
.FetchL1Axi4Plugin_logic_axi_ar_payload_id (icache_axi4.arid[0]),
.FetchL1Axi4Plugin_logic_axi_ar_payload_len (icache_axi4.arlen),
.FetchL1Axi4Plugin_logic_axi_ar_payload_size (icache_axi4.arsize),
.FetchL1Axi4Plugin_logic_axi_ar_payload_burst (icache_axi4.arburst),
.FetchL1Axi4Plugin_logic_axi_ar_payload_cache (icache_axi4.arcache),
.FetchL1Axi4Plugin_logic_axi_ar_payload_prot (icache_axi4.arprot),
.FetchL1Axi4Plugin_logic_axi_r_valid (icache_axi4.rvalid),
.FetchL1Axi4Plugin_logic_axi_r_ready (icache_axi4.rready),
.FetchL1Axi4Plugin_logic_axi_r_payload_data (icache_axi4.rdata),
.FetchL1Axi4Plugin_logic_axi_r_payload_id (icache_axi4.rid[0]),
.FetchL1Axi4Plugin_logic_axi_r_payload_resp (icache_axi4.rresp),
.FetchL1Axi4Plugin_logic_axi_r_payload_last (icache_axi4.rlast),
.PrivilegedPlugin_logic_harts_0_debug_stoptime (s_unused_debug_stoptime),
.LsuCachelessAxi4Plugin_logic_axi_aw_valid (mmio_axi4.awvalid),
.LsuCachelessAxi4Plugin_logic_axi_aw_ready (mmio_axi4.awready),
.LsuCachelessAxi4Plugin_logic_axi_aw_payload_addr (mmio_axi4.awaddr),
.LsuCachelessAxi4Plugin_logic_axi_aw_payload_size (mmio_axi4.awsize),
.LsuCachelessAxi4Plugin_logic_axi_aw_payload_cache (mmio_axi4.awcache),
.LsuCachelessAxi4Plugin_logic_axi_aw_payload_prot (mmio_axi4.awprot),
.LsuCachelessAxi4Plugin_logic_axi_w_valid (mmio_axi4.wvalid),
.LsuCachelessAxi4Plugin_logic_axi_w_ready (mmio_axi4.wready),
.LsuCachelessAxi4Plugin_logic_axi_w_payload_data (mmio_axi4.wdata),
.LsuCachelessAxi4Plugin_logic_axi_w_payload_strb (mmio_axi4.wstrb),
.LsuCachelessAxi4Plugin_logic_axi_w_payload_last (mmio_axi4.wlast),
.LsuCachelessAxi4Plugin_logic_axi_b_valid (mmio_axi4.bvalid),
.LsuCachelessAxi4Plugin_logic_axi_b_ready (mmio_axi4.bready),
.LsuCachelessAxi4Plugin_logic_axi_b_payload_resp (mmio_axi4.bresp),
.LsuCachelessAxi4Plugin_logic_axi_ar_valid (mmio_axi4.arvalid),
.LsuCachelessAxi4Plugin_logic_axi_ar_ready (mmio_axi4.arready),
.LsuCachelessAxi4Plugin_logic_axi_ar_payload_addr (mmio_axi4.araddr),
.LsuCachelessAxi4Plugin_logic_axi_ar_payload_size (mmio_axi4.arsize),
.LsuCachelessAxi4Plugin_logic_axi_ar_payload_cache (mmio_axi4.arcache),
.LsuCachelessAxi4Plugin_logic_axi_ar_payload_prot (mmio_axi4.arprot),
.LsuCachelessAxi4Plugin_logic_axi_r_valid (mmio_axi4.rvalid),
.LsuCachelessAxi4Plugin_logic_axi_r_ready (mmio_axi4.rready),
.LsuCachelessAxi4Plugin_logic_axi_r_payload_data (mmio_axi4.rdata),
.LsuCachelessAxi4Plugin_logic_axi_r_payload_resp (mmio_axi4.rresp),
.LsuCachelessAxi4Plugin_logic_axi_r_payload_last (mmio_axi4.rlast),
.clk (clk_i),
.reset (!rst_n_i || core_reset_i),
.EmbeddedRiscvJtag_logic_debug_reset (!jtag_trst_n_i)
);
// verilog_format: on
assign icache_axi4.arid[2:1] = '0;
assign icache_axi4.arlock = 1'b0;
assign icache_axi4.arqos = '0;
assign icache_axi4.arregion = '0;
assign icache_axi4.aruser = '0;
assign dcache_axi4.awlock = 1'b0;
assign dcache_axi4.awqos = '0;
assign dcache_axi4.awregion = '0;
assign dcache_axi4.awuser = '0;
assign dcache_axi4.wuser = '0;
assign dcache_axi4.arlock = 1'b0;
assign dcache_axi4.arqos = '0;
assign dcache_axi4.arregion = '0;
assign dcache_axi4.aruser = '0;
assign mmio_axi4.awid = '0;
assign mmio_axi4.awlen = '0;
assign mmio_axi4.awburst = `AXI4_BURST_TYPE_INCR;
assign mmio_axi4.awlock = 1'b0;
assign mmio_axi4.awqos = '0;
assign mmio_axi4.awregion = '0;
assign mmio_axi4.awuser = '0;
assign mmio_axi4.wuser = '0;
assign mmio_axi4.arid = '0;
assign mmio_axi4.arlen = '0;
assign mmio_axi4.arburst = `AXI4_BURST_TYPE_INCR;
assign mmio_axi4.arlock = 1'b0;
assign mmio_axi4.arqos = '0;
assign mmio_axi4.arregion = '0;
assign mmio_axi4.aruser = '0;
endmodule
+148
View File
@@ -0,0 +1,148 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module hp_subsystem (
// verilog_format: off -- preserve HP integration boundary alignment
input logic clk_i,
input logic rst_n_i,
input logic core_reset_i,
input logic [63:0] time_i,
input logic timer_irq_i,
input logic software_irq_i,
input logic machine_external_irq_i,
input logic supervisor_external_irq_i,
input logic jtag_tck_i,
input logic jtag_tms_i,
input logic jtag_tdi_i,
input logic jtag_trst_n_i,
output logic jtag_tdo_o,
output logic debug_reset_req_o,
axi4_if.master axi4
// verilog_format: on
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) u_icache_wide_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) u_dcache_wide_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) u_mmio_wide_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_icache_narrow_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_dcache_narrow_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_mmio_narrow_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) u_hp_mux_axi4_if (
.aclk (clk_i),
.aresetn(rst_n_i)
);
hp_core_wrapper u_hp_core_wrapper (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.core_reset_i (core_reset_i),
.time_i (time_i),
.timer_irq_i (timer_irq_i),
.software_irq_i (software_irq_i),
.machine_external_irq_i (machine_external_irq_i),
.supervisor_external_irq_i(supervisor_external_irq_i),
.jtag_tck_i (jtag_tck_i),
.jtag_tms_i (jtag_tms_i),
.jtag_tdi_i (jtag_tdi_i),
.jtag_trst_n_i (jtag_trst_n_i),
.jtag_tdo_o (jtag_tdo_o),
.debug_reset_req_o (debug_reset_req_o),
.icache_axi4 (u_icache_wide_axi4_if),
.dcache_axi4 (u_dcache_wide_axi4_if),
.mmio_axi4 (u_mmio_wide_axi4_if)
);
axi4_downsizer_64to32 u_icache_downsizer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.wide (u_icache_wide_axi4_if),
.narrow (u_icache_narrow_axi4_if)
);
axi4_downsizer_64to32 u_dcache_downsizer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.wide (u_dcache_wide_axi4_if),
.narrow (u_dcache_narrow_axi4_if)
);
axi4_downsizer_64to32 u_mmio_downsizer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.wide (u_mmio_wide_axi4_if),
.narrow (u_mmio_narrow_axi4_if)
);
hp_axi4_mux3 u_hp_axi4_mux3 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.icache (u_icache_narrow_axi4_if),
.dcache (u_dcache_narrow_axi4_if),
.mmio (u_mmio_narrow_axi4_if),
.axi4 (u_hp_mux_axi4_if)
);
axi4_regslice #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1),
.BYPASS (1'b0)
) u_hp_axi4_regslice (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(core_reset_i),
.slv (u_hp_mux_axi4_if),
.mst (axi4)
);
endmodule
+134 -40
View File
@@ -25,6 +25,8 @@ module retrosoc (
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,
input logic uart1_rx_i,
output logic uart1_tx_o,
xpi_if.dut xpi, xpi_if.dut xpi,
sdram_if.dut sdram, sdram_if.dut sdram,
sdio_if.dut sdio1, sdio_if.dut sdio1,
@@ -58,10 +60,13 @@ module retrosoc (
u_spisd_axi4_if (.aclk(clk_i), .aresetn(rst_n_i)); u_spisd_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_opipsram_axi4_if (.aclk(clk_i), .aresetn(rst_n_i)); u_opipsram_axi4_if (.aclk(clk_i), .aresetn(rst_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1))
u_hp_axi4_if (.aclk(clk_i), .aresetn(rst_n_i));
user_gpio_if u_user_gpio_if (); user_gpio_if u_user_gpio_if ();
// ip interface // ip interface
gpio_if u_gpio_if (); gpio_if u_gpio_if ();
uart_if u_uart0_if (); uart_if u_uart0_if ();
uart_if u_uart1_if ();
psram_if u_psram_if (); psram_if u_psram_if ();
spi_if u_spisd_if (); spi_if u_spisd_if ();
i2c_if u_i2c0_if (); i2c_if u_i2c0_if ();
@@ -103,6 +108,15 @@ module retrosoc (
logic [ 63:0] s_perf_opipsram_wait; logic [ 63:0] s_perf_opipsram_wait;
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;
logic s_hp_jtag_tdo;
logic s_hp_debug_reset_req;
logic s_lp_jtag_tdo;
logic s_debug_hp_sel_jtag;
logic [ 63:0] s_hp_time;
logic s_hp_timer_irq;
logic s_hp_software_irq;
logic s_hp_machine_external_irq;
logic s_hp_supervisor_external_irq;
`ifdef HAVE_SRAM_IF `ifdef HAVE_SRAM_IF
localparam bit SramPresent = 1'b1; localparam bit SramPresent = 1'b1;
@@ -131,8 +145,15 @@ module retrosoc (
assign u_sysctrl_if.perf_psram_wait_i = s_perf_psram_wait + s_perf_opipsram_wait; assign u_sysctrl_if.perf_psram_wait_i = s_perf_psram_wait + s_perf_opipsram_wait;
assign u_sysctrl_if.perf_flash_wait_i = s_perf_flash_wait; assign u_sysctrl_if.perf_flash_wait_i = s_perf_flash_wait;
assign u_sysctrl_if.rtc_wake_i = s_rtc_wake; assign u_sysctrl_if.rtc_wake_i = s_rtc_wake;
assign u_uart0_if.rx_i = uart_rx_i; `ifdef HAVE_HP
assign uart_tx_o = u_uart0_if.tx_o; assign u_sysctrl_if.hp_present_i = 1'b1;
`else
assign u_sysctrl_if.hp_present_i = 1'b0;
`endif
assign u_uart0_if.rx_i = uart_rx_i;
assign uart_tx_o = u_uart0_if.tx_o;
assign u_uart1_if.rx_i = uart1_rx_i;
assign uart1_tx_o = u_uart1_if.tx_o;
gpio_pad_bridge u_gpio_pad_bridge ( gpio_pad_bridge u_gpio_pad_bridge (
.inner(u_gpio_if), .inner(u_gpio_if),
@@ -151,13 +172,25 @@ core_wrapper u_core_wrapper (
`include "soc_mgmt_core_wrapper_fabric.svh" `include "soc_mgmt_core_wrapper_fabric.svh"
.irq_i (s_irq), .irq_i (s_irq),
.jtag_tck_i (jtag_tck_i), .jtag_tck_i (jtag_tck_i),
.jtag_tms_i (jtag_tms_i), .jtag_tms_i (s_debug_hp_sel_jtag ? 1'b1 : jtag_tms_i),
.jtag_tdi_i (jtag_tdi_i), .jtag_tdi_i (s_debug_hp_sel_jtag ? 1'b0 : jtag_tdi_i),
.jtag_trst_n_i (jtag_trst_n_i), .jtag_trst_n_i (jtag_trst_n_i),
.jtag_tdo_o (jtag_tdo_o), .jtag_tdo_o (s_lp_jtag_tdo),
.debug_halted_o(s_mgmt_debug_halted) .debug_halted_o(s_mgmt_debug_halted)
); );
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_debug_select_sync (
.clk_i (jtag_tck_i),
.rst_n_i(jtag_trst_n_i),
.dat_i (u_sysctrl_if.debug_hp_select_o),
.dat_o (s_debug_hp_sel_jtag)
);
assign jtag_tdo_o = s_debug_hp_sel_jtag ? s_hp_jtag_tdo : s_lp_jtag_tdo;
assign s_user_irq = u_sysctrl_if.user_bus_enable_o ? (s_irq & `SOC_USER_IRQ_MASK) : '0; assign s_user_irq = u_sysctrl_if.user_bus_enable_o ? (s_irq & `SOC_USER_IRQ_MASK) : '0;
user_core_top u_user_core_top ( user_core_top u_user_core_top (
.clk_i (clk_i), .clk_i (clk_i),
@@ -168,6 +201,60 @@ core_wrapper u_core_wrapper (
.core_reset_i(u_sysctrl_if.core_reset_o) .core_reset_i(u_sysctrl_if.core_reset_o)
); );
`ifdef HAVE_HP
hp_subsystem u_hp_subsystem (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.core_reset_i (!u_sysctrl_if.hp_release_o || s_hp_debug_reset_req),
.time_i (s_hp_time),
.timer_irq_i (s_hp_timer_irq),
.software_irq_i (s_hp_software_irq),
.machine_external_irq_i (s_hp_machine_external_irq),
.supervisor_external_irq_i(s_hp_supervisor_external_irq),
.jtag_tck_i (jtag_tck_i),
.jtag_tms_i (s_debug_hp_sel_jtag ? jtag_tms_i : 1'b1),
.jtag_tdi_i (s_debug_hp_sel_jtag ? jtag_tdi_i : 1'b0),
.jtag_trst_n_i (jtag_trst_n_i),
.jtag_tdo_o (s_hp_jtag_tdo),
.debug_reset_req_o (s_hp_debug_reset_req),
.axi4 (u_hp_axi4_if)
);
`else
assign u_hp_axi4_if.awid = '0;
assign u_hp_axi4_if.awaddr = '0;
assign u_hp_axi4_if.awlen = '0;
assign u_hp_axi4_if.awsize = '0;
assign u_hp_axi4_if.awburst = '0;
assign u_hp_axi4_if.awlock = '0;
assign u_hp_axi4_if.awcache = '0;
assign u_hp_axi4_if.awprot = '0;
assign u_hp_axi4_if.awqos = '0;
assign u_hp_axi4_if.awregion = '0;
assign u_hp_axi4_if.awuser = '0;
assign u_hp_axi4_if.awvalid = 1'b0;
assign u_hp_axi4_if.wdata = '0;
assign u_hp_axi4_if.wstrb = '0;
assign u_hp_axi4_if.wlast = 1'b0;
assign u_hp_axi4_if.wuser = '0;
assign u_hp_axi4_if.wvalid = 1'b0;
assign u_hp_axi4_if.bready = 1'b1;
assign u_hp_axi4_if.arid = '0;
assign u_hp_axi4_if.araddr = '0;
assign u_hp_axi4_if.arlen = '0;
assign u_hp_axi4_if.arsize = '0;
assign u_hp_axi4_if.arburst = '0;
assign u_hp_axi4_if.arlock = '0;
assign u_hp_axi4_if.arcache = '0;
assign u_hp_axi4_if.arprot = '0;
assign u_hp_axi4_if.arqos = '0;
assign u_hp_axi4_if.arregion = '0;
assign u_hp_axi4_if.aruser = '0;
assign u_hp_axi4_if.arvalid = 1'b0;
assign u_hp_axi4_if.rready = 1'b1;
assign s_hp_jtag_tdo = 1'b0;
assign s_hp_debug_reset_req = 1'b0;
`endif
onchip_ram #( onchip_ram #(
.Present (SramPresent), .Present (SramPresent),
.CapacityKiB(`SOC_ADDR_SRAM_SIZE >> 10) .CapacityKiB(`SOC_ADDR_SRAM_SIZE >> 10)
@@ -187,6 +274,7 @@ core_wrapper u_core_wrapper (
.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"
.hp_axi4 (u_hp_axi4_if),
.sdram_axi4 (u_sdram_axi4_if), .sdram_axi4 (u_sdram_axi4_if),
.psram_axi4 (u_psram_axi4_if), .psram_axi4 (u_psram_axi4_if),
.xpi_axi4 (u_xpi_axi4_if), .xpi_axi4 (u_xpi_axi4_if),
@@ -216,42 +304,48 @@ core_wrapper u_core_wrapper (
); );
apb4_periph u_apb4_periph ( apb4_periph u_apb4_periph (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.clk_aud_i (clk_aud_i), .clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i), .rst_aud_n_i (rst_aud_n_i),
.clk_ulpi_i (clk_ulpi_i), .clk_ulpi_i (clk_ulpi_i),
.debug_halted_i (s_mgmt_debug_halted), .debug_halted_i (s_mgmt_debug_halted),
.timebase_tick_i (timebase_tick_i), .timebase_tick_i (timebase_tick_i),
`include "apb4_periph_fabric.svh" `include "apb4_periph_fabric.svh"
.psram_axi4 (u_psram_axi4_if), .psram_axi4 (u_psram_axi4_if),
.xpi_axi4 (u_xpi_axi4_if), .xpi_axi4 (u_xpi_axi4_if),
.spisd_axi4 (u_spisd_axi4_if), .spisd_axi4 (u_spisd_axi4_if),
.opipsram_axi4 (u_opipsram_axi4_if), .opipsram_axi4 (u_opipsram_axi4_if),
.gpio (u_gpio_if), .gpio (u_gpio_if),
.user_gpio (u_user_gpio_if), .user_gpio (u_user_gpio_if),
.uart (u_uart0_if), .uart (u_uart0_if),
.psram (u_psram_if), .uart1 (u_uart1_if),
.spisd (u_spisd_if), .psram (u_psram_if),
.i2c0 (u_i2c0_if), .spisd (u_spisd_if),
.i2s (u_i2s_if), .i2c0 (u_i2c0_if),
.ws2812 (u_ws2812_if), .i2s (u_i2s_if),
.xpi (xpi), .ws2812 (u_ws2812_if),
.sysctrl (u_sysctrl_if), .xpi (xpi),
.pll_ctrl (pll_ctrl), .sysctrl (u_sysctrl_if),
.sdram_cfg (u_sdram_cfg_if), .pll_ctrl (pll_ctrl),
.sram_cfg (u_sram_cfg_if), .sdram_cfg (u_sdram_cfg_if),
.dvp (u_dvp_if), .sram_cfg (u_sram_cfg_if),
.sdio0 (u_sdio0_if), .dvp (u_dvp_if),
.sdio1 (sdio1), .sdio0 (u_sdio0_if),
.usb2 (usb2), .sdio1 (sdio1),
.opipsram (u_opipsram_if), .usb2 (usb2),
.i2c1 (u_i2c1_if), .opipsram (u_opipsram_if),
.fault_valid_i (s_bus_fault_valid), .i2c1 (u_i2c1_if),
.fault_addr_i (s_bus_fault_addr), .fault_valid_i (s_bus_fault_valid),
.fault_wstrb_i (s_bus_fault_wstrb), .fault_addr_i (s_bus_fault_addr),
.fault_reserved_i(s_bus_fault_reserved), .fault_wstrb_i (s_bus_fault_wstrb),
.irq_o (s_apb4_periph_irq) .fault_reserved_i (s_bus_fault_reserved),
.hp_time_o (s_hp_time),
.hp_timer_irq_o (s_hp_timer_irq),
.hp_software_irq_o (s_hp_software_irq),
.hp_machine_external_irq_o (s_hp_machine_external_irq),
.hp_supervisor_external_irq_o(s_hp_supervisor_external_irq),
.irq_o (s_apb4_periph_irq)
); );
axi4_sdram u_axi4_sdram ( axi4_sdram u_axi4_sdram (
+4
View File
@@ -77,6 +77,8 @@ module retrosoc_asic (
logic s_jtag_tdo; logic s_jtag_tdo;
logic s_uart0_rx; logic s_uart0_rx;
logic s_uart0_tx; logic s_uart0_tx;
logic s_uart1_rx;
logic s_uart1_tx;
logic s_usb2_ulpi_clk; logic s_usb2_ulpi_clk;
(* keep = "true" *)logic s_test_done; (* keep = "true" *)logic s_test_done;
(* keep = "true" *)logic s_test_pass; (* keep = "true" *)logic s_test_pass;
@@ -123,6 +125,8 @@ rcu #(
.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),
.uart1_rx_i (s_uart1_rx),
.uart1_tx_o (s_uart1_tx),
.xpi (u_xpi_if), .xpi (u_xpi_if),
.sdram (u_sdram_if), .sdram (u_sdram_if),
.sdio1 (u_sdio1_if), .sdio1 (u_sdio1_if),
+4
View File
@@ -36,6 +36,8 @@ module retrosoc_core (
logic s_jtag_tdo; logic s_jtag_tdo;
logic s_uart0_rx; logic s_uart0_rx;
logic s_uart0_tx; logic s_uart0_tx;
logic s_uart1_rx;
logic s_uart1_tx;
logic s_usb2_ulpi_clk; logic s_usb2_ulpi_clk;
(* keep = "true" *)logic s_test_done; (* keep = "true" *)logic s_test_done;
(* keep = "true" *)logic s_test_pass; (* keep = "true" *)logic s_test_pass;
@@ -82,6 +84,8 @@ rcu #(
.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),
.uart1_rx_i (s_uart1_rx),
.uart1_tx_o (s_uart1_tx),
.xpi (u_xpi_if), .xpi (u_xpi_if),
.sdram (u_sdram_if), .sdram (u_sdram_if),
.sdio1 (u_sdio1_if), .sdio1 (u_sdio1_if),
+7
View File
@@ -142,6 +142,8 @@
"apb4_clint.sv", "apb4_clint.sv",
"apb4_dma.sv", "apb4_dma.sv",
"apb4_gpio.sv", "apb4_gpio.sv",
"apb4_hp_mailbox.sv",
"apb4_plic.sv",
"apb4_sysctrl.sv", "apb4_sysctrl.sv",
"apb4_timer.sv", "apb4_timer.sv",
"clint_core.sv", "clint_core.sv",
@@ -159,6 +161,7 @@
"gpio_define.svh", "gpio_define.svh",
"gpio_if.sv", "gpio_if.sv",
"gpio_reg.sv", "gpio_reg.sv",
"hp_mailbox_define.svh",
"pll_ctrl_if.sv", "pll_ctrl_if.sv",
"sysctrl_core.sv", "sysctrl_core.sv",
"sysctrl_define.svh", "sysctrl_define.svh",
@@ -277,11 +280,15 @@
"axi42ribp_burst.sv", "axi42ribp_burst.sv",
"axi4_bus.sv", "axi4_bus.sv",
"axi4_connector.sv", "axi4_connector.sv",
"axi4_downsizer_64to32.sv",
"axi4_error_slave.sv", "axi4_error_slave.sv",
"axi4_interconnect.sv", "axi4_interconnect.sv",
"axi4_word_bridge.sv", "axi4_word_bridge.sv",
"core_wrapper.sv", "core_wrapper.sv",
"gpio_pad_bridge.sv", "gpio_pad_bridge.sv",
"hp_axi4_mux3.sv",
"hp_core_wrapper.sv",
"hp_subsystem.sv",
"mgmt_core_wrapper.sv", "mgmt_core_wrapper.sv",
"mgmt_debug_reset.sv", "mgmt_debug_reset.sv",
"mgmt_debug_wrapper.sv", "mgmt_debug_wrapper.sv",
+9
View File
@@ -25,6 +25,15 @@ SRAM CoreMark quick report into `meta/coremark.json`; it requires one valid
`COREMARK_RESULT` record and `COREMARK_PASS`. Both reports complement, rather `COREMARK_RESULT` record and `COREMARK_PASS`. Both reports complement, rather
than replace, the common `SIM_TEST_PASS` simulation verdict. than replace, the common `SIM_TEST_PASS` simulation verdict.
`generate_vexiiriscv.py` verifies the locked VexiiRiscv revision and generates
the fixed HP core below `build/`; generated RTL is never tracked.
`setup_hp_linux.py` installs the locked OpenSBI, Linux, and Buildroot revisions
and can resume a checkout left without `HEAD` by an interrupted fetch.
`build_hp_linux.py` builds the RV32 `ilp32d` image set with the repo-owned
external OpenSBI platform. `package_hp_boot.py` creates the v1 LP/HP flash
bundle and its SHA-256/CRC manifest. `check_lp_hp_performance.py` applies the
measured 2.5x CoreMark/MHz gate.
`run_debug_session.py` is the local Hazard3 debug acceptance driver. It starts `run_debug_session.py` is the local Hazard3 debug acceptance driver. It starts
the Verilator remote-bitbang endpoint, the lock-pinned OpenOCD binary, and the Verilator remote-bitbang endpoint, the lock-pinned OpenOCD binary, and
RISC-V GDB, then records their logs and a structured result. Invoke it through RISC-V GDB, then records their logs and a structured result. Invoke it through
+206
View File
@@ -0,0 +1,206 @@
#!/usr/bin/env python3
"""Build the pinned RV32 HP Buildroot, Linux, DTB, and OpenSBI images."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import shutil
import subprocess
from pathlib import Path
LAYOUT = {
"fw_jump.bin": (0x38000000, 512 * 1024),
"retrosoc_hp.dtb": (0x38080000, 64 * 1024),
"Image": (0x38400000, 12 * 1024 * 1024),
"rootfs.cpio.gz": (0x39000000, 8 * 1024 * 1024),
}
def command(arguments: list[str], cwd: Path) -> None:
environment = dict(os.environ)
environment.pop("CONFIG", None)
environment.pop("MAKEFLAGS", None)
environment.pop("MAKEOVERRIDES", None)
environment.pop("MFLAGS", None)
subprocess.run(arguments, cwd=cwd, check=True, env=environment)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def require_source(path: Path, name: str) -> Path:
resolved = path.resolve()
if not (resolved / ".git").exists():
raise FileNotFoundError(f"missing locked {name} source: {resolved}; run setup-hp-linux")
return resolved
def source_revision(path: Path) -> str:
return subprocess.check_output(
["git", "-C", str(path), "rev-parse", "HEAD"], text=True
).strip()
def find_toolchain(buildroot_output: Path) -> str:
matches = sorted((buildroot_output / "host/bin").glob("riscv32*-linux-*-gcc"))
if len(matches) != 1:
raise RuntimeError(f"expected one RV32 Linux GCC in {buildroot_output / 'host/bin'}")
return str(matches[0])[: -len("gcc")]
def build(args: argparse.Namespace) -> None:
root = args.root.resolve()
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
buildroot = require_source(args.buildroot, "Buildroot")
linux = require_source(args.linux, "Linux")
opensbi = require_source(args.opensbi, "OpenSBI")
external = root / "app/ports/linux"
buildroot_output = output / "buildroot"
command(
["make", f"O={buildroot_output}", f"BR2_EXTERNAL={external}", "retrosoc_hp_defconfig"],
buildroot,
)
command(["make", f"O={buildroot_output}", f"BR2_EXTERNAL={external}", f"-j{args.jobs}"], buildroot)
cross_compile = find_toolchain(buildroot_output)
linux_output = output / "linux"
command(
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}", "tinyconfig"],
linux,
)
command(
[
str(linux / "scripts/kconfig/merge_config.sh"),
"-m",
"-O",
str(linux_output),
str(linux_output / ".config"),
str(external / "linux/retrosoc_hp.config"),
],
linux,
)
command(
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}", "olddefconfig"],
linux,
)
command(
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}",
f"-j{args.jobs}", "Image"],
linux,
)
image_dir = output / "images"
image_dir.mkdir(parents=True, exist_ok=True)
rootfs_source = buildroot_output / "images/rootfs.cpio.gz"
if not rootfs_source.is_file():
raise FileNotFoundError(f"HP Linux build output is missing: {rootfs_source}")
initrd_end = LAYOUT["rootfs.cpio.gz"][0] + rootfs_source.stat().st_size
command(
[
"dtc",
"-I",
"dts",
"-O",
"dtb",
"-o",
str(image_dir / "retrosoc_hp.dtb"),
str(external / "linux/retrosoc_hp.dts"),
],
root,
)
command(
[
"fdtput",
"-t",
"x",
str(image_dir / "retrosoc_hp.dtb"),
"/chosen",
"linux,initrd-end",
f"0x{initrd_end:08x}",
],
root,
)
opensbi_output = output / "opensbi"
command(
[
"make",
f"O={opensbi_output}",
f"CROSS_COMPILE={cross_compile}",
"PLATFORM=retrosoc_hp",
f"PLATFORM_DIR={external / 'opensbi'}",
"PLATFORM_RISCV_XLEN=32",
"PLATFORM_RISCV_ABI=ilp32d",
"PLATFORM_RISCV_ISA=rv32imafdc_zicsr_zifencei",
"FW_TEXT_START=0x38000000",
"FW_JUMP=y",
"FW_JUMP_ADDR=0x38400000",
"FW_JUMP_FDT_ADDR=0x38080000",
f"-j{args.jobs}",
],
opensbi,
)
copies = {
opensbi_output / "platform/retrosoc_hp/firmware/fw_jump.bin": image_dir / "fw_jump.bin",
linux_output / "arch/riscv/boot/Image": image_dir / "Image",
rootfs_source: image_dir / "rootfs.cpio.gz",
}
for source, destination in copies.items():
if not source.is_file():
raise FileNotFoundError(f"HP Linux build output is missing: {source}")
shutil.copy2(source, destination)
manifest: dict[str, object] = {
"schema_version": 1,
"boot_flow": "opensbi-fw_jump",
"hart_id": 1,
"sources": {
"buildroot": source_revision(buildroot),
"linux": source_revision(linux),
"opensbi": source_revision(opensbi),
},
"artifacts": {},
}
artifacts = manifest["artifacts"]
assert isinstance(artifacts, dict)
for name, (address, maximum) in LAYOUT.items():
path = image_dir / name
size = path.stat().st_size
if size > maximum:
raise ValueError(f"{name} is {size} bytes; maximum is {maximum}")
artifacts[name] = {
"address": f"0x{address:08X}",
"maximum_bytes": maximum,
"size_bytes": size,
"sha256": sha256(path),
}
(image_dir / "manifest.json").write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--buildroot", type=Path, required=True)
parser.add_argument("--linux", type=Path, required=True)
parser.add_argument("--opensbi", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--jobs", type=int, default=1)
build(parser.parse_args())
if __name__ == "__main__":
main()
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Build the minimal hart-1 smoke payload used by HP RTL simulation."""
from __future__ import annotations
import argparse
import subprocess
from pathlib import Path
def build(args: argparse.Namespace) -> None:
output = args.output.resolve()
images = output / "images"
images.mkdir(parents=True, exist_ok=True)
elf = output / "hp_smoke.elf"
compiler = f"{args.cross}gcc"
objcopy = f"{args.cross}objcopy"
subprocess.run(
[
compiler,
"-march=rv32imafdc_zicsr_zifencei",
"-mabi=ilp32d",
"-nostdlib",
"-nostartfiles",
"-Wl,--build-id=none",
f"-Wl,-T,{args.linker.resolve()}",
"-o",
str(elf),
str(args.source.resolve()),
],
check=True,
)
subprocess.run(
[objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")],
check=True,
)
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
(images / "Image").write_bytes(b"SMOK")
(images / "rootfs.cpio.gz").write_bytes(b"SMOK")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--linker", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--cross", default="riscv32-unknown-elf-")
build(parser.parse_args())
if __name__ == "__main__":
main()
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Enforce the measured HP-to-LP CoreMark/MHz performance ratio."""
from __future__ import annotations
import argparse
import json
import sys
from decimal import Decimal, InvalidOperation
from pathlib import Path
def result(report: dict[str, object], label: str) -> dict[str, object]:
if report.get("status") != "passed":
raise ValueError(f"{label} CoreMark report did not pass")
results = report.get("results")
if not isinstance(results, list) or len(results) != 1 or not isinstance(results[0], dict):
raise ValueError(f"{label} CoreMark report must contain exactly one result")
return results[0]
def compare(
lp_report: dict[str, object], hp_report: dict[str, object], threshold: Decimal
) -> dict[str, object]:
if threshold <= 0:
raise ValueError("minimum performance ratio must be positive")
lp = result(lp_report, "LP")
hp = result(hp_report, "HP")
if lp.get("mode") != hp.get("mode"):
raise ValueError("LP and HP CoreMark reports must use the same mode")
if lp.get("cpu_hz") != hp.get("cpu_hz"):
raise ValueError("LP and HP CoreMark reports must use the same fixed frequency")
try:
lp_score = Decimal(str(lp["coremark_per_mhz"]))
hp_score = Decimal(str(hp["coremark_per_mhz"]))
ratio = hp_score / lp_score
except (InvalidOperation, KeyError, ZeroDivisionError) as error:
raise ValueError("invalid CoreMark/MHz values") from error
passed = ratio >= threshold
return {
"schema_version": 1,
"status": "passed" if passed else "failed",
"frequency_hz": lp["cpu_hz"],
"mode": lp["mode"],
"lp_coremark_per_mhz": str(lp_score),
"hp_coremark_per_mhz": str(hp_score),
"minimum_ratio": str(threshold),
"measured_ratio": str(ratio.quantize(Decimal("0.001"))),
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--lp", type=Path, required=True)
parser.add_argument("--hp", type=Path, required=True)
parser.add_argument("--minimum-ratio", type=Decimal, default=Decimal("2.5"))
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
try:
report = compare(
json.loads(args.lp.read_text(encoding="utf-8")),
json.loads(args.hp.read_text(encoding="utf-8")),
args.minimum_ratio,
)
except (OSError, ValueError, json.JSONDecodeError) as error:
print(f"performance comparison failed: {error}", file=sys.stderr)
return 1
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(
json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
if report["status"] != "passed":
print(
f"HP/LP CoreMark/MHz ratio {report['measured_ratio']} is below "
f"{report['minimum_ratio']}",
file=sys.stderr,
)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""Generate the locked VexiiRiscv HP core below the selected build variant."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import subprocess
from pathlib import Path
from typing import Any
GENERATOR_CLASS = "vexiiriscv.GenerateRetroSocHp"
GENERATED_MODULE = "vexii_riscv_hp_generated"
def run(command: list[str], cwd: Path) -> str:
result = subprocess.run(command, cwd=cwd, check=True, capture_output=True, text=True)
return result.stdout.strip()
def git_revision(source: Path) -> str:
return run(["git", "rev-parse", "HEAD"], source)
def git_status(source: Path) -> list[str]:
output = run(["git", "status", "--short"], source)
return output.splitlines() if output else []
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def locked_revision(lock_path: Path) -> str:
document: dict[str, Any] = json.loads(lock_path.read_text(encoding="utf-8"))
source = document.get("sources", {}).get("vexiiriscv")
if not isinstance(source, dict) or not isinstance(source.get("revision"), str):
raise ValueError("dependency lock has no vexiiriscv source revision")
return source["revision"]
def generate(args: argparse.Namespace) -> None:
root = args.root.resolve()
source = args.source.resolve()
output = args.output.resolve()
expected = locked_revision(args.lock.resolve())
actual = git_revision(source)
if actual != expected:
raise ValueError(f"VexiiRiscv revision mismatch: expected {expected}, found {actual}")
scala_dir = root / "scripts" / "vexiiriscv"
output.mkdir(parents=True, exist_ok=True)
command = [
str(args.sbt),
f'set Compile / unmanagedSourceDirectories += file("{scala_dir}")',
f"runMain {GENERATOR_CLASS} {output}",
]
environment = os.environ.copy()
java_home = environment.get("HOME")
if java_home:
inherited = environment.get("JAVA_TOOL_OPTIONS", "").strip()
environment["JAVA_TOOL_OPTIONS"] = (
f"{inherited} -Duser.home={java_home}".strip()
)
subprocess.run(command, cwd=source, check=True, env=environment)
generated = output / "vexii_riscv_hp_generated.v"
if not generated.is_file():
raise FileNotFoundError(f"VexiiRiscv generator did not create {generated}")
submodules = run(["git", "submodule", "status", "--recursive"], source).splitlines()
manifest = {
"schema_version": 1,
"module": GENERATED_MODULE,
"configuration": "rv32imafdc_max",
"vexiiriscv_revision": actual,
"source_status": git_status(source),
"submodules": submodules,
"generator": str((scala_dir / "GenerateRetroSocHp.scala").relative_to(root)),
"files": {generated.name: sha256(generated)},
}
args.manifest.resolve().parent.mkdir(parents=True, exist_ok=True)
args.manifest.resolve().write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
(output / "vexiiriscv.fl").write_text(str(generated) + "\n", encoding="utf-8")
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--manifest", type=Path, required=True)
parser.add_argument("--lock", type=Path, required=True)
parser.add_argument("--sbt", type=Path, required=True)
generate(parser.parse_args())
if __name__ == "__main__":
main()
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
"""Package LP firmware and the fixed HP Linux image set into one flash image."""
from __future__ import annotations
import argparse
import hashlib
import json
import struct
import zlib
from pathlib import Path
FLASH_SIZE = 16 * 1024 * 1024
BUNDLE_OFFSET = 1 * 1024 * 1024
PAYLOAD_ALIGNMENT = 4096
MAGIC = 0x50485352
VERSION = 1
REQUIRED = 1
HEADER = struct.Struct("<8I")
ENTRY = struct.Struct("<6I")
ARTIFACTS = (
(1, "fw_jump.bin", 0x38000000, 512 * 1024),
(2, "retrosoc_hp.dtb", 0x38080000, 64 * 1024),
(3, "Image", 0x38400000, 12 * 1024 * 1024),
(4, "rootfs.cpio.gz", 0x39000000, 8 * 1024 * 1024),
)
HEADER_SIZE = HEADER.size + (ENTRY.size * len(ARTIFACTS))
def align_up(value: int, alignment: int) -> int:
return (value + alignment - 1) & ~(alignment - 1)
def sha256(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def package(args: argparse.Namespace) -> None:
firmware = args.firmware.read_bytes()
if len(firmware) > BUNDLE_OFFSET:
raise ValueError(
f"LP firmware is {len(firmware)} bytes; bundle starts at {BUNDLE_OFFSET}"
)
payload_offset = align_up(BUNDLE_OFFSET + HEADER_SIZE, PAYLOAD_ALIGNMENT)
entries: list[bytes] = []
payloads: list[tuple[int, bytes]] = []
artifact_manifest: dict[str, object] = {}
cursor = payload_offset
for kind, name, load_address, maximum in ARTIFACTS:
data = (args.images / name).read_bytes()
if not data or len(data) > maximum:
raise ValueError(f"{name} size {len(data)} is outside 1..{maximum} bytes")
crc32 = zlib.crc32(data) & 0xFFFFFFFF
entries.append(ENTRY.pack(kind, cursor, load_address, len(data), crc32, REQUIRED))
payloads.append((cursor, data))
artifact_manifest[name] = {
"crc32": f"0x{crc32:08X}",
"flash_offset": f"0x{cursor:08X}",
"load_address": f"0x{load_address:08X}",
"sha256": sha256(data),
"size_bytes": len(data),
}
cursor = align_up(cursor + len(data), PAYLOAD_ALIGNMENT)
if cursor > FLASH_SIZE:
raise ValueError(f"HP boot image needs {cursor} bytes; flash capacity is {FLASH_SIZE}")
total_size = cursor - BUNDLE_OFFSET
header = HEADER.pack(
MAGIC,
VERSION,
HEADER_SIZE,
len(entries),
total_size,
0,
REQUIRED,
0,
) + b"".join(entries)
header_crc32 = zlib.crc32(header) & 0xFFFFFFFF
header = HEADER.pack(
MAGIC,
VERSION,
HEADER_SIZE,
len(entries),
total_size,
header_crc32,
REQUIRED,
0,
) + b"".join(entries)
image = bytearray(b"\xFF" * cursor)
image[: len(firmware)] = firmware
image[BUNDLE_OFFSET : BUNDLE_OFFSET + len(header)] = header
for offset, data in payloads:
image[offset : offset + len(data)] = data
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_bytes(image)
manifest = {
"schema_version": 1,
"format": "retrosoc-hp-boot-bundle-v1",
"bundle_offset": f"0x{BUNDLE_OFFSET:08X}",
"flash_capacity_bytes": FLASH_SIZE,
"header_crc32": f"0x{header_crc32:08X}",
"image_sha256": sha256(image),
"image_size_bytes": len(image),
"lp_firmware_size_bytes": len(firmware),
"artifacts": artifact_manifest,
}
args.manifest.parent.mkdir(parents=True, exist_ok=True)
args.manifest.write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--firmware", type=Path, required=True)
parser.add_argument("--images", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--manifest", type=Path, required=True)
package(parser.parse_args())
if __name__ == "__main__":
main()
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Install the pinned HP Linux, OpenSBI, and Buildroot source trees."""
from __future__ import annotations
import argparse
import os
import subprocess
from pathlib import Path
from dependency_lock import source
from setup_helpers import ensure_git_repo, run
ROOT = Path(__file__).resolve().parents[1]
def repair_incomplete_checkout(
destination: Path, url: str, revision: str
) -> None:
if not (destination / ".git").is_dir():
return
result = subprocess.run(
["git", "-C", str(destination), "rev-parse", "--verify", "HEAD"],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if result.returncode == 0:
return
run(("git", "remote", "set-url", "origin", url), cwd=destination)
run(("git", "fetch", "--depth", "1", "origin", revision), cwd=destination)
run(("git", "checkout", "--detach", "FETCH_HEAD"), cwd=destination)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--update", action="store_true")
args = parser.parse_args()
# The stable Linux mirror is large enough to expose HTTP/2 proxy framing failures.
os.environ.setdefault("GIT_CONFIG_COUNT", "1")
os.environ.setdefault("GIT_CONFIG_KEY_0", "http.version")
os.environ.setdefault("GIT_CONFIG_VALUE_0", "HTTP/1.1")
for name in ("opensbi_hp", "linux_hp", "buildroot_hp"):
dependency = source(name)
destination = ROOT / dependency["destination"]
repair_incomplete_checkout(
destination, dependency["url"], dependency["revision"]
)
ensure_git_repo(
dependency["url"],
destination,
dependency["revision"],
recursive=dependency.get("recursive", False),
update=args.update,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+125
View File
@@ -0,0 +1,125 @@
package vexiiriscv
import spinal.core._
import spinal.lib.bus.misc.SizeMapping
import spinal.lib.bus.tilelink.M2sTransfers
import spinal.lib.system.tag.PmaRegionImpl
import vexiiriscv.misc.EmbeddedRiscvJtag
import java.nio.file.{Files, Paths}
/** Generates the fixed retroSoC RV32 Linux application core. */
object GenerateRetroSocHp extends App {
require(args.length == 1, "usage: GenerateRetroSocHp <output-directory>")
val output = Paths.get(args(0)).toAbsolutePath.normalize
Files.createDirectories(output)
val param = new ParamSimple()
param.xlen = 32
param.physicalWidth = 32
param.resetVector = 0x38000000L
param.asidWidth = 9
param.addISA("m", "a", "f", "d", "c", "s", "u", "zicntr", "zihpm")
param.fixIsaParams()
param.decoders = 2
param.lanes = 2
param.withBtb = true
param.withGShare = true
param.withRas = true
param.withLateAlu = true
param.withAlignerBuffer = true
param.withDispatcherBuffer = true
param.allowBypassFrom = 0
param.storeRs2Late = true
param.divRadix = 4
param.divArea = false
param.fetchL1Enable = true
param.fetchL1Sets = 64
param.fetchL1Ways = 4
param.fetchMemDataWidthMin = 64
param.fetchL1RefillCount = 2
param.fetchL1Prefetch = "nl"
param.fetchBus = FetchBusEnum.axi4
param.lsuL1Enable = true
param.lsuL1Sets = 64
param.lsuL1Ways = 4
param.lsuMemDataWidthMin = 64
param.lsuL1RefillCount = 8
param.lsuL1WritebackCount = 8
param.lsuStoreBufferSlots = 4
param.lsuStoreBufferOps = 32
param.lsuSoftwarePrefetch = true
param.lsuHardwarePrefetch = "rpt"
param.withLsuBypass = true
param.lsuBus = LsuBusEnum.axi4
param.lsuL1Bus = LsuL1BusEnum.axi4
param.pmpParam.pmpSize = 16
param.pmpParam.granularity = 4096
param.additionalPerformanceCounters = 8
param.privParam.withRdTime = true
param.privParam.withDebug = true
param.privParam.debugTriggers = 4
param.embeddedJtagTap = true
val config = SpinalConfig(
targetDirectory = output.toString,
inlineRom = true
)
config.generateVerilog {
val plugins = param.plugins(hartId = 1)
plugins.foreach {
case plugin: EmbeddedRiscvJtag =>
plugin.debugCd = ClockDomain.current.copy(
reset = Bool().setName("EmbeddedRiscvJtag_logic_debug_reset")
)
case _ =>
}
ParamSimple.setPma(
plugins,
Seq(
new PmaRegionImpl(
mapping = SizeMapping(0x00000000L, 0x01000000L),
isMain = false,
isExecutable = true,
transfers = M2sTransfers.all
),
new PmaRegionImpl(
mapping = SizeMapping(0x02000000L, 0x2E000000L),
isMain = false,
isExecutable = false,
transfers = M2sTransfers.all
),
new PmaRegionImpl(
mapping = SizeMapping(0x30000000L, 0x00020000L),
isMain = true,
isExecutable = true,
transfers = M2sTransfers.all
),
new PmaRegionImpl(
mapping = SizeMapping(0x38000000L, 0x04000000L),
isMain = true,
isExecutable = true,
transfers = M2sTransfers.all
),
new PmaRegionImpl(
mapping = SizeMapping(0x40000000L, 0x02000000L),
isMain = true,
isExecutable = true,
transfers = M2sTransfers.all
),
new PmaRegionImpl(
mapping = SizeMapping(0x48000000L, 0x08000000L),
isMain = true,
isExecutable = true,
transfers = M2sTransfers.all
)
)
)
VexiiRiscv(plugins).setDefinitionName("vexii_riscv_hp_generated")
}
}
+4
View File
@@ -16,6 +16,10 @@ tooling.
- `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot - `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot
2 GPIO register offsets synchronized with their application-owned C 2 GPIO register offsets synchronized with their application-owned C
definitions and checks their extension-manifest slot assignments. definitions and checks their extension-manifest slot assignments.
- `test_hp_boot_bundle.py` checks the HP flash ABI, CRCs, payload placement,
and handwritten mailbox RTL/C offset parity. `test_hp_platform.py`,
`test_hp_mailbox.py`, `test_plic.py`, and the AXI tests cover the remaining
HP integration contracts.
Add a host C test when changing deterministic runtime or media logic; add a Add a host C test when changing deterministic runtime or media logic; add a
Python test when changing scripts or build policy. Run: Python test when changing scripts or build policy. Run:
+273
View File
@@ -0,0 +1,273 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
module axi4_downsizer_64to32_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) wide (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) narrow (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
axi4_downsizer_64to32 u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.wide (wide),
.narrow (narrow)
);
task automatic init_bus;
begin
wide.awid = '0;
wide.awaddr = '0;
wide.awlen = '0;
wide.awsize = `AXI4_BURST_SIZE_8BYTES;
wide.awburst = `AXI4_BURST_TYPE_INCR;
wide.awlock = `AXI4_LOCK_NORM;
wide.awcache = `AXI4_CACHE_NO_BUF;
wide.awprot = `AXI4_PROT_DATA;
wide.awqos = `AXI4_QOS_NORMAL;
wide.awregion = `AXI4_REGION_NORMAL;
wide.awuser = '0;
wide.awvalid = 1'b0;
wide.wdata = '0;
wide.wstrb = '0;
wide.wlast = 1'b0;
wide.wuser = '0;
wide.wvalid = 1'b0;
wide.bready = 1'b0;
wide.arid = '0;
wide.araddr = '0;
wide.arlen = '0;
wide.arsize = `AXI4_BURST_SIZE_8BYTES;
wide.arburst = `AXI4_BURST_TYPE_INCR;
wide.arlock = `AXI4_LOCK_NORM;
wide.arcache = `AXI4_CACHE_NO_BUF;
wide.arprot = `AXI4_PROT_DATA;
wide.arqos = `AXI4_QOS_NORMAL;
wide.arregion = `AXI4_REGION_NORMAL;
wide.aruser = '0;
wide.arvalid = 1'b0;
wide.rready = 1'b0;
narrow.awready = 1'b0;
narrow.wready = 1'b0;
narrow.bid = '0;
narrow.bresp = `AXI4_RESP_OKAY;
narrow.buser = '0;
narrow.bvalid = 1'b0;
narrow.arready = 1'b0;
narrow.rid = '0;
narrow.rdata = '0;
narrow.rresp = `AXI4_RESP_OKAY;
narrow.rlast = 1'b0;
narrow.ruser = '0;
narrow.rvalid = 1'b0;
end
endtask
task automatic accept_read(input logic [31:0] address, input logic [7:0] length,
input logic [2:0] size, input logic [2:0] id,
input logic [7:0] expected_length);
begin
@(negedge clk_i);
wide.araddr = address;
wide.arlen = length;
wide.arsize = size;
wide.arid = id;
wide.arvalid = 1'b1;
#1;
if (wide.arready) $fatal(1, "read address ignored narrow backpressure");
narrow.arready = 1'b1;
#1;
if (!narrow.arvalid || narrow.araddr != address || narrow.arlen != expected_length ||
narrow.arsize != ((size == 3'd3) ? 3'd2 : size)) begin
$fatal(1, "read address conversion mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.arvalid = 1'b0;
narrow.arready = 1'b0;
end
endtask
task automatic accept_write(input logic [31:0] address, input logic [7:0] length,
input logic [2:0] size, input logic [2:0] id,
input logic [7:0] expected_length);
begin
@(negedge clk_i);
wide.awaddr = address;
wide.awlen = length;
wide.awsize = size;
wide.awid = id;
wide.awvalid = 1'b1;
#1;
if (wide.awready) $fatal(1, "write address ignored narrow backpressure");
narrow.awready = 1'b1;
#1;
if (!narrow.awvalid || narrow.awaddr != address || narrow.awlen != expected_length ||
narrow.awsize != ((size == 3'd3) ? 3'd2 : size)) begin
$fatal(1, "write address conversion mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.awvalid = 1'b0;
narrow.awready = 1'b0;
end
endtask
task automatic return_write_response(input logic [2:0] expected_id, input logic [1:0] response);
begin
@(negedge clk_i);
narrow.bresp = response;
narrow.bvalid = 1'b1;
wide.bready = 1'b0;
#1;
if (!narrow.bready) $fatal(1, "narrow write response was not accepted into buffer");
@(posedge clk_i);
@(negedge clk_i);
narrow.bvalid = 1'b0;
#1;
if (!wide.bvalid || wide.bid != expected_id || wide.bresp != response) begin
$fatal(1, "buffered write response mismatch");
end
repeat (2) @(posedge clk_i);
if (!wide.bvalid || wide.bid != expected_id || wide.bresp != response) begin
$fatal(1, "write response changed under backpressure");
end
@(negedge clk_i);
wide.bready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
wide.bready = 1'b0;
end
endtask
initial begin
init_bus();
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
accept_read(32'h0000_0040, 8'd0, 3'd3, 3'd5, 8'd1);
@(negedge clk_i);
narrow.rdata = 32'h1122_3344;
narrow.rresp = `AXI4_RESP_SLAVE_ERROR;
narrow.rlast = 1'b0;
narrow.rvalid = 1'b1;
wide.rready = 1'b0;
#1;
if (!narrow.rready || wide.rvalid) $fatal(1, "lower read beat was not buffered");
@(posedge clk_i);
@(negedge clk_i);
narrow.rdata = 32'h5566_7788;
narrow.rresp = `AXI4_RESP_OKAY;
narrow.rlast = 1'b1;
#1;
if (!wide.rvalid || narrow.rready || wide.rid != 3'd5 ||
wide.rdata != 64'h5566_7788_1122_3344 || wide.rresp != `AXI4_RESP_SLAVE_ERROR ||
!wide.rlast) begin
$fatal(1, "64-bit read assembly or backpressure mismatch");
end
repeat (2) @(posedge clk_i);
@(negedge clk_i);
wide.rready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
wide.rready = 1'b0;
narrow.rvalid = 1'b0;
accept_read(32'h0000_0000, 8'd1, 3'd2, 3'd3, 8'd1);
@(negedge clk_i);
narrow.rdata = 32'hA5A5_0000;
narrow.rresp = `AXI4_RESP_OKAY;
narrow.rlast = 1'b0;
narrow.rvalid = 1'b1;
wide.rready = 1'b1;
#1;
if (wide.rdata != 64'h0000_0000_A5A5_0000 || wide.rid != 3'd3) begin
$fatal(1, "lower 32-bit read lane mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
narrow.rdata = 32'hA5A5_0001;
narrow.rlast = 1'b1;
#1;
if (wide.rdata != 64'hA5A5_0001_0000_0000 || !wide.rlast) begin
$fatal(1, "upper 32-bit read lane mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.rready = 1'b0;
narrow.rvalid = 1'b0;
accept_write(32'h0000_0080, 8'd0, 3'd3, 3'd6, 8'd1);
@(negedge clk_i);
wide.wdata = 64'h1122_3344_5566_7788;
wide.wstrb = 8'hF3;
wide.wlast = 1'b1;
wide.wvalid = 1'b1;
narrow.wready = 1'b1;
#1;
if (narrow.wdata != 32'h5566_7788 || narrow.wstrb != 4'h3 || narrow.wlast || wide.wready) begin
$fatal(1, "lower 64-bit write split mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
#1;
if (narrow.wdata != 32'h1122_3344 || narrow.wstrb != 4'hF || !narrow.wlast ||
!wide.wready) begin
$fatal(1, "upper 64-bit write split mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.wvalid = 1'b0;
narrow.wready = 1'b0;
return_write_response(3'd6, `AXI4_RESP_DECODE_ERROR);
accept_write(32'h0000_0004, 8'd1, 3'd2, 3'd2, 8'd1);
@(negedge clk_i);
wide.wdata = 64'hCAFE_0000_DEAD_0000;
wide.wstrb = 8'hF0;
wide.wlast = 1'b0;
wide.wvalid = 1'b1;
narrow.wready = 1'b1;
#1;
if (narrow.wdata != 32'hCAFE_0000 || narrow.wstrb != 4'hF || !wide.wready) begin
$fatal(1, "upper 32-bit write lane mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.wdata = 64'hDEAD_0000_CAFE_0001;
wide.wstrb = 8'h0F;
wide.wlast = 1'b1;
#1;
if (narrow.wdata != 32'hCAFE_0001 || narrow.wstrb != 4'hF || !wide.wready) begin
$fatal(1, "lower 32-bit write lane mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
wide.wvalid = 1'b0;
narrow.wready = 1'b0;
return_write_response(3'd2, `AXI4_RESP_OKAY);
$display("AXI4 64-to-32 downsizer test passed");
$finish;
end
endmodule
+48 -1
View File
@@ -4,7 +4,7 @@
`include "rib_defs.svh" `include "rib_defs.svh"
module axi4_interconnect_tb; module axi4_interconnect_tb;
localparam int NumMasters = 7; localparam int NumMasters = 8;
localparam int NumTargets = 9; localparam int NumTargets = 9;
logic clk_i = 1'b0; logic clk_i = 1'b0;
@@ -288,6 +288,50 @@ module axi4_interconnect_tb;
end end
endtask endtask
task automatic issue_hp_accesses;
integer beat;
begin
@(negedge clk_i);
masters[7].araddr = 32'h3800_0000;
masters[7].arlen = 8'd15;
masters[7].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[7].arready);
@(negedge clk_i);
masters[7].arvalid = 1'b0;
masters[7].rready = 1'b1;
beat = 0;
while (beat < 16) begin
@(negedge clk_i);
if (masters[7].rvalid) begin
if ((masters[7].rresp != `AXI4_RESP_OKAY) ||
(masters[7].rlast != (beat == 15)) || fault_valid_o) begin
$fatal(1, "HP SDRAM burst was rejected or misclassified");
end
beat = beat + 1;
end
end
@(negedge clk_i);
masters[7].rready = 1'b0;
masters[7].araddr = 32'hA000_7000;
masters[7].arlen = 8'd0;
masters[7].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[7].arready);
@(negedge clk_i);
masters[7].arvalid = 1'b0;
masters[7].rready = 1'b1;
while (!masters[7].rvalid) @(negedge clk_i);
#1;
if ((masters[7].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd7) || (fault_addr_o != 32'hA000_7000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "HP decode fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[7].rready = 1'b0;
end
endtask
initial begin initial begin
`INIT_MASTER(0) `INIT_MASTER(0)
`INIT_MASTER(1) `INIT_MASTER(1)
@@ -295,6 +339,8 @@ module axi4_interconnect_tb;
`INIT_MASTER(3) `INIT_MASTER(3)
`INIT_MASTER(4) `INIT_MASTER(4)
`INIT_MASTER(5) `INIT_MASTER(5)
`INIT_MASTER(6)
`INIT_MASTER(7)
repeat (4) @(posedge clk_i); repeat (4) @(posedge clk_i);
rst_n_i = 1'b1; rst_n_i = 1'b1;
@@ -303,6 +349,7 @@ module axi4_interconnect_tb;
issue_mgmt_error_read(32'h3000_0000, 8'd16, `AXI4_RESP_SLAVE_ERROR, `RIB_RESP_BURSTERR); issue_mgmt_error_read(32'h3000_0000, 8'd16, `AXI4_RESP_SLAVE_ERROR, `RIB_RESP_BURSTERR);
issue_denied_user_write(32'h1000_B000); issue_denied_user_write(32'h1000_B000);
issue_contending_sdio_faults(); issue_contending_sdio_faults();
issue_hp_accesses();
$display("AXI4 interconnect fault classification test passed"); $display("AXI4 interconnect fault classification test passed");
$finish; $finish;
+89
View File
@@ -0,0 +1,89 @@
`timescale 1ns / 1ps
module hp_mailbox_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic lp_irq_o;
logic hp_irq_o;
logic [31:0] value;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
apb4_hp_mailbox u_dut (
.clk_i,
.rst_n_i,
.apb4,
.lp_irq_o,
.hp_irq_o
);
task automatic apb_write(input logic [31:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = address;
apb4.pwdata = data;
apb4.pstrb = 4'hf;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [31:0] address, output logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = address;
apb4.pstrb = '0;
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
data = apb4.prdata;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.paddr = '0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
apb_read(32'h0, value);
if (value != 32'h0001_0000) $fatal(1, "mailbox version mismatch");
apb_write(32'h44, 32'h1);
apb_write(32'h10, 32'h1);
apb_write(32'h18, 32'h1234);
apb_write(32'h1c, 32'h1);
if (!hp_irq_o) $fatal(1, "LP-to-HP doorbell did not assert");
apb_read(32'h18, value);
if (value != 32'h1234) $fatal(1, "LP sequence did not persist");
apb_write(32'h40, 32'h1);
if (hp_irq_o) $fatal(1, "HP interrupt did not clear");
apb_write(32'h34, 32'h1);
apb_write(32'h20, 32'h1);
apb_write(32'h2c, 32'h1);
if (!lp_irq_o) $fatal(1, "HP-to-LP doorbell did not assert");
apb_write(32'h30, 32'h1);
if (lp_irq_o) $fatal(1, "LP interrupt did not clear");
$display("HP mailbox register and doorbell test passed");
$finish;
end
endmodule
+92
View File
@@ -0,0 +1,92 @@
`timescale 1ns / 1ps
module plic_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [31:0] source_i = '0;
logic [ 1:0] context_irq_o;
logic [31:0] value;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
apb4_plic u_dut (
.clk_i,
.rst_n_i,
.source_i,
.apb4,
.context_irq_o
);
task automatic apb_write(input logic [31:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = address;
apb4.pwdata = data;
apb4.pstrb = 4'hf;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
if (apb4.pslverr) $fatal(1, "PLIC write failed at %h", address);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [31:0] address, output logic [31:0] data);
begin
@(negedge clk_i);
apb4.paddr = address;
apb4.pstrb = '0;
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
if (apb4.pslverr) $fatal(1, "PLIC read failed at %h", address);
data = apb4.prdata;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.paddr = '0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
apb_write(32'h0000_0004, 32'd3);
apb_write(32'h0000_0008, 32'd5);
apb_write(32'h0000_2080, 32'h0000_0006);
apb_write(32'h0020_1000, 32'd0);
source_i[1] = 1'b1;
source_i[2] = 1'b1;
repeat (2) @(posedge clk_i);
if (!context_irq_o[1]) $fatal(1, "supervisor context IRQ did not assert");
apb_read(32'h0020_1004, value);
if (value != 32'd2) $fatal(1, "highest-priority source was not claimed");
source_i[2] = 1'b0;
apb_write(32'h0020_1004, 32'd2);
repeat (2) @(posedge clk_i);
apb_read(32'h0020_1004, value);
if (value != 32'd1) $fatal(1, "remaining source was not claimed");
source_i[1] = 1'b0;
apb_write(32'h0020_1004, 32'd1);
repeat (2) @(posedge clk_i);
if (context_irq_o[1]) $fatal(1, "supervisor context IRQ did not clear");
$display("PLIC priority, claim, and completion test passed");
$finish;
end
endmodule
+21 -8
View File
@@ -120,14 +120,27 @@ module pll_ctrl_tb;
endtask endtask
initial begin initial begin
apb4.psel = 1'b0; apb4.psel = 1'b0;
apb4.paddr = '0; apb4.paddr = '0;
apb4.pwdata = '0; apb4.pwdata = '0;
apb4.pstrb = '0; apb4.pstrb = '0;
sysctrl.user_bus_idle_i = 1'b1; sysctrl.user_bus_idle_i = 1'b1;
sysctrl.fault_access_i = 1'b0; sysctrl.fault_access_i = 1'b0;
sysctrl.fault_master_i = '0; sysctrl.fault_master_i = '0;
sysctrl.rtc_wake_i = 1'b0; sysctrl.fault_code_i = '0;
sysctrl.perf_mgmt_wait_i = '0;
sysctrl.perf_user_wait_i = '0;
sysctrl.perf_dma_wait_i = '0;
sysctrl.perf_sdio0_wait_i = '0;
sysctrl.perf_sdio1_wait_i = '0;
sysctrl.perf_usb2_wait_i = '0;
sysctrl.perf_apb4_periph_wait_i = '0;
sysctrl.perf_apb4_system_wait_i = '0;
sysctrl.perf_sdram_wait_i = '0;
sysctrl.perf_psram_wait_i = '0;
sysctrl.perf_flash_wait_i = '0;
sysctrl.rtc_wake_i = 1'b0;
sysctrl.hp_present_i = 1'b0;
#100; #100;
rst_n_i = 1'b1; rst_n_i = 1'b1;
repeat (2) begin repeat (2) begin
+3 -1
View File
@@ -40,6 +40,8 @@ module soc_irq_topology_tb;
17: expect_irq(32'd1 << 21); 17: expect_irq(32'd1 << 21);
18: expect_irq(32'd1 << 23); 18: expect_irq(32'd1 << 23);
19: expect_irq(32'd1 << 24); 19: expect_irq(32'd1 << 24);
20: expect_irq(32'd1 << 25);
21: expect_irq(32'd1 << 26);
default: expect_irq(32'd1 << (bit_index + 7)); default: expect_irq(32'd1 << (bit_index + 7));
endcase endcase
end end
@@ -64,7 +66,7 @@ module soc_irq_topology_tb;
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b", $fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
s_irq[10], s_irq[21]); s_irq[10], s_irq[21]);
end end
if ((s_irq[31:25] !== '0) || (s_irq[19:18] !== '0)) begin if ((s_irq[31:27] !== '0) || (s_irq[19:18] !== '0)) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq); $fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
end end
+28 -1
View File
@@ -93,6 +93,7 @@ module sysctrl_tb;
sysctrl.perf_psram_wait_i = 64'd17; sysctrl.perf_psram_wait_i = 64'd17;
sysctrl.perf_flash_wait_i = 64'd18; sysctrl.perf_flash_wait_i = 64'd18;
sysctrl.rtc_wake_i = 1'b0; sysctrl.rtc_wake_i = 1'b0;
sysctrl.hp_present_i = 1'b0;
pll_ctrl.req_ready_i = 1'b1; pll_ctrl.req_ready_i = 1'b1;
pll_ctrl.rsp_active_sel_i = '0; pll_ctrl.rsp_active_sel_i = '0;
pll_ctrl.rsp_active_valid_i = 1'b0; pll_ctrl.rsp_active_valid_i = 1'b0;
@@ -216,7 +217,33 @@ module sysctrl_tb;
$fatal(1, "RTC wake sticky status W1C failed"); $fatal(1, "RTC wake sticky status W1C failed");
end end
$display("SystemCtrl register, lifecycle, fault, performance, and RTC wake test passed"); read_register(32'h1000_B0A8, read_data);
if (read_data !== 32'h0000_0000) $fatal(1, "absent HP status was not zero");
write_register(32'h1000_B0A4, 32'h0000_0001);
write_register(32'h1000_B0AC, 32'h0000_0001);
if (sysctrl.hp_release_o || sysctrl.debug_hp_select_o) begin
$fatal(1, "absent HP accepted lifecycle control");
end
sysctrl.hp_present_i = 1'b1;
read_register(32'h1000_B0A8, read_data);
if (read_data !== 32'h0000_0005) $fatal(1, "present HP reset status mismatch");
write_register(32'h1000_B0AC, 32'h0000_0001);
if (!sysctrl.debug_hp_select_o) $fatal(1, "HP debug selection was not accepted in reset");
write_register(32'h1000_B0A4, 32'h0000_0001);
read_register(32'h1000_B0A8, read_data);
if ((read_data !== 32'h0000_0003) || !sysctrl.hp_release_o) begin
$fatal(1, "HP release status mismatch");
end
write_register(32'h1000_B0AC, 32'h0000_0000);
if (!sysctrl.debug_hp_select_o) $fatal(1, "running HP allowed JTAG selection change");
write_register(32'h1000_B0A4, 32'h0000_0000);
write_register(32'h1000_B0AC, 32'h0000_0000);
if (sysctrl.hp_release_o || sysctrl.debug_hp_select_o) begin
$fatal(1, "HP reset/debug return did not complete");
end
$display("SystemCtrl register, lifecycle, fault, performance, RTC wake, and HP test passed");
$finish; $finish;
end end
endmodule endmodule
+43
View File
@@ -12,6 +12,49 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
def test_axi4_downsizer_preserves_bursts_lanes_and_backpressure(
tmp_path: Path,
) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / "axi4_downsizer_64to32_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"axi4_downsizer_64to32_tb",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/mini/top/axi4_downsizer_64to32.sv"),
str(ROOT / "tests/rtl/axi4_downsizer_64to32_tb.sv"),
"-Mdir",
str(tmp_path / "obj"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "AXI4 64-to-32 downsizer test passed" in result.stdout
def test_axi4_word_bridge_supports_bursts_backpressure_and_errors(tmp_path: Path) -> None: def test_axi4_word_bridge_supports_bursts_backpressure_and_errors(tmp_path: Path) -> None:
verilator = shutil.which("verilator") verilator = shutil.which("verilator")
if verilator is None: if verilator is None:
+107
View File
@@ -0,0 +1,107 @@
"""HP flash bundle and handwritten mailbox ABI tests."""
from __future__ import annotations
import argparse
import importlib.util
import re
import struct
import zlib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PACKAGER = ROOT / "scripts/package_hp_boot.py"
MAILBOX_RTL = ROOT / "rtl/ip/peripheral/hp_mailbox_define.svh"
MAILBOX_C = ROOT / "crt/src/hal/hp_mailbox.c"
def load_packager():
spec = importlib.util.spec_from_file_location("package_hp_boot", PACKAGER)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_hp_boot_bundle_layout_crc_and_payloads(tmp_path: Path) -> None:
module = load_packager()
firmware = tmp_path / "lp.bin"
firmware.write_bytes(b"LP" * 32)
images = tmp_path / "images"
images.mkdir()
expected: dict[str, bytes] = {}
for index, (_, name, _, _) in enumerate(module.ARTIFACTS, start=1):
expected[name] = bytes([index]) * (31 + index)
(images / name).write_bytes(expected[name])
output = tmp_path / "boot.bin"
manifest = tmp_path / "boot.json"
module.package(
argparse.Namespace(
firmware=firmware,
images=images,
output=output,
manifest=manifest,
)
)
image = output.read_bytes()
header_values = module.HEADER.unpack_from(image, module.BUNDLE_OFFSET)
magic, version, header_size, entry_count, total_size, header_crc, flags, reserved = (
header_values
)
assert magic == module.MAGIC
assert version == module.VERSION
assert header_size == module.HEADER_SIZE == 128
assert entry_count == len(module.ARTIFACTS) == 4
assert total_size == len(image) - module.BUNDLE_OFFSET
assert flags == module.REQUIRED
assert reserved == 0
header = bytearray(image[module.BUNDLE_OFFSET : module.BUNDLE_OFFSET + header_size])
struct.pack_into("<I", header, 20, 0)
assert zlib.crc32(header) & 0xFFFFFFFF == header_crc
for index, (_, name, load_address, _) in enumerate(module.ARTIFACTS):
entry_offset = module.BUNDLE_OFFSET + module.HEADER.size + index * module.ENTRY.size
kind, flash_offset, entry_address, size, crc32, entry_flags = module.ENTRY.unpack_from(
image, entry_offset
)
assert kind == index + 1
assert entry_address == load_address
assert image[flash_offset : flash_offset + size] == expected[name]
assert zlib.crc32(expected[name]) & 0xFFFFFFFF == crc32
assert entry_flags == module.REQUIRED
def test_mailbox_c_offsets_match_handwritten_rtl() -> None:
rtl_pattern = re.compile(r"^`define\s+APB4_HP_MAILBOX__(\w+)\s+12'h([0-9A-Fa-f]+)$")
c_pattern = re.compile(
r"^#define\s+RS_HP_MAILBOX_(\w+)_OFFSET\s+UINT32_C\(0x([0-9A-Fa-f]+)\)$"
)
rtl = {
match.group(1): int(match.group(2), 16)
for line in MAILBOX_RTL.read_text(encoding="utf-8").splitlines()
if (match := rtl_pattern.match(line)) is not None
}
c = {
match.group(1): int(match.group(2), 16)
for line in MAILBOX_C.read_text(encoding="utf-8").splitlines()
if (match := c_pattern.match(line)) is not None
}
assert c == {
name: rtl[name]
for name in (
"IP_VERSION",
"CAPABILITY",
"LP_COMMAND",
"LP_ARG0",
"LP_SEQUENCE",
"LP_DOORBELL",
"HP_EVENT",
"HP_ARG0",
"HP_SEQUENCE",
"LP_INTR_STATE",
"LP_INTR_ENABLE",
)
}
+52
View File
@@ -0,0 +1,52 @@
"""HP mailbox RTL regression test."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_hp_mailbox_registers_and_doorbells(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
common = ROOT / "rtl/managed/clusterip/common/rtl"
peripheral = ROOT / "rtl/ip/peripheral"
filelist = tmp_path / "hp_mailbox.fl"
filelist.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{common / 'utils'}",
f"+incdir+{peripheral}",
str(common / "interface/apb4_if.sv"),
str(common / "utils/register.sv"),
str(peripheral / "apb4_hp_mailbox.sv"),
str(ROOT / "tests/rtl/hp_mailbox_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "hp_mailbox_tb.v"
subprocess.run(
[sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(filelist),
"--output", str(converted)],
check=True,
)
simulation = tmp_path / "hp_mailbox_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "hp_mailbox_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True)
assert "HP mailbox register and doorbell test passed" in result.stdout
+133
View File
@@ -0,0 +1,133 @@
"""HP platform configuration and generated-source boundary tests."""
from __future__ import annotations
import json
import shutil
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_hp_profile_and_locked_generator_contract() -> None:
lock = json.loads((ROOT / "dependencies/dependencies.lock.json").read_text(encoding="utf-8"))
vexii = lock["sources"]["vexiiriscv"]
assert vexii["revision"] == "ef7da4134ff11ec6a681d0632123b232b853b046"
assert vexii["recursive"] is True
assert vexii["destination"].startswith(".cache/")
profile = (ROOT / "configs/ci/ihp130-hp.mk").read_text(encoding="utf-8")
assert "HAVE_HP" in profile and "YES" in profile
assert "rv32imafdc_max" in profile
assert "APP" in profile and "hp_boot" in profile
assert "LINK_TYPE" in profile and "ld2_all_sram" in profile
generator = (ROOT / "scripts/vexiiriscv/GenerateRetroSocHp.scala").read_text(
encoding="utf-8"
)
for requirement in (
'param.addISA("m", "a", "f", "d", "c", "s", "u", "zicntr", "zihpm")',
"param.decoders = 2",
"param.lanes = 2",
"param.fetchL1Ways = 4",
"param.lsuL1Ways = 4",
"param.resetVector = 0x38000000L",
"param.plugins(hartId = 1)",
):
assert requirement in generator
def test_makefile_uses_path_resolved_sbt() -> None:
makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
assert "SBT ?= sbt" in makefile
assert "/nfs/home/miaoyuchi/sbt/bin/sbt" not in makefile
def test_hp_address_and_sysctrl_contract() -> None:
document = json.loads(
(ROOT / "rtl/mini/address_map/memory_map.json").read_text(encoding="utf-8")
)
regions = {region["symbol"]: region for region in document["regions"]}
assert regions["HP_ACLINT"]["base"] == "0x02000000"
assert regions["HP_PLIC"]["base"] == "0x0C000000"
assert regions["APB4_UART1"]["base"] == "0x10018000"
assert regions["APB4_HP_MAILBOX"]["base"] == "0x10019000"
registers = {register["symbol"]: register["offset"] for register in document["sysctrl_registers"]}
assert registers["HP_CTRL"] == "0xA4"
assert registers["HP_STATUS"] == "0xA8"
assert registers["DEBUG_SELECT"] == "0xAC"
def test_generated_vexii_rtl_is_not_tracked() -> None:
tracked = subprocess.run(
["git", "ls-files", "*vexii_riscv_hp_generated*"],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
).stdout
assert tracked == ""
def test_hp_smoke_payload_uses_hart1_platform_abi() -> None:
source = (ROOT / "app/ports/linux/smoke/start.S").read_text(encoding="utf-8")
assert "0x10018000" in source
assert "0x10019000" in source
assert "0x4c4e5801" in source.lower()
assert "HP_SMOKE_READY" in source
def test_linux_build_uses_external_opensbi_platform_and_actual_initrd_end() -> None:
build = (ROOT / "scripts/build_hp_linux.py").read_text(encoding="utf-8")
assert '"tinyconfig"' in build
assert 'environment.pop("MAKEOVERRIDES", None)' in build
assert '"PLATFORM=retrosoc_hp"' in build
assert 'f"PLATFORM_DIR={external / \'opensbi\'}"' in build
assert '"fdtput"' in build
assert '"linux,initrd-end"' in build
platform = (
ROOT / "app/ports/linux/opensbi/retrosoc_hp/platform.c"
).read_text(encoding="utf-8")
assert "s_hart_index_to_id[] = {1U}" in platform
assert "RETROSOC_HP_UART_BASE" in platform
assert "aclint_mtimer_cold_init" in platform
def test_hp_linux_device_tree_compiles_and_can_patch_initrd_end(tmp_path: Path) -> None:
dtc = shutil.which("dtc")
fdtput = shutil.which("fdtput")
fdtget = shutil.which("fdtget")
if dtc is None or fdtput is None or fdtget is None:
return
dtb = tmp_path / "retrosoc_hp.dtb"
subprocess.run(
[
dtc,
"-I",
"dts",
"-O",
"dtb",
"-o",
str(dtb),
str(ROOT / "app/ports/linux/linux/retrosoc_hp.dts"),
],
check=True,
capture_output=True,
text=True,
)
subprocess.run(
[fdtput, "-t", "x", str(dtb), "/chosen", "linux,initrd-end", "0x39123456"],
check=True,
)
initrd_end = subprocess.check_output(
[fdtget, "-t", "x", str(dtb), "/chosen", "linux,initrd-end"], text=True
).strip()
hart_id = subprocess.check_output(
[fdtget, "-t", "x", str(dtb), "/cpus/cpu@1", "reg"], text=True
).strip()
assert initrd_end == "39123456"
assert hart_id == "1"
+2 -2
View File
@@ -44,10 +44,10 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
sides = {name: config[f"PAD_{name.upper()}"] for name in module.SIDE_ORDER} sides = {name: config[f"PAD_{name.upper()}"] for name in module.SIDE_ORDER}
placed = [instance for side in module.SIDE_ORDER for instance in sides[side]] placed = [instance for side in module.SIDE_ORDER for instance in sides[side]]
assert len(placed) == 189 assert len(placed) == 191
assert len(placed) == len(set(placed)) assert len(placed) == len(set(placed))
assert {side: len(instances) for side, instances in sides.items()} == { assert {side: len(instances) for side, instances in sides.items()} == {
"south": 30, "south": 32,
"east": 52, "east": 52,
"north": 48, "north": 48,
"west": 59, "west": 59,
+21
View File
@@ -2,6 +2,9 @@
from __future__ import annotations from __future__ import annotations
from decimal import Decimal
from scripts.check_lp_hp_performance import compare as compare_lp_hp
from scripts.parse_coremark_log import parse_log as parse_coremark_log from scripts.parse_coremark_log import parse_log as parse_coremark_log
from scripts.parse_performance_log import parse_log from scripts.parse_performance_log import parse_log
@@ -91,3 +94,21 @@ def test_parse_coremark_rejects_failure_or_non_sram_result() -> None:
assert "SRAM" in str(error) assert "SRAM" in str(error)
else: else:
raise AssertionError("non-SRAM CoreMark result was accepted") raise AssertionError("non-SRAM CoreMark result was accepted")
def test_lp_hp_coremark_ratio_gate() -> None:
lp = parse_coremark_log(
"COREMARK_RESULT mode=quick qualified=0 memory=sram iterations=4 cycles=2000 "
"cpu_hz=72000000\nCOREMARK_PASS\n"
)
hp = parse_coremark_log(
"COREMARK_RESULT mode=quick qualified=0 memory=sram iterations=12 cycles=2000 "
"cpu_hz=72000000\nCOREMARK_PASS\n"
)
report = compare_lp_hp(lp, hp, Decimal("2.5"))
assert report["status"] == "passed"
assert report["measured_ratio"] == "3.000"
hp["results"][0]["coremark_per_mhz"] = "4000.000"
report = compare_lp_hp(lp, hp, Decimal("2.5"))
assert report["status"] == "failed"
+51
View File
@@ -0,0 +1,51 @@
"""HP PLIC RTL regression test."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_plic_priority_claim_and_completion(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
common = ROOT / "rtl/managed/clusterip/common/rtl"
peripheral = ROOT / "rtl/ip/peripheral"
filelist = tmp_path / "plic.fl"
filelist.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{common / 'utils'}",
str(common / "interface/apb4_if.sv"),
str(common / "utils/register.sv"),
str(peripheral / "apb4_plic.sv"),
str(ROOT / "tests/rtl/plic_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "plic_tb.v"
subprocess.run(
[sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(filelist),
"--output", str(converted)],
check=True,
)
simulation = tmp_path / "plic_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "plic_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True)
assert "PLIC priority, claim, and completion test passed" in result.stdout
+8 -2
View File
@@ -75,7 +75,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
irq_sva = (tmp_path / "rtl/soc_irq_sva.svh").read_text(encoding="utf-8") irq_sva = (tmp_path / "rtl/soc_irq_sva.svh").read_text(encoding="utf-8")
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8") filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
assert interfaces.count("apb4_if u_") == 20 assert interfaces.count("apb4_if u_") == 24
assert "nmi_if" not in interfaces assert "nmi_if" not in interfaces
assert "soc_nmi" not in interfaces assert "soc_nmi" not in interfaces
assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes
@@ -86,6 +86,10 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign s_psel_comb[18] = `SOC_ADDR_IS_APB4_SDIO1(s_decode_addr);" in routes assert "assign s_psel_comb[18] = `SOC_ADDR_IS_APB4_SDIO1(s_decode_addr);" in routes
assert "assign s_psel_comb[19] = `SOC_ADDR_IS_APB4_CRYPTO(s_decode_addr);" in routes assert "assign s_psel_comb[19] = `SOC_ADDR_IS_APB4_CRYPTO(s_decode_addr);" in routes
assert "assign s_psel_comb[20] = `SOC_ADDR_IS_APB4_USB2(s_decode_addr);" in routes assert "assign s_psel_comb[20] = `SOC_ADDR_IS_APB4_USB2(s_decode_addr);" in routes
assert "assign s_psel_comb[22] = `SOC_ADDR_IS_APB4_UART1(s_decode_addr);" in routes
assert "assign s_psel_comb[23] = `SOC_ADDR_IS_APB4_HP_MAILBOX(s_decode_addr);" in routes
assert "assign s_psel_comb[24] = `SOC_ADDR_IS_HP_ACLINT(s_decode_addr);" in routes
assert "assign s_psel_comb[25] = `SOC_ADDR_IS_HP_PLIC(s_decode_addr);" in routes
assert gpio.count("// GPIO") == 64 assert gpio.count("// GPIO") == 64
assert "u_uart1_if" not in gpio assert "u_uart1_if" not in gpio
assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio
@@ -132,7 +136,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 20" in irq_config assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 22" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 5" in irq_config assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 5" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
@@ -206,6 +210,8 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("sdio1", "apb4_periph", 17, 21, "sdio1.irq_o"), ("sdio1", "apb4_periph", 17, 21, "sdio1.irq_o"),
("crypto", "apb4_periph", 18, 23, "s_crypto_irq"), ("crypto", "apb4_periph", 18, 23, "s_crypto_irq"),
("usb2", "apb4_periph", 19, 24, "s_usb2_irq"), ("usb2", "apb4_periph", 19, 24, "s_usb2_irq"),
("hp_mailbox_lp", "apb4_periph", 20, 25, "s_mailbox_lp_irq"),
("uart1", "apb4_periph", 21, 26, "uart1.irq_o"),
] ]
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more