30179df8e6
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.
852 lines
23 KiB
Python
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()
|