feat: enhance build variant ID generation and regression environment setup

- Added timestamp normalization to  function in , allowing for optional timestamp input.
- Updated command-line interface to accept a timestamp argument for generating deterministic build variant IDs.
- Modified  in  to include a build timestamp in the environment variables.
- Introduced a new test suite in  to validate the pin-map generator, including tests for duplicate pads and invalid entries.
- Added a test for ensuring the regression runner uses a consistent build timestamp.
- Created a new SystemVerilog testbench for the APB4 interface bridge to verify correct signal crossing.
This commit is contained in:
maksyuki committed 2026-07-21 10:59:31 +08:00
1 parent fc898bc4ab
commit f5b373fd65
28 files changed
+1082 -754

No files matched your search

+1 -1
View File
@@ -98,7 +98,7 @@ README that states its ownership, source of truth, and validation expectations.
## Build and Repository Safety ## Build and Repository Safety
- Start from a committed configuration profile. Build products belong below - Start from a committed configuration profile. Build products belong below
`build/<profile>-<config-hash>/`; caches belong below `.cache/`. Do not `build/<profile>-<YYYY-MM-DD-HH-MM>-<config-hash>/`; caches belong below `.cache/`. Do not
commit generated outputs. commit generated outputs.
- Preserve unrelated worktree changes. Do not reset, discard, or reformat files - Preserve unrelated worktree changes. Do not reset, discard, or reformat files
outside the requested scope. outside the requested scope.
+8 -3
View File
@@ -51,6 +51,7 @@ LINK_TYPE ?= ld2_sram
BUILD_ROOT ?= $(ROOT_PATH)/build BUILD_ROOT ?= $(ROOT_PATH)/build
CACHE_ROOT ?= $(ROOT_PATH)/.cache/retrosoc CACHE_ROOT ?= $(ROOT_PATH)/.cache/retrosoc
BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%d-%H-%M')
MAX_JOBS ?= 16 MAX_JOBS ?= 16
HOST_CC ?= cc HOST_CC ?= cc
CLANG_FORMAT ?= clang-format-14 CLANG_FORMAT ?= clang-format-14
@@ -60,12 +61,13 @@ JOBS ?= $(shell count=$$(nproc 2>/dev/null || printf '1'); \
CONFIG_KEY_VARS := SOC CORE IP PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO HAVE_SVA \ CONFIG_KEY_VARS := SOC CORE IP PDK HAVE_PLL HAVE_SRAM_IF HAVE_SRAM_MACRO HAVE_SVA \
ISA HAVE_CSR APP LINK_TYPE RTL_TOP FIRMWARE_NAME ISA HAVE_CSR APP LINK_TYPE RTL_TOP FIRMWARE_NAME
VARIANT_ID := $(strip $(shell python3 $(ROOT_PATH)/scripts/config_key.py \ VARIANT_ID := $(strip $(shell python3 $(ROOT_PATH)/scripts/config_key.py \
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) \ --lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
$(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))))) $(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var)))))
ifeq ($(VARIANT_ID),) ifeq ($(VARIANT_ID),)
$(error Failed to calculate build variant ID) $(error Failed to calculate build variant ID)
endif endif
LOCK_DIGEST := $(strip $(shell python3 $(ROOT_PATH)/scripts/dependency_lock.py --lock $(LOCK_FILE) --digest)) LOCK_DIGEST := $(strip $(shell python3 $(ROOT_PATH)/scripts/dependency_lock.py --lock $(LOCK_FILE) --digest))
export BUILD_TIMESTAMP
VARIANT_ROOT := $(abspath $(BUILD_ROOT))/$(VARIANT_ID) VARIANT_ROOT := $(abspath $(BUILD_ROOT))/$(VARIANT_ID)
SW_BUILD_DIR := $(VARIANT_ROOT)/sw SW_BUILD_DIR := $(VARIANT_ROOT)/sw
SIM_TOOL_NAME := $(shell printf '%s' '$(SIMU)' | tr '[:upper:]' '[:lower:]') SIM_TOOL_NAME := $(shell printf '%s' '$(SIMU)' | tr '[:upper:]' '[:lower:]')
@@ -161,7 +163,8 @@ endif
.PHONY: help config doctor setup setup-mpw setup-core setup-clusterip setup-ip setup-pdk setup-app \ .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 \ clean-all purge-cache manifest check-warnings metrics check-metrics package \
regress-pr regress-nightly sim-asm sw-format sw-format-check sw-policy-check sw-host-test regress-pr regress-nightly sim-asm sw-format sw-format-check sw-policy-check sw-host-test \
pin-map check-pin-map
.NOTPARALLEL: setup .NOTPARALLEL: setup
help: help:
@@ -178,6 +181,8 @@ help:
' config | manifest print/write the effective configuration' \ ' config | manifest print/write the effective configuration' \
' memory-map generate the selected address-map artifacts' \ ' memory-map generate the selected address-map artifacts' \
' check-memory-map validate the canonical address map' \ ' 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' \
' check-warnings | metrics analyze flow logs and reports' \ ' check-warnings | metrics analyze flow logs and reports' \
' check-metrics apply the committed metrics policy' \ ' check-metrics apply the committed metrics policy' \
' sw-format apply clang-format to self-owned embedded C code' \ ' sw-format apply clang-format to self-owned embedded C code' \
@@ -194,7 +199,7 @@ help:
config: config:
@printf '%-18s %s\n' \ @printf '%-18s %s\n' \
ROOT_PATH '$(ROOT_PATH)' CONFIG '$(or $(CONFIG_PATH),<defaults>)' \ ROOT_PATH '$(ROOT_PATH)' CONFIG '$(or $(CONFIG_PATH),<defaults>)' \
VARIANT_ID '$(VARIANT_ID)' VARIANT_ROOT '$(VARIANT_ROOT)' \ BUILD_TIMESTAMP '$(BUILD_TIMESTAMP)' VARIANT_ID '$(VARIANT_ID)' VARIANT_ROOT '$(VARIANT_ROOT)' \
JOBS '$(JOBS)' \ JOBS '$(JOBS)' \
SOC '$(SOC)' CORE '$(CORE)' IP '$(IP)' \ SOC '$(SOC)' CORE '$(CORE)' IP '$(IP)' \
SIMU '$(SIMU)' SYNTH '$(SYNTH)' STA '$(STA)' PDK '$(PDK)' \ SIMU '$(SIMU)' SYNTH '$(SYNTH)' STA '$(STA)' PDK '$(PDK)' \
+10 -1
View File
@@ -116,12 +116,21 @@ make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG \
| Nightly regression suite | `make regress-nightly` | | Nightly regression suite | `make regress-nightly` |
| Script and policy checks | `make sw-format-check sw-policy-check sw-host-test` | | Script and policy checks | `make sw-format-check sw-policy-check sw-host-test` |
Build outputs are isolated below `build/<profile>-<config-hash>/`. Each variant keeps its 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 firmware, generated sources, simulator output, synthesis and timing reports, manifest, warning
analysis, and metrics separate from other configurations. Use `make clean` to remove the analysis, and metrics separate from other configurations. Use `make clean` to remove the
selected backend, `make clean-all` to remove all build output, and `make purge-cache` to remove selected backend, `make clean-all` to remove all build output, and `make purge-cache` to remove
download and compiler caches. download and compiler caches.
`BUILD_TIMESTAMP` defaults to the local build-start time. Set it explicitly when separate Make
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
```
## Reproducibility And CI ## Reproducibility And CI
[`config/dependencies.lock.json`](config/dependencies.lock.json) is the source of truth for [`config/dependencies.lock.json`](config/dependencies.lock.json) is the source of truth for
+2
View File
@@ -9,6 +9,8 @@ the root README and subsystem guides.
baseline, scope, partial automation, and deviation process. baseline, scope, partial automation, and deviation process.
- [pll-clock-control.md](pll-clock-control.md) describes the SYSCTRL PLL - [pll-clock-control.md](pll-clock-control.md) describes the SYSCTRL PLL
register protocol and software quiesce contract. register protocol and software quiesce contract.
- [soc-integration-wiring.md](soc-integration-wiring.md) defines the generated
pin-map workflow and SoC integration boundary.
Keep policy descriptions here concise and link to executable configuration as Keep policy descriptions here concise and link to executable configuration as
the source of truth. Changes that alter process requirements must also update the source of truth. Changes that alter process requirements must also update
+7 -3
View File
@@ -60,13 +60,17 @@ affected implementation change.
## Build Layout ## Build Layout
The variant identifier is the profile name plus a hash over effective hardware/software build The variant identifier is the profile name, the local creation timestamp, and a hash over effective
settings and the dependency lock. Runtime-only settings such as wave capture and timeout do not hardware/software build settings and the dependency lock. The timestamp has the
create a new variant. `YYYY-MM-DD-HH-MM` format and does not alter the hash. Runtime-only settings such as wave capture
and timeout do not alter the hash either. `BUILD_TIMESTAMP` defaults to the current local minute;
set it explicitly when separate Make commands must share one variant. The regression runner exports
one timestamp to all of its child Make commands.
```text ```text
build/<variant>/ build/<variant>/
generated/mpw/<simulator>/ generated/mpw/<simulator>/
generated/pin_map/
sw/ sw/
sim/<simulator>/ sim/<simulator>/
syn/yosys/ syn/yosys/
+27
View File
@@ -0,0 +1,27 @@
# SoC Integration Wiring
`rtl/mini/pin_map/pin_map.json` is the single source for the Mini SoC logical
pad ABI and its ASIC, FPGA, Verilator, and RTL-testbench bindings. It does not
contain board package locations; FPGA package constraints remain in the board
`.xdc` file.
The generator emits temporary SystemVerilog include files below
`build/<variant>/generated/pin_map/`. The ASIC top includes the generated port
and pad bindings, while each platform wrapper includes its own generated
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
```
When adding a pad, define its whitelisted pad template, direction, conditional
feature, and every profile binding in the JSON map. Use `null` for an open
binding. The generator rejects duplicate pad names, unknown profile bindings,
unsupported features, and unapproved connection syntax.
SoC wrappers use the existing clusterIP interfaces for protocol boundaries.
`apb4_if_bridge` only adapts `apb4_pure_if` to `apb4_if`, and `gpio_pad_bridge`
only exposes the pad-side subset of `gpio_if`; neither module contains state.
+7
View File
@@ -4,6 +4,13 @@ This directory contains FPGA-facing top-level integration for the Mini SoC.
`mini/retrosoc_top.sv` is the FPGA top wrapper and `mini/retrosoc_top.sv` is the FPGA top wrapper and
`mini/starrysky_v2.xdc` contains board timing and pin constraints. `mini/starrysky_v2.xdc` contains board timing and pin constraints.
The wrapper consumes the generated `retrosoc_asic_fpga_mini_bindings.svh`
include. Before invoking an FPGA tool, generate the selected profile with
`make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk pin-map` and add
`build/<variant>/generated/pin_map/rtl` to the tool include path. Logical pad
names and wrapper bindings are owned by `rtl/mini/pin_map/pin_map.json`;
package locations remain in the board `.xdc` file.
FPGA constraints are board-specific implementation inputs, not generic ASIC FPGA constraints are board-specific implementation inputs, not generic ASIC
RTL. Validate changes with the relevant FPGA tool flow and hardware target; do RTL. Validate changes with the relevant FPGA tool flow and hardware target; do
not reuse them as ASIC timing constraints. Follow the RTL and hardware-facing not reuse them as ASIC timing constraints. Follow the RTL and hardware-facing
+2 -116
View File
@@ -3,7 +3,6 @@
module retrosoc_top ( module retrosoc_top (
input clk_i, input clk_i,
inout rst_n_i, inout rst_n_i,
// gpio
inout ps2_clk_i, inout ps2_clk_i,
inout ps2_dat_i, inout ps2_dat_i,
output onewire_dat_o, output onewire_dat_o,
@@ -75,7 +74,6 @@ module retrosoc_top (
inout sdram_dq15_io inout sdram_dq15_io
); );
wire s_sys_clk; wire s_sys_clk;
wire s_aud_clk; wire s_aud_clk;
clk_wiz_0 u_clk_wiz_0 ( clk_wiz_0 u_clk_wiz_0 (
@@ -85,119 +83,7 @@ module retrosoc_top (
); );
retrosoc_asic u_retrosoc ( retrosoc_asic u_retrosoc (
.extclk_i_pad (s_sys_clk), `include "retrosoc_asic_fpga_mini_bindings.svh"
.audclk_i_pad (s_aud_clk),
.ext_rst_n_i_pad (rst_n_i),
`ifdef HAVE_PLL
.xi_i_pad (s_sys_clk),
.xo_o_pad (),
`endif
`ifdef CORE_MDD
.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),
`endif
`ifdef IP_MDD
.user_gpio_0_io_pad (),
.user_gpio_1_io_pad (),
.user_gpio_2_io_pad (),
.user_gpio_3_io_pad (),
.user_gpio_4_io_pad (),
.user_gpio_5_io_pad (),
.user_gpio_6_io_pad (),
.user_gpio_7_io_pad (),
.user_gpio_8_io_pad (),
.user_gpio_9_io_pad (),
.user_gpio_10_io_pad(),
.user_gpio_11_io_pad(),
.user_gpio_12_io_pad(),
.user_gpio_13_io_pad(),
.user_gpio_14_io_pad(),
.user_gpio_15_io_pad(),
`endif
.gpio_0_io_pad (ps2_clk_i),
.gpio_1_io_pad (ps2_dat_i),
.gpio_2_io_pad (onewire_dat_o),
.gpio_3_io_pad (i2c_scl_io),
.gpio_4_io_pad (i2c_sda_io),
.gpio_5_io_pad (pwm_2_o),
.gpio_6_io_pad (spisd_sck_o),
.gpio_7_io_pad (spisd_nss_o),
.gpio_8_io_pad (spisd_mosi_o),
.gpio_9_io_pad (spisd_miso_i),
.gpio_10_io_pad (i2s_mclk_o),
.gpio_11_io_pad (i2s_sclk_o),
.gpio_12_io_pad (i2s_lrck_o),
.gpio_13_io_pad (i2s_dacdat_o),
.gpio_14_io_pad (i2s_adcdat_i),
.gpio_15_io_pad (),
.gpio_16_io_pad (),
.gpio_17_io_pad (),
.gpio_18_io_pad (),
.gpio_19_io_pad (),
.gpio_20_io_pad (),
.gpio_21_io_pad (psram_sck_o),
.gpio_22_io_pad (psram_nss0_o),
.gpio_23_io_pad (psram_dat0_io),
.gpio_24_io_pad (psram_dat1_io),
.gpio_25_io_pad (psram_dat2_io),
.gpio_26_io_pad (psram_dat3_io),
.gpio_27_io_pad (),
.gpio_28_io_pad (),
.gpio_29_io_pad (),
.gpio_30_io_pad (gpio_io30),
.gpio_31_io_pad (),
.uart0_tx_o_pad (uart0_tx_o),
.uart0_rx_i_pad (uart0_rx_i),
.xpi_sck_o_pad (xpi_sck_o),
.xpi_nss0_o_pad (xpi_nss0_o),
.xpi_nss1_o_pad (),
.xpi_nss2_o_pad (),
.xpi_nss3_o_pad (),
.xpi_dat0_io_pad (xpi_dat0_io),
.xpi_dat1_io_pad (xpi_dat1_io),
.xpi_dat2_io_pad (xpi_dat2_io),
.xpi_dat3_io_pad (xpi_dat3_io),
.sdram_clk_o_pad (sdram_clk_o),
.sdram_cke_o_pad (sdram_cke_o),
.sdram_cs_n_o_pad (sdram_cs_n_o),
.sdram_ras_n_o_pad (sdram_ras_n_o),
.sdram_cas_n_o_pad (sdram_cas_n_o),
.sdram_we_n_o_pad (sdram_we_n_o),
.sdram_ba0_o_pad (sdram_ba0_o),
.sdram_ba1_o_pad (sdram_ba1_o),
.sdram_addr0_o_pad (sdram_addr0_o),
.sdram_addr1_o_pad (sdram_addr1_o),
.sdram_addr2_o_pad (sdram_addr2_o),
.sdram_addr3_o_pad (sdram_addr3_o),
.sdram_addr4_o_pad (sdram_addr4_o),
.sdram_addr5_o_pad (sdram_addr5_o),
.sdram_addr6_o_pad (sdram_addr6_o),
.sdram_addr7_o_pad (sdram_addr7_o),
.sdram_addr8_o_pad (sdram_addr8_o),
.sdram_addr9_o_pad (sdram_addr9_o),
.sdram_addr10_o_pad (sdram_addr10_o),
.sdram_addr11_o_pad (sdram_addr11_o),
.sdram_addr12_o_pad (sdram_addr12_o),
.sdram_dqm0_o_pad (sdram_dqm0_o),
.sdram_dqm1_o_pad (sdram_dqm1_o),
.sdram_dq0_io_pad (sdram_dq0_io),
.sdram_dq1_io_pad (sdram_dq1_io),
.sdram_dq2_io_pad (sdram_dq2_io),
.sdram_dq3_io_pad (sdram_dq3_io),
.sdram_dq4_io_pad (sdram_dq4_io),
.sdram_dq5_io_pad (sdram_dq5_io),
.sdram_dq6_io_pad (sdram_dq6_io),
.sdram_dq7_io_pad (sdram_dq7_io),
.sdram_dq8_io_pad (sdram_dq8_io),
.sdram_dq9_io_pad (sdram_dq9_io),
.sdram_dq10_io_pad (sdram_dq10_io),
.sdram_dq11_io_pad (sdram_dq11_io),
.sdram_dq12_io_pad (sdram_dq12_io),
.sdram_dq13_io_pad (sdram_dq13_io),
.sdram_dq14_io_pad (sdram_dq14_io),
.sdram_dq15_io_pad (sdram_dq15_io)
); );
endmodule endmodule
+6 -2
View File
@@ -57,6 +57,10 @@ interface gpio_if ();
input alt1_oe_i, input alt1_oe_i,
output irq_o output irq_o
); );
modport pad(input oe_o, input cs_o, input pu_o, input pd_o, input do_o, output di_i);
modport soc_pad(output oe_o, output cs_o, output pu_o, output pd_o, output do_o, input di_i);
endinterface endinterface
module nmi_gpio ( module nmi_gpio (
@@ -120,8 +124,8 @@ module nmi_gpio (
assign s_gpio_alt_out[i] = s_gpio_pinmux_q[i] ? gpio.alt1_do_i[i] : gpio.alt0_do_i[i]; assign s_gpio_alt_out[i] = s_gpio_pinmux_q[i] ? gpio.alt1_do_i[i] : gpio.alt0_do_i[i];
end end
for (genvar i = 0; i < `NMI_GPIO_NUM; i++) begin : IOF_PINMUX_BLOCK for (genvar i = 0; i < `NMI_GPIO_NUM; i++) begin : IOF_PINMUX_BLOCK
assign gpio.oe_o[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_oe[i] : s_gpio_oe_q[i]; assign gpio.oe_o[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_oe[i] : s_gpio_oe_q[i];
assign gpio.do_o[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_q[i]; assign gpio.do_o[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_q[i];
end end
assign s_irq_stat = |s_gpio_istat_q; assign s_irq_stat = |s_gpio_istat_q;
assign gpio.irq_o = s_irq_stat; assign gpio.irq_o = s_irq_stat;
+17 -3
View File
@@ -7,6 +7,11 @@ MEMORY_MAP_RTL_DIR := $(MEMORY_MAP_DIR)/rtl
MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include
MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker
MEMORY_MAP_FILELIST := $(MEMORY_MAP_DIR)/memory_map.fl 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_STAMP := $(CACHE_ROOT)/stamps/mpw-$(LOCK_DIGEST).prepared
MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME) MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME)
MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp
@@ -43,8 +48,17 @@ memory-map: $(MEMORY_MAP_STAMP)
check-memory-map: check-memory-map:
python3 $(MEMORY_MAP_SCRIPT) --map $(MEMORY_MAP_SOURCE) --check python3 $(MEMORY_MAP_SCRIPT) --map $(MEMORY_MAP_SOURCE) --check
$(PIN_MAP_STAMP): $(PIN_MAP_SOURCE) $(PIN_MAP_SCRIPT)
python3 $(PIN_MAP_SCRIPT) --map $(PIN_MAP_SOURCE) --output-dir $(PIN_MAP_DIR)
@touch $@
pin-map: $(PIN_MAP_STAMP)
check-pin-map:
python3 $(PIN_MAP_SCRIPT) --map $(PIN_MAP_SOURCE) --check
$(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \ $(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py \
$(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP)
python3 $(RTL_PATH)/script/generate_filelist.py \ python3 $(RTL_PATH)/script/generate_filelist.py \
--output-dir $(GENERATED_FL_DIR) \ --output-dir $(GENERATED_FL_DIR) \
$(foreach define,$(DEF_LIST),--define $(define)) $(foreach define,$(DEF_LIST),--define $(define))
@@ -71,7 +85,7 @@ NET_FLIST += -f $(GENERATED_FL_DIR)/sys_def.fl
NET_FLIST += -f $(GENERATED_FL_DIR)/commonip.fl NET_FLIST += -f $(GENERATED_FL_DIR)/commonip.fl
NET_FLIST += -f $(GENERATED_FL_DIR)/inc.fl NET_FLIST += -f $(GENERATED_FL_DIR)/inc.fl
RTL_FLIST := -f $(MEMORY_MAP_FILELIST) $(NET_FLIST) RTL_FLIST := -f $(MEMORY_MAP_FILELIST) -f $(PIN_MAP_FILELIST) $(NET_FLIST)
RTL_FLIST += -f $(GENERATED_FL_DIR)/clusterip.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/clusterip.fl
RTL_FLIST += -f $(GENERATED_FL_DIR)/ip.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/ip.fl
RTL_FLIST += -f $(GENERATED_FL_DIR)/tech.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/tech.fl
@@ -99,4 +113,4 @@ else ifeq ($(SIMU), IVERILOG)
include $(RTL_PATH)/mk/iverilog.mk include $(RTL_PATH)/mk/iverilog.mk
endif endif
.PHONY: gen_mpw_code generate_filelist memory-map check-memory-map firmware .PHONY: gen_mpw_code generate_filelist memory-map check-memory-map pin-map check-pin-map firmware
+1 -114
View File
@@ -91,120 +91,7 @@ module retrosoc_tb;
assign s_user_gpio_0_io = r_user_gpio_0_io; assign s_user_gpio_0_io = r_user_gpio_0_io;
retrosoc_asic u_retrosoc_asic ( retrosoc_asic u_retrosoc_asic (
.extclk_i_pad (s_ext_clk), `include "retrosoc_asic_tb_bindings.svh"
.audclk_i_pad (s_aud_clk),
.ext_rst_n_i_pad (s_rst_n),
`ifdef HAVE_PLL
.xi_i_pad (r_xtal_clk),
.xo_o_pad (),
`endif
`ifdef CORE_MDD
.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]),
`endif
`ifdef IP_MDD
.user_gpio_0_io_pad (s_user_gpio_0_io),
.user_gpio_1_io_pad (),
.user_gpio_2_io_pad (),
.user_gpio_3_io_pad (),
.user_gpio_4_io_pad (),
.user_gpio_5_io_pad (),
.user_gpio_6_io_pad (),
.user_gpio_7_io_pad (),
.user_gpio_8_io_pad (),
.user_gpio_9_io_pad (),
.user_gpio_10_io_pad(),
.user_gpio_11_io_pad(),
.user_gpio_12_io_pad(),
.user_gpio_13_io_pad(),
.user_gpio_14_io_pad(),
.user_gpio_15_io_pad(),
`endif
.gpio_0_io_pad (s_gpio_0_io),
.gpio_1_io_pad (s_gpio_1_io),
.gpio_2_io_pad (),
.gpio_3_io_pad (),
.gpio_4_io_pad (),
.gpio_5_io_pad (),
.gpio_6_io_pad (),
.gpio_7_io_pad (s_i2c0_scl_io),
.gpio_8_io_pad (s_i2c0_sda_io),
.gpio_9_io_pad (),
.gpio_10_io_pad (),
.gpio_11_io_pad (),
.gpio_12_io_pad (),
.gpio_13_io_pad (),
.gpio_14_io_pad (),
.gpio_15_io_pad (),
.gpio_16_io_pad (),
.gpio_17_io_pad (),
.gpio_18_io_pad (),
.gpio_19_io_pad (),
.gpio_20_io_pad (),
.gpio_21_io_pad (s_psram_sck),
.gpio_22_io_pad (s_psram_nss0),
.gpio_23_io_pad (s_psram_dat0),
.gpio_24_io_pad (s_psram_dat1),
.gpio_25_io_pad (s_psram_dat2),
.gpio_26_io_pad (s_psram_dat3),
.gpio_27_io_pad (s_psram_nss1),
.gpio_28_io_pad (),
.gpio_29_io_pad (),
.gpio_30_io_pad (),
.gpio_31_io_pad (),
.uart0_tx_o_pad (s_uart0_tx),
.uart0_rx_i_pad (s_uart0_rx),
.xpi_sck_o_pad (s_xpi_sck_o),
.xpi_nss0_o_pad (s_xpi_nss0_o), // xpi flash
.xpi_nss1_o_pad (s_xpi_nss1_o), // qpi flash
.xpi_nss2_o_pad (s_xpi_nss2_o),
.xpi_nss3_o_pad (), // tft test
.xpi_dat0_io_pad (s_xpi_dat0_io),
.xpi_dat1_io_pad (s_xpi_dat1_io),
.xpi_dat2_io_pad (s_xpi_dat2_io),
.xpi_dat3_io_pad (s_xpi_dat3_io),
.sdram_clk_o_pad (s_sdram_clk),
.sdram_cke_o_pad (s_sdram_cke),
.sdram_cs_n_o_pad (s_sdram_cs_n),
.sdram_ras_n_o_pad (s_sdram_ras_n),
.sdram_cas_n_o_pad (s_sdram_cas_n),
.sdram_we_n_o_pad (s_sdram_we_n),
.sdram_ba0_o_pad (s_sdram_ba[0]),
.sdram_ba1_o_pad (s_sdram_ba[1]),
.sdram_addr0_o_pad (s_sdram_addr[0]),
.sdram_addr1_o_pad (s_sdram_addr[1]),
.sdram_addr2_o_pad (s_sdram_addr[2]),
.sdram_addr3_o_pad (s_sdram_addr[3]),
.sdram_addr4_o_pad (s_sdram_addr[4]),
.sdram_addr5_o_pad (s_sdram_addr[5]),
.sdram_addr6_o_pad (s_sdram_addr[6]),
.sdram_addr7_o_pad (s_sdram_addr[7]),
.sdram_addr8_o_pad (s_sdram_addr[8]),
.sdram_addr9_o_pad (s_sdram_addr[9]),
.sdram_addr10_o_pad (s_sdram_addr[10]),
.sdram_addr11_o_pad (s_sdram_addr[11]),
.sdram_addr12_o_pad (s_sdram_addr[12]),
.sdram_dqm0_o_pad (s_sdram_dqm[0]),
.sdram_dqm1_o_pad (s_sdram_dqm[1]),
.sdram_dq0_io_pad (s_sdram_dq[0]),
.sdram_dq1_io_pad (s_sdram_dq[1]),
.sdram_dq2_io_pad (s_sdram_dq[2]),
.sdram_dq3_io_pad (s_sdram_dq[3]),
.sdram_dq4_io_pad (s_sdram_dq[4]),
.sdram_dq5_io_pad (s_sdram_dq[5]),
.sdram_dq6_io_pad (s_sdram_dq[6]),
.sdram_dq7_io_pad (s_sdram_dq[7]),
.sdram_dq8_io_pad (s_sdram_dq[8]),
.sdram_dq9_io_pad (s_sdram_dq[9]),
.sdram_dq10_io_pad (s_sdram_dq[10]),
.sdram_dq11_io_pad (s_sdram_dq[11]),
.sdram_dq12_io_pad (s_sdram_dq[12]),
.sdram_dq13_io_pad (s_sdram_dq[13]),
.sdram_dq14_io_pad (s_sdram_dq[14]),
.sdram_dq15_io_pad (s_sdram_dq[15])
); );
+2 -115
View File
@@ -45,121 +45,9 @@ module retrosoc_top (
assign s_clk = ext_clk_i; assign s_clk = ext_clk_i;
assign s_rst_n = rst_n_i; assign s_rst_n = rst_n_i;
assign s_core_sel = core_sel_i; assign s_core_sel = core_sel_i;
retrosoc_asic u_retrosoc_asic ( retrosoc_asic u_retrosoc_asic (
.extclk_i_pad (s_clk), `include "retrosoc_asic_verilator_bindings.svh"
.audclk_i_pad (),
.ext_rst_n_i_pad (s_rst_n),
`ifdef HAVE_PLL
.xi_i_pad (),
.xo_o_pad (),
`endif
`ifdef CORE_MDD
.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]),
`endif
`ifdef IP_MDD
.user_gpio_0_io_pad (),
.user_gpio_1_io_pad (),
.user_gpio_2_io_pad (),
.user_gpio_3_io_pad (),
.user_gpio_4_io_pad (),
.user_gpio_5_io_pad (),
.user_gpio_6_io_pad (),
.user_gpio_7_io_pad (),
.user_gpio_8_io_pad (),
.user_gpio_9_io_pad (),
.user_gpio_10_io_pad(),
.user_gpio_11_io_pad(),
.user_gpio_12_io_pad(),
.user_gpio_13_io_pad(),
.user_gpio_14_io_pad(),
.user_gpio_15_io_pad(),
`endif
.gpio_0_io_pad (),
.gpio_1_io_pad (),
.gpio_2_io_pad (),
.gpio_3_io_pad (),
.gpio_4_io_pad (),
.gpio_5_io_pad (),
.gpio_6_io_pad (),
.gpio_7_io_pad (),
.gpio_8_io_pad (),
.gpio_9_io_pad (),
.gpio_10_io_pad (),
.gpio_11_io_pad (),
.gpio_12_io_pad (),
.gpio_13_io_pad (),
.gpio_14_io_pad (),
.gpio_15_io_pad (),
.gpio_16_io_pad (),
.gpio_17_io_pad (),
.gpio_18_io_pad (),
.gpio_19_io_pad (),
.gpio_20_io_pad (),
.gpio_21_io_pad (s_psram_sck),
.gpio_22_io_pad (s_psram_nss0),
.gpio_23_io_pad (s_psram_dat0),
.gpio_24_io_pad (s_psram_dat1),
.gpio_25_io_pad (s_psram_dat2),
.gpio_26_io_pad (s_psram_dat3),
.gpio_27_io_pad (),
.gpio_28_io_pad (),
.gpio_29_io_pad (),
.gpio_30_io_pad (),
.gpio_31_io_pad (),
.uart0_tx_o_pad (),
.uart0_rx_i_pad (),
.xpi_sck_o_pad (s_xpi_sck_o),
.xpi_nss0_o_pad (s_xpi_nss0_o),
.xpi_nss1_o_pad (),
.xpi_nss2_o_pad (),
.xpi_nss3_o_pad (),
.xpi_dat0_io_pad (s_xpi_dat0_io),
.xpi_dat1_io_pad (s_xpi_dat1_io),
.xpi_dat2_io_pad (s_xpi_dat2_io),
.xpi_dat3_io_pad (s_xpi_dat3_io),
.sdram_clk_o_pad (),
.sdram_cke_o_pad (),
.sdram_cs_n_o_pad (),
.sdram_ras_n_o_pad (),
.sdram_cas_n_o_pad (),
.sdram_we_n_o_pad (),
.sdram_ba0_o_pad (),
.sdram_ba1_o_pad (),
.sdram_addr0_o_pad (),
.sdram_addr1_o_pad (),
.sdram_addr2_o_pad (),
.sdram_addr3_o_pad (),
.sdram_addr4_o_pad (),
.sdram_addr5_o_pad (),
.sdram_addr6_o_pad (),
.sdram_addr7_o_pad (),
.sdram_addr8_o_pad (),
.sdram_addr9_o_pad (),
.sdram_addr10_o_pad (),
.sdram_addr11_o_pad (),
.sdram_addr12_o_pad (),
.sdram_dqm0_o_pad (),
.sdram_dqm1_o_pad (),
.sdram_dq0_io_pad (),
.sdram_dq1_io_pad (),
.sdram_dq2_io_pad (),
.sdram_dq3_io_pad (),
.sdram_dq4_io_pad (),
.sdram_dq5_io_pad (),
.sdram_dq6_io_pad (),
.sdram_dq7_io_pad (),
.sdram_dq8_io_pad (),
.sdram_dq9_io_pad (),
.sdram_dq10_io_pad (),
.sdram_dq11_io_pad (),
.sdram_dq12_io_pad (),
.sdram_dq13_io_pad (),
.sdram_dq14_io_pad (),
.sdram_dq15_io_pad ()
); );
QSPIFlash u_QSPIFlash ( QSPIFlash u_QSPIFlash (
@@ -177,5 +65,4 @@ module retrosoc_top (
.sio ({s_psram_dat3, s_psram_dat2, s_psram_dat1, s_psram_dat0}) .sio ({s_psram_dat3, s_psram_dat2, s_psram_dat1, s_psram_dat0})
); );
endmodule endmodule
+2
View File
@@ -1,3 +1,5 @@
/top/apb4_if_bridge.sv
/top/gpio_pad_bridge.sv
/top/core_wrapper.sv /top/core_wrapper.sv
/top/retrosoc.sv /top/retrosoc.sv
/top/retrosoc_asic.sv /top/retrosoc_asic.sv
+3 -3
View File
@@ -40,19 +40,19 @@ $(IVERILOG_BEHV_FLIST): $(FILELIST_STAMP) $(CONVERTED_SOC)
@mkdir -p $(@D) @mkdir -p $(@D)
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
--mode behv --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \ --mode behv --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
--output $@ --converted $(CONVERTED_SOC) --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)
@mkdir -p $(@D) @mkdir -p $(@D)
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
--mode netl --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \ --mode netl --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
--output $@ --netlist $(NETLIST_PATH) --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)
@mkdir -p $(@D) @mkdir -p $(@D)
python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \
--mode post --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \ --mode post --pdk $(PDK) --generated-dir $(GENERATED_FL_DIR) \
--output $@ --netlist $(POST_PATH) --sdf $(SDF_PATH) \ --pin-map-rtl-dir $(PIN_MAP_DIR)/rtl --output $@ --netlist $(POST_PATH) --sdf $(SDF_PATH) \
--sdf-scope $(SDF_SCOPE) --sdf-scope $(SDF_SCOPE)
convt_sv2v: $(CONVERTED_SOC) convt_sv2v: $(CONVERTED_SOC)
+10
View File
@@ -0,0 +1,10 @@
# Canonical SoC Pin Map
`pin_map.json` is the single source for the Mini SoC logical pad ABI and its
ASIC, FPGA, Verilator, and RTL-testbench bindings. The generator emits
temporary SystemVerilog include files below the selected build variant; do not
edit generated files.
The map intentionally does not contain FPGA package locations. Board-level
locations remain owned by the FPGA constraint file. Validate changes with
`make check-pin-map` and the pin-map tests before running a regression.
+290
View File
@@ -0,0 +1,290 @@
#!/usr/bin/env python3
"""Generate SoC pad declarations and platform bindings from one JSON 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
VALID_FEATURES = {"HAVE_PLL", "CORE_MDD", "IP_MDD"}
VALID_KINDS = {
"full",
"schmitt_input",
"sdram_dq",
"tri_input",
"tri_output",
"xpi_data",
"xtal",
}
REQUIRED_PROFILES = {"fpga_mini", "tb", "verilator"}
CONNECTION_RE = re.compile(r"(?:[A-Za-z_][A-Za-z0-9_]*)(?:\[[0-9]+\])?$")
CONSTANT_RE = re.compile(r"[0-9]+'[bBdDhHoO][0-9a-fA-F_xXzZ]+$")
@dataclass(frozen=True)
class Pad:
name: str
direction: str
feature: str | None
kind: str
index: int | None
signal: str | None
interface: str | None
peer: str | None
bind: bool
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_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 parse_feature(value: Any, field: str) -> str | None:
if value is None:
return None
feature = require_string(value, field)
if feature not in VALID_FEATURES:
raise ValueError(f"{field} must be one of {sorted(VALID_FEATURES)}")
return feature
def format_template(value: str | None, index: int | None, field: str) -> str | None:
if value is None:
return None
if not isinstance(value, str):
raise ValueError(f"{field} must be a string")
if "{index}" in value:
if index is None:
raise ValueError(f"{field} uses {{index}} without an indexed pad")
return value.format(index=index)
return value
def parse_pad(entry: dict[str, Any], defaults: dict[str, Any], index: int | None) -> Pad:
data = {**defaults, **entry}
name = require_string(format_template(data.get("name"), index, "pad.name"), "pad.name")
direction = require_string(data.get("direction"), f"{name}.direction")
if direction not in {"input", "output", "inout"}:
raise ValueError(f"{name}.direction must be input, output, or inout")
kind = require_string(data.get("kind"), f"{name}.kind")
if kind not in VALID_KINDS:
raise ValueError(f"{name}.kind is not supported")
feature = parse_feature(data.get("feature"), f"{name}.feature")
signal = format_template(data.get("signal"), index, f"{name}.signal")
interface = format_template(data.get("interface"), index, f"{name}.interface")
peer = format_template(data.get("peer"), index, f"{name}.peer")
bind = data.get("bind", True)
if not isinstance(bind, bool):
raise ValueError(f"{name}.bind must be boolean")
if kind in {"tri_input", "schmitt_input", "tri_output"} and signal is None:
raise ValueError(f"{name}.signal is required for {kind}")
if kind == "full" and (interface is None or index is None):
raise ValueError(f"{name} requires an interface and index")
if kind in {"xpi_data", "sdram_dq"} and index is None:
raise ValueError(f"{name} requires an index")
if kind == "xtal" and bind and (signal is None or peer is None):
raise ValueError(f"{name} requires signal and peer")
return Pad(name, direction, feature, kind, index, signal, interface, peer, bind)
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"}}
ports = group.get("ports")
if ports is not None:
if not isinstance(ports, list) or not ports:
raise ValueError(f"pads[{index}].ports must be a non-empty list")
if not all(isinstance(port, dict) for port in ports):
raise ValueError(f"pads[{index}].ports entries must be objects")
return [parse_pad(port, defaults, None) for port in ports]
count = group.get("count")
if not isinstance(count, int) or count <= 0:
raise ValueError(f"pads[{index}].count must be a positive integer")
prefix = require_string(group.get("prefix"), f"pads[{index}].prefix")
suffix = require_string(group.get("suffix"), f"pads[{index}].suffix")
return [
parse_pad({"name": f"{prefix}{pad_index}{suffix}"}, defaults, pad_index)
for pad_index in range(count)
]
def validate_connection(value: Any, field: str) -> str | None:
if value is None:
return None
connection = require_string(value, field)
if CONNECTION_RE.fullmatch(connection) or CONSTANT_RE.fullmatch(connection):
return connection
raise ValueError(f"{field} must be a signal, bit-select, constant, or null")
def read_map(path: Path) -> tuple[list[Pad], dict[str, dict[str, str | None]]]:
with path.open(encoding="utf-8") as source:
document = json.load(source)
if document.get("schema_version") != 1:
raise ValueError("schema_version must be 1")
groups = document.get("pads")
if not isinstance(groups, list) or not groups:
raise ValueError("pads must be a non-empty list")
pads: list[Pad] = []
for index, group in enumerate(groups):
expanded = expand_group(group, index)
if len(expanded) == 0:
raise ValueError(f"pads[{index}] does not expand to any pads")
pads.extend(expanded)
names = [pad.name for pad in pads]
if len(names) != len(set(names)):
raise ValueError("pad names must be unique")
known_names = set(names)
for pad in pads:
if pad.peer is not None and pad.peer not in known_names:
raise ValueError(f"{pad.name}.peer is not a known pad")
profiles = document.get("profiles")
if not isinstance(profiles, dict) or set(profiles) != REQUIRED_PROFILES:
raise ValueError(f"profiles must contain exactly {sorted(REQUIRED_PROFILES)}")
normalized_profiles: dict[str, dict[str, str | None]] = {}
for profile, profile_data in profiles.items():
if not isinstance(profile_data, dict):
raise ValueError(f"profiles.{profile} must be an object")
bindings = profile_data.get("bindings", {})
if not isinstance(bindings, dict):
raise ValueError(f"profiles.{profile}.bindings must be an object")
if any(name not in known_names for name in bindings):
raise ValueError(f"profiles.{profile} contains an unknown pad")
normalized_profiles[profile] = {
name: validate_connection(value, f"profiles.{profile}.bindings.{name}")
for name, value in bindings.items()
}
return pads, normalized_profiles
def wrap_feature(pad: Pad, line: str) -> list[str]:
if pad.feature is None:
return [line]
return [f"`ifdef {pad.feature}", line, "`endif"]
def render_ports(pads: list[Pad]) -> str:
lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."]
for index, pad in enumerate(pads):
suffix = "," if index != len(pads) - 1 else ""
lines.extend(wrap_feature(pad, f" {pad.direction} {pad.name}{suffix}"))
return "\n".join(lines) + "\n"
def render_pad_instance(pad: Pad) -> str | None:
instance = f"u_{pad.name}"
if pad.kind == "tri_input":
return (
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p(1'b0), .c2p_en(1'b0), "
f".p2c({pad.signal}));"
)
if pad.kind == "schmitt_input":
return (
f" tc_io_tri_schmitt_pad {instance} (.pad({pad.name}), .c2p(1'b0), "
f".c2p_en(1'b0), .p2c({pad.signal}));"
)
if pad.kind == "tri_output":
return (
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p({pad.signal}), .c2p_en(1'b1), "
".p2c());"
)
if pad.kind == "full":
return (
f" tc_io_tri_full_pad {instance} (.pad({pad.name}), .c2p({pad.interface}.do_o[{pad.index}]), "
f".c2p_en({pad.interface}.oe_o[{pad.index}]), .p2c({pad.interface}.di_i[{pad.index}]), "
f".cs({pad.interface}.cs_o[{pad.index}]), .pu({pad.interface}.pu_o[{pad.index}]), "
f".pd({pad.interface}.pd_o[{pad.index}]));"
)
if pad.kind == "xpi_data":
return (
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p(u_xpi_if.io_do_o[{pad.index}]), "
f".c2p_en(u_xpi_if.io_oe_o[{pad.index}]), .p2c(u_xpi_if.io_di_i[{pad.index}]));"
)
if pad.kind == "sdram_dq":
return (
f" tc_io_tri_pad {instance} (.pad({pad.name}), .c2p(u_sdram_if.dq_o[{pad.index}]), "
f".c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[{pad.index}]));"
)
if pad.kind == "xtal" and pad.bind:
return (
f" tc_io_xtl_pad u_xtal_io_pad (.xi_pad({pad.name}), .xo_pad({pad.peer}), "
f".en(1'b1), .clk({pad.signal}));"
)
return None
def render_pad_bindings(pads: list[Pad]) -> str:
lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."]
for pad in pads:
line = render_pad_instance(pad)
if line is not None:
lines.extend(wrap_feature(pad, line))
return "\n".join(lines) + "\n"
def render_profile_bindings(pads: list[Pad], bindings: dict[str, str | None]) -> str:
lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."]
for index, pad in enumerate(pads):
connection = bindings.get(pad.name)
suffix = "," if index != len(pads) - 1 else ""
value = "" if connection is None else connection
lines.extend(wrap_feature(pad, f" .{pad.name}({value}){suffix}"))
return "\n".join(lines) + "\n"
def generate(map_path: Path, output_dir: Path) -> None:
pads, profiles = read_map(map_path)
rtl_dir = output_dir / "rtl"
atomic_write(rtl_dir / "retrosoc_asic_ports.svh", render_ports(pads))
atomic_write(rtl_dir / "retrosoc_asic_pad_bindings.svh", render_pad_bindings(pads))
for profile, bindings in profiles.items():
atomic_write(
rtl_dir / f"retrosoc_asic_{profile}_bindings.svh",
render_profile_bindings(pads, bindings),
)
atomic_write(output_dir / "pin_map.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_map(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())
+314
View File
@@ -0,0 +1,314 @@
{
"schema_version": 1,
"pads": [
{
"kind": "tri_input",
"direction": "inout",
"ports": [
{"name": "extclk_i_pad", "signal": "s_ext_clk"},
{"name": "audclk_i_pad", "signal": "s_aud_clk"}
]
},
{
"kind": "schmitt_input",
"direction": "inout",
"ports": [
{"name": "ext_rst_n_i_pad", "signal": "s_ext_rst_n"}
]
},
{
"kind": "xtal",
"feature": "HAVE_PLL",
"ports": [
{"name": "xi_i_pad", "direction": "input", "signal": "s_xtal_io", "peer": "xo_o_pad"},
{"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",
"feature": "IP_MDD",
"prefix": "user_gpio_",
"suffix": "_io_pad",
"count": 16,
"interface": "u_user_gpio_if"
},
{
"kind": "full",
"direction": "inout",
"prefix": "gpio_",
"suffix": "_io_pad",
"count": 32,
"interface": "u_gpio_if"
},
{
"kind": "tri_output",
"direction": "output",
"ports": [
{"name": "uart0_tx_o_pad", "signal": "u_uart0_if.tx_o"}
]
},
{
"kind": "tri_input",
"direction": "inout",
"ports": [
{"name": "uart0_rx_i_pad", "signal": "u_uart0_if.rx_i"}
]
},
{
"kind": "tri_output",
"direction": "output",
"ports": [
{"name": "xpi_sck_o_pad", "signal": "u_xpi_if.sck_o"}
]
},
{
"kind": "tri_output",
"direction": "output",
"prefix": "xpi_nss",
"suffix": "_o_pad",
"count": 4,
"signal": "u_xpi_if.nss_o[{index}]"
},
{
"kind": "xpi_data",
"direction": "inout",
"prefix": "xpi_dat",
"suffix": "_io_pad",
"count": 4
},
{
"kind": "tri_output",
"direction": "output",
"ports": [
{"name": "sdram_clk_o_pad", "signal": "u_sdram_if.clk_o"},
{"name": "sdram_cke_o_pad", "signal": "u_sdram_if.cke_o"},
{"name": "sdram_cs_n_o_pad", "signal": "u_sdram_if.cs_n_o"},
{"name": "sdram_ras_n_o_pad", "signal": "u_sdram_if.ras_n_o"},
{"name": "sdram_cas_n_o_pad", "signal": "u_sdram_if.cas_n_o"},
{"name": "sdram_we_n_o_pad", "signal": "u_sdram_if.we_n_o"}
]
},
{
"kind": "tri_output",
"direction": "output",
"prefix": "sdram_ba",
"suffix": "_o_pad",
"count": 2,
"signal": "u_sdram_if.ba_o[{index}]"
},
{
"kind": "tri_output",
"direction": "output",
"prefix": "sdram_addr",
"suffix": "_o_pad",
"count": 13,
"signal": "u_sdram_if.addr_o[{index}]"
},
{
"kind": "tri_output",
"direction": "output",
"prefix": "sdram_dqm",
"suffix": "_o_pad",
"count": 2,
"signal": "u_sdram_if.dqm_o[{index}]"
},
{
"kind": "sdram_dq",
"direction": "inout",
"prefix": "sdram_dq",
"suffix": "_io_pad",
"count": 16
}
],
"profiles": {
"fpga_mini": {
"bindings": {
"extclk_i_pad": "s_sys_clk",
"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",
"gpio_3_io_pad": "i2c_scl_io",
"gpio_4_io_pad": "i2c_sda_io",
"gpio_5_io_pad": "pwm_2_o",
"gpio_6_io_pad": "spisd_sck_o",
"gpio_7_io_pad": "spisd_nss_o",
"gpio_8_io_pad": "spisd_mosi_o",
"gpio_9_io_pad": "spisd_miso_i",
"gpio_10_io_pad": "i2s_mclk_o",
"gpio_11_io_pad": "i2s_sclk_o",
"gpio_12_io_pad": "i2s_lrck_o",
"gpio_13_io_pad": "i2s_dacdat_o",
"gpio_14_io_pad": "i2s_adcdat_i",
"gpio_21_io_pad": "psram_sck_o",
"gpio_22_io_pad": "psram_nss0_o",
"gpio_23_io_pad": "psram_dat0_io",
"gpio_24_io_pad": "psram_dat1_io",
"gpio_25_io_pad": "psram_dat2_io",
"gpio_26_io_pad": "psram_dat3_io",
"gpio_30_io_pad": "gpio_io30",
"uart0_tx_o_pad": "uart0_tx_o",
"uart0_rx_i_pad": "uart0_rx_i",
"xpi_sck_o_pad": "xpi_sck_o",
"xpi_nss0_o_pad": "xpi_nss0_o",
"xpi_dat0_io_pad": "xpi_dat0_io",
"xpi_dat1_io_pad": "xpi_dat1_io",
"xpi_dat2_io_pad": "xpi_dat2_io",
"xpi_dat3_io_pad": "xpi_dat3_io",
"sdram_clk_o_pad": "sdram_clk_o",
"sdram_cke_o_pad": "sdram_cke_o",
"sdram_cs_n_o_pad": "sdram_cs_n_o",
"sdram_ras_n_o_pad": "sdram_ras_n_o",
"sdram_cas_n_o_pad": "sdram_cas_n_o",
"sdram_we_n_o_pad": "sdram_we_n_o",
"sdram_ba0_o_pad": "sdram_ba0_o",
"sdram_ba1_o_pad": "sdram_ba1_o",
"sdram_addr0_o_pad": "sdram_addr0_o",
"sdram_addr1_o_pad": "sdram_addr1_o",
"sdram_addr2_o_pad": "sdram_addr2_o",
"sdram_addr3_o_pad": "sdram_addr3_o",
"sdram_addr4_o_pad": "sdram_addr4_o",
"sdram_addr5_o_pad": "sdram_addr5_o",
"sdram_addr6_o_pad": "sdram_addr6_o",
"sdram_addr7_o_pad": "sdram_addr7_o",
"sdram_addr8_o_pad": "sdram_addr8_o",
"sdram_addr9_o_pad": "sdram_addr9_o",
"sdram_addr10_o_pad": "sdram_addr10_o",
"sdram_addr11_o_pad": "sdram_addr11_o",
"sdram_addr12_o_pad": "sdram_addr12_o",
"sdram_dqm0_o_pad": "sdram_dqm0_o",
"sdram_dqm1_o_pad": "sdram_dqm1_o",
"sdram_dq0_io_pad": "sdram_dq0_io",
"sdram_dq1_io_pad": "sdram_dq1_io",
"sdram_dq2_io_pad": "sdram_dq2_io",
"sdram_dq3_io_pad": "sdram_dq3_io",
"sdram_dq4_io_pad": "sdram_dq4_io",
"sdram_dq5_io_pad": "sdram_dq5_io",
"sdram_dq6_io_pad": "sdram_dq6_io",
"sdram_dq7_io_pad": "sdram_dq7_io",
"sdram_dq8_io_pad": "sdram_dq8_io",
"sdram_dq9_io_pad": "sdram_dq9_io",
"sdram_dq10_io_pad": "sdram_dq10_io",
"sdram_dq11_io_pad": "sdram_dq11_io",
"sdram_dq12_io_pad": "sdram_dq12_io",
"sdram_dq13_io_pad": "sdram_dq13_io",
"sdram_dq14_io_pad": "sdram_dq14_io",
"sdram_dq15_io_pad": "sdram_dq15_io"
}
},
"verilator": {
"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",
"gpio_24_io_pad": "s_psram_dat1",
"gpio_25_io_pad": "s_psram_dat2",
"gpio_26_io_pad": "s_psram_dat3",
"xpi_sck_o_pad": "s_xpi_sck_o",
"xpi_nss0_o_pad": "s_xpi_nss0_o",
"xpi_dat0_io_pad": "s_xpi_dat0_io",
"xpi_dat1_io_pad": "s_xpi_dat1_io",
"xpi_dat2_io_pad": "s_xpi_dat2_io",
"xpi_dat3_io_pad": "s_xpi_dat3_io"
}
},
"tb": {
"bindings": {
"extclk_i_pad": "s_ext_clk",
"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]",
"user_gpio_0_io_pad": "s_user_gpio_0_io",
"gpio_0_io_pad": "s_gpio_0_io",
"gpio_1_io_pad": "s_gpio_1_io",
"gpio_7_io_pad": "s_i2c0_scl_io",
"gpio_8_io_pad": "s_i2c0_sda_io",
"gpio_21_io_pad": "s_psram_sck",
"gpio_22_io_pad": "s_psram_nss0",
"gpio_23_io_pad": "s_psram_dat0",
"gpio_24_io_pad": "s_psram_dat1",
"gpio_25_io_pad": "s_psram_dat2",
"gpio_26_io_pad": "s_psram_dat3",
"gpio_27_io_pad": "s_psram_nss1",
"uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx",
"xpi_sck_o_pad": "s_xpi_sck_o",
"xpi_nss0_o_pad": "s_xpi_nss0_o",
"xpi_nss1_o_pad": "s_xpi_nss1_o",
"xpi_nss2_o_pad": "s_xpi_nss2_o",
"xpi_dat0_io_pad": "s_xpi_dat0_io",
"xpi_dat1_io_pad": "s_xpi_dat1_io",
"xpi_dat2_io_pad": "s_xpi_dat2_io",
"xpi_dat3_io_pad": "s_xpi_dat3_io",
"sdram_clk_o_pad": "s_sdram_clk",
"sdram_cke_o_pad": "s_sdram_cke",
"sdram_cs_n_o_pad": "s_sdram_cs_n",
"sdram_ras_n_o_pad": "s_sdram_ras_n",
"sdram_cas_n_o_pad": "s_sdram_cas_n",
"sdram_we_n_o_pad": "s_sdram_we_n",
"sdram_ba0_o_pad": "s_sdram_ba[0]",
"sdram_ba1_o_pad": "s_sdram_ba[1]",
"sdram_addr0_o_pad": "s_sdram_addr[0]",
"sdram_addr1_o_pad": "s_sdram_addr[1]",
"sdram_addr2_o_pad": "s_sdram_addr[2]",
"sdram_addr3_o_pad": "s_sdram_addr[3]",
"sdram_addr4_o_pad": "s_sdram_addr[4]",
"sdram_addr5_o_pad": "s_sdram_addr[5]",
"sdram_addr6_o_pad": "s_sdram_addr[6]",
"sdram_addr7_o_pad": "s_sdram_addr[7]",
"sdram_addr8_o_pad": "s_sdram_addr[8]",
"sdram_addr9_o_pad": "s_sdram_addr[9]",
"sdram_addr10_o_pad": "s_sdram_addr[10]",
"sdram_addr11_o_pad": "s_sdram_addr[11]",
"sdram_addr12_o_pad": "s_sdram_addr[12]",
"sdram_dqm0_o_pad": "s_sdram_dqm[0]",
"sdram_dqm1_o_pad": "s_sdram_dqm[1]",
"sdram_dq0_io_pad": "s_sdram_dq[0]",
"sdram_dq1_io_pad": "s_sdram_dq[1]",
"sdram_dq2_io_pad": "s_sdram_dq[2]",
"sdram_dq3_io_pad": "s_sdram_dq[3]",
"sdram_dq4_io_pad": "s_sdram_dq[4]",
"sdram_dq5_io_pad": "s_sdram_dq[5]",
"sdram_dq6_io_pad": "s_sdram_dq[6]",
"sdram_dq7_io_pad": "s_sdram_dq[7]",
"sdram_dq8_io_pad": "s_sdram_dq[8]",
"sdram_dq9_io_pad": "s_sdram_dq[9]",
"sdram_dq10_io_pad": "s_sdram_dq[10]",
"sdram_dq11_io_pad": "s_sdram_dq[11]",
"sdram_dq12_io_pad": "s_sdram_dq[12]",
"sdram_dq13_io_pad": "s_sdram_dq[13]",
"sdram_dq14_io_pad": "s_sdram_dq[14]",
"sdram_dq15_io_pad": "s_sdram_dq[15]"
}
}
}
}
+5
View File
@@ -19,6 +19,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--mode", required=True, choices=("behv", "netl", "post")) parser.add_argument("--mode", required=True, choices=("behv", "netl", "post"))
parser.add_argument("--pdk", required=True) parser.add_argument("--pdk", required=True)
parser.add_argument("--generated-dir", type=Path, default=DEFAULT_GENERATED_DIR) parser.add_argument("--generated-dir", type=Path, default=DEFAULT_GENERATED_DIR)
parser.add_argument("--pin-map-rtl-dir", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True) parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--converted", type=Path) parser.add_argument("--converted", type=Path)
parser.add_argument("--netlist", type=Path) parser.add_argument("--netlist", type=Path)
@@ -60,6 +61,10 @@ def main() -> int:
base_names = ["def.fl", "sys_def.fl", "inc.fl", "tb.fl"] base_names = ["def.fl", "sys_def.fl", "inc.fl", "tb.fl"]
base = parse_filelists(generated_dir / name for name in base_names) base = parse_filelists(generated_dir / name for name in base_names)
base.options.insert(0, "+timescale+1ns/1ps") base.options.insert(0, "+timescale+1ns/1ps")
pin_map_rtl_dir = args.pin_map_rtl_dir.resolve()
if not pin_map_rtl_dir.is_dir():
raise FileNotFoundError(f"pin-map RTL directory not found: {pin_map_rtl_dir}")
base.incdirs.append(pin_map_rtl_dir)
if args.mode == "behv": if args.mode == "behv":
base.files.append(_require(args.converted, "converted behavioral RTL")) base.files.append(_require(args.converted, "converted behavioral RTL"))
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module apb4_if_bridge (
apb4_pure_if.slave apb_pure,
apb4_if.master timed
);
assign timed.paddr = apb_pure.paddr;
assign timed.pprot = apb_pure.pprot;
assign timed.psel = apb_pure.psel;
assign timed.penable = apb_pure.penable;
assign timed.pwrite = apb_pure.pwrite;
assign timed.pwdata = apb_pure.pwdata;
assign timed.pstrb = apb_pure.pstrb;
assign apb_pure.pready = timed.pready;
assign apb_pure.prdata = timed.prdata;
assign apb_pure.pslverr = timed.pslverr;
endmodule
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module gpio_pad_bridge (
gpio_if.pad inner,
gpio_if.soc_pad outer
);
assign outer.oe_o = inner.oe_o;
assign outer.cs_o = inner.cs_o;
assign outer.pu_o = inner.pu_o;
assign outer.pd_o = inner.pd_o;
assign outer.do_o = inner.do_o;
assign inner.di_i = outer.di_i;
endmodule
+40 -109
View File
@@ -70,116 +70,47 @@ module ip_apb_wrapper (
`endif `endif
// verilog_format: on // verilog_format: on
// archinfo apb4_if_bridge u_archinfo_apb_bridge (
assign u_archinfo_apb_if.paddr = u_archinfo_apb_pure_if.paddr; .apb_pure(u_archinfo_apb_pure_if),
assign u_archinfo_apb_if.pprot = u_archinfo_apb_pure_if.pprot; .timed (u_archinfo_apb_if)
assign u_archinfo_apb_if.psel = u_archinfo_apb_pure_if.psel; );
assign u_archinfo_apb_if.penable = u_archinfo_apb_pure_if.penable; apb4_if_bridge u_rng_apb_bridge (
assign u_archinfo_apb_if.pwrite = u_archinfo_apb_pure_if.pwrite; .apb_pure(u_rng_apb_pure_if),
assign u_archinfo_apb_if.pwdata = u_archinfo_apb_pure_if.pwdata; .timed (u_rng_apb_if)
assign u_archinfo_apb_if.pstrb = u_archinfo_apb_pure_if.pstrb; );
assign u_archinfo_apb_pure_if.pready = u_archinfo_apb_if.pready; apb4_if_bridge u_uart1_apb_bridge (
assign u_archinfo_apb_pure_if.prdata = u_archinfo_apb_if.prdata; .apb_pure(u_uart1_apb_pure_if),
assign u_archinfo_apb_pure_if.pslverr = u_archinfo_apb_if.pslverr; .timed (u_uart1_apb_if)
// rng );
assign u_rng_apb_if.paddr = u_rng_apb_pure_if.paddr; apb4_if_bridge u_pwm_apb_bridge (
assign u_rng_apb_if.pprot = u_rng_apb_pure_if.pprot; .apb_pure(u_pwm_apb_pure_if),
assign u_rng_apb_if.psel = u_rng_apb_pure_if.psel; .timed (u_pwm_apb_if)
assign u_rng_apb_if.penable = u_rng_apb_pure_if.penable; );
assign u_rng_apb_if.pwrite = u_rng_apb_pure_if.pwrite; apb4_if_bridge u_ps2_apb_bridge (
assign u_rng_apb_if.pwdata = u_rng_apb_pure_if.pwdata; .apb_pure(u_ps2_apb_pure_if),
assign u_rng_apb_if.pstrb = u_rng_apb_pure_if.pstrb; .timed (u_ps2_apb_if)
assign u_rng_apb_pure_if.pready = u_rng_apb_if.pready; );
assign u_rng_apb_pure_if.prdata = u_rng_apb_if.prdata; apb4_if_bridge u_rtc_apb_bridge (
assign u_rng_apb_pure_if.pslverr = u_rng_apb_if.pslverr; .apb_pure(u_rtc_apb_pure_if),
// uart1 .timed (u_rtc_apb_if)
assign u_uart1_apb_if.paddr = u_uart1_apb_pure_if.paddr; );
assign u_uart1_apb_if.pprot = u_uart1_apb_pure_if.pprot; apb4_if_bridge u_wdg_apb_bridge (
assign u_uart1_apb_if.psel = u_uart1_apb_pure_if.psel; .apb_pure(u_wdg_apb_pure_if),
assign u_uart1_apb_if.penable = u_uart1_apb_pure_if.penable; .timed (u_wdg_apb_if)
assign u_uart1_apb_if.pwrite = u_uart1_apb_pure_if.pwrite; );
assign u_uart1_apb_if.pwdata = u_uart1_apb_pure_if.pwdata; apb4_if_bridge u_crc_apb_bridge (
assign u_uart1_apb_if.pstrb = u_uart1_apb_pure_if.pstrb; .apb_pure(u_crc_apb_pure_if),
assign u_uart1_apb_pure_if.pready = u_uart1_apb_if.pready; .timed (u_crc_apb_if)
assign u_uart1_apb_pure_if.prdata = u_uart1_apb_if.prdata; );
assign u_uart1_apb_pure_if.pslverr = u_uart1_apb_if.pslverr; apb4_if_bridge u_tmr_apb_bridge (
// pwm .apb_pure(u_tmr_apb_pure_if),
assign u_pwm_apb_if.paddr = u_pwm_apb_pure_if.paddr; .timed (u_tmr_apb_if)
assign u_pwm_apb_if.pprot = u_pwm_apb_pure_if.pprot; );
assign u_pwm_apb_if.psel = u_pwm_apb_pure_if.psel;
assign u_pwm_apb_if.penable = u_pwm_apb_pure_if.penable;
assign u_pwm_apb_if.pwrite = u_pwm_apb_pure_if.pwrite;
assign u_pwm_apb_if.pwdata = u_pwm_apb_pure_if.pwdata;
assign u_pwm_apb_if.pstrb = u_pwm_apb_pure_if.pstrb;
assign u_pwm_apb_pure_if.pready = u_pwm_apb_if.pready;
assign u_pwm_apb_pure_if.prdata = u_pwm_apb_if.prdata;
assign u_pwm_apb_pure_if.pslverr = u_pwm_apb_if.pslverr;
// ps2
assign u_ps2_apb_if.paddr = u_ps2_apb_pure_if.paddr;
assign u_ps2_apb_if.pprot = u_ps2_apb_pure_if.pprot;
assign u_ps2_apb_if.psel = u_ps2_apb_pure_if.psel;
assign u_ps2_apb_if.penable = u_ps2_apb_pure_if.penable;
assign u_ps2_apb_if.pwrite = u_ps2_apb_pure_if.pwrite;
assign u_ps2_apb_if.pwdata = u_ps2_apb_pure_if.pwdata;
assign u_ps2_apb_if.pstrb = u_ps2_apb_pure_if.pstrb;
assign u_ps2_apb_pure_if.pready = u_ps2_apb_if.pready;
assign u_ps2_apb_pure_if.prdata = u_ps2_apb_if.prdata;
assign u_ps2_apb_pure_if.pslverr = u_ps2_apb_if.pslverr;
// rtc
assign u_rtc_apb_if.paddr = u_rtc_apb_pure_if.paddr;
assign u_rtc_apb_if.pprot = u_rtc_apb_pure_if.pprot;
assign u_rtc_apb_if.psel = u_rtc_apb_pure_if.psel;
assign u_rtc_apb_if.penable = u_rtc_apb_pure_if.penable;
assign u_rtc_apb_if.pwrite = u_rtc_apb_pure_if.pwrite;
assign u_rtc_apb_if.pwdata = u_rtc_apb_pure_if.pwdata;
assign u_rtc_apb_if.pstrb = u_rtc_apb_pure_if.pstrb;
assign u_rtc_apb_pure_if.pready = u_rtc_apb_if.pready;
assign u_rtc_apb_pure_if.prdata = u_rtc_apb_if.prdata;
assign u_rtc_apb_pure_if.pslverr = u_rtc_apb_if.pslverr;
// wdg
assign u_wdg_apb_if.paddr = u_wdg_apb_pure_if.paddr;
assign u_wdg_apb_if.pprot = u_wdg_apb_pure_if.pprot;
assign u_wdg_apb_if.psel = u_wdg_apb_pure_if.psel;
assign u_wdg_apb_if.penable = u_wdg_apb_pure_if.penable;
assign u_wdg_apb_if.pwrite = u_wdg_apb_pure_if.pwrite;
assign u_wdg_apb_if.pwdata = u_wdg_apb_pure_if.pwdata;
assign u_wdg_apb_if.pstrb = u_wdg_apb_pure_if.pstrb;
assign u_wdg_apb_pure_if.pready = u_wdg_apb_if.pready;
assign u_wdg_apb_pure_if.prdata = u_wdg_apb_if.prdata;
assign u_wdg_apb_pure_if.pslverr = u_wdg_apb_if.pslverr;
// crc
assign u_crc_apb_if.paddr = u_crc_apb_pure_if.paddr;
assign u_crc_apb_if.pprot = u_crc_apb_pure_if.pprot;
assign u_crc_apb_if.psel = u_crc_apb_pure_if.psel;
assign u_crc_apb_if.penable = u_crc_apb_pure_if.penable;
assign u_crc_apb_if.pwrite = u_crc_apb_pure_if.pwrite;
assign u_crc_apb_if.pwdata = u_crc_apb_pure_if.pwdata;
assign u_crc_apb_if.pstrb = u_crc_apb_pure_if.pstrb;
assign u_crc_apb_pure_if.pready = u_crc_apb_if.pready;
assign u_crc_apb_pure_if.prdata = u_crc_apb_if.prdata;
assign u_crc_apb_pure_if.pslverr = u_crc_apb_if.pslverr;
// tmr
assign u_tmr_apb_if.paddr = u_tmr_apb_pure_if.paddr;
assign u_tmr_apb_if.pprot = u_tmr_apb_pure_if.pprot;
assign u_tmr_apb_if.psel = u_tmr_apb_pure_if.psel;
assign u_tmr_apb_if.penable = u_tmr_apb_pure_if.penable;
assign u_tmr_apb_if.pwrite = u_tmr_apb_pure_if.pwrite;
assign u_tmr_apb_if.pwdata = u_tmr_apb_pure_if.pwdata;
assign u_tmr_apb_if.pstrb = u_tmr_apb_pure_if.pstrb;
assign u_tmr_apb_pure_if.pready = u_tmr_apb_if.pready;
assign u_tmr_apb_pure_if.prdata = u_tmr_apb_if.prdata;
assign u_tmr_apb_pure_if.pslverr = u_tmr_apb_if.pslverr;
`ifdef IP_MDD `ifdef IP_MDD
assign u_user_ip_apb_if.paddr = u_user_ip_apb_pure_if.paddr; apb4_if_bridge u_user_ip_apb_bridge (
assign u_user_ip_apb_if.pprot = u_user_ip_apb_pure_if.pprot; .apb_pure(u_user_ip_apb_pure_if),
assign u_user_ip_apb_if.psel = u_user_ip_apb_pure_if.psel; .timed (u_user_ip_apb_if)
assign u_user_ip_apb_if.penable = u_user_ip_apb_pure_if.penable; );
assign u_user_ip_apb_if.pwrite = u_user_ip_apb_pure_if.pwrite;
assign u_user_ip_apb_if.pwdata = u_user_ip_apb_pure_if.pwdata;
assign u_user_ip_apb_if.pstrb = u_user_ip_apb_pure_if.pstrb;
assign u_user_ip_apb_pure_if.pready = u_user_ip_apb_if.pready;
assign u_user_ip_apb_pure_if.prdata = u_user_ip_apb_if.prdata;
assign u_user_ip_apb_pure_if.pslverr = u_user_ip_apb_if.pslverr;
`endif `endif
// verilog_format: off // verilog_format: off
+6 -12
View File
@@ -28,12 +28,7 @@ module retrosoc (
`ifdef HAVE_SRAM_IF `ifdef HAVE_SRAM_IF
ram_if.master ram, ram_if.master ram,
`endif `endif
output logic [31:0] gpio_oe_o, gpio_if.soc_pad gpio,
output logic [31:0] gpio_cs_o,
output logic [31:0] gpio_pu_o,
output logic [31:0] gpio_pd_o,
output logic [31:0] gpio_do_o,
input logic [31:0] gpio_di_i,
uart_if.dut uart0, uart_if.dut uart0,
xpi_if.dut xpi, xpi_if.dut xpi,
sdram_if.dut sdram sdram_if.dut sdram
@@ -73,6 +68,11 @@ module retrosoc (
logic [ 3:0] s_bus_fault_wstrb; logic [ 3:0] s_bus_fault_wstrb;
logic s_bus_fault_reserved; logic s_bus_fault_reserved;
gpio_pad_bridge u_gpio_pad_bridge (
.inner(u_gpio_if),
.outer(gpio)
);
`ifdef CORE_MDD `ifdef CORE_MDD
assign u_sysctrl_if.core_sel_i = core_sel_i; assign u_sysctrl_if.core_sel_i = core_sel_i;
`else `else
@@ -122,12 +122,6 @@ module retrosoc (
// | 30 | opipsram_dat7_io_pad | __NONE_FUNC__ | // | 30 | opipsram_dat7_io_pad | __NONE_FUNC__ |
// | 31 | opipsram_dqs_o_pad | __NONE_FUNC__ | // | 31 | opipsram_dqs_o_pad | __NONE_FUNC__ |
// ===================================================== // =====================================================
assign gpio_oe_o = u_gpio_if.oe_o;
assign gpio_cs_o = u_gpio_if.cs_o;
assign gpio_pu_o = u_gpio_if.pu_o;
assign gpio_pd_o = u_gpio_if.pd_o;
assign gpio_do_o = u_gpio_if.do_o;
assign u_gpio_if.di_i = gpio_di_i;
// GPIO0 FUNC0 // GPIO0 FUNC0
// pad0 // pad0
assign u_uart1_if.rx_i = u_gpio_if.di_i[0]; assign u_uart1_if.rx_i = u_gpio_if.di_i[0];
+10 -262
View File
@@ -5,135 +5,15 @@
// http://license.coscl.org.cn/MulanPSL2 // http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, // 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, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details. // See the Mulan PSL v2 for more details.
// NOTE: need to focus on the port dir
`include "gpio_define.svh" `include "gpio_define.svh"
`include "mdd_config.svh" `include "mdd_config.svh"
module retrosoc_asic ( module retrosoc_asic (
inout extclk_i_pad, `include "retrosoc_asic_ports.svh"
inout audclk_i_pad,
inout ext_rst_n_i_pad,
`ifdef HAVE_PLL
input xi_i_pad,
output xo_o_pad,
`endif
`ifdef CORE_MDD
inout core_sel_0_i_pad,
inout core_sel_1_i_pad,
inout core_sel_2_i_pad,
inout core_sel_3_i_pad,
inout core_sel_4_i_pad,
`endif
`ifdef IP_MDD
inout user_gpio_0_io_pad,
inout user_gpio_1_io_pad,
inout user_gpio_2_io_pad,
inout user_gpio_3_io_pad,
inout user_gpio_4_io_pad,
inout user_gpio_5_io_pad,
inout user_gpio_6_io_pad,
inout user_gpio_7_io_pad,
inout user_gpio_8_io_pad,
inout user_gpio_9_io_pad,
inout user_gpio_10_io_pad,
inout user_gpio_11_io_pad,
inout user_gpio_12_io_pad,
inout user_gpio_13_io_pad,
inout user_gpio_14_io_pad,
inout user_gpio_15_io_pad,
`endif
// gpio
inout gpio_0_io_pad,
inout gpio_1_io_pad,
inout gpio_2_io_pad,
inout gpio_3_io_pad,
inout gpio_4_io_pad,
inout gpio_5_io_pad,
inout gpio_6_io_pad,
inout gpio_7_io_pad,
inout gpio_8_io_pad,
inout gpio_9_io_pad,
inout gpio_10_io_pad,
inout gpio_11_io_pad,
inout gpio_12_io_pad,
inout gpio_13_io_pad,
inout gpio_14_io_pad,
inout gpio_15_io_pad,
inout gpio_16_io_pad,
inout gpio_17_io_pad,
inout gpio_18_io_pad,
inout gpio_19_io_pad,
inout gpio_20_io_pad,
inout gpio_21_io_pad,
inout gpio_22_io_pad,
inout gpio_23_io_pad,
inout gpio_24_io_pad,
inout gpio_25_io_pad,
inout gpio_26_io_pad,
inout gpio_27_io_pad,
inout gpio_28_io_pad,
inout gpio_29_io_pad,
inout gpio_30_io_pad,
inout gpio_31_io_pad,
// uart
output uart0_tx_o_pad,
inout uart0_rx_i_pad,
// xpi
output xpi_sck_o_pad,
output xpi_nss0_o_pad,
output xpi_nss1_o_pad,
output xpi_nss2_o_pad,
output xpi_nss3_o_pad,
inout xpi_dat0_io_pad,
inout xpi_dat1_io_pad,
inout xpi_dat2_io_pad,
inout xpi_dat3_io_pad,
// sdram
output sdram_clk_o_pad,
output sdram_cke_o_pad,
output sdram_cs_n_o_pad,
output sdram_ras_n_o_pad,
output sdram_cas_n_o_pad,
output sdram_we_n_o_pad,
output sdram_ba0_o_pad,
output sdram_ba1_o_pad,
output sdram_addr0_o_pad,
output sdram_addr1_o_pad,
output sdram_addr2_o_pad,
output sdram_addr3_o_pad,
output sdram_addr4_o_pad,
output sdram_addr5_o_pad,
output sdram_addr6_o_pad,
output sdram_addr7_o_pad,
output sdram_addr8_o_pad,
output sdram_addr9_o_pad,
output sdram_addr10_o_pad,
output sdram_addr11_o_pad,
output sdram_addr12_o_pad,
output sdram_dqm0_o_pad,
output sdram_dqm1_o_pad,
inout sdram_dq0_io_pad,
inout sdram_dq1_io_pad,
inout sdram_dq2_io_pad,
inout sdram_dq3_io_pad,
inout sdram_dq4_io_pad,
inout sdram_dq5_io_pad,
inout sdram_dq6_io_pad,
inout sdram_dq7_io_pad,
inout sdram_dq8_io_pad,
inout sdram_dq9_io_pad,
inout sdram_dq10_io_pad,
inout sdram_dq11_io_pad,
inout sdram_dq12_io_pad,
inout sdram_dq13_io_pad,
inout sdram_dq14_io_pad,
inout sdram_dq15_io_pad
); );
// clk&rst
logic s_ext_clk; logic s_ext_clk;
logic s_aud_clk; logic s_aud_clk;
logic s_sys_clkdiv4; logic s_sys_clkdiv4;
@@ -144,7 +24,6 @@ module retrosoc_asic (
logic s_ext_rst_n; logic s_ext_rst_n;
logic s_sys_rst_n; logic s_sys_rst_n;
logic s_aud_rst_n; logic s_aud_rst_n;
logic s_tmr_capch;
`ifdef CORE_MDD `ifdef CORE_MDD
logic [`USER_CORESEL_WIDTH-1:0] s_core_sel; logic [`USER_CORESEL_WIDTH-1:0] s_core_sel;
@@ -158,141 +37,15 @@ module retrosoc_asic (
ram_if u_ram_if (); ram_if u_ram_if ();
`endif `endif
// verilog_format: off gpio_if u_gpio_if ();
logic [31:0] s_gpio_oe; uart_if u_uart0_if ();
logic [31:0] s_gpio_cs; xpi_if u_xpi_if ();
logic [31:0] s_gpio_pu; sdram_if u_sdram_if ();
logic [31:0] s_gpio_pd; pll_ctrl_if u_pll_ctrl_if ();
logic [31:0] s_gpio_do;
logic [31:0] s_gpio_di;
uart_if u_uart0_if ();
xpi_if u_xpi_if ();
sdram_if u_sdram_if ();
pll_ctrl_if u_pll_ctrl_if ();
`include "retrosoc_asic_pad_bindings.svh"
tc_io_tri_pad u_extclk_i_pad (.pad(extclk_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_ext_clk)); rcu u_rcu (
tc_io_tri_pad u_audclk_i_pad (.pad(audclk_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_aud_clk));
tc_io_tri_schmitt_pad u_ext_rst_n_i_pad (.pad(ext_rst_n_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_ext_rst_n));
`ifdef HAVE_PLL
tc_io_xtl_pad u_xtal_io_pad (.xi_pad(xi_i_pad), .xo_pad(xo_o_pad), .en(1'b1), .clk(s_xtal_io));
`endif
`ifdef CORE_MDD
tc_io_tri_pad u_core_sel_0_i_pad (.pad(core_sel_0_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_core_sel[0]));
tc_io_tri_pad u_core_sel_1_i_pad (.pad(core_sel_1_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_core_sel[1]));
tc_io_tri_pad u_core_sel_2_i_pad (.pad(core_sel_2_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_core_sel[2]));
tc_io_tri_pad u_core_sel_3_i_pad (.pad(core_sel_3_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_core_sel[3]));
tc_io_tri_pad u_core_sel_4_i_pad (.pad(core_sel_4_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(s_core_sel[4]));
`endif
`ifdef IP_MDD
tc_io_tri_full_pad u_user_gpio_0_io_pad (.pad(user_gpio_0_io_pad), .c2p(u_user_gpio_if.do_o[0]), .c2p_en(u_user_gpio_if.oe_o[0]), .p2c(u_user_gpio_if.di_i[0]), .cs(u_user_gpio_if.cs_o[0]), .pu(u_user_gpio_if.pu_o[0]), .pd(u_user_gpio_if.pd_o[0]));
tc_io_tri_full_pad u_user_gpio_1_io_pad (.pad(user_gpio_1_io_pad), .c2p(u_user_gpio_if.do_o[1]), .c2p_en(u_user_gpio_if.oe_o[1]), .p2c(u_user_gpio_if.di_i[1]), .cs(u_user_gpio_if.cs_o[1]), .pu(u_user_gpio_if.pu_o[1]), .pd(u_user_gpio_if.pd_o[1]));
tc_io_tri_full_pad u_user_gpio_2_io_pad (.pad(user_gpio_2_io_pad), .c2p(u_user_gpio_if.do_o[2]), .c2p_en(u_user_gpio_if.oe_o[2]), .p2c(u_user_gpio_if.di_i[2]), .cs(u_user_gpio_if.cs_o[2]), .pu(u_user_gpio_if.pu_o[2]), .pd(u_user_gpio_if.pd_o[2]));
tc_io_tri_full_pad u_user_gpio_3_io_pad (.pad(user_gpio_3_io_pad), .c2p(u_user_gpio_if.do_o[3]), .c2p_en(u_user_gpio_if.oe_o[3]), .p2c(u_user_gpio_if.di_i[3]), .cs(u_user_gpio_if.cs_o[3]), .pu(u_user_gpio_if.pu_o[3]), .pd(u_user_gpio_if.pd_o[3]));
tc_io_tri_full_pad u_user_gpio_4_io_pad (.pad(user_gpio_4_io_pad), .c2p(u_user_gpio_if.do_o[4]), .c2p_en(u_user_gpio_if.oe_o[4]), .p2c(u_user_gpio_if.di_i[4]), .cs(u_user_gpio_if.cs_o[4]), .pu(u_user_gpio_if.pu_o[4]), .pd(u_user_gpio_if.pd_o[4]));
tc_io_tri_full_pad u_user_gpio_5_io_pad (.pad(user_gpio_5_io_pad), .c2p(u_user_gpio_if.do_o[5]), .c2p_en(u_user_gpio_if.oe_o[5]), .p2c(u_user_gpio_if.di_i[5]), .cs(u_user_gpio_if.cs_o[5]), .pu(u_user_gpio_if.pu_o[5]), .pd(u_user_gpio_if.pd_o[5]));
tc_io_tri_full_pad u_user_gpio_6_io_pad (.pad(user_gpio_6_io_pad), .c2p(u_user_gpio_if.do_o[6]), .c2p_en(u_user_gpio_if.oe_o[6]), .p2c(u_user_gpio_if.di_i[6]), .cs(u_user_gpio_if.cs_o[6]), .pu(u_user_gpio_if.pu_o[6]), .pd(u_user_gpio_if.pd_o[6]));
tc_io_tri_full_pad u_user_gpio_7_io_pad (.pad(user_gpio_7_io_pad), .c2p(u_user_gpio_if.do_o[7]), .c2p_en(u_user_gpio_if.oe_o[7]), .p2c(u_user_gpio_if.di_i[7]), .cs(u_user_gpio_if.cs_o[7]), .pu(u_user_gpio_if.pu_o[7]), .pd(u_user_gpio_if.pd_o[7]));
tc_io_tri_full_pad u_user_gpio_8_io_pad (.pad(user_gpio_8_io_pad), .c2p(u_user_gpio_if.do_o[8]), .c2p_en(u_user_gpio_if.oe_o[8]), .p2c(u_user_gpio_if.di_i[8]), .cs(u_user_gpio_if.cs_o[8]), .pu(u_user_gpio_if.pu_o[8]), .pd(u_user_gpio_if.pd_o[8]));
tc_io_tri_full_pad u_user_gpio_9_io_pad (.pad(user_gpio_9_io_pad), .c2p(u_user_gpio_if.do_o[9]), .c2p_en(u_user_gpio_if.oe_o[9]), .p2c(u_user_gpio_if.di_i[9]), .cs(u_user_gpio_if.cs_o[9]), .pu(u_user_gpio_if.pu_o[9]), .pd(u_user_gpio_if.pd_o[9]));
tc_io_tri_full_pad u_user_gpio_10_io_pad (.pad(user_gpio_10_io_pad), .c2p(u_user_gpio_if.do_o[10]), .c2p_en(u_user_gpio_if.oe_o[10]), .p2c(u_user_gpio_if.di_i[10]), .cs(u_user_gpio_if.cs_o[10]), .pu(u_user_gpio_if.pu_o[10]), .pd(u_user_gpio_if.pd_o[10]));
tc_io_tri_full_pad u_user_gpio_11_io_pad (.pad(user_gpio_11_io_pad), .c2p(u_user_gpio_if.do_o[11]), .c2p_en(u_user_gpio_if.oe_o[11]), .p2c(u_user_gpio_if.di_i[11]), .cs(u_user_gpio_if.cs_o[11]), .pu(u_user_gpio_if.pu_o[11]), .pd(u_user_gpio_if.pd_o[11]));
tc_io_tri_full_pad u_user_gpio_12_io_pad (.pad(user_gpio_12_io_pad), .c2p(u_user_gpio_if.do_o[12]), .c2p_en(u_user_gpio_if.oe_o[12]), .p2c(u_user_gpio_if.di_i[12]), .cs(u_user_gpio_if.cs_o[12]), .pu(u_user_gpio_if.pu_o[12]), .pd(u_user_gpio_if.pd_o[12]));
tc_io_tri_full_pad u_user_gpio_13_io_pad (.pad(user_gpio_13_io_pad), .c2p(u_user_gpio_if.do_o[13]), .c2p_en(u_user_gpio_if.oe_o[13]), .p2c(u_user_gpio_if.di_i[13]), .cs(u_user_gpio_if.cs_o[13]), .pu(u_user_gpio_if.pu_o[13]), .pd(u_user_gpio_if.pd_o[13]));
tc_io_tri_full_pad u_user_gpio_14_io_pad (.pad(user_gpio_14_io_pad), .c2p(u_user_gpio_if.do_o[14]), .c2p_en(u_user_gpio_if.oe_o[14]), .p2c(u_user_gpio_if.di_i[14]), .cs(u_user_gpio_if.cs_o[14]), .pu(u_user_gpio_if.pu_o[14]), .pd(u_user_gpio_if.pd_o[14]));
tc_io_tri_full_pad u_user_gpio_15_io_pad (.pad(user_gpio_15_io_pad), .c2p(u_user_gpio_if.do_o[15]), .c2p_en(u_user_gpio_if.oe_o[15]), .p2c(u_user_gpio_if.di_i[15]), .cs(u_user_gpio_if.cs_o[15]), .pu(u_user_gpio_if.pu_o[15]), .pd(u_user_gpio_if.pd_o[15]));
`endif
// gpio
tc_io_tri_full_pad u_gpio_0_io_pad (.pad(gpio_0_io_pad), .c2p(s_gpio_do[0]), .c2p_en(s_gpio_oe[0]), .p2c(s_gpio_di[0]), .cs(s_gpio_cs[0]), .pu(s_gpio_pu[0]), .pd(s_gpio_pd[0]));
tc_io_tri_full_pad u_gpio_1_io_pad (.pad(gpio_1_io_pad), .c2p(s_gpio_do[1]), .c2p_en(s_gpio_oe[1]), .p2c(s_gpio_di[1]), .cs(s_gpio_cs[1]), .pu(s_gpio_pu[1]), .pd(s_gpio_pd[1]));
tc_io_tri_full_pad u_gpio_2_io_pad (.pad(gpio_2_io_pad), .c2p(s_gpio_do[2]), .c2p_en(s_gpio_oe[2]), .p2c(s_gpio_di[2]), .cs(s_gpio_cs[2]), .pu(s_gpio_pu[2]), .pd(s_gpio_pd[2]));
tc_io_tri_full_pad u_gpio_3_io_pad (.pad(gpio_3_io_pad), .c2p(s_gpio_do[3]), .c2p_en(s_gpio_oe[3]), .p2c(s_gpio_di[3]), .cs(s_gpio_cs[3]), .pu(s_gpio_pu[3]), .pd(s_gpio_pd[3]));
tc_io_tri_full_pad u_gpio_4_io_pad (.pad(gpio_4_io_pad), .c2p(s_gpio_do[4]), .c2p_en(s_gpio_oe[4]), .p2c(s_gpio_di[4]), .cs(s_gpio_cs[4]), .pu(s_gpio_pu[4]), .pd(s_gpio_pd[4]));
tc_io_tri_full_pad u_gpio_5_io_pad (.pad(gpio_5_io_pad), .c2p(s_gpio_do[5]), .c2p_en(s_gpio_oe[5]), .p2c(s_gpio_di[5]), .cs(s_gpio_cs[5]), .pu(s_gpio_pu[5]), .pd(s_gpio_pd[5]));
tc_io_tri_full_pad u_gpio_6_io_pad (.pad(gpio_6_io_pad), .c2p(s_gpio_do[6]), .c2p_en(s_gpio_oe[6]), .p2c(s_gpio_di[6]), .cs(s_gpio_cs[6]), .pu(s_gpio_pu[6]), .pd(s_gpio_pd[6]));
tc_io_tri_full_pad u_gpio_7_io_pad (.pad(gpio_7_io_pad), .c2p(s_gpio_do[7]), .c2p_en(s_gpio_oe[7]), .p2c(s_gpio_di[7]), .cs(s_gpio_cs[7]), .pu(s_gpio_pu[7]), .pd(s_gpio_pd[7]));
tc_io_tri_full_pad u_gpio_8_io_pad (.pad(gpio_8_io_pad), .c2p(s_gpio_do[8]), .c2p_en(s_gpio_oe[8]), .p2c(s_gpio_di[8]), .cs(s_gpio_cs[8]), .pu(s_gpio_pu[8]), .pd(s_gpio_pd[8]));
tc_io_tri_full_pad u_gpio_9_io_pad (.pad(gpio_9_io_pad), .c2p(s_gpio_do[9]), .c2p_en(s_gpio_oe[9]), .p2c(s_gpio_di[9]), .cs(s_gpio_cs[9]), .pu(s_gpio_pu[9]), .pd(s_gpio_pd[9]));
tc_io_tri_full_pad u_gpio_10_io_pad (.pad(gpio_10_io_pad), .c2p(s_gpio_do[10]), .c2p_en(s_gpio_oe[10]), .p2c(s_gpio_di[10]), .cs(s_gpio_cs[10]), .pu(s_gpio_pu[10]), .pd(s_gpio_pd[10]));
tc_io_tri_full_pad u_gpio_11_io_pad (.pad(gpio_11_io_pad), .c2p(s_gpio_do[11]), .c2p_en(s_gpio_oe[11]), .p2c(s_gpio_di[11]), .cs(s_gpio_cs[11]), .pu(s_gpio_pu[11]), .pd(s_gpio_pd[11]));
tc_io_tri_full_pad u_gpio_12_io_pad (.pad(gpio_12_io_pad), .c2p(s_gpio_do[12]), .c2p_en(s_gpio_oe[12]), .p2c(s_gpio_di[12]), .cs(s_gpio_cs[12]), .pu(s_gpio_pu[12]), .pd(s_gpio_pd[12]));
tc_io_tri_full_pad u_gpio_13_io_pad (.pad(gpio_13_io_pad), .c2p(s_gpio_do[13]), .c2p_en(s_gpio_oe[13]), .p2c(s_gpio_di[13]), .cs(s_gpio_cs[13]), .pu(s_gpio_pu[13]), .pd(s_gpio_pd[13]));
tc_io_tri_full_pad u_gpio_14_io_pad (.pad(gpio_14_io_pad), .c2p(s_gpio_do[14]), .c2p_en(s_gpio_oe[14]), .p2c(s_gpio_di[14]), .cs(s_gpio_cs[14]), .pu(s_gpio_pu[14]), .pd(s_gpio_pd[14]));
tc_io_tri_full_pad u_gpio_15_io_pad (.pad(gpio_15_io_pad), .c2p(s_gpio_do[15]), .c2p_en(s_gpio_oe[15]), .p2c(s_gpio_di[15]), .cs(s_gpio_cs[15]), .pu(s_gpio_pu[15]), .pd(s_gpio_pd[15]));
tc_io_tri_full_pad u_gpio_16_io_pad (.pad(gpio_16_io_pad), .c2p(s_gpio_do[16]), .c2p_en(s_gpio_oe[16]), .p2c(s_gpio_di[16]), .cs(s_gpio_cs[16]), .pu(s_gpio_pu[16]), .pd(s_gpio_pd[16]));
tc_io_tri_full_pad u_gpio_17_io_pad (.pad(gpio_17_io_pad), .c2p(s_gpio_do[17]), .c2p_en(s_gpio_oe[17]), .p2c(s_gpio_di[17]), .cs(s_gpio_cs[17]), .pu(s_gpio_pu[17]), .pd(s_gpio_pd[17]));
tc_io_tri_full_pad u_gpio_18_io_pad (.pad(gpio_18_io_pad), .c2p(s_gpio_do[18]), .c2p_en(s_gpio_oe[18]), .p2c(s_gpio_di[18]), .cs(s_gpio_cs[18]), .pu(s_gpio_pu[18]), .pd(s_gpio_pd[18]));
tc_io_tri_full_pad u_gpio_19_io_pad (.pad(gpio_19_io_pad), .c2p(s_gpio_do[19]), .c2p_en(s_gpio_oe[19]), .p2c(s_gpio_di[19]), .cs(s_gpio_cs[19]), .pu(s_gpio_pu[19]), .pd(s_gpio_pd[19]));
tc_io_tri_full_pad u_gpio_20_io_pad (.pad(gpio_20_io_pad), .c2p(s_gpio_do[20]), .c2p_en(s_gpio_oe[20]), .p2c(s_gpio_di[20]), .cs(s_gpio_cs[20]), .pu(s_gpio_pu[20]), .pd(s_gpio_pd[20]));
tc_io_tri_full_pad u_gpio_21_io_pad (.pad(gpio_21_io_pad), .c2p(s_gpio_do[21]), .c2p_en(s_gpio_oe[21]), .p2c(s_gpio_di[21]), .cs(s_gpio_cs[21]), .pu(s_gpio_pu[21]), .pd(s_gpio_pd[21]));
tc_io_tri_full_pad u_gpio_22_io_pad (.pad(gpio_22_io_pad), .c2p(s_gpio_do[22]), .c2p_en(s_gpio_oe[22]), .p2c(s_gpio_di[22]), .cs(s_gpio_cs[22]), .pu(s_gpio_pu[22]), .pd(s_gpio_pd[22]));
tc_io_tri_full_pad u_gpio_23_io_pad (.pad(gpio_23_io_pad), .c2p(s_gpio_do[23]), .c2p_en(s_gpio_oe[23]), .p2c(s_gpio_di[23]), .cs(s_gpio_cs[23]), .pu(s_gpio_pu[23]), .pd(s_gpio_pd[23]));
tc_io_tri_full_pad u_gpio_24_io_pad (.pad(gpio_24_io_pad), .c2p(s_gpio_do[24]), .c2p_en(s_gpio_oe[24]), .p2c(s_gpio_di[24]), .cs(s_gpio_cs[24]), .pu(s_gpio_pu[24]), .pd(s_gpio_pd[24]));
tc_io_tri_full_pad u_gpio_25_io_pad (.pad(gpio_25_io_pad), .c2p(s_gpio_do[25]), .c2p_en(s_gpio_oe[25]), .p2c(s_gpio_di[25]), .cs(s_gpio_cs[25]), .pu(s_gpio_pu[25]), .pd(s_gpio_pd[25]));
tc_io_tri_full_pad u_gpio_26_io_pad (.pad(gpio_26_io_pad), .c2p(s_gpio_do[26]), .c2p_en(s_gpio_oe[26]), .p2c(s_gpio_di[26]), .cs(s_gpio_cs[26]), .pu(s_gpio_pu[26]), .pd(s_gpio_pd[26]));
tc_io_tri_full_pad u_gpio_27_io_pad (.pad(gpio_27_io_pad), .c2p(s_gpio_do[27]), .c2p_en(s_gpio_oe[27]), .p2c(s_gpio_di[27]), .cs(s_gpio_cs[27]), .pu(s_gpio_pu[27]), .pd(s_gpio_pd[27]));
tc_io_tri_full_pad u_gpio_28_io_pad (.pad(gpio_28_io_pad), .c2p(s_gpio_do[28]), .c2p_en(s_gpio_oe[28]), .p2c(s_gpio_di[28]), .cs(s_gpio_cs[28]), .pu(s_gpio_pu[28]), .pd(s_gpio_pd[28]));
tc_io_tri_full_pad u_gpio_29_io_pad (.pad(gpio_29_io_pad), .c2p(s_gpio_do[29]), .c2p_en(s_gpio_oe[29]), .p2c(s_gpio_di[29]), .cs(s_gpio_cs[29]), .pu(s_gpio_pu[29]), .pd(s_gpio_pd[29]));
tc_io_tri_full_pad u_gpio_30_io_pad (.pad(gpio_30_io_pad), .c2p(s_gpio_do[30]), .c2p_en(s_gpio_oe[30]), .p2c(s_gpio_di[30]), .cs(s_gpio_cs[30]), .pu(s_gpio_pu[30]), .pd(s_gpio_pd[30]));
tc_io_tri_full_pad u_gpio_31_io_pad (.pad(gpio_31_io_pad), .c2p(s_gpio_do[31]), .c2p_en(s_gpio_oe[31]), .p2c(s_gpio_di[31]), .cs(s_gpio_cs[31]), .pu(s_gpio_pu[31]), .pd(s_gpio_pd[31]));
// uart0
tc_io_tri_pad u_uart0_tx_o_pad (.pad(uart0_tx_o_pad), .c2p(u_uart0_if.tx_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_uart0_rx_i_pad (.pad(uart0_rx_i_pad), .c2p(1'b0), .c2p_en(1'b0), .p2c(u_uart0_if.rx_i));
// xpi
tc_io_tri_pad u_xpi_sck_o_pad (.pad(xpi_sck_o_pad), .c2p(u_xpi_if.sck_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_xpi_nss0_o_pad (.pad(xpi_nss0_o_pad), .c2p(u_xpi_if.nss_o[0]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_xpi_nss1_o_pad (.pad(xpi_nss1_o_pad), .c2p(u_xpi_if.nss_o[1]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_xpi_nss2_o_pad (.pad(xpi_nss2_o_pad), .c2p(u_xpi_if.nss_o[2]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_xpi_nss3_o_pad (.pad(xpi_nss3_o_pad), .c2p(u_xpi_if.nss_o[3]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_xpi_dat0_io_pad (.pad(xpi_dat0_io_pad), .c2p(u_xpi_if.io_do_o[0]), .c2p_en(u_xpi_if.io_oe_o[0]), .p2c(u_xpi_if.io_di_i[0]));
tc_io_tri_pad u_xpi_dat1_io_pad (.pad(xpi_dat1_io_pad), .c2p(u_xpi_if.io_do_o[1]), .c2p_en(u_xpi_if.io_oe_o[1]), .p2c(u_xpi_if.io_di_i[1]));
tc_io_tri_pad u_xpi_dat2_io_pad (.pad(xpi_dat2_io_pad), .c2p(u_xpi_if.io_do_o[2]), .c2p_en(u_xpi_if.io_oe_o[2]), .p2c(u_xpi_if.io_di_i[2]));
tc_io_tri_pad u_xpi_dat3_io_pad (.pad(xpi_dat3_io_pad), .c2p(u_xpi_if.io_do_o[3]), .c2p_en(u_xpi_if.io_oe_o[3]), .p2c(u_xpi_if.io_di_i[3]));
// sdram
tc_io_tri_pad u_sdram_clk_o_pad (.pad(sdram_clk_o_pad), .c2p(u_sdram_if.clk_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_cke_o_pad (.pad(sdram_cke_o_pad), .c2p(u_sdram_if.cke_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_cs_n_o_pad (.pad(sdram_cs_n_o_pad), .c2p(u_sdram_if.cs_n_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_ras_n_o_pad (.pad(sdram_ras_n_o_pad), .c2p(u_sdram_if.ras_n_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_cas_n_o_pad (.pad(sdram_cas_n_o_pad), .c2p(u_sdram_if.cas_n_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_we_n_o_pad (.pad(sdram_we_n_o_pad), .c2p(u_sdram_if.we_n_o), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_ba0_o_pad (.pad(sdram_ba0_o_pad), .c2p(u_sdram_if.ba_o[0]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_ba1_o_pad (.pad(sdram_ba1_o_pad), .c2p(u_sdram_if.ba_o[1]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr0_o_pad (.pad(sdram_addr0_o_pad), .c2p(u_sdram_if.addr_o[0]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr1_o_pad (.pad(sdram_addr1_o_pad), .c2p(u_sdram_if.addr_o[1]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr2_o_pad (.pad(sdram_addr2_o_pad), .c2p(u_sdram_if.addr_o[2]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr3_o_pad (.pad(sdram_addr3_o_pad), .c2p(u_sdram_if.addr_o[3]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr4_o_pad (.pad(sdram_addr4_o_pad), .c2p(u_sdram_if.addr_o[4]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr5_o_pad (.pad(sdram_addr5_o_pad), .c2p(u_sdram_if.addr_o[5]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr6_o_pad (.pad(sdram_addr6_o_pad), .c2p(u_sdram_if.addr_o[6]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr7_o_pad (.pad(sdram_addr7_o_pad), .c2p(u_sdram_if.addr_o[7]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr8_o_pad (.pad(sdram_addr8_o_pad), .c2p(u_sdram_if.addr_o[8]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr9_o_pad (.pad(sdram_addr9_o_pad), .c2p(u_sdram_if.addr_o[9]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr10_o_pad (.pad(sdram_addr10_o_pad), .c2p(u_sdram_if.addr_o[10]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr11_o_pad (.pad(sdram_addr11_o_pad), .c2p(u_sdram_if.addr_o[11]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_addr12_o_pad (.pad(sdram_addr12_o_pad), .c2p(u_sdram_if.addr_o[12]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_dqm0_o_pad (.pad(sdram_dqm0_o_pad), .c2p(u_sdram_if.dqm_o[0]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_dqm1_o_pad (.pad(sdram_dqm1_o_pad), .c2p(u_sdram_if.dqm_o[1]), .c2p_en(1'b1), .p2c());
tc_io_tri_pad u_sdram_dq0_io_pad (.pad(sdram_dq0_io_pad), .c2p(u_sdram_if.dq_o[0]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[0]));
tc_io_tri_pad u_sdram_dq1_io_pad (.pad(sdram_dq1_io_pad), .c2p(u_sdram_if.dq_o[1]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[1]));
tc_io_tri_pad u_sdram_dq2_io_pad (.pad(sdram_dq2_io_pad), .c2p(u_sdram_if.dq_o[2]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[2]));
tc_io_tri_pad u_sdram_dq3_io_pad (.pad(sdram_dq3_io_pad), .c2p(u_sdram_if.dq_o[3]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[3]));
tc_io_tri_pad u_sdram_dq4_io_pad (.pad(sdram_dq4_io_pad), .c2p(u_sdram_if.dq_o[4]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[4]));
tc_io_tri_pad u_sdram_dq5_io_pad (.pad(sdram_dq5_io_pad), .c2p(u_sdram_if.dq_o[5]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[5]));
tc_io_tri_pad u_sdram_dq6_io_pad (.pad(sdram_dq6_io_pad), .c2p(u_sdram_if.dq_o[6]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[6]));
tc_io_tri_pad u_sdram_dq7_io_pad (.pad(sdram_dq7_io_pad), .c2p(u_sdram_if.dq_o[7]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[7]));
tc_io_tri_pad u_sdram_dq8_io_pad (.pad(sdram_dq8_io_pad), .c2p(u_sdram_if.dq_o[8]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[8]));
tc_io_tri_pad u_sdram_dq9_io_pad (.pad(sdram_dq9_io_pad), .c2p(u_sdram_if.dq_o[9]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[9]));
tc_io_tri_pad u_sdram_dq10_io_pad (.pad(sdram_dq10_io_pad), .c2p(u_sdram_if.dq_o[10]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[10]));
tc_io_tri_pad u_sdram_dq11_io_pad (.pad(sdram_dq11_io_pad), .c2p(u_sdram_if.dq_o[11]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[11]));
tc_io_tri_pad u_sdram_dq12_io_pad (.pad(sdram_dq12_io_pad), .c2p(u_sdram_if.dq_o[12]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[12]));
tc_io_tri_pad u_sdram_dq13_io_pad (.pad(sdram_dq13_io_pad), .c2p(u_sdram_if.dq_o[13]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[13]));
tc_io_tri_pad u_sdram_dq14_io_pad (.pad(sdram_dq14_io_pad), .c2p(u_sdram_if.dq_o[14]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[14]));
tc_io_tri_pad u_sdram_dq15_io_pad (.pad(sdram_dq15_io_pad), .c2p(u_sdram_if.dq_o[15]), .c2p_en(u_sdram_if.oe_o), .p2c(u_sdram_if.dq_i[15]));
// verilog_format: on
// clk buffer & mux
rcu u_rcu (
.ext_clk_i (s_ext_clk), .ext_clk_i (s_ext_clk),
.aud_clk_i (s_aud_clk), .aud_clk_i (s_aud_clk),
.ext_rst_n_i (s_ext_rst_n), .ext_rst_n_i (s_ext_rst_n),
@@ -329,12 +82,7 @@ module retrosoc_asic (
`ifdef HAVE_SRAM_IF `ifdef HAVE_SRAM_IF
.ram (u_ram_if), .ram (u_ram_if),
`endif `endif
.gpio_oe_o (s_gpio_oe), .gpio (u_gpio_if),
.gpio_cs_o (s_gpio_cs),
.gpio_pu_o (s_gpio_pu),
.gpio_pd_o (s_gpio_pd),
.gpio_do_o (s_gpio_do),
.gpio_di_i (s_gpio_di),
.uart0 (u_uart0_if), .uart0 (u_uart0_if),
.xpi (u_xpi_if), .xpi (u_xpi_if),
.sdram (u_sdram_if) .sdram (u_sdram_if)
+26 -4
View File
@@ -7,19 +7,40 @@ import hashlib
import json import json
import re import re
import sys import sys
from datetime import datetime
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT))
from scripts.dependency_lock import DEFAULT_LOCK, lock_digest, load_lock # noqa: E402 from scripts.dependency_lock import DEFAULT_LOCK, lock_digest, load_lock # noqa: E402
TIMESTAMP_FORMAT = "%Y-%m-%d-%H-%M"
TIMESTAMP_RE = re.compile(r"[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}")
def slug(value: str) -> str: def slug(value: str) -> str:
normalized = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-") normalized = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
return normalized or "manual" return normalized or "manual"
def variant_id(profile: str, values: list[str], lock: Path) -> str: def normalize_timestamp(value: str | None) -> str:
if value is None:
return datetime.now().astimezone().strftime(TIMESTAMP_FORMAT)
if TIMESTAMP_RE.fullmatch(value) is None:
raise ValueError(f"timestamp must use {TIMESTAMP_FORMAT}")
try:
datetime.strptime(value, TIMESTAMP_FORMAT)
except ValueError as error:
raise ValueError(f"timestamp must use {TIMESTAMP_FORMAT}") from error
return value
def variant_id(
profile: str,
values: list[str],
lock: Path,
timestamp: str | None = None,
) -> str:
parsed: dict[str, str] = {} parsed: dict[str, str] = {}
for item in values: for item in values:
name, separator, value = item.partition("=") name, separator, value = item.partition("=")
@@ -34,18 +55,19 @@ def variant_id(profile: str, values: list[str], lock: Path) -> str:
digest = hashlib.sha256( digest = hashlib.sha256(
json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
).hexdigest()[:12] ).hexdigest()[:12]
return f"{slug(profile)}-{digest}" return f"{slug(profile)}-{normalize_timestamp(timestamp)}-{digest}"
def main() -> int: def main() -> int:
parser = argparse.ArgumentParser(description="Generate a deterministic build variant ID") parser = argparse.ArgumentParser(description="Generate a timestamped build variant ID")
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK) parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
parser.add_argument("--profile", default="manual") parser.add_argument("--profile", default="manual")
parser.add_argument("--value", action="append", default=[]) parser.add_argument("--value", action="append", default=[])
parser.add_argument("--timestamp", help=f"creation time in {TIMESTAMP_FORMAT} format")
args = parser.parse_args() args = parser.parse_args()
load_lock(args.lock) load_lock(args.lock)
try: try:
print(variant_id(args.profile, args.value, args.lock)) print(variant_id(args.profile, args.value, args.lock, args.timestamp))
except ValueError as error: except ValueError as error:
parser.error(str(error)) parser.error(str(error))
return 0 return 0
+18 -4
View File
@@ -3,8 +3,10 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import os
import subprocess import subprocess
import sys import sys
from datetime import datetime
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
@@ -37,14 +39,25 @@ PR_PROFILES = (
NIGHTLY_PROFILES = PR_PROFILES + ("configs/nightly/picorv32-rv32im-ihp130.mk",) NIGHTLY_PROFILES = PR_PROFILES + ("configs/nightly/picorv32-rv32im-ihp130.mk",)
def run_command(command: list[str], root: Path, capture_output: bool) -> str: def regression_environment() -> dict[str, str]:
environment = os.environ.copy()
environment.setdefault(
"BUILD_TIMESTAMP", datetime.now().astimezone().strftime("%Y-%m-%d-%H-%M")
)
return environment
def run_command(
command: list[str], root: Path, capture_output: bool, environment: dict[str, str]
) -> str:
if not capture_output: if not capture_output:
subprocess.run(command, cwd=root, check=True) subprocess.run(command, cwd=root, env=environment, check=True)
return "" return ""
result = subprocess.run( result = subprocess.run(
command, command,
cwd=root, cwd=root,
env=environment,
check=False, check=False,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
@@ -63,11 +76,12 @@ def main() -> int:
args = parser.parse_args() args = parser.parse_args()
commands = PR_COMMANDS if args.suite == "pr" else NIGHTLY_COMMANDS commands = PR_COMMANDS if args.suite == "pr" else NIGHTLY_COMMANDS
profiles = PR_PROFILES if args.suite == "pr" else NIGHTLY_PROFILES profiles = PR_PROFILES if args.suite == "pr" else NIGHTLY_PROFILES
environment = regression_environment()
for profile, values in commands: for profile, values in commands:
command = ["make", f"CONFIG={profile}", *values] command = ["make", f"CONFIG={profile}", *values]
print("+ " + " ".join(command), flush=True) print("+ " + " ".join(command), flush=True)
if not args.dry_run: if not args.dry_run:
output = run_command(command, args.root, "firmware" in values) output = run_command(command, args.root, "firmware" in values, environment)
warnings = self_owned_warnings(args.root, output) warnings = self_owned_warnings(args.root, output)
if warnings: if warnings:
print("self-owned C compiler warnings:", flush=True) print("self-owned C compiler warnings:", flush=True)
@@ -82,7 +96,7 @@ def main() -> int:
] ]
print("+ " + " ".join(command), flush=True) print("+ " + " ".join(command), flush=True)
if not args.dry_run: if not args.dry_run:
subprocess.run(command, cwd=args.root, check=True) subprocess.run(command, cwd=args.root, env=environment, check=True)
return 0 return 0
+44
View File
@@ -0,0 +1,44 @@
`timescale 1ns / 1ps
module apb4_if_bridge_tb;
logic pclk = 1'b0;
logic presetn = 1'b0;
apb4_pure_if apb_pure ();
apb4_if apb_timed (
.pclk (pclk),
.presetn(presetn)
);
apb4_if_bridge u_apb4_if_bridge (
.apb_pure(apb_pure),
.timed (apb_timed)
);
initial begin
apb_pure.paddr = 32'h1234_5678;
apb_pure.pprot = 3'b101;
apb_pure.psel = 1'b1;
apb_pure.penable = 1'b1;
apb_pure.pwrite = 1'b1;
apb_pure.pwdata = 32'hCAFE_BABE;
apb_pure.pstrb = 4'b1101;
apb_timed.pready = 1'b1;
apb_timed.prdata = 32'hDEAD_BEEF;
apb_timed.pslverr = 1'b0;
#1;
if ({apb_timed.paddr, apb_timed.pprot, apb_timed.psel, apb_timed.penable,
apb_timed.pwrite, apb_timed.pwdata, apb_timed.pstrb} !==
{apb_pure.paddr, apb_pure.pprot, apb_pure.psel, apb_pure.penable,
apb_pure.pwrite, apb_pure.pwdata, apb_pure.pstrb}) begin
$fatal(1, "APB request fields did not cross the bridge");
end
if ({apb_pure.pready, apb_pure.prdata, apb_pure.pslverr} !==
{apb_timed.pready, apb_timed.prdata, apb_timed.pslverr}) begin
$fatal(1, "APB response fields did not cross the bridge");
end
$display("apb4 interface bridge test passed");
$finish;
end
endmodule
+149
View File
@@ -0,0 +1,149 @@
"""Tests for the canonical Mini SoC pin-map generator."""
from __future__ import annotations
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
GENERATOR = ROOT / "rtl/mini/pin_map/generate_pin_map.py"
PIN_MAP = ROOT / "rtl/mini/pin_map/pin_map.json"
FPGA_TOP = ROOT / "fpga/mini/retrosoc_top.sv"
FPGA_XDC = ROOT / "fpga/mini/starrysky_v2.xdc"
def generate(output_dir: Path) -> None:
subprocess.run(
[
sys.executable,
str(GENERATOR),
"--map",
str(PIN_MAP),
"--output-dir",
str(output_dir),
],
check=True,
)
def validate(map_path: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(GENERATOR), "--map", str(map_path), "--check"],
text=True,
capture_output=True,
)
def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
generate(tmp_path)
ports = (tmp_path / "rtl/retrosoc_asic_ports.svh").read_text(encoding="utf-8")
pads = (tmp_path / "rtl/retrosoc_asic_pad_bindings.svh").read_text(encoding="utf-8")
fpga = (tmp_path / "rtl/retrosoc_asic_fpga_mini_bindings.svh").read_text(
encoding="utf-8"
)
testbench = (tmp_path / "rtl/retrosoc_asic_tb_bindings.svh").read_text(
encoding="utf-8"
)
assert "inout extclk_i_pad," in ports
assert "input xi_i_pad," in ports
assert "inout sdram_dq15_io_pad" in ports
assert "u_gpio_31_io_pad" in pads
assert "u_sdram_dq15_io_pad" in pads
assert ".gpio_30_io_pad(gpio_io30)" in fpga
assert ".gpio_15_io_pad()" in fpga
assert ".gpio_24_io_pad(s_psram_dat1)" in testbench
assert testbench.count(".gpio_24_io_pad(") == 1
def test_pin_map_rejects_duplicate_pads(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["pads"][0]["ports"].append(
{"name": "extclk_i_pad", "signal": "s_duplicate_clock"}
)
invalid_map = tmp_path / "duplicate.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "pad names must be unique" in result.stderr
def test_pin_map_rejects_non_object_port_entry(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["pads"][0]["ports"].append("extclk_i_pad")
invalid_map = tmp_path / "invalid-port.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "ports entries must be objects" in result.stderr
def test_pin_map_rejects_unknown_profile_pad(tmp_path: Path) -> None:
document = json.loads(PIN_MAP.read_text(encoding="utf-8"))
document["profiles"]["tb"]["bindings"]["unknown_pad"] = "s_unknown"
invalid_map = tmp_path / "unknown.json"
invalid_map.write_text(json.dumps(document), encoding="utf-8")
result = validate(invalid_map)
assert result.returncode != 0
assert "unknown pad" in result.stderr
def test_fpga_constraints_reference_declared_top_ports() -> None:
top_ports = set(re.findall(r"^\s*(?:input|output|inout)\s+([A-Za-z_][A-Za-z0-9_]*)", FPGA_TOP.read_text(encoding="utf-8"), re.MULTILINE))
xdc_ports = set(re.findall(r"\[get_ports\s+([A-Za-z_][A-Za-z0-9_]*)\]", FPGA_XDC.read_text(encoding="utf-8")))
assert xdc_ports
assert xdc_ports <= top_ports
def test_apb4_interface_bridge(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
sv2v = shutil.which("sv2v")
vvp = shutil.which("vvp")
if iverilog is None or sv2v is None or vvp is None:
return
source_list = tmp_path / "apb4_if_bridge.fl"
source_list.write_text(
"\n".join(
[
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv"),
str(ROOT / "rtl/mini/top/apb4_if_bridge.sv"),
str(ROOT / "tests/rtl/apb4_if_bridge_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "apb4_if_bridge_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "apb4_if_bridge_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "apb4_if_bridge_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "apb4 interface bridge test passed" in result.stdout
+24 -2
View File
@@ -25,6 +25,7 @@ from scripts.analyze_warnings import normalize # noqa: E402
from scripts.check_c_warnings import self_owned_warnings # noqa: E402 from scripts.check_c_warnings import self_owned_warnings # noqa: E402
from scripts.dependency_lock import LockError, load_lock # noqa: E402 from scripts.dependency_lock import LockError, load_lock # noqa: E402
from scripts.install_toolchain import safe_extract # noqa: E402 from scripts.install_toolchain import safe_extract # noqa: E402
from scripts.regress import regression_environment # noqa: E402
from scripts.setup_helpers import download_file, ensure_git_repo # noqa: E402 from scripts.setup_helpers import download_file, ensure_git_repo # noqa: E402
@@ -190,7 +191,7 @@ def test_make_dry_run_and_validation_do_not_write_filelists() -> None:
assert "Invalid SIMU='UNKNOWN'" in invalid.stderr assert "Invalid SIMU='UNKNOWN'" in invalid.stderr
def test_dependency_lock_and_config_key_are_deterministic(tmp_path: Path) -> None: def test_dependency_lock_and_config_key_include_a_fixed_timestamp(tmp_path: Path) -> None:
lock = load_lock(ROOT / "config/dependencies.lock.json") lock = load_lock(ROOT / "config/dependencies.lock.json")
assert lock["schema_version"] == 1 assert lock["schema_version"] == 1
assert len(lock["sources"]["mpw"]["revision"]) == 40 assert len(lock["sources"]["mpw"]["revision"]) == 40
@@ -202,6 +203,8 @@ def test_dependency_lock_and_config_key_are_deterministic(tmp_path: Path) -> Non
str(ROOT / "config/dependencies.lock.json"), str(ROOT / "config/dependencies.lock.json"),
"--profile", "--profile",
"unit", "unit",
"--timestamp",
"2026-07-21-10-39",
"--value", "--value",
"CORE=HAZARD3", "CORE=HAZARD3",
"--value", "--value",
@@ -210,7 +213,15 @@ def test_dependency_lock_and_config_key_are_deterministic(tmp_path: Path) -> Non
first = run(*command).stdout.strip() first = run(*command).stdout.strip()
second = run(*command).stdout.strip() second = run(*command).stdout.strip()
assert first == second assert first == second
assert first.startswith("unit-") assert re.fullmatch(r"unit-2026-07-21-10-39-[0-9a-f]{12}", first)
invalid_timestamp = subprocess.run(
[*command, "--timestamp", "2026-07-21-10-99"],
text=True,
capture_output=True,
)
assert invalid_timestamp.returncode != 0
assert "timestamp must use %Y-%m-%d-%H-%M" in invalid_timestamp.stderr
broken = tmp_path / "broken.json" broken = tmp_path / "broken.json"
broken.write_text('{"schema_version": 1}\n', encoding="utf-8") broken.write_text('{"schema_version": 1}\n', encoding="utf-8")
@@ -222,6 +233,17 @@ def test_dependency_lock_and_config_key_are_deterministic(tmp_path: Path) -> Non
raise AssertionError("invalid dependency lock was accepted") raise AssertionError("invalid dependency lock was accepted")
def test_regression_runner_uses_one_build_timestamp(monkeypatch) -> None:
monkeypatch.setenv("BUILD_TIMESTAMP", "2026-07-21-10-39")
assert regression_environment()["BUILD_TIMESTAMP"] == "2026-07-21-10-39"
monkeypatch.delenv("BUILD_TIMESTAMP")
assert re.fullmatch(
r"[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}",
regression_environment()["BUILD_TIMESTAMP"],
)
def test_run_flow_writes_structured_result(tmp_path: Path) -> None: def test_run_flow_writes_structured_result(tmp_path: Path) -> None:
log = tmp_path / "flow.log" log = tmp_path / "flow.log"
result = tmp_path / "result.json" result = tmp_path / "result.json"