blob: ff708cf9e4be78347ecac1bba485410a29d91887 [file] [log] [blame]
#!/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()