mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
163 files changed
+21994
-2552
No files matched your search
+3
-2
@@ -4,8 +4,9 @@ This directory owns repository automation metadata: pull-request templates,
|
||||
CODEOWNERS, Dependabot configuration, reusable actions, and CI/release
|
||||
workflows.
|
||||
|
||||
`workflows/quality.yml` runs the fast quality gate. `workflows/regression.yml`
|
||||
and `workflows/nightly.yml` call the reusable regression workflow;
|
||||
`workflows/quality.yml` runs the fast quality gate. `workflows/regression-smoke.yml`,
|
||||
`workflows/regression-ihp130.yml`, `workflows/regression-gf180.yml`,
|
||||
`workflows/regression-sky130.yml`, and `workflows/nightly.yml` call the reusable regression workflow;
|
||||
`workflows/release.yml` packages tagged releases. `actions/locked-tools/`
|
||||
installs the locked open-source tools used by CI.
|
||||
|
||||
|
||||
@@ -7,11 +7,39 @@ on:
|
||||
description: Regression suite from scripts/regress.py
|
||||
required: true
|
||||
type: string
|
||||
pdk:
|
||||
description: PDK matrix selected by scripts/regress.py
|
||||
required: true
|
||||
type: string
|
||||
profile:
|
||||
description: Primary profile used for setup and environment checks
|
||||
required: true
|
||||
type: string
|
||||
retention_days:
|
||||
description: Report artifact retention
|
||||
required: false
|
||||
default: 30
|
||||
type: number
|
||||
timeout_minutes:
|
||||
description: Maximum job duration
|
||||
required: false
|
||||
default: 240
|
||||
type: number
|
||||
toolset:
|
||||
description: Space-separated locked tools required by this suite
|
||||
required: false
|
||||
default: verilator sv2v iverilog yosys opensta riscv_gnu
|
||||
type: string
|
||||
full_flow_checks:
|
||||
description: Check Yosys and OpenSTA environments before running the suite
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
formal_checks:
|
||||
description: Run SBY-managed Yosys, sv2v, and Bitwuzla protocol proofs before the suite
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -19,7 +47,7 @@ permissions:
|
||||
jobs:
|
||||
regression:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 240
|
||||
timeout-minutes: ${{ inputs.timeout_minutes }}
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
||||
steps:
|
||||
@@ -33,6 +61,8 @@ jobs:
|
||||
app/coremark/coremark-main
|
||||
app/fatfs/ff16
|
||||
pdk/IHP-Open-PDK
|
||||
pdk/gf180mcu-pdk
|
||||
pdk/skywater-pdk
|
||||
rtl/managed/clusterip/archinfo
|
||||
rtl/managed/clusterip/clint
|
||||
rtl/managed/clusterip/common
|
||||
@@ -50,8 +80,9 @@ jobs:
|
||||
rtl/managed/clusterip/wdg
|
||||
rtl/managed/third_party
|
||||
rtl/managed/mpw
|
||||
.cache/retrosoc/pdk
|
||||
.cache/retrosoc/stamps
|
||||
key: sources-${{ runner.os }}-${{ hashFiles('config/dependencies.lock.json') }}
|
||||
key: sources-${{ inputs.pdk }}-${{ runner.os }}-${{ hashFiles('config/dependencies.lock.json') }}
|
||||
|
||||
- name: Restore locked toolchains
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
@@ -76,19 +107,34 @@ jobs:
|
||||
- name: Set up locked EDA and compiler tools
|
||||
uses: ./.github/actions/locked-tools
|
||||
with:
|
||||
tools: verilator sv2v iverilog yosys opensta riscv_gnu
|
||||
tools: ${{ inputs.toolset }}
|
||||
|
||||
- name: Set up locked source dependencies
|
||||
run: make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk setup
|
||||
run: make CONFIG=${{ inputs.profile }} setup
|
||||
|
||||
- name: Check environment
|
||||
- name: Check behavioral environments
|
||||
run: |
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk \
|
||||
SIMU=IVERILOG SYNTH=YOSYS STA=OPENSTA doctor
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=VERILATOR doctor
|
||||
make CONFIG=${{ inputs.profile }} \
|
||||
SIMU=IVERILOG SYNTH=NONE STA=NONE doctor
|
||||
make CONFIG=${{ inputs.profile }} \
|
||||
SIMU=VERILATOR SYNTH=NONE STA=NONE doctor
|
||||
|
||||
- name: Check synthesis and timing environments
|
||||
if: inputs.full_flow_checks
|
||||
run: |
|
||||
make CONFIG=${{ inputs.profile }} \
|
||||
SIMU=IVERILOG SYNTH=YOSYS STA=NONE doctor
|
||||
make CONFIG=${{ inputs.profile }} \
|
||||
SIMU=IVERILOG SYNTH=NONE STA=OPENSTA doctor
|
||||
|
||||
- name: Check formal environment
|
||||
if: inputs.formal_checks
|
||||
run: |
|
||||
make CONFIG=${{ inputs.profile }} formal-doctor
|
||||
make CONFIG=${{ inputs.profile }} formal
|
||||
|
||||
- name: Run ${{ inputs.suite }} regression
|
||||
run: python3 scripts/regress.py --root . --suite "${{ inputs.suite }}"
|
||||
run: python3 scripts/regress.py --root . --suite "${{ inputs.suite }}" --pdk "${{ inputs.pdk }}"
|
||||
|
||||
- name: Upload reports and structured results
|
||||
if: always()
|
||||
@@ -97,6 +143,11 @@ jobs:
|
||||
name: ${{ inputs.suite }}-regression-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: |
|
||||
build/**/meta/*.json
|
||||
build/**/formal/**/*.sby
|
||||
build/**/formal/**/*.smt2
|
||||
build/**/formal/**/*.vcd
|
||||
build/**/formal/**/*.yw
|
||||
build/**/formal/**/status
|
||||
build/**/*.log
|
||||
build/**/*.rpt
|
||||
if-no-files-found: warn
|
||||
|
||||
@@ -17,4 +17,6 @@ jobs:
|
||||
uses: ./.github/workflows/_regression.yml
|
||||
with:
|
||||
suite: nightly
|
||||
pdk: IHP130
|
||||
profile: configs/ci/ihp130.mk
|
||||
retention_days: 90
|
||||
@@ -44,6 +44,9 @@ jobs:
|
||||
- name: Validate dependency lock
|
||||
run: python3 scripts/dependency_lock.py --lock config/dependencies.lock.json
|
||||
|
||||
- name: Validate SoC integration topology
|
||||
run: make check-soc-topology check-user-extensions check-clock-reset-domains
|
||||
|
||||
- name: Lint Python
|
||||
run: ruff check .
|
||||
|
||||
@@ -74,5 +77,8 @@ jobs:
|
||||
|
||||
- name: Validate regression definitions
|
||||
run: |
|
||||
python3 scripts/regress.py --root . --suite pr --dry-run
|
||||
python3 scripts/regress.py --root . --suite nightly --dry-run
|
||||
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --dry-run
|
||||
python3 scripts/regress.py --root . --suite pr --pdk GF180 --dry-run
|
||||
python3 scripts/regress.py --root . --suite pr --pdk SKY130 --dry-run
|
||||
python3 scripts/regress.py --root . --suite nightly --pdk IHP130 --dry-run
|
||||
python3 scripts/regress.py --root . --suite smoke --pdk IHP130 --dry-run
|
||||
@@ -0,0 +1,24 @@
|
||||
name: regression-gf180
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: regression-gf180-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verified-open-source-flows:
|
||||
uses: ./.github/workflows/_regression.yml
|
||||
with:
|
||||
suite: pr
|
||||
pdk: GF180
|
||||
profile: configs/ci/gf180.mk
|
||||
retention_days: 30
|
||||
@@ -1,4 +1,4 @@
|
||||
name: regression
|
||||
name: regression-ihp130
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,4 +19,6 @@ jobs:
|
||||
uses: ./.github/workflows/_regression.yml
|
||||
with:
|
||||
suite: pr
|
||||
pdk: IHP130
|
||||
profile: configs/ci/ihp130.mk
|
||||
retention_days: 30
|
||||
@@ -0,0 +1,24 @@
|
||||
name: regression-sky130
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: regression-sky130-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verified-open-source-flows:
|
||||
uses: ./.github/workflows/_regression.yml
|
||||
with:
|
||||
suite: pr
|
||||
pdk: SKY130
|
||||
profile: configs/ci/sky130.mk
|
||||
retention_days: 30
|
||||
@@ -0,0 +1,28 @@
|
||||
name: regression-smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: regression-smoke-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
ihp130-smoke:
|
||||
uses: ./.github/workflows/_regression.yml
|
||||
with:
|
||||
suite: smoke
|
||||
pdk: IHP130
|
||||
profile: configs/ci/ihp130.mk
|
||||
retention_days: 14
|
||||
timeout_minutes: 60
|
||||
toolset: verilator sv2v iverilog yosys sby bitwuzla riscv_gnu
|
||||
full_flow_checks: false
|
||||
formal_checks: true
|
||||
@@ -47,10 +47,10 @@ jobs:
|
||||
run: python3 -m pip install --requirement requirements/build.txt
|
||||
|
||||
- name: Set up locked source dependencies
|
||||
run: make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk setup
|
||||
run: make CONFIG=configs/ci/ihp130.mk setup
|
||||
|
||||
- name: Build checksummed source package and SBOM
|
||||
run: make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk package
|
||||
run: make CONFIG=configs/ci/ihp130.mk package
|
||||
|
||||
- name: Upload workflow artifact
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
|
||||
@@ -139,9 +139,12 @@ warning checks, and metric collection.
|
||||
configured success marker, and the log contains no `FAILED`, `FATAL`,
|
||||
`assertion failed`, or `%Error` marker.
|
||||
- New self-owned C compiler warnings fail the regression. New or increased EDA
|
||||
warning signatures fail `check-warnings`; resolved warnings are reported.
|
||||
warning signatures make `check-warnings` fail when invoked directly, but the
|
||||
regression runner records them as non-blocking observations so CI uploads the
|
||||
warning and metric reports; resolved warnings are reported.
|
||||
- `check-metrics` records firmware size, area, cell count, timing, and flow
|
||||
duration, but its current policy is observational.
|
||||
duration. It is non-blocking in the regression runner, including if a future
|
||||
policy changes the standalone command into a gate.
|
||||
- Verilator currently runs through `scripts/run_flow.py`, which captures the
|
||||
emulator stdout in a pipe. UART output can therefore appear only after a
|
||||
newline or when simulation ends. Treat `sim.log` and `result-sim*.json` as
|
||||
|
||||
@@ -18,7 +18,12 @@ CONFIG_PATH :=
|
||||
PROFILE_NAME := manual
|
||||
endif
|
||||
|
||||
ifneq ($(origin IP),undefined)
|
||||
$(error IP is no longer configurable; user IP integration is fixed)
|
||||
endif
|
||||
|
||||
SOC ?= MINI
|
||||
CORE ?= HAZARD3
|
||||
SIMU ?= VCS
|
||||
SYNTH ?= NONE
|
||||
STA ?= NONE
|
||||
@@ -30,16 +35,14 @@ HAVE_SRAM_IF ?= NO
|
||||
HAVE_SRAM_MACRO ?= NO
|
||||
HAVE_SVA ?= NO
|
||||
WAVE ?= NO
|
||||
FORMAL ?= NO
|
||||
|
||||
RTL_SIM_CORESEL ?= 0
|
||||
RTL_SIM_TIMEOUT ?= -1
|
||||
SIM_FIRMWARE_NAME ?= $(FIRMWARE_NAME)
|
||||
SIM_SUCCESS_MARKER ?= retroSoC: A Customized ASIC for Retro Stuff
|
||||
|
||||
RTL_PATH := $(ROOT_PATH)/rtl/mini
|
||||
|
||||
CORE ?= HAZARD3
|
||||
IP ?= NONE
|
||||
RTL_TOP ?= retrosoc_tb
|
||||
|
||||
# SW
|
||||
@@ -60,7 +63,7 @@ VERIBLE_FORMAT ?= verible-verilog-format
|
||||
JOBS ?= $(shell count=$$(nproc 2>/dev/null || printf '1'); \
|
||||
if [ "$$count" -gt "$(MAX_JOBS)" ]; then printf '%s' '$(MAX_JOBS)'; \
|
||||
else printf '%s' "$$count"; fi)
|
||||
CONFIG_KEY_VARS := SOC CORE IP PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO HAVE_SVA \
|
||||
CONFIG_KEY_VARS := SOC CORE PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO HAVE_SVA \
|
||||
ISA HAVE_CSR APP LINK_TYPE RTL_TOP FIRMWARE_NAME
|
||||
VARIANT_ID := $(strip $(shell python3 $(ROOT_PATH)/scripts/config_key.py \
|
||||
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
|
||||
@@ -84,8 +87,7 @@ FLOW_FILELIST_DIR := $(SIM_BUILD_ROOT)/filelists
|
||||
endif
|
||||
|
||||
VALID_SOC := MINI
|
||||
VALID_CORE := PICORV32 HAZARD3 MDD
|
||||
VALID_IP := NONE MDD
|
||||
VALID_CORE := HAZARD3 PICORV32
|
||||
VALID_SIMU := VCS VERILATOR IVERILOG
|
||||
VALID_SYNTH := NONE YOSYS
|
||||
VALID_STA := NONE OPENSTA
|
||||
@@ -101,7 +103,6 @@ endef
|
||||
|
||||
$(call validate_value,SOC,$(VALID_SOC))
|
||||
$(call validate_value,CORE,$(VALID_CORE))
|
||||
$(call validate_value,IP,$(VALID_IP))
|
||||
$(call validate_value,SIMU,$(VALID_SIMU))
|
||||
$(call validate_value,SYNTH,$(VALID_SYNTH))
|
||||
$(call validate_value,STA,$(VALID_STA))
|
||||
@@ -111,29 +112,32 @@ $(call validate_value,HAVE_SRAM_IF,$(VALID_BOOL))
|
||||
$(call validate_value,HAVE_SRAM_MACRO,$(VALID_BOOL))
|
||||
$(call validate_value,HAVE_SVA,$(VALID_BOOL))
|
||||
$(call validate_value,WAVE,$(VALID_BOOL))
|
||||
$(call validate_value,FORMAL,$(VALID_BOOL))
|
||||
$(call validate_value,ISA,$(VALID_ISA))
|
||||
$(call validate_value,HAVE_CSR,$(VALID_BOOL))
|
||||
$(call validate_value,APP,$(VALID_APP))
|
||||
$(call validate_value,LINK_TYPE,$(VALID_LINK_TYPE))
|
||||
|
||||
ifeq ($(SYNTH),YOSYS)
|
||||
ifeq ($(filter $(PDK),IHP130 ICS55),)
|
||||
$(error Yosys synthesis supports PDK=IHP130 or PDK=ICS55, not PDK=$(PDK))
|
||||
ifeq ($(filter $(PDK),IHP130 ICS55 GF180 SKY130),)
|
||||
$(error Yosys synthesis does not support PDK=$(PDK))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(STA),OPENSTA)
|
||||
ifneq ($(PDK),IHP130)
|
||||
$(error OpenSTA currently supports PDK=IHP130 only)
|
||||
ifeq ($(HAVE_PLL),YES)
|
||||
$(error STA=OPENSTA requires a qualified PDK PLL timing profile; HAVE_PLL=YES is unsupported)
|
||||
endif
|
||||
endif
|
||||
|
||||
DEF_LIST ?= +define+PDK_$(PDK)
|
||||
DEF_LIST += +define+CORE_$(CORE)
|
||||
DEF_LIST += +define+IP_$(IP)
|
||||
DEF_LIST += +define+SIMU_$(SIMU)
|
||||
DEF_LIST += +define+CORE_$(CORE)
|
||||
|
||||
ifeq ($(HAVE_PLL), YES)
|
||||
ifneq ($(filter $(PDK),GF180 SKY130),)
|
||||
$(error HAVE_PLL=YES requires a qualified crystal pad and is unsupported for PDK=$(PDK))
|
||||
endif
|
||||
DEF_LIST += +define+HAVE_PLL
|
||||
endif
|
||||
|
||||
@@ -154,6 +158,7 @@ ifeq ($(SYNTH), YOSYS)
|
||||
endif
|
||||
|
||||
include rtl/mini/Makefile
|
||||
include rtl/mini/mk/formal.mk
|
||||
|
||||
ifeq ($(SYNTH), YOSYS)
|
||||
include syn/yosys/yosys.mk
|
||||
@@ -165,9 +170,11 @@ endif
|
||||
|
||||
.PHONY: help config doctor setup setup-mpw setup-core setup-clusterip setup-ip setup-pdk setup-app \
|
||||
clean-all purge-cache manifest check-warnings metrics check-metrics package \
|
||||
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 sw-policy-check sw-host-test \
|
||||
pin-map check-pin-map
|
||||
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
||||
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
|
||||
formal formal-bus formal-nmi2apb formal-clean formal-doctor
|
||||
.NOTPARALLEL: setup
|
||||
|
||||
help:
|
||||
@@ -186,6 +193,15 @@ help:
|
||||
' check-memory-map validate the canonical address map' \
|
||||
' pin-map generate the selected pin-map artifacts' \
|
||||
' check-pin-map validate the canonical SoC pin map' \
|
||||
' soc-topology generate the selected internal SoC integration artifacts' \
|
||||
' check-soc-topology validate the canonical internal SoC integration map' \
|
||||
' user-extensions generate the selected scalar user-extension bindings' \
|
||||
' check-user-extensions validate the canonical user-extension map' \
|
||||
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
|
||||
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
|
||||
' formal | formal-bus | formal-nmi2apb | formal-sysctrl | formal-pll-rcu | formal-gpio-user run SBY protocol proofs' \
|
||||
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
|
||||
' tech-cell-test test GF180/SKY130 technology IO and clock wrappers' \
|
||||
' check-warnings | metrics analyze flow logs and reports' \
|
||||
' check-metrics apply the committed metrics policy' \
|
||||
' format format self-owned C, Makefile, and RTL sources' \
|
||||
@@ -196,20 +212,21 @@ help:
|
||||
' rtl-format | rtl-format-check apply/check self-owned RTL formatting' \
|
||||
' sw-policy-check check embedded C API and naming policy' \
|
||||
' sw-host-test run host tests for deterministic SDK utilities' \
|
||||
' regress-pr | regress-nightly run supported regression suites' \
|
||||
' regress-smoke run the IHP130 fast regression suite' \
|
||||
' regress-pr | regress-nightly run supported full regression suites' \
|
||||
' package create checksummed source deliverables' \
|
||||
' clean | clean-all clean current flow or all build output' \
|
||||
' purge-cache remove dependency and compiler caches' \
|
||||
'' \
|
||||
'Usage: make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG sim'
|
||||
'Usage: make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG sim'
|
||||
|
||||
config:
|
||||
@printf '%-18s %s\n' \
|
||||
ROOT_PATH '$(ROOT_PATH)' CONFIG '$(or $(CONFIG_PATH),<defaults>)' \
|
||||
BUILD_TIMESTAMP '$(BUILD_TIMESTAMP)' VARIANT_ID '$(VARIANT_ID)' VARIANT_ROOT '$(VARIANT_ROOT)' \
|
||||
JOBS '$(JOBS)' \
|
||||
SOC '$(SOC)' CORE '$(CORE)' IP '$(IP)' \
|
||||
SIMU '$(SIMU)' SYNTH '$(SYNTH)' STA '$(STA)' PDK '$(PDK)' \
|
||||
SOC '$(SOC)' CORE '$(CORE)' \
|
||||
SIMU '$(SIMU)' SYNTH '$(SYNTH)' STA '$(STA)' FORMAL '$(FORMAL)' PDK '$(PDK)' \
|
||||
HAVE_PLL '$(HAVE_PLL)' HAVE_SRAM_IF '$(HAVE_SRAM_IF)' \
|
||||
HAVE_SRAM_MACRO '$(HAVE_SRAM_MACRO)' HAVE_SVA '$(HAVE_SVA)' \
|
||||
ISA '$(ISA)' HAVE_CSR '$(HAVE_CSR)' APP '$(APP)' \
|
||||
@@ -218,7 +235,7 @@ config:
|
||||
doctor:
|
||||
@python3 $(ROOT_PATH)/scripts/doctor.py \
|
||||
--root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \
|
||||
--pdk $(PDK) --core $(CORE) --ip $(IP) --lock $(LOCK_FILE)
|
||||
--pdk $(PDK) --formal $(FORMAL) --lock $(LOCK_FILE)
|
||||
|
||||
setup: setup-mpw setup-core setup-clusterip setup-ip setup-pdk setup-app
|
||||
|
||||
@@ -307,8 +324,13 @@ package: $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) manifest
|
||||
python3 $(ROOT_PATH)/scripts/package.py --root $(ROOT_PATH) --lock $(LOCK_FILE) \
|
||||
--variant-root $(VARIANT_ROOT) --output-dir $(ROOT_PATH)/dist/$(VARIANT_ID)
|
||||
|
||||
regress-smoke:
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130
|
||||
|
||||
regress-pr:
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk SKY130
|
||||
|
||||
regress-nightly:
|
||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# retroSoC
|
||||
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/quality.yml)
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/regression.yml)
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ihp130.yml)
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/regression-gf180.yml)
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/regression-sky130.yml)
|
||||
[](https://github.com/retroSoC/retroSoC/actions/workflows/nightly.yml)
|
||||
|
||||
retroSoC is a fully open-source RISC-V SoC project. The repository brings together
|
||||
@@ -11,9 +13,11 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
|
||||
|
||||
## Highlights
|
||||
|
||||
- Selectable RV32IM CPU configurations using HAZARD3, PicoRV32, or MDD cores.
|
||||
- Configurable IHP130 and ICS55 implementation targets, with open-source IHP130 flows
|
||||
available in CI and ICS55 flows intended for a configured cluster environment.
|
||||
- A fixed architecture with a Hazard3 management core by default (PicoRV32 is
|
||||
an explicit build option) and software-selected
|
||||
user-core and user-IP extension slots.
|
||||
- Configurable GF180, SKY130, IHP130, and ICS55 implementation targets. GF180, SKY130,
|
||||
and IHP130 have open-source CI coverage; ICS55 remains a configured cluster target.
|
||||
- A memory-mapped peripheral subsystem with GPIO, UART, timers, PWM, I2C, I2S, PS2,
|
||||
1-Wire, SPI/QSPI, SDIO, PSRAM/OPI-PSRAM, SDRAM, DMA, LCD, RTC, watchdog, RNG, and CRC
|
||||
support. Available interfaces depend on the selected SoC configuration.
|
||||
@@ -40,18 +44,18 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
|
||||
|
||||
## Supported Configurations
|
||||
|
||||
The committed profiles are the supported starting points. They select a core, ISA, PDK,
|
||||
application, linker layout, and optional features as one reproducible configuration.
|
||||
The committed profiles are the supported starting points. They select an ISA,
|
||||
PDK, application, linker layout, and optional features as one reproducible
|
||||
configuration. The user-extension fabric is fixed; `CORE` selects the management
|
||||
core and defaults to `HAZARD3`.
|
||||
|
||||
| Profile | Core / ISA | Application | Coverage |
|
||||
| Profile | ISA | Application | Coverage |
|
||||
| --- | --- | --- | --- |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130.mk`](configs/ci/hazard3-rv32im-ihp130.mk) | HAZARD3 / RV32IM | `bringup` | Pull-request open-source regression: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-shell.mk`](configs/ci/hazard3-rv32im-ihp130-shell.mk) | HAZARD3 / RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk`](configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk) | HAZARD3 / RV32IM + MDD IP | `bringup` | Pull-request firmware and Verilator integration coverage for the user-IP GPIO ownership path. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk`](configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk) | HAZARD3 / RV32IM + MDD IP | `shell` | Pull-request firmware compilation coverage for the user-IP software command. |
|
||||
| [`configs/ci/mdd-rv32im-ihp130.mk`](configs/ci/mdd-rv32im-ihp130.mk) | MDD / RV32IM | `bringup` | Pull-request firmware and Verilator regression. |
|
||||
| [`configs/nightly/picorv32-rv32im-ihp130.mk`](configs/nightly/picorv32-rv32im-ihp130.mk) | PicoRV32 / RV32IM | `bringup` | Nightly firmware, Verilator, and Icarus regression. |
|
||||
| [`configs/cluster/hazard3-ics55.mk`](configs/cluster/hazard3-ics55.mk) | HAZARD3 / RV32IM | `bringup` | Cluster-only configuration; requires the site PDK and licensed-tool environment. |
|
||||
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Pull-request open-source regression: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
|
||||
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
|
||||
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
|
||||
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
|
||||
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Cluster-only configuration; requires the site PDK and licensed-tool environment. |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -76,20 +80,20 @@ and dependency update procedure.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Start with the CI-verified HAZARD3/IHP130 `bringup` profile. `setup` retrieves the pinned
|
||||
Start with the CI-verified IHP130 `bringup` profile. `setup` retrieves the pinned
|
||||
source dependencies, and `doctor` reports any missing local executables or setup inputs.
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk setup
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG doctor
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk firmware
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG sim
|
||||
make CONFIG=configs/ci/ihp130.mk setup
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG doctor
|
||||
make CONFIG=configs/ci/ihp130.mk firmware
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG sim
|
||||
```
|
||||
|
||||
Select the interactive shell application without editing source files:
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130-shell.mk firmware
|
||||
make CONFIG=configs/ci/ihp130-shell.mk firmware
|
||||
```
|
||||
|
||||
## Common Flows
|
||||
@@ -99,10 +103,13 @@ variant identifier; `make help` lists all available targets. Netlist simulation
|
||||
analysis consume the Yosys netlist, so run the synthesis command first. The CI-proven netlist
|
||||
regression uses the assembly self-test image:
|
||||
|
||||
Verilator simulations run for 180 seconds by default. Set `SOC_SIM_TIME` explicitly only when
|
||||
an exploratory local run needs a different limit.
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG RTL_SIM_TIMEOUT=5200000 sim-asm
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SYNTH=YOSYS synth
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG \
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG RTL_SIM_TIMEOUT=5200000 sim-asm
|
||||
make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS synth
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG \
|
||||
SIM_FIRMWARE_NAME=retrosoc_asm \
|
||||
SIM_SUCCESS_MARKER='Mem wr/rd test success' \
|
||||
RTL_SIM_TIMEOUT=5200000 netsim
|
||||
@@ -110,18 +117,27 @@ make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG \
|
||||
|
||||
| Goal | Command |
|
||||
| --- | --- |
|
||||
| Icarus behavioral simulation | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG sim` |
|
||||
| Verilator behavioral simulation | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=VERILATOR sim` |
|
||||
| Assembly self-test with Icarus | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG RTL_SIM_TIMEOUT=5200000 sim-asm` |
|
||||
| Yosys synthesis | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SYNTH=YOSYS synth` |
|
||||
| Icarus netlist simulation after synthesis | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG netsim` |
|
||||
| OpenSTA timing analysis after synthesis | `make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk STA=OPENSTA sta` |
|
||||
| Icarus behavioral simulation | `make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG sim` |
|
||||
| Verilator behavioral simulation | `make CONFIG=configs/ci/ihp130.mk SIMU=VERILATOR sim` |
|
||||
| Assembly self-test with Icarus | `make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG RTL_SIM_TIMEOUT=5200000 sim-asm` |
|
||||
| Yosys synthesis | `make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS synth` |
|
||||
| Icarus netlist simulation after synthesis | `make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG netsim` |
|
||||
| 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` |
|
||||
| IHP130 fast smoke suite | `make regress-smoke` |
|
||||
| Pull-request regression suite | `make regress-pr` |
|
||||
| Nightly regression suite | `make regress-nightly` |
|
||||
| Format C, Makefile, and RTL sources | `make format` |
|
||||
| Check C, Makefile, and RTL formatting | `make format-check` |
|
||||
| Script and policy checks | `make sw-policy-check sw-host-test` |
|
||||
|
||||
`make regress-smoke` builds the IHP130 firmware, compiles the Verilator SVA
|
||||
configuration, and runs the Icarus assembly self-test. It runs strict RTL lint
|
||||
before those flows and omits synthesis,
|
||||
timing, and netlist simulation for fast feedback. `make regress-pr` runs the
|
||||
supported IHP130, GF180, and SKY130 PR matrices in sequence, including
|
||||
slow-corner OpenSTA core timing analysis for each PDK.
|
||||
|
||||
Build outputs are isolated below `build/<profile>-<YYYY-MM-DD-HH-MM>-<config-hash>/`. Each variant keeps its
|
||||
firmware, generated sources, simulator output, synthesis and timing reports, manifest, warning
|
||||
analysis, and metrics separate from other configurations. Use `make clean` to remove the
|
||||
@@ -133,8 +149,8 @@ commands must reuse one variant:
|
||||
|
||||
```sh
|
||||
export BUILD_TIMESTAMP=2026-07-21-10-39
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk firmware
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG sim
|
||||
make CONFIG=configs/ci/ihp130.mk firmware
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG sim
|
||||
```
|
||||
|
||||
## Reproducibility And CI
|
||||
|
||||
+3
-3
@@ -38,10 +38,10 @@ Build and simulate the supported profiles from the repository root:
|
||||
|
||||
```sh
|
||||
# Bringup firmware and Verilator simulation
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=VERILATOR firmware sim
|
||||
make CONFIG=configs/ci/ihp130.mk SIMU=VERILATOR firmware sim
|
||||
|
||||
# Shell firmware and Verilator simulation
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130-shell.mk SIMU=VERILATOR firmware sim
|
||||
make CONFIG=configs/ci/ihp130-shell.mk SIMU=VERILATOR firmware sim
|
||||
```
|
||||
|
||||
For a firmware-only build, omit `SIMU=VERILATOR sim`.
|
||||
@@ -54,7 +54,7 @@ For a firmware-only build, omit `SIMU=VERILATOR sim`.
|
||||
3. Add `<name>` to the allowed `APP` values in the top-level `Makefile`; this
|
||||
keeps profile selection explicit and rejects misspellings.
|
||||
4. Add a committed configuration profile under `configs/` that selects
|
||||
`APP := <name>` and the required core, ISA, CSR, and linker options.
|
||||
`APP := <name>` and the required ISA, CSR, and linker options.
|
||||
5. Add host tests for deterministic parsing or utility code, and add a
|
||||
regression command when the application must execute in simulation.
|
||||
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
# User IP is optional and is linked only for MDD configurations.
|
||||
ifeq ($(IP), MDD)
|
||||
APP_SRCS += $(ROOT_PATH)/app/network/userip/userip.c
|
||||
APP_INC_DIRS += $(ROOT_PATH)/app/network/userip
|
||||
endif
|
||||
APP_INC_DIRS += $(ROOT_PATH)/app/network/userip
|
||||
@@ -45,6 +45,21 @@
|
||||
"recursive": true,
|
||||
"license": "NOASSERTION"
|
||||
},
|
||||
"pdk_gf180": {
|
||||
"url": "https://github.com/google/gf180mcu-pdk.git",
|
||||
"revision": "de3240d7529a6970437ac3344820aaae7839f215",
|
||||
"destination": "pdk/gf180mcu-pdk",
|
||||
"recursive": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"pdk_sky130": {
|
||||
"url": "https://github.com/google/skywater-pdk.git",
|
||||
"revision": "7198cf647113f56041e02abf3eb623692820c5e1",
|
||||
"destination": "pdk/skywater-pdk",
|
||||
"recursive": true,
|
||||
"submodules": ["libraries/sky130_fd_sc_hd/latest"],
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"cluster_archinfo": {"url": "https://github.com/retroSoC/archinfo.git", "revision": "d65dd13c83a1176668bf5178004814a1551bf6ba", "destination": "rtl/managed/clusterip/archinfo", "license": "NOASSERTION"},
|
||||
"cluster_clint": {"url": "https://github.com/retroSoC/clint.git", "revision": "ce49f1a5501c0a9f18c0e3f358417648f3f2fb18", "destination": "rtl/managed/clusterip/clint", "license": "NOASSERTION"},
|
||||
"cluster_common": {"url": "https://github.com/retroSoC/common.git", "revision": "d9921eb05160be873aebc46bb2f820bee2c7eebc", "destination": "rtl/managed/clusterip/common", "license": "NOASSERTION"},
|
||||
@@ -63,7 +78,7 @@
|
||||
},
|
||||
"archives": {
|
||||
"fatfs": {
|
||||
"url": "https://elm-chan.org/fsw/ff/arc/ff16.zip",
|
||||
"url": "https://github.com/retroSoC/artifact/releases/download/fatfs-r0.16/fatfs-r0.16.zip",
|
||||
"sha256": "99f7dc1f7e095356e4a9e3dbe29959090d8b948afe2bbc5441e52fdf4b85449e",
|
||||
"destination": "app/fatfs/ff16/ff16.zip",
|
||||
"license": "LicenseRef-FatFs"
|
||||
@@ -112,6 +127,20 @@
|
||||
"archive": "yosys-v0.58-ubuntu-22.04-gcc.tar.gz",
|
||||
"path": "bin"
|
||||
},
|
||||
"sby": {
|
||||
"version": "0.67",
|
||||
"url": "https://github.com/retroSoC/artifact/releases/download/sby-v0.67/sby-v0.67-ubuntu-22.04.tar.gz",
|
||||
"sha256": "f97053fa43d01e9e343fd459e9a42424d31277204f2b0f19f00bbead657a7e3a",
|
||||
"archive": "sby-v0.67-ubuntu-22.04.tar.gz",
|
||||
"path": "bin"
|
||||
},
|
||||
"bitwuzla": {
|
||||
"version": "0.9.1",
|
||||
"url": "https://github.com/retroSoC/artifact/releases/download/bitwuzla-0.9.1/bitwuzla-0.9.1-ubuntu-22.04-gcc.tar.gz",
|
||||
"sha256": "1f4a4c3f173ebb398628a033a867e0370694151223f2761eaaf9cba9e83ff996",
|
||||
"archive": "bitwuzla-0.9.1-ubuntu-22.04-gcc.tar.gz",
|
||||
"path": "bin"
|
||||
},
|
||||
"opensta": {
|
||||
"version": "2.2.0-1767-ga93b51ec",
|
||||
"url": "https://github.com/retroSoC/artifact/releases/download/opensta-v2.2.0-1767-ga93b51ec/opensta-v2.2.0-1767-ga93b51ec-ubuntu-22.04-gcc.tar.gz",
|
||||
|
||||
+5
-4
@@ -1,11 +1,12 @@
|
||||
# Reproducible Build Profiles
|
||||
|
||||
This directory contains committed Make configuration profiles. Each profile
|
||||
selects SoC, CPU/IP, PDK, ISA, CSR support, application, linker layout, and
|
||||
the supported validation tier.
|
||||
selects SoC, PDK, ISA, CSR support, application, linker layout, and the
|
||||
supported validation tier. The user-extension fabric is fixed; the management
|
||||
core defaults to `HAZARD3` and may be set to `PICORV32` with `CORE`.
|
||||
|
||||
`ci/` contains pull-request profiles, `nightly/` adds extended open-source
|
||||
coverage, and `cluster/` contains configurations requiring site tools or PDKs.
|
||||
`ci/` contains pull-request profiles and `cluster/` contains configurations
|
||||
requiring site tools or PDKs. The nightly workflow reuses the IHP130 CI profile.
|
||||
Start builds from a committed profile rather than setting an unreviewed mix of
|
||||
variables on the command line.
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := MDD
|
||||
PDK := IHP130
|
||||
PDK := GF180
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
@@ -1,12 +0,0 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := NONE
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
HAVE_SVA := NO
|
||||
ISA := RV32IM
|
||||
HAVE_CSR := YES
|
||||
APP := shell
|
||||
LINK_TYPE := ld2_psram
|
||||
@@ -1,6 +1,4 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := MDD
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
@@ -1,6 +1,4 @@
|
||||
SOC := MINI
|
||||
CORE := MDD
|
||||
IP := NONE
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
@@ -1,7 +1,5 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := NONE
|
||||
PDK := IHP130
|
||||
PDK := SKY130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
@@ -1,6 +1,4 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := NONE
|
||||
PDK := ICS55
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
@@ -1,12 +0,0 @@
|
||||
SOC := MINI
|
||||
CORE := PICORV32
|
||||
IP := NONE
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
HAVE_SVA := NO
|
||||
ISA := RV32IM
|
||||
HAVE_CSR := NO
|
||||
APP := bringup
|
||||
LINK_TYPE := ld2_psram
|
||||
+2
-2
@@ -42,8 +42,8 @@ selected.
|
||||
Start from a committed configuration profile:
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk setup
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk firmware
|
||||
make CONFIG=configs/ci/ihp130.mk setup
|
||||
make CONFIG=configs/ci/ihp130.mk firmware
|
||||
```
|
||||
|
||||
The resulting firmware and generated headers are isolated under
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <retrosoc/generated/memory_map.h>
|
||||
#include <retrosoc/generated/user_extensions.h>
|
||||
|
||||
#ifdef CORE_PICORV32
|
||||
#define HW_CORE "PicoRV32"
|
||||
#elif CORE_HAZARD3
|
||||
#define HW_CORE "HAZARD3"
|
||||
#if defined(RS_SOC_MGMT_CORE_HAZARD3)
|
||||
#define HW_CORE "Management Hazard3"
|
||||
#elif defined(RS_SOC_MGMT_CORE_PICORV32)
|
||||
#define HW_CORE "Management PicoRV32"
|
||||
#else
|
||||
#define HW_CORE "none"
|
||||
#error "Management core selection is missing"
|
||||
#endif
|
||||
|
||||
#define CPU_FREQ 72 // unit: MHz
|
||||
@@ -158,6 +159,9 @@
|
||||
#define reg_sysctrl_bus_fault_addr RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_FAULT_ADDR_OFFSET)
|
||||
#define reg_sysctrl_bus_fault_count RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_FAULT_COUNT_OFFSET)
|
||||
#define reg_sysctrl_pll_status RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_PLL_STATUS_OFFSET)
|
||||
#define reg_sysctrl_user_core_reset RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_USER_CORE_RESET_OFFSET)
|
||||
#define reg_sysctrl_user_core_status RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_USER_CORE_STATUS_OFFSET)
|
||||
#define reg_sysctrl_bus_fault_master RS_SOC_SYSCTRL_REG32(RS_SOC_SYSCTRL_FAULT_MASTER_OFFSET)
|
||||
#define RS_SOC_BUS_FAULT_PENDING UINT32_C(0x00000001)
|
||||
#define RS_SOC_BUS_FAULT_WRITE UINT32_C(0x00000002)
|
||||
#define RS_SOC_BUS_FAULT_REASON_MASK UINT32_C(0x0000000C)
|
||||
@@ -241,13 +245,11 @@
|
||||
#define reg_tim3_cnt RS_SOC_REG32(RS_SOC_APB_TMR_BASE, UINT32_C(0x08))
|
||||
#define reg_tim3_cmp RS_SOC_REG32(RS_SOC_APB_TMR_BASE, UINT32_C(0x0C))
|
||||
#define reg_tim3_stat RS_SOC_REG32(RS_SOC_APB_TMR_BASE, UINT32_C(0x10))
|
||||
#ifdef IP_MDD
|
||||
#define reg_user_ip_reg0 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x00))
|
||||
#define reg_user_ip_reg1 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x04))
|
||||
#define reg_user_ip_reg2 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x08))
|
||||
#define reg_user_ip_reg3 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x0C))
|
||||
#define reg_user_ip_reg4 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x10))
|
||||
#define reg_user_ip_reg5 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x14))
|
||||
#define reg_user_ip_reg6 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x18))
|
||||
#endif
|
||||
#define reg_user_ip_reg0 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x00))
|
||||
#define reg_user_ip_reg1 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x04))
|
||||
#define reg_user_ip_reg2 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x08))
|
||||
#define reg_user_ip_reg3 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x0C))
|
||||
#define reg_user_ip_reg4 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x10))
|
||||
#define reg_user_ip_reg5 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x14))
|
||||
#define reg_user_ip_reg6 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x18))
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef RETROSOC_HAL_USER_CORE_H
|
||||
#define RETROSOC_HAL_USER_CORE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <retrosoc/core/status.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t reset_mask;
|
||||
uint8_t selected_core;
|
||||
bool bus_enabled;
|
||||
bool bus_idle;
|
||||
bool draining;
|
||||
bool config_error;
|
||||
} rs_user_core_status_t;
|
||||
|
||||
rs_status_t rs_user_core_get_status(rs_user_core_status_t *status);
|
||||
rs_status_t rs_user_core_stop(rs_timeout_t timeout);
|
||||
rs_status_t rs_user_core_start(uint8_t core_id, rs_timeout_t timeout);
|
||||
rs_status_t rs_user_core_reset(uint8_t core_id, rs_timeout_t timeout);
|
||||
|
||||
#endif
|
||||
+55
-50
@@ -6,9 +6,6 @@
|
||||
#include <retrosoc/hal/qspi.h>
|
||||
#include <retrosoc/hal/lcd.h>
|
||||
#include <retrosoc/hal/dma.h>
|
||||
#ifdef CSR_ENABLE
|
||||
#include <retrosoc/arch/riscv/system_base.h>
|
||||
#endif
|
||||
// #include "image.h"
|
||||
// #include "video.h"
|
||||
|
||||
@@ -257,57 +254,65 @@ void lcd_fill_video(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend,
|
||||
lcd_wr_data32(data + j, (total_pixels - i) / 2U);
|
||||
}
|
||||
|
||||
static uint64_t rs_lcd_read_cycle_counter(void) {
|
||||
uint32_t high_before;
|
||||
uint32_t low;
|
||||
uint32_t high_after;
|
||||
|
||||
do {
|
||||
__asm__ volatile("rdcycleh %0" : "=r"(high_before));
|
||||
__asm__ volatile("rdcycle %0" : "=r"(low));
|
||||
__asm__ volatile("rdcycleh %0" : "=r"(high_after));
|
||||
} while (high_before != high_after);
|
||||
|
||||
return ((uint64_t)high_after << 32U) | (uint64_t)low;
|
||||
}
|
||||
|
||||
static uint64_t rs_lcd_read_instret_counter(void) {
|
||||
uint32_t high_before;
|
||||
uint32_t low;
|
||||
uint32_t high_after;
|
||||
|
||||
do {
|
||||
__asm__ volatile("rdinstreth %0" : "=r"(high_before));
|
||||
__asm__ volatile("rdinstret %0" : "=r"(low));
|
||||
__asm__ volatile("rdinstreth %0" : "=r"(high_after));
|
||||
} while (high_before != high_after);
|
||||
|
||||
return ((uint64_t)high_after << 32U) | (uint64_t)low;
|
||||
}
|
||||
|
||||
static void lcd_frame(uint32_t first, uint32_t pref_cnt) {
|
||||
#ifdef CORE_PICORV32
|
||||
static uint32_t cycle_start, cycle_end;
|
||||
static uint32_t cycleh_start, cycleh_end;
|
||||
static uint32_t inst_start, inst_end;
|
||||
static uint32_t insth_start, insth_end;
|
||||
if (first) {
|
||||
__asm__ volatile("rdcycle %0" : "=r"(cycle_start));
|
||||
__asm__ volatile("rdcycleh %0" : "=r"(cycleh_start));
|
||||
__asm__ volatile("rdinstret %0" : "=r"(inst_start));
|
||||
__asm__ volatile("rdinstreth %0" : "=r"(insth_start));
|
||||
} else {
|
||||
__asm__ volatile("rdcycle %0" : "=r"(cycle_end));
|
||||
__asm__ volatile("rdcycleh %0" : "=r"(cycleh_end));
|
||||
__asm__ volatile("rdinstret %0" : "=r"(inst_end));
|
||||
__asm__ volatile("rdinstreth %0" : "=r"(insth_end));
|
||||
static uint64_t cycle_start;
|
||||
static uint64_t instret_start;
|
||||
static bool measurement_active;
|
||||
|
||||
printf("cycles num: %d(high: %d)\n", cycle_end - cycle_start, cycleh_end - cycleh_start);
|
||||
printf("insts num: %d(high: %d)\n", inst_end - inst_start, insth_end - insth_start);
|
||||
if (cycle_end > cycle_start) {
|
||||
const uint32_t elapsed_us = (cycle_end - cycle_start) / CPU_FREQ;
|
||||
if (elapsed_us != 0U) {
|
||||
printf("flush rate: %dfps\n", (pref_cnt * 1000000U) / elapsed_us);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif CORE_HAZARD3
|
||||
static uint64_t cycle_start, cycle_end;
|
||||
static uint64_t inst_start, inst_end;
|
||||
if (first) {
|
||||
// cycle_start = __get_rv_cycle();
|
||||
// inst_start = __get_rv_instret();
|
||||
} else {
|
||||
// cycle_end = __get_rv_cycle();
|
||||
// inst_end = __get_rv_instret();
|
||||
if (cycle_end > cycle_start) {
|
||||
const uint64_t elapsed_us = (cycle_end - cycle_start) / CPU_FREQ;
|
||||
printf("cycles num: %lld\n", cycle_end - cycle_start);
|
||||
printf("insts num: %lld\n", inst_end - inst_start);
|
||||
if (elapsed_us != 0U) {
|
||||
printf("flush rate: %lldfps\n", ((uint64_t)pref_cnt * 1000000U) / elapsed_us);
|
||||
}
|
||||
} else {
|
||||
printf("frame timing is unavailable on this core\n");
|
||||
}
|
||||
if (first != 0U) {
|
||||
cycle_start = rs_lcd_read_cycle_counter();
|
||||
instret_start = rs_lcd_read_instret_counter();
|
||||
measurement_active = true;
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
(void)first;
|
||||
(void)pref_cnt;
|
||||
#endif
|
||||
if (!measurement_active) {
|
||||
printf("lcd frame timing is unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t cycle_count = rs_lcd_read_cycle_counter() - cycle_start;
|
||||
const uint64_t instret_count = rs_lcd_read_instret_counter() - instret_start;
|
||||
const uint64_t elapsed_us = cycle_count / (uint64_t)CPU_FREQ;
|
||||
|
||||
measurement_active = false;
|
||||
printf("cycles num: %llu\n", (unsigned long long)cycle_count);
|
||||
printf("insts num: %llu\n", (unsigned long long)instret_count);
|
||||
if (elapsed_us == 0U) {
|
||||
printf("lcd frame timing is unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("flush rate: %llufps\n",
|
||||
(unsigned long long)(((uint64_t)pref_cnt * UINT64_C(1000000)) / elapsed_us));
|
||||
}
|
||||
|
||||
void ip_lcd_test(int argc, char **argv) {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <retrosoc/core/soc.h>
|
||||
#include <retrosoc/hal/user_core.h>
|
||||
|
||||
#define RS_USER_CORE_STATUS_SEL_MASK UINT32_C(0x0000001F)
|
||||
#define RS_USER_CORE_STATUS_BUS_ENABLED UINT32_C(0x00000100)
|
||||
#define RS_USER_CORE_STATUS_BUS_IDLE UINT32_C(0x00000200)
|
||||
#define RS_USER_CORE_STATUS_DRAINING UINT32_C(0x00000400)
|
||||
#define RS_USER_CORE_STATUS_CONFIG_ERROR UINT32_C(0x00000800)
|
||||
|
||||
static uint32_t rs_user_core_reset_mask_all(void) {
|
||||
return UINT32_MAX >> (UINT32_C(32) - RS_SOC_USER_CORE_COUNT);
|
||||
}
|
||||
|
||||
rs_status_t rs_user_core_get_status(rs_user_core_status_t *status) {
|
||||
uint32_t value;
|
||||
|
||||
if (status == NULL) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
value = reg_sysctrl_user_core_status;
|
||||
status->reset_mask = reg_sysctrl_user_core_reset;
|
||||
status->selected_core = (uint8_t)(value & RS_USER_CORE_STATUS_SEL_MASK);
|
||||
status->bus_enabled = (value & RS_USER_CORE_STATUS_BUS_ENABLED) != UINT32_C(0);
|
||||
status->bus_idle = (value & RS_USER_CORE_STATUS_BUS_IDLE) != UINT32_C(0);
|
||||
status->draining = (value & RS_USER_CORE_STATUS_DRAINING) != UINT32_C(0);
|
||||
status->config_error = (value & RS_USER_CORE_STATUS_CONFIG_ERROR) != UINT32_C(0);
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_user_core_stop(rs_timeout_t timeout) {
|
||||
rs_user_core_status_t status;
|
||||
|
||||
reg_sysctrl_user_core_reset = rs_user_core_reset_mask_all();
|
||||
while (timeout-- != 0U) {
|
||||
if (rs_user_core_get_status(&status) != RS_OK) {
|
||||
return RS_EIO;
|
||||
}
|
||||
if (status.config_error) {
|
||||
return RS_EIO;
|
||||
}
|
||||
if (!status.bus_enabled && status.bus_idle && !status.draining) {
|
||||
return RS_OK;
|
||||
}
|
||||
}
|
||||
return RS_ETIMEOUT;
|
||||
}
|
||||
|
||||
rs_status_t rs_user_core_start(uint8_t core_id, rs_timeout_t timeout) {
|
||||
rs_user_core_status_t status;
|
||||
rs_status_t result;
|
||||
uint32_t reset_mask;
|
||||
|
||||
if ((uint32_t)core_id >= RS_SOC_USER_CORE_COUNT) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
result = rs_user_core_stop(timeout);
|
||||
if (result != RS_OK) {
|
||||
return result;
|
||||
}
|
||||
reg_sysctrl_user_core_status = RS_USER_CORE_STATUS_CONFIG_ERROR;
|
||||
reg_sysctrl_coresel = (uint32_t)core_id;
|
||||
if (rs_user_core_get_status(&status) != RS_OK) {
|
||||
return RS_EIO;
|
||||
}
|
||||
if (status.config_error || status.selected_core != core_id) {
|
||||
return RS_EIO;
|
||||
}
|
||||
reset_mask = rs_user_core_reset_mask_all() & ~(UINT32_C(1) << core_id);
|
||||
reg_sysctrl_user_core_reset = reset_mask;
|
||||
while (timeout-- != 0U) {
|
||||
if (rs_user_core_get_status(&status) != RS_OK) {
|
||||
return RS_EIO;
|
||||
}
|
||||
if (status.config_error) {
|
||||
return RS_EIO;
|
||||
}
|
||||
if (status.bus_enabled && !status.draining && status.selected_core == core_id) {
|
||||
return RS_OK;
|
||||
}
|
||||
}
|
||||
return RS_ETIMEOUT;
|
||||
}
|
||||
|
||||
rs_status_t rs_user_core_reset(uint8_t core_id, rs_timeout_t timeout) {
|
||||
if ((uint32_t)core_id >= RS_SOC_USER_CORE_COUNT) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return rs_user_core_stop(timeout);
|
||||
}
|
||||
@@ -9,9 +9,7 @@
|
||||
#include <retrosoc/lib/string.h>
|
||||
#include <retrosoc/service/shell.h>
|
||||
#include <retrosoc/service/booter.h>
|
||||
#if defined(CORE_MDD) || defined(IP_MDD)
|
||||
#include <user_design_info.h>
|
||||
#endif
|
||||
|
||||
void rs_app_info(void) {
|
||||
printf("#############################################################\n");
|
||||
@@ -51,9 +49,6 @@ void rs_app_info(void) {
|
||||
printf(" version: %s(commit: %s)\n\n", RETROSOC_BRANCH, RETROSOC_COMMIT);
|
||||
|
||||
printf("User Processors:\n");
|
||||
#ifndef CORE_MDD
|
||||
printf(" CORE: %s\n", HW_CORE);
|
||||
#else
|
||||
uint32_t core_size = sizeof(user_core_info) / sizeof(user_core_info[0]);
|
||||
printf(" %-15s %-12s %-12s %s\n", "[name]", "[isa]", "[maintainer]", "[repo]");
|
||||
for (uint32_t i = 0; i < core_size; ++i) {
|
||||
@@ -64,9 +59,7 @@ void rs_app_info(void) {
|
||||
printf("[%d]: %-15s %-12s %-12s %s\n", i, user_core_info[i].name, user_core_info[i].isa,
|
||||
user_core_info[i].maintainer, user_core_info[i].repo);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef IP_MDD
|
||||
printf("\nUser IPs:\n");
|
||||
uint32_t ip_size = sizeof(user_ip_info) / sizeof(user_ip_info[0]);
|
||||
printf(" %-15s %-12s %-12s %s\n", "[name]", "[isa]", "[maintainer]", "[repo]");
|
||||
@@ -78,7 +71,6 @@ void rs_app_info(void) {
|
||||
printf("[%d]: %-15s %-12s %-12s %s\n", i, user_ip_info[i].name, user_ip_info[i].isa,
|
||||
user_ip_info[i].maintainer, user_ip_info[i].repo);
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("\nSoftware:\n");
|
||||
printf(" COMPILER: %s\n", COMPILER_NAME);
|
||||
@@ -128,9 +120,7 @@ void rs_app_info(void) {
|
||||
printf(" 1 x WDG @%p\n", (void *)®_wdg_ctrl);
|
||||
printf(" 1 x CRC @%p\n", (void *)®_crc_ctrl);
|
||||
printf(" 1 x TIMER3(ADV) @%p\n", (void *)®_tim3_ctrl);
|
||||
#ifdef IP_MDD
|
||||
printf(" 1 x USER_IP(4KiB) @%p\n", (void *)®_user_ip_reg0);
|
||||
#endif
|
||||
printf("\n");
|
||||
printf("#############################################################\n");
|
||||
printf("#############################################################\n");
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
#include <core_portme.h>
|
||||
// #include <lvgl.h>
|
||||
// #include <lv_port_disp.h>
|
||||
#ifdef IP_MDD
|
||||
#include <userip.h>
|
||||
#endif
|
||||
|
||||
static char sh_argv_buf[RS_SHELL_MAX_ARGC][RS_SHELL_MAX_COMMAND_LENGTH + 1U];
|
||||
static char *sh_argv[RS_SHELL_MAX_ARGC + 1U];
|
||||
@@ -689,7 +687,6 @@ static void rs_shell_app_coremark_cmd(int argc, char **argv) {
|
||||
core_main();
|
||||
}
|
||||
|
||||
#ifdef IP_MDD
|
||||
static void rs_shell_app_userip_cmd(int argc, char **argv) {
|
||||
if (argc != 1 && argc != 2) {
|
||||
printf("[userip] cmd param error\n");
|
||||
@@ -713,7 +710,6 @@ static void rs_shell_app_userip_cmd(int argc, char **argv) {
|
||||
}
|
||||
// userip: list all info/current id
|
||||
}
|
||||
#endif
|
||||
|
||||
void rs_shell_launch(void) {
|
||||
char type_res[RS_SHELL_MAX_COMMAND_LENGTH + 1U];
|
||||
@@ -728,9 +724,7 @@ void rs_shell_launch(void) {
|
||||
(void)rs_shell_register("help", "help info", false, rs_shell_help);
|
||||
(void)rs_shell_register("history", "print history list", false, rs_shell_history_list);
|
||||
(void)rs_shell_register("coremark", "coremark test", false, rs_shell_app_coremark_cmd);
|
||||
#ifdef IP_MDD
|
||||
(void)rs_shell_register("userip", "run user ip program", false, rs_shell_app_userip_cmd);
|
||||
#endif
|
||||
if (fs_init_state == RS_OK) {
|
||||
(void)rs_shell_register("ls", "list directory contents", false, rs_shell_fat32_ls_cmd);
|
||||
(void)rs_shell_register("lsr", "list directory contents recursively", false,
|
||||
|
||||
+61
-12
@@ -4,10 +4,20 @@
|
||||
|
||||
| Tier | Profile | Automated coverage |
|
||||
| --- | --- | --- |
|
||||
| Pull request | `configs/ci/hazard3-rv32im-ihp130.mk` | firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
|
||||
| Pull request | `configs/ci/mdd-rv32im-ihp130.mk` | firmware and Verilator |
|
||||
| Nightly | `configs/nightly/picorv32-rv32im-ihp130.mk` | firmware, Verilator, and Icarus |
|
||||
| Cluster | `configs/cluster/hazard3-ics55.mk` | configuration only; run with site PDK/tool access |
|
||||
| Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test |
|
||||
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
|
||||
| Pull request | `configs/ci/gf180.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
|
||||
| Pull request | `configs/ci/sky130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
|
||||
| Nightly | `configs/ci/ihp130.mk` | repeated full IHP130 regression |
|
||||
| Cluster | `configs/cluster/ics55.mk` | configuration only; run with site PDK/tool access |
|
||||
|
||||
OpenSTA runs a reproducible core-STA baseline for every CI PDK: IHP130 uses
|
||||
`slow_1p08V_125C`, GF180 uses `ss_125C_4v50`, and SKY130 uses `ss_100C_1v40`.
|
||||
The SDC is generated from `rtl/mini/integration/clock_reset_domains.json`, applies
|
||||
the external, audio, and DVP clocks at their technology-buffer observation pins,
|
||||
and groups unrelated clocks asynchronously. It intentionally excludes package,
|
||||
board, pad-ring, extraction, and PLL timing, so it is a regression-quality core
|
||||
analysis rather than physical signoff.
|
||||
|
||||
VCS and ICS55 flows remain cluster-only because public runners do not have the licensed simulator or
|
||||
the validated site environment. They use the same variant layout, manifests, result JSON, warning
|
||||
@@ -73,6 +83,7 @@ build/<variant>/
|
||||
generated/pin_map/
|
||||
sw/
|
||||
sim/<simulator>/
|
||||
formal/<proof>/
|
||||
syn/yosys/
|
||||
sta/opensta/
|
||||
meta/manifest.json
|
||||
@@ -92,21 +103,58 @@ only when the command succeeds, its log contains the firmware startup marker, an
|
||||
marker is present. Icarus regressions use the assembly self-test as `retrosoc_asm`, while the normal
|
||||
`retrosoc_fw` image remains available for firmware size tracking and Verilator regressions.
|
||||
|
||||
## Formal Protocol Proofs
|
||||
|
||||
`make CONFIG=configs/ci/ihp130.mk formal` proves selected
|
||||
protocol invariants with SymbiYosys, Yosys, `sv2v`, and Bitwuzla. The current
|
||||
targets are `bus`, `nmi2apb`, `sysctrl`, `pll_rcu`, and `gpio_user`; each uses
|
||||
the SBY `prove` task for bounded model checking and k-induction, plus a
|
||||
`cover` task, at depth 20. `sysctrl` checks register side effects, PLL request
|
||||
handling, and fault reporting. `pll_rcu` checks the clock-switch controller
|
||||
state machine. `gpio_user` checks fixed user-GPIO ownership, lock, handoff,
|
||||
and mux safety.
|
||||
`FORMAL_DEPTH` and `FORMAL_TIMEOUT` set the proof bound and per-task
|
||||
wall-clock limit. Use `make CONFIG=<profile> formal-doctor` before a local
|
||||
proof to verify that the required tools are available.
|
||||
|
||||
The locked Bitwuzla 0.9.1 CLI uses `--lang smt2`, while the selected Yosys
|
||||
version invokes legacy Bitwuzla options. `scripts/bitwuzla_smt2.py` is the
|
||||
single compatibility adapter used by SBY; it translates only solver startup
|
||||
arguments and does not modify the generated SMT2 model or proof properties.
|
||||
|
||||
Proof environments instantiate the production protocol RTL and the shared
|
||||
ClusterIP interfaces, registers, and utility cells. The generated conversion,
|
||||
SBY configuration, SMT2 model, logs, traces, task status, and structured
|
||||
verdict are stored below
|
||||
`build/<variant>/formal/<proof>/` and `build/<variant>/meta/formal.json`.
|
||||
The IHP130 smoke workflow enables this target; the full PDK regressions do not
|
||||
repeat PDK-independent protocol proofs. The reusable regression workflow runs
|
||||
the target only when its `formal_checks` input is enabled and its locked
|
||||
toolset provides SBY and Bitwuzla.
|
||||
|
||||
The `pll_rcu` proof uses one formal clock for its system and external-clock
|
||||
inputs to verify the controller protocol independently of analogue clock
|
||||
behavior. It is not asynchronous CDC/RDC signoff; implementation timing,
|
||||
clock-tree checks, and the technology PLL model remain separate signoff work.
|
||||
|
||||
`make check-warnings` normalizes tool messages and compares them with
|
||||
`quality/warnings/<profile>/<tool>.json`. New signatures and increased counts fail; resolved warnings
|
||||
are reported but do not fail. To refresh one baseline after review:
|
||||
`quality/warnings/<profile>/<tool>.json`. New signatures and increased counts make the standalone
|
||||
command fail; resolved warnings are reported but do not fail. Regression suites run warning and
|
||||
metric checks as non-blocking observations so their JSON reports are generated and uploaded even
|
||||
when a baseline delta is present. Compilation, simulation, synthesis, and STA failures remain
|
||||
blocking. To refresh one baseline after review:
|
||||
|
||||
```sh
|
||||
python3 scripts/analyze_warnings.py baseline \
|
||||
--root . --profile hazard3-rv32im-ihp130 --tool verilator \
|
||||
--root . --profile ihp130 --tool verilator \
|
||||
--log build/<variant>/sim/verilator/verilating.log \
|
||||
--output quality/warnings/hazard3-rv32im-ihp130/verilator.json
|
||||
--output quality/warnings/ihp130/verilator.json
|
||||
```
|
||||
|
||||
## Metrics Promotion
|
||||
|
||||
`make check-metrics` currently uses `quality/metrics/policy.json` in `observe` mode. Regression
|
||||
artifacts retain the metrics from each successful run. After ten successful `main` runs for the same
|
||||
artifacts retain the metrics from every completed regression run. After ten successful `main` runs for the same
|
||||
profile and locked tools, create the median baseline:
|
||||
|
||||
```sh
|
||||
@@ -126,9 +174,10 @@ for diagnosis but is initially non-blocking.
|
||||
## CI And Releases
|
||||
|
||||
`quality.yml` validates C, Makefile, and self-owned RTL formatting as well as Python, YAML, GitHub
|
||||
Actions, the dependency lock, and script tests. `regression.yml` runs verified configurations on pull
|
||||
requests and main branches without repeating the format checks. `nightly.yml` adds PICORV32. Source
|
||||
dependencies, locked tool archives, and Verilator `ccache` use separate cache keys.
|
||||
Actions, the dependency lock, and script tests. `regression-smoke.yml` provides fast IHP130 feedback;
|
||||
the three full PDK regression workflows remain required PR coverage and do not repeat the format checks.
|
||||
`nightly.yml` repeats the fixed IHP130 architecture. Source dependencies, locked tool archives, and Verilator `ccache` use
|
||||
separate cache keys.
|
||||
|
||||
Tags matching `v*` run `release.yml`. The release contains a flattened SystemVerilog export, a source
|
||||
tarball, build manifest, dependency lock, CycloneDX SBOM, and `SHA256SUMS`. `make package` creates the
|
||||
|
||||
@@ -55,7 +55,7 @@ Run the smallest relevant validation set in addition to a MISRA review:
|
||||
make sw-format-check
|
||||
make sw-policy-check
|
||||
make sw-host-test
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk firmware
|
||||
make CONFIG=configs/ci/ihp130.mk firmware
|
||||
```
|
||||
|
||||
Use the affected profile and simulator for hardware-facing changes. See
|
||||
|
||||
@@ -13,8 +13,8 @@ named-port binding. Do not edit generated files.
|
||||
Use the following commands after editing the map:
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk check-pin-map
|
||||
make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk pin-map
|
||||
make CONFIG=configs/ci/ihp130.mk check-pin-map
|
||||
make CONFIG=configs/ci/ihp130.mk pin-map
|
||||
```
|
||||
|
||||
When adding a pad, define its whitelisted pad template, direction, conditional
|
||||
@@ -28,8 +28,8 @@ only exposes the pad-side subset of `gpio_if`; neither module contains state.
|
||||
|
||||
## User IP GPIO ownership
|
||||
|
||||
The IP=MDD configuration does not add dedicated user GPIO pads. A user IP can
|
||||
instead drive any of the 32 native GPIO pads through `user_gpio_if`. Software
|
||||
The fixed user-IP integration does not add dedicated user GPIO pads. A user IP
|
||||
can instead drive any of the 32 native GPIO pads through `user_gpio_if`. Software
|
||||
selects an owner per pin with the native GPIO `USER_SEL` register at offset
|
||||
`0x30`; clear selects the existing software/alternate GPIO path and set selects
|
||||
the user IP. `USER_LOCK` at `0x34` is write-one-set and prevents a selected
|
||||
|
||||
@@ -9,3 +9,35 @@ Use the dependency lock and PDK setup targets to acquire or update managed
|
||||
inputs. Do not reformat, patch, or delete vendor content as part of unrelated
|
||||
work. PDK changes require the affected synthesis, timing, physical-design, or
|
||||
FPGA validation flow and the ownership review defined by `.github/CODEOWNERS`.
|
||||
|
||||
The supported CI PDKs are IHP130, GF180, and SKY130. GF180 assembles its
|
||||
locked `gf180mcu_fd_sc_mcu7t5v0` Liberty fragments and the two SoC-used IO
|
||||
cells (`in_c` and `bi_t`) at both its TT and core-STA slow corners. SKY130
|
||||
generates its `sky130_fd_sc_hd` Liberty model from locked upstream JSON files
|
||||
at TT and slow corners. Derived Liberty files are stored below
|
||||
`.cache/retrosoc/pdk/` and are not committed. SKY130 setup initializes only
|
||||
the locked HD library submodule; the remaining upstream libraries are outside
|
||||
the SoC integration boundary.
|
||||
|
||||
All three PDK profiles run slow-corner OpenSTA core timing in CI. The analysis
|
||||
uses the PDK standard-cell Liberty plus the linked IO-cell model required to
|
||||
read the synthesized top-level wrapper. It excludes board, pad-ring,
|
||||
extraction, and PLL timing and is therefore not a physical signoff result.
|
||||
|
||||
The GF180 and SKY130 CI flows synth the digital core and run functional
|
||||
standard-cell netlist simulation. Their generic SoC wrappers explicitly use
|
||||
the locked PDK GPIO and clock cells for RTL and netlist simulation: GF180 uses
|
||||
`gf180mcu_fd_io__in_c`/`gf180mcu_fd_io__bi_t` and MCU7T5V0 clock cells, while
|
||||
SKY130 uses `sky130_fd_io__top_gpiov2` and HD clock cells. This integration
|
||||
does not add supply, corner, filler, placement, or physical verification
|
||||
collateral; physical pad-ring integration remains a technology-specific
|
||||
signoff task. GF180/SKY130 PLL configurations are rejected because no
|
||||
qualified crystal-pad implementation is selected. SKY130 maps the generic
|
||||
Schmitt-control input to its threshold-select control and therefore does not
|
||||
provide hysteresis. Verilator uses a pin-compatible local functional model
|
||||
for the GF180 IO cells because the upstream model contains unsupported `rnmos`
|
||||
primitives. SKY130 uses the same approach because its upstream IO model uses
|
||||
unsupported switch primitives and drive strengths. Synthesis still preserves
|
||||
the original PDK cell instances. GF180 netlist simulation also substitutes
|
||||
pin-compatible sequential-cell models because the upstream functional models
|
||||
propagate an unconnected synthesized notifier as an unknown value.
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
|
||||
DEFAULT_CORNER = "tt_025C_5v00"
|
||||
DEFAULT_LIBRARY = "gf180mcu_fd_sc_mcu7t5v0"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Assemble a GF180 Liberty model from locked PDK fragments"
|
||||
)
|
||||
parser.add_argument("--source", type=Path, required=True)
|
||||
parser.add_argument("--output-dir", type=Path, required=True)
|
||||
parser.add_argument("--revision", required=True)
|
||||
parser.add_argument("--library", default=DEFAULT_LIBRARY)
|
||||
parser.add_argument("--cell", action="append")
|
||||
parser.add_argument("--output-name")
|
||||
parser.add_argument("--corner", default=DEFAULT_CORNER)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
source = args.source.resolve()
|
||||
library_dir = source / "libraries" / args.library / "latest"
|
||||
header = library_dir / "liberty" / f"{args.library}__{args.corner}.lib"
|
||||
cells_dir = library_dir / "cells"
|
||||
if args.cell:
|
||||
cell_libraries = [
|
||||
cells_dir / cell / f"{args.library}__{cell}__{args.corner}.lib"
|
||||
for cell in sorted(set(args.cell))
|
||||
]
|
||||
else:
|
||||
cell_libraries = sorted(cells_dir.glob(f"*/*__{args.corner}.lib"))
|
||||
if not header.is_file() or not cell_libraries:
|
||||
raise FileNotFoundError(f"missing GF180 Liberty inputs below {library_dir}")
|
||||
|
||||
output_dir = args.output_dir.resolve()
|
||||
output_name = args.output_name or f"{args.library}__{args.corner}.lib"
|
||||
output = output_dir / output_name
|
||||
revision = output.with_suffix(output.suffix + ".revision")
|
||||
revision_contents = (
|
||||
f"{args.revision}\n{args.library}\n{','.join(sorted(set(args.cell or ())))}\n"
|
||||
)
|
||||
if (
|
||||
output.is_file()
|
||||
and revision.is_file()
|
||||
and revision.read_text(encoding="utf-8") == revision_contents
|
||||
):
|
||||
print(f"GF180 Liberty is ready: {output}")
|
||||
return 0
|
||||
|
||||
prefix, separator, _ = header.read_text(encoding="utf-8").rpartition("\n}")
|
||||
if not separator:
|
||||
raise RuntimeError(f"GF180 Liberty header is malformed: {header}")
|
||||
cells = "\n\n".join(path.read_text(encoding="utf-8") for path in cell_libraries)
|
||||
atomic_write(output, f"{prefix}\n\n{cells}\n}}\n")
|
||||
atomic_write(revision, revision_contents)
|
||||
print(f"generated GF180 Liberty: {output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
|
||||
DEFAULT_CORNER = "tt_025C_1v80"
|
||||
LIBRARY = "sky130_fd_sc_hd"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate the SKY130 HD Liberty model from locked PDK JSON sources"
|
||||
)
|
||||
parser.add_argument("--source", type=Path, required=True)
|
||||
parser.add_argument("--output-dir", type=Path, required=True)
|
||||
parser.add_argument("--revision", required=True)
|
||||
parser.add_argument("--corner", default=DEFAULT_CORNER)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
source = args.source.resolve()
|
||||
library_dir = source / "libraries" / LIBRARY / "latest"
|
||||
generator_dir = source / "scripts" / "python-skywater-pdk"
|
||||
required = (
|
||||
library_dir / "timing" / f"{LIBRARY}__common.lib.json",
|
||||
library_dir / "timing" / f"{LIBRARY}__{args.corner}.lib.json",
|
||||
generator_dir / "skywater_pdk" / "liberty.py",
|
||||
)
|
||||
missing = [str(path) for path in required if not path.is_file()]
|
||||
if missing:
|
||||
raise FileNotFoundError("missing SKY130 Liberty input(s): " + ", ".join(missing))
|
||||
|
||||
output_dir = args.output_dir.resolve()
|
||||
output = output_dir / f"{LIBRARY}__{args.corner}.lib"
|
||||
revision = output.with_suffix(output.suffix + ".revision")
|
||||
if (
|
||||
output.is_file()
|
||||
and revision.is_file()
|
||||
and revision.read_text(encoding="utf-8") == f"{args.revision}\n"
|
||||
):
|
||||
print(f"SKY130 Liberty is ready: {output}")
|
||||
return 0
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(prefix="sky130-liberty-", dir=output_dir) as temporary:
|
||||
temporary_dir = Path(temporary)
|
||||
command = (
|
||||
sys.executable,
|
||||
"-m",
|
||||
"skywater_pdk.liberty",
|
||||
str(library_dir),
|
||||
args.corner,
|
||||
"--output_directory",
|
||||
str(temporary_dir),
|
||||
)
|
||||
print("+ " + " ".join(command))
|
||||
environment = os.environ.copy()
|
||||
environment["PYTHONPATH"] = str(generator_dir)
|
||||
subprocess.run(command, check=True, env=environment)
|
||||
generated = temporary_dir / output.name
|
||||
if not generated.is_file():
|
||||
raise RuntimeError(f"SKY130 Liberty generator did not create {generated}")
|
||||
atomic_write(output, generated.read_text(encoding="utf-8"))
|
||||
atomic_write(revision, f"{args.revision}\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+93
-4
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -14,19 +15,107 @@ from scripts.setup_helpers import ensure_git_repo # noqa: E402
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Install pinned open PDKs")
|
||||
parser.add_argument("--update", action="store_true")
|
||||
parser.add_argument("--pdk", choices=("IHP130", "ICS55"), action="append")
|
||||
parser.add_argument("--pdk", choices=("IHP130", "ICS55", "GF180", "SKY130"), action="append")
|
||||
args = parser.parse_args()
|
||||
selected = args.pdk or ("IHP130", "ICS55")
|
||||
names = {"IHP130": "pdk_ihp130", "ICS55": "pdk_ics55"}
|
||||
selected = args.pdk or ("IHP130", "ICS55", "GF180", "SKY130")
|
||||
names = {
|
||||
"IHP130": "pdk_ihp130",
|
||||
"ICS55": "pdk_ics55",
|
||||
"GF180": "pdk_gf180",
|
||||
"SKY130": "pdk_sky130",
|
||||
}
|
||||
for name in (names[pdk] for pdk in selected):
|
||||
dependency = source(name)
|
||||
destination = ROOT / dependency["destination"]
|
||||
ensure_git_repo(
|
||||
dependency["url"],
|
||||
ROOT / dependency["destination"],
|
||||
destination,
|
||||
dependency["revision"],
|
||||
recursive=dependency.get("recursive", False),
|
||||
submodules=tuple(dependency.get("submodules", ())),
|
||||
update=args.update,
|
||||
)
|
||||
if name == "pdk_gf180":
|
||||
subprocess.run(
|
||||
(
|
||||
sys.executable,
|
||||
str(ROOT / "pdk/generate_gf180_liberty.py"),
|
||||
"--source",
|
||||
str(destination),
|
||||
"--output-dir",
|
||||
str(ROOT / ".cache/retrosoc/pdk/gf180"),
|
||||
"--revision",
|
||||
dependency["revision"],
|
||||
),
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
(
|
||||
sys.executable,
|
||||
str(ROOT / "pdk/generate_gf180_liberty.py"),
|
||||
"--source",
|
||||
str(destination),
|
||||
"--output-dir",
|
||||
str(ROOT / ".cache/retrosoc/pdk/gf180"),
|
||||
"--revision",
|
||||
dependency["revision"],
|
||||
"--corner",
|
||||
"ss_125C_4v50",
|
||||
),
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
(
|
||||
sys.executable,
|
||||
str(ROOT / "pdk/generate_gf180_liberty.py"),
|
||||
"--source",
|
||||
str(destination),
|
||||
"--output-dir",
|
||||
str(ROOT / ".cache/retrosoc/pdk/gf180"),
|
||||
"--revision",
|
||||
dependency["revision"],
|
||||
"--library",
|
||||
"gf180mcu_fd_io",
|
||||
"--cell",
|
||||
"bi_t",
|
||||
"--cell",
|
||||
"in_c",
|
||||
"--corner",
|
||||
"ss_125C_4v50",
|
||||
"--output-name",
|
||||
"gf180mcu_fd_io_retrosoc__ss_125C_4v50.lib",
|
||||
),
|
||||
check=True,
|
||||
)
|
||||
elif name == "pdk_sky130":
|
||||
subprocess.run(
|
||||
(
|
||||
sys.executable,
|
||||
str(ROOT / "pdk/generate_sky130_liberty.py"),
|
||||
"--source",
|
||||
str(destination),
|
||||
"--output-dir",
|
||||
str(ROOT / ".cache/retrosoc/pdk/sky130"),
|
||||
"--revision",
|
||||
dependency["revision"],
|
||||
),
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
(
|
||||
sys.executable,
|
||||
str(ROOT / "pdk/generate_sky130_liberty.py"),
|
||||
"--source",
|
||||
str(destination),
|
||||
"--output-dir",
|
||||
str(ROOT / ".cache/retrosoc/pdk/sky130"),
|
||||
"--revision",
|
||||
dependency["revision"],
|
||||
"--corner",
|
||||
"ss_100C_1v40",
|
||||
),
|
||||
check=True,
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -3,5 +3,9 @@
|
||||
Baselines are scoped by committed profile and tool. Each entry stores a normalized warning signature,
|
||||
its accepted count, and one example. Path roots and source line numbers are removed before hashing.
|
||||
|
||||
`rtl-lint.json` is independent from `verilator.json`: it contains warnings from the strict
|
||||
`rtl-lint` elaboration, while `verilator.json` contains warnings emitted by emulator generation
|
||||
and simulation.
|
||||
|
||||
Do not hand-edit signatures. Regenerate only from a successful flow using
|
||||
`scripts/analyze_warnings.py baseline`, review the full diff, and run `make check-warnings`.
|
||||
+2
-7
@@ -1,17 +1,12 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"count": 2,
|
||||
"example": "warning:Found both default and explicit timescale based delays. Use",
|
||||
"signature": "aee22756a35889cc"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "sorry:ifnone with an edge-sensitive path is not supported.",
|
||||
"signature": "c4297f454f443fed"
|
||||
}
|
||||
],
|
||||
"profile": "picorv32-rv32im-ihp130",
|
||||
"profile": "gf180",
|
||||
"schema_version": 1,
|
||||
"tool": "iverilog"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "010491867f065cec"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "411d9ab46fef9d7d"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "813090adc9f1e8a8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/bus.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "9f53ff454a8648a4"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "c963ef155f9a032c"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "ef9f314dab2ac31d"
|
||||
}
|
||||
],
|
||||
"profile": "gf180",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\debug_mode is used but has no driver.",
|
||||
"signature": "1097d6b92f19e707"
|
||||
},
|
||||
{
|
||||
"count": 9,
|
||||
"example": "warning:Detected 6 multi-output cells (for example, \"gf180mcu_fd_sc_mcu7t5v0__addf_1\").",
|
||||
"signature": "44b9341f5ba5de06"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_irq_reg_arrays.i'",
|
||||
"signature": "56c5f69398be8d7b"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\pending_dbg_resume_prev is used but has no driver.",
|
||||
"signature": "5bbc6025e51705ec"
|
||||
},
|
||||
{
|
||||
"count": 9,
|
||||
"example": "warning:The network is combinational (run \"fraig\" or \"fraig_sweep\").",
|
||||
"signature": "62cc5ee554312205"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.prev'",
|
||||
"signature": "a30df20b98d49196"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.nonconst'",
|
||||
"signature": "d35463a1de40c7b2"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\dbg_req_resume_prev is used but has no driver.",
|
||||
"signature": "ef03dcca25b39dcb"
|
||||
}
|
||||
],
|
||||
"profile": "gf180",
|
||||
"schema_version": 1,
|
||||
"tool": "yosys"
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "010491867f065cec"
|
||||
},
|
||||
{
|
||||
"count": 4,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator EQ expects 32 bits on the LHS, but LHS's SEL generates 8 bits.",
|
||||
"signature": "3bba21d5150bdc2e"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "411d9ab46fef9d7d"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/ip_nmi_wrapper.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "7350cffccb478d64"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "7a759d6e07651fd8"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "813090adc9f1e8a8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h0' generates 8 bits.",
|
||||
"signature": "8ad9b6921bd232e7"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/bus.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "9f53ff454a8648a4"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h4' generates 8 bits.",
|
||||
"signature": "be2cb5333a881eca"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case expression, but Case expression's SEL generates 8 bits.",
|
||||
"signature": "c43a29f4c975e9c1"
|
||||
},
|
||||
{
|
||||
"count": 68,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "c54331448f63ac73"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "c963ef155f9a032c"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "ef9f314dab2ac31d"
|
||||
},
|
||||
{
|
||||
"count": 11,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_io/verilog/sg13g2_io.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "faa191e2dbf4a242"
|
||||
}
|
||||
],
|
||||
"profile": "hazard3-rv32im-ihp130-ip-mdd",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
"signature": "c4297f454f443fed"
|
||||
}
|
||||
],
|
||||
"profile": "hazard3-rv32im-ihp130",
|
||||
"profile": "ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "iverilog"
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"entries": [],
|
||||
"profile": "hazard3-rv32im-ihp130",
|
||||
"profile": "ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "opensta"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -81,7 +81,7 @@
|
||||
"signature": "faa191e2dbf4a242"
|
||||
}
|
||||
],
|
||||
"profile": "hazard3-rv32im-ihp130",
|
||||
"profile": "ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"signature": "ef03dcca25b39dcb"
|
||||
}
|
||||
],
|
||||
"profile": "hazard3-rv32im-ihp130",
|
||||
"profile": "ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "yosys"
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "010491867f065cec"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "PINMISSING:$BUILD/generated/mpw/verilator/core/username4/./serv/serv_synth_wrapper_username4.v:<line>: Instance has missing pin: 'o_ren'",
|
||||
"signature": "173218df65cf7bcd"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$BUILD/generated/mpw/verilator/core/username1/./kianV/alu_username1.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_core_wrapper.u_user_core_wrapper.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.datapath_unit_I.alu_I.is_sub'",
|
||||
"signature": "180cdbb151faa02d"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$BUILD/generated/mpw/verilator/core/username1/./kianV/kianv_harris_mc_edition_username1.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_core_wrapper.u_user_core_wrapper.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.alu_valid'",
|
||||
"signature": "25979c35fe4eb73f"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$BUILD/generated/mpw/verilator/core/username6/./darkriscv/darkriscv_username6.v:<line>: Operator COND expects 32 bits on the Conditional True, but Conditional True's LT generates 1 bits.",
|
||||
"signature": "259baf70503bfe0d"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h8' generates 8 bits.",
|
||||
"signature": "2f1913ee897eabb8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$BUILD/generated/mpw/verilator/core/username6/./darkriscv/darkriscv_username6.v:<line>: Operator COND expects 32 bits on the Conditional True, but Conditional True's LTS generates 1 bits.",
|
||||
"signature": "323be25f98c25432"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator EQ expects 32 bits on the LHS, but LHS's SEL generates 8 bits.",
|
||||
"signature": "3bba21d5150bdc2e"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "411d9ab46fef9d7d"
|
||||
},
|
||||
{
|
||||
"count": 14,
|
||||
"example": "WIDTHTRUNC:$BUILD/generated/mpw/verilator/core/username6/./darkriscv/darkriscv_username6.v:<line>: Logical operator COND expects 1 bit on the Conditional Test, but Conditional Test's VARREF 'FLUSH' generates 2 bits.",
|
||||
"signature": "4a2065ce464b898c"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$BUILD/generated/mpw/verilator/core/username1/./kianV/kianv_harris_mc_edition_username1.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_core_wrapper.u_user_core_wrapper.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.ALUSrcA'",
|
||||
"signature": "4edf683bf8ef29d3"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$BUILD/generated/mpw/verilator/core/username1/./kianV/alu_decoder_username1.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_core_wrapper.u_user_core_wrapper.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.control_unit_I.alu_decoder_I.ALUControl'",
|
||||
"signature": "5dc2cc2658c8e2c1"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/ip_nmi_wrapper.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "7350cffccb478d64"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "7a759d6e07651fd8"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "813090adc9f1e8a8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h0' generates 8 bits.",
|
||||
"signature": "8ad9b6921bd232e7"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/bus.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "9f53ff454a8648a4"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h4' generates 8 bits.",
|
||||
"signature": "be2cb5333a881eca"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case expression, but Case expression's SEL generates 8 bits.",
|
||||
"signature": "c43a29f4c975e9c1"
|
||||
},
|
||||
{
|
||||
"count": 68,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "c54331448f63ac73"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "c963ef155f9a032c"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "ef9f314dab2ac31d"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$BUILD/generated/mpw/verilator/core/username1/./kianV/kianv_harris_mc_edition_username1.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_core_wrapper.u_user_core_wrapper.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.ALUSrcB'",
|
||||
"signature": "f1f9cfed209f63b9"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'hc' generates 8 bits.",
|
||||
"signature": "f6f2aa6bb2b3f6c4"
|
||||
},
|
||||
{
|
||||
"count": 11,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_io/verilog/sg13g2_io.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "faa191e2dbf4a242"
|
||||
}
|
||||
],
|
||||
"profile": "mdd-rv32im-ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "11b9d285d4747e4f"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "7a759d6e07651fd8"
|
||||
},
|
||||
{
|
||||
"count": 68,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "c54331448f63ac73"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "cdd5c3ca84a609c6"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "ee29e23bc895ee17"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "f8f8278c6ce4431d"
|
||||
},
|
||||
{
|
||||
"count": 11,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_io/verilog/sg13g2_io.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "faa191e2dbf4a242"
|
||||
}
|
||||
],
|
||||
"profile": "picorv32-rv32im-ihp130",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 2,
|
||||
"example": "warning:Found both default and explicit timescale based delays. Use",
|
||||
"signature": "aee22756a35889cc"
|
||||
}
|
||||
],
|
||||
"profile": "sky130",
|
||||
"schema_version": 1,
|
||||
"tool": "iverilog"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "1171:$ROOT/.cache/retrosoc/pdk/sky130/sky130_fd_sc_hd__ss_100C_1v40.lib line 24, default_fanout_load is 0.0.",
|
||||
"signature": "3c380feb1d0cd00a"
|
||||
}
|
||||
],
|
||||
"profile": "sky130",
|
||||
"schema_version": 1,
|
||||
"tool": "opensta"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "010491867f065cec"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "411d9ab46fef9d7d"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "813090adc9f1e8a8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/bus.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "9f53ff454a8648a4"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "c963ef155f9a032c"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "ef9f314dab2ac31d"
|
||||
}
|
||||
],
|
||||
"profile": "sky130",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 9,
|
||||
"example": "warning:Detected 9 multi-output cells (for example, \"sky130_fd_sc_hd__fa_1\").",
|
||||
"signature": "09d23991154c9af8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\debug_mode is used but has no driver.",
|
||||
"signature": "1097d6b92f19e707"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_irq_reg_arrays.i'",
|
||||
"signature": "56c5f69398be8d7b"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\pending_dbg_resume_prev is used but has no driver.",
|
||||
"signature": "5bbc6025e51705ec"
|
||||
},
|
||||
{
|
||||
"count": 9,
|
||||
"example": "warning:The network is combinational (run \"fraig\" or \"fraig_sweep\").",
|
||||
"signature": "62cc5ee554312205"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.prev'",
|
||||
"signature": "a30df20b98d49196"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.nonconst'",
|
||||
"signature": "d35463a1de40c7b2"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\dbg_req_resume_prev is used but has no driver.",
|
||||
"signature": "ef03dcca25b39dcb"
|
||||
}
|
||||
],
|
||||
"profile": "sky130",
|
||||
"schema_version": 1,
|
||||
"tool": "yosys"
|
||||
}
|
||||
@@ -3,3 +3,10 @@
|
||||
--require-hashes
|
||||
|
||||
tomli==2.4.1 --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe
|
||||
dataclasses-json==0.6.7 --hash=sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a
|
||||
marshmallow==3.26.2 --hash=sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73
|
||||
mypy-extensions==1.1.0 --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505
|
||||
packaging==26.2 --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e
|
||||
typing-extensions==4.16.0 --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8
|
||||
typing-inspect==0.9.0 --hash=sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f
|
||||
click==8.1.8 --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2
|
||||
@@ -1,2 +1,8 @@
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_io/latest/cells/bi_t/gf180mcu_fd_io__bi_t.v
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_io/latest/cells/in_c/gf180mcu_fd_io__in_c.v
|
||||
# GF180 standard-cell models are selected by gen_iverilog_filelist.py.
|
||||
# Technology wrappers instantiate these models directly in RTL simulation.
|
||||
/tech/gf180_io_sim_cells.v
|
||||
/tech/gf180_seq_sim_cells.v
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/clkbuf/gf180mcu_fd_sc_mcu7t5v0__clkbuf_1.functional.v
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/clkinv/gf180mcu_fd_sc_mcu7t5v0__clkinv_1.functional.v
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/mux2/gf180mcu_fd_sc_mcu7t5v0__mux2_1.functional.v
|
||||
/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/xor2/gf180mcu_fd_sc_mcu7t5v0__xor2_1.functional.v
|
||||
@@ -1,2 +1,12 @@
|
||||
/pdk/skywater-sdk/libs.ref/sg13g2_io/verilog/sg13g2_io.v
|
||||
/pdk/skywater-sdk/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v
|
||||
# SKY130 standard-cell models are selected by gen_iverilog_filelist.py.
|
||||
# Technology wrappers instantiate these functional models directly in RTL simulation.
|
||||
+define+FUNCTIONAL
|
||||
+incdir+/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkbuf
|
||||
+incdir+/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkinv
|
||||
+incdir+/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/mux2
|
||||
+incdir+/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/xor2
|
||||
/tech/sky130_io_sim_cells.v
|
||||
/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkbuf/sky130_fd_sc_hd__clkbuf_1.v
|
||||
/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkinv/sky130_fd_sc_hd__clkinv_1.v
|
||||
/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/mux2/sky130_fd_sc_hd__mux2_1.v
|
||||
/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/xor2/sky130_fd_sc_hd__xor2_1.v
|
||||
@@ -15,18 +15,7 @@ module nmi2apb (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
nmi_if.slave nmi,
|
||||
`ifdef IP_MDD
|
||||
apb4_pure_if.master user_ip,
|
||||
`endif
|
||||
apb4_pure_if.master archinfo,
|
||||
apb4_pure_if.master rng,
|
||||
apb4_pure_if.master uart,
|
||||
apb4_pure_if.master pwm,
|
||||
apb4_pure_if.master ps2,
|
||||
apb4_pure_if.master rtc,
|
||||
apb4_pure_if.master wdg,
|
||||
apb4_pure_if.master crc,
|
||||
apb4_pure_if.master tmr
|
||||
`include "soc_apb_ports.svh"
|
||||
// verilog_format: on
|
||||
);
|
||||
|
||||
@@ -35,109 +24,25 @@ module nmi2apb (
|
||||
localparam FSM_ENAB = 2'd2;
|
||||
|
||||
logic [31:0] s_rd_data;
|
||||
logic [31:0] s_rd_data_base;
|
||||
logic s_xfer_valid, s_xfer_ready;
|
||||
logic s_xfer_ready_base;
|
||||
logic s_mem_valid_re;
|
||||
logic [1:0] s_fsm_d, s_fsm_q;
|
||||
|
||||
// Registered slave-select one-hot for response mux
|
||||
`ifdef IP_MDD
|
||||
localparam NSLV = 10;
|
||||
`else
|
||||
localparam NSLV = 9;
|
||||
`endif
|
||||
logic [NSLV-1:0] s_psel_comb, s_psel_d, s_psel_q;
|
||||
`include "soc_apb_declarations.svh"
|
||||
|
||||
|
||||
// Generated APB request routing retains the scalar interface protocol.
|
||||
`include "soc_apb_request_routes.svh"
|
||||
|
||||
// verilog_format: off
|
||||
assign archinfo.paddr = nmi.addr;
|
||||
assign rng.paddr = nmi.addr;
|
||||
assign uart.paddr = nmi.addr;
|
||||
assign pwm.paddr = nmi.addr;
|
||||
assign ps2.paddr = nmi.addr;
|
||||
assign rtc.paddr = nmi.addr;
|
||||
assign wdg.paddr = nmi.addr;
|
||||
assign crc.paddr = nmi.addr;
|
||||
assign tmr.paddr = nmi.addr;
|
||||
|
||||
assign archinfo.pprot = '0;
|
||||
assign rng.pprot = '0;
|
||||
assign uart.pprot = '0;
|
||||
assign pwm.pprot = '0;
|
||||
assign ps2.pprot = '0;
|
||||
assign rtc.pprot = '0;
|
||||
assign wdg.pprot = '0;
|
||||
assign crc.pprot = '0;
|
||||
assign tmr.pprot = '0;
|
||||
|
||||
assign archinfo.psel = s_xfer_valid && `SOC_ADDR_IS_APB_ARCHINFO(nmi.addr);
|
||||
assign rng.psel = s_xfer_valid && `SOC_ADDR_IS_APB_RNG(nmi.addr);
|
||||
assign uart.psel = s_xfer_valid && `SOC_ADDR_IS_APB_UART1(nmi.addr);
|
||||
assign pwm.psel = s_xfer_valid && `SOC_ADDR_IS_APB_PWM(nmi.addr);
|
||||
assign ps2.psel = s_xfer_valid && `SOC_ADDR_IS_APB_PS2(nmi.addr);
|
||||
assign rtc.psel = s_xfer_valid && `SOC_ADDR_IS_APB_RTC(nmi.addr);
|
||||
assign wdg.psel = s_xfer_valid && `SOC_ADDR_IS_APB_WDG(nmi.addr);
|
||||
assign crc.psel = s_xfer_valid && `SOC_ADDR_IS_APB_CRC(nmi.addr);
|
||||
assign tmr.psel = s_xfer_valid && `SOC_ADDR_IS_APB_TMR(nmi.addr);
|
||||
|
||||
assign archinfo.penable = s_fsm_q == FSM_ENAB;
|
||||
assign rng.penable = s_fsm_q == FSM_ENAB;
|
||||
assign uart.penable = s_fsm_q == FSM_ENAB;
|
||||
assign pwm.penable = s_fsm_q == FSM_ENAB;
|
||||
assign ps2.penable = s_fsm_q == FSM_ENAB;
|
||||
assign rtc.penable = s_fsm_q == FSM_ENAB;
|
||||
assign wdg.penable = s_fsm_q == FSM_ENAB;
|
||||
assign crc.penable = s_fsm_q == FSM_ENAB;
|
||||
assign tmr.penable = s_fsm_q == FSM_ENAB;
|
||||
|
||||
assign archinfo.pwrite = |nmi.wstrb;
|
||||
assign rng.pwrite = |nmi.wstrb;
|
||||
assign uart.pwrite = |nmi.wstrb;
|
||||
assign pwm.pwrite = |nmi.wstrb;
|
||||
assign ps2.pwrite = |nmi.wstrb;
|
||||
assign rtc.pwrite = |nmi.wstrb;
|
||||
assign wdg.pwrite = |nmi.wstrb;
|
||||
assign crc.pwrite = |nmi.wstrb;
|
||||
assign tmr.pwrite = |nmi.wstrb;
|
||||
|
||||
assign archinfo.pwdata = nmi.wdata;
|
||||
assign rng.pwdata = nmi.wdata;
|
||||
assign uart.pwdata = nmi.wdata;
|
||||
assign pwm.pwdata = nmi.wdata;
|
||||
assign ps2.pwdata = nmi.wdata;
|
||||
assign rtc.pwdata = nmi.wdata;
|
||||
assign wdg.pwdata = nmi.wdata;
|
||||
assign crc.pwdata = nmi.wdata;
|
||||
assign tmr.pwdata = nmi.wdata;
|
||||
|
||||
assign archinfo.pstrb = nmi.wstrb;
|
||||
assign rng.pstrb = nmi.wstrb;
|
||||
assign uart.pstrb = nmi.wstrb;
|
||||
assign pwm.pstrb = nmi.wstrb;
|
||||
assign ps2.pstrb = nmi.wstrb;
|
||||
assign rtc.pstrb = nmi.wstrb;
|
||||
assign wdg.pstrb = nmi.wstrb;
|
||||
assign crc.pstrb = nmi.wstrb;
|
||||
assign tmr.pstrb = nmi.wstrb;
|
||||
|
||||
`ifdef IP_MDD
|
||||
assign user_ip.paddr = nmi.addr;
|
||||
assign user_ip.pprot = '0;
|
||||
assign user_ip.psel = s_xfer_valid && `SOC_ADDR_IS_APB_USER_IP(nmi.addr);
|
||||
assign user_ip.penable = s_fsm_q == FSM_ENAB;
|
||||
assign user_ip.pwrite = |nmi.wstrb;
|
||||
assign user_ip.pwdata = nmi.wdata;
|
||||
assign user_ip.pstrb = nmi.wstrb;
|
||||
`endif
|
||||
// verilog_format: on
|
||||
|
||||
edge_det_sync_re #(1) u_mem_valid_edge_det_sync_re (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
nmi.valid,
|
||||
s_mem_valid_re
|
||||
);
|
||||
// verilog_format: on
|
||||
|
||||
assign s_xfer_valid = ((s_fsm_q == FSM_IDLE) && s_mem_valid_re) ||
|
||||
(s_fsm_q == FSM_SETP) || (s_fsm_q == FSM_ENAB);
|
||||
@@ -158,26 +63,13 @@ module nmi2apb (
|
||||
s_fsm_q
|
||||
);
|
||||
|
||||
assign nmi.ready = nmi.valid && (s_fsm_q == FSM_ENAB) && s_xfer_ready;
|
||||
assign nmi.rdata = {32{nmi.ready}} & s_rd_data;
|
||||
assign nmi.ready = nmi.valid && (s_fsm_q == FSM_ENAB) && s_xfer_ready;
|
||||
assign nmi.rdata = {32{nmi.ready}} & s_rd_data;
|
||||
|
||||
// Capture slave-select one-hot at FSM_SETP for cleaner response mux
|
||||
// verilog_format: off
|
||||
assign s_psel_comb[0] = `SOC_ADDR_IS_APB_ARCHINFO(nmi.addr);
|
||||
assign s_psel_comb[1] = `SOC_ADDR_IS_APB_RNG(nmi.addr);
|
||||
assign s_psel_comb[2] = `SOC_ADDR_IS_APB_UART1(nmi.addr);
|
||||
assign s_psel_comb[3] = `SOC_ADDR_IS_APB_PWM(nmi.addr);
|
||||
assign s_psel_comb[4] = `SOC_ADDR_IS_APB_PS2(nmi.addr);
|
||||
assign s_psel_comb[5] = `SOC_ADDR_IS_APB_RTC(nmi.addr);
|
||||
assign s_psel_comb[6] = `SOC_ADDR_IS_APB_WDG(nmi.addr);
|
||||
assign s_psel_comb[7] = `SOC_ADDR_IS_APB_CRC(nmi.addr);
|
||||
assign s_psel_comb[8] = `SOC_ADDR_IS_APB_TMR(nmi.addr);
|
||||
`ifdef IP_MDD
|
||||
assign s_psel_comb[9] = `SOC_ADDR_IS_APB_USER_IP(nmi.addr);
|
||||
`endif
|
||||
// Capture slave-select one-hot at FSM_SETP for cleaner response mux.
|
||||
`include "soc_apb_select_routes.svh"
|
||||
|
||||
assign s_psel_d = (s_fsm_q == FSM_IDLE && s_mem_valid_re) ? s_psel_comb : s_psel_q;
|
||||
// verilog_format: on
|
||||
dffr #(NSLV) u_psel_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
@@ -185,33 +77,7 @@ module nmi2apb (
|
||||
s_psel_q
|
||||
);
|
||||
|
||||
// Response mux using registered slave-select (cleaner timing)
|
||||
assign s_rd_data_base = ({32{s_psel_q[0]}} & archinfo.prdata) |
|
||||
({32{s_psel_q[1]}} & rng.prdata) |
|
||||
({32{s_psel_q[2]}} & uart.prdata) |
|
||||
({32{s_psel_q[3]}} & pwm.prdata) |
|
||||
({32{s_psel_q[4]}} & ps2.prdata) |
|
||||
({32{s_psel_q[5]}} & rtc.prdata) |
|
||||
({32{s_psel_q[6]}} & wdg.prdata) |
|
||||
({32{s_psel_q[7]}} & crc.prdata) |
|
||||
({32{s_psel_q[8]}} & tmr.prdata);
|
||||
|
||||
assign s_xfer_ready_base = (s_psel_q[0] & archinfo.pready) |
|
||||
(s_psel_q[1] & rng.pready) |
|
||||
(s_psel_q[2] & uart.pready) |
|
||||
(s_psel_q[3] & pwm.pready) |
|
||||
(s_psel_q[4] & ps2.pready) |
|
||||
(s_psel_q[5] & rtc.pready) |
|
||||
(s_psel_q[6] & wdg.pready) |
|
||||
(s_psel_q[7] & crc.pready) |
|
||||
(s_psel_q[8] & tmr.pready);
|
||||
|
||||
`ifdef IP_MDD
|
||||
assign s_rd_data = s_rd_data_base | ({32{s_psel_q[9]}} & user_ip.prdata);
|
||||
assign s_xfer_ready = s_xfer_ready_base | (s_psel_q[9] & user_ip.pready);
|
||||
`else
|
||||
assign s_rd_data = s_rd_data_base;
|
||||
assign s_xfer_ready = s_xfer_ready_base;
|
||||
`endif
|
||||
// Generated response mux uses the registered one-hot target selection.
|
||||
`include "soc_apb_response_mux.svh"
|
||||
|
||||
endmodule
|
||||
@@ -46,6 +46,7 @@ module nmi_dvp (
|
||||
// dvp clk
|
||||
logic s_dvp_pclk_buf;
|
||||
logic s_dvp_pclk_rst_n_sync;
|
||||
logic s_dvp_recven_pclk_q;
|
||||
// rx fifo
|
||||
logic s_rx_push_valid, s_rx_full, s_rx_empty;
|
||||
logic s_rx_pop_valid, s_rx_pop_ready;
|
||||
@@ -125,6 +126,17 @@ module nmi_dvp (
|
||||
.rst_n_o(s_dvp_pclk_rst_n_sync)
|
||||
);
|
||||
|
||||
// The software enable is stable while a frame is captured, but it originates
|
||||
// in the system domain and must be synchronized before it gates DVP writes.
|
||||
cdc_sync #(
|
||||
.STAGE (2),
|
||||
.DATA_WIDTH(1)
|
||||
) u_dvp_recven_cdc_sync (
|
||||
.clk_i (s_dvp_pclk_buf),
|
||||
.rst_n_i(s_dvp_pclk_rst_n_sync),
|
||||
.dat_i (s_dvp_recven_q),
|
||||
.dat_o (s_dvp_recven_pclk_q)
|
||||
);
|
||||
|
||||
async_fifo #(
|
||||
.DATA_WIDTH (32),
|
||||
@@ -132,7 +144,7 @@ module nmi_dvp (
|
||||
) u_rx_async_fifo (
|
||||
.wr_clk_i (s_dvp_pclk_buf),
|
||||
.wr_rst_n_i(s_dvp_pclk_rst_n_sync),
|
||||
.wr_en_i (s_rx_push_valid & s_dvp_recven_q),
|
||||
.wr_en_i (s_rx_push_valid & s_dvp_recven_pclk_q),
|
||||
.wr_data_i (s_rx_push_data),
|
||||
.wr_full_o (s_rx_full),
|
||||
.rd_clk_i (clk_i),
|
||||
|
||||
@@ -115,14 +115,12 @@ module nmi_gpio (
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_iofcfg_d, s_gpio_iofcfg_q;
|
||||
logic s_gpio_pinmux_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_pinmux_d, s_gpio_pinmux_q;
|
||||
`ifdef IP_MDD
|
||||
logic s_gpio_user_sel_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_sel_d, s_gpio_user_sel_q;
|
||||
logic s_gpio_user_lock_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_lock_d, s_gpio_user_lock_q;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_handoff_d, s_gpio_user_handoff_q;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_status;
|
||||
`endif
|
||||
// irq
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_di_re, s_gpio_di_fe, s_gpio_irq;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_irq_rise, s_irq_fall;
|
||||
@@ -150,7 +148,6 @@ module nmi_gpio (
|
||||
assign s_gpio_native_oe[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_oe[i] : s_gpio_oe_q[i];
|
||||
assign s_gpio_native_out[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_q[i];
|
||||
end
|
||||
`ifdef IP_MDD
|
||||
assign user_gpio.di_i = gpio.di_i;
|
||||
for (genvar i = 0; i < `NMI_GPIO_NUM; i++) begin : USER_GPIO_MUX_BLOCK
|
||||
assign gpio.oe_o[i] = s_gpio_user_handoff_q[i]
|
||||
@@ -158,11 +155,6 @@ module nmi_gpio (
|
||||
: (s_gpio_user_sel_q[i] ? user_gpio.oe_o[i] : s_gpio_native_oe[i]);
|
||||
assign gpio.do_o[i] = s_gpio_user_sel_q[i] ? user_gpio.do_o[i] : s_gpio_native_out[i];
|
||||
end
|
||||
`else
|
||||
assign gpio.oe_o = s_gpio_native_oe;
|
||||
assign gpio.do_o = s_gpio_native_out;
|
||||
assign user_gpio.di_i = '0;
|
||||
`endif
|
||||
assign s_irq_stat = |s_gpio_istat_q;
|
||||
assign gpio.irq_o = s_irq_stat;
|
||||
|
||||
@@ -320,7 +312,6 @@ module nmi_gpio (
|
||||
s_gpio_pinmux_q
|
||||
);
|
||||
|
||||
`ifdef IP_MDD
|
||||
assign s_gpio_user_sel_en = s_nmi_wr_hdshk && nmi.addr[7:0] == `NMI_GPIO_USER_SEL;
|
||||
assign s_gpio_user_sel_d = (s_gpio_user_sel_q & s_gpio_user_lock_q) |
|
||||
(nmi.wdata[`NMI_GPIO_NUM-1:0] & ~s_gpio_user_lock_q);
|
||||
@@ -350,11 +341,10 @@ module nmi_gpio (
|
||||
s_gpio_user_handoff_q
|
||||
);
|
||||
assign s_gpio_user_status = s_gpio_user_sel_q & ~s_gpio_user_handoff_q;
|
||||
`endif
|
||||
|
||||
|
||||
// nmi resp
|
||||
assign s_nmi_ready_d = nmi.valid && (~s_nmi_ready_q);
|
||||
assign s_nmi_ready_d = nmi.valid && (~s_nmi_ready_q);
|
||||
dffr #(1) u_nmi_ready_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
@@ -378,11 +368,9 @@ module nmi_gpio (
|
||||
`NMI_GPIO_ISTAT: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_istat_q};
|
||||
`NMI_GPIO_IOFCFG: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_iofcfg_q};
|
||||
`NMI_GPIO_PINMUX: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pinmux_q};
|
||||
`ifdef IP_MDD
|
||||
`NMI_GPIO_USER_SEL: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_sel_q};
|
||||
`NMI_GPIO_USER_LOCK: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_lock_q};
|
||||
`NMI_GPIO_USER_STATUS: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_status};
|
||||
`endif
|
||||
default: s_nmi_rdata_d = s_nmi_rdata_q;
|
||||
endcase
|
||||
end
|
||||
|
||||
@@ -8,14 +8,27 @@
|
||||
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`include "mdd_config.svh"
|
||||
`include "user_extensions.svh"
|
||||
`include "mmap_define.svh"
|
||||
|
||||
interface sysctrl_if ();
|
||||
logic [`USER_CORESEL_WIDTH-1:0] core_sel_i;
|
||||
logic [`USER_CORESEL_WIDTH-1:0] core_sel_o;
|
||||
logic [ `USER_CORE_COUNT-1:0] core_reset_o;
|
||||
logic user_bus_enable_o;
|
||||
logic user_bus_idle_i;
|
||||
logic fault_access_i;
|
||||
logic [ 1:0] fault_master_i;
|
||||
logic [ `USER_IPSEL_WIDTH-1:0] ip_sel_o;
|
||||
|
||||
modport dut(input core_sel_i, output ip_sel_o);
|
||||
modport dut(
|
||||
input user_bus_idle_i,
|
||||
input fault_access_i,
|
||||
input fault_master_i,
|
||||
output core_sel_o,
|
||||
output core_reset_o,
|
||||
output user_bus_enable_o,
|
||||
output ip_sel_o
|
||||
);
|
||||
endinterface
|
||||
|
||||
module nmi_sysctrl (
|
||||
@@ -42,6 +55,12 @@ module nmi_sysctrl (
|
||||
localparam sysctrl_offset_t SYSCTRL_FAULT_ADDR_OFFSET = sysctrl_offset_t'(`SOC_SYSCTRL_FAULT_ADDR_OFFSET);
|
||||
localparam sysctrl_offset_t SYSCTRL_FAULT_COUNT_OFFSET = sysctrl_offset_t'(`SOC_SYSCTRL_FAULT_COUNT_OFFSET);
|
||||
localparam sysctrl_offset_t SYSCTRL_PLL_STATUS_OFFSET = sysctrl_offset_t'(`SOC_SYSCTRL_PLL_STATUS_OFFSET);
|
||||
localparam sysctrl_offset_t SYSCTRL_USER_CORE_RESET_OFFSET =
|
||||
sysctrl_offset_t'(`SOC_SYSCTRL_USER_CORE_RESET_OFFSET);
|
||||
localparam sysctrl_offset_t SYSCTRL_USER_CORE_STATUS_OFFSET =
|
||||
sysctrl_offset_t'(`SOC_SYSCTRL_USER_CORE_STATUS_OFFSET);
|
||||
localparam sysctrl_offset_t SYSCTRL_FAULT_MASTER_OFFSET =
|
||||
sysctrl_offset_t'(`SOC_SYSCTRL_FAULT_MASTER_OFFSET);
|
||||
|
||||
logic s_nmi_wr_hdshk, s_nmi_rd_hdshk;
|
||||
logic s_nmi_ready_d, s_nmi_ready_q;
|
||||
@@ -49,7 +68,17 @@ module nmi_sysctrl (
|
||||
logic [31:0] s_nmi_rdata_d, s_nmi_rdata_q;
|
||||
|
||||
logic [`USER_CORESEL_WIDTH-1:0] s_sysctrl_coresel_d, s_sysctrl_coresel_q;
|
||||
logic s_sysctrl_ipsel_en;
|
||||
logic s_sysctrl_coresel_en;
|
||||
logic s_sysctrl_coresel_write_valid;
|
||||
logic [`USER_CORE_COUNT-1:0] s_user_reset_d, s_user_reset_q;
|
||||
logic s_user_reset_en;
|
||||
logic s_user_running_d, s_user_running_q;
|
||||
logic s_user_draining_d, s_user_draining_q;
|
||||
logic s_user_config_error_d, s_user_config_error_q;
|
||||
logic s_user_config_error_clear;
|
||||
logic s_user_reset_write_legal;
|
||||
logic [`USER_CORE_COUNT-1:0] s_user_reset_write_data;
|
||||
logic s_sysctrl_ipsel_en;
|
||||
logic [`USER_IPSEL_WIDTH-1:0] s_sysctrl_ipsel_d, s_sysctrl_ipsel_q;
|
||||
logic [2:0] s_pll_cfg_d, s_pll_cfg_q;
|
||||
logic s_pll_cfg_en;
|
||||
@@ -66,28 +95,107 @@ module nmi_sysctrl (
|
||||
logic s_fault_status_clear;
|
||||
logic s_fault_pending_d, s_fault_pending_q;
|
||||
logic s_fault_write_d, s_fault_write_q;
|
||||
logic [1:0] s_fault_reason_d, s_fault_reason_q;
|
||||
logic [2:0] s_fault_reason_d, s_fault_reason_q;
|
||||
logic [31:0] s_fault_addr_d, s_fault_addr_q;
|
||||
logic [31:0] s_fault_count_d, s_fault_count_q;
|
||||
logic [1:0] s_fault_master_d, s_fault_master_q;
|
||||
|
||||
assign s_nmi_wr_hdshk = nmi.valid && (~s_nmi_ready_q) && (|nmi.wstrb);
|
||||
assign s_nmi_rd_hdshk = nmi.valid && (~s_nmi_ready_q) && (~(|nmi.wstrb));
|
||||
assign nmi.ready = s_nmi_ready_q;
|
||||
assign nmi.rdata = s_nmi_rdata_q;
|
||||
assign s_nmi_wr_hdshk = nmi.valid && (~s_nmi_ready_q) && (|nmi.wstrb);
|
||||
assign s_nmi_rd_hdshk = nmi.valid && (~s_nmi_ready_q) && (~(|nmi.wstrb));
|
||||
assign nmi.ready = s_nmi_ready_q;
|
||||
assign nmi.rdata = s_nmi_rdata_q;
|
||||
|
||||
assign sysctrl.ip_sel_o = s_sysctrl_ipsel_q;
|
||||
assign pll_ctrl.req_sel_o = s_pll_cfg_q;
|
||||
assign sysctrl.ip_sel_o = s_sysctrl_ipsel_q;
|
||||
assign sysctrl.core_sel_o = s_sysctrl_coresel_q;
|
||||
assign sysctrl.core_reset_o = s_user_reset_q;
|
||||
assign sysctrl.user_bus_enable_o = s_user_running_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.rsp_ready_o = 1'b1;
|
||||
|
||||
assign s_sysctrl_coresel_d = sysctrl.core_sel_i;
|
||||
dffr #(`USER_CORESEL_WIDTH) u_sysctrl_coresel_dffr (
|
||||
assign s_sysctrl_coresel_write_valid = nmi.wdata[`USER_CORESEL_WIDTH-1:0] < `USER_CORE_COUNT;
|
||||
assign s_sysctrl_coresel_en = s_nmi_wr_hdshk && nmi.addr[7:0] == SYSCTRL_CORESEL_OFFSET &&
|
||||
nmi.wstrb[0] && s_sysctrl_coresel_write_valid &&
|
||||
(&s_user_reset_q) && sysctrl.user_bus_idle_i && ~s_user_running_q;
|
||||
assign s_sysctrl_coresel_d = nmi.wdata[`USER_CORESEL_WIDTH-1:0];
|
||||
dffer #(`USER_CORESEL_WIDTH) u_sysctrl_coresel_dffer (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_sysctrl_coresel_en,
|
||||
s_sysctrl_coresel_d,
|
||||
s_sysctrl_coresel_q
|
||||
);
|
||||
|
||||
assign s_user_reset_write_data = nmi.wdata[`USER_CORE_COUNT-1:0];
|
||||
assign s_user_reset_write_legal = (&s_user_reset_write_data) ||
|
||||
((s_sysctrl_coresel_q < `USER_CORE_COUNT) &&
|
||||
((~s_user_reset_write_data) ==
|
||||
({{(`USER_CORE_COUNT - 1){1'b0}}, 1'b1} << s_sysctrl_coresel_q)));
|
||||
assign s_user_reset_en = s_nmi_wr_hdshk && nmi.addr[7:0] == SYSCTRL_USER_CORE_RESET_OFFSET &&
|
||||
nmi.wstrb[0];
|
||||
assign s_user_config_error_clear = s_nmi_wr_hdshk &&
|
||||
nmi.addr[7:0] == SYSCTRL_USER_CORE_STATUS_OFFSET && nmi.wstrb[1] && nmi.wdata[11];
|
||||
|
||||
always_comb begin
|
||||
s_user_reset_d = s_user_reset_q;
|
||||
s_user_running_d = s_user_running_q;
|
||||
s_user_draining_d = s_user_draining_q;
|
||||
s_user_config_error_d = s_user_config_error_q;
|
||||
if (s_user_config_error_clear) begin
|
||||
s_user_config_error_d = 1'b0;
|
||||
end
|
||||
if (s_nmi_wr_hdshk && nmi.addr[7:0] == SYSCTRL_CORESEL_OFFSET && ~s_sysctrl_coresel_en) begin
|
||||
s_user_config_error_d = 1'b1;
|
||||
end
|
||||
if (s_user_reset_en) begin
|
||||
if (~s_user_reset_write_legal) begin
|
||||
s_user_config_error_d = 1'b1;
|
||||
end else if (&s_user_reset_write_data) begin
|
||||
s_user_running_d = 1'b0;
|
||||
if (sysctrl.user_bus_idle_i) begin
|
||||
s_user_reset_d = s_user_reset_write_data;
|
||||
s_user_draining_d = 1'b0;
|
||||
end else begin
|
||||
s_user_draining_d = 1'b1;
|
||||
end
|
||||
end else if (sysctrl.user_bus_idle_i && (&s_user_reset_q) && ~s_user_running_q) begin
|
||||
s_user_reset_d = s_user_reset_write_data;
|
||||
s_user_running_d = 1'b1;
|
||||
s_user_draining_d = 1'b0;
|
||||
end else begin
|
||||
s_user_config_error_d = 1'b1;
|
||||
end
|
||||
end
|
||||
if (s_user_draining_q && sysctrl.user_bus_idle_i) begin
|
||||
s_user_reset_d = '1;
|
||||
s_user_draining_d = 1'b0;
|
||||
end
|
||||
end
|
||||
dffrc #(`USER_CORE_COUNT, '1) u_user_reset_dffrc (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_user_reset_d,
|
||||
s_user_reset_q
|
||||
);
|
||||
dffr #(1) u_user_running_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_user_running_d,
|
||||
s_user_running_q
|
||||
);
|
||||
dffr #(1) u_user_draining_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_user_draining_d,
|
||||
s_user_draining_q
|
||||
);
|
||||
dffr #(1) u_user_config_error_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_user_config_error_d,
|
||||
s_user_config_error_q
|
||||
);
|
||||
|
||||
assign s_sysctrl_ipsel_en = s_nmi_wr_hdshk && nmi.addr[7:0] == SYSCTRL_IPSEL_OFFSET;
|
||||
assign s_sysctrl_ipsel_d = nmi.wdata[`USER_IPSEL_WIDTH-1:0];
|
||||
dffer #(`USER_IPSEL_WIDTH) u_sysctrl_ipsel_dffer (
|
||||
@@ -218,7 +326,7 @@ module nmi_sysctrl (
|
||||
if (fault_valid_i) begin
|
||||
s_fault_pending_d = 1'b1;
|
||||
s_fault_write_d = |fault_wstrb_i;
|
||||
s_fault_reason_d = fault_reserved_i ? 2'd2 : 2'd1;
|
||||
s_fault_reason_d = sysctrl.fault_access_i ? 3'd3 : (fault_reserved_i ? 3'd2 : 3'd1);
|
||||
s_fault_addr_d = fault_addr_i;
|
||||
if (~(&s_fault_count_q)) begin
|
||||
s_fault_count_d = s_fault_count_q + 32'd1;
|
||||
@@ -237,7 +345,7 @@ module nmi_sysctrl (
|
||||
s_fault_write_d,
|
||||
s_fault_write_q
|
||||
);
|
||||
dffr #(2) u_fault_reason_dffr (
|
||||
dffr #(3) u_fault_reason_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_fault_reason_d,
|
||||
@@ -255,6 +363,14 @@ module nmi_sysctrl (
|
||||
s_fault_count_d,
|
||||
s_fault_count_q
|
||||
);
|
||||
assign s_fault_master_d = sysctrl.fault_master_i;
|
||||
dffer #(2) u_fault_master_dffer (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
fault_valid_i,
|
||||
s_fault_master_d,
|
||||
s_fault_master_q
|
||||
);
|
||||
|
||||
assign s_nmi_ready_d = nmi.valid && (~s_nmi_ready_q);
|
||||
dffr #(1) u_nmi_ready_dffr (
|
||||
@@ -272,7 +388,7 @@ module nmi_sysctrl (
|
||||
SYSCTRL_CORESEL_OFFSET: s_nmi_rdata_d = {{(32 - `USER_CORESEL_WIDTH) {1'b0}}, s_sysctrl_coresel_q};
|
||||
SYSCTRL_IPSEL_OFFSET: s_nmi_rdata_d = {{(32 - `USER_IPSEL_WIDTH) {1'b0}}, s_sysctrl_ipsel_q};
|
||||
SYSCTRL_PLL_CFG_OFFSET: s_nmi_rdata_d = {29'd0, s_pll_cfg_q};
|
||||
SYSCTRL_FAULT_STATUS_OFFSET: s_nmi_rdata_d = {28'd0, s_fault_reason_q, s_fault_write_q, s_fault_pending_q};
|
||||
SYSCTRL_FAULT_STATUS_OFFSET: s_nmi_rdata_d = {27'd0, s_fault_reason_q, s_fault_write_q, s_fault_pending_q};
|
||||
SYSCTRL_FAULT_ADDR_OFFSET: s_nmi_rdata_d = s_fault_addr_q;
|
||||
SYSCTRL_FAULT_COUNT_OFFSET: s_nmi_rdata_d = s_fault_count_q;
|
||||
SYSCTRL_PLL_STATUS_OFFSET: s_nmi_rdata_d = {
|
||||
@@ -286,6 +402,17 @@ module nmi_sysctrl (
|
||||
s_pll_active_valid_q,
|
||||
s_pll_active_sel_q
|
||||
};
|
||||
SYSCTRL_USER_CORE_RESET_OFFSET: s_nmi_rdata_d = {{(32 - `USER_CORE_COUNT) {1'b0}}, s_user_reset_q};
|
||||
SYSCTRL_USER_CORE_STATUS_OFFSET: s_nmi_rdata_d = {
|
||||
20'd0,
|
||||
s_user_config_error_q,
|
||||
s_user_draining_q,
|
||||
sysctrl.user_bus_idle_i,
|
||||
s_user_running_q,
|
||||
{ (8 - `USER_CORESEL_WIDTH) {1'b0} },
|
||||
s_sysctrl_coresel_q
|
||||
};
|
||||
SYSCTRL_FAULT_MASTER_OFFSET: s_nmi_rdata_d = {30'd0, s_fault_master_q};
|
||||
default: s_nmi_rdata_d = s_nmi_rdata_q;
|
||||
endcase
|
||||
// verilog_format: on
|
||||
|
||||
+76
-38
@@ -1,22 +1,34 @@
|
||||
GENERATED_FL_DIR := $(FLOW_FILELIST_DIR)
|
||||
MEMORY_MAP_SOURCE := $(RTL_PATH)/address_map/memory_map.json
|
||||
MEMORY_MAP_SCRIPT := $(RTL_PATH)/address_map/generate_memory_map.py
|
||||
MEMORY_MAP_DIR := $(VARIANT_ROOT)/generated/memory_map
|
||||
MEMORY_MAP_STAMP := $(MEMORY_MAP_DIR)/.stamp
|
||||
MEMORY_MAP_RTL_DIR := $(MEMORY_MAP_DIR)/rtl
|
||||
MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include
|
||||
MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker
|
||||
MEMORY_MAP_FILELIST := $(MEMORY_MAP_DIR)/memory_map.fl
|
||||
PIN_MAP_SOURCE := $(RTL_PATH)/pin_map/pin_map.json
|
||||
PIN_MAP_SCRIPT := $(RTL_PATH)/pin_map/generate_pin_map.py
|
||||
PIN_MAP_DIR := $(VARIANT_ROOT)/generated/pin_map
|
||||
PIN_MAP_STAMP := $(PIN_MAP_DIR)/.stamp
|
||||
PIN_MAP_FILELIST := $(PIN_MAP_DIR)/pin_map.fl
|
||||
MPW_STAMP := $(CACHE_ROOT)/stamps/mpw-$(LOCK_DIGEST).prepared
|
||||
MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME)
|
||||
MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp
|
||||
FILELIST_STAMP := $(GENERATED_FL_DIR)/.stamp
|
||||
FILELIST_TEMPLATES := $(sort $(wildcard $(ROOT_PATH)/rtl/filelist/pdk_*.fl) \
|
||||
GENERATED_FL_DIR := $(FLOW_FILELIST_DIR)
|
||||
MEMORY_MAP_SOURCE := $(RTL_PATH)/address_map/memory_map.json
|
||||
MEMORY_MAP_SCRIPT := $(RTL_PATH)/address_map/generate_memory_map.py
|
||||
MEMORY_MAP_DIR := $(VARIANT_ROOT)/generated/memory_map
|
||||
MEMORY_MAP_STAMP := $(MEMORY_MAP_DIR)/.stamp
|
||||
MEMORY_MAP_RTL_DIR := $(MEMORY_MAP_DIR)/rtl
|
||||
MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include
|
||||
MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker
|
||||
MEMORY_MAP_FILELIST := $(MEMORY_MAP_DIR)/memory_map.fl
|
||||
PIN_MAP_SOURCE := $(RTL_PATH)/pin_map/pin_map.json
|
||||
PIN_MAP_SCRIPT := $(RTL_PATH)/pin_map/generate_pin_map.py
|
||||
PIN_MAP_DIR := $(VARIANT_ROOT)/generated/pin_map
|
||||
PIN_MAP_STAMP := $(PIN_MAP_DIR)/.stamp
|
||||
PIN_MAP_FILELIST := $(PIN_MAP_DIR)/pin_map.fl
|
||||
SOC_TOPOLOGY_SOURCE := $(RTL_PATH)/integration/soc_topology.json
|
||||
SOC_TOPOLOGY_SCRIPT := $(RTL_PATH)/integration/generate_soc_topology.py
|
||||
SOC_TOPOLOGY_DIR := $(VARIANT_ROOT)/generated/soc_topology
|
||||
SOC_TOPOLOGY_STAMP := $(SOC_TOPOLOGY_DIR)/.stamp
|
||||
SOC_TOPOLOGY_FILELIST := $(SOC_TOPOLOGY_DIR)/soc_topology.fl
|
||||
USER_EXTENSIONS_SOURCE := $(RTL_PATH)/integration/user_extensions.json
|
||||
USER_EXTENSIONS_SCRIPT := $(RTL_PATH)/integration/generate_user_extensions.py
|
||||
USER_EXTENSIONS_DIR := $(VARIANT_ROOT)/generated/user_extensions
|
||||
USER_EXTENSIONS_STAMP := $(USER_EXTENSIONS_DIR)/.stamp
|
||||
USER_EXTENSIONS_FILELIST := $(USER_EXTENSIONS_DIR)/user_extensions.fl
|
||||
CLOCK_RESET_DOMAINS_SOURCE := $(RTL_PATH)/integration/clock_reset_domains.json
|
||||
CLOCK_RESET_DOMAINS_SCRIPT := $(ROOT_PATH)/scripts/check_clock_reset_domains.py
|
||||
MPW_STAMP := $(CACHE_ROOT)/stamps/mpw-$(LOCK_DIGEST).prepared
|
||||
MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME)
|
||||
MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp
|
||||
FILELIST_STAMP := $(GENERATED_FL_DIR)/.stamp
|
||||
FILELIST_TEMPLATES := $(sort $(wildcard $(ROOT_PATH)/rtl/filelist/pdk_*.fl) \
|
||||
$(wildcard $(RTL_PATH)/filelist/*.fl) $(RTL_PATH)/lint.msg)
|
||||
|
||||
$(MPW_STAMP):
|
||||
@@ -31,7 +43,7 @@ $(MPW_STAMP):
|
||||
|
||||
$(MPW_VARIANT_STAMP): $(MPW_STAMP) $(ROOT_PATH)/scripts/generate_mpw.py
|
||||
python3 $(ROOT_PATH)/scripts/generate_mpw.py --root $(ROOT_PATH) \
|
||||
--output $(MPW_OUTPUT_DIR) --simu $(SIMU) --core $(CORE) --ip $(IP) \
|
||||
--output $(MPW_OUTPUT_DIR) --simu $(SIMU) \
|
||||
--lock-file $(CACHE_ROOT)/locks/mpw.lock
|
||||
@mkdir -p $(@D)
|
||||
@touch $@
|
||||
@@ -40,7 +52,7 @@ gen_mpw_code: $(MPW_VARIANT_STAMP)
|
||||
|
||||
$(MEMORY_MAP_STAMP): $(MEMORY_MAP_SOURCE) $(MEMORY_MAP_SCRIPT)
|
||||
python3 $(MEMORY_MAP_SCRIPT) --map $(MEMORY_MAP_SOURCE) \
|
||||
--output-dir $(MEMORY_MAP_DIR) --have-sram-if $(HAVE_SRAM_IF) --ip $(IP)
|
||||
--output-dir $(MEMORY_MAP_DIR) --have-sram-if $(HAVE_SRAM_IF)
|
||||
@touch $@
|
||||
|
||||
memory-map: $(MEMORY_MAP_STAMP)
|
||||
@@ -57,8 +69,33 @@ pin-map: $(PIN_MAP_STAMP)
|
||||
check-pin-map:
|
||||
python3 $(PIN_MAP_SCRIPT) --map $(PIN_MAP_SOURCE) --check
|
||||
|
||||
$(SOC_TOPOLOGY_STAMP): $(SOC_TOPOLOGY_SOURCE) $(SOC_TOPOLOGY_SCRIPT) $(MEMORY_MAP_SOURCE)
|
||||
python3 $(SOC_TOPOLOGY_SCRIPT) --map $(SOC_TOPOLOGY_SOURCE) \
|
||||
--memory-map $(MEMORY_MAP_SOURCE) --output-dir $(SOC_TOPOLOGY_DIR)
|
||||
@touch $@
|
||||
|
||||
soc-topology: $(SOC_TOPOLOGY_STAMP)
|
||||
|
||||
check-soc-topology:
|
||||
python3 $(SOC_TOPOLOGY_SCRIPT) --map $(SOC_TOPOLOGY_SOURCE) \
|
||||
--memory-map $(MEMORY_MAP_SOURCE) --check
|
||||
|
||||
$(USER_EXTENSIONS_STAMP): $(USER_EXTENSIONS_SOURCE) $(USER_EXTENSIONS_SCRIPT)
|
||||
python3 $(USER_EXTENSIONS_SCRIPT) --map $(USER_EXTENSIONS_SOURCE) \
|
||||
--output-dir $(USER_EXTENSIONS_DIR)
|
||||
@touch $@
|
||||
|
||||
user-extensions: $(USER_EXTENSIONS_STAMP)
|
||||
|
||||
check-user-extensions:
|
||||
python3 $(USER_EXTENSIONS_SCRIPT) --map $(USER_EXTENSIONS_SOURCE) --check
|
||||
|
||||
check-clock-reset-domains:
|
||||
python3 $(CLOCK_RESET_DOMAINS_SCRIPT) --map $(CLOCK_RESET_DOMAINS_SOURCE) --root $(ROOT_PATH)
|
||||
|
||||
$(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \
|
||||
$(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP)
|
||||
$(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) \
|
||||
$(USER_EXTENSIONS_STAMP)
|
||||
python3 $(RTL_PATH)/script/generate_filelist.py \
|
||||
--output-dir $(GENERATED_FL_DIR) \
|
||||
$(foreach define,$(DEF_LIST),--define $(define))
|
||||
@@ -71,36 +108,35 @@ include $(RTL_PATH)/mk/software.mk
|
||||
|
||||
#====================== filelist ======================
|
||||
ifeq ($(PDK), IHP130)
|
||||
NET_FLIST := -f $(GENERATED_FL_DIR)/pdk_ihp130.fl
|
||||
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ihp130.fl
|
||||
else ifeq ($(PDK), SKY130)
|
||||
NET_FLIST := -f $(GENERATED_FL_DIR)/pdk_sky130.fl
|
||||
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_sky130.fl
|
||||
else ifeq ($(PDK), ICS55)
|
||||
NET_FLIST := -f $(GENERATED_FL_DIR)/pdk_ics55.fl
|
||||
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ics55.fl
|
||||
else ifeq ($(PDK), GF180)
|
||||
NET_FLIST := -f $(GENERATED_FL_DIR)/pdk_gf180.fl
|
||||
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_gf180.fl
|
||||
endif
|
||||
|
||||
NET_FLIST := -f $(PDK_FILELIST)
|
||||
NET_FLIST += -f $(GENERATED_FL_DIR)/def.fl
|
||||
NET_FLIST += -f $(GENERATED_FL_DIR)/sys_def.fl
|
||||
NET_FLIST += -f $(GENERATED_FL_DIR)/commonip.fl
|
||||
NET_FLIST += -f $(GENERATED_FL_DIR)/inc.fl
|
||||
|
||||
RTL_FLIST := -f $(MEMORY_MAP_FILELIST) -f $(PIN_MAP_FILELIST) $(NET_FLIST)
|
||||
RTL_FLIST := -f $(MEMORY_MAP_FILELIST) -f $(PIN_MAP_FILELIST) -f $(SOC_TOPOLOGY_FILELIST) \
|
||||
-f $(USER_EXTENSIONS_FILELIST) $(NET_FLIST)
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/clusterip.fl
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/ip.fl
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/tech.fl
|
||||
|
||||
ifeq ($(CORE), PICORV32)
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/core_picorv32.fl
|
||||
else ifeq ($(CORE), HAZARD3)
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/core_hazard3.fl
|
||||
else ifeq ($(CORE), MDD)
|
||||
RTL_FLIST += -f $(MPW_OUTPUT_DIR)/core/core.fl
|
||||
ifeq ($(CORE),HAZARD3)
|
||||
MGMT_CORE_FILELIST := core_hazard3.fl
|
||||
else
|
||||
MGMT_CORE_FILELIST := core_picorv32.fl
|
||||
endif
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/$(MGMT_CORE_FILELIST)
|
||||
|
||||
ifeq ($(IP), MDD)
|
||||
RTL_FLIST += -f $(MPW_OUTPUT_DIR)/ip/ip.fl
|
||||
endif
|
||||
RTL_FLIST += -f $(MPW_OUTPUT_DIR)/core/core.fl
|
||||
RTL_FLIST += -f $(MPW_OUTPUT_DIR)/ip/ip.fl
|
||||
|
||||
RTL_FLIST += -f $(GENERATED_FL_DIR)/top.fl
|
||||
#====================== end ======================
|
||||
@@ -113,4 +149,6 @@ else ifeq ($(SIMU), IVERILOG)
|
||||
include $(RTL_PATH)/mk/iverilog.mk
|
||||
endif
|
||||
|
||||
.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map firmware
|
||||
.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map \
|
||||
soc-topology check-soc-topology user-extensions check-user-extensions \
|
||||
check-clock-reset-domains firmware
|
||||
@@ -14,6 +14,7 @@ from typing import Any
|
||||
MAX_ADDRESS = 1 << 32
|
||||
VALID_ROUTES = {"native", "apb", "ram", "reserved"}
|
||||
VALID_KINDS = {"active", "reserved"}
|
||||
VALID_USER_ACCESS = {"none", "ro", "rw"}
|
||||
|
||||
|
||||
def parse_integer(value: Any, field: str) -> int:
|
||||
@@ -53,9 +54,13 @@ def read_map(path: Path) -> tuple[int, list[dict[str, Any]]]:
|
||||
raise ValueError(f"regions[{index}] has invalid route or kind")
|
||||
if (route == "reserved") != (kind == "reserved"):
|
||||
raise ValueError(f"regions[{index}] reserved route and kind must agree")
|
||||
requires_ip = region.get("requires_ip")
|
||||
if requires_ip is not None and requires_ip != "MDD":
|
||||
raise ValueError(f"regions[{index}].requires_ip must be MDD when present")
|
||||
user_access = region.get("user_access")
|
||||
if user_access not in VALID_USER_ACCESS:
|
||||
raise ValueError(
|
||||
f"regions[{index}].user_access must be one of {sorted(VALID_USER_ACCESS)}"
|
||||
)
|
||||
if kind == "reserved" and user_access != "none":
|
||||
raise ValueError(f"regions[{index}] reserved regions must deny user access")
|
||||
base = parse_integer(region.get("base"), f"regions[{index}].base")
|
||||
size = parse_integer(region.get("size"), f"regions[{index}].size")
|
||||
if base < 0 or size <= 0 or base + size > MAX_ADDRESS:
|
||||
@@ -72,7 +77,7 @@ def read_map(path: Path) -> tuple[int, list[dict[str, Any]]]:
|
||||
"kind": kind,
|
||||
"public": region.get("public", False) is True,
|
||||
"linker": region.get("linker"),
|
||||
"requires_ip": requires_ip,
|
||||
"user_access": user_access,
|
||||
}
|
||||
)
|
||||
previous: dict[str, Any] | None = None
|
||||
@@ -91,8 +96,13 @@ def c_hex32(value: int) -> str:
|
||||
return f"UINT32_C(0x{value:08X})"
|
||||
|
||||
|
||||
def range_expression(symbol: str) -> str:
|
||||
return f"((addr) >= `SOC_ADDR_{symbol}_BASE && (addr) <= `SOC_ADDR_{symbol}_END)"
|
||||
def range_expression(symbol: str, base: int, end: int) -> str:
|
||||
conditions: list[str] = []
|
||||
if base != 0:
|
||||
conditions.append(f"(addr) >= `SOC_ADDR_{symbol}_BASE")
|
||||
if end != MAX_ADDRESS - 1:
|
||||
conditions.append(f"(addr) <= `SOC_ADDR_{symbol}_END")
|
||||
return "(" + " && ".join(conditions or ["1'b1"]) + ")"
|
||||
|
||||
|
||||
def join_or(expressions: list[str]) -> str:
|
||||
@@ -117,7 +127,8 @@ def render_rtl(cpu_reset: int, regions: list[dict[str, Any]]) -> str:
|
||||
lines += [
|
||||
f"`define SOC_ADDR_{symbol}_BASE {hex32(region['base'])}",
|
||||
f"`define SOC_ADDR_{symbol}_END {hex32(region['end'])}",
|
||||
f"`define SOC_ADDR_IS_{symbol}(addr) {range_expression(symbol)}",
|
||||
f"`define SOC_ADDR_IS_{symbol}(addr) "
|
||||
f"{range_expression(symbol, region['base'], region['end'])}",
|
||||
]
|
||||
lines.append("")
|
||||
for route in ("native", "apb", "ram", "reserved"):
|
||||
@@ -127,6 +138,21 @@ def render_rtl(cpu_reset: int, regions: list[dict[str, Any]]) -> str:
|
||||
if region["route"] == route
|
||||
]
|
||||
lines.append(f"`define SOC_ADDR_IS_{route.upper()}(addr) ({join_or(selected)})")
|
||||
user_readable = [
|
||||
f"`SOC_ADDR_IS_{region['symbol']}(addr)"
|
||||
for region in regions
|
||||
if region["user_access"] in {"ro", "rw"}
|
||||
]
|
||||
user_writable = [
|
||||
f"`SOC_ADDR_IS_{region['symbol']}(addr)"
|
||||
for region in regions
|
||||
if region["user_access"] == "rw"
|
||||
]
|
||||
lines += [
|
||||
"",
|
||||
f"`define SOC_USER_ADDR_READABLE(addr) ({join_or(user_readable)})",
|
||||
f"`define SOC_USER_ADDR_WRITABLE(addr) ({join_or(user_writable)})",
|
||||
]
|
||||
lines += [
|
||||
"",
|
||||
"`define SOC_SYSCTRL_PLL_CFG_OFFSET 32'h00000008",
|
||||
@@ -135,6 +161,9 @@ def render_rtl(cpu_reset: int, regions: list[dict[str, Any]]) -> str:
|
||||
"`define SOC_SYSCTRL_FAULT_ADDR_OFFSET 32'h00000014",
|
||||
"`define SOC_SYSCTRL_FAULT_COUNT_OFFSET 32'h00000018",
|
||||
"`define SOC_SYSCTRL_PLL_STATUS_OFFSET 32'h0000001C",
|
||||
"`define SOC_SYSCTRL_USER_CORE_RESET_OFFSET 32'h00000020",
|
||||
"`define SOC_SYSCTRL_USER_CORE_STATUS_OFFSET 32'h00000024",
|
||||
"`define SOC_SYSCTRL_FAULT_MASTER_OFFSET 32'h00000028",
|
||||
"",
|
||||
"`endif",
|
||||
"",
|
||||
@@ -171,6 +200,9 @@ def render_c(cpu_reset: int, regions: list[dict[str, Any]], have_sram_if: str) -
|
||||
"#define RS_SOC_SYSCTRL_FAULT_ADDR_OFFSET UINT32_C(0x00000014)",
|
||||
"#define RS_SOC_SYSCTRL_FAULT_COUNT_OFFSET UINT32_C(0x00000018)",
|
||||
"#define RS_SOC_SYSCTRL_PLL_STATUS_OFFSET UINT32_C(0x0000001C)",
|
||||
"#define RS_SOC_SYSCTRL_USER_CORE_RESET_OFFSET UINT32_C(0x00000020)",
|
||||
"#define RS_SOC_SYSCTRL_USER_CORE_STATUS_OFFSET UINT32_C(0x00000024)",
|
||||
"#define RS_SOC_SYSCTRL_FAULT_MASTER_OFFSET UINT32_C(0x00000028)",
|
||||
"",
|
||||
"#endif",
|
||||
"",
|
||||
@@ -211,18 +243,15 @@ def atomic_write(path: Path, content: str) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def generate(map_path: Path, output_dir: Path, have_sram_if: str, ip: str) -> None:
|
||||
def generate(map_path: Path, output_dir: Path, have_sram_if: str) -> None:
|
||||
cpu_reset, regions = read_map(map_path)
|
||||
enabled_regions = [
|
||||
region for region in regions if region["requires_ip"] is None or region["requires_ip"] == ip
|
||||
]
|
||||
atomic_write(output_dir / "rtl" / "mmap_define.svh", render_rtl(cpu_reset, enabled_regions))
|
||||
atomic_write(output_dir / "rtl" / "mmap_define.svh", render_rtl(cpu_reset, regions))
|
||||
atomic_write(output_dir / "memory_map.fl", f"+incdir+{output_dir / 'rtl'}\n")
|
||||
atomic_write(
|
||||
output_dir / "include" / "retrosoc" / "generated" / "memory_map.h",
|
||||
render_c(cpu_reset, enabled_regions, have_sram_if),
|
||||
render_c(cpu_reset, regions, have_sram_if),
|
||||
)
|
||||
atomic_write(output_dir / "linker" / "memory_regions.ld", render_linker(enabled_regions))
|
||||
atomic_write(output_dir / "linker" / "memory_regions.ld", render_linker(regions))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -230,7 +259,6 @@ def main() -> int:
|
||||
parser.add_argument("--map", required=True, type=Path)
|
||||
parser.add_argument("--output-dir", type=Path)
|
||||
parser.add_argument("--have-sram-if", choices=("YES", "NO"), default="NO")
|
||||
parser.add_argument("--ip", choices=("NONE", "MDD"), default="NONE")
|
||||
parser.add_argument("--check", action="store_true", help="validate only; write nothing")
|
||||
arguments = parser.parse_args()
|
||||
try:
|
||||
@@ -239,7 +267,7 @@ def main() -> int:
|
||||
else:
|
||||
if arguments.output_dir is None:
|
||||
parser.error("--output-dir is required unless --check is used")
|
||||
generate(arguments.map, arguments.output_dir, arguments.have_sram_if, arguments.ip)
|
||||
generate(arguments.map, arguments.output_dir, arguments.have_sram_if)
|
||||
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||
parser.error(str(error))
|
||||
return 0
|
||||
|
||||
@@ -2,42 +2,42 @@
|
||||
"schema_version": 1,
|
||||
"cpu_reset": "0x00000000",
|
||||
"regions": [
|
||||
{"symbol": "FLASH", "base": "0x00000000", "size": "0x01000000", "route": "native", "kind": "active", "public": true, "linker": "FLASH"},
|
||||
{"symbol": "NMI_GPIO", "base": "0x10000000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_UART0", "base": "0x10001000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_TIM0", "base": "0x10002000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_TIM1", "base": "0x10003000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_PSRAM", "base": "0x10004000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_SPISD", "base": "0x10005000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_I2C0", "base": "0x10006000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_I2S", "base": "0x10007000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_ONEWIRE", "base": "0x10008000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_XPI", "base": "0x10009000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_DMA", "base": "0x1000A000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_SYSCTRL", "base": "0x1000B000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_CLINT", "base": "0x1000C000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_SDRAM", "base": "0x1000D000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_DVP", "base": "0x1000E000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_SDIO", "base": "0x1000F000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false},
|
||||
{"symbol": "NMI_OPIPSRAM", "base": "0x10010000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false},
|
||||
{"symbol": "NMI_I2C1", "base": "0x10011000", "size": "0x00001000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "NMI_GA", "base": "0x10012000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false},
|
||||
{"symbol": "NMI_APU", "base": "0x10013000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false},
|
||||
{"symbol": "APB_ARCHINFO", "base": "0x20000000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_RNG", "base": "0x20001000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_UART1", "base": "0x20002000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_PWM", "base": "0x20003000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_PS2", "base": "0x20004000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_RTC", "base": "0x20005000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_WDG", "base": "0x20006000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_CRC", "base": "0x20007000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_TMR", "base": "0x20008000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true},
|
||||
{"symbol": "APB_USER_IP", "base": "0x2000F000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "requires_ip": "MDD"},
|
||||
{"symbol": "SRAM", "base": "0x30000000", "size": "0x00020000", "route": "ram", "kind": "active", "public": true, "linker": "SRAM"},
|
||||
{"symbol": "SDRAM", "base": "0x38000000", "size": "0x02000000", "route": "native", "kind": "active", "public": true, "linker": "SDRAM"},
|
||||
{"symbol": "PSRAM", "base": "0x40000000", "size": "0x00800000", "route": "native", "kind": "active", "public": true, "linker": "PSRAM"},
|
||||
{"symbol": "OPIPSRAM", "base": "0x48000000", "size": "0x08000000", "route": "reserved", "kind": "reserved", "public": false},
|
||||
{"symbol": "XPI", "base": "0x50000000", "size": "0x10000000", "route": "native", "kind": "active", "public": true},
|
||||
{"symbol": "SPISD", "base": "0x60000000", "size": "0x40000000", "route": "native", "kind": "active", "public": true}
|
||||
{"symbol": "FLASH", "base": "0x00000000", "size": "0x01000000", "route": "native", "kind": "active", "public": true, "linker": "FLASH", "user_access": "ro"},
|
||||
{"symbol": "NMI_GPIO", "base": "0x10000000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_UART0", "base": "0x10001000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_TIM0", "base": "0x10002000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_TIM1", "base": "0x10003000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_PSRAM", "base": "0x10004000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_SPISD", "base": "0x10005000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_I2C0", "base": "0x10006000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_I2S", "base": "0x10007000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_ONEWIRE", "base": "0x10008000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_XPI", "base": "0x10009000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_DMA", "base": "0x1000A000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_SYSCTRL", "base": "0x1000B000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_CLINT", "base": "0x1000C000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_SDRAM", "base": "0x1000D000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "NMI_DVP", "base": "0x1000E000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_SDIO", "base": "0x1000F000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
|
||||
{"symbol": "NMI_OPIPSRAM", "base": "0x10010000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
|
||||
{"symbol": "NMI_I2C1", "base": "0x10011000", "size": "0x00001000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "NMI_GA", "base": "0x10012000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
|
||||
{"symbol": "NMI_APU", "base": "0x10013000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
|
||||
{"symbol": "APB_ARCHINFO", "base": "0x20000000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "ro"},
|
||||
{"symbol": "APB_RNG", "base": "0x20001000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_UART1", "base": "0x20002000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_PWM", "base": "0x20003000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_PS2", "base": "0x20004000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_RTC", "base": "0x20005000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_WDG", "base": "0x20006000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "none"},
|
||||
{"symbol": "APB_CRC", "base": "0x20007000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_TMR", "base": "0x20008000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "APB_USER_IP", "base": "0x2000F000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "SRAM", "base": "0x30000000", "size": "0x00020000", "route": "ram", "kind": "active", "public": true, "linker": "SRAM", "user_access": "rw"},
|
||||
{"symbol": "SDRAM", "base": "0x38000000", "size": "0x02000000", "route": "native", "kind": "active", "public": true, "linker": "SDRAM", "user_access": "rw"},
|
||||
{"symbol": "PSRAM", "base": "0x40000000", "size": "0x00800000", "route": "native", "kind": "active", "public": true, "linker": "PSRAM", "user_access": "rw"},
|
||||
{"symbol": "OPIPSRAM", "base": "0x48000000", "size": "0x08000000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
|
||||
{"symbol": "XPI", "base": "0x50000000", "size": "0x10000000", "route": "native", "kind": "active", "public": true, "user_access": "rw"},
|
||||
{"symbol": "SPISD", "base": "0x60000000", "size": "0x40000000", "route": "native", "kind": "active", "public": true, "user_access": "rw"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
`ifndef SV_ASSRT_DISABLE
|
||||
`ifndef SYNTHESIS
|
||||
|
||||
module soc_bus_sva (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic fault_sel_i,
|
||||
input logic fault_valid_i,
|
||||
input logic access_denied_i,
|
||||
input logic arb_locked_i,
|
||||
input logic [1:0] arb_owner_i,
|
||||
input logic mgmt_valid_i,
|
||||
input logic user_valid_i,
|
||||
input logic dma_valid_i
|
||||
);
|
||||
|
||||
// A decode miss must be reported and terminate the transaction locally.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i) fault_sel_i |-> fault_valid_i);
|
||||
|
||||
// A sole management request must acquire ownership rather than being dropped.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
!arb_locked_i && mgmt_valid_i && !user_valid_i && !dma_valid_i |=>
|
||||
arb_locked_i && arb_owner_i == 2'd0);
|
||||
|
||||
// A denied user request terminates locally and cannot reach a target.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i) access_denied_i |-> fault_valid_i);
|
||||
|
||||
endmodule
|
||||
|
||||
bind bus soc_bus_sva u_soc_bus_sva (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.fault_sel_i (s_fault_sel),
|
||||
.fault_valid_i (fault_valid_o),
|
||||
.arb_locked_i (s_mstr_lock_q),
|
||||
.access_denied_i(s_access_denied),
|
||||
.arb_owner_i (s_mstr_id_q),
|
||||
.mgmt_valid_i (mgmt_nmi.valid),
|
||||
.user_valid_i (user_nmi.valid),
|
||||
.dma_valid_i (dma_nmi.valid)
|
||||
);
|
||||
|
||||
module soc_nmi2apb_sva #(
|
||||
parameter int NSLV = 1
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [NSLV-1:0] psel_comb_i,
|
||||
input logic [NSLV-1:0] psel_q_i,
|
||||
input logic nmi_ready_i,
|
||||
input logic xfer_ready_i
|
||||
);
|
||||
|
||||
// Address regions are disjoint and only the registered select drives a response.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(psel_comb_i));
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i) $onehot0(psel_q_i));
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i) nmi_ready_i |-> xfer_ready_i);
|
||||
|
||||
endmodule
|
||||
|
||||
bind nmi2apb soc_nmi2apb_sva #(
|
||||
.NSLV(NSLV)
|
||||
) u_soc_nmi2apb_sva (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.psel_comb_i (s_psel_comb),
|
||||
.psel_q_i (s_psel_q),
|
||||
.nmi_ready_i (nmi.ready),
|
||||
.xfer_ready_i(s_xfer_ready)
|
||||
);
|
||||
|
||||
module soc_pll_rcu_sva (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [2:0] state_i,
|
||||
input logic pll_capable_i,
|
||||
input logic pll_apply_i,
|
||||
input logic select_ext_clk_i
|
||||
);
|
||||
|
||||
localparam logic [2:0] PLL_SAFE = 3'd1;
|
||||
localparam logic [2:0] PLL_APPLY = 3'd2;
|
||||
localparam logic [2:0] PLL_WAIT_LOCK = 3'd3;
|
||||
localparam logic [2:0] PLL_RESPOND = 3'd5;
|
||||
|
||||
// The output remains on the external clock until the PLL switch stage completes.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
state_i == PLL_SAFE |-> select_ext_clk_i);
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
pll_apply_i |-> state_i inside {PLL_APPLY, PLL_WAIT_LOCK});
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
state_i == PLL_SAFE && !pll_capable_i |=> state_i == PLL_RESPOND);
|
||||
|
||||
endmodule
|
||||
|
||||
bind pll_rcu_controller soc_pll_rcu_sva u_soc_pll_rcu_sva (
|
||||
.clk_i (ext_clk_i),
|
||||
.rst_n_i (ext_rst_n_i),
|
||||
.state_i (s_state_q),
|
||||
.pll_capable_i (pll_capable_i),
|
||||
.pll_apply_i (pll_apply_o),
|
||||
.select_ext_clk_i(select_ext_clk_o)
|
||||
);
|
||||
|
||||
module soc_gpio_user_handoff_sva #(
|
||||
parameter int DATA_WIDTH = 1
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [DATA_WIDTH-1:0] user_sel_i,
|
||||
input logic [DATA_WIDTH-1:0] user_handoff_i,
|
||||
input logic [DATA_WIDTH-1:0] user_oe_i,
|
||||
input logic [DATA_WIDTH-1:0] user_do_i,
|
||||
input logic [DATA_WIDTH-1:0] native_oe_i,
|
||||
input logic [DATA_WIDTH-1:0] native_do_i,
|
||||
input logic [DATA_WIDTH-1:0] gpio_oe_i,
|
||||
input logic [DATA_WIDTH-1:0] gpio_do_i
|
||||
);
|
||||
|
||||
// A changing owner first drives output-enable low for a full handoff cycle.
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
gpio_oe_i == ((~user_handoff_i & user_sel_i & user_oe_i) |
|
||||
(~user_handoff_i & ~user_sel_i & native_oe_i)));
|
||||
assert property (@(posedge clk_i) disable iff (!rst_n_i)
|
||||
gpio_do_i == ((user_sel_i & user_do_i) | (~user_sel_i & native_do_i)));
|
||||
|
||||
endmodule
|
||||
|
||||
bind nmi_gpio soc_gpio_user_handoff_sva #(
|
||||
.DATA_WIDTH(`NMI_GPIO_NUM)
|
||||
) u_soc_gpio_user_handoff_sva (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.user_sel_i (s_gpio_user_sel_q),
|
||||
.user_handoff_i(s_gpio_user_handoff_q),
|
||||
.user_oe_i (user_gpio.oe_o),
|
||||
.user_do_i (user_gpio.do_o),
|
||||
.native_oe_i (s_gpio_native_oe),
|
||||
.native_do_i (s_gpio_native_out),
|
||||
.gpio_oe_i (gpio.oe_o),
|
||||
.gpio_do_i (gpio.do_o)
|
||||
);
|
||||
|
||||
`include "soc_irq_sva.svh"
|
||||
`endif
|
||||
|
||||
`endif
|
||||
@@ -28,8 +28,6 @@ module retrosoc_tb;
|
||||
`ifdef HAVE_PLL
|
||||
reg r_xtal_clk;
|
||||
`endif
|
||||
reg [ 4:0] r_core_sel;
|
||||
wire [ 4:0] s_core_sel;
|
||||
wire s_uart0_tx;
|
||||
// for handle x-prop issue
|
||||
wire s_uart0_rx = 1'b1;
|
||||
@@ -82,10 +80,9 @@ module retrosoc_tb;
|
||||
always #(1000 / AUD_CPU_FREQ / 2) r_aud_clk = (r_aud_clk === 1'b0);
|
||||
|
||||
// connect inout pad
|
||||
assign s_ext_clk = r_ext_clk;
|
||||
assign s_aud_clk = r_aud_clk;
|
||||
assign s_rst_n = r_rst_n;
|
||||
assign s_core_sel = r_core_sel;
|
||||
assign s_ext_clk = r_ext_clk;
|
||||
assign s_aud_clk = r_aud_clk;
|
||||
assign s_rst_n = r_rst_n;
|
||||
|
||||
retrosoc_asic u_retrosoc_asic (
|
||||
`include "retrosoc_asic_tb_bindings.svh"
|
||||
@@ -268,11 +265,6 @@ module retrosoc_tb;
|
||||
|
||||
initial begin
|
||||
$display("========================================================");
|
||||
`ifdef CORE_MDD
|
||||
if (!$value$plusargs("core_sel=%d", r_core_sel)) r_core_sel = 5'd0;
|
||||
$display("core_sel: %0d", r_core_sel);
|
||||
`endif
|
||||
|
||||
`ifdef HAVE_PLL
|
||||
$display("pll clock control: sysctrl");
|
||||
`else
|
||||
|
||||
@@ -4,30 +4,27 @@
|
||||
|
||||
static int signal_received = 0;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
if (signal_received != 0)
|
||||
{
|
||||
void sig_handler(int signo) {
|
||||
if (signal_received != 0) {
|
||||
std::cout << "SIGINT received, forcely shutting down." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "\nSIGINT received, gracefully shutting down... Type Ctrl+C again to stop forcely." << std::endl;
|
||||
std::cout << "\nSIGINT received, gracefully shutting down... Type Ctrl+C again to stop forcely."
|
||||
<< std::endl;
|
||||
signal_received = signo;
|
||||
}
|
||||
|
||||
void env_init()
|
||||
{
|
||||
std::cout << rang::fg::magenta << "Emulator compiled at " << __DATE__ << ", " << __TIME__ << rang::fg::reset << std::endl;
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR)
|
||||
{
|
||||
void env_init() {
|
||||
std::cout << rang::fg::magenta << "Emulator compiled at " << __DATE__ << ", " << __TIME__
|
||||
<< rang::fg::reset << std::endl;
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR) {
|
||||
std::cout << "can't catch SIGINT" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void flash_init(const char *img);
|
||||
|
||||
Emulator::Emulator(cxxopts::ParseResult &res)
|
||||
{
|
||||
Emulator::Emulator(cxxopts::ParseResult &res) {
|
||||
args.dumpWave = res["dump-wave"].as<bool>();
|
||||
args.dumpBegin = res["log-begin"].as<unsigned long>();
|
||||
auto tmp = res["log-end"].as<unsigned long>();
|
||||
@@ -41,104 +38,101 @@ Emulator::Emulator(cxxopts::ParseResult &res)
|
||||
args.image = res["image"].as<std::string>();
|
||||
|
||||
startTime = chrono::system_clock::now();
|
||||
if (args.image == "")
|
||||
{
|
||||
std::cout << rang::fg::red << "Image file unspecified. Use -i to provide the image of flash" << rang::fg::reset << std::endl;
|
||||
if (args.image == "") {
|
||||
std::cout << rang::fg::red << "Image file unspecified. Use -i to provide the image of flash"
|
||||
<< rang::fg::reset << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::cout << rang::fg::green << "Initializing flash with " << args.image << " ..." << rang::fg::reset << std::endl;
|
||||
std::cout << rang::fg::green << "Initializing flash with " << args.image << " ..."
|
||||
<< rang::fg::reset << std::endl;
|
||||
flash_init(args.image.c_str());
|
||||
|
||||
dutPtr = new Vretrosoc_top;
|
||||
dutPtr->core_sel_i = res["core-sel"].as<unsigned long>();;
|
||||
reset();
|
||||
|
||||
if (args.dumpWave)
|
||||
{
|
||||
if (args.dumpWave) {
|
||||
#ifdef DUMP_WAVE_FST
|
||||
wavePtr = new VerilatedFstC;
|
||||
#endif
|
||||
Verilated::traceEverOn(true);
|
||||
std::cout << rang::fg::yellow << "`dump-wave` enabled, waves will be written to \"soc.wave\"." << rang::fg::reset << std::endl;
|
||||
std::cout << rang::fg::yellow
|
||||
<< "`dump-wave` enabled, waves will be written to \"soc.wave\"."
|
||||
<< rang::fg::reset << std::endl;
|
||||
dutPtr->trace(wavePtr, 1);
|
||||
wavePtr->open("soc.wave");
|
||||
wavePtr->dump(0);
|
||||
}
|
||||
}
|
||||
|
||||
Emulator::~Emulator()
|
||||
{
|
||||
if (wavePtr)
|
||||
{
|
||||
Emulator::~Emulator() {
|
||||
if (wavePtr) {
|
||||
wavePtr->close();
|
||||
delete wavePtr;
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::wave()
|
||||
{
|
||||
void Emulator::wave() {
|
||||
++cycle;
|
||||
if (args.dumpWave && args.dumpBegin <= cycle && cycle <= args.dumpEnd)
|
||||
{
|
||||
if (args.dumpWave && args.dumpBegin <= cycle && cycle <= args.dumpEnd) {
|
||||
wavePtr->dump((vluint64_t)cycle);
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::reset()
|
||||
{
|
||||
std::cout << rang::fg::yellow << "Initializing and resetting DUT ..." << rang::fg::reset << std::endl;
|
||||
void Emulator::reset() {
|
||||
std::cout << rang::fg::yellow << "Initializing and resetting DUT ..." << rang::fg::reset
|
||||
<< std::endl;
|
||||
dutPtr->rst_n_i = 1;
|
||||
dutPtr->ext_clk_i = 0;
|
||||
dutPtr->eval();
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " << static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " <<
|
||||
// static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dutPtr->ext_clk_i = !dutPtr->ext_clk_i;
|
||||
dutPtr->eval();
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " << static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " <<
|
||||
// static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
}
|
||||
|
||||
dutPtr->rst_n_i = 0;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dutPtr->ext_clk_i = !dutPtr->ext_clk_i;
|
||||
dutPtr->eval();
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " << static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " <<
|
||||
// static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
}
|
||||
|
||||
dutPtr->rst_n_i = 1;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
for (int i = 0; i < 5; i++) {
|
||||
dutPtr->ext_clk_i = !dutPtr->ext_clk_i;
|
||||
dutPtr->eval();
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " << static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " <<
|
||||
// static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << rang::fg::yellow << "Initializing and resetting DUT done" << rang::fg::reset << std::endl;
|
||||
std::cout << rang::fg::yellow << "Initializing and resetting DUT done" << rang::fg::reset
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void Emulator::step()
|
||||
{
|
||||
void Emulator::step() {
|
||||
dutPtr->ext_clk_i = !dutPtr->ext_clk_i;
|
||||
dutPtr->eval();
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " << static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
// std::cout << "rst_n_i: " << static_cast<unsigned>(dutPtr->rst_n_i) << " ext_clk_i: " <<
|
||||
// static_cast<unsigned>(dutPtr->ext_clk_i) << std::endl;
|
||||
++cycle;
|
||||
if (args.dumpWave && args.dumpBegin <= cycle && cycle <= args.dumpEnd)
|
||||
{
|
||||
if (args.dumpWave && args.dumpBegin <= cycle && cycle <= args.dumpEnd) {
|
||||
wavePtr->dump((vluint64_t)cycle);
|
||||
}
|
||||
}
|
||||
|
||||
void Emulator::state()
|
||||
{
|
||||
void Emulator::state() {
|
||||
auto elapsed = chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - startTime);
|
||||
std::cout << rang::fg::yellow << "Simulation " << cycle << " cycles in " << elapsed.count() << "s" << rang::fg::reset << std::endl;
|
||||
std::cout << rang::fg::yellow << "Simulation " << cycle << " cycles in " << elapsed.count()
|
||||
<< "s" << rang::fg::reset << std::endl;
|
||||
}
|
||||
|
||||
bool Emulator::getArriveTime()
|
||||
{
|
||||
bool Emulator::getArriveTime() {
|
||||
auto elapsed = chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - startTime);
|
||||
if (elapsed.count() > args.simTime)
|
||||
return true;
|
||||
@@ -146,11 +140,9 @@ bool Emulator::getArriveTime()
|
||||
return false;
|
||||
}
|
||||
|
||||
void Emulator::runSim()
|
||||
{
|
||||
void Emulator::runSim() {
|
||||
std::cout << rang::fg::yellow << "Running DUT simulation..." << rang::fg::reset << std::endl;
|
||||
while (!Verilated::gotFinish() && signal_received == 0 && !getArriveTime())
|
||||
{
|
||||
while (!Verilated::gotFinish() && signal_received == 0 && !getArriveTime()) {
|
||||
step();
|
||||
}
|
||||
dutPtr->final();
|
||||
|
||||
@@ -10,24 +10,20 @@
|
||||
static Emulator *emu = nullptr;
|
||||
cxxopts::Options args("emu", "retroSoC Verilator Emulator");
|
||||
|
||||
void release()
|
||||
{
|
||||
if (emu != nullptr)
|
||||
{
|
||||
void release() {
|
||||
if (emu != nullptr) {
|
||||
emu->state();
|
||||
delete emu;
|
||||
}
|
||||
}
|
||||
|
||||
void parseArgs(int argc, char *argv[])
|
||||
{
|
||||
void parseArgs(int argc, char *argv[]) {
|
||||
// clang-format off
|
||||
args.add_options()
|
||||
("h, help", "print usage")
|
||||
("d, dump-wave", "dump FST format waveform when log is enabled", cxxopts::value<bool>()->default_value("false"))
|
||||
("b, log-begin", "display log from [NUM] th cycle", cxxopts::value<unsigned long>()->default_value("0"))
|
||||
("e, log-end", "stop display log at [NUM] th cycle", cxxopts::value<unsigned long>()->default_value("0"))
|
||||
("s, core-sel", "select id [NUM] core", cxxopts::value<unsigned long>()->default_value("0"))
|
||||
("t, sim-time", "stop simulation after [NUM] seconds", cxxopts::value<unsigned long>()->default_value("0"))
|
||||
("i, image", "run with this image file", cxxopts::value<std::string>());
|
||||
// clang-format on
|
||||
@@ -35,21 +31,20 @@ void parseArgs(int argc, char *argv[])
|
||||
}
|
||||
|
||||
extern void env_init();
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int main(int argc, char *argv[]) {
|
||||
env_init();
|
||||
atexit(release);
|
||||
parseArgs(argc, argv);
|
||||
auto res = args.parse(argc, argv);
|
||||
if (res.count("help"))
|
||||
{
|
||||
if (res.count("help")) {
|
||||
std::cout << args.help() << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
emu = new Emulator(res);
|
||||
std::cout << rang::fg::yellow << "[verilator]start simulating ..." << rang::fg::reset << std::endl;
|
||||
std::cout << rang::fg::yellow << "[verilator]start simulating ..." << rang::fg::reset
|
||||
<< std::endl;
|
||||
emu->runSim();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -21,31 +21,27 @@
|
||||
`timescale 1 ns / 1 ps
|
||||
|
||||
module retrosoc_top (
|
||||
input wire ext_clk_i,
|
||||
input wire rst_n_i,
|
||||
input wire [4:0] core_sel_i
|
||||
input wire ext_clk_i,
|
||||
input wire rst_n_i
|
||||
);
|
||||
|
||||
wire s_clk;
|
||||
wire s_rst_n;
|
||||
wire [4:0] s_core_sel;
|
||||
wire s_psram_sck;
|
||||
wire s_psram_nss0;
|
||||
wire s_psram_dat0;
|
||||
wire s_psram_dat1;
|
||||
wire s_psram_dat2;
|
||||
wire s_psram_dat3;
|
||||
wire s_xpi_nss0_o;
|
||||
wire s_xpi_sck_o;
|
||||
wire s_xpi_dat0_io;
|
||||
wire s_xpi_dat1_io;
|
||||
wire s_xpi_dat2_io;
|
||||
wire s_xpi_dat3_io;
|
||||
|
||||
assign s_clk = ext_clk_i;
|
||||
assign s_rst_n = rst_n_i;
|
||||
assign s_core_sel = core_sel_i;
|
||||
wire s_clk;
|
||||
wire s_rst_n;
|
||||
wire s_psram_sck;
|
||||
wire s_psram_nss0;
|
||||
wire s_psram_dat0;
|
||||
wire s_psram_dat1;
|
||||
wire s_psram_dat2;
|
||||
wire s_psram_dat3;
|
||||
wire s_xpi_nss0_o;
|
||||
wire s_xpi_sck_o;
|
||||
wire s_xpi_dat0_io;
|
||||
wire s_xpi_dat1_io;
|
||||
wire s_xpi_dat2_io;
|
||||
wire s_xpi_dat3_io;
|
||||
|
||||
assign s_clk = ext_clk_i;
|
||||
assign s_rst_n = rst_n_i;
|
||||
retrosoc_asic u_retrosoc_asic (
|
||||
`include "retrosoc_asic_verilator_bindings.svh"
|
||||
);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/tech/tc_io.sv
|
||||
/tech/gf180_io_cells_blackbox.sv
|
||||
/tech/sky130_io_cells_blackbox.sv
|
||||
/tech/tc_clk.sv
|
||||
/tech/tc_pll.sv
|
||||
/tech/tc_sram.sv
|
||||
@@ -11,7 +11,7 @@
|
||||
/top/mgmt_core_wrapper.sv
|
||||
/top/user_core_design_template.sv
|
||||
/top/user_core_top.sv
|
||||
/top/user_core_wrapper.sv
|
||||
/top/user_ip_design_template.sv
|
||||
/top/user_ip_top.sv
|
||||
/top/user_ip_wrapper.sv
|
||||
/sva/soc_protocol_sva.sv
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module bus_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic mgmt_valid,
|
||||
output logic [31:0] mgmt_addr,
|
||||
output logic [ 3:0] mgmt_wstrb,
|
||||
output logic mgmt_ready,
|
||||
output logic user_valid,
|
||||
output logic [31:0] user_addr,
|
||||
output logic [ 3:0] user_wstrb,
|
||||
output logic user_ready,
|
||||
output logic dma_valid,
|
||||
output logic dma_ready,
|
||||
output logic natv_valid,
|
||||
output logic apb_valid,
|
||||
output logic fault_valid,
|
||||
output logic fault_access,
|
||||
output logic arb_locked,
|
||||
output logic [ 1:0] arb_owner
|
||||
);
|
||||
|
||||
nmi_if mgmt_nmi ();
|
||||
nmi_if user_nmi ();
|
||||
nmi_if dma_nmi ();
|
||||
nmi_if natv_nmi ();
|
||||
nmi_if apb_nmi ();
|
||||
|
||||
(* anyseq *)logic f_mgmt_valid;
|
||||
(* anyseq *)logic [31:0] f_mgmt_addr;
|
||||
(* anyseq *)logic [31:0] f_mgmt_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_mgmt_wstrb;
|
||||
(* anyseq *)logic f_user_valid;
|
||||
(* anyseq *)logic [31:0] f_user_addr;
|
||||
(* anyseq *)logic [31:0] f_user_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_user_wstrb;
|
||||
(* anyseq *)logic f_dma_valid;
|
||||
(* anyseq *)logic [31:0] f_dma_addr;
|
||||
(* anyseq *)logic [31:0] f_dma_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_dma_wstrb;
|
||||
(* anyseq *)logic f_natv_ready;
|
||||
(* anyseq *)logic [31:0] f_natv_rdata;
|
||||
(* anyseq *)logic f_apb_ready;
|
||||
(* anyseq *)logic [31:0] f_apb_rdata;
|
||||
logic [31:0] fault_addr;
|
||||
logic [ 3:0] fault_wstrb;
|
||||
logic fault_reserved;
|
||||
logic [ 1:0] fault_master;
|
||||
logic user_idle;
|
||||
|
||||
assign mgmt_nmi.valid = f_mgmt_valid;
|
||||
assign mgmt_nmi.addr = f_mgmt_addr;
|
||||
assign mgmt_nmi.wdata = f_mgmt_wdata;
|
||||
assign mgmt_nmi.wstrb = f_mgmt_wstrb;
|
||||
assign user_nmi.valid = f_user_valid;
|
||||
assign user_nmi.addr = f_user_addr;
|
||||
assign user_nmi.wdata = f_user_wdata;
|
||||
assign user_nmi.wstrb = f_user_wstrb;
|
||||
assign dma_nmi.valid = f_dma_valid;
|
||||
assign dma_nmi.addr = f_dma_addr;
|
||||
assign dma_nmi.wdata = f_dma_wdata;
|
||||
assign dma_nmi.wstrb = f_dma_wstrb;
|
||||
assign natv_nmi.ready = f_natv_ready;
|
||||
assign natv_nmi.rdata = f_natv_rdata;
|
||||
assign apb_nmi.ready = f_apb_ready;
|
||||
assign apb_nmi.rdata = f_apb_rdata;
|
||||
|
||||
assign mgmt_valid = mgmt_nmi.valid;
|
||||
assign mgmt_addr = mgmt_nmi.addr;
|
||||
assign mgmt_wstrb = mgmt_nmi.wstrb;
|
||||
assign mgmt_ready = mgmt_nmi.ready;
|
||||
assign user_valid = user_nmi.valid;
|
||||
assign user_addr = user_nmi.addr;
|
||||
assign user_wstrb = user_nmi.wstrb;
|
||||
assign user_ready = user_nmi.ready;
|
||||
assign dma_valid = dma_nmi.valid;
|
||||
assign dma_ready = dma_nmi.ready;
|
||||
assign natv_valid = natv_nmi.valid;
|
||||
assign apb_valid = apb_nmi.valid;
|
||||
|
||||
bus u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.mgmt_nmi (mgmt_nmi),
|
||||
.user_nmi (user_nmi),
|
||||
.dma_nmi (dma_nmi),
|
||||
.user_bus_enable_i(1'b1),
|
||||
.user_bus_idle_o (user_idle),
|
||||
.natv_nmi (natv_nmi),
|
||||
.apb_nmi (apb_nmi),
|
||||
.fault_valid_o (fault_valid),
|
||||
.fault_addr_o (fault_addr),
|
||||
.fault_wstrb_o (fault_wstrb),
|
||||
.fault_reserved_o (fault_reserved),
|
||||
.fault_access_o (fault_access),
|
||||
.fault_master_o (fault_master)
|
||||
);
|
||||
|
||||
assign arb_locked = u_dut.s_mstr_lock_q;
|
||||
assign arb_owner = u_dut.s_mstr_id_q;
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module bus_formal;
|
||||
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire mgmt_valid;
|
||||
wire [31:0] mgmt_addr;
|
||||
wire [ 3:0] mgmt_wstrb;
|
||||
wire mgmt_ready;
|
||||
wire user_valid;
|
||||
wire [31:0] user_addr;
|
||||
wire [ 3:0] user_wstrb;
|
||||
wire user_ready;
|
||||
wire dma_valid;
|
||||
wire dma_ready;
|
||||
wire natv_valid;
|
||||
wire apb_valid;
|
||||
wire fault_valid;
|
||||
wire fault_access;
|
||||
wire arb_locked;
|
||||
wire [ 1:0] arb_owner;
|
||||
|
||||
bus_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.f_past_valid(f_past_valid),
|
||||
.mgmt_valid (mgmt_valid),
|
||||
.mgmt_addr (mgmt_addr),
|
||||
.mgmt_wstrb (mgmt_wstrb),
|
||||
.mgmt_ready (mgmt_ready),
|
||||
.user_valid (user_valid),
|
||||
.user_addr (user_addr),
|
||||
.user_wstrb (user_wstrb),
|
||||
.user_ready (user_ready),
|
||||
.dma_valid (dma_valid),
|
||||
.dma_ready (dma_ready),
|
||||
.natv_valid (natv_valid),
|
||||
.apb_valid (apb_valid),
|
||||
.fault_valid (fault_valid),
|
||||
.fault_access(fault_access),
|
||||
.arb_locked (arb_locked),
|
||||
.arb_owner (arb_owner)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (f_past_valid && $past(rst_n_i && mgmt_valid && !mgmt_ready)) begin
|
||||
assume (mgmt_valid);
|
||||
assume (mgmt_addr == $past(mgmt_addr));
|
||||
assume (mgmt_wstrb == $past(mgmt_wstrb));
|
||||
end
|
||||
if (f_past_valid && $past(rst_n_i && user_valid && !user_ready)) begin
|
||||
assume (user_valid);
|
||||
assume (user_addr == $past(user_addr));
|
||||
assume (user_wstrb == $past(user_wstrb));
|
||||
end
|
||||
if (rst_n_i) begin
|
||||
assert (!(natv_valid && apb_valid));
|
||||
assert (!(fault_valid && (natv_valid || apb_valid)));
|
||||
if (fault_access) begin
|
||||
assert (fault_valid);
|
||||
end
|
||||
if (f_past_valid && $past(
|
||||
rst_n_i && !arb_locked && mgmt_valid && !user_valid && !dma_valid
|
||||
)) begin
|
||||
assert (arb_locked && arb_owner == 2'd0);
|
||||
end
|
||||
cover (arb_locked && arb_owner == 2'd1);
|
||||
cover (fault_access);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Collect formal flow result records into one variant-level verdict."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
|
||||
STEPS = ("sv2v", "prove", "cover")
|
||||
|
||||
|
||||
def read_result(path: Path) -> dict[str, object]:
|
||||
try:
|
||||
result = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as error:
|
||||
raise RuntimeError(f"cannot read formal result {path}: {error}") from error
|
||||
if result.get("status") != "passed":
|
||||
raise RuntimeError(f"formal step did not pass: {path}")
|
||||
return result
|
||||
|
||||
|
||||
def read_sby_status(path: Path) -> str:
|
||||
try:
|
||||
fields = path.read_text(encoding="utf-8").split()
|
||||
except OSError as error:
|
||||
raise RuntimeError(f"cannot read SymbiYosys status {path}: {error}") from error
|
||||
status = fields[0] if fields else ""
|
||||
if status != "PASS":
|
||||
raise RuntimeError(f"SymbiYosys task did not pass: {path} ({status or 'missing'})")
|
||||
return status
|
||||
|
||||
|
||||
def parse_proof(value: str) -> tuple[str, Path]:
|
||||
name, separator, directory = value.partition("=")
|
||||
if not separator or not name or not directory:
|
||||
raise argparse.ArgumentTypeError("proof must use NAME=DIRECTORY")
|
||||
return name, Path(directory)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--proof", type=parse_proof, action="append", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
proofs: dict[str, dict[str, object]] = {}
|
||||
for name, directory in args.proof:
|
||||
resolved = directory.resolve()
|
||||
proof: dict[str, object] = {"sv2v": read_result(resolved / "result-sv2v.json")}
|
||||
for step in ("prove", "cover"):
|
||||
proof[step] = {
|
||||
"result": read_result(resolved / f"result-{step}.json"),
|
||||
"sby_status": read_sby_status(resolved / step / "status"),
|
||||
}
|
||||
proofs[name] = proof
|
||||
atomic_write(
|
||||
args.output.resolve(),
|
||||
json.dumps(
|
||||
{"schema_version": 2, "status": "passed", "proofs": proofs}, indent=2, sort_keys=True
|
||||
)
|
||||
+ "\n",
|
||||
)
|
||||
print(f"formal results: {args.output.resolve()}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the narrow formal source list for one Mini SoC protocol proof."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
ROOT = SCRIPT_DIR.parents[2]
|
||||
FILELIST_DIR = ROOT / "rtl/mini/script"
|
||||
sys.path.insert(0, str(FILELIST_DIR))
|
||||
|
||||
from filelist import FileList, write_filelist # noqa: E402
|
||||
|
||||
|
||||
COMMON_RTL = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
INTERCONNECT = ROOT / "rtl/ip/native/interconnect"
|
||||
PERIPHERAL = ROOT / "rtl/ip/native/peripheral"
|
||||
TOP = ROOT / "rtl/mini/top"
|
||||
|
||||
|
||||
def target_defines(target: str) -> list[str]:
|
||||
return ["+define+SV_ASSRT_DISABLE"]
|
||||
|
||||
|
||||
def source_files(target: str) -> list[Path]:
|
||||
common = [
|
||||
COMMON_RTL / "interface/nmi_if.sv",
|
||||
COMMON_RTL / "utils/register.sv",
|
||||
]
|
||||
if target == "bus":
|
||||
return [
|
||||
*common,
|
||||
INTERCONNECT / "nmi_regslice.sv",
|
||||
TOP / "bus.sv",
|
||||
SCRIPT_DIR / "bus_formal.sv",
|
||||
]
|
||||
if target == "nmi2apb":
|
||||
return [
|
||||
COMMON_RTL / "interface/nmi_if.sv",
|
||||
COMMON_RTL / "interface/apb4_pure_if.sv",
|
||||
COMMON_RTL / "utils/register.sv",
|
||||
COMMON_RTL / "utils/edge_det.sv",
|
||||
INTERCONNECT / "nmi2apb.sv",
|
||||
SCRIPT_DIR / "nmi2apb_formal.sv",
|
||||
]
|
||||
if target == "sysctrl":
|
||||
return [
|
||||
*common,
|
||||
PERIPHERAL / "pll_ctrl_if.sv",
|
||||
PERIPHERAL / "sysctrl.sv",
|
||||
SCRIPT_DIR / "sysctrl_formal.sv",
|
||||
]
|
||||
if target == "pll_rcu":
|
||||
return [
|
||||
*common,
|
||||
COMMON_RTL / "cdc/cdc_sync.sv",
|
||||
COMMON_RTL / "cdc/cdc_2phase.sv",
|
||||
PERIPHERAL / "pll_ctrl_if.sv",
|
||||
TOP / "rcu.sv",
|
||||
SCRIPT_DIR / "pll_rcu_formal.sv",
|
||||
]
|
||||
if target == "gpio_user":
|
||||
return [
|
||||
*common,
|
||||
COMMON_RTL / "cdc/cdc_sync.sv",
|
||||
COMMON_RTL / "utils/edge_det.sv",
|
||||
PERIPHERAL / "gpio.sv",
|
||||
SCRIPT_DIR / "gpio_user_formal.sv",
|
||||
]
|
||||
raise ValueError(f"unsupported formal target: {target}")
|
||||
|
||||
|
||||
def generate(
|
||||
target: str,
|
||||
output: Path,
|
||||
memory_map_dir: Path,
|
||||
soc_topology_dir: Path,
|
||||
user_extensions_dir: Path,
|
||||
) -> bool:
|
||||
filelist = FileList(
|
||||
defines=target_defines(target),
|
||||
incdirs=[
|
||||
memory_map_dir.resolve() / "rtl",
|
||||
soc_topology_dir.resolve() / "rtl",
|
||||
user_extensions_dir.resolve() / "rtl",
|
||||
TOP,
|
||||
COMMON_RTL,
|
||||
COMMON_RTL / "interface",
|
||||
COMMON_RTL / "utils",
|
||||
INTERCONNECT,
|
||||
PERIPHERAL,
|
||||
],
|
||||
files=source_files(target),
|
||||
).deduplicate()
|
||||
return write_filelist(output.resolve(), filelist)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--target",
|
||||
choices=("bus", "nmi2apb", "sysctrl", "pll_rcu", "gpio_user"),
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--memory-map-dir", type=Path, required=True)
|
||||
parser.add_argument("--soc-topology-dir", type=Path, required=True)
|
||||
parser.add_argument("--user-extensions-dir", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
changed = generate(
|
||||
args.target,
|
||||
args.output,
|
||||
args.memory_map_dir,
|
||||
args.soc_topology_dir,
|
||||
args.user_extensions_dir,
|
||||
)
|
||||
print(f"formal filelist {'updated' if changed else 'unchanged'}: {args.output.resolve()}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a deterministic SymbiYosys configuration for one protocol proof."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
|
||||
def render(top: str, design: Path, properties: Path, solver: str, mode: str, depth: int) -> str:
|
||||
return "\n".join(
|
||||
(
|
||||
"[options]",
|
||||
f"mode {mode}",
|
||||
f"depth {depth}",
|
||||
"",
|
||||
"[engines]",
|
||||
f"smtbmc --presat --nounroll {solver}",
|
||||
"",
|
||||
"[script]",
|
||||
"read_verilog -formal -sv design.v",
|
||||
"read_verilog -formal properties.v",
|
||||
f"prep -top {top}",
|
||||
"async2sync",
|
||||
"dffunmap",
|
||||
"opt_clean",
|
||||
"",
|
||||
"[files]",
|
||||
f"design.v {design.resolve()}",
|
||||
f"properties.v {properties.resolve()}",
|
||||
"",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def positive_int(value: str) -> int:
|
||||
parsed = int(value)
|
||||
if parsed <= 0:
|
||||
raise argparse.ArgumentTypeError("value must be positive")
|
||||
return parsed
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--top", required=True)
|
||||
parser.add_argument("--input", type=Path, required=True)
|
||||
parser.add_argument("--properties", type=Path, required=True)
|
||||
parser.add_argument("--solver", required=True)
|
||||
parser.add_argument("--mode", choices=("prove", "cover"), required=True)
|
||||
parser.add_argument("--depth", type=positive_int, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
atomic_write(
|
||||
args.output.resolve(),
|
||||
render(args.top, args.input, args.properties, args.solver, args.mode, args.depth),
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module gpio_user_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic nmi_valid,
|
||||
output logic [31:0] nmi_addr,
|
||||
output logic [31:0] nmi_wdata,
|
||||
output logic [ 3:0] nmi_wstrb,
|
||||
output logic nmi_ready,
|
||||
output logic [31:0] gpio_di,
|
||||
output logic [31:0] gpio_oe,
|
||||
output logic [31:0] gpio_do,
|
||||
output logic [31:0] user_di,
|
||||
output logic [31:0] user_oe,
|
||||
output logic [31:0] user_do,
|
||||
output logic [31:0] user_sel,
|
||||
output logic [31:0] user_lock,
|
||||
output logic [31:0] user_handoff,
|
||||
output logic [31:0] user_status,
|
||||
output logic [31:0] native_oe,
|
||||
output logic [31:0] native_do
|
||||
);
|
||||
|
||||
nmi_if nmi ();
|
||||
gpio_if gpio ();
|
||||
user_gpio_if user_gpio ();
|
||||
|
||||
(* anyseq *)logic f_nmi_valid;
|
||||
(* anyseq *)logic [31:0] f_nmi_addr;
|
||||
(* anyseq *)logic [31:0] f_nmi_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_nmi_wstrb;
|
||||
(* anyseq *)logic [31:0] f_gpio_di;
|
||||
(* anyseq *)logic [31:0] f_alt0_do;
|
||||
(* anyseq *)logic [31:0] f_alt0_oe;
|
||||
(* anyseq *)logic [31:0] f_alt1_do;
|
||||
(* anyseq *)logic [31:0] f_alt1_oe;
|
||||
(* anyseq *)logic [31:0] f_user_do;
|
||||
(* anyseq *)logic [31:0] f_user_oe;
|
||||
|
||||
assign nmi.valid = f_nmi_valid;
|
||||
assign nmi.addr = f_nmi_addr;
|
||||
assign nmi.wdata = f_nmi_wdata;
|
||||
assign nmi.wstrb = f_nmi_wstrb;
|
||||
assign gpio.di_i = f_gpio_di;
|
||||
assign gpio.alt0_do_i = f_alt0_do;
|
||||
assign gpio.alt0_oe_i = f_alt0_oe;
|
||||
assign gpio.alt1_do_i = f_alt1_do;
|
||||
assign gpio.alt1_oe_i = f_alt1_oe;
|
||||
assign user_gpio.do_o = f_user_do;
|
||||
assign user_gpio.oe_o = f_user_oe;
|
||||
|
||||
assign nmi_valid = nmi.valid;
|
||||
assign nmi_addr = nmi.addr;
|
||||
assign nmi_wdata = nmi.wdata;
|
||||
assign nmi_wstrb = nmi.wstrb;
|
||||
assign nmi_ready = nmi.ready;
|
||||
assign gpio_di = gpio.di_i;
|
||||
assign gpio_oe = gpio.oe_o;
|
||||
assign gpio_do = gpio.do_o;
|
||||
assign user_di = user_gpio.di_i;
|
||||
assign user_oe = user_gpio.oe_o;
|
||||
assign user_do = user_gpio.do_o;
|
||||
assign user_sel = u_dut.s_gpio_user_sel_q;
|
||||
assign user_lock = u_dut.s_gpio_user_lock_q;
|
||||
assign user_handoff = u_dut.s_gpio_user_handoff_q;
|
||||
assign user_status = u_dut.s_gpio_user_status;
|
||||
assign native_oe = u_dut.s_gpio_native_oe;
|
||||
assign native_do = u_dut.s_gpio_native_out;
|
||||
|
||||
nmi_gpio u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.nmi (nmi),
|
||||
.gpio (gpio),
|
||||
.user_gpio(user_gpio)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module gpio_user_formal;
|
||||
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire nmi_valid;
|
||||
wire [31:0] nmi_addr;
|
||||
wire [31:0] nmi_wdata;
|
||||
wire [ 3:0] nmi_wstrb;
|
||||
wire nmi_ready;
|
||||
wire [31:0] gpio_di;
|
||||
wire [31:0] gpio_oe;
|
||||
wire [31:0] gpio_do;
|
||||
wire [31:0] user_di;
|
||||
wire [31:0] user_oe;
|
||||
wire [31:0] user_do;
|
||||
wire [31:0] user_sel;
|
||||
wire [31:0] user_lock;
|
||||
wire [31:0] user_handoff;
|
||||
wire [31:0] user_status;
|
||||
wire [31:0] native_oe;
|
||||
wire [31:0] native_do;
|
||||
|
||||
gpio_user_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.f_past_valid(f_past_valid),
|
||||
.nmi_valid (nmi_valid),
|
||||
.nmi_addr (nmi_addr),
|
||||
.nmi_wdata (nmi_wdata),
|
||||
.nmi_wstrb (nmi_wstrb),
|
||||
.nmi_ready (nmi_ready),
|
||||
.gpio_di (gpio_di),
|
||||
.gpio_oe (gpio_oe),
|
||||
.gpio_do (gpio_do),
|
||||
.user_di (user_di),
|
||||
.user_oe (user_oe),
|
||||
.user_do (user_do),
|
||||
.user_sel (user_sel),
|
||||
.user_lock (user_lock),
|
||||
.user_handoff(user_handoff),
|
||||
.user_status (user_status),
|
||||
.native_oe (native_oe),
|
||||
.native_do (native_do)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (f_past_valid && $past(rst_n_i && nmi_valid && !nmi_ready)) begin
|
||||
assume (nmi_valid);
|
||||
assume (nmi_addr == $past(nmi_addr));
|
||||
assume (nmi_wdata == $past(nmi_wdata));
|
||||
assume (nmi_wstrb == $past(nmi_wstrb));
|
||||
end
|
||||
|
||||
if (rst_n_i) begin
|
||||
assert (user_di == gpio_di);
|
||||
assert(gpio_oe == ((~user_handoff & user_sel & user_oe) |
|
||||
(~user_handoff & ~user_sel & native_oe)));
|
||||
assert (gpio_do == ((user_sel & user_do) | (~user_sel & native_do)));
|
||||
assert (user_status == (user_sel & ~user_handoff));
|
||||
if (f_past_valid) begin
|
||||
assert (user_handoff == (user_sel ^ $past(user_sel)));
|
||||
if ($past(user_lock)) begin
|
||||
assert (user_lock);
|
||||
assert ((user_sel & $past(user_lock)) == ($past(user_sel) & $past(user_lock)));
|
||||
end
|
||||
end
|
||||
cover (|user_handoff);
|
||||
cover (|user_lock && |user_sel);
|
||||
cover (|user_status && |gpio_oe);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
module formal_apb_slave (
|
||||
apb4_pure_if.slave apb
|
||||
);
|
||||
|
||||
(* anyseq *)logic f_ready;
|
||||
(* anyseq *)logic [31:0] f_rdata;
|
||||
(* anyseq *)logic f_slverr;
|
||||
|
||||
assign apb.pready = f_ready;
|
||||
assign apb.prdata = f_rdata;
|
||||
assign apb.pslverr = f_slverr;
|
||||
|
||||
endmodule
|
||||
|
||||
module nmi2apb_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic nmi_valid,
|
||||
output logic [31:0] nmi_addr,
|
||||
output logic [31:0] nmi_wdata,
|
||||
output logic [ 3:0] nmi_wstrb,
|
||||
output logic nmi_ready,
|
||||
output logic [ 9:0] psel_comb,
|
||||
output logic [ 9:0] psel_q,
|
||||
output logic xfer_ready,
|
||||
output logic [ 1:0] fsm_q
|
||||
);
|
||||
|
||||
nmi_if nmi ();
|
||||
apb4_pure_if archinfo ();
|
||||
apb4_pure_if rng ();
|
||||
apb4_pure_if uart ();
|
||||
apb4_pure_if pwm ();
|
||||
apb4_pure_if ps2 ();
|
||||
apb4_pure_if rtc ();
|
||||
apb4_pure_if wdg ();
|
||||
apb4_pure_if crc ();
|
||||
apb4_pure_if tmr ();
|
||||
apb4_pure_if user_ip ();
|
||||
|
||||
(* anyseq *)logic f_nmi_valid;
|
||||
(* anyseq *)logic [31:0] f_nmi_addr;
|
||||
(* anyseq *)logic [31:0] f_nmi_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_nmi_wstrb;
|
||||
|
||||
assign nmi.valid = f_nmi_valid;
|
||||
assign nmi.addr = f_nmi_addr;
|
||||
assign nmi.wdata = f_nmi_wdata;
|
||||
assign nmi.wstrb = f_nmi_wstrb;
|
||||
assign nmi_valid = nmi.valid;
|
||||
assign nmi_addr = nmi.addr;
|
||||
assign nmi_wdata = nmi.wdata;
|
||||
assign nmi_wstrb = nmi.wstrb;
|
||||
assign nmi_ready = nmi.ready;
|
||||
|
||||
formal_apb_slave u_archinfo_slave (.apb(archinfo));
|
||||
formal_apb_slave u_rng_slave (.apb(rng));
|
||||
formal_apb_slave u_uart_slave (.apb(uart));
|
||||
formal_apb_slave u_pwm_slave (.apb(pwm));
|
||||
formal_apb_slave u_ps2_slave (.apb(ps2));
|
||||
formal_apb_slave u_rtc_slave (.apb(rtc));
|
||||
formal_apb_slave u_wdg_slave (.apb(wdg));
|
||||
formal_apb_slave u_crc_slave (.apb(crc));
|
||||
formal_apb_slave u_tmr_slave (.apb(tmr));
|
||||
formal_apb_slave u_user_ip_slave (.apb(user_ip));
|
||||
|
||||
nmi2apb u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.nmi (nmi),
|
||||
.archinfo(archinfo),
|
||||
.rng (rng),
|
||||
.uart (uart),
|
||||
.pwm (pwm),
|
||||
.ps2 (ps2),
|
||||
.rtc (rtc),
|
||||
.wdg (wdg),
|
||||
.crc (crc),
|
||||
.tmr (tmr),
|
||||
.user_ip (user_ip)
|
||||
);
|
||||
|
||||
assign psel_comb = u_dut.s_psel_comb;
|
||||
assign psel_q = u_dut.s_psel_q;
|
||||
assign xfer_ready = u_dut.s_xfer_ready;
|
||||
assign fsm_q = u_dut.s_fsm_q;
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
// You may obtain a copy of Mulan PSL v2 at:
|
||||
// http://license.coscl.org.cn/MulanPSL2
|
||||
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the Mulan PSL v2 for more details.
|
||||
|
||||
module nmi2apb_formal;
|
||||
|
||||
localparam [1:0] FSM_SETP = 2'd1;
|
||||
localparam [1:0] FSM_ENAB = 2'd2;
|
||||
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire nmi_valid;
|
||||
wire [31:0] nmi_addr;
|
||||
wire [31:0] nmi_wdata;
|
||||
wire [ 3:0] nmi_wstrb;
|
||||
wire nmi_ready;
|
||||
wire [ 9:0] psel_comb;
|
||||
wire [ 9:0] psel_q;
|
||||
wire xfer_ready;
|
||||
wire [ 1:0] fsm_q;
|
||||
|
||||
nmi2apb_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.f_past_valid(f_past_valid),
|
||||
.nmi_valid (nmi_valid),
|
||||
.nmi_addr (nmi_addr),
|
||||
.nmi_wdata (nmi_wdata),
|
||||
.nmi_wstrb (nmi_wstrb),
|
||||
.nmi_ready (nmi_ready),
|
||||
.psel_comb (psel_comb),
|
||||
.psel_q (psel_q),
|
||||
.xfer_ready (xfer_ready),
|
||||
.fsm_q (fsm_q)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (f_past_valid && $past(rst_n_i && nmi_valid && !nmi_ready)) begin
|
||||
assume (nmi_valid);
|
||||
assume (nmi_addr == $past(nmi_addr));
|
||||
assume (nmi_wdata == $past(nmi_wdata));
|
||||
assume (nmi_wstrb == $past(nmi_wstrb));
|
||||
end
|
||||
|
||||
if (rst_n_i) begin
|
||||
assert ($onehot0(psel_comb));
|
||||
assert ($onehot0(psel_q));
|
||||
if (nmi_ready) begin
|
||||
assert (xfer_ready);
|
||||
assert (fsm_q == FSM_ENAB);
|
||||
end
|
||||
if (f_past_valid && $past(rst_n_i && fsm_q == FSM_SETP)) begin
|
||||
assert (psel_q == $past(psel_q));
|
||||
end
|
||||
if (f_past_valid && $past(rst_n_i && fsm_q == FSM_ENAB && !xfer_ready)) begin
|
||||
assert (fsm_q == FSM_ENAB);
|
||||
assert (psel_q == $past(psel_q));
|
||||
end
|
||||
cover (fsm_q == FSM_ENAB && nmi_ready);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module pll_rcu_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic req_valid,
|
||||
output logic [2:0] req_sel,
|
||||
output logic req_ready,
|
||||
output logic rsp_valid,
|
||||
output logic rsp_ready,
|
||||
output logic pll_lock,
|
||||
output logic pll_capable,
|
||||
output logic [2:0] state,
|
||||
output logic pll_apply,
|
||||
output logic select_ext_clk,
|
||||
output logic active_valid,
|
||||
output logic safe_clk,
|
||||
output logic active_lock,
|
||||
output logic [1:0] error
|
||||
);
|
||||
|
||||
pll_ctrl_if pll_ctrl ();
|
||||
|
||||
(* anyseq *)logic f_req_valid;
|
||||
(* anyseq *)logic [2:0] f_req_sel;
|
||||
(* anyseq *)logic f_rsp_ready;
|
||||
(* anyseq *)logic f_pll_lock;
|
||||
(* anyseq *)logic f_pll_capable;
|
||||
|
||||
assign pll_ctrl.req_valid_o = f_req_valid;
|
||||
assign pll_ctrl.req_sel_o = f_req_sel;
|
||||
assign pll_ctrl.rsp_ready_o = f_rsp_ready;
|
||||
assign req_valid = pll_ctrl.req_valid_o;
|
||||
assign req_sel = pll_ctrl.req_sel_o;
|
||||
assign req_ready = pll_ctrl.req_ready_i;
|
||||
assign rsp_valid = pll_ctrl.rsp_valid_i;
|
||||
assign rsp_ready = pll_ctrl.rsp_ready_o;
|
||||
assign pll_lock = f_pll_lock;
|
||||
assign pll_capable = f_pll_capable;
|
||||
assign state = u_dut.s_state_q;
|
||||
assign pll_apply = u_dut.s_pll_apply_q;
|
||||
assign select_ext_clk = u_dut.s_select_ext_clk_q;
|
||||
assign active_valid = u_dut.s_active_valid_q;
|
||||
assign safe_clk = u_dut.s_safe_clk_q;
|
||||
assign active_lock = u_dut.s_lock_q;
|
||||
assign error = u_dut.s_error_q;
|
||||
|
||||
// Both controller domains share the formal clock. This proves the control
|
||||
// FSM and the production cdc_2phase composition, not asynchronous CDC timing.
|
||||
pll_rcu_controller #(
|
||||
.LOCK_TIMEOUT(4)
|
||||
) u_dut (
|
||||
.sys_clk_i (clk_i),
|
||||
.sys_rst_n_i (rst_n_i),
|
||||
.ext_clk_i (clk_i),
|
||||
.ext_rst_n_i (rst_n_i),
|
||||
.pll_lock_i (f_pll_lock),
|
||||
.pll_capable_i (f_pll_capable),
|
||||
.pll_sel_o (),
|
||||
.pll_apply_o (),
|
||||
.select_ext_clk_o(),
|
||||
.pll_ctrl (pll_ctrl)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module pll_rcu_formal;
|
||||
|
||||
localparam [2:0] PLL_IDLE = 3'd0;
|
||||
localparam [2:0] PLL_SAFE = 3'd1;
|
||||
localparam [2:0] PLL_APPLY = 3'd2;
|
||||
localparam [2:0] PLL_WAIT_LOCK = 3'd3;
|
||||
localparam [2:0] PLL_SWITCH = 3'd4;
|
||||
localparam [2:0] PLL_RESPOND = 3'd5;
|
||||
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire req_valid;
|
||||
wire [2:0] req_sel;
|
||||
wire req_ready;
|
||||
wire rsp_valid;
|
||||
wire rsp_ready;
|
||||
wire pll_lock;
|
||||
wire pll_capable;
|
||||
wire [2:0] state;
|
||||
wire pll_apply;
|
||||
wire select_ext_clk;
|
||||
wire active_valid;
|
||||
wire safe_clk;
|
||||
wire active_lock;
|
||||
wire [1:0] error;
|
||||
|
||||
pll_rcu_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.f_past_valid (f_past_valid),
|
||||
.req_valid (req_valid),
|
||||
.req_sel (req_sel),
|
||||
.req_ready (req_ready),
|
||||
.rsp_valid (rsp_valid),
|
||||
.rsp_ready (rsp_ready),
|
||||
.pll_lock (pll_lock),
|
||||
.pll_capable (pll_capable),
|
||||
.state (state),
|
||||
.pll_apply (pll_apply),
|
||||
.select_ext_clk(select_ext_clk),
|
||||
.active_valid (active_valid),
|
||||
.safe_clk (safe_clk),
|
||||
.active_lock (active_lock),
|
||||
.error (error)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (f_past_valid && $past(rst_n_i && req_valid && !req_ready)) begin
|
||||
assume (req_valid);
|
||||
assume (req_sel == $past(req_sel));
|
||||
end
|
||||
|
||||
if (rst_n_i) begin
|
||||
assert(!(state == PLL_SAFE || state == PLL_APPLY || state == PLL_WAIT_LOCK) ||
|
||||
select_ext_clk);
|
||||
assert (!pll_apply || state != PLL_IDLE && state != PLL_SAFE);
|
||||
if (f_past_valid && $past(state == PLL_SAFE && !pll_capable)) begin
|
||||
assert (state == PLL_RESPOND);
|
||||
assert (safe_clk);
|
||||
assert (!active_valid);
|
||||
assert (error == 2'd1);
|
||||
end
|
||||
if (f_past_valid && $past(state == PLL_WAIT_LOCK && pll_lock)) begin
|
||||
assert (state == PLL_SWITCH);
|
||||
assert (active_valid);
|
||||
assert (!safe_clk);
|
||||
assert (active_lock);
|
||||
assert (error == 2'd0);
|
||||
end
|
||||
if (f_past_valid && $past(state == PLL_SWITCH)) begin
|
||||
assert (state == PLL_RESPOND);
|
||||
assert (!select_ext_clk);
|
||||
end
|
||||
cover (state == PLL_SWITCH && active_valid && active_lock);
|
||||
cover (state == PLL_RESPOND && error == 2'd1);
|
||||
cover (state == PLL_RESPOND && error == 2'd2);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module sysctrl_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic nmi_valid,
|
||||
output logic [31:0] nmi_addr,
|
||||
output logic [31:0] nmi_wdata,
|
||||
output logic [ 3:0] nmi_wstrb,
|
||||
output logic nmi_ready,
|
||||
output logic [ 7:0] ip_sel,
|
||||
output logic [ 4:0] core_sel,
|
||||
output logic [ 5:0] user_reset,
|
||||
output logic user_bus_enable,
|
||||
output logic user_config_error,
|
||||
output logic [ 2:0] pll_cfg,
|
||||
output logic pll_req_valid,
|
||||
output logic pll_req_ready,
|
||||
output logic pll_busy,
|
||||
output logic pll_error,
|
||||
output logic [ 1:0] pll_error_reason,
|
||||
output logic pll_rsp_valid,
|
||||
output logic fault_valid,
|
||||
output logic [31:0] fault_addr,
|
||||
output logic [ 3:0] fault_wstrb,
|
||||
output logic fault_reserved,
|
||||
output logic fault_pending,
|
||||
output logic fault_write,
|
||||
output logic [ 2:0] fault_reason,
|
||||
output logic [31:0] fault_addr_q,
|
||||
output logic [31:0] fault_count
|
||||
);
|
||||
|
||||
nmi_if nmi ();
|
||||
sysctrl_if sysctrl ();
|
||||
pll_ctrl_if pll_ctrl ();
|
||||
|
||||
(* anyseq *)logic f_nmi_valid;
|
||||
(* anyseq *)logic [31:0] f_nmi_addr;
|
||||
(* anyseq *)logic [31:0] f_nmi_wdata;
|
||||
(* anyseq *)logic [ 3:0] f_nmi_wstrb;
|
||||
(* anyseq *)logic f_pll_req_ready;
|
||||
(* anyseq *)logic [ 2:0] f_pll_active_sel;
|
||||
(* anyseq *)logic f_pll_active_valid;
|
||||
(* anyseq *)logic f_pll_safe_clk;
|
||||
(* anyseq *)logic f_pll_lock;
|
||||
(* anyseq *)logic [ 1:0] f_pll_rsp_error;
|
||||
(* anyseq *)logic f_pll_rsp_valid;
|
||||
(* anyseq *)logic f_pll_capable;
|
||||
(* anyseq *)logic f_fault_valid;
|
||||
(* anyseq *)logic [31:0] f_fault_addr;
|
||||
(* anyseq *)logic [ 3:0] f_fault_wstrb;
|
||||
(* anyseq *)logic f_fault_reserved;
|
||||
|
||||
assign nmi.valid = f_nmi_valid;
|
||||
assign nmi.addr = f_nmi_addr;
|
||||
assign nmi.wdata = f_nmi_wdata;
|
||||
assign nmi.wstrb = f_nmi_wstrb;
|
||||
assign sysctrl.user_bus_idle_i = 1'b1;
|
||||
assign sysctrl.fault_access_i = 1'b0;
|
||||
assign sysctrl.fault_master_i = '0;
|
||||
assign pll_ctrl.req_ready_i = f_pll_req_ready;
|
||||
assign pll_ctrl.rsp_active_sel_i = f_pll_active_sel;
|
||||
assign pll_ctrl.rsp_active_valid_i = f_pll_active_valid;
|
||||
assign pll_ctrl.rsp_safe_clk_i = f_pll_safe_clk;
|
||||
assign pll_ctrl.rsp_pll_lock_i = f_pll_lock;
|
||||
assign pll_ctrl.rsp_error_i = f_pll_rsp_error;
|
||||
assign pll_ctrl.rsp_valid_i = f_pll_rsp_valid;
|
||||
assign pll_ctrl.capable_i = f_pll_capable;
|
||||
|
||||
assign nmi_valid = nmi.valid;
|
||||
assign nmi_addr = nmi.addr;
|
||||
assign nmi_wdata = nmi.wdata;
|
||||
assign nmi_wstrb = nmi.wstrb;
|
||||
assign nmi_ready = nmi.ready;
|
||||
assign ip_sel = sysctrl.ip_sel_o;
|
||||
assign core_sel = u_dut.s_sysctrl_coresel_q;
|
||||
assign user_reset = u_dut.s_user_reset_q;
|
||||
assign user_bus_enable = u_dut.s_user_running_q;
|
||||
assign user_config_error = u_dut.s_user_config_error_q;
|
||||
assign pll_cfg = u_dut.s_pll_cfg_q;
|
||||
assign pll_req_valid = pll_ctrl.req_valid_o;
|
||||
assign pll_req_ready = pll_ctrl.req_ready_i;
|
||||
assign pll_busy = u_dut.s_pll_busy_q;
|
||||
assign pll_error = u_dut.s_pll_error_q;
|
||||
assign pll_error_reason = u_dut.s_pll_error_reason_q;
|
||||
assign pll_rsp_valid = pll_ctrl.rsp_valid_i;
|
||||
assign fault_valid = f_fault_valid;
|
||||
assign fault_addr = f_fault_addr;
|
||||
assign fault_wstrb = f_fault_wstrb;
|
||||
assign fault_reserved = f_fault_reserved;
|
||||
assign fault_pending = u_dut.s_fault_pending_q;
|
||||
assign fault_write = u_dut.s_fault_write_q;
|
||||
assign fault_reason = u_dut.s_fault_reason_q;
|
||||
assign fault_addr_q = u_dut.s_fault_addr_q;
|
||||
assign fault_count = u_dut.s_fault_count_q;
|
||||
|
||||
nmi_sysctrl u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.fault_valid_i (f_fault_valid),
|
||||
.fault_addr_i (f_fault_addr),
|
||||
.fault_wstrb_i (f_fault_wstrb),
|
||||
.fault_reserved_i(f_fault_reserved),
|
||||
.nmi (nmi),
|
||||
.sysctrl (sysctrl),
|
||||
.pll_ctrl (pll_ctrl)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module sysctrl_formal;
|
||||
|
||||
localparam [7:0] SYSCTRL_IPSEL_OFFSET = 8'h04;
|
||||
localparam [7:0] SYSCTRL_PLL_CFG_OFFSET = 8'h08;
|
||||
localparam [7:0] SYSCTRL_PLL_CMD_OFFSET = 8'h0c;
|
||||
localparam [7:0] SYSCTRL_FAULT_STATUS_OFFSET = 8'h10;
|
||||
localparam [7:0] SYSCTRL_USER_CORE_RESET_OFFSET = 8'h20;
|
||||
|
||||
(* anyseq *) (* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire nmi_valid;
|
||||
wire [31:0] nmi_addr;
|
||||
wire [31:0] nmi_wdata;
|
||||
wire [ 3:0] nmi_wstrb;
|
||||
wire nmi_ready;
|
||||
wire [ 7:0] ip_sel;
|
||||
wire [ 4:0] core_sel;
|
||||
wire [ 5:0] user_reset;
|
||||
wire user_bus_enable;
|
||||
wire user_config_error;
|
||||
wire [ 2:0] pll_cfg;
|
||||
wire pll_req_valid;
|
||||
wire pll_req_ready;
|
||||
wire pll_busy;
|
||||
wire pll_error;
|
||||
wire [ 1:0] pll_error_reason;
|
||||
wire pll_rsp_valid;
|
||||
wire fault_valid;
|
||||
wire [31:0] fault_addr;
|
||||
wire [ 3:0] fault_wstrb;
|
||||
wire fault_reserved;
|
||||
wire fault_pending;
|
||||
wire fault_write;
|
||||
wire [ 2:0] fault_reason;
|
||||
wire [31:0] fault_addr_q;
|
||||
wire [31:0] fault_count;
|
||||
|
||||
sysctrl_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.f_past_valid (f_past_valid),
|
||||
.nmi_valid (nmi_valid),
|
||||
.nmi_addr (nmi_addr),
|
||||
.nmi_wdata (nmi_wdata),
|
||||
.nmi_wstrb (nmi_wstrb),
|
||||
.nmi_ready (nmi_ready),
|
||||
.ip_sel (ip_sel),
|
||||
.core_sel (core_sel),
|
||||
.user_reset (user_reset),
|
||||
.user_bus_enable (user_bus_enable),
|
||||
.user_config_error(user_config_error),
|
||||
.pll_cfg (pll_cfg),
|
||||
.pll_req_valid (pll_req_valid),
|
||||
.pll_req_ready (pll_req_ready),
|
||||
.pll_busy (pll_busy),
|
||||
.pll_error (pll_error),
|
||||
.pll_error_reason (pll_error_reason),
|
||||
.pll_rsp_valid (pll_rsp_valid),
|
||||
.fault_valid (fault_valid),
|
||||
.fault_addr (fault_addr),
|
||||
.fault_wstrb (fault_wstrb),
|
||||
.fault_reserved (fault_reserved),
|
||||
.fault_pending (fault_pending),
|
||||
.fault_write (fault_write),
|
||||
.fault_reason (fault_reason),
|
||||
.fault_addr_q (fault_addr_q),
|
||||
.fault_count (fault_count)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (f_past_valid && $past(rst_n_i && nmi_valid && !nmi_ready)) begin
|
||||
assume (nmi_valid);
|
||||
assume (nmi_addr == $past(nmi_addr));
|
||||
assume (nmi_wdata == $past(nmi_wdata));
|
||||
assume (nmi_wstrb == $past(nmi_wstrb));
|
||||
end
|
||||
if (rst_n_i && pll_rsp_valid) begin
|
||||
assume (pll_busy);
|
||||
end
|
||||
|
||||
if (rst_n_i && f_past_valid) begin
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] &&
|
||||
nmi_addr[7:0] == 8'h00 && nmi_wdata[4:0] < 6 && user_reset == 6'h3f && !user_bus_enable
|
||||
)) begin
|
||||
assert (core_sel == $past(nmi_wdata[4:0]));
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] &&
|
||||
nmi_addr[7:0] == 8'h00 && nmi_wdata[4:0] >= 6
|
||||
)) begin
|
||||
assert (core_sel == $past(core_sel));
|
||||
assert (user_config_error);
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] &&
|
||||
nmi_addr[7:0] == SYSCTRL_USER_CORE_RESET_OFFSET &&
|
||||
nmi_wdata[5:0] == 6'h3f
|
||||
)) begin
|
||||
assert (user_reset == 6'h3f);
|
||||
assert (!user_bus_enable);
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && |nmi_wstrb &&
|
||||
nmi_addr[7:0] == SYSCTRL_IPSEL_OFFSET
|
||||
)) begin
|
||||
assert (ip_sel == $past(nmi_wdata[7:0]));
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] &&
|
||||
nmi_addr[7:0] == SYSCTRL_PLL_CFG_OFFSET
|
||||
)) begin
|
||||
assert (pll_cfg == $past(nmi_wdata[2:0]));
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] && nmi_wdata[0] &&
|
||||
nmi_addr[7:0] == SYSCTRL_PLL_CMD_OFFSET && !pll_busy && !pll_req_valid
|
||||
)) begin
|
||||
assert (pll_busy);
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] && nmi_wdata[0] &&
|
||||
nmi_addr[7:0] == SYSCTRL_PLL_CMD_OFFSET && pll_busy
|
||||
)) begin
|
||||
assert (pll_error);
|
||||
if (!$past(pll_rsp_valid)) begin
|
||||
assert (pll_error_reason == 2'd3);
|
||||
end
|
||||
end
|
||||
if ($past(rst_n_i && fault_valid)) begin
|
||||
assert (fault_pending);
|
||||
assert (fault_write == (|$past(fault_wstrb)));
|
||||
assert (fault_reason == ($past(fault_reserved) ? 3'd2 : 3'd1));
|
||||
assert (fault_addr_q == $past(fault_addr));
|
||||
if (!$past(&fault_count)) begin
|
||||
assert (fault_count == $past(fault_count) + 32'd1);
|
||||
end
|
||||
end
|
||||
if ($past(
|
||||
rst_n_i && nmi_valid && !nmi_ready && nmi_wstrb[0] && nmi_wdata[0] &&
|
||||
nmi_addr[7:0] == SYSCTRL_FAULT_STATUS_OFFSET && !fault_valid
|
||||
)) begin
|
||||
assert (!fault_pending);
|
||||
end
|
||||
end
|
||||
|
||||
if (rst_n_i) begin
|
||||
cover (pll_busy && pll_req_valid);
|
||||
cover (pll_error && pll_error_reason == 2'd3);
|
||||
cover (fault_pending && fault_reason == 2'd2);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,62 @@
|
||||
# Mini SoC Topology
|
||||
|
||||
`soc_topology.json` is the source of truth for internal Mini SoC integration
|
||||
that is not part of the software address-map ABI or the package pad map.
|
||||
|
||||
The topology generator validates native NMI and APB target ownership against
|
||||
`../address_map/memory_map.json` and emits generated SystemVerilog include
|
||||
files for `ip_nmi_wrapper.sv`, `ip_apb_wrapper.sv`, `nmi2apb.sv`, and
|
||||
`retrosoc.sv`. The output retains scalar interface instances and ports, so it
|
||||
does not depend on interface-array support in FPGA or netlist simulation tools.
|
||||
|
||||
The `native_targets` list uses stable response slots. Every active native
|
||||
memory-map region must appear exactly once. Disabled targets keep their slot
|
||||
and interface declaration but cannot own an address region.
|
||||
|
||||
Each `gpio_alt_functions` entry defines both alternate modes for one GPIO pin.
|
||||
`inputs` are driven from the selected GPIO input, and `do` and `oe` define the
|
||||
peripheral output path. Values are restricted to scalar signal references and
|
||||
one-bit constants so malformed expressions are rejected before RTL generation.
|
||||
|
||||
The `apb_targets` list defines the stable APB response slot, timed/pure APB
|
||||
interface names, and corresponding active APB address-map region. The user-IP
|
||||
target is part of the fixed platform integration and its presence is checked
|
||||
against the canonical memory map.
|
||||
|
||||
`irq_vector_width`, `irq_groups`, and `interrupts` define the complete core
|
||||
interrupt topology. `nmi` and `apb` group entries declare the wrapper output
|
||||
width and the associated `retrosoc.sv` signal. Every group bit must be present
|
||||
exactly once, each core-vector bit must have one source, and sources are limited
|
||||
to scalar signal references or one-bit constants. The generator emits the two
|
||||
wrapper bindings, the core-vector wiring, and simulation-only assertions. Core
|
||||
IRQ bits 0 through 16 retain their current compatibility mapping; additions
|
||||
must use currently unallocated bits and preserve the existing entries. Bits
|
||||
without an interrupt entry are driven low. Firmware does not expose a generic
|
||||
external interrupt API until the SoC includes a claim/complete interrupt
|
||||
controller.
|
||||
|
||||
`user_extensions.json` separately defines the supported scalar user-core and
|
||||
user-IP slots. `generate_user_extensions.py` creates isolated default routing
|
||||
and one explicit instance per selected slot for `user_core_top.sv` and
|
||||
`user_ip_top.sv`. Adding an extension therefore requires a reviewed JSON entry
|
||||
rather than manual edits to the SoC muxes. The module port contract remains the
|
||||
existing user design template contract.
|
||||
|
||||
`clock_reset_domains.json` is the checked inventory of root clock domains,
|
||||
reset synchronizers, and RCU PLL control crossings. It records the primitive,
|
||||
source file, and instance name so an RTL refactor cannot silently remove the
|
||||
declared reset or CDC structure. It complements, but does not replace, a
|
||||
signoff CDC/RDC analysis tool.
|
||||
|
||||
Use these commands to validate or generate the selected build artifacts:
|
||||
|
||||
```sh
|
||||
make check-soc-topology
|
||||
make soc-topology
|
||||
make check-user-extensions
|
||||
make user-extensions
|
||||
make check-clock-reset-domains
|
||||
```
|
||||
|
||||
Generated files are written under `build/<variant>/generated/soc_topology/` and
|
||||
`build/<variant>/generated/user_extensions/`; they must not be committed.
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"reset_ports": ["ext_rst_n_i_pad"],
|
||||
"domains": [
|
||||
{
|
||||
"name": "external",
|
||||
"clock": "s_ext_clk_buf",
|
||||
"reset": "s_ext_rst_n_sync",
|
||||
"reset_source": "ext_rst_n_i",
|
||||
"reset_primitive": "rst_sync",
|
||||
"path": "rtl/mini/top/rcu.sv",
|
||||
"instance": "u_ext_rst_sync",
|
||||
"sta": {
|
||||
"source_port": "extclk_i_pad",
|
||||
"pin": "u_rcu.u_ext_clk_buf/clk_o",
|
||||
"period_ns": 13.888888889,
|
||||
"async_group": "external"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"clock": "sys_clk_o",
|
||||
"reset": "s_sys_rst_n_sync",
|
||||
"reset_source": "ext_rst_n_i",
|
||||
"reset_primitive": "rst_sync",
|
||||
"path": "rtl/mini/top/rcu.sv",
|
||||
"instance": "u_sys_rst_sync",
|
||||
"sta": {
|
||||
"source_domain": "external",
|
||||
"pin": "u_rcu.u_sys_clk_buf/clk_o",
|
||||
"period_ns": 13.888888889,
|
||||
"async_group": "external"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
"clock": "s_aud_clk_buf",
|
||||
"reset": "s_aud_rst_n_sync",
|
||||
"reset_source": "ext_rst_n_i",
|
||||
"reset_primitive": "rst_sync",
|
||||
"path": "rtl/mini/top/rcu.sv",
|
||||
"instance": "u_aud_rst_sync",
|
||||
"sta": {
|
||||
"source_port": "audclk_i_pad",
|
||||
"pin": "u_rcu.u_aud_clk_buf/clk_o",
|
||||
"period_ns": 54.253472222,
|
||||
"async_group": "audio"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dvp",
|
||||
"clock": "s_dvp_pclk_buf",
|
||||
"reset": "s_dvp_pclk_rst_n_sync",
|
||||
"reset_source": "rst_n_i",
|
||||
"reset_primitive": "rst_sync",
|
||||
"path": "rtl/ip/native/multimedia/dvp.sv",
|
||||
"instance": "u_dvp_pclk_rst_sync",
|
||||
"sta": {
|
||||
"source_port": "gpio_10_io_pad",
|
||||
"pin": "u_retrosoc.u_ip_nmi_wrapper.u_nmi_dvp.u_dvp_pclk_clk_buf/clk_o",
|
||||
"period_ns": 41.666666667,
|
||||
"async_group": "dvp"
|
||||
}
|
||||
}
|
||||
],
|
||||
"crossings": [
|
||||
{
|
||||
"name": "pll_request",
|
||||
"source": "system",
|
||||
"destination": "external",
|
||||
"primitive": "cdc_2phase",
|
||||
"path": "rtl/mini/top/rcu.sv",
|
||||
"instance": "u_pll_request_cdc"
|
||||
},
|
||||
{
|
||||
"name": "pll_response",
|
||||
"source": "external",
|
||||
"destination": "system",
|
||||
"primitive": "cdc_2phase",
|
||||
"path": "rtl/mini/top/rcu.sv",
|
||||
"instance": "u_pll_response_cdc"
|
||||
},
|
||||
{
|
||||
"name": "dvp_enable",
|
||||
"source": "system",
|
||||
"destination": "dvp",
|
||||
"primitive": "cdc_sync",
|
||||
"path": "rtl/ip/native/multimedia/dvp.sv",
|
||||
"instance": "u_dvp_recven_cdc_sync"
|
||||
},
|
||||
{
|
||||
"name": "dvp_fifo",
|
||||
"source": "dvp",
|
||||
"destination": "system",
|
||||
"primitive": "async_fifo",
|
||||
"path": "rtl/ip/native/multimedia/dvp.sv",
|
||||
"instance": "u_rx_async_fifo"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,920 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate Mini SoC internal integration bindings from one topology map."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
SV_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
|
||||
SV_REFERENCE_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*(?:\[[0-9]+\])?$")
|
||||
SV_CONSTANT_RE = re.compile(r"(?:1'[bB][01]|'[01])$")
|
||||
FABRIC_LINK_NAMES = ("mgmt", "user", "dma", "native", "apb")
|
||||
IRQ_GROUP_NAMES = ("nmi", "apb")
|
||||
IRQ_VECTOR_WIDTH = 32
|
||||
COMPATIBILITY_IRQ_BINDINGS = (
|
||||
("clint_software", "nmi", 0, 0, "u_clint_if.sfr_irq_o"),
|
||||
("clint_timer", "nmi", 1, 1, "u_clint_if.tmr_irq_o"),
|
||||
("uart0", "nmi", 2, 2, "uart.irq_o"),
|
||||
("timer0", "nmi", 3, 3, "s_tim0_irq"),
|
||||
("timer1", "nmi", 4, 4, "s_tim1_irq"),
|
||||
("psram", "nmi", 5, 5, "psram.irq_o"),
|
||||
("spisd", "nmi", 6, 6, "spisd.irq_o"),
|
||||
("i2c0", "nmi", 7, 7, "i2c0.irq_o"),
|
||||
("i2s", "nmi", 8, 8, "i2s.irq_o"),
|
||||
("xpi", "nmi", 9, 9, "xpi.irq_o"),
|
||||
("uart1", "apb", 0, 10, "uart.irq_o"),
|
||||
("pwm", "apb", 1, 11, "pwm.irq_o"),
|
||||
("ps2", "apb", 2, 12, "ps2.irq_o"),
|
||||
("rtc", "apb", 3, 13, "u_rtc_if.irq_o"),
|
||||
("watchdog_reset", "apb", 4, 14, "u_wdg_if.rst_o"),
|
||||
("advanced_timer", "apb", 5, 15, "u_tmr_if.irq_o"),
|
||||
("reserved", "apb", 6, 16, "1'b0"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NativeTarget:
|
||||
slot: int
|
||||
name: str
|
||||
interface: str
|
||||
regions: tuple[str, ...]
|
||||
disabled: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApbTarget:
|
||||
slot: int
|
||||
name: str
|
||||
timed_interface: str
|
||||
pure_interface: str
|
||||
region: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FabricLink:
|
||||
name: str
|
||||
interface: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GpioMode:
|
||||
inputs: tuple[str, ...]
|
||||
do: str
|
||||
oe: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GpioFunction:
|
||||
pin: int
|
||||
alt0: GpioMode
|
||||
alt1: GpioMode
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IrqGroup:
|
||||
name: str
|
||||
width: int
|
||||
top_signal: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Interrupt:
|
||||
name: str
|
||||
description: str
|
||||
group: str
|
||||
group_bit: int
|
||||
core_bit: int
|
||||
signal: str
|
||||
|
||||
|
||||
def atomic_write(path: Path, content: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if path.exists() and path.read_text(encoding="utf-8") == content:
|
||||
return
|
||||
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
|
||||
try:
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as output:
|
||||
output.write(content)
|
||||
os.replace(temporary_name, path)
|
||||
except BaseException:
|
||||
Path(temporary_name).unlink(missing_ok=True)
|
||||
raise
|
||||
|
||||
|
||||
def require_object(value: Any, field: str) -> dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError(f"{field} must be an object")
|
||||
return value
|
||||
|
||||
|
||||
def require_string(value: Any, field: str) -> str:
|
||||
if not isinstance(value, str) or not value:
|
||||
raise ValueError(f"{field} must be a non-empty string")
|
||||
return value
|
||||
|
||||
|
||||
def require_comment_text(value: Any, field: str) -> str:
|
||||
text = require_string(value, field)
|
||||
if "\n" in text or "\r" in text:
|
||||
raise ValueError(f"{field} must be a single-line string")
|
||||
return text
|
||||
|
||||
|
||||
def require_identifier(value: Any, field: str) -> str:
|
||||
identifier = require_string(value, field)
|
||||
if SV_IDENTIFIER_RE.fullmatch(identifier) is None:
|
||||
raise ValueError(f"{field} must be a SystemVerilog identifier")
|
||||
return identifier
|
||||
|
||||
|
||||
def require_reference(value: Any, field: str) -> str:
|
||||
reference = require_string(value, field)
|
||||
if SV_REFERENCE_RE.fullmatch(reference) is None:
|
||||
raise ValueError(f"{field} must be a scalar SystemVerilog reference")
|
||||
return reference
|
||||
|
||||
|
||||
def require_value(value: Any, field: str) -> str:
|
||||
rendered = require_string(value, field)
|
||||
if SV_REFERENCE_RE.fullmatch(rendered) is None and SV_CONSTANT_RE.fullmatch(rendered) is None:
|
||||
raise ValueError(f"{field} must be a scalar SystemVerilog reference or constant")
|
||||
return rendered
|
||||
|
||||
|
||||
def read_memory_regions(path: Path) -> dict[str, dict[str, Any]]:
|
||||
document = require_object(json.loads(path.read_text(encoding="utf-8")), "memory map")
|
||||
regions = document.get("regions")
|
||||
if not isinstance(regions, list) or not regions:
|
||||
raise ValueError("memory map regions must be a non-empty list")
|
||||
parsed: dict[str, dict[str, Any]] = {}
|
||||
for index, entry in enumerate(regions):
|
||||
region = require_object(entry, f"memory map regions[{index}]")
|
||||
symbol = require_identifier(region.get("symbol"), f"memory map regions[{index}].symbol")
|
||||
if symbol in parsed:
|
||||
raise ValueError(f"memory map region {symbol} is duplicated")
|
||||
parsed[symbol] = region
|
||||
return parsed
|
||||
|
||||
|
||||
def parse_regions(value: Any, field: str) -> tuple[str, ...]:
|
||||
if not isinstance(value, list) or not value:
|
||||
raise ValueError(f"{field} must be a non-empty list")
|
||||
regions = tuple(require_identifier(item, f"{field} entry") for item in value)
|
||||
if len(regions) != len(set(regions)):
|
||||
raise ValueError(f"{field} contains duplicate regions")
|
||||
return regions
|
||||
|
||||
|
||||
def parse_native_targets(
|
||||
value: Any, memory_regions: dict[str, dict[str, Any]]
|
||||
) -> list[NativeTarget]:
|
||||
if not isinstance(value, list) or not value:
|
||||
raise ValueError("native_targets must be a non-empty list")
|
||||
targets: list[NativeTarget] = []
|
||||
claimed_regions: set[str] = set()
|
||||
names: set[str] = set()
|
||||
interfaces: set[str] = set()
|
||||
slots: set[int] = set()
|
||||
for index, entry in enumerate(value):
|
||||
target = require_object(entry, f"native_targets[{index}]")
|
||||
slot = target.get("slot")
|
||||
if not isinstance(slot, int) or slot < 0:
|
||||
raise ValueError(f"native_targets[{index}].slot must be a non-negative integer")
|
||||
name = require_identifier(target.get("name"), f"native_targets[{index}].name")
|
||||
interface = require_identifier(
|
||||
target.get("interface"), f"native_targets[{index}].interface"
|
||||
)
|
||||
disabled = target.get("disabled", False)
|
||||
if not isinstance(disabled, bool):
|
||||
raise ValueError(f"native_targets[{index}].disabled must be boolean")
|
||||
if disabled:
|
||||
if "regions" in target:
|
||||
raise ValueError(f"native_targets[{index}] is disabled but declares regions")
|
||||
regions: tuple[str, ...] = ()
|
||||
else:
|
||||
regions = parse_regions(target.get("regions"), f"native_targets[{index}].regions")
|
||||
for region_name in regions:
|
||||
region = memory_regions.get(region_name)
|
||||
if region is None:
|
||||
raise ValueError(
|
||||
f"native_targets[{index}] references unknown region {region_name}"
|
||||
)
|
||||
if region.get("route") != "native" or region.get("kind") != "active":
|
||||
raise ValueError(
|
||||
f"native_targets[{index}] region {region_name} is not an active native region"
|
||||
)
|
||||
if region_name in claimed_regions:
|
||||
raise ValueError(f"native region {region_name} has multiple targets")
|
||||
claimed_regions.add(region_name)
|
||||
if name in names:
|
||||
raise ValueError(f"native target name {name} is duplicated")
|
||||
if interface in interfaces:
|
||||
raise ValueError(f"native target interface {interface} is duplicated")
|
||||
if slot in slots:
|
||||
raise ValueError(f"native target slot {slot} is duplicated")
|
||||
names.add(name)
|
||||
interfaces.add(interface)
|
||||
slots.add(slot)
|
||||
targets.append(NativeTarget(slot, name, interface, regions, disabled))
|
||||
|
||||
ordered = sorted(targets, key=lambda item: item.slot)
|
||||
if [target.slot for target in ordered] != list(range(len(ordered))):
|
||||
raise ValueError("native target slots must be contiguous from zero")
|
||||
active_native_regions = {
|
||||
symbol
|
||||
for symbol, region in memory_regions.items()
|
||||
if region.get("route") == "native" and region.get("kind") == "active"
|
||||
}
|
||||
if claimed_regions != active_native_regions:
|
||||
missing = sorted(active_native_regions - claimed_regions)
|
||||
extra = sorted(claimed_regions - active_native_regions)
|
||||
details: list[str] = []
|
||||
if missing:
|
||||
details.append(f"missing {', '.join(missing)}")
|
||||
if extra:
|
||||
details.append(f"extra {', '.join(extra)}")
|
||||
raise ValueError(
|
||||
f"native target regions do not cover the memory map ({'; '.join(details)})"
|
||||
)
|
||||
return ordered
|
||||
|
||||
|
||||
def parse_apb_targets(value: Any, memory_regions: dict[str, dict[str, Any]]) -> list[ApbTarget]:
|
||||
if not isinstance(value, list) or not value:
|
||||
raise ValueError("apb_targets must be a non-empty list")
|
||||
targets: list[ApbTarget] = []
|
||||
names: set[str] = set()
|
||||
timed_interfaces: set[str] = set()
|
||||
pure_interfaces: set[str] = set()
|
||||
regions: set[str] = set()
|
||||
slots: set[int] = set()
|
||||
for index, entry in enumerate(value):
|
||||
target = require_object(entry, f"apb_targets[{index}]")
|
||||
slot = target.get("slot")
|
||||
if not isinstance(slot, int) or slot < 0:
|
||||
raise ValueError(f"apb_targets[{index}].slot must be a non-negative integer")
|
||||
name = require_identifier(target.get("name"), f"apb_targets[{index}].name")
|
||||
timed_interface = require_identifier(
|
||||
target.get("timed_interface"), f"apb_targets[{index}].timed_interface"
|
||||
)
|
||||
pure_interface = require_identifier(
|
||||
target.get("pure_interface"), f"apb_targets[{index}].pure_interface"
|
||||
)
|
||||
region_name = require_identifier(target.get("region"), f"apb_targets[{index}].region")
|
||||
region = memory_regions.get(region_name)
|
||||
if region is None:
|
||||
raise ValueError(f"apb_targets[{index}] references unknown region {region_name}")
|
||||
if region.get("route") != "apb" or region.get("kind") != "active":
|
||||
raise ValueError(
|
||||
f"apb_targets[{index}] region {region_name} is not an active APB region"
|
||||
)
|
||||
if name in names:
|
||||
raise ValueError(f"APB target name {name} is duplicated")
|
||||
if timed_interface in timed_interfaces:
|
||||
raise ValueError(f"APB timed interface {timed_interface} is duplicated")
|
||||
if pure_interface in pure_interfaces:
|
||||
raise ValueError(f"APB pure interface {pure_interface} is duplicated")
|
||||
if region_name in regions:
|
||||
raise ValueError(f"APB region {region_name} has multiple targets")
|
||||
if slot in slots:
|
||||
raise ValueError(f"APB target slot {slot} is duplicated")
|
||||
names.add(name)
|
||||
timed_interfaces.add(timed_interface)
|
||||
pure_interfaces.add(pure_interface)
|
||||
regions.add(region_name)
|
||||
slots.add(slot)
|
||||
targets.append(ApbTarget(slot, name, timed_interface, pure_interface, region_name))
|
||||
|
||||
ordered = sorted(targets, key=lambda item: item.slot)
|
||||
if [target.slot for target in ordered] != list(range(len(ordered))):
|
||||
raise ValueError("enabled APB target slots must be contiguous from zero")
|
||||
active_regions = {
|
||||
symbol
|
||||
for symbol, region in memory_regions.items()
|
||||
if region.get("route") == "apb" and region.get("kind") == "active"
|
||||
}
|
||||
claimed_regions = {target.region for target in ordered}
|
||||
if claimed_regions != active_regions:
|
||||
missing = sorted(active_regions - claimed_regions)
|
||||
extra = sorted(claimed_regions - active_regions)
|
||||
details: list[str] = []
|
||||
if missing:
|
||||
details.append(f"missing {', '.join(missing)}")
|
||||
if extra:
|
||||
details.append(f"extra {', '.join(extra)}")
|
||||
raise ValueError(f"APB target regions do not cover the memory map ({'; '.join(details)})")
|
||||
return ordered
|
||||
|
||||
|
||||
def parse_fabric_links(value: Any) -> list[FabricLink]:
|
||||
if not isinstance(value, list) or len(value) != len(FABRIC_LINK_NAMES):
|
||||
raise ValueError("fabric_links must define every SoC fabric role exactly once")
|
||||
links: list[FabricLink] = []
|
||||
names: set[str] = set()
|
||||
interfaces: set[str] = set()
|
||||
for index, entry in enumerate(value):
|
||||
link = require_object(entry, f"fabric_links[{index}]")
|
||||
name = require_identifier(link.get("name"), f"fabric_links[{index}].name")
|
||||
interface = require_identifier(link.get("interface"), f"fabric_links[{index}].interface")
|
||||
if name not in FABRIC_LINK_NAMES:
|
||||
raise ValueError(f"fabric_links[{index}].name is not a supported SoC fabric role")
|
||||
if name in names:
|
||||
raise ValueError(f"SoC fabric role {name} is duplicated")
|
||||
if interface in interfaces:
|
||||
raise ValueError(f"SoC fabric interface {interface} is duplicated")
|
||||
names.add(name)
|
||||
interfaces.add(interface)
|
||||
links.append(FabricLink(name, interface))
|
||||
if names != set(FABRIC_LINK_NAMES):
|
||||
missing = ", ".join(sorted(set(FABRIC_LINK_NAMES) - names))
|
||||
raise ValueError(f"fabric_links does not cover required roles: {missing}")
|
||||
return sorted(links, key=lambda item: FABRIC_LINK_NAMES.index(item.name))
|
||||
|
||||
|
||||
def parse_gpio_mode(value: Any, field: str) -> GpioMode:
|
||||
mode = require_object(value, field)
|
||||
inputs_value = mode.get("inputs")
|
||||
if not isinstance(inputs_value, list):
|
||||
raise ValueError(f"{field}.inputs must be a list")
|
||||
inputs = tuple(
|
||||
require_reference(item, f"{field}.inputs[{index}]")
|
||||
for index, item in enumerate(inputs_value)
|
||||
)
|
||||
if len(inputs) != len(set(inputs)):
|
||||
raise ValueError(f"{field}.inputs contains duplicates")
|
||||
return GpioMode(
|
||||
inputs=inputs,
|
||||
do=require_value(mode.get("do"), f"{field}.do"),
|
||||
oe=require_value(mode.get("oe"), f"{field}.oe"),
|
||||
)
|
||||
|
||||
|
||||
def parse_gpio_functions(value: Any, gpio_pins: int) -> list[GpioFunction]:
|
||||
if not isinstance(value, list) or len(value) != gpio_pins:
|
||||
raise ValueError("gpio_alt_functions must contain one entry for every GPIO pin")
|
||||
parsed: list[GpioFunction] = []
|
||||
pins: set[int] = set()
|
||||
for index, entry in enumerate(value):
|
||||
function = require_object(entry, f"gpio_alt_functions[{index}]")
|
||||
pin = function.get("pin")
|
||||
if not isinstance(pin, int) or not 0 <= pin < gpio_pins:
|
||||
raise ValueError(f"gpio_alt_functions[{index}].pin is out of range")
|
||||
if pin in pins:
|
||||
raise ValueError(f"gpio pin {pin} is duplicated")
|
||||
pins.add(pin)
|
||||
parsed.append(
|
||||
GpioFunction(
|
||||
pin=pin,
|
||||
alt0=parse_gpio_mode(function.get("alt0"), f"gpio_alt_functions[{index}].alt0"),
|
||||
alt1=parse_gpio_mode(function.get("alt1"), f"gpio_alt_functions[{index}].alt1"),
|
||||
)
|
||||
)
|
||||
if pins != set(range(gpio_pins)):
|
||||
raise ValueError("gpio_alt_functions must cover contiguous pins from zero")
|
||||
return sorted(parsed, key=lambda item: item.pin)
|
||||
|
||||
|
||||
def parse_irq_groups(value: Any) -> list[IrqGroup]:
|
||||
if not isinstance(value, list) or len(value) != len(IRQ_GROUP_NAMES):
|
||||
raise ValueError("irq_groups must define every supported interrupt group exactly once")
|
||||
groups: list[IrqGroup] = []
|
||||
names: set[str] = set()
|
||||
top_signals: set[str] = set()
|
||||
for index, entry in enumerate(value):
|
||||
group = require_object(entry, f"irq_groups[{index}]")
|
||||
name = require_identifier(group.get("name"), f"irq_groups[{index}].name")
|
||||
width = group.get("width")
|
||||
if not isinstance(width, int) or width <= 0:
|
||||
raise ValueError(f"irq_groups[{index}].width must be a positive integer")
|
||||
top_signal = require_identifier(group.get("top_signal"), f"irq_groups[{index}].top_signal")
|
||||
if name not in IRQ_GROUP_NAMES:
|
||||
raise ValueError(f"irq_groups[{index}].name is not a supported interrupt group")
|
||||
if name in names:
|
||||
raise ValueError(f"interrupt group {name} is duplicated")
|
||||
if top_signal in top_signals:
|
||||
raise ValueError(f"interrupt group top signal {top_signal} is duplicated")
|
||||
names.add(name)
|
||||
top_signals.add(top_signal)
|
||||
groups.append(IrqGroup(name, width, top_signal))
|
||||
if names != set(IRQ_GROUP_NAMES):
|
||||
missing = ", ".join(sorted(set(IRQ_GROUP_NAMES) - names))
|
||||
raise ValueError(f"irq_groups does not cover required groups: {missing}")
|
||||
return sorted(groups, key=lambda item: IRQ_GROUP_NAMES.index(item.name))
|
||||
|
||||
|
||||
def parse_interrupts(value: Any, groups: list[IrqGroup], vector_width: int) -> list[Interrupt]:
|
||||
if not isinstance(value, list) or not value:
|
||||
raise ValueError("interrupts must be a non-empty list")
|
||||
group_widths = {group.name: group.width for group in groups}
|
||||
interrupts: list[Interrupt] = []
|
||||
names: set[str] = set()
|
||||
group_bits: dict[str, set[int]] = {group.name: set() for group in groups}
|
||||
core_bits: set[int] = set()
|
||||
for index, entry in enumerate(value):
|
||||
interrupt = require_object(entry, f"interrupts[{index}]")
|
||||
name = require_identifier(interrupt.get("name"), f"interrupts[{index}].name")
|
||||
description = require_comment_text(
|
||||
interrupt.get("description"), f"interrupts[{index}].description"
|
||||
)
|
||||
group = require_identifier(interrupt.get("group"), f"interrupts[{index}].group")
|
||||
group_bit = interrupt.get("group_bit")
|
||||
core_bit = interrupt.get("core_bit")
|
||||
signal = require_value(interrupt.get("signal"), f"interrupts[{index}].signal")
|
||||
if group not in group_widths:
|
||||
raise ValueError(f"interrupts[{index}].group is not a supported interrupt group")
|
||||
if not isinstance(group_bit, int) or not 0 <= group_bit < group_widths[group]:
|
||||
raise ValueError(f"interrupts[{index}].group_bit is out of range")
|
||||
if not isinstance(core_bit, int) or not 0 <= core_bit < vector_width:
|
||||
raise ValueError(f"interrupts[{index}].core_bit is out of range")
|
||||
if name in names:
|
||||
raise ValueError(f"interrupt name {name} is duplicated")
|
||||
if group_bit in group_bits[group]:
|
||||
raise ValueError(f"interrupt group {group} bit {group_bit} is duplicated")
|
||||
if core_bit in core_bits:
|
||||
raise ValueError(f"core interrupt bit {core_bit} is duplicated")
|
||||
names.add(name)
|
||||
group_bits[group].add(group_bit)
|
||||
core_bits.add(core_bit)
|
||||
interrupts.append(Interrupt(name, description, group, group_bit, core_bit, signal))
|
||||
|
||||
for group in groups:
|
||||
expected = set(range(group.width))
|
||||
if group_bits[group.name] != expected:
|
||||
missing = sorted(expected - group_bits[group.name])
|
||||
extra = sorted(group_bits[group.name] - expected)
|
||||
details: list[str] = []
|
||||
if missing:
|
||||
details.append(f"missing {', '.join(str(bit) for bit in missing)}")
|
||||
if extra:
|
||||
details.append(f"extra {', '.join(str(bit) for bit in extra)}")
|
||||
raise ValueError(
|
||||
f"interrupt group {group.name} does not cover every group bit "
|
||||
f"({'; '.join(details)})"
|
||||
)
|
||||
return sorted(interrupts, key=lambda item: item.core_bit)
|
||||
|
||||
|
||||
def validate_compatibility_irq_bindings(interrupts: list[Interrupt]) -> None:
|
||||
by_core_bit = {interrupt.core_bit: interrupt for interrupt in interrupts}
|
||||
for name, group, group_bit, core_bit, signal in COMPATIBILITY_IRQ_BINDINGS:
|
||||
interrupt = by_core_bit.get(core_bit)
|
||||
actual = None
|
||||
if interrupt is not None:
|
||||
actual = (
|
||||
interrupt.name,
|
||||
interrupt.group,
|
||||
interrupt.group_bit,
|
||||
interrupt.core_bit,
|
||||
interrupt.signal,
|
||||
)
|
||||
expected = (name, group, group_bit, core_bit, signal)
|
||||
if actual != expected:
|
||||
raise ValueError(f"core interrupt bit {core_bit} must retain its compatibility binding")
|
||||
|
||||
|
||||
def read_topology(
|
||||
topology_path: Path, memory_map_path: Path
|
||||
) -> tuple[
|
||||
list[NativeTarget],
|
||||
list[ApbTarget],
|
||||
list[FabricLink],
|
||||
list[GpioFunction],
|
||||
int,
|
||||
list[IrqGroup],
|
||||
list[Interrupt],
|
||||
]:
|
||||
document = require_object(json.loads(topology_path.read_text(encoding="utf-8")), "topology")
|
||||
if document.get("schema_version") != 1:
|
||||
raise ValueError("schema_version must be 1")
|
||||
gpio_pins = document.get("gpio_pins")
|
||||
if not isinstance(gpio_pins, int) or gpio_pins <= 0:
|
||||
raise ValueError("gpio_pins must be a positive integer")
|
||||
irq_vector_width = document.get("irq_vector_width")
|
||||
if irq_vector_width != IRQ_VECTOR_WIDTH:
|
||||
raise ValueError(f"irq_vector_width must be {IRQ_VECTOR_WIDTH}")
|
||||
memory_regions = read_memory_regions(memory_map_path)
|
||||
irq_groups = parse_irq_groups(document.get("irq_groups"))
|
||||
interrupts = parse_interrupts(document.get("interrupts"), irq_groups, irq_vector_width)
|
||||
validate_compatibility_irq_bindings(interrupts)
|
||||
return (
|
||||
parse_native_targets(document.get("native_targets"), memory_regions),
|
||||
parse_apb_targets(document.get("apb_targets"), memory_regions),
|
||||
parse_fabric_links(document.get("fabric_links")),
|
||||
parse_gpio_functions(document.get("gpio_alt_functions"), gpio_pins),
|
||||
irq_vector_width,
|
||||
irq_groups,
|
||||
interrupts,
|
||||
)
|
||||
|
||||
|
||||
def render_nmi_interfaces(targets: list[NativeTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
lines.extend(f" nmi_if {target.interface} ();" for target in targets)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_region_select(region: str, memory_region: dict[str, Any]) -> str:
|
||||
base = int(require_string(memory_region.get("base"), f"memory map region {region}.base"), 0)
|
||||
if base == 0:
|
||||
return f"(nmi.addr <= `SOC_ADDR_{region}_END)"
|
||||
return f"`SOC_ADDR_IS_{region}(nmi.addr)"
|
||||
|
||||
|
||||
def render_nmi_routes(
|
||||
targets: list[NativeTarget], memory_regions: dict[str, dict[str, Any]]
|
||||
) -> str:
|
||||
count = len(targets)
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
lines.extend(
|
||||
[
|
||||
f" logic [{count - 1}:0] s_slv_sel_d, s_slv_sel_q;",
|
||||
f" logic [{count - 1}:0] s_slv_ready;",
|
||||
f" logic [31:0] s_slv_rdata [0:{count - 1}];",
|
||||
"",
|
||||
]
|
||||
)
|
||||
for target in targets:
|
||||
if not target.disabled:
|
||||
lines.append(f" logic [{len(target.regions) - 1}:0] s_{target.name}_region_sel;")
|
||||
lines.append("")
|
||||
for target in targets:
|
||||
if target.disabled:
|
||||
lines.append(f" assign {target.interface}.valid = 1'b0;")
|
||||
else:
|
||||
for index, region in enumerate(target.regions):
|
||||
lines.append(
|
||||
f" assign s_{target.name}_region_sel[{index}] = "
|
||||
f"{render_region_select(region, memory_regions[region])};"
|
||||
)
|
||||
lines.append(
|
||||
f" assign {target.interface}.valid = nmi.valid && (|s_{target.name}_region_sel);"
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
f" assign {target.interface}.addr = nmi.addr;",
|
||||
f" assign {target.interface}.wdata = nmi.wdata;",
|
||||
f" assign {target.interface}.wstrb = nmi.wstrb;",
|
||||
f" assign s_slv_sel_d[{target.slot}] = {target.interface}.valid;",
|
||||
f" assign s_slv_ready[{target.slot}] = {target.interface}.ready;",
|
||||
f" assign s_slv_rdata[{target.slot}] = {target.interface}.rdata;",
|
||||
"",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
f" dffr #({count}) u_slv_sel_dffr (",
|
||||
" clk_i,",
|
||||
" rst_n_i,",
|
||||
" s_slv_sel_d,",
|
||||
" s_slv_sel_q",
|
||||
" );",
|
||||
"",
|
||||
" assign nmi.ready = |(s_slv_sel_q & s_slv_ready);",
|
||||
"",
|
||||
" always_comb begin",
|
||||
" nmi.rdata = '0;",
|
||||
f" for (int index = 0; index < {count}; index++) begin",
|
||||
" if (s_slv_sel_q[index]) begin",
|
||||
" nmi.rdata = nmi.rdata | s_slv_rdata[index];",
|
||||
" end",
|
||||
" end",
|
||||
" end",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_interfaces(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for target in targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" apb4_if {target.timed_interface} (clk_i, rst_n_i);",
|
||||
f" apb4_pure_if {target.pure_interface} ();",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_bridges(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for target in targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" apb4_if_bridge u_{target.name}_apb_bridge (",
|
||||
f" .apb_pure({target.pure_interface}),",
|
||||
f" .timed ({target.timed_interface})",
|
||||
" );",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_ports(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for index, target in enumerate(targets):
|
||||
separator = "," if index != len(targets) - 1 else ""
|
||||
lines.append(f" apb4_pure_if.master {target.name}{separator}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_connections(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for index, target in enumerate(targets):
|
||||
separator = "," if index != len(targets) - 1 else ""
|
||||
lines.append(f" .{target.name}({target.pure_interface}){separator}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_declarations(targets: list[ApbTarget]) -> str:
|
||||
count = len(targets)
|
||||
return "\n".join(
|
||||
[
|
||||
"// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit.",
|
||||
f" localparam int NSLV = {count};",
|
||||
" logic [NSLV-1:0] s_psel_comb, s_psel_d, s_psel_q;",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def render_apb_request_routes(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for target in targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" assign {target.name}.paddr = nmi.addr;",
|
||||
f" assign {target.name}.pprot = '0;",
|
||||
f" assign {target.name}.psel = s_xfer_valid && `SOC_ADDR_IS_{target.region}(nmi.addr);",
|
||||
f" assign {target.name}.penable = s_fsm_q == FSM_ENAB;",
|
||||
f" assign {target.name}.pwrite = |nmi.wstrb;",
|
||||
f" assign {target.name}.pwdata = nmi.wdata;",
|
||||
f" assign {target.name}.pstrb = nmi.wstrb;",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_select_routes(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
lines.extend(
|
||||
f" assign s_psel_comb[{target.slot}] = `SOC_ADDR_IS_{target.region}(nmi.addr);"
|
||||
for target in targets
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_apb_response_mux(targets: list[ApbTarget]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for signal, expression in (("s_rd_data", "prdata"), ("s_xfer_ready", "pready")):
|
||||
terms = [
|
||||
f"({{{'32' if signal == 's_rd_data' else '1'}{{s_psel_q[{target.slot}]}}}} & "
|
||||
f"{target.name}.{expression})"
|
||||
for target in targets
|
||||
]
|
||||
lines.append(f" assign {signal} = {' | '.join(terms)};")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_fabric_interfaces(links: list[FabricLink]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
lines.extend(f" nmi_if {link.interface} ();" for link in links)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_fabric_connection(links: list[FabricLink], name: str, port: str) -> str:
|
||||
interface = next(link.interface for link in links if link.name == name)
|
||||
return "\n".join(
|
||||
[
|
||||
"// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit.",
|
||||
f" .{port}({interface}),",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def render_bus_fabric_connections(links: list[FabricLink]) -> str:
|
||||
port_names = {
|
||||
"mgmt": "mgmt_nmi",
|
||||
"user": "user_nmi",
|
||||
"dma": "dma_nmi",
|
||||
"native": "natv_nmi",
|
||||
"apb": "apb_nmi",
|
||||
}
|
||||
interfaces = {link.name: link.interface for link in links}
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
lines.extend(f" .{port_names[name]}({interfaces[name]})," for name in FABRIC_LINK_NAMES)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_gpio_bindings(functions: list[GpioFunction]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for function in functions:
|
||||
for mode_name, mode in (("alt0", function.alt0), ("alt1", function.alt1)):
|
||||
lines.append(f" // GPIO{function.pin} {mode_name}")
|
||||
lines.extend(
|
||||
f" assign {input_signal} = u_gpio_if.di_i[{function.pin}];"
|
||||
for input_signal in mode.inputs
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
f" assign u_gpio_if.{mode_name}_do_i[{function.pin}] = {mode.do};",
|
||||
f" assign u_gpio_if.{mode_name}_oe_i[{function.pin}] = {mode.oe};",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def irq_width_macro(group: IrqGroup) -> str:
|
||||
return f"SOC_IRQ_{group.name.upper()}_WIDTH"
|
||||
|
||||
|
||||
def render_irq_config(vector_width: int, groups: list[IrqGroup]) -> str:
|
||||
lines = [
|
||||
"// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit.",
|
||||
"`ifndef RETROSOC_SOC_IRQ_CONFIG_SVH",
|
||||
"`define RETROSOC_SOC_IRQ_CONFIG_SVH",
|
||||
"",
|
||||
f"`define SOC_IRQ_VECTOR_WIDTH {vector_width}",
|
||||
]
|
||||
lines.extend(f"`define {irq_width_macro(group)} {group.width}" for group in groups)
|
||||
lines.extend(["", "`endif"])
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_irq_group_bindings(group: IrqGroup, interrupts: list[Interrupt]) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit."]
|
||||
for interrupt in interrupts:
|
||||
if interrupt.group != group.name:
|
||||
continue
|
||||
lines.extend(
|
||||
[
|
||||
f" // Core IRQ {interrupt.core_bit}: {interrupt.name} ({interrupt.description}).",
|
||||
f" assign irq_o[{interrupt.group_bit}] = {interrupt.signal};",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def render_irq_wiring(
|
||||
vector_width: int, groups: list[IrqGroup], interrupts: list[Interrupt]
|
||||
) -> str:
|
||||
group_signals = {group.name: group.top_signal for group in groups}
|
||||
lines = [
|
||||
"// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit.",
|
||||
" always_comb begin",
|
||||
" s_irq = '0;",
|
||||
]
|
||||
for interrupt in interrupts:
|
||||
lines.extend(
|
||||
[
|
||||
f" // {interrupt.name}: {interrupt.description}",
|
||||
f" s_irq[{interrupt.core_bit}] = {group_signals[interrupt.group]}[{interrupt.group_bit}];",
|
||||
]
|
||||
)
|
||||
lines.extend([" end", ""])
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def render_irq_sva(vector_width: int, groups: list[IrqGroup], interrupts: list[Interrupt]) -> str:
|
||||
group_signals = {group.name: group.top_signal for group in groups}
|
||||
lines = [
|
||||
"// Generated by rtl/mini/integration/generate_soc_topology.py; do not edit.",
|
||||
'`include "soc_irq_config.svh"',
|
||||
"",
|
||||
"module soc_irq_topology_sva (",
|
||||
" input logic clk_i,",
|
||||
" input logic rst_n_i,",
|
||||
" input logic [`SOC_IRQ_VECTOR_WIDTH-1:0] irq_i,",
|
||||
]
|
||||
lines.extend(
|
||||
f" input logic [`{irq_width_macro(group)}-1:0] {group.name}_irq_i," for group in groups
|
||||
)
|
||||
lines[-1] = lines[-1].removesuffix(",")
|
||||
lines.extend([");", ""])
|
||||
for interrupt in interrupts:
|
||||
lines.extend(
|
||||
[
|
||||
f" // Core IRQ {interrupt.core_bit} remains bound to {interrupt.name}.",
|
||||
" assert property (@(posedge clk_i) disable iff (!rst_n_i)",
|
||||
f" irq_i[{interrupt.core_bit}] == {interrupt.group}_irq_i[{interrupt.group_bit}]);",
|
||||
]
|
||||
)
|
||||
used_core_bits = {interrupt.core_bit for interrupt in interrupts}
|
||||
for core_bit in range(vector_width):
|
||||
if core_bit not in used_core_bits:
|
||||
lines.extend(
|
||||
[
|
||||
f" // Unallocated core IRQ {core_bit} must remain inactive.",
|
||||
" assert property (@(posedge clk_i) disable iff (!rst_n_i)",
|
||||
f" irq_i[{core_bit}] == 1'b0);",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
["", "endmodule", "", "bind retrosoc soc_irq_topology_sva u_soc_irq_topology_sva ("]
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
" .clk_i(clk_i),",
|
||||
" .rst_n_i(rst_n_i),",
|
||||
" .irq_i(s_irq),",
|
||||
]
|
||||
)
|
||||
for index, group in enumerate(groups):
|
||||
separator = "," if index != len(groups) - 1 else ""
|
||||
lines.append(f" .{group.name}_irq_i({group_signals[group.name]}){separator}")
|
||||
lines.append(");")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def generate(topology_path: Path, memory_map_path: Path, output_dir: Path) -> None:
|
||||
(
|
||||
targets,
|
||||
apb_targets,
|
||||
fabric_links,
|
||||
gpio_functions,
|
||||
irq_vector_width,
|
||||
irq_groups,
|
||||
interrupts,
|
||||
) = read_topology(topology_path, memory_map_path)
|
||||
memory_regions = read_memory_regions(memory_map_path)
|
||||
rtl_dir = output_dir / "rtl"
|
||||
atomic_write(rtl_dir / "soc_nmi_interfaces.svh", render_nmi_interfaces(targets))
|
||||
atomic_write(rtl_dir / "soc_nmi_routes.svh", render_nmi_routes(targets, memory_regions))
|
||||
atomic_write(rtl_dir / "soc_apb_interfaces.svh", render_apb_interfaces(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_bridges.svh", render_apb_bridges(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_ports.svh", render_apb_ports(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_connections.svh", render_apb_connections(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_declarations.svh", render_apb_declarations(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_request_routes.svh", render_apb_request_routes(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_select_routes.svh", render_apb_select_routes(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_apb_response_mux.svh", render_apb_response_mux(apb_targets))
|
||||
atomic_write(rtl_dir / "soc_fabric_interfaces.svh", render_fabric_interfaces(fabric_links))
|
||||
atomic_write(
|
||||
rtl_dir / "soc_mgmt_core_wrapper_fabric.svh",
|
||||
render_fabric_connection(fabric_links, "mgmt", "nmi"),
|
||||
)
|
||||
atomic_write(
|
||||
rtl_dir / "soc_user_core_fabric.svh",
|
||||
render_fabric_connection(fabric_links, "user", "nmi"),
|
||||
)
|
||||
atomic_write(rtl_dir / "soc_bus_fabric.svh", render_bus_fabric_connections(fabric_links))
|
||||
atomic_write(
|
||||
rtl_dir / "soc_ip_nmi_wrapper_fabric.svh",
|
||||
render_fabric_connection(fabric_links, "native", "nmi")
|
||||
+ render_fabric_connection(fabric_links, "dma", "dma_nmi"),
|
||||
)
|
||||
atomic_write(
|
||||
rtl_dir / "soc_ip_apb_wrapper_fabric.svh",
|
||||
render_fabric_connection(fabric_links, "apb", "nmi"),
|
||||
)
|
||||
atomic_write(rtl_dir / "soc_gpio_alt_bindings.svh", render_gpio_bindings(gpio_functions))
|
||||
atomic_write(rtl_dir / "soc_irq_config.svh", render_irq_config(irq_vector_width, irq_groups))
|
||||
for group in irq_groups:
|
||||
atomic_write(
|
||||
rtl_dir / f"soc_{group.name}_irq_bindings.svh",
|
||||
render_irq_group_bindings(group, interrupts),
|
||||
)
|
||||
atomic_write(
|
||||
rtl_dir / "soc_irq_wiring.svh",
|
||||
render_irq_wiring(irq_vector_width, irq_groups, interrupts),
|
||||
)
|
||||
atomic_write(
|
||||
rtl_dir / "soc_irq_sva.svh",
|
||||
render_irq_sva(irq_vector_width, irq_groups, interrupts),
|
||||
)
|
||||
atomic_write(output_dir / "soc_topology.fl", f"+incdir+{rtl_dir}\n")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--map", required=True, type=Path)
|
||||
parser.add_argument("--memory-map", required=True, type=Path)
|
||||
parser.add_argument("--output-dir", type=Path)
|
||||
parser.add_argument("--check", action="store_true", help="validate only; write nothing")
|
||||
arguments = parser.parse_args()
|
||||
try:
|
||||
if arguments.check:
|
||||
read_topology(arguments.map, arguments.memory_map)
|
||||
else:
|
||||
if arguments.output_dir is None:
|
||||
parser.error("--output-dir is required unless --check is used")
|
||||
generate(arguments.map, arguments.memory_map, arguments.output_dir)
|
||||
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||
parser.error(str(error))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate scalar user core and user IP integration bindings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
|
||||
APB_REQUEST_SIGNALS = ("paddr", "pprot", "psel", "penable", "pwrite", "pwdata", "pstrb")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtensionTarget:
|
||||
slot: int
|
||||
module: str
|
||||
instance: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtensionMap:
|
||||
core_selector_width: int
|
||||
ip_selector_width: int
|
||||
gpio_width: int
|
||||
core_targets: tuple[ExtensionTarget, ...]
|
||||
ip_targets: tuple[ExtensionTarget, ...]
|
||||
|
||||
|
||||
def atomic_write(path: Path, content: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if path.exists() and path.read_text(encoding="utf-8") == content:
|
||||
return
|
||||
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
|
||||
try:
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as output:
|
||||
output.write(content)
|
||||
os.replace(temporary_name, path)
|
||||
except BaseException:
|
||||
Path(temporary_name).unlink(missing_ok=True)
|
||||
raise
|
||||
|
||||
|
||||
def require_object(value: Any, field: str) -> dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError(f"{field} must be an object")
|
||||
return value
|
||||
|
||||
|
||||
def require_identifier(value: Any, field: str) -> str:
|
||||
if not isinstance(value, str) or IDENTIFIER_RE.fullmatch(value) is None:
|
||||
raise ValueError(f"{field} must be a SystemVerilog identifier")
|
||||
return value
|
||||
|
||||
|
||||
def require_positive_integer(value: Any, field: str) -> int:
|
||||
if not isinstance(value, int) or value <= 0:
|
||||
raise ValueError(f"{field} must be a positive integer")
|
||||
return value
|
||||
|
||||
|
||||
def parse_targets(
|
||||
value: Any, field: str, selector_width: int, first_slot: int
|
||||
) -> tuple[ExtensionTarget, ...]:
|
||||
if not isinstance(value, list) or not value:
|
||||
raise ValueError(f"{field} must be a non-empty list")
|
||||
targets: list[ExtensionTarget] = []
|
||||
slots: set[int] = set()
|
||||
instances: set[str] = set()
|
||||
for index, entry in enumerate(value):
|
||||
target = require_object(entry, f"{field}[{index}]")
|
||||
slot = target.get("slot")
|
||||
if not isinstance(slot, int) or not first_slot <= slot < (1 << selector_width):
|
||||
raise ValueError(f"{field}[{index}].slot must be within the selector range")
|
||||
module = require_identifier(target.get("module"), f"{field}[{index}].module")
|
||||
instance = require_identifier(target.get("instance"), f"{field}[{index}].instance")
|
||||
if slot in slots:
|
||||
raise ValueError(f"{field} slot {slot} is duplicated")
|
||||
if instance in instances:
|
||||
raise ValueError(f"{field} instance {instance} is duplicated")
|
||||
slots.add(slot)
|
||||
instances.add(instance)
|
||||
targets.append(ExtensionTarget(slot, module, instance))
|
||||
ordered = tuple(sorted(targets, key=lambda item: item.slot))
|
||||
if [target.slot for target in ordered] != list(range(first_slot, first_slot + len(ordered))):
|
||||
raise ValueError(f"{field} slots must be contiguous from {first_slot}")
|
||||
return ordered
|
||||
|
||||
|
||||
def read_extensions(path: Path) -> ExtensionMap:
|
||||
document = require_object(json.loads(path.read_text(encoding="utf-8")), "user extensions")
|
||||
if document.get("schema_version") != 1:
|
||||
raise ValueError("schema_version must be 1")
|
||||
core_selector_width = require_positive_integer(
|
||||
document.get("core_selector_width"), "core_selector_width"
|
||||
)
|
||||
ip_selector_width = require_positive_integer(
|
||||
document.get("ip_selector_width"), "ip_selector_width"
|
||||
)
|
||||
gpio_width = require_positive_integer(document.get("gpio_width"), "gpio_width")
|
||||
return ExtensionMap(
|
||||
core_selector_width,
|
||||
ip_selector_width,
|
||||
gpio_width,
|
||||
parse_targets(document.get("core_targets"), "core_targets", core_selector_width, 0),
|
||||
parse_targets(document.get("ip_targets"), "ip_targets", ip_selector_width, 1),
|
||||
)
|
||||
|
||||
|
||||
def render_core_bindings(extensions: ExtensionMap) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_user_extensions.py; do not edit."]
|
||||
for target in extensions.core_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" nmi_if u_user_{target.slot}_nmi_if ();",
|
||||
f" logic [31:0] s_user_{target.slot}_irq;",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
" always_comb begin",
|
||||
" nmi.valid = '0;",
|
||||
" nmi.addr = '0;",
|
||||
" nmi.wdata = '0;",
|
||||
" nmi.wstrb = '0;",
|
||||
]
|
||||
)
|
||||
for target in extensions.core_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" u_user_{target.slot}_nmi_if.rdata = '0;",
|
||||
f" u_user_{target.slot}_nmi_if.ready = '0;",
|
||||
f" s_user_{target.slot}_irq = '0;",
|
||||
]
|
||||
)
|
||||
lines.append(" unique case (sel_i)")
|
||||
for target in extensions.core_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" {extensions.core_selector_width}'d{target.slot}: begin",
|
||||
f" nmi.valid = u_user_{target.slot}_nmi_if.valid;",
|
||||
f" nmi.addr = u_user_{target.slot}_nmi_if.addr;",
|
||||
f" nmi.wdata = u_user_{target.slot}_nmi_if.wdata;",
|
||||
f" nmi.wstrb = u_user_{target.slot}_nmi_if.wstrb;",
|
||||
f" u_user_{target.slot}_nmi_if.rdata = nmi.rdata;",
|
||||
f" u_user_{target.slot}_nmi_if.ready = nmi.ready;",
|
||||
f" s_user_{target.slot}_irq = irq_i;",
|
||||
" end",
|
||||
]
|
||||
)
|
||||
lines.extend([" default: ;", " endcase", " end", ""])
|
||||
for target in extensions.core_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" {target.module} #({target.slot}) {target.instance} (",
|
||||
" .clk_i (clk_i),",
|
||||
f" .rst_n_i(rst_n_i && ~core_reset_i[{target.slot}]),",
|
||||
f" .irq_i (s_user_{target.slot}_irq),",
|
||||
f" .nmi (u_user_{target.slot}_nmi_if)",
|
||||
" );",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def render_config(extensions: ExtensionMap) -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
"// Generated by rtl/mini/integration/generate_user_extensions.py; do not edit.",
|
||||
"`ifndef RETROSOC_USER_EXTENSIONS_CONFIG_SVH",
|
||||
"`define RETROSOC_USER_EXTENSIONS_CONFIG_SVH",
|
||||
"",
|
||||
f"`define USER_CORESEL_WIDTH {extensions.core_selector_width}",
|
||||
f"`define USER_CORE_COUNT {len(extensions.core_targets)}",
|
||||
f"`define USER_IPSEL_WIDTH {extensions.ip_selector_width}",
|
||||
f"`define USER_IP_COUNT {len(extensions.ip_targets)}",
|
||||
f"`define USER_GPIO_NUM {extensions.gpio_width}",
|
||||
"",
|
||||
"`endif",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def render_c_config(extensions: ExtensionMap) -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
"/* Generated by rtl/mini/integration/generate_user_extensions.py; do not edit. */",
|
||||
"#ifndef RETROSOC_GENERATED_USER_EXTENSIONS_H",
|
||||
"#define RETROSOC_GENERATED_USER_EXTENSIONS_H",
|
||||
"",
|
||||
"#include <stdint.h>",
|
||||
"",
|
||||
f"#define RS_SOC_USER_CORE_COUNT UINT32_C({len(extensions.core_targets)})",
|
||||
f"#define RS_SOC_USER_CORESEL_WIDTH UINT32_C({extensions.core_selector_width})",
|
||||
f"#define RS_SOC_USER_IP_COUNT UINT32_C({len(extensions.ip_targets)})",
|
||||
"",
|
||||
"#endif",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def render_ip_bindings(extensions: ExtensionMap) -> str:
|
||||
lines = ["// Generated by rtl/mini/integration/generate_user_extensions.py; do not edit."]
|
||||
for target in extensions.ip_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" user_gpio_if #(`USER_GPIO_NUM) u_user_{target.slot}_gpio_if ();",
|
||||
f" apb4_if u_user_{target.slot}_apb_if (clk_i, rst_n_i);",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
" always_comb begin",
|
||||
" gpio.do_o = '0;",
|
||||
" gpio.oe_o = '0;",
|
||||
" apb.pready = '0;",
|
||||
" apb.prdata = '0;",
|
||||
" apb.pslverr = '0;",
|
||||
]
|
||||
)
|
||||
for target in extensions.ip_targets:
|
||||
lines.extend(
|
||||
f" u_user_{target.slot}_apb_if.{signal} = '0;" for signal in APB_REQUEST_SIGNALS
|
||||
)
|
||||
lines.append(" unique case (sel_i)")
|
||||
for target in extensions.ip_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" {extensions.ip_selector_width}'d{target.slot}: begin",
|
||||
f" gpio.do_o = u_user_{target.slot}_gpio_if.do_o;",
|
||||
f" gpio.oe_o = u_user_{target.slot}_gpio_if.oe_o;",
|
||||
f" apb.pready = u_user_{target.slot}_apb_if.pready;",
|
||||
f" apb.prdata = u_user_{target.slot}_apb_if.prdata;",
|
||||
f" apb.pslverr = u_user_{target.slot}_apb_if.pslverr;",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
f" u_user_{target.slot}_apb_if.{signal} = apb.{signal};"
|
||||
for signal in APB_REQUEST_SIGNALS
|
||||
)
|
||||
lines.append(" end")
|
||||
lines.extend([" default: ;", " endcase", " end", ""])
|
||||
for target in extensions.ip_targets:
|
||||
lines.append(f" assign u_user_{target.slot}_gpio_if.di_i = gpio.di_i;")
|
||||
lines.append("")
|
||||
for target in extensions.ip_targets:
|
||||
lines.extend(
|
||||
[
|
||||
f" {target.module} #({target.slot}) {target.instance} (",
|
||||
" .clk_i (clk_i),",
|
||||
" .rst_n_i(rst_n_i),",
|
||||
f" .gpio (u_user_{target.slot}_gpio_if),",
|
||||
f" .apb (u_user_{target.slot}_apb_if)",
|
||||
" );",
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate(source: Path, output_dir: Path) -> None:
|
||||
extensions = read_extensions(source)
|
||||
rtl_dir = output_dir / "rtl"
|
||||
atomic_write(rtl_dir / "user_core_bindings.svh", render_core_bindings(extensions))
|
||||
atomic_write(rtl_dir / "user_ip_bindings.svh", render_ip_bindings(extensions))
|
||||
atomic_write(rtl_dir / "user_extensions_config.svh", render_config(extensions))
|
||||
atomic_write(
|
||||
output_dir / "include" / "retrosoc" / "generated" / "user_extensions.h",
|
||||
render_c_config(extensions),
|
||||
)
|
||||
atomic_write(output_dir / "user_extensions.fl", f"+incdir+{rtl_dir}\n")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--map", required=True, type=Path)
|
||||
parser.add_argument("--output-dir", type=Path)
|
||||
parser.add_argument("--check", action="store_true", help="validate only; write nothing")
|
||||
arguments = parser.parse_args()
|
||||
try:
|
||||
if arguments.check:
|
||||
read_extensions(arguments.map)
|
||||
else:
|
||||
if arguments.output_dir is None:
|
||||
parser.error("--output-dir is required unless --check is used")
|
||||
generate(arguments.map, arguments.output_dir)
|
||||
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||
parser.error(str(error))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"gpio_pins": 32,
|
||||
"irq_vector_width": 32,
|
||||
"irq_groups": [
|
||||
{"name": "nmi", "width": 10, "top_signal": "s_nmi_irq"},
|
||||
{"name": "apb", "width": 7, "top_signal": "s_apb_irq"}
|
||||
],
|
||||
"interrupts": [
|
||||
{"name": "clint_software", "description": "CLINT machine software interrupt", "group": "nmi", "group_bit": 0, "core_bit": 0, "signal": "u_clint_if.sfr_irq_o"},
|
||||
{"name": "clint_timer", "description": "CLINT machine timer interrupt", "group": "nmi", "group_bit": 1, "core_bit": 1, "signal": "u_clint_if.tmr_irq_o"},
|
||||
{"name": "uart0", "description": "Native UART0 interrupt", "group": "nmi", "group_bit": 2, "core_bit": 2, "signal": "uart.irq_o"},
|
||||
{"name": "timer0", "description": "Native timer0 interrupt", "group": "nmi", "group_bit": 3, "core_bit": 3, "signal": "s_tim0_irq"},
|
||||
{"name": "timer1", "description": "Native timer1 interrupt", "group": "nmi", "group_bit": 4, "core_bit": 4, "signal": "s_tim1_irq"},
|
||||
{"name": "psram", "description": "PSRAM controller interrupt", "group": "nmi", "group_bit": 5, "core_bit": 5, "signal": "psram.irq_o"},
|
||||
{"name": "spisd", "description": "SPI SD controller interrupt", "group": "nmi", "group_bit": 6, "core_bit": 6, "signal": "spisd.irq_o"},
|
||||
{"name": "i2c0", "description": "Native I2C0 interrupt", "group": "nmi", "group_bit": 7, "core_bit": 7, "signal": "i2c0.irq_o"},
|
||||
{"name": "i2s", "description": "Native I2S interrupt", "group": "nmi", "group_bit": 8, "core_bit": 8, "signal": "i2s.irq_o"},
|
||||
{"name": "xpi", "description": "XPI controller interrupt", "group": "nmi", "group_bit": 9, "core_bit": 9, "signal": "xpi.irq_o"},
|
||||
{"name": "uart1", "description": "APB UART1 interrupt", "group": "apb", "group_bit": 0, "core_bit": 10, "signal": "uart.irq_o"},
|
||||
{"name": "pwm", "description": "APB PWM interrupt", "group": "apb", "group_bit": 1, "core_bit": 11, "signal": "pwm.irq_o"},
|
||||
{"name": "ps2", "description": "APB PS2 interrupt", "group": "apb", "group_bit": 2, "core_bit": 12, "signal": "ps2.irq_o"},
|
||||
{"name": "rtc", "description": "APB RTC interrupt", "group": "apb", "group_bit": 3, "core_bit": 13, "signal": "u_rtc_if.irq_o"},
|
||||
{"name": "watchdog_reset", "description": "APB watchdog reset request", "group": "apb", "group_bit": 4, "core_bit": 14, "signal": "u_wdg_if.rst_o"},
|
||||
{"name": "advanced_timer", "description": "APB advanced timer interrupt", "group": "apb", "group_bit": 5, "core_bit": 15, "signal": "u_tmr_if.irq_o"},
|
||||
{"name": "reserved", "description": "Reserved interrupt slot", "group": "apb", "group_bit": 6, "core_bit": 16, "signal": "1'b0"}
|
||||
],
|
||||
"native_targets": [
|
||||
{"slot": 0, "name": "uart0", "interface": "u_uart_nmi_if", "regions": ["NMI_UART0"]},
|
||||
{"slot": 1, "name": "gpio", "interface": "u_gpio_nmi_if", "regions": ["NMI_GPIO"]},
|
||||
{"slot": 2, "name": "tim0", "interface": "u_tim0_nmi_if", "regions": ["NMI_TIM0"]},
|
||||
{"slot": 3, "name": "tim1", "interface": "u_tim1_nmi_if", "regions": ["NMI_TIM1"]},
|
||||
{"slot": 4, "name": "psram", "interface": "u_psram_nmi_if", "regions": ["NMI_PSRAM", "PSRAM"]},
|
||||
{"slot": 5, "name": "spisd", "interface": "u_spisd_nmi_if", "regions": ["NMI_SPISD", "SPISD"]},
|
||||
{"slot": 6, "name": "i2c0", "interface": "u_i2c0_nmi_if", "regions": ["NMI_I2C0"]},
|
||||
{"slot": 7, "name": "i2s", "interface": "u_i2s_nmi_if", "regions": ["NMI_I2S"]},
|
||||
{"slot": 8, "name": "onewire", "interface": "u_onewire_nmi_if", "regions": ["NMI_ONEWIRE"]},
|
||||
{"slot": 9, "name": "xpi", "interface": "u_xpi_nmi_if", "regions": ["NMI_XPI", "FLASH", "XPI"]},
|
||||
{"slot": 10, "name": "dma", "interface": "u_dma_nmi_if", "regions": ["NMI_DMA"]},
|
||||
{"slot": 11, "name": "sysctrl", "interface": "u_sysctrl_nmi_if", "regions": ["NMI_SYSCTRL"]},
|
||||
{"slot": 12, "name": "clint", "interface": "u_clint_nmi_if", "regions": ["NMI_CLINT"]},
|
||||
{"slot": 13, "name": "sdram", "interface": "u_sdram_nmi_if", "regions": ["NMI_SDRAM", "SDRAM"]},
|
||||
{"slot": 14, "name": "dvp", "interface": "u_dvp_nmi_if", "regions": ["NMI_DVP"]},
|
||||
{"slot": 15, "name": "sdio", "interface": "u_sdio_nmi_if", "disabled": true},
|
||||
{"slot": 16, "name": "opipsram", "interface": "u_opipsram_nmi_if", "disabled": true},
|
||||
{"slot": 17, "name": "i2c1", "interface": "u_i2c1_nmi_if", "regions": ["NMI_I2C1"]}
|
||||
],
|
||||
"fabric_links": [
|
||||
{"name": "mgmt", "interface": "u_mgmt_nmi_if"},
|
||||
{"name": "user", "interface": "u_user_nmi_if"},
|
||||
{"name": "dma", "interface": "u_dma_nmi_if"},
|
||||
{"name": "native", "interface": "u_nmi_nmi_if"},
|
||||
{"name": "apb", "interface": "u_apb_nmi_if"}
|
||||
],
|
||||
"apb_targets": [
|
||||
{"slot": 0, "name": "archinfo", "timed_interface": "u_archinfo_apb_if", "pure_interface": "u_archinfo_apb_pure_if", "region": "APB_ARCHINFO"},
|
||||
{"slot": 1, "name": "rng", "timed_interface": "u_rng_apb_if", "pure_interface": "u_rng_apb_pure_if", "region": "APB_RNG"},
|
||||
{"slot": 2, "name": "uart", "timed_interface": "u_uart1_apb_if", "pure_interface": "u_uart1_apb_pure_if", "region": "APB_UART1"},
|
||||
{"slot": 3, "name": "pwm", "timed_interface": "u_pwm_apb_if", "pure_interface": "u_pwm_apb_pure_if", "region": "APB_PWM"},
|
||||
{"slot": 4, "name": "ps2", "timed_interface": "u_ps2_apb_if", "pure_interface": "u_ps2_apb_pure_if", "region": "APB_PS2"},
|
||||
{"slot": 5, "name": "rtc", "timed_interface": "u_rtc_apb_if", "pure_interface": "u_rtc_apb_pure_if", "region": "APB_RTC"},
|
||||
{"slot": 6, "name": "wdg", "timed_interface": "u_wdg_apb_if", "pure_interface": "u_wdg_apb_pure_if", "region": "APB_WDG"},
|
||||
{"slot": 7, "name": "crc", "timed_interface": "u_crc_apb_if", "pure_interface": "u_crc_apb_pure_if", "region": "APB_CRC"},
|
||||
{"slot": 8, "name": "tmr", "timed_interface": "u_tmr_apb_if", "pure_interface": "u_tmr_apb_pure_if", "region": "APB_TMR"},
|
||||
{"slot": 9, "name": "user_ip", "timed_interface": "u_user_ip_apb_if", "pure_interface": "u_user_ip_apb_pure_if", "region": "APB_USER_IP"}
|
||||
],
|
||||
"gpio_alt_functions": [
|
||||
{"pin": 0, "alt0": {"inputs": ["u_uart1_if.rx_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_clk_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 1, "alt0": {"inputs": [], "do": "u_uart1_if.tx_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_ps2_if.ps2_dat_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 2, "alt0": {"inputs": ["s_tmr_capch"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": [], "do": "u_onewire_if.dat_o", "oe": "1'b1"}},
|
||||
{"pin": 3, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[0]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.scl_i"], "do": "u_i2c1_if.scl_o", "oe": "u_i2c1_if.scl_oe_o"}},
|
||||
{"pin": 4, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[1]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.sda_i"], "do": "u_i2c1_if.sda_o", "oe": "u_i2c1_if.sda_oe_o"}},
|
||||
{"pin": 5, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[2]", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "clkdiv4_i", "oe": "1'b1"}},
|
||||
{"pin": 6, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[3]", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_spisd_if.sck_o", "oe": "1'b1"}},
|
||||
{"pin": 7, "alt0": {"inputs": ["u_i2c0_if.scl_i"], "do": "u_i2c0_if.scl_o", "oe": "u_i2c0_if.scl_oe_o"}, "alt1": {"inputs": [], "do": "u_spisd_if.nss_o", "oe": "1'b1"}},
|
||||
{"pin": 8, "alt0": {"inputs": ["u_i2c0_if.sda_i"], "do": "u_i2c0_if.sda_o", "oe": "u_i2c0_if.sda_oe_o"}, "alt1": {"inputs": [], "do": "u_spisd_if.mosi_o", "oe": "1'b1"}},
|
||||
{"pin": 9, "alt0": {"inputs": [], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_spisd_if.miso_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 10, "alt0": {"inputs": [], "do": "u_i2s_if.mclk_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.pclk_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 11, "alt0": {"inputs": [], "do": "u_i2s_if.sclk_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.href_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 12, "alt0": {"inputs": [], "do": "u_i2s_if.lrck_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.vsync_i"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 13, "alt0": {"inputs": [], "do": "u_i2s_if.dacdat_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.dat_i[0]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 14, "alt0": {"inputs": ["u_i2s_if.adcdat_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_dvp_if.dat_i[1]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 15, "alt0": {"inputs": [], "do": "u_sdio_if.sck_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_dvp_if.dat_i[2]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 16, "alt0": {"inputs": ["u_sdio_if.cmd_di_i"], "do": "u_sdio_if.cmd_do_o", "oe": "u_sdio_if.cmd_oe_o"}, "alt1": {"inputs": ["u_dvp_if.dat_i[3]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 17, "alt0": {"inputs": ["u_sdio_if.dat_di_i[0]"], "do": "u_sdio_if.dat_do_o[0]", "oe": "u_sdio_if.dat_oe_o[0]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[4]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 18, "alt0": {"inputs": ["u_sdio_if.dat_di_i[1]"], "do": "u_sdio_if.dat_do_o[1]", "oe": "u_sdio_if.dat_oe_o[1]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[5]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 19, "alt0": {"inputs": ["u_sdio_if.dat_di_i[2]"], "do": "u_sdio_if.dat_do_o[2]", "oe": "u_sdio_if.dat_oe_o[2]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[6]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 20, "alt0": {"inputs": ["u_sdio_if.dat_di_i[3]"], "do": "u_sdio_if.dat_do_o[3]", "oe": "u_sdio_if.dat_oe_o[3]"}, "alt1": {"inputs": ["u_dvp_if.dat_i[7]"], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 21, "alt0": {"inputs": [], "do": "u_opipsram_if.sck_o", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_psram_if.sck_o", "oe": "1'b1"}},
|
||||
{"pin": 22, "alt0": {"inputs": [], "do": "u_opipsram_if.ce_o", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[0]", "oe": "1'b1"}},
|
||||
{"pin": 23, "alt0": {"inputs": ["u_opipsram_if.io_di_i[0]"], "do": "u_opipsram_if.io_do_o[0]", "oe": "u_opipsram_if.io_oe_o[0]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[0]"], "do": "u_psram_if.io_do_o[0]", "oe": "u_psram_if.io_oe_o[0]"}},
|
||||
{"pin": 24, "alt0": {"inputs": ["u_opipsram_if.io_di_i[1]"], "do": "u_opipsram_if.io_do_o[1]", "oe": "u_opipsram_if.io_oe_o[1]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[1]"], "do": "u_psram_if.io_do_o[1]", "oe": "u_psram_if.io_oe_o[1]"}},
|
||||
{"pin": 25, "alt0": {"inputs": ["u_opipsram_if.io_di_i[2]"], "do": "u_opipsram_if.io_do_o[2]", "oe": "u_opipsram_if.io_oe_o[2]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[2]"], "do": "u_psram_if.io_do_o[2]", "oe": "u_psram_if.io_oe_o[2]"}},
|
||||
{"pin": 26, "alt0": {"inputs": ["u_opipsram_if.io_di_i[3]"], "do": "u_opipsram_if.io_do_o[3]", "oe": "u_opipsram_if.io_oe_o[3]"}, "alt1": {"inputs": ["u_psram_if.io_di_i[3]"], "do": "u_psram_if.io_do_o[3]", "oe": "u_psram_if.io_oe_o[3]"}},
|
||||
{"pin": 27, "alt0": {"inputs": ["u_opipsram_if.io_di_i[4]"], "do": "u_opipsram_if.io_do_o[4]", "oe": "u_opipsram_if.io_oe_o[4]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[1]", "oe": "1'b1"}},
|
||||
{"pin": 28, "alt0": {"inputs": ["u_opipsram_if.io_di_i[5]"], "do": "u_opipsram_if.io_do_o[5]", "oe": "u_opipsram_if.io_oe_o[5]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[2]", "oe": "1'b1"}},
|
||||
{"pin": 29, "alt0": {"inputs": ["u_opipsram_if.io_di_i[6]"], "do": "u_opipsram_if.io_do_o[6]", "oe": "u_opipsram_if.io_oe_o[6]"}, "alt1": {"inputs": [], "do": "u_psram_if.nss_o[3]", "oe": "1'b1"}},
|
||||
{"pin": 30, "alt0": {"inputs": ["u_opipsram_if.io_di_i[7]"], "do": "u_opipsram_if.io_do_o[7]", "oe": "u_opipsram_if.io_oe_o[7]"}, "alt1": {"inputs": [], "do": "1'b0", "oe": "1'b0"}},
|
||||
{"pin": 31, "alt0": {"inputs": ["u_opipsram_if.dqs_di_i"], "do": "u_opipsram_if.dqs_do_o", "oe": "u_opipsram_if.dqs_oe_o"}, "alt1": {"inputs": [], "do": "1'b0", "oe": "1'b0"}}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"core_selector_width": 5,
|
||||
"ip_selector_width": 8,
|
||||
"gpio_width": 32,
|
||||
"core_targets": [
|
||||
{"slot": 0, "module": "user_core_design_username1", "instance": "u_user_core_design_username1"},
|
||||
{"slot": 1, "module": "user_core_design_username2", "instance": "u_user_core_design_username2"},
|
||||
{"slot": 2, "module": "user_core_design_username3", "instance": "u_user_core_design_username3"},
|
||||
{"slot": 3, "module": "user_core_design_username4", "instance": "u_user_core_design_username4"},
|
||||
{"slot": 4, "module": "user_core_design_username5", "instance": "u_user_core_design_username5"},
|
||||
{"slot": 5, "module": "user_core_design_username6", "instance": "u_user_core_design_username6"}
|
||||
],
|
||||
"ip_targets": [
|
||||
{"slot": 1, "module": "user_ip_design_username1", "instance": "u_user_ip_design_username1"},
|
||||
{"slot": 2, "module": "user_ip_design_username2", "instance": "u_user_ip_design_username2"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
FORMAL_DIR := $(VARIANT_ROOT)/formal
|
||||
FORMAL_SBY ?= sby
|
||||
FORMAL_BITWUZLA ?= $(shell command -v bitwuzla)
|
||||
FORMAL_SOLVER := bitwuzla
|
||||
FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
|
||||
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
|
||||
FORMAL_DEPTH ?= 20
|
||||
FORMAL_TIMEOUT ?= 60
|
||||
FORMAL_TARGETS := bus nmi2apb sysctrl pll_rcu gpio_user
|
||||
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
|
||||
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
||||
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
|
||||
FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
|
||||
$(RTL_PATH)/formal/bus_formal_props.v \
|
||||
$(RTL_PATH)/formal/nmi2apb_formal.sv \
|
||||
$(RTL_PATH)/formal/nmi2apb_formal_props.v \
|
||||
$(RTL_PATH)/formal/sysctrl_formal.sv \
|
||||
$(RTL_PATH)/formal/sysctrl_formal_props.v \
|
||||
$(RTL_PATH)/formal/pll_rcu_formal.sv \
|
||||
$(RTL_PATH)/formal/pll_rcu_formal_props.v \
|
||||
$(RTL_PATH)/formal/gpio_user_formal.sv \
|
||||
$(RTL_PATH)/formal/gpio_user_formal_props.v \
|
||||
$(RTL_PATH)/top/bus.sv \
|
||||
$(ROOT_PATH)/rtl/ip/native/interconnect/nmi2apb.sv \
|
||||
$(ROOT_PATH)/rtl/ip/native/interconnect/nmi_regslice.sv \
|
||||
$(ROOT_PATH)/rtl/ip/native/peripheral/gpio.sv \
|
||||
$(ROOT_PATH)/rtl/ip/native/peripheral/pll_ctrl_if.sv \
|
||||
$(ROOT_PATH)/rtl/ip/native/peripheral/sysctrl.sv \
|
||||
$(RTL_PATH)/top/rcu.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/nmi_if.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/cdc/cdc_2phase.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/edge_det.sv \
|
||||
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/register.sv \
|
||||
$(ROOT_PATH)/scripts/bitwuzla_smt2.py
|
||||
FORMAL_STAMPS := $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_TARGETS)))
|
||||
FORMAL_INTERMEDIATES := $(foreach target,$(FORMAL_TARGETS), \
|
||||
$(FORMAL_DIR)/$(target)/formal.fl $(FORMAL_DIR)/$(target)/design.v \
|
||||
$(FORMAL_DIR)/$(target)/prove.sby $(FORMAL_DIR)/$(target)/cover.sby \
|
||||
$(FORMAL_DIR)/$(target)/prove.stamp \
|
||||
$(FORMAL_DIR)/$(target)/cover.stamp) $(FORMAL_SOLVER_WRAPPER)
|
||||
FORMAL_RESULT := $(META_DIR)/formal.json
|
||||
|
||||
.SECONDARY: $(FORMAL_INTERMEDIATES)
|
||||
|
||||
$(FORMAL_DIR)/%/formal.fl: $(FORMAL_FILELIST_GENERATOR) $(FORMAL_SOURCE_FILES) \
|
||||
$(MEMORY_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) $(USER_EXTENSIONS_STAMP)
|
||||
@mkdir -p $(@D)
|
||||
python3 $(FORMAL_FILELIST_GENERATOR) --target $* --output $@ \
|
||||
--memory-map-dir $(MEMORY_MAP_DIR) --soc-topology-dir $(SOC_TOPOLOGY_DIR) \
|
||||
--user-extensions-dir $(USER_EXTENSIONS_DIR)
|
||||
|
||||
$(FORMAL_DIR)/%/design.v: $(FORMAL_DIR)/%/formal.fl $(RTL_PATH)/script/convt_sv2v.py \
|
||||
$(RTL_PATH)/script/filelist.py
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool formal-sv2v \
|
||||
--log $(@D)/sv2v.log --result $(@D)/result-sv2v.json \
|
||||
-- python3 $(RTL_PATH)/script/convt_sv2v.py -f $< --output $@
|
||||
|
||||
$(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.v \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
||||
--properties $(RTL_PATH)/formal/$*_formal_props.v --solver $(FORMAL_SOLVER) \
|
||||
--mode prove --depth $(FORMAL_DEPTH) --output $@
|
||||
|
||||
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.v \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
|
||||
--properties $(RTL_PATH)/formal/$*_formal_props.v --solver $(FORMAL_SOLVER) \
|
||||
--mode cover --depth $(FORMAL_DEPTH) --output $@
|
||||
|
||||
$(FORMAL_SOLVER_WRAPPER): $(ROOT_PATH)/scripts/bitwuzla_smt2.py
|
||||
@mkdir -p $(@D)
|
||||
cp $< $@
|
||||
chmod +x $@
|
||||
|
||||
$(FORMAL_DIR)/%/prove.stamp: $(FORMAL_DIR)/%/prove.sby $(FORMAL_SOLVER_WRAPPER)
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool formal-sby-prove \
|
||||
--log $(@D)/prove.log --result $(@D)/result-prove.json \
|
||||
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
|
||||
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
|
||||
-- timeout --foreground --kill-after=5s $(FORMAL_TIMEOUT)s $(FORMAL_SBY) \
|
||||
-f -d $(@D)/prove $<
|
||||
@test "$$(awk '{print $$1}' $(@D)/prove/status)" = PASS
|
||||
@touch $@
|
||||
|
||||
$(FORMAL_DIR)/%/cover.stamp: $(FORMAL_DIR)/%/cover.sby $(FORMAL_SOLVER_WRAPPER)
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool formal-sby-cover \
|
||||
--log $(@D)/cover.log --result $(@D)/result-cover.json \
|
||||
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
|
||||
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
|
||||
-- timeout --foreground --kill-after=5s $(FORMAL_TIMEOUT)s $(FORMAL_SBY) \
|
||||
-f -d $(@D)/cover $<
|
||||
@test "$$(awk '{print $$1}' $(@D)/cover/status)" = PASS
|
||||
@touch $@
|
||||
|
||||
$(FORMAL_DIR)/%/.stamp: $(FORMAL_DIR)/%/prove.stamp $(FORMAL_DIR)/%/cover.stamp
|
||||
@touch $@
|
||||
|
||||
$(FORMAL_RESULT): $(FORMAL_STAMPS) $(FORMAL_RESULT_GENERATOR)
|
||||
python3 $(FORMAL_RESULT_GENERATOR) --output $@ \
|
||||
$(foreach target,$(FORMAL_TARGETS),--proof $(target)=$(FORMAL_DIR)/$(target))
|
||||
|
||||
formal: $(FORMAL_RESULT) | manifest
|
||||
|
||||
formal-bus: $(FORMAL_DIR)/bus/.stamp | manifest
|
||||
|
||||
formal-nmi2apb: $(FORMAL_DIR)/nmi2apb/.stamp | manifest
|
||||
|
||||
formal-sysctrl: $(FORMAL_DIR)/sysctrl/.stamp | manifest
|
||||
|
||||
formal-pll-rcu: $(FORMAL_DIR)/pll_rcu/.stamp | manifest
|
||||
|
||||
formal-gpio-user: $(FORMAL_DIR)/gpio_user/.stamp | manifest
|
||||
|
||||
formal-doctor:
|
||||
$(MAKE) FORMAL=YES SIMU=IVERILOG SYNTH=YOSYS STA=NONE doctor
|
||||
|
||||
formal-clean:
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
|
||||
|
||||
.PHONY: formal formal-bus formal-nmi2apb formal-sysctrl formal-pll-rcu formal-gpio-user formal-doctor formal-clean
|
||||
+25
-5
@@ -22,13 +22,17 @@ IVERILOG_POST_SIMV := $(IVERILOG_POST_DIR)/simv
|
||||
IVERILOG_BEHV_DEPFILE := $(IVERILOG_BEHV_DIR)/simv.d
|
||||
IVERILOG_NETL_DEPFILE := $(IVERILOG_NETL_DIR)/simv.d
|
||||
IVERILOG_POST_DEPFILE := $(IVERILOG_POST_DIR)/simv.d
|
||||
IVERILOG_FILELIST_GEN := $(RTL_PATH)/script/gen_iverilog_filelist.py
|
||||
NETLIST_SIM_MODELS := $(ROOT_PATH)/rtl/tech/netlist_sim_cells.v $(ROOT_PATH)/rtl/tech/gf180_sim_cells.v
|
||||
TECH_CELL_TEST := $(ROOT_PATH)/tests/rtl/tc_pdk_cells_tb.sv
|
||||
TECH_CELL_TEST_DIR := $(IVERILOG_ROOT)/tech-cells
|
||||
|
||||
-include $(CONVERTED_DEPFILE) $(IVERILOG_BEHV_DEPFILE) $(IVERILOG_NETL_DEPFILE) $(IVERILOG_POST_DEPFILE)
|
||||
|
||||
IVERILOG_COMMON_OPTS := -g2012
|
||||
IVERILOG_TIME_OPTS := -gno-specify
|
||||
IVERILOG_POST_OPTS := -gspecify -Tmin
|
||||
IVERILOG_SIM_OPTS := +core_sel=$(RTL_SIM_CORESEL) +sim_timeout=$(RTL_SIM_TIMEOUT) +wave_$(WAVE)
|
||||
IVERILOG_SIM_OPTS := +sim_timeout=$(RTL_SIM_TIMEOUT) +wave_$(WAVE)
|
||||
|
||||
$(CONVERTED_SOC): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP)
|
||||
@mkdir -p $(@D)
|
||||
@@ -36,19 +40,19 @@ $(CONVERTED_SOC): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP)
|
||||
python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) --target $@ \
|
||||
--output $(CONVERTED_DEPFILE)
|
||||
|
||||
$(IVERILOG_BEHV_FLIST): $(FILELIST_STAMP) $(CONVERTED_SOC)
|
||||
$(IVERILOG_BEHV_FLIST): $(FILELIST_STAMP) $(CONVERTED_SOC) $(IVERILOG_FILELIST_GEN)
|
||||
@mkdir -p $(@D)
|
||||
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
|
||||
--mode behv --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
|
||||
--pin-map-rtl-dir $(PIN_MAP_DIR)/rtl --output $@ --converted $(CONVERTED_SOC)
|
||||
|
||||
$(IVERILOG_NETL_FLIST): $(FILELIST_STAMP) $(NETLIST_PATH)
|
||||
$(IVERILOG_NETL_FLIST): $(FILELIST_STAMP) $(NETLIST_PATH) $(IVERILOG_FILELIST_GEN) $(NETLIST_SIM_MODELS)
|
||||
@mkdir -p $(@D)
|
||||
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
|
||||
--mode netl --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
|
||||
--pin-map-rtl-dir $(PIN_MAP_DIR)/rtl --output $@ --netlist $(NETLIST_PATH)
|
||||
|
||||
$(IVERILOG_POST_FLIST): $(FILELIST_STAMP) $(POST_PATH) $(SDF_PATH)
|
||||
$(IVERILOG_POST_FLIST): $(FILELIST_STAMP) $(POST_PATH) $(SDF_PATH) $(IVERILOG_FILELIST_GEN) $(NETLIST_SIM_MODELS)
|
||||
@mkdir -p $(@D)
|
||||
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
|
||||
--mode post --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
|
||||
@@ -58,6 +62,22 @@ $(IVERILOG_POST_FLIST): $(FILELIST_STAMP) $(POST_PATH) $(SDF_PATH)
|
||||
convt_sv2v: $(CONVERTED_SOC)
|
||||
gen_iverilog_filelist: $(IVERILOG_BEHV_FLIST)
|
||||
|
||||
tech-cell-test: $(FILELIST_STAMP) $(TECH_CELL_TEST) $(ROOT_PATH)/rtl/tech/tc_io.sv \
|
||||
$(ROOT_PATH)/rtl/tech/tc_clk.sv
|
||||
@case "$(PDK)" in \
|
||||
GF180|SKY130) ;; \
|
||||
*) echo "tech-cell-test supports PDK=GF180 or PDK=SKY130" >&2; exit 2 ;; \
|
||||
esac
|
||||
@mkdir -p $(TECH_CELL_TEST_DIR)
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-tech-cells \
|
||||
--log $(TECH_CELL_TEST_DIR)/compile.log --result $(TECH_CELL_TEST_DIR)/result-compile.json \
|
||||
--cwd $(TECH_CELL_TEST_DIR) -- $(IVERILOG) $(IVERILOG_COMMON_OPTS) $(IVERILOG_TIME_OPTS) \
|
||||
-f $(GENERATED_FL_DIR)/def.fl -f $(PDK_FILELIST) $(ROOT_PATH)/rtl/tech/tc_io.sv \
|
||||
$(ROOT_PATH)/rtl/tech/tc_clk.sv $(TECH_CELL_TEST) -o simv -s tc_pdk_cells_tb
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-tech-cells-sim \
|
||||
--log $(TECH_CELL_TEST_DIR)/sim.log --result $(TECH_CELL_TEST_DIR)/result-sim.json \
|
||||
--cwd $(TECH_CELL_TEST_DIR) -- $(VVP) simv
|
||||
|
||||
$(IVERILOG_BEHV_SIMV): $(IVERILOG_BEHV_FLIST)
|
||||
@mkdir -p $(IVERILOG_BEHV_DIR)
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog \
|
||||
@@ -128,4 +148,4 @@ postwave:
|
||||
clean:
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(IVERILOG_ROOT)
|
||||
|
||||
.PHONY: convt_sv2v gen_iverilog_filelist comp netcomp postcomp sim netsim postsim wave netwave postwave clean
|
||||
.PHONY: convt_sv2v gen_iverilog_filelist tech-cell-test comp netcomp postcomp sim netsim postsim wave netwave postwave clean
|
||||
@@ -40,9 +40,8 @@ ISA_FLAGS += -march=rv32im
|
||||
endif
|
||||
endif
|
||||
|
||||
DEF_VAL += -DCORE_$(CORE)
|
||||
DEF_VAL += -DIP_$(IP)
|
||||
DEF_VAL += -DAPP_$(APP)
|
||||
DEF_VAL += -DRS_SOC_MGMT_CORE_$(CORE)
|
||||
DEF_VAL += -DCOMPILER_NAME='"$(CC)"'
|
||||
DEF_VAL += -DCOMPILER_CFLAGS='"$(GCC_FLAGS) $(SW_WARN_FLAGS)"'
|
||||
DEF_VAL += -DCOMPILER_ISA='"$(ISA_FLAGS)"'
|
||||
@@ -83,7 +82,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
|
||||
$(ROOT_PATH)/crt/src/hal/lcd.c \
|
||||
$(ROOT_PATH)/crt/src/hal/psram.c \
|
||||
$(ROOT_PATH)/crt/src/hal/spisd.c \
|
||||
$(ROOT_PATH)/crt/src/hal/qspi.c
|
||||
$(ROOT_PATH)/crt/src/hal/qspi.c \
|
||||
$(ROOT_PATH)/crt/src/hal/user_core.c
|
||||
|
||||
ifeq ($(HAVE_CSR),YES)
|
||||
CRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/system_irq.S
|
||||
@@ -100,9 +100,7 @@ $(error Application profile not found: $(APP_MK))
|
||||
endif
|
||||
include $(APP_MK)
|
||||
|
||||
ifneq ($(findstring MDD,$(CORE) $(IP)),)
|
||||
APP_INC_DIRS += $(MPW_OUTPUT_DIR)
|
||||
endif
|
||||
|
||||
ifneq ($(filter RV32E RV32I,$(ISA)),)
|
||||
CRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/libgcc/div.S
|
||||
@@ -112,6 +110,7 @@ endif
|
||||
|
||||
INC_PATH := -I$(SW_BUILD_DIR)/include \
|
||||
-I$(MEMORY_MAP_C_DIR) \
|
||||
-I$(USER_EXTENSIONS_DIR)/include \
|
||||
-I$(ROOT_PATH)/crt/include \
|
||||
$(addprefix -I,$(APP_INC_DIRS))
|
||||
SRC_PATH := $(CRT_SRCS) $(APP_SRCS)
|
||||
@@ -138,7 +137,7 @@ asm: $(MPW_VARIANT_STAMP)
|
||||
cp $(SW_BUILD_DIR)/asm/hello-asm.bin $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).bin
|
||||
cp $(SW_BUILD_DIR)/asm/hello-asm.txt $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME)_all.txt
|
||||
|
||||
$(FIRMWARE_ELF): $(MPW_VARIANT_STAMP) $(MEMORY_MAP_STAMP) $(VERSION_HEADER) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \
|
||||
$(FIRMWARE_ELF): $(MPW_VARIANT_STAMP) $(MEMORY_MAP_STAMP) $(USER_EXTENSIONS_STAMP) $(VERSION_HEADER) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \
|
||||
$(ROOT_PATH)/rtl/mini/mk/software.mk
|
||||
@mkdir -p $(SW_BUILD_DIR)
|
||||
cd $(SW_BUILD_DIR) && $(CP) -P -o $(LINK_TYPE).lds $(LDS_PATH)
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ POST_OPTS := -sdf min:retrosoc_tb.u_retrosoc_asic:$(SDF_PATH) \
|
||||
|
||||
TIME_OPTION := +notimingcheck +nospecify
|
||||
SIM_OPTS := +vcs+loopreport+1000 -suppress=ASLR_DETECTED_INFO \
|
||||
+core_sel=$(RTL_SIM_CORESEL) +sim_timeout=$(RTL_SIM_TIMEOUT) +wave_$(WAVE)
|
||||
+sim_timeout=$(RTL_SIM_TIMEOUT) +wave_$(WAVE)
|
||||
|
||||
VCS_BEHV_DIR := $(SIM_BUILD_ROOT)/behv
|
||||
VCS_NETL_DIR := $(SIM_BUILD_ROOT)/netl
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
BUILD_DIR := $(SIM_BUILD_ROOT)
|
||||
RTL_LINT_DIR := $(VARIANT_ROOT)/lint/verilator
|
||||
SOC_CSRC_HOME += $(RTL_PATH)/dv/verilator/csrc
|
||||
SOC_CSRC_LIB_HOME += $(RTL_PATH)/dv/verilator/csrc
|
||||
SOC_CXXFILES += $(sort $(wildcard $(SOC_CSRC_HOME)/*.cpp))
|
||||
@@ -27,10 +28,16 @@ VERILATOR_FLAGS += -o $(BUILD_DIR)/emu
|
||||
VERILATOR_FLAGS += -Mdir $(SOC_COMPILE_HOME)
|
||||
VERILATOR_FLAGS += $(SOC_VSRC_INCLPATH) $(SOC_CXXFILES) $(SOC_VXXFILES)
|
||||
|
||||
SOC_SIM_TIME ?= 40
|
||||
RTL_LINT_FLAGS := --lint-only --no-timing --top-module $(SOC_VSRC_TOP)
|
||||
RTL_LINT_FLAGS += --assert --Wall --timescale "1ns/1ns" -Wno-fatal
|
||||
RTL_LINT_FLAGS += $(SOC_VSRC_INCLPATH) $(SOC_VXXFILES)
|
||||
|
||||
SOC_SIM_TIME ?= 180
|
||||
VERILATOR_STAMP := $(BUILD_DIR)/verilate.stamp
|
||||
VERILATOR_DEPFILE := $(BUILD_DIR)/verilate.d
|
||||
VERILATOR_EMU := $(BUILD_DIR)/emu
|
||||
RTL_LINT_STAMP := $(RTL_LINT_DIR)/rtl-lint.stamp
|
||||
RTL_LINT_DEPFILE := $(RTL_LINT_DIR)/rtl-lint.d
|
||||
VERILATOR_EXTRA_SOURCES := $(RTL_PATH)/dv/model/ESP_PSRAM64H.sv \
|
||||
$(RTL_PATH)/dv/verilator/rtl/flash_read_binder.sv \
|
||||
$(RTL_PATH)/dv/verilator/rtl/QSPIFlash.sv \
|
||||
@@ -38,7 +45,7 @@ VERILATOR_EXTRA_SOURCES := $(RTL_PATH)/dv/model/ESP_PSRAM64H.sv \
|
||||
$(SOC_CXXFILES) $(wildcard $(SOC_CSRC_HOME)/*.h) \
|
||||
$(wildcard $(SOC_CSRC_HOME)/*.hpp)
|
||||
|
||||
-include $(VERILATOR_DEPFILE)
|
||||
-include $(VERILATOR_DEPFILE) $(RTL_LINT_DEPFILE)
|
||||
|
||||
CCACHE := $(shell command -v ccache 2>/dev/null)
|
||||
ifneq ($(CCACHE),)
|
||||
@@ -64,7 +71,24 @@ $(VERILATOR_EMU): $(VERILATOR_STAMP)
|
||||
-- make -j$(VERILATOR_JOBS) VM_PARALLEL_BUILDS=1 OPT_FAST=-O3 -C $(SOC_COMPILE_HOME) \
|
||||
-f V$(SOC_VSRC_TOP).mk
|
||||
|
||||
lint: $(VERILATOR_STAMP)
|
||||
$(RTL_LINT_STAMP): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) $(VERILATOR_EXTRA_SOURCES)
|
||||
@mkdir -p $(RTL_LINT_DIR)
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool rtl-lint \
|
||||
--log $(RTL_LINT_DIR)/lint.log --result $(RTL_LINT_DIR)/result-rtl-lint.json \
|
||||
-- $(VERILATOR) $(RTL_LINT_FLAGS)
|
||||
python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) \
|
||||
$(foreach source,$(VERILATOR_EXTRA_SOURCES),--extra $(source)) \
|
||||
--target $@ --output $(RTL_LINT_DEPFILE)
|
||||
@touch $@
|
||||
|
||||
rtl-lint: $(RTL_LINT_STAMP) | manifest
|
||||
|
||||
check-rtl-lint: rtl-lint
|
||||
python3 $(ROOT_PATH)/scripts/analyze_warnings.py check --root $(ROOT_PATH) \
|
||||
--profile $(PROFILE_NAME) --variant-root $(VARIANT_ROOT) --tool rtl-lint \
|
||||
--output $(META_DIR)/rtl-lint-warnings.json
|
||||
|
||||
lint: rtl-lint
|
||||
comp: $(VERILATOR_EMU)
|
||||
|
||||
sim: comp
|
||||
@@ -73,7 +97,7 @@ sim: comp
|
||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool verilator-sim \
|
||||
--log $(BUILD_DIR)/sim.log --result $(BUILD_DIR)/result-sim.json \
|
||||
--cwd $(BUILD_DIR) -- $(BUILD_DIR)/emu -i $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).bin \
|
||||
-s $(RTL_SIM_CORESEL) -t $(SOC_SIM_TIME)
|
||||
-t $(SOC_SIM_TIME)
|
||||
python3 $(ROOT_PATH)/scripts/check_simulation.py --log $(BUILD_DIR)/sim.log \
|
||||
--result $(BUILD_DIR)/result-sim-check.json --require '$(SIM_SUCCESS_MARKER)'
|
||||
|
||||
@@ -83,5 +107,6 @@ wave:
|
||||
|
||||
clean:
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(BUILD_DIR)
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(RTL_LINT_DIR)
|
||||
|
||||
.PHONY: comp sim clean
|
||||
.PHONY: lint rtl-lint check-rtl-lint comp sim clean
|
||||
@@ -13,7 +13,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
VALID_FEATURES = {"HAVE_PLL", "CORE_MDD", "IP_MDD"}
|
||||
VALID_FEATURES = {"HAVE_PLL"}
|
||||
VALID_KINDS = {
|
||||
"full",
|
||||
"schmitt_input",
|
||||
@@ -112,7 +112,11 @@ def parse_pad(entry: dict[str, Any], defaults: dict[str, Any], index: int | None
|
||||
def expand_group(group: Any, index: int) -> list[Pad]:
|
||||
if not isinstance(group, dict):
|
||||
raise ValueError(f"pads[{index}] must be an object")
|
||||
defaults = {key: value for key, value in group.items() if key not in {"ports", "count", "prefix", "suffix"}}
|
||||
defaults = {
|
||||
key: value
|
||||
for key, value in group.items()
|
||||
if key not in {"ports", "count", "prefix", "suffix"}
|
||||
}
|
||||
ports = group.get("ports")
|
||||
if ports is not None:
|
||||
if not isinstance(ports, list) or not ports:
|
||||
|
||||
@@ -24,15 +24,6 @@
|
||||
{"name": "xo_o_pad", "direction": "output", "bind": false}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "tri_input",
|
||||
"direction": "inout",
|
||||
"feature": "CORE_MDD",
|
||||
"prefix": "core_sel_",
|
||||
"suffix": "_i_pad",
|
||||
"count": 5,
|
||||
"signal": "s_core_sel[{index}]"
|
||||
},
|
||||
{
|
||||
"kind": "full",
|
||||
"direction": "inout",
|
||||
@@ -128,11 +119,6 @@
|
||||
"audclk_i_pad": "s_aud_clk",
|
||||
"ext_rst_n_i_pad": "rst_n_i",
|
||||
"xi_i_pad": "s_sys_clk",
|
||||
"core_sel_0_i_pad": "1'b0",
|
||||
"core_sel_1_i_pad": "1'b0",
|
||||
"core_sel_2_i_pad": "1'b0",
|
||||
"core_sel_3_i_pad": "1'b0",
|
||||
"core_sel_4_i_pad": "1'b0",
|
||||
"gpio_0_io_pad": "ps2_clk_i",
|
||||
"gpio_1_io_pad": "ps2_dat_i",
|
||||
"gpio_2_io_pad": "onewire_dat_o",
|
||||
@@ -208,11 +194,6 @@
|
||||
"bindings": {
|
||||
"extclk_i_pad": "s_clk",
|
||||
"ext_rst_n_i_pad": "s_rst_n",
|
||||
"core_sel_0_i_pad": "s_core_sel[0]",
|
||||
"core_sel_1_i_pad": "s_core_sel[1]",
|
||||
"core_sel_2_i_pad": "s_core_sel[2]",
|
||||
"core_sel_3_i_pad": "s_core_sel[3]",
|
||||
"core_sel_4_i_pad": "s_core_sel[4]",
|
||||
"gpio_21_io_pad": "s_psram_sck",
|
||||
"gpio_22_io_pad": "s_psram_nss0",
|
||||
"gpio_23_io_pad": "s_psram_dat0",
|
||||
@@ -233,11 +214,6 @@
|
||||
"audclk_i_pad": "s_aud_clk",
|
||||
"ext_rst_n_i_pad": "s_rst_n",
|
||||
"xi_i_pad": "r_xtal_clk",
|
||||
"core_sel_0_i_pad": "s_core_sel[0]",
|
||||
"core_sel_1_i_pad": "s_core_sel[1]",
|
||||
"core_sel_2_i_pad": "s_core_sel[2]",
|
||||
"core_sel_3_i_pad": "s_core_sel[3]",
|
||||
"core_sel_4_i_pad": "s_core_sel[4]",
|
||||
"gpio_0_io_pad": "s_gpio_0_io",
|
||||
"gpio_1_io_pad": "s_gpio_1_io",
|
||||
"gpio_7_io_pad": "s_i2c0_scl_io",
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import gzip
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from filelist import atomic_write, parse_filelists, write_filelist
|
||||
@@ -11,6 +12,7 @@ from filelist import atomic_write, parse_filelists, write_filelist
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
MINI_DIR = SCRIPT_DIR.parent
|
||||
ROOT_DIR = MINI_DIR.parents[1]
|
||||
DEFAULT_GENERATED_DIR = MINI_DIR / ".generated_fl"
|
||||
|
||||
|
||||
@@ -55,6 +57,50 @@ def _prepare_sdf(source: Path, output_dir: Path, scope: str) -> Path:
|
||||
return annotator
|
||||
|
||||
|
||||
def _standard_cell_models(pdk: str, netlist: Path | None = None) -> list[Path]:
|
||||
if pdk == "GF180":
|
||||
cells_dir = (
|
||||
ROOT_DIR
|
||||
/ "pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells"
|
||||
)
|
||||
sequential_models = {
|
||||
"gf180mcu_fd_sc_mcu7t5v0__dffq_1",
|
||||
"gf180mcu_fd_sc_mcu7t5v0__dffrnq_1",
|
||||
"gf180mcu_fd_sc_mcu7t5v0__dffsnq_1",
|
||||
}
|
||||
return [
|
||||
ROOT_DIR / "rtl/tech/gf180_sim_cells.v",
|
||||
*sorted(
|
||||
path
|
||||
for path in cells_dir.glob("*/*.functional.v")
|
||||
if path.name.removesuffix(".functional.v") not in sequential_models
|
||||
),
|
||||
]
|
||||
if pdk == "SKY130":
|
||||
cells_dir = ROOT_DIR / "pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells"
|
||||
if netlist is None:
|
||||
raise ValueError("SKY130 standard-cell models require a netlist")
|
||||
cell_models = {
|
||||
path.stem: path
|
||||
for path in cells_dir.glob("*/*.v")
|
||||
if "." not in path.stem
|
||||
}
|
||||
cell_types = set(
|
||||
re.findall(
|
||||
r"^\s*(sky130_fd_sc_hd__[A-Za-z0-9_]+)\s+",
|
||||
netlist.read_text(encoding="utf-8", errors="replace"),
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
)
|
||||
missing = sorted(cell_types - cell_models.keys())
|
||||
if missing:
|
||||
raise FileNotFoundError(
|
||||
"missing SKY130 functional model(s): " + ", ".join(missing)
|
||||
)
|
||||
return sorted(cell_models[cell_type] for cell_type in cell_types)
|
||||
return []
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
generated_dir = args.generated_dir.resolve()
|
||||
@@ -79,6 +125,15 @@ def main() -> int:
|
||||
|
||||
pdk_filelist = generated_dir / f"pdk_{args.pdk.lower()}.fl"
|
||||
base.extend(parse_filelists([pdk_filelist]))
|
||||
if args.mode != "behv":
|
||||
base.files.append(ROOT_DIR / "rtl/tech/netlist_sim_cells.v")
|
||||
cell_models = _standard_cell_models(args.pdk, args.netlist)
|
||||
if args.pdk == "SKY130":
|
||||
base.defines.append("+define+FUNCTIONAL")
|
||||
base.defines.append("+define+UNIT_DELAY=#0")
|
||||
# SKY130 wrappers include functional models from their cell directory.
|
||||
base.incdirs.extend(sorted(path.parent for path in cell_models))
|
||||
base.files.extend(cell_models)
|
||||
base.deduplicate()
|
||||
write_filelist(args.output.resolve(), base)
|
||||
print(f"generated {args.mode} filelist: {args.output.resolve()}")
|
||||
|
||||
@@ -26,6 +26,7 @@ PATH_PREFIXES = {
|
||||
"picorv32": RTL_DIR / "managed" / "picorv32" / "rtl",
|
||||
"tb": MINI_DIR / "dv" / "tb",
|
||||
"model": MINI_DIR / "dv" / "model",
|
||||
"sva": MINI_DIR / "dv" / "sva",
|
||||
"top": MINI_DIR / "top",
|
||||
}
|
||||
|
||||
@@ -101,9 +102,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument(
|
||||
"--output-dir", type=Path, default=DEFAULT_OUTPUT_DIR, help="output directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--define", action="append", default=[], help="filelist +define+ token"
|
||||
)
|
||||
parser.add_argument("--define", action="append", default=[], help="filelist +define+ token")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
|
||||
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user