Files
gps/targets/fpga/genesys2/tools/gen_genesys2_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

842 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 decl(width, name):
return f" wire {width + ' ' if width else ''}{name};"
def axi_slave(prefix, module_name, addr_width):
return "\n".join([
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)",
" );",
])
def has_axi_slave(prefix, port_by_name):
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)
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 keeps the old internal RF clock with zero data, ethernet enables the Genesys2 RGMII RF streamer path.",
)
args = parser.parse_args()
ports = parse_ports(args.rocket_top)
port_by_name = {name: (direction, width) for direction, width, name in ports}
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 = "uart_tx_in"
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}({'pmod_irq_sync[2]' if name == 'pmod_irq' else 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_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),
// Keep the transport TAP alive while the fabric is in reset. Otherwise
// OpenOCD cannot examine the target to diagnose a PLL/reset-ready failure.
.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)
);
"""
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 = """\
assign spi_dq_i = qspi_d;
assign qspi_d[0] = spi_dq_oe[0] ? spi_dq_o[0] : 1'bz;
assign qspi_d[1] = spi_dq_oe[1] ? spi_dq_o[1] : 1'bz;
assign qspi_d[2] = spi_dq_oe[2] ? spi_dq_o[2] : 1'bz;
assign qspi_d[3] = spi_dq_oe[3] ? spi_dq_o[3] : 1'bz;
assign qspi_csn = spi_csn;
STARTUPE2 #(
.PROG_USR("FALSE"),
.SIM_CCLK_FREQ(0.0)
) u_cfg_flash_startup (
.CFGCLK(),
.CFGMCLK(),
.EOS(),
.PREQ(),
.CLK(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 = ""
pmod_irq_block = ""
if "pmod_irq" in port_by_name:
pmod_irq_top_ports = """\
input wire pmod_irq,
"""
pmod_irq_block = """\
// JB1 is asynchronous to the 50 MHz Rocket fabric. Keep this as three
// physical flip-flops: ASYNC_REG guides placement for metastability MTBF,
// and SHREG_EXTRACT prevents Vivado from collapsing stages into an SRL.
(* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *)
reg [2:0] pmod_irq_sync = 3'b000;
always @(posedge fabric_clk) begin
if (fabric_reset) begin
pmod_irq_sync <= 3'b000;
end else begin
pmod_irq_sync <= {pmod_irq_sync[1:0], pmod_irq};
end
end
"""
pps_top_ports = ""
if port_by_name.get("gps_pps") == ("output", ""):
pps_top_ports = """\
output wire gps_pps,
"""
rocket_module = Path(args.rocket_top).stem
rf_top_ports = ""
eth_top_ports = ""
rf_extra_wires = ""
eth_block = ""
led_prefix = "heartbeat[25], heartbeat[24], ~cpu_resetn, fabric_reset"
if args.rf_input_mode == "pmod":
rf_top_ports = """\
// External RF PMOD input. The connector side is 3.3 V; XDC sets the FPGA I/O standard.
input wire rf_clk_pmod,
input wire [3:0] rf_data_pmod,
"""
rf_clock_block = """\
wire rf_clk_pmod_ibuf;
// PMOD mode feeds the GPS RF domain directly 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;
"""
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 = """\
// Generated mode is useful when no RF PMOD source is connected.
MMCME2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(17.125),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(5.000),
.CLKIN2_PERIOD(0.000),
.CLKOUT0_DIVIDE_F(69.750),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(3),
.REF_JITTER1(0.010),
.REF_JITTER2(0.010),
.STARTUP_WAIT("FALSE")
) u_gps_rf_mmcm (
.CLKIN1(sysclk_200),
.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(board_reset)
);
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 eth_rxck,
input wire [3:0] eth_rxd,
input wire eth_rxctl,
output wire eth_txck,
output wire [3:0] eth_txd,
output wire eth_txctl,
output wire eth_phyrst_n,
input wire eth_int_b,
input wire eth_pme_b,
inout wire eth_mdio,
output wire eth_mdc,
"""
eth_block = """\
wire [3:0] ethernet_rf_data;
wire [7:0] ethernet_rf_status;
// Genesys2 Ethernet mode uses the board RTL8211E-VL RGMII PHY.
eth_rf_genesys2 u_ethernet_rf (
.mgmt_clk(fabric_clk),
.mgmt_rst(fabric_reset),
.rf_clk(gps_rf_clk_gen),
.rf_rst(fabric_reset),
.eth_rxck(eth_rxck),
.eth_rxd(eth_rxd),
.eth_rxctl(eth_rxctl),
.eth_txck(eth_txck),
.eth_txd(eth_txd),
.eth_txctl(eth_txctl),
.eth_phyrst_n(eth_phyrst_n),
.eth_int_b(eth_int_b),
.eth_pme_b(eth_pme_b),
.eth_mdio(eth_mdio),
.eth_mdc(eth_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], ethernet_rf_status[6], ethernet_rf_status[5], ethernet_rf_status[4]"
else:
eth_block = """\
assign gps_rf_data_selected = 4'd0;
"""
content = f"""`timescale 1ns/1ps
module genesys2_rocket_top (
input wire sysclk_p,
input wire sysclk_n,
input wire cpu_resetn,
// Digilent labels these from the PC/FTDI side: TX_IN is FPGA RX, RX_OUT is FPGA TX.
input wire uart_tx_in,
output wire uart_rx_out,
{rf_top_ports}{eth_top_ports}{pmod_spi_top_ports}{pmod_irq_top_ports}{pps_top_ports}\
inout wire [3:0] qspi_d,
output wire qspi_csn,
output wire [7:0] led
);
wire sysclk_200;
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_gps_acq_clk;
wire pll_fabric_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;
wire board_reset = ~cpu_resetn;
{rf_extra_wires}
IBUFDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("FALSE")
) u_sysclk_ibufds (
.I(sysclk_p),
.IB(sysclk_n),
.O(sysclk_200)
);
MMCME2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(5.000),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(5.000),
.CLKIN2_PERIOD(0.000),
.CLKOUT0_DIVIDE_F(12.500),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.CLKOUT1_DIVIDE(20),
.CLKOUT1_PHASE(0.000),
.CLKOUT1_DUTY_CYCLE(0.500),
.CLKOUT2_DIVIDE(8),
.CLKOUT2_PHASE(0.000),
.CLKOUT2_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.REF_JITTER1(0.010),
.REF_JITTER2(0.010),
.STARTUP_WAIT("FALSE")
) u_clock_mmcm (
.CLKIN1(sysclk_200),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(fabric_clkfb_buf),
.CLKFBOUT(fabric_clkfb),
.CLKFBOUTB(),
.CLKOUT0(),
.CLKOUT0B(),
.CLKOUT1(pll_fabric_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(fabric_pll_locked),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE(),
.PWRDWN(1'b0),
.RST(board_reset)
);
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 80/125 MHz within 0.01 ppm of nominal
// for the 16.367981 MHz sampler while leaving the Rocket fabric independent.
MMCME2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(58.125),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(61.095),
.CLKIN2_PERIOD(0.000),
.CLKOUT0_DIVIDE_F(17.125),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.REF_JITTER1(0.010),
.REF_JITTER2(0.010),
.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(board_reset)
);
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)
);
MMCME2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(18.000),
.CLKFBOUT_PHASE(0.000),
.CLKIN1_PERIOD(18.000),
.CLKIN2_PERIOD(0.000),
.CLKOUT0_DIVIDE_F(12.500),
.CLKOUT0_PHASE(0.000),
.CLKOUT0_DUTY_CYCLE(0.500),
.CLKOUT1_DIVIDE(8),
.CLKOUT1_PHASE(0.000),
.CLKOUT1_DUTY_CYCLE(0.500),
.DIVCLK_DIVIDE(1),
.REF_JITTER1(0.010),
.REF_JITTER2(0.010),
.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(board_reset | ~gps_ref_pll_locked)
);
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_resetn;
(* 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
{eth_block}
{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}
{pmod_irq_block}
{chr(10).join(assigns)}
assign led = {{
{led_prefix},
{led_debug[2]},
{led_debug[1]},
{led_debug[0]},
uart_txd
}};
assign uart_rx_out = {"uart_txd" if "uart_txd" in port_by_name else "1'b1"};
{axi_slave("mem", "axi4_ram_64", 32) if has_axi_slave("mem", port_by_name) else ""}
{axi_slave("mmio", "axi4_slave_tieoff_64", 31) if has_axi_slave("mmio", port_by_name) else ""}
(* dont_touch = "true", keep_hierarchy = "yes" *)
{rocket_module} 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()