diff --git a/AGENTS.md b/AGENTS.md index 85c51401..5bbcf830 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,7 +98,7 @@ README that states its ownership, source of truth, and validation expectations. ## Build and Repository Safety - Start from a committed configuration profile. Build products belong below - `build/-/`; caches belong below `.cache/`. Do not + `build/--/`; caches belong below `.cache/`. Do not commit generated outputs. - Preserve unrelated worktree changes. Do not reset, discard, or reformat files outside the requested scope. diff --git a/Makefile b/Makefile index 32800f5b..8d3d6027 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,7 @@ LINK_TYPE ?= ld2_sram BUILD_ROOT ?= $(ROOT_PATH)/build CACHE_ROOT ?= $(ROOT_PATH)/.cache/retrosoc +BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%d-%H-%M') MAX_JOBS ?= 16 HOST_CC ?= cc 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 \ ISA HAVE_CSR APP LINK_TYPE RTL_TOP FIRMWARE_NAME VARIANT_ID := $(strip $(shell python3 $(ROOT_PATH)/scripts/config_key.py \ - --lock $(LOCK_FILE) --profile $(PROFILE_NAME) \ + --lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \ $(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))))) ifeq ($(VARIANT_ID),) $(error Failed to calculate build variant ID) endif 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) SW_BUILD_DIR := $(VARIANT_ROOT)/sw 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 \ 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 help: @@ -178,6 +181,8 @@ help: ' config | manifest print/write the effective configuration' \ ' memory-map generate the selected address-map artifacts' \ ' 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-metrics apply the committed metrics policy' \ ' sw-format apply clang-format to self-owned embedded C code' \ @@ -194,7 +199,7 @@ help: config: @printf '%-18s %s\n' \ ROOT_PATH '$(ROOT_PATH)' CONFIG '$(or $(CONFIG_PATH),)' \ - VARIANT_ID '$(VARIANT_ID)' VARIANT_ROOT '$(VARIANT_ROOT)' \ + BUILD_TIMESTAMP '$(BUILD_TIMESTAMP)' VARIANT_ID '$(VARIANT_ID)' VARIANT_ROOT '$(VARIANT_ROOT)' \ JOBS '$(JOBS)' \ SOC '$(SOC)' CORE '$(CORE)' IP '$(IP)' \ SIMU '$(SIMU)' SYNTH '$(SYNTH)' STA '$(STA)' PDK '$(PDK)' \ diff --git a/README.md b/README.md index 693987d9..5ec30584 100644 --- a/README.md +++ b/README.md @@ -116,12 +116,21 @@ make CONFIG=configs/ci/hazard3-rv32im-ihp130.mk SIMU=IVERILOG \ | Nightly regression suite | `make regress-nightly` | | Script and policy checks | `make sw-format-check sw-policy-check sw-host-test` | -Build outputs are isolated below `build/-/`. Each variant keeps its +Build outputs are isolated below `build/--/`. Each variant keeps its firmware, generated sources, simulator output, synthesis and timing reports, manifest, warning analysis, and metrics separate from other configurations. Use `make clean` to remove the selected backend, `make clean-all` to remove all build output, and `make purge-cache` to remove 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 [`config/dependencies.lock.json`](config/dependencies.lock.json) is the source of truth for diff --git a/docs/README.md b/docs/README.md index f39fa35f..e8d2f837 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,8 @@ the root README and subsystem guides. baseline, scope, partial automation, and deviation process. - [pll-clock-control.md](pll-clock-control.md) describes the SYSCTRL PLL 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 the source of truth. Changes that alter process requirements must also update diff --git a/docs/engineering.md b/docs/engineering.md index 02ead953..ec4a0b28 100644 --- a/docs/engineering.md +++ b/docs/engineering.md @@ -60,13 +60,17 @@ affected implementation change. ## Build Layout -The variant identifier is the profile name plus a hash over effective hardware/software build -settings and the dependency lock. Runtime-only settings such as wave capture and timeout do not -create a new variant. +The variant identifier is the profile name, the local creation timestamp, and a hash over effective +hardware/software build settings and the dependency lock. The timestamp has the +`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 build// generated/mpw// + generated/pin_map/ sw/ sim// syn/yosys/ diff --git a/docs/soc-integration-wiring.md b/docs/soc-integration-wiring.md new file mode 100644 index 00000000..8c794444 --- /dev/null +++ b/docs/soc-integration-wiring.md @@ -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//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. diff --git a/fpga/README.md b/fpga/README.md index 79f4876f..6aa36f78 100644 --- a/fpga/README.md +++ b/fpga/README.md @@ -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/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//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 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 diff --git a/fpga/mini/retrosoc_top.sv b/fpga/mini/retrosoc_top.sv index 88cc7dea..bba10e6e 100644 --- a/fpga/mini/retrosoc_top.sv +++ b/fpga/mini/retrosoc_top.sv @@ -3,7 +3,6 @@ module retrosoc_top ( input clk_i, inout rst_n_i, - // gpio inout ps2_clk_i, inout ps2_dat_i, output onewire_dat_o, @@ -75,7 +74,6 @@ module retrosoc_top ( inout sdram_dq15_io ); - wire s_sys_clk; wire s_aud_clk; clk_wiz_0 u_clk_wiz_0 ( @@ -85,119 +83,7 @@ module retrosoc_top ( ); retrosoc_asic u_retrosoc ( - .extclk_i_pad (s_sys_clk), - .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) + `include "retrosoc_asic_fpga_mini_bindings.svh" ); + endmodule diff --git a/rtl/ip/native/peripheral/gpio.sv b/rtl/ip/native/peripheral/gpio.sv index 637469c7..93f195a9 100644 --- a/rtl/ip/native/peripheral/gpio.sv +++ b/rtl/ip/native/peripheral/gpio.sv @@ -57,6 +57,10 @@ interface gpio_if (); input alt1_oe_i, 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 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]; end 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.do_o[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_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]; end assign s_irq_stat = |s_gpio_istat_q; assign gpio.irq_o = s_irq_stat; diff --git a/rtl/mini/Makefile b/rtl/mini/Makefile index 9405fa16..8052b82a 100644 --- a/rtl/mini/Makefile +++ b/rtl/mini/Makefile @@ -7,6 +7,11 @@ MEMORY_MAP_RTL_DIR := $(MEMORY_MAP_DIR)/rtl MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker MEMORY_MAP_FILELIST := $(MEMORY_MAP_DIR)/memory_map.fl +PIN_MAP_SOURCE := $(RTL_PATH)/pin_map/pin_map.json +PIN_MAP_SCRIPT := $(RTL_PATH)/pin_map/generate_pin_map.py +PIN_MAP_DIR := $(VARIANT_ROOT)/generated/pin_map +PIN_MAP_STAMP := $(PIN_MAP_DIR)/.stamp +PIN_MAP_FILELIST := $(PIN_MAP_DIR)/pin_map.fl MPW_STAMP := $(CACHE_ROOT)/stamps/mpw-$(LOCK_DIGEST).prepared MPW_OUTPUT_DIR := $(VARIANT_ROOT)/generated/mpw/$(SIM_TOOL_NAME) MPW_VARIANT_STAMP := $(MPW_OUTPUT_DIR)/.stamp @@ -43,8 +48,17 @@ memory-map: $(MEMORY_MAP_STAMP) check-memory-map: 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 \ - $(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 \ --output-dir $(GENERATED_FL_DIR) \ $(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)/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)/ip.fl RTL_FLIST += -f $(GENERATED_FL_DIR)/tech.fl @@ -99,4 +113,4 @@ else ifeq ($(SIMU), IVERILOG) include $(RTL_PATH)/mk/iverilog.mk 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 diff --git a/rtl/mini/dv/tb/retrosoc_tb.sv b/rtl/mini/dv/tb/retrosoc_tb.sv index cb2868fc..20189ce6 100644 --- a/rtl/mini/dv/tb/retrosoc_tb.sv +++ b/rtl/mini/dv/tb/retrosoc_tb.sv @@ -91,120 +91,7 @@ module retrosoc_tb; assign s_user_gpio_0_io = r_user_gpio_0_io; retrosoc_asic u_retrosoc_asic ( - .extclk_i_pad (s_ext_clk), - .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]) + `include "retrosoc_asic_tb_bindings.svh" ); diff --git a/rtl/mini/dv/verilator/rtl/retrosoc_top.sv b/rtl/mini/dv/verilator/rtl/retrosoc_top.sv index 824a714b..0247a840 100644 --- a/rtl/mini/dv/verilator/rtl/retrosoc_top.sv +++ b/rtl/mini/dv/verilator/rtl/retrosoc_top.sv @@ -45,121 +45,9 @@ module retrosoc_top ( assign s_clk = ext_clk_i; assign s_rst_n = rst_n_i; assign s_core_sel = core_sel_i; + retrosoc_asic u_retrosoc_asic ( - .extclk_i_pad (s_clk), - .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 () + `include "retrosoc_asic_verilator_bindings.svh" ); QSPIFlash u_QSPIFlash ( @@ -177,5 +65,4 @@ module retrosoc_top ( .sio ({s_psram_dat3, s_psram_dat2, s_psram_dat1, s_psram_dat0}) ); - endmodule diff --git a/rtl/mini/filelist/top.fl b/rtl/mini/filelist/top.fl index 5c3854c0..27ea20ee 100644 --- a/rtl/mini/filelist/top.fl +++ b/rtl/mini/filelist/top.fl @@ -1,3 +1,5 @@ +/top/apb4_if_bridge.sv +/top/gpio_pad_bridge.sv /top/core_wrapper.sv /top/retrosoc.sv /top/retrosoc_asic.sv diff --git a/rtl/mini/mk/iverilog.mk b/rtl/mini/mk/iverilog.mk index ac623176..ba7ce6c4 100644 --- a/rtl/mini/mk/iverilog.mk +++ b/rtl/mini/mk/iverilog.mk @@ -40,19 +40,19 @@ $(IVERILOG_BEHV_FLIST): $(FILELIST_STAMP) $(CONVERTED_SOC) @mkdir -p $(@D) python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ --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) @mkdir -p $(@D) python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ --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) @mkdir -p $(@D) python3 $(RTL_PATH)/script/gen_iverilog_filelist.py \ --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) convt_sv2v: $(CONVERTED_SOC) diff --git a/rtl/mini/pin_map/README.md b/rtl/mini/pin_map/README.md new file mode 100644 index 00000000..205e258b --- /dev/null +++ b/rtl/mini/pin_map/README.md @@ -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. diff --git a/rtl/mini/pin_map/generate_pin_map.py b/rtl/mini/pin_map/generate_pin_map.py new file mode 100644 index 00000000..b59befd6 --- /dev/null +++ b/rtl/mini/pin_map/generate_pin_map.py @@ -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()) diff --git a/rtl/mini/pin_map/pin_map.json b/rtl/mini/pin_map/pin_map.json new file mode 100644 index 00000000..0865bf99 --- /dev/null +++ b/rtl/mini/pin_map/pin_map.json @@ -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]" + } + } + } +} diff --git a/rtl/mini/script/gen_iverilog_filelist.py b/rtl/mini/script/gen_iverilog_filelist.py index 427516d6..8094c3e0 100644 --- a/rtl/mini/script/gen_iverilog_filelist.py +++ b/rtl/mini/script/gen_iverilog_filelist.py @@ -19,6 +19,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--mode", required=True, choices=("behv", "netl", "post")) parser.add_argument("--pdk", required=True) 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("--converted", 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 = parse_filelists(generated_dir / name for name in base_names) 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": base.files.append(_require(args.converted, "converted behavioral RTL")) diff --git a/rtl/mini/top/apb4_if_bridge.sv b/rtl/mini/top/apb4_if_bridge.sv new file mode 100644 index 00000000..e7b5e5cd --- /dev/null +++ b/rtl/mini/top/apb4_if_bridge.sv @@ -0,0 +1,28 @@ +// Copyright (c) 2023-2026 Yuchi Miao +// 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 diff --git a/rtl/mini/top/gpio_pad_bridge.sv b/rtl/mini/top/gpio_pad_bridge.sv new file mode 100644 index 00000000..a478c4ab --- /dev/null +++ b/rtl/mini/top/gpio_pad_bridge.sv @@ -0,0 +1,23 @@ +// Copyright (c) 2023-2026 Yuchi Miao +// 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 diff --git a/rtl/mini/top/ip_apb_wrapper.sv b/rtl/mini/top/ip_apb_wrapper.sv index c0a4e274..65c8ffa2 100644 --- a/rtl/mini/top/ip_apb_wrapper.sv +++ b/rtl/mini/top/ip_apb_wrapper.sv @@ -70,116 +70,47 @@ module ip_apb_wrapper ( `endif // verilog_format: on - // archinfo - assign u_archinfo_apb_if.paddr = u_archinfo_apb_pure_if.paddr; - assign u_archinfo_apb_if.pprot = u_archinfo_apb_pure_if.pprot; - assign u_archinfo_apb_if.psel = u_archinfo_apb_pure_if.psel; - assign u_archinfo_apb_if.penable = u_archinfo_apb_pure_if.penable; - assign u_archinfo_apb_if.pwrite = u_archinfo_apb_pure_if.pwrite; - assign u_archinfo_apb_if.pwdata = u_archinfo_apb_pure_if.pwdata; - assign u_archinfo_apb_if.pstrb = u_archinfo_apb_pure_if.pstrb; - assign u_archinfo_apb_pure_if.pready = u_archinfo_apb_if.pready; - assign u_archinfo_apb_pure_if.prdata = u_archinfo_apb_if.prdata; - assign u_archinfo_apb_pure_if.pslverr = u_archinfo_apb_if.pslverr; - // rng - assign u_rng_apb_if.paddr = u_rng_apb_pure_if.paddr; - assign u_rng_apb_if.pprot = u_rng_apb_pure_if.pprot; - assign u_rng_apb_if.psel = u_rng_apb_pure_if.psel; - assign u_rng_apb_if.penable = u_rng_apb_pure_if.penable; - assign u_rng_apb_if.pwrite = u_rng_apb_pure_if.pwrite; - assign u_rng_apb_if.pwdata = u_rng_apb_pure_if.pwdata; - assign u_rng_apb_if.pstrb = u_rng_apb_pure_if.pstrb; - assign u_rng_apb_pure_if.pready = u_rng_apb_if.pready; - assign u_rng_apb_pure_if.prdata = u_rng_apb_if.prdata; - assign u_rng_apb_pure_if.pslverr = u_rng_apb_if.pslverr; - // uart1 - assign u_uart1_apb_if.paddr = u_uart1_apb_pure_if.paddr; - assign u_uart1_apb_if.pprot = u_uart1_apb_pure_if.pprot; - assign u_uart1_apb_if.psel = u_uart1_apb_pure_if.psel; - assign u_uart1_apb_if.penable = u_uart1_apb_pure_if.penable; - assign u_uart1_apb_if.pwrite = u_uart1_apb_pure_if.pwrite; - assign u_uart1_apb_if.pwdata = u_uart1_apb_pure_if.pwdata; - assign u_uart1_apb_if.pstrb = u_uart1_apb_pure_if.pstrb; - assign u_uart1_apb_pure_if.pready = u_uart1_apb_if.pready; - assign u_uart1_apb_pure_if.prdata = u_uart1_apb_if.prdata; - assign u_uart1_apb_pure_if.pslverr = u_uart1_apb_if.pslverr; - // pwm - assign u_pwm_apb_if.paddr = u_pwm_apb_pure_if.paddr; - 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; + apb4_if_bridge u_archinfo_apb_bridge ( + .apb_pure(u_archinfo_apb_pure_if), + .timed (u_archinfo_apb_if) + ); + apb4_if_bridge u_rng_apb_bridge ( + .apb_pure(u_rng_apb_pure_if), + .timed (u_rng_apb_if) + ); + apb4_if_bridge u_uart1_apb_bridge ( + .apb_pure(u_uart1_apb_pure_if), + .timed (u_uart1_apb_if) + ); + apb4_if_bridge u_pwm_apb_bridge ( + .apb_pure(u_pwm_apb_pure_if), + .timed (u_pwm_apb_if) + ); + apb4_if_bridge u_ps2_apb_bridge ( + .apb_pure(u_ps2_apb_pure_if), + .timed (u_ps2_apb_if) + ); + apb4_if_bridge u_rtc_apb_bridge ( + .apb_pure(u_rtc_apb_pure_if), + .timed (u_rtc_apb_if) + ); + apb4_if_bridge u_wdg_apb_bridge ( + .apb_pure(u_wdg_apb_pure_if), + .timed (u_wdg_apb_if) + ); + apb4_if_bridge u_crc_apb_bridge ( + .apb_pure(u_crc_apb_pure_if), + .timed (u_crc_apb_if) + ); + apb4_if_bridge u_tmr_apb_bridge ( + .apb_pure(u_tmr_apb_pure_if), + .timed (u_tmr_apb_if) + ); `ifdef IP_MDD - assign u_user_ip_apb_if.paddr = u_user_ip_apb_pure_if.paddr; - assign u_user_ip_apb_if.pprot = u_user_ip_apb_pure_if.pprot; - assign u_user_ip_apb_if.psel = u_user_ip_apb_pure_if.psel; - 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; + apb4_if_bridge u_user_ip_apb_bridge ( + .apb_pure(u_user_ip_apb_pure_if), + .timed (u_user_ip_apb_if) + ); `endif // verilog_format: off diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index 4deb882c..443615fa 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -28,12 +28,7 @@ module retrosoc ( `ifdef HAVE_SRAM_IF ram_if.master ram, `endif - output logic [31:0] gpio_oe_o, - 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, + gpio_if.soc_pad gpio, uart_if.dut uart0, xpi_if.dut xpi, sdram_if.dut sdram @@ -73,6 +68,11 @@ module retrosoc ( logic [ 3:0] s_bus_fault_wstrb; logic s_bus_fault_reserved; + gpio_pad_bridge u_gpio_pad_bridge ( + .inner(u_gpio_if), + .outer(gpio) + ); + `ifdef CORE_MDD assign u_sysctrl_if.core_sel_i = core_sel_i; `else @@ -122,12 +122,6 @@ module retrosoc ( // | 30 | opipsram_dat7_io_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 // pad0 assign u_uart1_if.rx_i = u_gpio_if.di_i[0]; diff --git a/rtl/mini/top/retrosoc_asic.sv b/rtl/mini/top/retrosoc_asic.sv index 18ea996e..68c48077 100644 --- a/rtl/mini/top/retrosoc_asic.sv +++ b/rtl/mini/top/retrosoc_asic.sv @@ -5,135 +5,15 @@ // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -// NOTE: need to focus on the port dir - `include "gpio_define.svh" `include "mdd_config.svh" module retrosoc_asic ( - inout extclk_i_pad, - 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 + `include "retrosoc_asic_ports.svh" ); - // clk&rst logic s_ext_clk; logic s_aud_clk; logic s_sys_clkdiv4; @@ -144,7 +24,6 @@ module retrosoc_asic ( logic s_ext_rst_n; logic s_sys_rst_n; logic s_aud_rst_n; - logic s_tmr_capch; `ifdef CORE_MDD logic [`USER_CORESEL_WIDTH-1:0] s_core_sel; @@ -158,141 +37,15 @@ module retrosoc_asic ( ram_if u_ram_if (); `endif - // verilog_format: off - logic [31:0] s_gpio_oe; - logic [31:0] s_gpio_cs; - logic [31:0] s_gpio_pu; - logic [31:0] s_gpio_pd; - 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 (); + gpio_if u_gpio_if (); + 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)); - 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 ( +rcu u_rcu ( .ext_clk_i (s_ext_clk), .aud_clk_i (s_aud_clk), .ext_rst_n_i (s_ext_rst_n), @@ -329,12 +82,7 @@ module retrosoc_asic ( `ifdef HAVE_SRAM_IF .ram (u_ram_if), `endif - .gpio_oe_o (s_gpio_oe), - .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), + .gpio (u_gpio_if), .uart0 (u_uart0_if), .xpi (u_xpi_if), .sdram (u_sdram_if) diff --git a/scripts/config_key.py b/scripts/config_key.py index 20e9a01a..55243a29 100644 --- a/scripts/config_key.py +++ b/scripts/config_key.py @@ -7,19 +7,40 @@ import hashlib import json import re import sys +from datetime import datetime from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) 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: normalized = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-") 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] = {} for item in values: name, separator, value = item.partition("=") @@ -34,18 +55,19 @@ def variant_id(profile: str, values: list[str], lock: Path) -> str: digest = hashlib.sha256( json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") ).hexdigest()[:12] - return f"{slug(profile)}-{digest}" + return f"{slug(profile)}-{normalize_timestamp(timestamp)}-{digest}" 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("--profile", default="manual") parser.add_argument("--value", action="append", default=[]) + parser.add_argument("--timestamp", help=f"creation time in {TIMESTAMP_FORMAT} format") args = parser.parse_args() load_lock(args.lock) 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: parser.error(str(error)) return 0 diff --git a/scripts/regress.py b/scripts/regress.py index 8534eb8f..d4a36b86 100644 --- a/scripts/regress.py +++ b/scripts/regress.py @@ -3,8 +3,10 @@ from __future__ import annotations import argparse +import os import subprocess import sys +from datetime import datetime from pathlib import Path ROOT = Path(__file__).resolve().parents[1] @@ -37,14 +39,25 @@ PR_PROFILES = ( 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: - subprocess.run(command, cwd=root, check=True) + subprocess.run(command, cwd=root, env=environment, check=True) return "" result = subprocess.run( command, cwd=root, + env=environment, check=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, @@ -63,11 +76,12 @@ def main() -> int: args = parser.parse_args() commands = PR_COMMANDS if args.suite == "pr" else NIGHTLY_COMMANDS profiles = PR_PROFILES if args.suite == "pr" else NIGHTLY_PROFILES + environment = regression_environment() for profile, values in commands: command = ["make", f"CONFIG={profile}", *values] print("+ " + " ".join(command), flush=True) 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) if warnings: print("self-owned C compiler warnings:", flush=True) @@ -82,7 +96,7 @@ def main() -> int: ] print("+ " + " ".join(command), flush=True) 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 diff --git a/tests/rtl/apb4_if_bridge_tb.sv b/tests/rtl/apb4_if_bridge_tb.sv new file mode 100644 index 00000000..4678bbf9 --- /dev/null +++ b/tests/rtl/apb4_if_bridge_tb.sv @@ -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 diff --git a/tests/test_pin_map.py b/tests/test_pin_map.py new file mode 100644 index 00000000..79d8eef3 --- /dev/null +++ b/tests/test_pin_map.py @@ -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 diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index 92c7de19..f8ce5512 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -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.dependency_lock import LockError, load_lock # 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 @@ -190,7 +191,7 @@ def test_make_dry_run_and_validation_do_not_write_filelists() -> None: 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") assert lock["schema_version"] == 1 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"), "--profile", "unit", + "--timestamp", + "2026-07-21-10-39", "--value", "CORE=HAZARD3", "--value", @@ -210,7 +213,15 @@ def test_dependency_lock_and_config_key_are_deterministic(tmp_path: Path) -> Non first = run(*command).stdout.strip() second = run(*command).stdout.strip() 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.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") +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: log = tmp_path / "flow.log" result = tmp_path / "result.json"