mirror of https://github.com/openXC7/prjxray.git
513 lines
15 KiB
Python
513 lines
15 KiB
Python
#!/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()
|