| #!/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() |