blob: aa1dcb7584139661528b1f6df76e6aebb3136e6b [file] [log] [blame] [edit]
#!/usr/bin/env python3
"""
The generator
"""
import argparse
# =============================================================================
PINOUT = {
"basys3-full":
{
"clock": "W5",
"led":
[
"U16",
"E19",
"U19",
"V19",
"W18",
"U15",
"U14",
"V14",
"V13",
"V3",
"W3",
"U3",
"P3",
"N3",
"P1",
"L1",
],
"single-ended":
[
# Basys3 JB 1-4, 7-10
"A14",
"A16",
"B15",
"B16",
"A15",
"A17",
"C15",
"C16",
# Basys3 JC 1-4, 7-10
"K17",
"M18",
"N17",
"P18",
"L17",
"M19",
"P17",
"R18"
],
"differential":
[
# Basys3 JB
("A14", "A15"),
("A16", "A17"),
("C15", "B15"),
("B16", "C16"),
# Basys3 JC
("M19", "M18"),
("K17", "L17"),
("N17", "P17"),
("P18", "R18"),
],
"iobanks": [16, 14],
},
"arty-full":
{
"clock":
"E3",
"led":
[
"G6", # R0
"G3", # R1
"J3", # R2
"K1", # R3
"F6", # G0
"J4", # G1
"J2", # G2
"H6", # G3
"E1", # B0
"G4", # B1
"H4", # B2
"K2", # B3
"H5", # LED4
"J5", # LED5
"T9", # LED6
"T10", # LED7
],
"single-ended":
[
# Pmod JB
(15, "E15"),
(15, "E16"),
(15, "D15"),
(15, "C15"),
(15, "J17"),
(15, "J18"),
(15, "K15"),
(15, "J15"),
# Pmod JC
(14, "U12"),
(14, "V12"),
(14, "V10"),
(14, "V11"),
(14, "U14"),
(14, "V14"),
(14, "T13"),
(14, "U13"),
],
"differential":
[
# Pmod JB
(15, "E15", "E16"),
(15, "D15", "C15"),
(15, "J17", "J18"),
(15, "K15", "J15"),
# Pmod JC
(15, "U12", "V12"),
(15, "V10", "V11"),
(15, "U14", "V14"),
(15, "T13", "U13"),
]
},
# Pinout for "bottom" routing graph of 50t, only for Basys3. These pins may
# not correspond to actual LEDs so the design may not be suitable for testing
# on hardware but it will pass all the checks on CI.
"basys3-bottom":
{
"clock": "W5", # Bank 34
"led":
[
"V3", # LED9
"W3", # LED10
"U3", # LED11
"W7", # CA
"W6", # CB
"U8", # CC
"V8", # CD
"U5", # CE
"V5", # CF
"U7", # CG
],
"single-ended":
[
# Basys3 JC 1-4, 7-10
"K17",
"M18",
"N17",
"P18",
"L17",
"M19",
"P17",
"R18",
"U15", # LEDs
"U16",
"V13",
"V14",
],
"differential":
[
# Basys3 JC
("M18", "M19"),
("L17", "K17"),
("P17", "N17"),
("R18", "P18"),
],
"iobanks": [14],
},
}
def unquote(s):
if isinstance(s, str):
return s.replace("\"", "")
return s
# =============================================================================
def generate_output(board, iostandard, drives, slews):
"""
Generates a design which outputs 100Hz square wave to a number of pins
in which each one has different DRIVE+SLEW setting. The IOSTANDARD is
common for all of them.
"""
num_ports = len(drives) * len(slews)
iosettings = {}
# Header
verilog = """
module top(
input wire clk,
output wire [{}:0] out
);
""".format(num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# 100Hz square wave generator
verilog += """
wire clk_bufg;
reg [31:0] cnt_ps;
reg tick;
BUFG bufg (.I(clk), .O(clk_bufg));
initial cnt_ps <= 0;
initial tick <= 0;
always @(posedge clk_bufg)
if (cnt_ps >= (100000000 / (2*100)) - 1) begin
cnt_ps <= 0;
tick <= !tick;
end else begin
cnt_ps <= cnt_ps + 1;
tick <= tick;
end
"""
# Output buffers
index = 0
for slew in slews:
for drive in drives:
params = {"IOSTANDARD": "\"{}\"".format(iostandard)}
if drive is not None:
params["DRIVE"] = int(drive)
if slew is not None:
params["SLEW"] = "\"{}\"".format(slew)
pin = PINOUT[board]["single-ended"][index][1]
verilog += """
OBUF # ({params}) obuf_{index} (
.I(tick),
.O(out[{index}])
);
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io out[{}] {}\n".format(index, pin)
else:
pcf += "set_io out {}\n".format(pin)
iosettings[pin] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
return verilog, pcf, "", iosettings
def generate_input(board, iostandard, in_terms, vref):
"""
Generates a design with singnals from external pins go through IBUFs and
registers to LEDs. Each IBUF has differen IN_TERM setting.
"""
num_ports = len(in_terms)
iosettings = {}
used_iobanks = set()
# Header
verilog = """
module top(
input wire clk,
input wire [{N}:0] inp,
output reg [{N}:0] led
);
initial led <= 0;
""".format(N=num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# BUFG
verilog += """
wire clk_bufg;
BUFG bufg (.I(clk), .O(clk_bufg));
"""
verilog += """
wire inp_b[{}:0];
""".format(len(in_terms) - 1)
# Input buffers + registers
index = 0
for in_term in in_terms:
params = {
"IOSTANDARD": "\"{}\"".format(iostandard),
}
if in_term is not None:
params["IN_TERM"] = "\"{}\"".format(in_term)
iobank, pin = PINOUT[board]["single-ended"][index]
used_iobanks.add(iobank)
verilog += """
IBUF # ({params}) ibuf_{index} (
.I(inp[{index}]),
.O(inp_b[{index}])
);
always @(posedge clk_bufg)
led[{index}] <= inp_b[{index}];
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io inp[{}] {}\n".format(index, pin)
pcf += "set_io led[{}] {}\n".format(
index, PINOUT[board]["led"][index]
)
else:
pcf += "set_io inp {}\n".format(pin)
pcf += "set_io led {}\n".format(PINOUT[board]["led"][index])
iosettings[pin] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
# VREF
tcl = ""
if vref is not None:
for iobank in used_iobanks:
tcl += "set_property INTERNAL_VREF {} [get_iobanks {}]\n".format(
vref, iobank
)
return verilog, pcf, tcl, iosettings
def generate_inout(board, iostandard, drives, slews, vref):
"""
Generates a design with INOUT buffers. Buffers cycle through states:
L,Z,H,Z with 100Hz frequency. During the Z state, IO pins are latched
and their state is presented on LEDs.
"""
num_ports = len(drives) * len(slews)
iosettings = {}
used_iobanks = set()
# Header
verilog = """
module top(
input wire clk,
inout wire [{N}:0] ino,
output reg [{N}:0] led
);
initial led <= 0;
wire [{N}:0] ino_i;
reg ino_o;
reg ino_t;
""".format(N=num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# Control signal generator, data sampler
verilog += """
wire clk_bufg;
reg [31:0] cnt_ps;
BUFG bufg (.I(clk), .O(clk_bufg));
initial cnt_ps <= 32'd0;
initial ino_o <= 1'b0;
initial ino_t <= 1'b1;
always @(posedge clk_bufg)
if (cnt_ps >= (100000000 / (2*100)) - 1) begin
cnt_ps <= 0;
ino_t <= !ino_t;
if (ino_t == 1'b1)
ino_o <= !ino_o;
end else begin
cnt_ps <= cnt_ps + 1;
ino_t <= ino_t;
ino_o <= ino_o;
end
always @(posedge clk_bufg)
if (ino_t == 1'b1)
led <= ino_i;
else
led <= led;
"""
# INOUT buffers
index = 0
for slew in slews:
for drive in drives:
params = {"IOSTANDARD": "\"{}\"".format(iostandard)}
if drive is not None:
params["DRIVE"] = int(drive)
if slew is not None:
params["SLEW"] = "\"{}\"".format(slew)
iobank, pin = PINOUT[board]["single-ended"][index]
used_iobanks.add(iobank)
verilog += """
IOBUF # ({params}) iobuf_{index} (
.I(ino_o),
.O(ino_i[{index}]),
.T(ino_t),
.IO(ino[{index}])
);
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io ino[{}] {}\n".format(index, pin)
pcf += "set_io led[{}] {}\n".format(
index, PINOUT[board]["led"][index]
)
else:
pcf += "set_io ino {}\n".format(pin)
pcf += "set_io led {}\n".format(PINOUT[board]["led"][index])
iosettings[pin] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
# VREF
tcl = ""
if vref is not None:
for iobank in used_iobanks:
tcl += "set_property INTERNAL_VREF {} [get_iobanks {}]\n".format(
vref, iobank
)
return verilog, pcf, tcl, iosettings
# =============================================================================
def generate_diff_output(board, iostandard, drives, slews):
"""
Generates a design which outputs 100Hz square wave to a number of pins
in which each one has different DRIVE+SLEW setting. The IOSTANDARD is
common for all of them.
"""
num_ports = len(drives) * len(slews)
iosettings = {}
# Header
verilog = """
module top(
input wire clk,
output wire [{N}:0] out_p,
output wire [{N}:0] out_n
);
""".format(N=num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# 100Hz square wave generator
verilog += """
wire clk_bufg;
reg [31:0] cnt_ps;
reg tick;
BUFG bufg (.I(clk), .O(clk_bufg));
initial cnt_ps <= 0;
initial tick <= 0;
always @(posedge clk_bufg)
if (cnt_ps >= (100000000 / (2*100)) - 1) begin
cnt_ps <= 0;
tick <= !tick;
end else begin
cnt_ps <= cnt_ps + 1;
tick <= tick;
end
"""
# Output buffers
index = 0
for slew in slews:
for drive in drives:
params = {"IOSTANDARD": "\"{}\"".format(iostandard)}
if drive is not None:
params["DRIVE"] = int(drive)
if slew is not None:
params["SLEW"] = "\"{}\"".format(slew)
iobank, *pins = PINOUT[board]["differential"][index]
verilog += """
OBUFDS # ({params}) obuf_{index} (
.I(tick),
.O(out_p[{index}]),
.OB(out_n[{index}])
);
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io out_p[{}] {}\n".format(index, pins[0])
pcf += "set_io out_n[{}] {}\n".format(index, pins[1])
else:
pcf += "set_io out_p {}\n".format(pins[0])
pcf += "set_io out_n {}\n".format(pins[1])
iosettings[pins[0]] = {k: unquote(v) for k, v in params.items()}
iosettings[pins[1]] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
return verilog, pcf, "", iosettings
def generate_diff_input(board, iostandard, in_terms, vref):
"""
Generates a design with singnals from external pins go through IBUFs and
registers to LEDs. Each IBUF has differen IN_TERM setting.
"""
num_ports = len(in_terms)
iosettings = {}
used_iobanks = set()
# Header
verilog = """
module top(
input wire clk,
input wire [{N}:0] inp_p,
input wire [{N}:0] inp_n,
output reg [{N}:0] led
);
initial led <= 0;
""".format(N=num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# BUFG
verilog += """
wire clk_bufg;
BUFG bufg (.I(clk), .O(clk_bufg));
"""
verilog += """
wire inp_b[{}:0];
""".format(len(in_terms) - 1)
# Input buffers + registers
index = 0
for in_term in in_terms:
params = {
"IOSTANDARD": "\"{}\"".format(iostandard),
}
if in_term is not None:
params["IN_TERM"] = "\"{}\"".format(in_term)
iobank, *pins = PINOUT[board]["differential"][index]
used_iobanks.add(iobank)
verilog += """
IBUFDS # ({params}) ibuf_{index} (
.I(inp_p[{index}]),
.IB(inp_n[{index}]),
.O(inp_b[{index}])
);
always @(posedge clk_bufg)
led[{index}] <= inp_b[{index}];
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io inp_p[{}] {}\n".format(index, pins[0])
pcf += "set_io inp_n[{}] {}\n".format(index, pins[1])
pcf += "set_io led[{}] {}\n".format(
index, PINOUT[board]["led"][index]
)
else:
pcf += "set_io inp_p {}\n".format(pins[0])
pcf += "set_io inp_n {}\n".format(pins[1])
pcf += "set_io led {}\n".format(PINOUT[board]["led"][index])
iosettings[pins[0]] = {k: unquote(v) for k, v in params.items()}
iosettings[pins[1]] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
# VREF
tcl = ""
if vref is not None:
for iobank in used_iobanks:
tcl += "set_property INTERNAL_VREF {} [get_iobanks {}]\n".format(
vref, iobank
)
return verilog, pcf, tcl, iosettings
def generate_diff_inout(board, iostandard, drives, slews, vref):
"""
Generates a design with INOUT buffers. Buffers cycle through states:
L,Z,H,Z with 100Hz frequency. During the Z state, IO pins are latched
and their state is presented on LEDs.
"""
num_ports = len(drives) * len(slews)
iosettings = {}
used_iobanks = set()
# Header
verilog = """
module top(
input wire clk,
inout wire [{N}:0] ino_p,
inout wire [{N}:0] ino_n,
output reg [{N}:0] led
);
initial led <= 0;
wire [{N}:0] ino_i;
reg ino_o;
reg ino_t;
""".format(N=num_ports - 1)
pcf = """
set_io clk {}
""".format(PINOUT[board]["clock"])
# Control signal generator, data sampler
verilog += """
wire clk_bufg;
reg [31:0] cnt_ps;
BUFG bufg (.I(clk), .O(clk_bufg));
initial cnt_ps <= 32'd0;
initial ino_o <= 1'b0;
initial ino_t <= 1'b1;
always @(posedge clk_bufg)
if (cnt_ps >= (100000000 / (2*100)) - 1) begin
cnt_ps <= 0;
ino_t <= !ino_t;
if (ino_t == 1'b1)
ino_o <= !ino_o;
end else begin
cnt_ps <= cnt_ps + 1;
ino_t <= ino_t;
ino_o <= ino_o;
end
always @(posedge clk_bufg)
if (ino_t == 1'b1)
led <= ino_i;
else
led <= led;
"""
# INOUT buffers
index = 0
for slew in slews:
for drive in drives:
params = {"IOSTANDARD": "\"{}\"".format(iostandard)}
if drive is not None:
params["DRIVE"] = int(drive)
if slew is not None:
params["SLEW"] = "\"{}\"".format(slew)
iobank, *pins = PINOUT[board]["differential"][index]
used_iobanks.add(iobank)
verilog += """
IOBUFDS # ({params}) iobuf_{index} (
.I(ino_o),
.O(ino_i[{index}]),
.T(ino_t),
.IO(ino_p[{index}]),
.IOB(ino_n[{index}])
);
""".format(
params=",".join(
[".{}({})".format(k, v) for k, v in params.items()]
),
index=index
)
if num_ports > 1:
pcf += "set_io ino_p[{}] {}\n".format(index, pins[0])
pcf += "set_io ino_n[{}] {}\n".format(index, pins[1])
pcf += "set_io led[{}] {}\n".format(
index, PINOUT[board]["led"][index]
)
else:
pcf += "set_io ino_p {}\n".format(pins[0])
pcf += "set_io ino_n {}\n".format(pins[1])
pcf += "set_io led {}\n".format(PINOUT[board]["led"][index])
iosettings[pins[0]] = {k: unquote(v) for k, v in params.items()}
iosettings[pins[1]] = {k: unquote(v) for k, v in params.items()}
index += 1
# Footer
verilog += """
endmodule
"""
tcl = ""
if vref is not None:
for iobank in PINOUT[board]["iobanks"]:
tcl += "set_property INTERNAL_VREF {} [get_iobanks {}]\n".format(
vref, iobank
)
return verilog, pcf, tcl, iosettings
# =============================================================================
def main():
# Parse arguments
parser = argparse.ArgumentParser()
parser.add_argument("--board", required=True, help="Board")
parser.add_argument("--mode", required=True, help="Generation mode")
parser.add_argument("--iostandard", required=True, help="IOSTANDARD")
parser.add_argument(
"--drive", required=False, nargs="+", default=[None], help="DRIVE(s)"
)
parser.add_argument(
"--slew", required=False, nargs="+", default=[None], help="SLEW(s)"
)
parser.add_argument("--vref", required=False, default=None, help="VREF")
parser.add_argument(
"--in_term",
required=False,
nargs="+",
default=[None],
help="IN_TERM(s)"
)
parser.add_argument("-o", required=True, help="Design name")
args = parser.parse_args()
# Generate design for output IO settings
if args.mode == "output":
verilog, pcf, tcl, iosettings = generate_output(
args.board, args.iostandard, args.drive, args.slew
)
elif args.mode == "input":
verilog, pcf, tcl, iosettings = generate_input(
args.board, args.iostandard, args.in_term, args.vref
)
elif args.mode == "inout":
verilog, pcf, tcl, iosettings = generate_inout(
args.board, args.iostandard, args.drive, args.slew, args.vref
)
elif args.mode == "diff_output":
verilog, pcf, tcl, iosettings = generate_diff_output(
args.board, args.iostandard, args.drive, args.slew
)
elif args.mode == "diff_input":
verilog, pcf, tcl, iosettings = generate_diff_input(
args.board, args.iostandard, args.in_term, args.vref
)
elif args.mode == "diff_inout":
verilog, pcf, tcl, iosettings = generate_diff_inout(
args.board, args.iostandard, args.drive, args.slew, args.vref
)
else:
raise RuntimeError("Unknown generation mode '{}'".format(args.mode))
# Write verilog
with open(args.o + ".v", "w") as fp:
fp.write(verilog)
# Write PCF
with open(args.o + ".pcf", "w") as fp:
fp.write(pcf)
# Write XDC
with open(args.o + ".xdc", "w") as fp:
fp.write(tcl)
if __name__ == "__main__":
main()