mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: refactor GPIO Interface and Migrate Legacy Code
- Updated the GPIO interface in multiple modules to use the new user_gpio_if structure instead of the legacy gpio_if. - Modified the ip_apb_wrapper, ip_nmi_wrapper, user_ip_design_template, user_ip_top, and user_ip_wrapper to reflect the new GPIO interface. - Increased the USER_GPIO_NUM from 16 to 32 in mdd_config.svh to accommodate more GPIOs. - Removed legacy GPIO handling from retrosoc and retrosoc_asic modules. - Implemented a migration script in generate_mpw.py to automatically update legacy GPIO ports in source files. - Added tests for the new GPIO user interface and ensured proper functionality through gpio_user_mux_tb and gpio_user_mux.py. - Created new makefiles for IP MDD configurations to support the updated GPIO structure. - Updated regression tests to include new configurations and ensure compatibility with the changes.
This commit is contained in:
24 files changed
+664
-182
No files matched your search
@@ -47,6 +47,8 @@ application, linker layout, and optional features as one reproducible configurat
|
||||
| --- | --- | --- | --- |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130.mk`](configs/ci/hazard3-rv32im-ihp130.mk) | HAZARD3 / RV32IM | `bringup` | Pull-request open-source regression: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-shell.mk`](configs/ci/hazard3-rv32im-ihp130-shell.mk) | HAZARD3 / RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk`](configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk) | HAZARD3 / RV32IM + MDD IP | `bringup` | Pull-request firmware and Verilator integration coverage for the user-IP GPIO ownership path. |
|
||||
| [`configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk`](configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk) | HAZARD3 / RV32IM + MDD IP | `shell` | Pull-request firmware compilation coverage for the user-IP software command. |
|
||||
| [`configs/ci/mdd-rv32im-ihp130.mk`](configs/ci/mdd-rv32im-ihp130.mk) | MDD / RV32IM | `bringup` | Pull-request firmware and Verilator regression. |
|
||||
| [`configs/nightly/picorv32-rv32im-ihp130.mk`](configs/nightly/picorv32-rv32im-ihp130.mk) | PicoRV32 / RV32IM | `bringup` | Nightly firmware, Verilator, and Icarus regression. |
|
||||
| [`configs/cluster/hazard3-ics55.mk`](configs/cluster/hazard3-ics55.mk) | HAZARD3 / RV32IM | `bringup` | Cluster-only configuration; requires the site PDK and licensed-tool environment. |
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := MDD
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
HAVE_SVA := NO
|
||||
ISA := RV32IM
|
||||
HAVE_CSR := YES
|
||||
APP := shell
|
||||
LINK_TYPE := ld2_psram
|
||||
@@ -0,0 +1,12 @@
|
||||
SOC := MINI
|
||||
CORE := HAZARD3
|
||||
IP := MDD
|
||||
PDK := IHP130
|
||||
HAVE_PLL := NO
|
||||
HAVE_SRAM_IF := NO
|
||||
HAVE_SRAM_MACRO := NO
|
||||
HAVE_SVA := NO
|
||||
ISA := RV32IM
|
||||
HAVE_CSR := NO
|
||||
APP := bringup
|
||||
LINK_TYPE := ld2_psram
|
||||
@@ -49,6 +49,9 @@
|
||||
#define reg_gpio_istat RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x24))
|
||||
#define reg_gpio_iofcfg RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x28))
|
||||
#define reg_gpio_pinmux RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x2C))
|
||||
#define reg_gpio_user_sel RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x30))
|
||||
#define reg_gpio_user_lock RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x34))
|
||||
#define reg_gpio_user_status RS_SOC_REG32(RS_SOC_NMI_GPIO_BASE, UINT32_C(0x38))
|
||||
// uart0
|
||||
#define reg_uart0_clkdiv RS_SOC_REG32(RS_SOC_NMI_UART0_BASE, UINT32_C(0x00))
|
||||
#define reg_uart0_data RS_SOC_REG32(RS_SOC_NMI_UART0_BASE, UINT32_C(0x04))
|
||||
|
||||
@@ -25,3 +25,25 @@ 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.
|
||||
|
||||
## User IP GPIO ownership
|
||||
|
||||
The IP=MDD configuration does not add dedicated user GPIO pads. A user IP can
|
||||
instead drive any of the 32 native GPIO pads through `user_gpio_if`. Software
|
||||
selects an owner per pin with the native GPIO `USER_SEL` register at offset
|
||||
`0x30`; clear selects the existing software/alternate GPIO path and set selects
|
||||
the user IP. `USER_LOCK` at `0x34` is write-one-set and prevents a selected
|
||||
owner bit from changing until reset. `USER_STATUS` at `0x38` reports the user
|
||||
IP pins that are actively connected.
|
||||
|
||||
On every accepted ownership change, the pad output enable is forced low for one
|
||||
full system clock. The native GPIO block retains `CS`, `PU`, and `PD` control
|
||||
in all modes. User IPs provide only output data, output enable, and sampled pad
|
||||
input. Configure the target output data and enable before writing `USER_SEL`,
|
||||
then program `USER_LOCK` after the handoff when the assignment is permanent.
|
||||
|
||||
User-IP RTL must declare its GPIO port as `user_gpio_if.user_ip gpio`; the only
|
||||
signals available through that port are `do_o`, `oe_o`, and `di_i`. The MPW
|
||||
generator migrates the locked legacy examples in its isolated build output, so
|
||||
they cannot drive native GPIO electrical controls. New IP submissions must use
|
||||
the `user_gpio_if` declaration directly.
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "010491867f065cec"
|
||||
},
|
||||
{
|
||||
"count": 4,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator EQ expects 32 bits on the LHS, but LHS's SEL generates 8 bits.",
|
||||
"signature": "3bba21d5150bdc2e"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
|
||||
"signature": "411d9ab46fef9d7d"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/ip_nmi_wrapper.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "7350cffccb478d64"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "7a759d6e07651fd8"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
|
||||
"signature": "813090adc9f1e8a8"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h0' generates 8 bits.",
|
||||
"signature": "8ad9b6921bd232e7"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "UNSIGNED:$ROOT/rtl/mini/top/bus.sv:<line>: Comparison is constant due to unsigned arithmetic",
|
||||
"signature": "9f53ff454a8648a4"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case Item, but Case Item's CONST '8'h4' generates 8 bits.",
|
||||
"signature": "be2cb5333a881eca"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHEXPAND:$ROOT/rtl/ip/native/peripheral/sysctrl.sv:<line>: Operator CASE expects 32 bits on the Case expression, but Case expression's SEL generates 8 bits.",
|
||||
"signature": "c43a29f4c975e9c1"
|
||||
},
|
||||
{
|
||||
"count": 68,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "c54331448f63ac73"
|
||||
},
|
||||
{
|
||||
"count": 1,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
|
||||
"signature": "c963ef155f9a032c"
|
||||
},
|
||||
{
|
||||
"count": 3,
|
||||
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
|
||||
"signature": "ef9f314dab2ac31d"
|
||||
},
|
||||
{
|
||||
"count": 11,
|
||||
"example": "SPECIFYIGN:$ROOT/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_io/verilog/sg13g2_io.v:<line>: Ignoring unsupported: specify block construct",
|
||||
"signature": "faa191e2dbf4a242"
|
||||
}
|
||||
],
|
||||
"profile": "hazard3-rv32im-ihp130-ip-mdd",
|
||||
"schema_version": 1,
|
||||
"tool": "verilator"
|
||||
}
|
||||
@@ -31,18 +31,20 @@
|
||||
|
||||
`include "gpio_define.svh"
|
||||
|
||||
interface gpio_if ();
|
||||
logic [`NMI_GPIO_NUM-1:0] oe_o;
|
||||
logic [`NMI_GPIO_NUM-1:0] cs_o;
|
||||
logic [`NMI_GPIO_NUM-1:0] pu_o;
|
||||
logic [`NMI_GPIO_NUM-1:0] pd_o;
|
||||
logic [`NMI_GPIO_NUM-1:0] do_o;
|
||||
logic [`NMI_GPIO_NUM-1:0] di_i;
|
||||
logic [`NMI_GPIO_NUM-1:0] alt0_do_i;
|
||||
logic [`NMI_GPIO_NUM-1:0] alt0_oe_i;
|
||||
logic [`NMI_GPIO_NUM-1:0] alt1_do_i;
|
||||
logic [`NMI_GPIO_NUM-1:0] alt1_oe_i;
|
||||
logic irq_o;
|
||||
interface gpio_if #(
|
||||
parameter int DATA_WIDTH = `NMI_GPIO_NUM
|
||||
) ();
|
||||
logic [DATA_WIDTH-1:0] oe_o;
|
||||
logic [DATA_WIDTH-1:0] cs_o;
|
||||
logic [DATA_WIDTH-1:0] pu_o;
|
||||
logic [DATA_WIDTH-1:0] pd_o;
|
||||
logic [DATA_WIDTH-1:0] do_o;
|
||||
logic [DATA_WIDTH-1:0] di_i;
|
||||
logic [DATA_WIDTH-1:0] alt0_do_i;
|
||||
logic [DATA_WIDTH-1:0] alt0_oe_i;
|
||||
logic [DATA_WIDTH-1:0] alt1_do_i;
|
||||
logic [DATA_WIDTH-1:0] alt1_oe_i;
|
||||
logic irq_o;
|
||||
|
||||
modport dut(
|
||||
output oe_o,
|
||||
@@ -63,12 +65,24 @@ interface gpio_if ();
|
||||
modport soc_pad(output oe_o, output cs_o, output pu_o, output pd_o, output do_o, input di_i);
|
||||
endinterface
|
||||
|
||||
interface user_gpio_if #(
|
||||
parameter int DATA_WIDTH = `NMI_GPIO_NUM
|
||||
) ();
|
||||
logic [DATA_WIDTH-1:0] do_o;
|
||||
logic [DATA_WIDTH-1:0] oe_o;
|
||||
logic [DATA_WIDTH-1:0] di_i;
|
||||
|
||||
modport user_ip(output do_o, output oe_o, input di_i);
|
||||
modport padctrl(input do_o, input oe_o, output di_i);
|
||||
endinterface
|
||||
|
||||
module nmi_gpio (
|
||||
// verilog_format: off
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
nmi_if.slave nmi,
|
||||
gpio_if.dut gpio
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.padctrl user_gpio
|
||||
// verilog_format: on
|
||||
);
|
||||
|
||||
@@ -101,6 +115,14 @@ module nmi_gpio (
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_iofcfg_d, s_gpio_iofcfg_q;
|
||||
logic s_gpio_pinmux_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_pinmux_d, s_gpio_pinmux_q;
|
||||
`ifdef IP_MDD
|
||||
logic s_gpio_user_sel_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_sel_d, s_gpio_user_sel_q;
|
||||
logic s_gpio_user_lock_en;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_lock_d, s_gpio_user_lock_q;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_handoff_d, s_gpio_user_handoff_q;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_user_status;
|
||||
`endif
|
||||
// irq
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_di_re, s_gpio_di_fe, s_gpio_irq;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_irq_rise, s_irq_fall;
|
||||
@@ -109,6 +131,7 @@ module nmi_gpio (
|
||||
logic s_irq_trg, s_irq_stat;
|
||||
// alt
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_alt_out, s_gpio_alt_oe;
|
||||
logic [`NMI_GPIO_NUM-1:0] s_gpio_native_out, s_gpio_native_oe;
|
||||
|
||||
// shake
|
||||
assign s_nmi_wr_hdshk = nmi.valid && (~s_nmi_ready_q) && (|nmi.wstrb);
|
||||
@@ -124,9 +147,22 @@ 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 s_gpio_native_oe[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_oe[i] : s_gpio_oe_q[i];
|
||||
assign s_gpio_native_out[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_q[i];
|
||||
end
|
||||
`ifdef IP_MDD
|
||||
assign user_gpio.di_i = gpio.di_i;
|
||||
for (genvar i = 0; i < `NMI_GPIO_NUM; i++) begin : USER_GPIO_MUX_BLOCK
|
||||
assign gpio.oe_o[i] = s_gpio_user_handoff_q[i]
|
||||
? 1'b0
|
||||
: (s_gpio_user_sel_q[i] ? user_gpio.oe_o[i] : s_gpio_native_oe[i]);
|
||||
assign gpio.do_o[i] = s_gpio_user_sel_q[i] ? user_gpio.do_o[i] : s_gpio_native_out[i];
|
||||
end
|
||||
`else
|
||||
assign gpio.oe_o = s_gpio_native_oe;
|
||||
assign gpio.do_o = s_gpio_native_out;
|
||||
assign user_gpio.di_i = '0;
|
||||
`endif
|
||||
assign s_irq_stat = |s_gpio_istat_q;
|
||||
assign gpio.irq_o = s_irq_stat;
|
||||
|
||||
@@ -284,6 +320,38 @@ module nmi_gpio (
|
||||
s_gpio_pinmux_q
|
||||
);
|
||||
|
||||
`ifdef IP_MDD
|
||||
assign s_gpio_user_sel_en = s_nmi_wr_hdshk && nmi.addr[7:0] == `NMI_GPIO_USER_SEL;
|
||||
assign s_gpio_user_sel_d = (s_gpio_user_sel_q & s_gpio_user_lock_q) |
|
||||
(nmi.wdata[`NMI_GPIO_NUM-1:0] & ~s_gpio_user_lock_q);
|
||||
dffer #(`NMI_GPIO_NUM) u_gpio_user_sel_dffer (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_gpio_user_sel_en,
|
||||
s_gpio_user_sel_d,
|
||||
s_gpio_user_sel_q
|
||||
);
|
||||
|
||||
assign s_gpio_user_lock_en = s_nmi_wr_hdshk && nmi.addr[7:0] == `NMI_GPIO_USER_LOCK;
|
||||
assign s_gpio_user_lock_d = s_gpio_user_lock_q | nmi.wdata[`NMI_GPIO_NUM-1:0];
|
||||
dffer #(`NMI_GPIO_NUM) u_gpio_user_lock_dffer (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_gpio_user_lock_en,
|
||||
s_gpio_user_lock_d,
|
||||
s_gpio_user_lock_q
|
||||
);
|
||||
|
||||
assign s_gpio_user_handoff_d = s_gpio_user_sel_en ? (s_gpio_user_sel_d ^ s_gpio_user_sel_q) : '0;
|
||||
dffr #(`NMI_GPIO_NUM) u_gpio_user_handoff_dffr (
|
||||
clk_i,
|
||||
rst_n_i,
|
||||
s_gpio_user_handoff_d,
|
||||
s_gpio_user_handoff_q
|
||||
);
|
||||
assign s_gpio_user_status = s_gpio_user_sel_q & ~s_gpio_user_handoff_q;
|
||||
`endif
|
||||
|
||||
|
||||
// nmi resp
|
||||
assign s_nmi_ready_d = nmi.valid && (~s_nmi_ready_q);
|
||||
@@ -298,19 +366,24 @@ module nmi_gpio (
|
||||
always_comb begin
|
||||
s_nmi_rdata_d = s_nmi_rdata_q;
|
||||
unique case (nmi.addr[7:0])
|
||||
`NMI_GPIO_OE: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_oe_q};
|
||||
`NMI_GPIO_CS: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_cs_q};
|
||||
`NMI_GPIO_PU: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pu_q};
|
||||
`NMI_GPIO_PD: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pd_q};
|
||||
`NMI_GPIO_DO: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_do_q};
|
||||
`NMI_GPIO_DI: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_di};
|
||||
`NMI_GPIO_IEN: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_ien_q};
|
||||
`NMI_GPIO_ITYPE0: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_itype0_q};
|
||||
`NMI_GPIO_ITYPE1: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_itype1_q};
|
||||
`NMI_GPIO_ISTAT: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_istat_q};
|
||||
`NMI_GPIO_IOFCFG: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_iofcfg_q};
|
||||
`NMI_GPIO_PINMUX: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pinmux_q};
|
||||
default: s_nmi_rdata_d = s_nmi_rdata_q;
|
||||
`NMI_GPIO_OE: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_oe_q};
|
||||
`NMI_GPIO_CS: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_cs_q};
|
||||
`NMI_GPIO_PU: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pu_q};
|
||||
`NMI_GPIO_PD: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pd_q};
|
||||
`NMI_GPIO_DO: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_do_q};
|
||||
`NMI_GPIO_DI: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_di};
|
||||
`NMI_GPIO_IEN: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_ien_q};
|
||||
`NMI_GPIO_ITYPE0: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_itype0_q};
|
||||
`NMI_GPIO_ITYPE1: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_itype1_q};
|
||||
`NMI_GPIO_ISTAT: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_istat_q};
|
||||
`NMI_GPIO_IOFCFG: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_iofcfg_q};
|
||||
`NMI_GPIO_PINMUX: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_pinmux_q};
|
||||
`ifdef IP_MDD
|
||||
`NMI_GPIO_USER_SEL: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_sel_q};
|
||||
`NMI_GPIO_USER_LOCK: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_lock_q};
|
||||
`NMI_GPIO_USER_STATUS: s_nmi_rdata_d = {{(32 - `NMI_GPIO_NUM) {1'b0}}, s_gpio_user_status};
|
||||
`endif
|
||||
default: s_nmi_rdata_d = s_nmi_rdata_q;
|
||||
endcase
|
||||
end
|
||||
dffer #(32) u_nmi_rdata_dffer (
|
||||
|
||||
@@ -25,5 +25,8 @@
|
||||
`define NMI_GPIO_ISTAT 8'h24 // ro
|
||||
`define NMI_GPIO_IOFCFG 8'h28 // rw
|
||||
`define NMI_GPIO_PINMUX 8'h2c // rw
|
||||
`define NMI_GPIO_USER_SEL 8'h30 // rw
|
||||
`define NMI_GPIO_USER_LOCK 8'h34 // rw1s
|
||||
`define NMI_GPIO_USER_STATUS 8'h38 // ro
|
||||
// verilog_format: on
|
||||
`endif
|
||||
@@ -30,8 +30,6 @@ module retrosoc_tb;
|
||||
`endif
|
||||
reg [ 4:0] r_core_sel;
|
||||
wire [ 4:0] s_core_sel;
|
||||
wire s_user_gpio_0_io;
|
||||
reg r_user_gpio_0_io;
|
||||
wire s_uart0_tx;
|
||||
// for handle x-prop issue
|
||||
wire s_uart0_rx = 1'b1;
|
||||
@@ -84,11 +82,10 @@ module retrosoc_tb;
|
||||
always #(1000 / AUD_CPU_FREQ / 2) r_aud_clk = (r_aud_clk === 1'b0);
|
||||
|
||||
// connect inout pad
|
||||
assign s_ext_clk = r_ext_clk;
|
||||
assign s_aud_clk = r_aud_clk;
|
||||
assign s_rst_n = r_rst_n;
|
||||
assign s_core_sel = r_core_sel;
|
||||
assign s_user_gpio_0_io = r_user_gpio_0_io;
|
||||
assign s_ext_clk = r_ext_clk;
|
||||
assign s_aud_clk = r_aud_clk;
|
||||
assign s_rst_n = r_rst_n;
|
||||
assign s_core_sel = r_core_sel;
|
||||
|
||||
retrosoc_asic u_retrosoc_asic (
|
||||
`include "retrosoc_asic_tb_bindings.svh"
|
||||
@@ -241,16 +238,6 @@ module retrosoc_tb;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin : USER_GPIO_BLOCK
|
||||
integer i;
|
||||
#1000;
|
||||
r_user_gpio_0_io = 1'b0;
|
||||
while (1) begin
|
||||
#150000;
|
||||
r_user_gpio_0_io = ~r_user_gpio_0_io;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
if ($test$plusargs("wave_YES")) begin
|
||||
$display("== generate simulation wave ==");
|
||||
|
||||
@@ -33,15 +33,6 @@
|
||||
"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",
|
||||
@@ -247,7 +238,6 @@
|
||||
"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",
|
||||
|
||||
@@ -27,7 +27,7 @@ module ip_apb_wrapper (
|
||||
ps2_if.dut ps2,
|
||||
`ifdef IP_MDD
|
||||
input logic [`USER_IPSEL_WIDTH-1:0] ip_sel_i,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.user_ip user_gpio,
|
||||
`endif
|
||||
output logic [ 6:0] irq_o
|
||||
// verilog_format: on
|
||||
@@ -157,7 +157,7 @@ module ip_apb_wrapper (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.sel_i (ip_sel_i),
|
||||
.gpio (gpio),
|
||||
.gpio (user_gpio),
|
||||
.apb (u_user_ip_apb_if)
|
||||
);
|
||||
`endif
|
||||
|
||||
@@ -20,6 +20,7 @@ module ip_nmi_wrapper (
|
||||
// natv if
|
||||
nmi_if.slave nmi,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.padctrl user_gpio,
|
||||
uart_if.dut uart,
|
||||
psram_if.dut psram,
|
||||
spi_if.dut spisd,
|
||||
@@ -282,10 +283,11 @@ module ip_nmi_wrapper (
|
||||
|
||||
|
||||
nmi_gpio u_nmi_gpio (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.nmi (u_gpio_nmi_if),
|
||||
.gpio (gpio)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.nmi (u_gpio_nmi_if),
|
||||
.gpio (gpio),
|
||||
.user_gpio(user_gpio)
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
|
||||
`define USER_CORESEL_WIDTH 5
|
||||
`define USER_IPSEL_WIDTH 8
|
||||
`define USER_GPIO_NUM 16
|
||||
`define USER_GPIO_NUM 32
|
||||
|
||||
`endif
|
||||
@@ -22,9 +22,6 @@ module retrosoc (
|
||||
`ifdef CORE_MDD
|
||||
input logic [`USER_CORESEL_WIDTH-1:0] core_sel_i,
|
||||
`endif
|
||||
`ifdef IP_MDD
|
||||
gpio_if.dut user_gpio,
|
||||
`endif
|
||||
`ifdef HAVE_SRAM_IF
|
||||
ram_if.master ram,
|
||||
`endif
|
||||
@@ -42,6 +39,7 @@ module retrosoc (
|
||||
nmi_if u_dma_nmi_if ();
|
||||
nmi_if u_nmi_nmi_if ();
|
||||
nmi_if u_apb_nmi_if ();
|
||||
user_gpio_if u_user_gpio_if ();
|
||||
// ip interface
|
||||
gpio_if u_gpio_if ();
|
||||
psram_if u_psram_if ();
|
||||
@@ -73,6 +71,11 @@ module retrosoc (
|
||||
.outer(gpio)
|
||||
);
|
||||
|
||||
`ifndef IP_MDD
|
||||
assign u_user_gpio_if.do_o = '0;
|
||||
assign u_user_gpio_if.oe_o = '0;
|
||||
`endif
|
||||
|
||||
`ifdef CORE_MDD
|
||||
assign u_sysctrl_if.core_sel_i = core_sel_i;
|
||||
`else
|
||||
@@ -395,6 +398,7 @@ module retrosoc (
|
||||
.rst_aud_n_i (rst_aud_n_i),
|
||||
.nmi (u_nmi_nmi_if),
|
||||
.gpio (u_gpio_if),
|
||||
.user_gpio (u_user_gpio_if),
|
||||
.uart (uart0),
|
||||
.psram (u_psram_if),
|
||||
.spisd (u_spisd_if),
|
||||
@@ -430,7 +434,7 @@ module retrosoc (
|
||||
.ps2 (u_ps2_if),
|
||||
`ifdef IP_MDD
|
||||
.ip_sel_i (u_sysctrl_if.ip_sel_o),
|
||||
.gpio (user_gpio),
|
||||
.user_gpio (u_user_gpio_if),
|
||||
`endif
|
||||
.irq_o (s_apb_irq)
|
||||
);
|
||||
|
||||
@@ -29,10 +29,6 @@ module retrosoc_asic (
|
||||
logic [`USER_CORESEL_WIDTH-1:0] s_core_sel;
|
||||
`endif
|
||||
|
||||
`ifdef IP_MDD
|
||||
gpio_if #(`USER_GPIO_NUM) u_user_gpio_if ();
|
||||
`endif
|
||||
|
||||
`ifdef HAVE_SRAM_IF
|
||||
ram_if u_ram_if ();
|
||||
`endif
|
||||
@@ -76,9 +72,6 @@ rcu u_rcu (
|
||||
`ifdef CORE_MDD
|
||||
.core_sel_i (s_core_sel),
|
||||
`endif
|
||||
`ifdef IP_MDD
|
||||
.user_gpio (u_user_gpio_if),
|
||||
`endif
|
||||
`ifdef HAVE_SRAM_IF
|
||||
.ram (u_ram_if),
|
||||
`endif
|
||||
|
||||
@@ -25,7 +25,7 @@ module user_ip_design_template #(
|
||||
// verilog_format: off
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.user_ip gpio,
|
||||
apb4_if.slave apb
|
||||
// verilog_format: on
|
||||
);
|
||||
@@ -67,13 +67,10 @@ module user_ip_design_template #(
|
||||
|
||||
// ========== USER CUSTOM AREA ==============
|
||||
// NOTE: If needed, define io logic here.
|
||||
// `gpio_oe` is active high, meaning gpio is
|
||||
// output when `gpio_oe[x]` = 1'b1.
|
||||
assign gpio.gpio_oe = '1;
|
||||
assign gpio.gpio_cs = '1; // 1: CMOS 0: SCHMI
|
||||
assign gpio.gpio_pu = '0;
|
||||
assign gpio.gpio_pd = '0;
|
||||
assign gpio.gpio_out = '0;
|
||||
// `oe_o` is active high, meaning GPIO is output when `oe_o[x]` is 1'b1.
|
||||
// Pad electrical controls remain owned by the native GPIO controller.
|
||||
assign gpio.oe_o = '0;
|
||||
assign gpio.do_o = '0;
|
||||
|
||||
// ========== USER CUSTOM AREA END ==========
|
||||
|
||||
|
||||
+68
-83
@@ -16,15 +16,15 @@ module user_ip_top (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [`USER_IPSEL_WIDTH-1:0] sel_i,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.user_ip gpio,
|
||||
apb4_if.slave apb
|
||||
// verilog_format: on
|
||||
);
|
||||
|
||||
// ====== GENERATED BY SCRIPT ======
|
||||
// verilog_format: off
|
||||
gpio_if u_user_1_gpio_if();
|
||||
gpio_if u_user_2_gpio_if();
|
||||
user_gpio_if #(`USER_GPIO_NUM) u_user_1_gpio_if();
|
||||
user_gpio_if #(`USER_GPIO_NUM) u_user_2_gpio_if();
|
||||
|
||||
apb4_if u_user_1_apb_if(clk_i, rst_n_i);
|
||||
apb4_if u_user_2_apb_if(clk_i, rst_n_i);
|
||||
@@ -32,103 +32,88 @@ module user_ip_top (
|
||||
// verilog_format: on
|
||||
always_comb begin
|
||||
// gpio
|
||||
gpio.gpio_out = '0;
|
||||
gpio.gpio_oe = '0;
|
||||
gpio.gpio_cs = '0;
|
||||
gpio.gpio_pu = '0;
|
||||
gpio.gpio_pd = '0;
|
||||
u_user_1_gpio_if.gpio_in = '0;
|
||||
u_user_2_gpio_if.gpio_in = '0;
|
||||
gpio.do_o = '0;
|
||||
gpio.oe_o = '0;
|
||||
// apb
|
||||
apb.pready = '0;
|
||||
apb.prdata = '0;
|
||||
apb.pslverr = '0;
|
||||
u_user_1_apb_if.paddr = '0;
|
||||
u_user_1_apb_if.pprot = '0;
|
||||
u_user_1_apb_if.psel = '0;
|
||||
u_user_1_apb_if.penable = '0;
|
||||
u_user_1_apb_if.pwrite = '0;
|
||||
u_user_1_apb_if.pwdata = '0;
|
||||
u_user_1_apb_if.pstrb = '0;
|
||||
u_user_2_apb_if.paddr = '0;
|
||||
u_user_2_apb_if.pprot = '0;
|
||||
u_user_2_apb_if.psel = '0;
|
||||
u_user_2_apb_if.penable = '0;
|
||||
u_user_2_apb_if.pwrite = '0;
|
||||
u_user_2_apb_if.pwdata = '0;
|
||||
u_user_2_apb_if.pstrb = '0;
|
||||
apb.pready = '0;
|
||||
apb.prdata = '0;
|
||||
apb.pslverr = '0;
|
||||
u_user_1_apb_if.paddr = '0;
|
||||
u_user_1_apb_if.pprot = '0;
|
||||
u_user_1_apb_if.psel = '0;
|
||||
u_user_1_apb_if.penable = '0;
|
||||
u_user_1_apb_if.pwrite = '0;
|
||||
u_user_1_apb_if.pwdata = '0;
|
||||
u_user_1_apb_if.pstrb = '0;
|
||||
u_user_2_apb_if.paddr = '0;
|
||||
u_user_2_apb_if.pprot = '0;
|
||||
u_user_2_apb_if.psel = '0;
|
||||
u_user_2_apb_if.penable = '0;
|
||||
u_user_2_apb_if.pwrite = '0;
|
||||
u_user_2_apb_if.pwdata = '0;
|
||||
u_user_2_apb_if.pstrb = '0;
|
||||
unique case (sel_i)
|
||||
8'd1: begin
|
||||
// gpio
|
||||
gpio.gpio_out = u_user_1_gpio_if.gpio_out;
|
||||
gpio.gpio_oe = u_user_1_gpio_if.gpio_oe;
|
||||
gpio.gpio_cs = u_user_1_gpio_if.gpio_cs;
|
||||
gpio.gpio_pu = u_user_1_gpio_if.gpio_pu;
|
||||
gpio.gpio_pd = u_user_1_gpio_if.gpio_pd;
|
||||
u_user_1_gpio_if.gpio_in = gpio.gpio_in;
|
||||
gpio.do_o = u_user_1_gpio_if.do_o;
|
||||
gpio.oe_o = u_user_1_gpio_if.oe_o;
|
||||
// apb
|
||||
apb.pready = u_user_1_apb_if.pready;
|
||||
apb.prdata = u_user_1_apb_if.prdata;
|
||||
apb.pslverr = u_user_1_apb_if.pslverr;
|
||||
u_user_1_apb_if.paddr = apb.paddr;
|
||||
u_user_1_apb_if.pprot = apb.pprot;
|
||||
u_user_1_apb_if.psel = apb.psel;
|
||||
u_user_1_apb_if.penable = apb.penable;
|
||||
u_user_1_apb_if.pwrite = apb.pwrite;
|
||||
u_user_1_apb_if.pwdata = apb.pwdata;
|
||||
u_user_1_apb_if.pstrb = apb.pstrb;
|
||||
apb.pready = u_user_1_apb_if.pready;
|
||||
apb.prdata = u_user_1_apb_if.prdata;
|
||||
apb.pslverr = u_user_1_apb_if.pslverr;
|
||||
u_user_1_apb_if.paddr = apb.paddr;
|
||||
u_user_1_apb_if.pprot = apb.pprot;
|
||||
u_user_1_apb_if.psel = apb.psel;
|
||||
u_user_1_apb_if.penable = apb.penable;
|
||||
u_user_1_apb_if.pwrite = apb.pwrite;
|
||||
u_user_1_apb_if.pwdata = apb.pwdata;
|
||||
u_user_1_apb_if.pstrb = apb.pstrb;
|
||||
end
|
||||
8'd2: begin
|
||||
// gpio
|
||||
gpio.gpio_out = u_user_2_gpio_if.gpio_out;
|
||||
gpio.gpio_oe = u_user_2_gpio_if.gpio_oe;
|
||||
gpio.gpio_cs = u_user_2_gpio_if.gpio_cs;
|
||||
gpio.gpio_pu = u_user_2_gpio_if.gpio_pu;
|
||||
gpio.gpio_pd = u_user_2_gpio_if.gpio_pd;
|
||||
u_user_2_gpio_if.gpio_in = gpio.gpio_in;
|
||||
gpio.do_o = u_user_2_gpio_if.do_o;
|
||||
gpio.oe_o = u_user_2_gpio_if.oe_o;
|
||||
// apb
|
||||
apb.pready = u_user_2_apb_if.pready;
|
||||
apb.prdata = u_user_2_apb_if.prdata;
|
||||
apb.pslverr = u_user_2_apb_if.pslverr;
|
||||
u_user_2_apb_if.paddr = apb.paddr;
|
||||
u_user_2_apb_if.pprot = apb.pprot;
|
||||
u_user_2_apb_if.psel = apb.psel;
|
||||
u_user_2_apb_if.penable = apb.penable;
|
||||
u_user_2_apb_if.pwrite = apb.pwrite;
|
||||
u_user_2_apb_if.pwdata = apb.pwdata;
|
||||
u_user_2_apb_if.pstrb = apb.pstrb;
|
||||
apb.pready = u_user_2_apb_if.pready;
|
||||
apb.prdata = u_user_2_apb_if.prdata;
|
||||
apb.pslverr = u_user_2_apb_if.pslverr;
|
||||
u_user_2_apb_if.paddr = apb.paddr;
|
||||
u_user_2_apb_if.pprot = apb.pprot;
|
||||
u_user_2_apb_if.psel = apb.psel;
|
||||
u_user_2_apb_if.penable = apb.penable;
|
||||
u_user_2_apb_if.pwrite = apb.pwrite;
|
||||
u_user_2_apb_if.pwdata = apb.pwdata;
|
||||
u_user_2_apb_if.pstrb = apb.pstrb;
|
||||
end
|
||||
default: begin
|
||||
// gpio
|
||||
gpio.gpio_out = '0;
|
||||
gpio.gpio_oe = '0;
|
||||
gpio.gpio_cs = '0;
|
||||
gpio.gpio_pu = '0;
|
||||
gpio.gpio_pd = '0;
|
||||
u_user_1_gpio_if.gpio_in = '0;
|
||||
u_user_2_gpio_if.gpio_in = '0;
|
||||
gpio.do_o = '0;
|
||||
gpio.oe_o = '0;
|
||||
// apb
|
||||
apb.pready = '0;
|
||||
apb.prdata = '0;
|
||||
apb.pslverr = '0;
|
||||
u_user_1_apb_if.paddr = '0;
|
||||
u_user_1_apb_if.pprot = '0;
|
||||
u_user_1_apb_if.psel = '0;
|
||||
u_user_1_apb_if.penable = '0;
|
||||
u_user_1_apb_if.pwrite = '0;
|
||||
u_user_1_apb_if.pwdata = '0;
|
||||
u_user_1_apb_if.pstrb = '0;
|
||||
u_user_2_apb_if.paddr = '0;
|
||||
u_user_2_apb_if.pprot = '0;
|
||||
u_user_2_apb_if.psel = '0;
|
||||
u_user_2_apb_if.penable = '0;
|
||||
u_user_2_apb_if.pwrite = '0;
|
||||
u_user_2_apb_if.pwdata = '0;
|
||||
u_user_2_apb_if.pstrb = '0;
|
||||
apb.pready = '0;
|
||||
apb.prdata = '0;
|
||||
apb.pslverr = '0;
|
||||
u_user_1_apb_if.paddr = '0;
|
||||
u_user_1_apb_if.pprot = '0;
|
||||
u_user_1_apb_if.psel = '0;
|
||||
u_user_1_apb_if.penable = '0;
|
||||
u_user_1_apb_if.pwrite = '0;
|
||||
u_user_1_apb_if.pwdata = '0;
|
||||
u_user_1_apb_if.pstrb = '0;
|
||||
u_user_2_apb_if.paddr = '0;
|
||||
u_user_2_apb_if.pprot = '0;
|
||||
u_user_2_apb_if.psel = '0;
|
||||
u_user_2_apb_if.penable = '0;
|
||||
u_user_2_apb_if.pwrite = '0;
|
||||
u_user_2_apb_if.pwdata = '0;
|
||||
u_user_2_apb_if.pstrb = '0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign u_user_1_gpio_if.di_i = gpio.di_i;
|
||||
assign u_user_2_gpio_if.di_i = gpio.di_i;
|
||||
|
||||
user_ip_design_username1 #(1) u_user_ip_design_username1 (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
|
||||
@@ -15,7 +15,7 @@ module user_ip_wrapper (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [`USER_IPSEL_WIDTH-1:0] sel_i,
|
||||
gpio_if.dut gpio,
|
||||
user_gpio_if.user_ip gpio,
|
||||
apb4_if.slave apb
|
||||
// verilog_format: on
|
||||
);
|
||||
|
||||
+62
-5
@@ -3,14 +3,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import fcntl
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import TextIO
|
||||
from typing import Iterator, TextIO
|
||||
|
||||
|
||||
def tree_digest(root: Path) -> str:
|
||||
@@ -49,6 +53,58 @@ def rewrite_filelists(root: Path, replacements: tuple[tuple[Path, Path], ...]) -
|
||||
filelist.write_text(content, encoding="utf-8")
|
||||
|
||||
|
||||
def migrate_user_gpio_interfaces(root: Path) -> list[Path]:
|
||||
"""Migrate legacy MPW user-IP GPIO ports in isolated build sources."""
|
||||
legacy_port = re.compile(r"\bgpio_if\.dut\s+gpio\b")
|
||||
legacy_pad_control = re.compile(
|
||||
r"^[ \t]*assign[ \t]+gpio\.gpio_(?:cs|pu|pd)[ \t]*=[^;]*;[^\n]*\n",
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
migrated: list[Path] = []
|
||||
for source in sorted(root.rglob("*.sv")):
|
||||
content = source.read_text(encoding="utf-8")
|
||||
if legacy_port.search(content) is None:
|
||||
continue
|
||||
|
||||
content, port_count = legacy_port.subn("user_gpio_if.user_ip gpio", content)
|
||||
if port_count != 1:
|
||||
raise ValueError(f"expected one legacy GPIO port in {source}")
|
||||
content = legacy_pad_control.sub("", content)
|
||||
for legacy_name, interface_name in (
|
||||
("gpio.gpio_oe", "gpio.oe_o"),
|
||||
("gpio.gpio_out", "gpio.do_o"),
|
||||
("gpio.gpio_in", "gpio.di_i"),
|
||||
):
|
||||
content = content.replace(legacy_name, interface_name)
|
||||
if "gpio.gpio_" in content:
|
||||
raise ValueError(f"unsupported legacy GPIO signal in {source}")
|
||||
source.write_text(content, encoding="utf-8")
|
||||
migrated.append(source)
|
||||
return migrated
|
||||
|
||||
|
||||
def remove_tree(path: Path) -> None:
|
||||
"""Remove an MPW workspace, tolerating transient NFS directory entries."""
|
||||
for attempt in range(3):
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
return
|
||||
except OSError as error:
|
||||
if error.errno != errno.ENOTEMPTY or attempt == 2:
|
||||
raise
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temporary_workspace(parent: Path, prefix: str) -> Iterator[Path]:
|
||||
workspace = Path(tempfile.mkdtemp(prefix=prefix, dir=parent))
|
||||
try:
|
||||
yield workspace
|
||||
finally:
|
||||
if workspace.exists():
|
||||
remove_tree(workspace)
|
||||
|
||||
|
||||
def prepare_legacy_mpw_workspace(root: Path, source: Path, workspace: Path) -> Path:
|
||||
legacy_mpw = workspace / "rtl" / "mini" / "mpw"
|
||||
legacy_mpw.mkdir(parents=True, exist_ok=True)
|
||||
@@ -75,11 +131,10 @@ def generate(args: argparse.Namespace) -> None:
|
||||
log_path = output.parent / f"{output.name}-generation.log"
|
||||
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
with log_path.open("w", encoding="utf-8") as log, tempfile.TemporaryDirectory(
|
||||
prefix=f".{output.name}.", dir=output.parent
|
||||
) as temp:
|
||||
with log_path.open("w", encoding="utf-8") as log, temporary_workspace(
|
||||
output.parent, f".{output.name}."
|
||||
) as workspace:
|
||||
log.write(f"simulator={args.simu} core={args.core} ip={args.ip}\n")
|
||||
workspace = Path(temp)
|
||||
candidate = workspace / "output"
|
||||
candidate.mkdir()
|
||||
if not needs_core and not needs_ip:
|
||||
@@ -100,6 +155,8 @@ def generate(args: argparse.Namespace) -> None:
|
||||
run(workspace, log, mpw / "ip.py")
|
||||
run(workspace, log, mpw / "info.py", "IP")
|
||||
shutil.copytree(shared / "ip", candidate / "ip")
|
||||
migrated = migrate_user_gpio_interfaces(candidate / "ip")
|
||||
log.write(f"migrated user GPIO interfaces: {len(migrated)}\n")
|
||||
if needs_core:
|
||||
run(workspace, log, mpw / "core.py", args.simu)
|
||||
run(workspace, log, mpw / "info.py", "CORE")
|
||||
|
||||
+38
-4
@@ -15,25 +15,59 @@ sys.path.insert(0, str(ROOT))
|
||||
from scripts.check_c_warnings import self_owned_warnings # noqa: E402
|
||||
|
||||
|
||||
MDD_VERILATOR_SIM_TIME = 180
|
||||
|
||||
|
||||
PR_COMMANDS = (
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("firmware",)),
|
||||
("configs/ci/hazard3-rv32im-ihp130-shell.mk", ("firmware",)),
|
||||
("configs/ci/hazard3-rv32im-ihp130-ip-mdd-shell.mk", ("firmware",)),
|
||||
(
|
||||
"configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk",
|
||||
(
|
||||
"SIMU=VERILATOR",
|
||||
f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}",
|
||||
"firmware",
|
||||
"sim",
|
||||
),
|
||||
),
|
||||
(
|
||||
"configs/ci/mdd-rv32im-ihp130.mk",
|
||||
("SIMU=VERILATOR", "SOC_SIM_TIME=120", "firmware", "sim"),
|
||||
(
|
||||
"SIMU=VERILATOR",
|
||||
f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}",
|
||||
"firmware",
|
||||
"sim",
|
||||
),
|
||||
),
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("SIMU=VERILATOR", "sim")),
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
|
||||
(
|
||||
"configs/ci/hazard3-rv32im-ihp130.mk",
|
||||
("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm"),
|
||||
),
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("SYNTH=YOSYS", "synth")),
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("SIMU=IVERILOG", "SIM_FIRMWARE_NAME=retrosoc_asm", "SIM_SUCCESS_MARKER=Mem wr/rd test success", "RTL_SIM_TIMEOUT=5200000", "netsim")),
|
||||
(
|
||||
"configs/ci/hazard3-rv32im-ihp130.mk",
|
||||
(
|
||||
"SIMU=IVERILOG",
|
||||
"SIM_FIRMWARE_NAME=retrosoc_asm",
|
||||
"SIM_SUCCESS_MARKER=Mem wr/rd test success",
|
||||
"RTL_SIM_TIMEOUT=5200000",
|
||||
"netsim",
|
||||
),
|
||||
),
|
||||
("configs/ci/hazard3-rv32im-ihp130.mk", ("STA=OPENSTA", "sta")),
|
||||
)
|
||||
NIGHTLY_COMMANDS = PR_COMMANDS + (
|
||||
("configs/nightly/picorv32-rv32im-ihp130.mk", ("SIMU=VERILATOR", "firmware", "sim")),
|
||||
("configs/nightly/picorv32-rv32im-ihp130.mk", ("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm")),
|
||||
(
|
||||
"configs/nightly/picorv32-rv32im-ihp130.mk",
|
||||
("SIMU=IVERILOG", "RTL_SIM_TIMEOUT=5200000", "sim-asm"),
|
||||
),
|
||||
)
|
||||
PR_PROFILES = (
|
||||
"configs/ci/hazard3-rv32im-ihp130.mk",
|
||||
"configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk",
|
||||
"configs/ci/mdd-rv32im-ihp130.mk",
|
||||
)
|
||||
NIGHTLY_PROFILES = PR_PROFILES + ("configs/nightly/picorv32-rv32im-ihp130.mk",)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module gpio_user_mux_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
nmi_if nmi ();
|
||||
gpio_if gpio ();
|
||||
user_gpio_if user_gpio ();
|
||||
logic [31:0] read_data;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
nmi_gpio dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.nmi (nmi),
|
||||
.gpio (gpio),
|
||||
.user_gpio(user_gpio)
|
||||
);
|
||||
|
||||
task automatic write_register(input logic [7:0] address, input logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
nmi.addr = {24'd0, address};
|
||||
nmi.wdata = data;
|
||||
nmi.wstrb = 4'hF;
|
||||
nmi.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
@(negedge clk_i);
|
||||
nmi.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_register(input logic [7:0] address, output logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
nmi.addr = {24'd0, address};
|
||||
nmi.wdata = '0;
|
||||
nmi.wstrb = '0;
|
||||
nmi.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
data = nmi.rdata;
|
||||
@(negedge clk_i);
|
||||
nmi.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic select_user_gpio(input logic [31:0] selection);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
nmi.addr = 32'h0000_0030;
|
||||
nmi.wdata = selection;
|
||||
nmi.wstrb = 4'hF;
|
||||
nmi.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (gpio.oe_o[0] !== 1'b0) $fatal(1, "GPIO handoff did not drive high impedance");
|
||||
@(negedge clk_i);
|
||||
nmi.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
nmi.valid = 1'b0;
|
||||
nmi.addr = '0;
|
||||
nmi.wdata = '0;
|
||||
nmi.wstrb = '0;
|
||||
gpio.di_i = '0;
|
||||
gpio.alt0_do_i = '0;
|
||||
gpio.alt0_oe_i = '0;
|
||||
gpio.alt1_do_i = '0;
|
||||
gpio.alt1_oe_i = '0;
|
||||
user_gpio.do_o = '0;
|
||||
user_gpio.oe_o = '0;
|
||||
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
|
||||
write_register(8'h00, 32'h0000_0001);
|
||||
write_register(8'h10, 32'h0000_0001);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b11) $fatal(1, "native software GPIO is not active");
|
||||
|
||||
user_gpio.do_o[0] = 1'b0;
|
||||
user_gpio.oe_o[0] = 1'b1;
|
||||
select_user_gpio(32'h0000_0001);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "user GPIO was not selected");
|
||||
read_register(8'h30, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_SEL readback is incorrect");
|
||||
read_register(8'h38, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_STATUS is not active after handoff");
|
||||
|
||||
gpio.di_i[0] = 1'b1;
|
||||
#1;
|
||||
if (user_gpio.di_i[0] !== 1'b1) $fatal(1, "user GPIO input path is disconnected");
|
||||
|
||||
write_register(8'h34, 32'h0000_0001);
|
||||
read_register(8'h34, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_LOCK is not write-one-set");
|
||||
write_register(8'h30, '0);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "USER_LOCK did not preserve ownership");
|
||||
read_register(8'h30, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "locked USER_SEL changed");
|
||||
|
||||
gpio.alt0_do_i[1] = 1'b1;
|
||||
gpio.alt0_oe_i[1] = 1'b1;
|
||||
write_register(8'h28, 32'h0000_0002);
|
||||
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b11) $fatal(1, "alternate GPIO mode regressed");
|
||||
|
||||
write_register(8'h04, 32'h0000_0001);
|
||||
write_register(8'h08, 32'h0000_0001);
|
||||
write_register(8'h0C, 32'h0000_0001);
|
||||
if ({gpio.cs_o[0], gpio.pu_o[0], gpio.pd_o[0]} !== 3'b111)
|
||||
$fatal(1, "native GPIO electrical controls changed under user ownership");
|
||||
|
||||
$display("GPIO user ownership test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,59 @@
|
||||
"""RTL tests for user-IP ownership of the native GPIO pad bank."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_user_ip_can_own_native_gpio_pads_with_a_safe_handoff(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 / "gpio_user_mux.fl"
|
||||
source_list.write_text(
|
||||
"\n".join(
|
||||
[
|
||||
"+define+SV_ASSRT_DISABLE",
|
||||
"+define+IP_MDD",
|
||||
f"+incdir+{ROOT / 'rtl/ip/native/peripheral'}",
|
||||
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/nmi_if.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv"),
|
||||
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/edge_det.sv"),
|
||||
str(ROOT / "rtl/ip/native/peripheral/gpio.sv"),
|
||||
str(ROOT / "tests/rtl/gpio_user_mux_tb.sv"),
|
||||
"",
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
converted = tmp_path / "gpio_user_mux_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 / "gpio_user_mux_tb"
|
||||
subprocess.run(
|
||||
[iverilog, "-g2012", "-s", "gpio_user_mux_tb", "-o", str(simulation), str(converted)],
|
||||
check=True,
|
||||
)
|
||||
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
|
||||
|
||||
assert "GPIO user ownership test passed" in result.stdout
|
||||
@@ -60,6 +60,9 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
||||
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
|
||||
assert "user_gpio_" not in ports
|
||||
assert "user_gpio_" not in pads
|
||||
assert "user_gpio_" not in testbench
|
||||
|
||||
|
||||
def test_pin_map_rejects_duplicate_pads(tmp_path: Path) -> None:
|
||||
|
||||
@@ -25,8 +25,9 @@ from scripts.analyze_warnings import normalize # noqa: E402
|
||||
from scripts.check_c_warnings import self_owned_warnings # noqa: E402
|
||||
from scripts.check_format import format_files # noqa: E402
|
||||
from scripts.dependency_lock import LockError, load_lock # noqa: E402
|
||||
from scripts.generate_mpw import migrate_user_gpio_interfaces # noqa: E402
|
||||
from scripts.install_toolchain import safe_extract # noqa: E402
|
||||
from scripts.regress import regression_environment # noqa: E402
|
||||
from scripts.regress import MDD_VERILATOR_SIM_TIME, PR_COMMANDS, regression_environment # noqa: E402
|
||||
from scripts.setup_helpers import download_file, ensure_git_repo # noqa: E402
|
||||
|
||||
|
||||
@@ -89,6 +90,33 @@ def test_generate_all_is_stable_and_expands_paths(tmp_path: Path) -> None:
|
||||
assert str(ROOT / "rtl/managed/clusterip") in cluster
|
||||
|
||||
|
||||
def test_legacy_user_gpio_interface_is_migrated_in_generated_source(tmp_path: Path) -> None:
|
||||
source = tmp_path / "user_ip_design.sv"
|
||||
source.write_text(
|
||||
"""module user_ip_design (gpio_if.dut gpio);
|
||||
logic value;
|
||||
assign gpio.gpio_oe = value;
|
||||
assign gpio.gpio_cs = value;
|
||||
assign gpio.gpio_pu = value;
|
||||
assign gpio.gpio_pd = value;
|
||||
assign gpio.gpio_out = value;
|
||||
always_comb value = gpio.gpio_in[0];
|
||||
endmodule
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
assert migrate_user_gpio_interfaces(tmp_path) == [source]
|
||||
|
||||
migrated = source.read_text(encoding="utf-8")
|
||||
assert "gpio_if.dut" not in migrated
|
||||
assert "gpio.gpio_" not in migrated
|
||||
assert "user_gpio_if.user_ip gpio" in migrated
|
||||
assert "gpio.oe_o" in migrated
|
||||
assert "gpio.do_o" in migrated
|
||||
assert "gpio.di_i" in migrated
|
||||
|
||||
|
||||
def test_prepare_norflash_and_missing_firmware(tmp_path: Path) -> None:
|
||||
models = tmp_path / "models"
|
||||
models.mkdir()
|
||||
@@ -270,6 +298,22 @@ def test_regression_runner_uses_one_build_timestamp(monkeypatch) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_mdd_regressions_allow_uart_boot_to_complete() -> None:
|
||||
mdd_profiles = {
|
||||
"configs/ci/hazard3-rv32im-ihp130-ip-mdd.mk",
|
||||
"configs/ci/mdd-rv32im-ihp130.mk",
|
||||
}
|
||||
expected_values = (
|
||||
"SIMU=VERILATOR",
|
||||
f"SOC_SIM_TIME={MDD_VERILATOR_SIM_TIME}",
|
||||
"firmware",
|
||||
"sim",
|
||||
)
|
||||
|
||||
assert MDD_VERILATOR_SIM_TIME == 180
|
||||
assert {profile for profile, values in PR_COMMANDS if values == expected_values} == mdd_profiles
|
||||
|
||||
|
||||
def test_run_flow_writes_structured_result(tmp_path: Path) -> None:
|
||||
log = tmp_path / "flow.log"
|
||||
result = tmp_path / "result.json"
|
||||
@@ -442,8 +486,7 @@ def test_warning_normalization_keeps_ranges_and_removes_variant_hash(tmp_path: P
|
||||
)
|
||||
normalized = normalize(tmp_path, "WIDTH", message)
|
||||
assert normalized == (
|
||||
"WIDTH:$BUILD/generated/core.sv:<line>: "
|
||||
"Bit extraction of var[7:0] is too wide"
|
||||
"WIDTH:$BUILD/generated/core.sv:<line>: Bit extraction of var[7:0] is too wide"
|
||||
)
|
||||
|
||||
|
||||
@@ -473,8 +516,7 @@ def test_metrics_collection_and_observe_policy(tmp_path: Path) -> None:
|
||||
report_dir = variant / "syn/yosys/rpt"
|
||||
report_dir.mkdir(parents=True)
|
||||
(report_dir / "retrosoc_asic_area.rpt").write_text(
|
||||
" 42 1.23E+02 retrosoc_asic\n"
|
||||
"Chip area for top module '\\retrosoc_asic': 123.0\n",
|
||||
" 42 1.23E+02 retrosoc_asic\nChip area for top module '\\retrosoc_asic': 123.0\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(report_dir / "retrosoc_asic_area.json").write_text(
|
||||
@@ -569,9 +611,7 @@ def test_ci_actions_are_pinned_to_commits() -> None:
|
||||
assert re.fullmatch(r"[^@]+@[0-9a-f]{40}", value), f"unpinned action in {path}: {value}"
|
||||
|
||||
|
||||
HASH_LOCKED_REQUIREMENT = re.compile(
|
||||
r"[^\s=]+==[^\s]+(?:\s+--hash=sha256:[0-9a-f]{64})+"
|
||||
)
|
||||
HASH_LOCKED_REQUIREMENT = re.compile(r"[^\s=]+==[^\s]+(?:\s+--hash=sha256:[0-9a-f]{64})+")
|
||||
|
||||
|
||||
def hash_locked_requirement_lines(content: str) -> list[str]:
|
||||
|
||||
Reference in new issue
Block a user