| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 2017-2020 The Project X-Ray Authors. |
| # |
| # Use of this source code is governed by a ISC-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/ISC |
| # |
| # SPDX-License-Identifier: ISC |
| import os |
| import random |
| import json |
| import csv |
| from collections import defaultdict |
| |
| try: |
| random.seed(int(os.getenv("SEED"), 16)) |
| except TypeError: |
| pass |
| |
| # ============================================================================= |
| |
| |
| def load_iob_sites(file_name): |
| """ |
| Loads IOB site dump from the given CSV file. |
| """ |
| |
| # Load the data |
| with open(file_name, "r") as fp: |
| data = [row for row in csv.DictReader(fp)] |
| |
| # Index IOB site data by clock regions |
| iob_sites = defaultdict(lambda: []) |
| for site_data in data: |
| iob_sites[site_data["clock_region"]].append(site_data) |
| |
| return iob_sites |
| |
| |
| # ============================================================================= |
| |
| IOBUF_NOT_ALLOWED = [ |
| 'HSTL_I', |
| 'HSTL_I_18', |
| 'SSTL18_I', |
| ] |
| |
| DIFF_MAP = { |
| 'SSTL135': 'DIFF_SSTL135', |
| 'SSTL15': 'DIFF_SSTL15', |
| } |
| |
| VREF_ALLOWED = [ |
| 'SSTL135', |
| 'SSTL15', |
| ] |
| |
| |
| def gen_iosettings(): |
| """ |
| A generator function which yields all possible IO settings combintions. |
| """ |
| |
| IOSTANDARDS = ( |
| 'LVCMOS12', |
| 'LVCMOS15', |
| 'LVCMOS18', |
| 'LVCMOS25', |
| 'LVCMOS33', |
| 'LVTTL', |
| 'SSTL135', |
| 'SSTL15', |
| |
| # Those are available but not currently fuzzed. |
| # 'SSTL135_R', |
| # 'SSTL15_R', |
| # 'SSTL18_I', |
| # 'SSTL18_II', |
| # 'HSTL_I', |
| # 'HSTL_I_18', |
| # 'HSTL_II', |
| # 'HSTL_II_18', |
| # 'HSUL_12', |
| # 'MOBILE_DDR', |
| ) |
| |
| DRIVES = defaultdict(lambda: [None]) |
| DRIVES.update( |
| { |
| "LVTTL": [4, 8, 12, 16, 24], |
| "LVCMOS12": [4, 8, 12], |
| "LVCMOS15": [4, 8, 12, 16], |
| "LVCMOS18": [4, 8, 12, 16, 24], |
| "LVCMOS25": [4, 8, 12, 16], |
| "LVCMOS33": [4, 8, 12, 16], |
| }) |
| |
| SLEWS = ("SLOW", "FAST") |
| |
| for iostandard in IOSTANDARDS: |
| |
| # Single ended |
| for drive in DRIVES[iostandard]: |
| for slew in SLEWS: |
| yield {"iostandard": iostandard, "drive": drive, "slew": slew} |
| |
| # Differential |
| if iostandard in DIFF_MAP: |
| for drive in DRIVES[iostandard]: |
| for slew in SLEWS: |
| yield { |
| "iostandard": DIFF_MAP[iostandard], |
| "drive": drive, |
| "slew": slew |
| } |
| |
| |
| # ============================================================================= |
| |
| |
| def run(): |
| """ |
| Main. |
| """ |
| |
| # Load IOB data |
| iob_sites = load_iob_sites("iobs-{}.csv".format(os.getenv("PART"))) |
| |
| # Generate IOB site to package pin map and *M site to *S site map. |
| site_to_pkg_pin = {} |
| master_to_slave = {} |
| |
| for region, sites in iob_sites.items(): |
| tiles = defaultdict(lambda: {}) |
| |
| for site in sites: |
| site_to_pkg_pin[site["site_name"]] = site["pkg_pin"] |
| if site["site_type"] == "IOB33M": |
| tiles[site["tile"]]["M"] = site |
| if site["site_type"] == "IOB33S": |
| tiles[site["tile"]]["S"] = site |
| |
| for sites in tiles.values(): |
| master_to_slave[sites["M"]["site_name"]] = sites["S"]["site_name"] |
| |
| # Generate designs |
| iosettings_gen = gen_iosettings() |
| design_index = 0 |
| while True: |
| |
| print("Design #{}".format(design_index)) |
| |
| num_inp = 0 |
| num_out = 0 |
| num_ino = 0 |
| |
| # Generate clock regions |
| region_data = [] |
| for region in sorted(list(iob_sites.keys())): |
| |
| # Get IO bank. All sites from a clock region have the same one. |
| bank = iob_sites[region][0]["bank"] |
| |
| # Get IO settings |
| try: |
| iosettings = next(iosettings_gen) |
| except StopIteration: |
| break |
| |
| # Get sites |
| sites = [ |
| ( |
| site["site_name"], |
| site["site_type"], |
| ) |
| for site in iob_sites[region] |
| if site["is_bonded"] and not int(site["is_vref"]) and "SING" |
| not in site["tile"] and not "PUDC_B" in site["pin_func"] |
| ] |
| if not len(sites): |
| continue |
| |
| # Differential / single ended |
| if "DIFF" in iosettings["iostandard"]: |
| |
| # Select 5 random sites (IBUFDS, IBUFDS, OBUFDS, OBUFDS, IOBUFDS) |
| site_names = [s[0] for s in sites if s[1] == "IOB33M"] |
| used_sites = random.sample(site_names, 5) |
| unused_sites = list(set(site_names) - set(used_sites)) |
| |
| num_inp += 4 |
| num_out += 4 |
| num_ino += 2 |
| |
| else: |
| # Select 5 random sites (IBUF, IBUF, OBUF, OBUF, IOBUF) |
| site_names = [s[0] for s in sites] |
| used_sites = random.sample(site_names, 5) |
| unused_sites = list(set(site_names) - set(used_sites)) |
| |
| num_inp += 2 |
| num_out += 2 |
| num_ino += 1 |
| |
| # Store data |
| region_data.append( |
| { |
| "region": region, |
| "bank": bank, |
| "iosettings": iosettings, |
| "unused_sites": unused_sites, |
| "input": used_sites[0:2], |
| "output": used_sites[2:4], |
| "inout": used_sites[4:5], |
| }) |
| print("", region, iosettings) |
| |
| # No more |
| if len(region_data) == 0: |
| break |
| |
| print("----") |
| |
| # Generate the design |
| verilog = """ |
| module top ( |
| input wire [{num_inp}:0] inp, |
| inout wire [{num_ino}:0] ino, |
| output wire [{num_out}:0] out |
| ); |
| """.format(num_inp=num_inp - 1, num_ino=num_ino - 1, num_out=num_out - 1) |
| |
| tcl = "" |
| |
| inp_idx = 0 |
| out_idx = 0 |
| ino_idx = 0 |
| |
| for i, data in enumerate(region_data): |
| |
| is_diff = "DIFF" in data["iosettings"]["iostandard"] |
| use_ino = data["iosettings"]["iostandard"] not in IOBUF_NOT_ALLOWED |
| |
| iostandard = data["iosettings"]["iostandard"] |
| drive = data["iosettings"]["drive"] |
| slew = data["iosettings"]["slew"] |
| |
| ibuf_param_str = ".IOSTANDARD(\"{}\")".format(iostandard) |
| obuf_param_str = str(ibuf_param_str) |
| |
| if drive is not None: |
| obuf_param_str += ", .DRIVE({})".format(drive) |
| if slew is not None: |
| obuf_param_str += ", .SLEW(\"{}\")".format(slew) |
| |
| bank = data["bank"] |
| vref = "0.75" # FIXME: Maybe loop over VREFs too ? |
| |
| keys = { |
| "region": data["region"], |
| "ibuf_0_loc": data["input"][0], |
| "ibuf_1_loc": data["input"][1], |
| "obuf_0_loc": data["output"][0], |
| "obuf_1_loc": data["output"][1], |
| "iobuf_loc": data["inout"][0], |
| "inp_0_p": inp_idx, |
| "inp_0_n": inp_idx + 2, |
| "inp_1_p": inp_idx + 1, |
| "inp_1_n": inp_idx + 3, |
| "out_0_p": out_idx, |
| "out_0_n": out_idx + 2, |
| "out_1_p": out_idx + 1, |
| "out_1_n": out_idx + 3, |
| "ino_p": ino_idx, |
| "ino_n": ino_idx + 1, |
| "ibuf_param_str": ibuf_param_str, |
| "obuf_param_str": obuf_param_str, |
| } |
| |
| if is_diff: |
| inp_idx += 4 |
| out_idx += 4 |
| ino_idx += 2 |
| else: |
| inp_idx += 2 |
| out_idx += 2 |
| ino_idx += 1 |
| |
| # Set VREF if necessary |
| if iostandard in VREF_ALLOWED: |
| tcl += "set_property INTERNAL_VREF {} [get_iobanks {}]\n".format( |
| vref, bank) |
| |
| # Single ended |
| if not is_diff: |
| |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[keys["ibuf_0_loc"]], keys["inp_0_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[keys["ibuf_1_loc"]], keys["inp_1_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[keys["obuf_0_loc"]], keys["inp_0_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[keys["obuf_1_loc"]], keys["inp_1_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports ino[{}]]\n".format( |
| site_to_pkg_pin[keys["iobuf_loc"]], keys["ino_p"]) |
| |
| verilog += """ |
| |
| // {region} |
| wire inp_0_{region}; |
| wire inp_1_{region}; |
| wire out_0_{region}; |
| wire out_1_{region}; |
| |
| wire ino_i_{region}; |
| wire ino_o_{region}; |
| wire ino_t_{region}; |
| """.format(**keys) |
| |
| verilog += """ |
| (* KEEP, DONT_TOUCH *) |
| IBUF # ({ibuf_param_str}) ibuf_0_{region} ( |
| .I(inp[{inp_0_p}]), |
| .O(inp_0_{region}) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| IBUF # ({ibuf_param_str}) ibuf_1_{region} ( |
| .I(inp[{inp_1_p}]), |
| .O(inp_1_{region}) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| OBUF # ({obuf_param_str}) obuf_0_{region} ( |
| .I(out_0_{region}), |
| .O(out[{out_0_p}]) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| OBUF # ({obuf_param_str}) obuf_1_{region} ( |
| .I(out_1_{region}), |
| .O(out[{out_1_p}]) |
| ); |
| """.format(**keys) |
| |
| if use_ino: |
| verilog += """ |
| |
| (* KEEP, DONT_TOUCH *) |
| IOBUF # ({obuf_param_str}) iobuf_{region} ( |
| .I(ino_i_{region}), |
| .O(ino_o_{region}), |
| .T(ino_t_{region}), |
| .IO(ino[{ino_p}]) |
| ); |
| |
| assign out_0_{region} = inp_0_{region}; |
| assign out_1_{region} = ino_o_{region}; |
| assign ino_i_{region} = inp_0_{region}; |
| assign ino_t_{region} = inp_1_{region}; |
| """.format(**keys) |
| |
| else: |
| verilog += """ |
| |
| assign out_0_{region} = inp_0_{region}; |
| assign out_1_{region} = inp_1_{region}; |
| assign ino[{ino}] = 1'b0; |
| """.format(**keys) |
| |
| # Differential |
| else: |
| |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[keys["ibuf_0_loc"]], keys["inp_0_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[master_to_slave[keys["ibuf_0_loc"]]], |
| keys["inp_0_n"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[keys["ibuf_1_loc"]], keys["inp_1_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports inp[{}]]\n".format( |
| site_to_pkg_pin[master_to_slave[keys["ibuf_1_loc"]]], |
| keys["inp_1_n"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[keys["obuf_0_loc"]], keys["inp_0_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[master_to_slave[keys["obuf_0_loc"]]], |
| keys["inp_0_n"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[keys["obuf_1_loc"]], keys["inp_1_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports out[{}]]\n".format( |
| site_to_pkg_pin[master_to_slave[keys["obuf_1_loc"]]], |
| keys["inp_1_n"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports ino[{}]]\n".format( |
| site_to_pkg_pin[keys["iobuf_loc"]], keys["ino_p"]) |
| tcl += "set_property PACKAGE_PIN {} [get_ports ino[{}]]\n".format( |
| site_to_pkg_pin[master_to_slave[keys["iobuf_loc"]]], |
| keys["ino_n"]) |
| |
| verilog += """ |
| |
| // {region} |
| wire inp_0_{region}; |
| wire inp_1_{region}; |
| wire out_0_{region}; |
| wire out_1_{region}; |
| |
| wire ino_i_{region}; |
| wire ino_o_{region}; |
| wire ino_t_{region}; |
| """.format(**keys) |
| |
| verilog += """ |
| (* KEEP, DONT_TOUCH *) |
| IBUFDS # ({ibuf_param_str}) ibufds_0_{region} ( |
| .I(inp[{inp_0_p}]), |
| .IB(inp[{inp_0_n}]), |
| .O(inp_0_{region}) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| IBUFDS # ({ibuf_param_str}) ibufds_1_{region} ( |
| .I(inp[{inp_1_p}]), |
| .IB(inp[{inp_1_n}]), |
| .O(inp_1_{region}) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| OBUFDS # ({obuf_param_str}) obufds_0_{region} ( |
| .I(out_0_{region}), |
| .O(out[{out_0_p}]), |
| .OB(out[{out_0_n}]) |
| ); |
| |
| (* KEEP, DONT_TOUCH *) |
| OBUFDS # ({obuf_param_str}) obufds_1_{region} ( |
| .I(out_1_{region}), |
| .O(out[{out_1_p}]), |
| .OB(out[{out_1_n}]) |
| ); |
| """.format(**keys) |
| |
| if use_ino: |
| verilog += """ |
| |
| (* KEEP, DONT_TOUCH *) |
| IOBUFDS # ({obuf_param_str}) iobufds_{region} ( |
| .I(ino_i_{region}), |
| .O(ino_o_{region}), |
| .T(ino_t_{region}), |
| .IO(ino[{ino_p}]), |
| .IOB(ino[{ino_n}]) |
| ); |
| |
| assign out_0_{region} = inp_0_{region}; |
| assign out_1_{region} = ino_o_{region}; |
| assign ino_i_{region} = inp_0_{region}; |
| assign ino_t_{region} = inp_1_{region}; |
| """.format(**keys) |
| |
| else: |
| verilog += """ |
| |
| assign out_0_{region} = inp_0_{region}; |
| assign out_1_{region} = inp_1_{region}; |
| assign ino[{ino_p}] = 1'b0; |
| assign ino[{ino_n}] = 1'b0; |
| """.format(**keys) |
| |
| verilog += "endmodule" |
| |
| # Write verilog |
| fname = "design_{:03d}.v".format(design_index) |
| with open(fname, "w") as fp: |
| fp.write(verilog) |
| |
| # Write TCL |
| fname = "design_{:03d}.tcl".format(design_index) |
| with open(fname, "w") as fp: |
| fp.write(tcl) |
| |
| # Write JSON |
| fname = "design_{:03d}.json".format(design_index) |
| |
| # Convert Vivado site names to fasm-style TILE.SITE names. |
| for data in region_data: |
| type_to_loc = { |
| "IOB33": "IOB_Y0", |
| "IOB33M": "IOB_Y0", |
| "IOB33S": "IOB_Y1" |
| } |
| site_to_loc = { |
| s["site_name"]: "{}.{}".format( |
| s["tile"], type_to_loc[s["site_type"]]) |
| for s in iob_sites[data["region"]] |
| } |
| |
| for l in ["input", "output", "inout", "unused_sites"]: |
| data[l] = [site_to_loc[s] for s in data[l]] |
| |
| # Write design settings to JSON |
| with open(fname, "w") as fp: |
| json.dump(region_data, fp, sort_keys=True, indent=1) |
| |
| design_index += 1 |
| |
| |
| if __name__ == "__main__": |
| run() |