Files
gps/targets/fpga/vcu118/tools/gen_vcu118_wrapper.py
T
onur 30179df8e6 feat(gps): add timer-based assisted reacquisition
Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
2026-08-04 02:58:11 +03:00

852 lines
23 KiB
Python

#!/usr/bin/env python3
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}")
left = int(match.group(1))
right = int(match.group(2))
return abs(left - right) + 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 = item_match.group(1)
name = item_match.group(2)
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 in {
"debug_clock",
"debug_clockeddmi_dmiClock",
}
def is_reset(name):
return name.endswith("_reset") or name in {
"debug_reset",
"debug_clockeddmi_dmiReset",
}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--rocket-top", required=True)
parser.add_argument("--out", required=True)
parser.add_argument(
"--rf-input-mode",
choices=["pmod", "generated", "ethernet"],
default="pmod",
help="GPS RF source: pmod uses external PMOD clock/data, generated uses an internal RF clock with zero data, ethernet enables the VCU118 Ethernet RF path.",
)
args = parser.parse_args()
ports = parse_ports(args.rocket_top)
port_by_name = {name: (direction, width) for direction, width, name in ports}
def decl(width, name):
return f" wire {width + ' ' if width else ''}{name};"
def connect(prefix, module_name, addr_width):
lines = [
f" {module_name} #(",
f" .ADDR_WIDTH({addr_width}),",
" .ID_WIDTH(4)",
f" ) u_{prefix}_slave (",
" .clk(fabric_clk),",
" .reset(fabric_reset),",
f" .aw_ready({prefix}_axi4_0_aw_ready),",
f" .aw_valid({prefix}_axi4_0_aw_valid),",
f" .aw_bits_id({prefix}_axi4_0_aw_bits_id),",
f" .aw_bits_addr({prefix}_axi4_0_aw_bits_addr),",
f" .aw_bits_len({prefix}_axi4_0_aw_bits_len),",
f" .aw_bits_size({prefix}_axi4_0_aw_bits_size),",
f" .aw_bits_burst({prefix}_axi4_0_aw_bits_burst),",
f" .w_ready({prefix}_axi4_0_w_ready),",
f" .w_valid({prefix}_axi4_0_w_valid),",
f" .w_bits_data({prefix}_axi4_0_w_bits_data),",
f" .w_bits_strb({prefix}_axi4_0_w_bits_strb),",
f" .w_bits_last({prefix}_axi4_0_w_bits_last),",
f" .b_ready({prefix}_axi4_0_b_ready),",
f" .b_valid({prefix}_axi4_0_b_valid),",
f" .b_bits_id({prefix}_axi4_0_b_bits_id),",
f" .b_bits_resp({prefix}_axi4_0_b_bits_resp),",
f" .ar_ready({prefix}_axi4_0_ar_ready),",
f" .ar_valid({prefix}_axi4_0_ar_valid),",
f" .ar_bits_id({prefix}_axi4_0_ar_bits_id),",
f" .ar_bits_addr({prefix}_axi4_0_ar_bits_addr),",
f" .ar_bits_len({prefix}_axi4_0_ar_bits_len),",
f" .ar_bits_size({prefix}_axi4_0_ar_bits_size),",
f" .ar_bits_burst({prefix}_axi4_0_ar_bits_burst),",
f" .r_ready({prefix}_axi4_0_r_ready),",
f" .r_valid({prefix}_axi4_0_r_valid),",
f" .r_bits_id({prefix}_axi4_0_r_bits_id),",
f" .r_bits_data({prefix}_axi4_0_r_bits_data),",
f" .r_bits_resp({prefix}_axi4_0_r_bits_resp),",
f" .r_bits_last({prefix}_axi4_0_r_bits_last)",
" );",
]
return "\n".join(lines)
def has_axi_slave(prefix):
required = [
f"{prefix}_axi4_0_aw_ready",
f"{prefix}_axi4_0_aw_valid",
f"{prefix}_axi4_0_w_ready",
f"{prefix}_axi4_0_w_valid",
f"{prefix}_axi4_0_b_ready",
f"{prefix}_axi4_0_b_valid",
f"{prefix}_axi4_0_ar_ready",
f"{prefix}_axi4_0_ar_valid",
f"{prefix}_axi4_0_r_ready",
f"{prefix}_axi4_0_r_valid",
]
return all(name in port_by_name for name in required)
top_passthrough_ports = {
name for name in ["pmod_spi_sck", "pmod_spi_csn", "pmod_irq", "gps_pps"]
if name in port_by_name
}
assigns = []
for direction, width, name in ports:
if direction != "input":
continue
if name in top_passthrough_ports:
continue
if name == "debug_systemjtag_reset":
value = "fabric_reset | bscan_reset"
elif name.startswith("debug_clockeddmi_dmi_req") or name == "debug_clockeddmi_dmi_resp_ready":
continue
elif name in {
"debug_clockeddmi_dmiClock",
"debug_clockeddmi_dmiReset",
}:
continue
elif is_clock(name):
value = "fabric_clk"
elif name == "debug_reset":
value = "fabric_reset"
elif name == "resetctrl_hartIsInReset_0":
value = "soc_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 == "gps_rf_clk":
value = "gps_rf_clk_selected"
elif name == "gps_rf_data":
value = "gps_rf_data_selected"
elif name == "gps_clock_ready":
value = "gps_clock_locked & gps_rf_ready"
elif name == "gps_afe_clk":
value = "gps_rf_clk_selected"
elif name == "debug_systemjtag_jtag_TCK":
value = "bscan_tck"
elif name == "debug_systemjtag_jtag_TMS":
value = "bscan_tms"
elif name == "debug_systemjtag_jtag_TDI":
value = "bscan_tdi"
elif name == "debug_systemjtag_mfr_id":
value = "11'd0"
elif name == "debug_systemjtag_part_number":
value = "16'd0"
elif name == "debug_systemjtag_version":
value = "4'd0"
elif name == "debug_clockeddmi_dmi_resp_ready":
value = "1'b1"
elif name == "debug_dmactiveAck":
value = "debug_dmactive"
elif name.startswith("debug_clockeddmi_dmi_req"):
value = const_for(width)
elif name.startswith("l2_frontend_bus_axi4_0_"):
if name.endswith("_b_ready") or name.endswith("_r_ready"):
value = "1'b1"
else:
value = const_for(width)
elif name == "interrupts":
value = "2'b00"
elif name == "uart_rxd":
value = "rs232_uart_rxd"
elif name == "spi_dq_i":
continue
elif name == "pmod_spi_dq_i":
continue
elif name.startswith("mem_axi4_0_") or name.startswith("mmio_axi4_0_"):
continue
else:
value = const_for(width)
assigns.append(f" assign {name} = {value};")
port_map = ",\n".join(f" .{name}({name})" for _, _, name in ports)
led_debug = []
for candidate in ["debug_ndreset", "debug_dmactive", "debug_clockeddmi_dmi_req_ready"]:
if candidate in port_by_name:
led_debug.append(candidate)
while len(led_debug) < 3:
led_debug.append("1'b0")
has_system_jtag = all(name in port_by_name for name in [
"debug_systemjtag_jtag_TCK",
"debug_systemjtag_jtag_TMS",
"debug_systemjtag_jtag_TDI",
"debug_systemjtag_jtag_TDO_data",
])
has_clocked_dmi = all(name in port_by_name for name in [
"debug_clockeddmi_dmi_req_ready",
"debug_clockeddmi_dmi_req_valid",
"debug_clockeddmi_dmi_req_bits_addr",
"debug_clockeddmi_dmi_req_bits_data",
"debug_clockeddmi_dmi_req_bits_op",
"debug_clockeddmi_dmi_resp_ready",
"debug_clockeddmi_dmi_resp_valid",
"debug_clockeddmi_dmi_resp_bits_data",
"debug_clockeddmi_dmi_resp_bits_resp",
"debug_clockeddmi_dmiClock",
"debug_clockeddmi_dmiReset",
])
bscan_block = ""
if has_clocked_dmi:
bscan_block = """\
wire bscan_capture;
wire bscan_drck;
wire bscan_reset;
wire bscan_runtest;
wire bscan_sel;
wire bscan_shift;
wire bscan_tck;
wire bscan_tdi;
wire bscan_tms;
wire bscan_tdo;
wire bscan_update;
BSCANE2 #(
.DISABLE_JTAG("FALSE"),
.JTAG_CHAIN(4)
) u_rocket_bscan (
.CAPTURE(bscan_capture),
.DRCK(bscan_drck),
.RESET(bscan_reset),
.RUNTEST(bscan_runtest),
.SEL(bscan_sel),
.SHIFT(bscan_shift),
.TCK(bscan_tck),
.TDI(bscan_tdi),
.TMS(bscan_tms),
.UPDATE(bscan_update),
.TDO(bscan_tdo)
);
rocket_bscan_dmi_tunnel u_rocket_bscan_dmi_tunnel (
.tck(bscan_tck),
.reset(fabric_reset | bscan_reset),
.bscan_capture(bscan_capture),
.bscan_sel(bscan_sel),
.bscan_shift(bscan_shift),
.bscan_tdi(bscan_tdi),
.bscan_update(bscan_update),
.bscan_tdo(bscan_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)
);
"""
elif has_system_jtag:
bscan_block = """\
wire bscan_capture;
wire bscan_drck;
wire bscan_reset;
wire bscan_runtest;
wire bscan_sel;
wire bscan_shift;
wire bscan_tck;
wire bscan_tdi;
wire bscan_tms;
wire bscan_update;
BSCANE2 #(
.DISABLE_JTAG("FALSE"),
.JTAG_CHAIN(1)
) u_rocket_bscan (
.CAPTURE(bscan_capture),
.DRCK(bscan_drck),
.RESET(bscan_reset),
.RUNTEST(bscan_runtest),
.SEL(bscan_sel),
.SHIFT(bscan_shift),
.TCK(bscan_tck),
.TDI(bscan_tdi),
.TMS(bscan_tms),
.UPDATE(bscan_update),
.TDO(debug_systemjtag_jtag_TDO_data)
);
"""
has_spi_flash = all(name in port_by_name for name in [
"spi_sck",
"spi_csn",
"spi_dq_i",
"spi_dq_o",
"spi_dq_oe",
])
startup_block = ""
if has_spi_flash:
startup_block = """\
wire [3:0] cfg_flash_dq_i;
wire cfg_flash_eos;
assign spi_dq_i = cfg_flash_dq_i;
STARTUPE3 #(
.PROG_USR("FALSE"),
.SIM_CCLK_FREQ(0.0)
) u_cfg_flash_startup (
.CFGCLK(),
.CFGMCLK(),
.DI(cfg_flash_dq_i),
.EOS(cfg_flash_eos),
.PREQ(),
.DO(spi_dq_o),
.DTS(~spi_dq_oe),
.FCSBO(spi_csn),
.FCSBTS(1'b0),
.GSR(1'b0),
.GTS(1'b0),
.KEYCLEARB(1'b1),
.PACK(1'b0),
.USRCCLKO(spi_sck),
.USRCCLKTS(1'b0),
.USRDONEO(1'b1),
.USRDONETS(1'b1)
);
"""
has_pmod_spi = all(name in port_by_name for name in [
"pmod_spi_sck",
"pmod_spi_csn",
"pmod_spi_dq_i",
"pmod_spi_dq_o",
"pmod_spi_dq_oe",
])
pmod_spi_top_ports = ""
pmod_spi_block = ""
if has_pmod_spi:
pmod_spi_top_ports = """\
output wire pmod_spi_sck,
output wire pmod_spi_csn,
inout wire [3:0] pmod_spi_dq,
"""
pmod_spi_block = """\
assign pmod_spi_dq_i = pmod_spi_dq;
assign pmod_spi_dq[0] = pmod_spi_dq_oe[0] ? pmod_spi_dq_o[0] : 1'bz;
assign pmod_spi_dq[1] = pmod_spi_dq_oe[1] ? pmod_spi_dq_o[1] : 1'bz;
assign pmod_spi_dq[2] = pmod_spi_dq_oe[2] ? pmod_spi_dq_o[2] : 1'bz;
assign pmod_spi_dq[3] = pmod_spi_dq_oe[3] ? pmod_spi_dq_o[3] : 1'bz;
"""
pmod_irq_top_ports = ""
if "pmod_irq" in port_by_name:
pmod_irq_top_ports = """\
input wire pmod_irq,
"""
pps_top_ports = ""
if port_by_name.get("gps_pps") == ("output", ""):
pps_top_ports = """\
output wire gps_pps,
"""
rf_top_ports = ""
eth_top_ports = ""
rf_extra_wires = ""
if args.rf_input_mode == "pmod":
rf_top_ports = """\
// External RF PMOD input. VCU118 PMOD connector side is 3.3 V through a level shifter.
input wire rf_clk_pmod,
input wire [3:0] rf_data_pmod,
"""
rf_clock_block = """\
wire rf_clk_pmod_ibuf;
// PMOD mode removes the Ethernet RF path and clocks GPS RF from the external sampler.
IBUF u_rf_clk_pmod_ibuf (
.I(rf_clk_pmod),
.O(rf_clk_pmod_ibuf)
);
BUFG u_rf_clk_pmod_buf (
.I(rf_clk_pmod_ibuf),
.O(gps_rf_clk_selected)
);
assign gps_rf_data_selected = rf_data_pmod;
assign gps_rf_ready = 1'b1;
"""
eth_block = ""
led_prefix = "rf_data_pmod"
else:
rf_extra_wires = """\
wire gps_rf_clk_gen;
wire gps_rf_clkfb;
wire gps_rf_clkfb_buf;
wire pll_gps_rf_exact_clk;
wire gps_rf_pll_locked;
"""
rf_clock_block = """\
// Internal RF clock used by generated and Ethernet RF modes.
MMCME4_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(42.625),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(3.333),
.CLKOUT0_DIVIDE_F(78.125),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(10),
.STARTUP_WAIT("FALSE")
) u_gps_rf_mmcm (
.CLKIN1(sysclk_300),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(gps_rf_clkfb_buf),
.CLKFBOUT(gps_rf_clkfb),
.CLKFBOUTB(),
.CLKOUT0(pll_gps_rf_exact_clk),
.CLKOUT0B(),
.CLKOUT1(),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.DADDR(7'd0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'd0),
.DWE(1'b0),
.DO(),
.DRDY(),
.LOCKED(gps_rf_pll_locked),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE(),
.PWRDWN(1'b0),
.RST(cpu_reset),
.CDDCDONE(),
.CDDCREQ(1'b0)
);
BUFG u_gps_rf_feedback_buf (
.I(gps_rf_clkfb),
.O(gps_rf_clkfb_buf)
);
BUFG u_gps_rf_clk_buf (
.I(pll_gps_rf_exact_clk),
.O(gps_rf_clk_gen)
);
assign gps_rf_clk_selected = gps_rf_clk_gen;
assign gps_rf_ready = gps_rf_pll_locked;
"""
if args.rf_input_mode == "ethernet":
eth_top_ports = """\
input wire phy_sgmii_rx_p,
input wire phy_sgmii_rx_n,
output wire phy_sgmii_tx_p,
output wire phy_sgmii_tx_n,
input wire phy_sgmii_clk_p,
input wire phy_sgmii_clk_n,
output wire phy_reset_n,
input wire phy_int_n,
inout wire phy_mdio,
output wire phy_mdc,
"""
eth_block = """\
wire [3:0] ethernet_rf_data;
wire [7:0] ethernet_rf_status;
// Ethernet RF mode keeps the VCU118 PCS/PMA path for the host RF streamer.
eth_rf_vcu118 u_ethernet_rf (
.mgmt_clk(fabric_clk),
.mgmt_rst(fabric_reset),
.rf_clk(gps_rf_clk_gen),
.rf_rst(fabric_reset),
.phy_sgmii_rx_p(phy_sgmii_rx_p),
.phy_sgmii_rx_n(phy_sgmii_rx_n),
.phy_sgmii_tx_p(phy_sgmii_tx_p),
.phy_sgmii_tx_n(phy_sgmii_tx_n),
.phy_sgmii_clk_p(phy_sgmii_clk_p),
.phy_sgmii_clk_n(phy_sgmii_clk_n),
.phy_reset_n(phy_reset_n),
.phy_int_n(phy_int_n),
.phy_mdio(phy_mdio),
.phy_mdc(phy_mdc),
.gps_rf_data(ethernet_rf_data),
.status(ethernet_rf_status)
);
assign gps_rf_data_selected = ethernet_rf_data;
"""
led_prefix = "ethernet_rf_status[7:4]"
else:
eth_block = """\
assign gps_rf_data_selected = 4'd0;
"""
led_prefix = "{heartbeat[25], heartbeat[24], 2'b00}"
content = f"""`timescale 1ns/1ps
module vcu118_rocket_top (
input wire sysclk1_300_p,
input wire sysclk1_300_n,
input wire cpu_reset,
input wire rs232_uart_rxd,
output wire rs232_uart_txd,
{rf_top_ports}{eth_top_ports}{pmod_spi_top_ports}{pmod_irq_top_ports}{pps_top_ports}\
output wire [7:0] led
);
wire sysclk_300;
wire fabric_clk;
wire gps_acq_clk_gen;
wire gps_trck_clk_gen;
wire gps_rf_clk_selected;
wire [3:0] gps_rf_data_selected;
wire gps_rf_ready;
wire fabric_clkfb;
wire fabric_clkfb_buf;
wire gps_ref_clkfb;
wire gps_ref_clkfb_buf;
wire gps_ref_clk_gen;
wire pll_gps_ref_clk;
wire gps_ref_pll_locked;
wire gps_clkfb;
wire gps_clkfb_buf;
wire pll_fabric_clk;
wire pll_gps_acq_clk;
wire pll_gps_trck_clk;
wire fabric_pll_locked;
wire gps_domain_pll_locked;
wire gps_clock_locked = gps_ref_pll_locked & gps_domain_pll_locked;
{rf_extra_wires}
IBUFDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("FALSE")
) u_sysclk_ibufds (
.I(sysclk1_300_p),
.IB(sysclk1_300_n),
.O(sysclk_300)
);
MMCME4_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(5.000),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(3.333),
.CLKOUT0_DIVIDE_F(91.625),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.CLKOUT1_DIVIDE(15),
.CLKOUT1_PHASE(0.000),
.CLKOUT1_DUTY_CYCLE(0.500),
.CLKOUT2_DIVIDE(12),
.CLKOUT2_PHASE(0.000),
.CLKOUT2_DUTY_CYCLE(0.500),
.CLKOUT3_DIVIDE(15),
.CLKOUT3_PHASE(0.000),
.CLKOUT3_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.STARTUP_WAIT("FALSE")
) u_clock_mmcm (
.CLKIN1(sysclk_300),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(fabric_clkfb_buf),
.CLKFBOUT(fabric_clkfb),
.CLKFBOUTB(),
.CLKOUT0(),
.CLKOUT0B(),
.CLKOUT1(),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(pll_fabric_clk),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.DADDR(7'd0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'd0),
.DWE(1'b0),
.DO(),
.DRDY(),
.LOCKED(fabric_pll_locked),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE(),
.PWRDWN(1'b0),
.RST(cpu_reset),
.CDDCDONE(),
.CDDCREQ(1'b0)
);
BUFG u_pll_feedback_buf (
.I(fabric_clkfb),
.O(fabric_clkfb_buf)
);
BUFG u_fabric_clk_buf (
.I(pll_fabric_clk),
.O(fabric_clk)
);
{rf_clock_block}
// GPS processing clocks are synthesized from the RF sample clock, not the
// board crystal. Two MMCM stages keep 100/125 MHz within 0.01 ppm of nominal
// for the 16.367981 MHz sampler while leaving the Rocket fabric independent.
MMCME4_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(58.125),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(61.095),
.CLKOUT0_DIVIDE_F(17.125),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.STARTUP_WAIT("FALSE")
) u_gps_ref_mmcm (
.CLKIN1(gps_rf_clk_selected),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(gps_ref_clkfb_buf),
.CLKFBOUT(gps_ref_clkfb),
.CLKFBOUTB(),
.CLKOUT0(pll_gps_ref_clk),
.CLKOUT0B(),
.CLKOUT1(),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.DADDR(7'd0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'd0),
.DWE(1'b0),
.DO(),
.DRDY(),
.LOCKED(gps_ref_pll_locked),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE(),
.PWRDWN(1'b0),
.RST(cpu_reset),
.CDDCDONE(),
.CDDCREQ(1'b0)
);
BUFG u_gps_ref_feedback_buf (
.I(gps_ref_clkfb),
.O(gps_ref_clkfb_buf)
);
BUFG u_gps_ref_clk_buf (
.I(pll_gps_ref_clk),
.O(gps_ref_clk_gen)
);
MMCME4_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(18.000),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(18.000),
.CLKOUT0_DIVIDE_F(10.000),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.CLKOUT1_DIVIDE(8),
.CLKOUT1_PHASE(0.000),
.CLKOUT1_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.STARTUP_WAIT("FALSE")
) u_gps_domain_mmcm (
.CLKIN1(gps_ref_clk_gen),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(gps_clkfb_buf),
.CLKFBOUT(gps_clkfb),
.CLKFBOUTB(),
.CLKOUT0(pll_gps_acq_clk),
.CLKOUT0B(),
.CLKOUT1(pll_gps_trck_clk),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.DADDR(7'd0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'd0),
.DWE(1'b0),
.DO(),
.DRDY(),
.LOCKED(gps_domain_pll_locked),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE(),
.PWRDWN(1'b0),
.RST(cpu_reset | ~gps_ref_pll_locked),
.CDDCDONE(),
.CDDCREQ(1'b0)
);
BUFG u_gps_domain_feedback_buf (
.I(gps_clkfb),
.O(gps_clkfb_buf)
);
BUFG u_gps_acq_clk_buf (
.I(pll_gps_acq_clk),
.O(gps_acq_clk_gen)
);
BUFG u_gps_trck_clk_buf (
.I(pll_gps_trck_clk),
.O(gps_trck_clk_gen)
);
// The Rocket/fabric clock and RAM cache must survive loss of the external RF
// clock. gps_clock_ready independently resets only the GPS DSP domains.
wire pll_ready_async = fabric_pll_locked & ~cpu_reset;
(* 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]) begin
reset_shift <= 16'hFFFF;
end else begin
reset_shift <= {{reset_shift[14:0], 1'b0}};
end
end
wire fabric_reset = reset_shift[15];
reg [31:0] heartbeat;
always @(posedge fabric_clk) begin
if (fabric_reset) begin
heartbeat <= 32'd0;
end else begin
heartbeat <= heartbeat + 32'd1;
end
end
{chr(10).join(decl(width, name) for _, width, name in ports if name not in top_passthrough_ports)}
wire soc_reset = {"fabric_reset | debug_ndreset" if "debug_ndreset" in port_by_name else "fabric_reset"};
{bscan_block}
{startup_block}
{pmod_spi_block}
{eth_block}
{chr(10).join(assigns)}
assign led = {{
{led_prefix},
fabric_reset,
{led_debug[2]},
{led_debug[1]},
{led_debug[0]}
}};
assign rs232_uart_txd = {"uart_txd" if "uart_txd" in port_by_name else "1'b1"};
{connect("mem", "axi4_ram_64", 32) if has_axi_slave("mem") else ""}
{connect("mmio", "axi4_slave_tieoff_64", 31) if has_axi_slave("mmio") else ""}
(* dont_touch = "true", keep_hierarchy = "yes" *)
{Path(args.rocket_top).stem} u_rocket (
{port_map}
);
endmodule
"""
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
Path(args.out).write_text(content)
if __name__ == "__main__":
main()