Merge pull request #1157 from antmicro/iostandard_minitest

Minitest for different IOSTANDARDs
This commit is contained in:
litghost 2019-12-09 11:09:33 -08:00 committed by GitHub
commit 0e8ff9b64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1042 additions and 0 deletions

View File

@ -0,0 +1,31 @@
proc dump_iobs {file_name} {
set fp [open $file_name w]
puts $fp "tile,site_name,site_type,clock_region,bank,pkg_pin,is_bonded,is_clock,is_global_clock,is_vref,pin_func"
foreach tile [get_tiles *IOB33*] {
foreach site [get_sites -of_objects $tile] {
set site_type [get_property SITE_TYPE $site]
set clock_region [get_property CLOCK_REGION $site]
set is_bonded [get_property IS_BONDED $site]
set pin [get_package_pins -of_objects $site]
set bank [get_property BANK $pin]
set pkg_pin [get_property NAME $pin]
set is_clock [get_property IS_CLK_CAPABLE $pin]
set is_global_clock [get_property IS_GLOBAL_CLK $pin]
set is_vref [get_property IS_VREF $pin]
set pin_func [get_property PIN_FUNC $pin]
puts $fp "$tile,$site,$site_type,$clock_region,$bank,$pkg_pin,$is_bonded,$is_clock,$is_global_clock,$is_vref,$pin_func"
}
}
close $fp
}
create_project -force -in_memory -name dump_iobs -part $::env(VIVADO_PART)
set_property design_mode PinPlanning [current_fileset]
open_io_design -name io_1
dump_iobs "iobs-$::env(VIVADO_PART).csv"

View File

@ -0,0 +1,76 @@
PART?=xc7a50tfgg484-1
VIVADO_PART?=$(PART)
BIT2FASM_ARGS= --part "$(XRAY_DIR)/database/$(XRAY_DATABASE)/$(PART)" --verbose
DESIGN_FILES=$(wildcard design*.v)
CSV_FILES=$(subst .v,.csv,$(DESIGN_FILES))
FASM_FILES=$(subst .v,.fasm,$(DESIGN_FILES))
.PHONY: all clean designs analysis
.PRECIOUS: %.bit %.fasm %.csv
all: analysis
clean:
@rm -rf design_*.v
@rm -rf design_*.tcl
@rm -rf design_*.json
@rm -rf design_*.csv
@rm -rf design_*.fasm
@rm -rf design_*.bit
@rm -rf design_*.dcp
@rm -rf designs.ok
@rm -rf *.log
@rm -rf *.jou
@rm -rf *.xml
@for f in build-*; \
do \
rm -rf $$f; \
done
@rm -rf .Xil
@rm -rf snippets
@rm -rf features.csv
@rm -rf results.json
@rm -rf unknown_bits.jl
@rm -rf iobs-$(VIVADO_PART).csv
iobs-$(VIVADO_PART).csv: ../dump_iobs.tcl
env VIVADO_PART=$(VIVADO_PART) $(XRAY_VIVADO) -mode batch -source ../dump_iobs.tcl -nojournal -log dump_iobs.log
designs.ok: iobs-$(VIVADO_PART).csv generate.py
env VIVADO_PART=$(VIVADO_PART) python3 ./generate.py
touch designs.ok
designs: designs.ok
%.bit: %.v designs.ok ../syn+par.tcl
mkdir -p build-$(basename $@)
cd build-$(basename $@) && env PROJECT_NAME=$(basename $@) VIVADO_PART=${VIVADO_PART} $(XRAY_VIVADO) -mode batch -source ../../syn+par.tcl -nojournal -log ../$@.log
rm -rf *.backup.log
%.fasm: %.bit
$(XRAY_BIT2FASM) $(BIT2FASM_ARGS) $< > $@
@sort -u -o $@ $@
%.csv: %.fasm
python3 ./analyze.py --fasm $< --design $(basename $@).json -o $@
analysis: features.csv unknown_bits.jl
features.csv: $(CSV_FILES) designs.ok
@head -n 1 $(word 1,$(CSV_FILES)) >features.csv
@rm -rf features.csv.tmp
@for f in $(CSV_FILES); \
do \
tail -n +2 $$f >>features.csv.tmp; \
done
@sort features.csv.tmp >>features.csv
@rm -rf features.csv.tmp
unknown_bits.jl: $(FASM_FILES) designs.ok
@cat $(FASM_FILES) | grep unknown_bit | cut -d: -f 2 | sort -u >unknown_bits.jl
snippets: features.csv
mkdir -p snippets
cd snippets && python3 ../snippets.py ../$<

