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