mirror of https://github.com/openXC7/prjxray.git
Minitest for different IOSTANDARDs
Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
parent
da33c29914
commit
e8b05c6b27
|
|
@ -0,0 +1,29 @@
|
|||
proc dump_iobs {file_name} {
|
||||
|
||||
set fp [open $file_name w]
|
||||
puts $fp "tile,site_name,site_type,clock_region,bank,is_bonded,is_clock,is_global_clock,is_vref"
|
||||
|
||||
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 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]
|
||||
|
||||
puts $fp "$tile,$site,$site_type,$clock_region,$bank,$is_bonded,$is_clock,$is_global_clock,$is_vref"
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
PART=xc7a50tfgg484-1
|
||||
VIVADO_PART=xc7a50tfgg484-1
|
||||
|
||||
BIT2FASM_ARGS= --part "$(XRAY_DIR)/database/artix7/$(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_*.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
|
||||
|
||||
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 ../$<
|
||||
|
|
@ -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). 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.
|
||||
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
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.
|
||||
"""
|
||||
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)
|
||||
|
||||
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 = [
|
||||
"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] = "".join(s)
|
||||
|
||||
# Write header
|
||||
line = [""] + 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()
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
import os
|
||||
import random
|
||||
import json
|
||||
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.
|
||||
"""
|
||||
iob_sites = defaultdict(lambda: [])
|
||||
|
||||
with open(file_name, "r") as fp:
|
||||
fp.readline()
|
||||
|
||||
for line in fp:
|
||||
|
||||
fields = line.split(",")
|
||||
if len(fields) != 9:
|
||||
continue
|
||||
|
||||
iob_sites[fields[3]].append(
|
||||
{
|
||||
"tile": fields[0],
|
||||
"name": fields[1],
|
||||
"type": fields[2],
|
||||
"bank": fields[4],
|
||||
"is_bonded": bool(int(fields[5])),
|
||||
"is_clock": bool(int(fields[6])),
|
||||
"is_global_clock": bool(int(fields[7])),
|
||||
"is_vref": bool(int(fields[8])),
|
||||
})
|
||||
|
||||
return iob_sites
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
IOBUF_NOT_ALLOWED = [
|
||||
'HSTL_I',
|
||||
'HSTL_I_18',
|
||||
'SSTL18_I',
|
||||
]
|
||||
|
||||
|
||||
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:
|
||||
for drive in DRIVES[iostandard]:
|
||||
for slew in SLEWS:
|
||||
yield {"iostandard": iostandard, "drive": drive, "slew": slew}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Main.
|
||||
"""
|
||||
|
||||
# Load IOB data
|
||||
iob_sites = load_iob_sites("iobs-{}.csv".format(os.getenv("VIVADO_PART")))
|
||||
|
||||
# Generate designs
|
||||
iosettings_gen = gen_iosettings()
|
||||
design_index = 0
|
||||
while True:
|
||||
|
||||
# Generate clock regions
|
||||
region_data = []
|
||||
for region in iob_sites:
|
||||
|
||||
# Get IO settings
|
||||
try:
|
||||
iosettings = next(iosettings_gen)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
# Get sites
|
||||
sites = [
|
||||
site["name"] for site in iob_sites[region] if site["is_bonded"]
|
||||
and not site["is_vref"] and "SING" not in site["tile"]
|
||||
]
|
||||
if not len(sites):
|
||||
continue
|
||||
|
||||
# Select 5 random sites (IBUF, IBUF, OBUF, OBUF, IOBUF)
|
||||
used_sites = random.sample(sites, 5)
|
||||
unused_sites = list(set(sites) - set(used_sites))
|
||||
|
||||
# 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],
|
||||
})
|
||||
|
||||
# No more
|
||||
if len(region_data) == 0:
|
||||
break
|
||||
|
||||
# Generate the design
|
||||
num_inp = len(region_data) * 2
|
||||
num_out = len(region_data) * 2
|
||||
num_ino = len(region_data)
|
||||
|
||||
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)
|
||||
|
||||
for i, data in enumerate(region_data):
|
||||
|
||||
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": 2 * i + 0,
|
||||
"inp_1": 2 * i + 1,
|
||||
"out_0": 2 * i + 0,
|
||||
"out_1": 2 * i + 1,
|
||||
"ino": i,
|
||||
"ibuf_param_str": ibuf_param_str,
|
||||
"obuf_param_str": obuf_param_str,
|
||||
}
|
||||
|
||||
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, LOC="{ibuf_0_loc}" *)
|
||||
IBUF # ({ibuf_param_str}) ibuf_0_{region} (
|
||||
.I(inp[{inp_0}]),
|
||||
.O(inp_0_{region})
|
||||
);
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="{ibuf_1_loc}" *)
|
||||
IBUF # ({ibuf_param_str}) ibuf_1_{region} (
|
||||
.I(inp[{inp_1}]),
|
||||
.O(inp_1_{region})
|
||||
);
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="{obuf_0_loc}" *)
|
||||
OBUF # ({obuf_param_str}) obuf_0_{region} (
|
||||
.I(out_0_{region}),
|
||||
.O(out[{out_0}])
|
||||
);
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="{obuf_1_loc}" *)
|
||||
OBUF # ({obuf_param_str}) obuf_1_{region} (
|
||||
.I(out_1_{region}),
|
||||
.O(out[{out_1}])
|
||||
);
|
||||
""".format(**keys)
|
||||
|
||||
if use_ino:
|
||||
verilog += """
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="{iobuf_loc}" *)
|
||||
IOBUF # ({obuf_param_str}) iobuf_{region} (
|
||||
.I(ino_i_{region}),
|
||||
.O(ino_o_{region}),
|
||||
.T(ino_t_{region}),
|
||||
.IO(ino[{ino}])
|
||||
);
|
||||
|
||||
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)
|
||||
|
||||
verilog += "endmodule"
|
||||
|
||||
# Write verilog
|
||||
fname = "design_{:03d}.v".format(design_index)
|
||||
with open(fname, "w") as fp:
|
||||
fp.write(verilog)
|
||||
|
||||
# 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["name"]: "{}.{}".format(s["tile"], type_to_loc[s["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()
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
import os
|
||||
import argparse
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
IOSettings = namedtuple("IOSettings", "iostandard, drive, slew")
|
||||
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def load_feature_data(fname):
|
||||
"""
|
||||
Load feature vs. IO settings correlation data from a CSV file.
|
||||
"""
|
||||
feature_data = {}
|
||||
|
||||
# Open the file
|
||||
with open(fname, "r") as fp:
|
||||
|
||||
# Header
|
||||
line = fp.readline()
|
||||
features = line.strip().split(",")[1:]
|
||||
|
||||
# Data
|
||||
for line in fp:
|
||||
fields = line.strip().split(",")
|
||||
|
||||
# IOSettings
|
||||
parts = fields[0].split(".")
|
||||
ios = IOSettings(
|
||||
parts[0],
|
||||
int(parts[1][1:]) if parts[1][1:] != "_FIXED" else None,
|
||||
parts[2])
|
||||
|
||||
# Feature activity
|
||||
feature_data[ios] = {
|
||||
f: fields[1 + i]
|
||||
for i, f in enumerate(features)
|
||||
}
|
||||
|
||||
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
|
||||
for feature in all_features:
|
||||
for iosettings in feature_data:
|
||||
if 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 = []
|
||||
|
||||
for feature, parameter in sorted(parameters):
|
||||
condition = []
|
||||
for iosettings in feature_data:
|
||||
|
||||
# Feature is set
|
||||
if iob_type in feature_data[iosettings][feature]:
|
||||
cond = "IOSTANDARD == \"{}\"".format(
|
||||
iosettings.iostandard.upper())
|
||||
|
||||
if 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"]:
|
||||
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()
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force ../$env(PROJECT_NAME).dcp
|
||||
write_bitstream -force ../$env(PROJECT_NAME).bit
|
||||
Loading…
Reference in New Issue