View File

@ -0,0 +1,41 @@
# IOSTANDARD feature correlation minitest
This test checks which fasm features present in the db are set for given IO settings (IOSTANDARD + DRIVE + SLEW) and for which IOB type (IBUF, OBUF, IOBUF, IBUFDS, OBUFDS, IOBUFDS). It also checks what features are set on unused IOBs of a given bank in which active IOBs are instantiated. To avoid conflicts, a single iosettings combination is used within an IO bank.
## Running the minitest
1. Dump IOBs and generate verilog designs
`
make designs
`
2. (can skip this and go to 3) Synthesize designs, decode bitstreams to fasm files, correlate with database features
`
make analysis
`
3. Generate verilog and XML snippets to be used in cell definition, techmap and architecture definition
`
make snippets
`
## How it works
At first IOB information is dumped from Vivado. Relevant IOB attributes are: IO bank, is it a Vref input, is the IOB bonded.
Next a set of design is generated. Each design contains two IBUFs, two OBUFs and one IOBUF (if IOSTANDARD allows it) per bank.
These designs are synthesized and their bitstreams are disassembled to fasms.
Each fasm is compared agains the prjxray db to see what features are activated for given IO settings. Results of that checks are stored in CSV files which are then concatenated to one named "features.csv". Unknown bits are reported and stored in the "unknown_bits.jl" file.
The "features.csv" consists of one row per IO settings and one column per fasm feature. Each "cell" contains a string identifying for which types of IOB a given feature was active:
- "I" input
- "O" output
- "T" inout
- "U" active in unused IOBs of the same bank
Finally during code snippet generation the "feature.csv" file is read back by a python script and code snippets for cell definition, cell techmap and XML architecture definition are generated. The goal is to automatically generate mappings between parameters of Vivado's IOB cells to VPR IOB cells.

View File

@ -0,0 +1,184 @@
"""
This script performs the analysis of disassembled bitstream and design information.
It correlates presence/absence of particular fasm features in the bitstream
with presence/absence of a particular IOB type. It also checks for features
that are set for unused IOBs that are in the same bank as the used ones.
The list of available fasm features is read from the prjxray database. The
analysis result is written to a CSV file.
"""
import os
import argparse
import json
import random
from collections import defaultdict
from prjxray import util
import fasm
# =============================================================================
def load_iob_segbits():
"""
Loads IOB segbits from the database to get fasm feature list.
This function assumes:
- LIOB33/RIOB33 have the same features.
- IOB_Y0 and IOB_Y1 have the same features (at least those of interest).
"""
features = []
fname = os.path.join(util.get_db_root(), "segbits_liob33.db")
with open(fname, "r") as fp:
for line in fp:
line = line.strip()
if len(line) == 0:
continue
# Parse the line
fields = line.split(maxsplit=1)
feature = fields[0]
bits = fields[1]
# Parse the feature
parts = feature.split(".", maxsplit=3)
if len(parts) >= 3 and parts[1] == "IOB_Y0":
features.append(".".join(parts[2:]))
return features
def correlate_features(features, tile, site, set_features):
"""
Correlate each feature with the fasm disassembly for given tile/site.
Given a set of all possible fasm features (for an IOB) in the first
argument, checks whether they are set or cleared for the given tile+site in
a design. The parameter set_features contains fasm dissassembly of the
design.
Returns a list of tuples with feature names and whether they are set or not.
"""
result = []
for feature in features:
full_feature = "{}.{}.{}".format(tile, site, feature)
if full_feature in set_features:
result.append((
feature,
True,
))
else:
result.append((
feature,
False,
))
return result
def run():
"""
Main.
"""
# Parse arguments
parser = argparse.ArgumentParser()
parser.add_argument(
"--design", type=str, required=True, help="Design JSON file")
parser.add_argument(
"--fasm", type=str, required=True, help="Decoded fasm file")
parser.add_argument(
"-o", type=str, default="results.csv", help="Output CSV file")
parser.add_argument("-j", type=str, default=None, help="Output JSON file")
args = parser.parse_args()
# Load IOB features
features = load_iob_segbits()
# Load the design data
with open(args.design, "r") as fp:
design = json.load(fp)
# Load disassembled fasm
fasm_tuples = fasm.parse_fasm_filename(args.fasm)
set_features = fasm.fasm_tuple_to_string(fasm_tuples).split("\n")
# Correlate features for given IOB types
results = []
for region in design:
result = dict(region["iosettings"])
for l in ["input", "output", "inout", "unused_sites"]:
# TODO: Check if this is true eg. for all unused sites, not just
# one random site.
tile, site = random.choice(region[l]).split(".")
matches = correlate_features(features, tile, site, set_features)
result[l] = matches
results.append(result)
# Save results
if args.j:
with open(args.j, "w") as fp:
json.dump(results, fp, indent=2, sort_keys=True)
# Save results to CSV
with open(args.o, "w") as fp:
csv_data = defaultdict(lambda: {})
# Collect data
for result in results:
iostandard = result["iostandard"]
drive = result["drive"]
slew = result["slew"]
if drive is None:
drive = "_FIXED"
iosettings = "{}.I{}.{}".format(iostandard, drive, slew)
is_diff = "DIFF" in iostandard
for feature in sorted(features):
I = [f[1] for f in result["input"] if f[0] == feature and f[1]]
O = [
f[1] for f in result["output"] if f[0] == feature and f[1]
]
T = [f[1] for f in result["inout"] if f[0] == feature and f[1]]
U = [
f[1]
for f in result["unused_sites"]
if f[0] == feature and f[1]
]
s = "".join(
[
"I" if len(I) > 0 else "",
"O" if len(O) > 0 else "",
"T" if len(T) > 0 else "",
"U" if len(U) > 0 else "",
])
csv_data[iosettings][feature] = s
# Write header
line = ["iosettings"] + sorted(features)
fp.write(",".join(line) + "\n")
# Write data
for iosettings in sorted(csv_data.keys()):
data = csv_data[iosettings]
line = [iosettings
] + [data[feature] for feature in sorted(features)]
fp.write(",".join(line) + "\n")
if __name__ == "__main__":
run()

