Files
gps/targets/fpga/ti375c529/tools/gen_ti375c529_wrapper.py
T
onur 1dbd1b5151 Merge tdk_new and Ti375 Efinity support
Keep the current GPS software and Genesys2/VCU118 flows while integrating Ti375C529 Efinity, flash, programming, and debug support. Add board-specific SPI NOR profiles and persistent GPS flash layouts across all three boards.
2026-08-13 01:28:46 +03:00

277 lines
9.8 KiB
Python

#!/usr/bin/env python3
"""Generate the Efinix Ti375 board shell around the shared Rocket subsystem."""
import argparse
import re
from pathlib import Path
def width_bits(width):
if not width:
return 1
match = re.fullmatch(r"\[(\d+):(\d+)\]", width.strip())
if not match:
raise ValueError(f"unsupported width: {width}")
return abs(int(match.group(1)) - int(match.group(2))) + 1
def parse_ports(verilog_path):
text = Path(verilog_path).read_text()
module_name = Path(verilog_path).stem
start = text.index(f"module {module_name}(")
end = text.index(");", start)
header = re.sub(r"//.*", "", text[start:end])
pieces = re.split(r"\b(input|output)\b", header)[1:]
ports = []
for direction, body in zip(pieces[0::2], pieces[1::2]):
body = body.replace("\n", " ")
body = re.split(r"\b(input|output)\b", body)[0]
default_width = ""
width_match = re.match(r"\s*(\[[^\]]+\])\s*(.*)", body)
if width_match:
default_width = width_match.group(1)
rest = width_match.group(2)
else:
rest = body
for item in rest.split(","):
item = item.strip()
if not item:
continue
item_match = re.match(r"(\[[^\]]+\])\s*(\w+)$", item)
if item_match:
width, name = item_match.groups()
else:
width = default_width
name = item.split()[-1]
if re.fullmatch(r"\w+", name):
ports.append((direction, width, name))
return ports
def const_for(width):
bits = width_bits(width)
return "1'b0" if bits == 1 else f"{bits}'d0"
def is_clock(name):
return name.endswith("_clock") or name == "debug_clock"
def is_reset(name):
return name.endswith("_reset") or name == "debug_reset"
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--rocket-top", required=True)
parser.add_argument("--out", required=True)
args = parser.parse_args()
ports = parse_ports(args.rocket_top)
port_by_name = {name: (direction, width) for direction, width, name in ports}
required = {
"uart_rxd", "uart_txd",
"spi_sck", "spi_csn", "spi_dq_i", "spi_dq_o", "spi_dq_oe",
"pmod_spi_sck", "pmod_spi_csn",
"pmod_spi_dq_i", "pmod_spi_dq_o", "pmod_spi_dq_oe",
"max2771_spi_sclk", "max2771_spi_csn",
"max2771_spi_sdata_i", "max2771_spi_sdata_o",
"max2771_spi_sdata_oe",
"pmod_irq", "gps_acq_clk", "gps_trck_clk",
"gps_rf_clk", "gps_rf_data", "gps_pps",
}
missing = sorted(required - port_by_name.keys())
if missing:
raise RuntimeError(f"Rocket top is missing required Ti375 ports: {', '.join(missing)}")
passthrough = {
"pmod_spi_sck", "pmod_spi_csn",
"max2771_spi_sclk", "max2771_spi_csn",
"pmod_irq", "gps_pps",
}
def decl(width, name):
return f" wire {width + ' ' if width else ''}{name};"
assigns = []
for direction, width, name in ports:
if direction != "input" or name in passthrough:
continue
if name.startswith("debug_clockeddmi_dmi_req"):
continue
if name in {
"debug_clockeddmi_dmi_resp_ready",
"debug_clockeddmi_dmiClock",
"debug_clockeddmi_dmiReset",
}:
continue
if name == "resetctrl_hartIsInReset_0":
value = "soc_reset"
elif name == "debug_reset":
value = "fabric_reset"
elif is_reset(name):
value = "soc_reset"
elif name == "gps_acq_clk":
value = "gps_acq_clk_gen"
elif name == "gps_trck_clk":
value = "gps_trck_clk_gen"
elif name in {"gps_rf_clk", "gps_afe_clk"}:
value = "rf_clk"
elif name == "gps_rf_data":
value = "rf_data"
elif is_clock(name):
value = "fabric_clk"
elif name == "debug_dmactiveAck":
value = "debug_dmactive"
elif name == "uart_rxd":
value = "uart_rx"
elif name == "spi_dq_i":
value = "flash_dq_IN"
elif name == "pmod_spi_dq_i":
value = "pmod_spi_dq_IN"
elif name == "max2771_spi_sdata_i":
value = "max2771_spi_sdata_IN"
elif name == "interrupts":
value = "2'b00"
else:
value = const_for(width)
assigns.append(f" assign {name} = {value};")
port_map = ",\n".join(f" .{name}({name})" for _, _, name in ports)
internal_decls = "\n".join(
decl(width, name) for _, width, name in ports if name not in passthrough
)
content = f"""`timescale 1ns/1ps
module ti375c529_rocket_top (
(* syn_peri_port = 0 *) input wire fabric_clk,
(* syn_peri_port = 0 *) input wire fabric_pll_locked,
(* syn_peri_port = 0 *) input wire board_clk_100,
(* syn_peri_port = 0 *) input wire gps_ref_clk_gen,
(* syn_peri_port = 0 *) input wire gps_acq_clk_gen,
(* syn_peri_port = 0 *) input wire gps_trck_clk_gen,
(* syn_peri_port = 0 *) input wire gps_domain_feedback,
(* syn_peri_port = 0 *) input wire gps_ref_pll_locked,
(* syn_peri_port = 0 *) input wire gps_domain_pll_locked,
(* syn_peri_port = 0 *) input wire reset_n,
(* syn_peri_port = 0 *) input wire uart_rx,
(* syn_peri_port = 0 *) output wire uart_tx,
(* syn_peri_port = 0 *) output wire flash_sck,
(* syn_peri_port = 0 *) output wire flash_csn,
(* syn_peri_port = 0 *) input wire [3:0] flash_dq_IN,
(* syn_peri_port = 0 *) output wire [3:0] flash_dq_OUT,
(* syn_peri_port = 0 *) output wire [3:0] flash_dq_OE,
(* syn_peri_port = 0 *) input wire rf_clk,
(* syn_peri_port = 0 *) input wire [3:0] rf_data,
(* syn_peri_port = 0 *) output wire pmod_spi_sck,
(* syn_peri_port = 0 *) output wire pmod_spi_csn,
(* syn_peri_port = 0 *) input wire [3:0] pmod_spi_dq_IN,
(* syn_peri_port = 0 *) output wire [3:0] pmod_spi_dq_OUT,
(* syn_peri_port = 0 *) output wire [3:0] pmod_spi_dq_OE,
(* syn_peri_port = 0 *) input wire pmod_irq,
(* syn_peri_port = 0 *) output wire max2771_spi_sclk,
(* syn_peri_port = 0 *) output wire max2771_spi_csn,
(* syn_peri_port = 0 *) input wire max2771_spi_sdata_IN,
(* syn_peri_port = 0 *) output wire max2771_spi_sdata_OUT,
(* syn_peri_port = 0 *) output wire max2771_spi_sdata_OE,
(* syn_peri_port = 0 *) output wire gps_pps,
(* syn_peri_port = 0 *) input wire jtag_CAPTURE,
(* syn_peri_port = 0 *) input wire jtag_DRCK,
(* syn_peri_port = 0 *) input wire jtag_RESET,
(* syn_peri_port = 0 *) input wire jtag_RUNTEST,
(* syn_peri_port = 0 *) input wire jtag_SEL,
(* syn_peri_port = 0 *) input wire jtag_SHIFT,
(* syn_peri_port = 0 *) input wire jtag_TCK,
(* syn_peri_port = 0 *) input wire jtag_TDI,
(* syn_peri_port = 0 *) input wire jtag_TMS,
(* syn_peri_port = 0 *) input wire jtag_UPDATE,
(* syn_peri_port = 0 *) output wire jtag_TDO,
(* syn_peri_port = 0 *) output wire [4:0] led
);
wire gps_clock_locked = gps_ref_pll_locked & gps_domain_pll_locked;
wire pll_ready_async = fabric_pll_locked & gps_clock_locked & reset_n;
(* async_reg = "true" *) reg [2:0] pll_ready_sync = 3'b000;
reg [15:0] reset_shift = 16'hffff;
always @(posedge fabric_clk) begin
pll_ready_sync <= {{pll_ready_sync[1:0], pll_ready_async}};
if (!pll_ready_sync[2])
reset_shift <= 16'hffff;
else
reset_shift <= {{reset_shift[14:0], 1'b0}};
end
wire fabric_reset = reset_shift[15];
reg [31:0] heartbeat = 32'd0;
always @(posedge fabric_clk) begin
if (fabric_reset)
heartbeat <= 32'd0;
else
heartbeat <= heartbeat + 32'd1;
end
{internal_decls}
wire soc_reset = fabric_reset | debug_ndreset;
rocket_bscan_dmi_tunnel u_rocket_dmi_tunnel (
.tck(jtag_TCK),
.reset(fabric_reset | jtag_RESET),
.bscan_capture(jtag_CAPTURE),
.bscan_sel(jtag_SEL),
.bscan_shift(jtag_SHIFT),
.bscan_tdi(jtag_TDI),
.bscan_update(jtag_UPDATE),
.bscan_tdo(jtag_TDO),
.dmi_clock(debug_clockeddmi_dmiClock),
.dmi_reset(debug_clockeddmi_dmiReset),
.dmi_req_ready(debug_clockeddmi_dmi_req_ready),
.dmi_req_valid(debug_clockeddmi_dmi_req_valid),
.dmi_req_addr(debug_clockeddmi_dmi_req_bits_addr),
.dmi_req_data(debug_clockeddmi_dmi_req_bits_data),
.dmi_req_op(debug_clockeddmi_dmi_req_bits_op),
.dmi_resp_ready(debug_clockeddmi_dmi_resp_ready),
.dmi_resp_valid(debug_clockeddmi_dmi_resp_valid),
.dmi_resp_data(debug_clockeddmi_dmi_resp_bits_data),
.dmi_resp_resp(debug_clockeddmi_dmi_resp_bits_resp)
);
assign pmod_spi_dq_OUT = pmod_spi_dq_o;
assign pmod_spi_dq_OE = pmod_spi_dq_oe;
assign max2771_spi_sdata_OUT = max2771_spi_sdata_o;
assign max2771_spi_sdata_OE = max2771_spi_sdata_oe;
assign flash_sck = spi_sck;
assign flash_csn = spi_csn;
assign flash_dq_OUT = spi_dq_o;
assign flash_dq_OE = spi_dq_oe;
assign uart_tx = uart_txd;
// The kit LEDs are active-low. Show heartbeat, PLL locks, reset release,
// and debug-module activity from LED13 down to LED9.
assign led = ~{{heartbeat[25], gps_clock_locked, fabric_pll_locked,
~fabric_reset, debug_dmactive}};
{chr(10).join(assigns)}
(* syn_preserve = 1 *)
{Path(args.rocket_top).stem} u_rocket (
{port_map}
);
// These hard-TAP outputs are intentionally unused by the DMI framing logic.
wire _unused_hard_signals = jtag_DRCK ^ jtag_RUNTEST ^ jtag_TMS ^
board_clk_100 ^ gps_ref_clk_gen ^
gps_domain_feedback;
endmodule
"""
output = Path(args.out)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(content)
if __name__ == "__main__":
main()