Merge pull request #188 from retroSoC/dev

Dev
This commit is contained in:
Yuchi Miao authored and GitHub committed 2026-07-26 17:43:17 +08:00
commit 310c1e0527
163 files changed
+21994 -2552

No files matched your search

+3 -2
View File
@@ -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.
+60 -9
View File
@@ -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
+2
View File
@@ -17,4 +17,6 @@ jobs:
uses: ./.github/workflows/_regression.yml
with:
suite: nightly
pdk: IHP130
profile: configs/ci/ihp130.mk
retention_days: 90
+8 -2
View File
@@ -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
+24
View File
@@ -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
+24
View File
@@ -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
+28
View File
@@ -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
+2 -2
View File
@@ -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
+5 -2
View File
@@ -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
+43 -21
View File
@@ -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
+47 -31
View File
@@ -1,7 +1,9 @@
# retroSoC
[![quality](https://github.com/retroSoC/retroSoC/actions/workflows/quality.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/quality.yml)
[![regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression.yml)
[![IHP130 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ihp130.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ihp130.yml)
[![GF180 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-gf180.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-gf180.yml)
[![SKY130 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-sky130.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-sky130.yml)
[![nightly](https://github.com/retroSoC/retroSoC/actions/workflows/nightly.yml/badge.svg)](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
View File
@@ -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 -4
View File
@@ -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
+30 -1
View File
@@ -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
View File
@@ -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
-12
View File
@@ -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
-12
View File
@@ -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
View File
@@ -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
+16 -14
View File
@@ -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
+23
View File
@@ -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
View File
@@ -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) {
+91
View File
@@ -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);
}
-10
View File
@@ -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 *)&reg_wdg_ctrl);
printf(" 1 x CRC @%p\n", (void *)&reg_crc_ctrl);
printf(" 1 x TIMER3(ADV) @%p\n", (void *)&reg_tim3_ctrl);
#ifdef IP_MDD
printf(" 1 x USER_IP(4KiB) @%p\n", (void *)&reg_user_ip_reg0);
#endif
printf("\n");
printf("#############################################################\n");
printf("#############################################################\n");
-6
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+4 -4
View File
@@ -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
+32
View File
@@ -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.
+75
View File
@@ -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())
+83
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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`.
@@ -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
+37
View File
@@ -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"
}
+47
View File
@@ -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"
}
@@ -11,7 +11,7 @@
"signature": "c4297f454f443fed"
}
],
"profile": "hazard3-rv32im-ihp130",
"profile": "ihp130",
"schema_version": 1,
"tool": "iverilog"
}
@@ -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
@@ -81,7 +81,7 @@
"signature": "faa191e2dbf4a242"
}
],
"profile": "hazard3-rv32im-ihp130",
"profile": "ihp130",
"schema_version": 1,
"tool": "verilator"
}
@@ -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"
}
+12
View File
@@ -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"
}
+12
View File
@@ -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
+37
View File
@@ -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"
}
+47
View File
@@ -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"
}
+7
View File
@@ -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
+8 -2
View File
@@ -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
+12 -2
View File
@@ -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
+12 -146
View File
@@ -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
+13 -1
View File
@@ -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),
+1 -13
View File
@@ -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
+143 -16
View File
@@ -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
View File
@@ -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
+44 -16
View File
@@ -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
+37 -37
View File
@@ -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"}
]
}
+157
View File
@@ -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
+3 -11
View File
@@ -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
+48 -56
View File
@@ -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();
+8 -13
View File
@@ -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;
}
}
+18 -22
View File
@@ -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"
);
+2
View File
@@ -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
+1 -1
View File
@@ -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
+115
View File
@@ -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
+75
View File
@@ -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
+77
View File
@@ -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())
+130
View File
@@ -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())
+72
View File
@@ -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())
+91
View File
@@ -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
+77
View File
@@ -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
+109
View File
@@ -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
+70
View File
@@ -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
+77
View File
@@ -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
+84
View File
@@ -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
+122
View File
@@ -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
+158
View File
@@ -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
+62
View File
@@ -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())
+101
View File
@@ -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"}}
]
}
+18
View File
@@ -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"}
]
}
+122
View File
@@ -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
View File
@@ -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
+5 -6
View File
@@ -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
View File
@@ -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
+30 -5
View File
@@ -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
+6 -2
View File
@@ -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
View File
@@ -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",
+55
View File
@@ -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()}")
+2 -3
View File
@@ -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