View File

@ -0,0 +1,483 @@
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)
print(data)
return iob_sites
# =============================================================================
IOBUF_NOT_ALLOWED = [
'HSTL_I',
'HSTL_I_18',
'SSTL18_I',
]
DIFF_MAP = {
'SSTL135': 'DIFF_SSTL135',
}
def gen_iosettings():
"""
A generator function which yields all possible IO settings combintions.
"""
IOSTANDARDS = (
'LVCMOS12',
'LVCMOS15',
'LVCMOS18',
'LVCMOS25',
'LVCMOS33',
'LVTTL',
'SSTL135',
# Those are available but not currently fuzzed.
# 'SSTL135_R',
# 'SSTL15',
# '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("VIVADO_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:
num_inp = 0
num_out = 0
num_ino = 0
# Generate clock regions
region_data = []
for region in sorted(list(iob_sites.keys())):
# 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,
"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)
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
# 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()

View File

@ -0,0 +1,212 @@
"""
This script generates code snippets for cell definition, technology mapping and
VPR architecture definition. It reads correlation of IOB fasm features with
IO standard settings such as IOSTANDARD, DRIVE and SLEW and generates:
- Verilog model parameter definitions that match fasm features.
- Xilinx's IOB parameter translation to those features.
- Assignment of model parameters to the actual fasm features for VPR arch def.
Generated code snippets need to be manually copied to relevant places in the
target code / arch. def.
"""
import os
import argparse
import csv
from collections import namedtuple
IOSettings = namedtuple("IOSettings", "iostandard, drive, slew, is_diff")
# =============================================================================
def load_feature_data(fname):
"""
Load feature vs. IO settings correlation data from a CSV file.
"""
# Load the data from CSV
feature_data = {}
with open(fname, "r") as fp:
for line in csv.DictReader(fp):
# Get IOSettings
ios_parts = line["iosettings"].split(".")
ios = IOSettings(
ios_parts[0],
int(ios_parts[1][1:]) if ios_parts[1][1:] != "_FIXED" else
None, ios_parts[2], "DIFF_" in ios_parts[0])
# Feature activity
feature_data[ios] = {k: line[k] for k in list(line.keys())[1:]}
return feature_data
def filter_feature_data(feature_data):
"""
Filters fasm features and leaves only those related to IO settings.
"""
for data in feature_data.values():
for feature in dict(data):
if "DISABLE" in feature:
del data[feature]
if "PULLTYPE" in feature:
del data[feature]
if "IN_TERM" in feature:
del data[feature]
if "LOW_PWR" in feature:
del data[feature]
return feature_data
def get_relevant_parameters(feature_data, iob_type):
"""
Gets features relevant to a specific IOB type and assign them verilog
parameter names.
"""
# Get all features and make parameter names
all_features = set(
[f for data in feature_data.values() for f in data.keys()])
parameters = []
# Check if a feature is relevant to particular IOB type
is_diff = "DIFF_" in iob_type
for feature in all_features:
for iosettings in feature_data:
if iosettings.is_diff == is_diff:
base_iob_type = iob_type.replace("DIFF_", "")
if base_iob_type in feature_data[iosettings][feature]:
parameters.append((
feature,
feature.replace(".", "_"),
))
break
return parameters
# =============================================================================
def generate_parameter_defs(parameters):
"""
Generates verilog parameter definitions for the IOB cell model.
"""
verilog = []
for feature, parameter in sorted(parameters):
verilog.append("parameter [0:0] {} = 1'b0;".format(parameter))
return "\n".join(verilog)
def generate_parameter_map(parameters):
"""
Generates mapping of parameters for fasm features for architecture
definition.
"""
xml = []
xml.append("<meta name=\"fasm_params\">")
for feature, parameter in sorted(parameters):
xml.append(" {} = {}".format(feature, parameter))
xml.append("</meta>")
return "\n".join(xml)
def generate_parameter_assignments(parameters, feature_data, iob_type):
"""
Generates parameter assignmets to be used inside the techmap. For
each IOB cell parameter a set of conditions is generated based on
feature activity observation.
"""
verilog = []
is_diff = "DIFF_" in iob_type
base_iob_type = iob_type.replace("DIFF_", "")
for feature, parameter in sorted(parameters):
condition = []
for iosettings in feature_data:
if iosettings.is_diff != is_diff:
continue
# Feature is set
if base_iob_type in feature_data[iosettings][feature]:
cond = "IOSTANDARD == \"{}\"".format(
iosettings.iostandard.upper())
if base_iob_type in ["O", "T"]:
if iosettings.drive is not None and "DRIVE" in feature:
cond += " && DRIVE == {}".format(iosettings.drive)
if "SLEW" in feature:
cond += " && SLEW == \"{}\"".format(iosettings.slew)
condition.append(cond)
condition = sorted(list(set(condition)))
condition_str = " || \n".join([" ({})".format(c) for c in condition])
verilog.append(".{}(\n{}\n)".format(parameter, condition_str))
return ",\n".join(verilog)
# =============================================================================
def run():
"""
Main.
"""
# Parse arguments
parser = argparse.ArgumentParser()
parser.add_argument("features", type=str, help="Input CSV file")
args = parser.parse_args()
# Load iosettings vs. feature map
feature_data = load_feature_data(args.features)
# Reject non-iostandard related features
feature_data = filter_feature_data(feature_data)
# Make parameters
parameters = {}
for iob_type in ["I", "O", "T", "DIFF_I", "DIFF_O", "DIFF_T"]:
parameters[iob_type] = get_relevant_parameters(feature_data, iob_type)
# Parameter definition
with open("cells_sim.v", "w") as fp:
for iob_type, params in parameters.items():
fp.write("// {} {}\n\n".format(iob_type, "=" * 70))
verilog = generate_parameter_defs(params)
fp.write(verilog + "\n\n")
# Parameter assignments
with open("cells_map.v", "w") as fp:
for iob_type, params in parameters.items():
fp.write("// {} {}\n\n".format(iob_type, "=" * 70))
verilog = generate_parameter_assignments(
params, feature_data, iob_type)
fp.write(verilog + "\n\n")
# Parameter assignments
with open("arch.xml", "w") as fp:
for iob_type, params in parameters.items():
fp.write("<!-- {} -->\n\n".format(iob_type))
xml = generate_parameter_map(params)
fp.write(xml + "\n\n")
if __name__ == "__main__":
run()

View File

@ -0,0 +1,15 @@
create_project -force -name $env(PROJECT_NAME) -part $env(VIVADO_PART)
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
read_verilog ../$env(PROJECT_NAME).v
synth_design -top top
source ../$env(PROJECT_NAME).tcl
place_design
route_design
write_checkpoint -force ../$env(PROJECT_NAME).dcp
write_bitstream -force ../$env(PROJECT_NAME).bit