From 5d140296d5948acd4884885085fee46be81379cb Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Mon, 9 Dec 2019 17:15:39 +0100 Subject: [PATCH 1/5] A simple TCL + python for PS7 cell definition extraction. Signed-off-by: Maciej Kurc --- minitests/ps7/xtra/Makefile | 16 +++ minitests/ps7/xtra/README.md | 3 + minitests/ps7/xtra/dump_ps7.tcl | 18 +++ minitests/ps7/xtra/make_cell.py | 192 ++++++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 minitests/ps7/xtra/Makefile create mode 100644 minitests/ps7/xtra/README.md create mode 100644 minitests/ps7/xtra/dump_ps7.tcl create mode 100644 minitests/ps7/xtra/make_cell.py diff --git a/minitests/ps7/xtra/Makefile b/minitests/ps7/xtra/Makefile new file mode 100644 index 00000000..fc98bc23 --- /dev/null +++ b/minitests/ps7/xtra/Makefile @@ -0,0 +1,16 @@ +.PHONY: all clean + +all: ps7.v + +clean: + rm -rf ps7.v + rm -rf ps7.csv + rm -rf *.xml + rm -rf *.log + rm -rf .Xil + +ps7.csv: dump_ps7.tcl + $(XRAY_VIVADO) -mode batch -source dump_ps7.tcl -nojournal -log $(basename $@).log + +ps7.v: ps7.csv make_cell.py + python3 make_cell.py $< diff --git a/minitests/ps7/xtra/README.md b/minitests/ps7/xtra/README.md new file mode 100644 index 00000000..d44b22a1 --- /dev/null +++ b/minitests/ps7/xtra/README.md @@ -0,0 +1,3 @@ +# PS7 verilog cell definition extractor + +Extracts all pins of the PS7 bel from Vivado, groups them into buses, removes those that are not connected (TEST*, DEBUG*) and writes the PS7 verilog cell definition. diff --git a/minitests/ps7/xtra/dump_ps7.tcl b/minitests/ps7/xtra/dump_ps7.tcl new file mode 100644 index 00000000..ffb23c03 --- /dev/null +++ b/minitests/ps7/xtra/dump_ps7.tcl @@ -0,0 +1,18 @@ +create_project -force -in_memory -name design -part xc7z020clg400-1 +set_property design_mode PinPlanning [current_fileset] +open_io_design -name io_1 + +set fp [open ps7.csv w] +puts $fp "name,is_input,is_output" + +set pins [get_bel_pins -of_objects [get_bels -of_objects [get_sites PS7* -of_objects [get_tiles PSS*]]]] +foreach pin $pins { + + set pin_name [lindex [split $pin "/"] 2] + set is_input [get_property IS_INPUT $pin] + set is_output [get_property IS_OUTPUT $pin] + + puts $fp "$pin_name,$is_input,$is_output" +} + +close $fp diff --git a/minitests/ps7/xtra/make_cell.py b/minitests/ps7/xtra/make_cell.py new file mode 100644 index 00000000..60d98b4b --- /dev/null +++ b/minitests/ps7/xtra/make_cell.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +import argparse +import csv +import re + +from collections import defaultdict + +# ============================================================================= + + +def main(): + + BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") + + # Parse arguments + parser = argparse.ArgumentParser() + parser.add_argument("csv", type=str, help="PS7 pin dump file") + args = parser.parse_args() + + # Load pin dump + with open(args.csv, "r") as fp: + pin_dump = list(csv.DictReader(fp)) + + # Group pins into buses + buses = defaultdict(lambda :{ + "direction": None, + "min": None, + "max": None, + "width": 0 + }) + + for pin in list(pin_dump): + + # Get bus name and signal index + match = BUS_REGEX.match(pin["name"]) + if match: + name = match.group(1) + idx = int(match.group(2)) + else: + name = pin["name"] + idx = 0 + + # Get direction + if int(pin["is_input"]): + direction = "input" + if int(pin["is_output"]): + direction = "output" + + # Add to bus + bus = buses[name] + + if bus["direction"] is None: + bus["direction"] = direction + else: + assert bus["direction"] == direction + + if bus["min"] is None: + bus["min"] = idx + else: + bus["min"] = min(bus["min"], idx) + + if bus["max"] is None: + bus["max"] = idx + else: + bus["max"] = max(bus["max"], idx) + + bus["width"] = bus["max"] - bus["min"] + 1 + + # Sort buses by their purpose + for name, bus in buses.items(): + + # A test pin (unconnected) + if name.startswith("TEST"): + cls = "test" + + # A debug pin (unconnected) + elif name.startswith("DEBUG"): + cls = "debug" + + # A MIO/DDR pin. + elif name.startswith("MIO") or name.startswith("DDR") and \ + name != "DDRARB": + cls = "mio" + + # "Normal" pin + else: + cls = "normal" + + bus["class"] = cls + + # ..................................................... + # Generate XML model + model_xml = """ + +""" + + # Inputs + model_xml += """ +""" + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins + if bus["class"] not in ["normal", "mio"]: + continue + + if bus["direction"] != "input": + continue + + model_xml += " \n".format(name) + + # Outputs + model_xml += """ + +""" + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins + if bus["class"] not in ["normal", "mio"]: + continue + + if bus["direction"] != "output": + continue + + model_xml += " \n".format(name) + + model_xml += """ +""" + + model_xml += """ +""" + + with open("ps7.model.xml", "w") as fp: + fp.write(model_xml) + + # ..................................................... + # Generate XML pb_type + pb_name = "PS7" + blif_model = "PS7" + + pb_xml = """ +""".format(pb_name, blif_model) + + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins + if bus["class"] not in ["normal", "mio"]: + continue + + pb_xml += " <{} name=\"{}\" num_pins=\"{}\"/>\n".format( + bus["direction"].ljust(6), name, bus["width"]) + + pb_xml += """ +""" + + with open("ps7.pb_type.xml", "w") as fp: + fp.write(pb_xml) + + # ..................................................... + # Prepare Verilog module definition for the PS7 + pin_strs = [] + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins + if bus["class"] not in ["normal", "mio"]: + continue + + if bus["width"] > 1: + pin_str = " {} [{:>2d}:{:>2d}] {}".format( + bus["direction"].ljust(6), bus["max"], bus["min"], name) + else: + pin_str = " {} {}".format(bus["direction"].ljust(6), name) + + pin_strs.append(pin_str) + + verilog = """(* blackbox *) +module PS7 ( +{} +); + +endmodule +""".format(",\n".join(pin_strs)) + + with open("ps7.v", "w") as fp: + fp.write(verilog) + + +if __name__ == "__main__": + main() From cb26746128e7537c6793156bb4c117e43bd07372 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 17 Dec 2019 13:50:38 +0100 Subject: [PATCH 2/5] Added dumping of dummy inout ports, added generation of techmap that ties unconnected ports to 0. Signed-off-by: Maciej Kurc --- minitests/ps7/xtra/Makefile | 9 +- minitests/ps7/xtra/README.md | 2 +- minitests/ps7/xtra/dump_ps7.tcl | 5 +- minitests/ps7/xtra/make_cell.py | 141 +++++++++++++++++++++++++++----- 4 files changed, 129 insertions(+), 28 deletions(-) diff --git a/minitests/ps7/xtra/Makefile b/minitests/ps7/xtra/Makefile index fc98bc23..af9920e4 100644 --- a/minitests/ps7/xtra/Makefile +++ b/minitests/ps7/xtra/Makefile @@ -1,9 +1,11 @@ .PHONY: all clean -all: ps7.v +all: xtra.ok clean: - rm -rf ps7.v + rm -rf xtra.ok + rm -rf ps7_sim.v + rm -rf ps7_map.v rm -rf ps7.csv rm -rf *.xml rm -rf *.log @@ -12,5 +14,6 @@ clean: ps7.csv: dump_ps7.tcl $(XRAY_VIVADO) -mode batch -source dump_ps7.tcl -nojournal -log $(basename $@).log -ps7.v: ps7.csv make_cell.py +xtra.ok: ps7.csv make_cell.py python3 make_cell.py $< + touch xtra.ok diff --git a/minitests/ps7/xtra/README.md b/minitests/ps7/xtra/README.md index d44b22a1..285bc14b 100644 --- a/minitests/ps7/xtra/README.md +++ b/minitests/ps7/xtra/README.md @@ -1,3 +1,3 @@ # PS7 verilog cell definition extractor -Extracts all pins of the PS7 bel from Vivado, groups them into buses, removes those that are not connected (TEST*, DEBUG*) and writes the PS7 verilog cell definition. +Extracts all pins of the PS7 bel from Vivado, groups them into buses, removes those that are not connected (TEST*, DEBUG*) and creates the VPR counterpart for it. It also generates model XML, pb_type XML and techmap which handles unconnected ports correctly. diff --git a/minitests/ps7/xtra/dump_ps7.tcl b/minitests/ps7/xtra/dump_ps7.tcl index ffb23c03..10586c8c 100644 --- a/minitests/ps7/xtra/dump_ps7.tcl +++ b/minitests/ps7/xtra/dump_ps7.tcl @@ -3,7 +3,7 @@ set_property design_mode PinPlanning [current_fileset] open_io_design -name io_1 set fp [open ps7.csv w] -puts $fp "name,is_input,is_output" +puts $fp "name,is_input,is_output,is_bidir" set pins [get_bel_pins -of_objects [get_bels -of_objects [get_sites PS7* -of_objects [get_tiles PSS*]]]] foreach pin $pins { @@ -11,8 +11,9 @@ foreach pin $pins { set pin_name [lindex [split $pin "/"] 2] set is_input [get_property IS_INPUT $pin] set is_output [get_property IS_OUTPUT $pin] + set is_bidir [get_property IS_BIDIR $pin] - puts $fp "$pin_name,$is_input,$is_output" + puts $fp "$pin_name,$is_input,$is_output,$is_bidir" } close $fp diff --git a/minitests/ps7/xtra/make_cell.py b/minitests/ps7/xtra/make_cell.py index 60d98b4b..03860534 100644 --- a/minitests/ps7/xtra/make_cell.py +++ b/minitests/ps7/xtra/make_cell.py @@ -7,7 +7,6 @@ from collections import defaultdict # ============================================================================= - def main(): BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") @@ -41,10 +40,18 @@ def main(): idx = 0 # Get direction - if int(pin["is_input"]): + is_input = int(pin["is_input"]) + is_output = int(pin["is_output"]) + is_bidir = int(pin["is_bidir"]) + + if is_input and not is_output and not is_bidir: direction = "input" - if int(pin["is_output"]): + elif not is_input and is_output and not is_bidir: direction = "output" + elif not is_input and not is_output and is_bidir: + direction = "inout" + else: + assert False, pin # Add to bus bus = buses[name] @@ -90,9 +97,12 @@ def main(): # ..................................................... # Generate XML model + pb_name = "PS7" + blif_model = "PS7_VPR" + model_xml = """ - -""" + +""".format(blif_model) # Inputs model_xml += """ @@ -101,7 +111,7 @@ def main(): bus = buses[name] # Skip not relevant pins - if bus["class"] not in ["normal", "mio"]: + if bus["class"] not in ["normal"]: continue if bus["direction"] != "input": @@ -117,7 +127,7 @@ def main(): bus = buses[name] # Skip not relevant pins - if bus["class"] not in ["normal", "mio"]: + if bus["class"] not in ["normal"]: continue if bus["direction"] != "output": @@ -136,9 +146,6 @@ def main(): # ..................................................... # Generate XML pb_type - pb_name = "PS7" - blif_model = "PS7" - pb_xml = """ """.format(pb_name, blif_model) @@ -146,7 +153,7 @@ def main(): bus = buses[name] # Skip not relevant pins - if bus["class"] not in ["normal", "mio"]: + if bus["class"] not in ["normal"]: continue pb_xml += " <{} name=\"{}\" num_pins=\"{}\"/>\n".format( @@ -159,8 +166,42 @@ def main(): fp.write(pb_xml) # ..................................................... - # Prepare Verilog module definition for the PS7 - pin_strs = [] + # Prepare Verilog module definition for the PS7_VPR + port_defs = [] + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins (eg. MIO and DDR) + if bus["class"] not in ["normal"]: + continue + + # Generate port definition + if bus["width"] > 1: + port_str = " {} [{:>2d}:{:>2d}] {}".format( + bus["direction"].ljust(6), bus["max"], bus["min"], name) + else: + port_str = " {} {}".format(bus["direction"].ljust(6), name) + + port_defs.append(port_str) + + verilog = """(* blackbox *) +module PS7_VPR ( +{} +); + +endmodule +""".format(",\n".join(port_defs)) + + with open("ps7_sim.v", "w") as fp: + fp.write(verilog) + + # ..................................................... + # Prepare techmap that maps PS7 to PS7_VPR and handles + # unconnected inputs (ties them to GND) + port_defs = [] + port_conns = [] + param_defs = [] + wire_defs = [] for name in sorted(buses.keys()): bus = buses[name] @@ -168,25 +209,81 @@ def main(): if bus["class"] not in ["normal", "mio"]: continue + # Generate port definition if bus["width"] > 1: - pin_str = " {} [{:>2d}:{:>2d}] {}".format( + port_str = " {} [{:>2d}:{:>2d}] {}".format( bus["direction"].ljust(6), bus["max"], bus["min"], name) else: - pin_str = " {} {}".format(bus["direction"].ljust(6), name) + port_str = " {} {}".format(bus["direction"].ljust(6), name) - pin_strs.append(pin_str) + port_defs.append(port_str) - verilog = """(* blackbox *) -module PS7 ( -{} + # MIO and DDR pins are not mapped as they are dummy + if bus["class"] == "mio": + continue + + # This is an input port, needs to be tied to GND if unconnected + if bus["direction"] == "input": + + # Techmap parameter definition + param_defs.append(" parameter _TECHMAP_CONSTMSK_{}_ = 0;".format(name.upper())) + param_defs.append(" parameter _TECHMAP_CONSTVAL_{}_ = 0;".format(name.upper())) + + # Wire definition using generate statement. Necessary for detection + # of unconnected ports. + wire_defs.append(""" + generate if((_TECHMAP_CONSTMSK_{name_upr}_ == {N}'d0) && (_TECHMAP_CONSTVAL_{name_upr}_ == {N}'d0)) + wire [{M}:0] {name_lwr} = {N}'d0; + else + wire [{M}:0] {name_lwr} = {name}; + end""".format( + name=name, + name_upr=name.upper(), + name_lwr=name.lower(), + N=bus["width"], + M=bus["width"]-1 + )) + + # Connection to the "generated" wire. + port_conns.append(" .{name:<25}({name_lwr})".format( + name=name, + name_lwr=name.lower() + )) + + # An output port + else: + + # Direct connection + port_conns.append(" .{name:<25}({name})".format(name=name)) + + # Format the final verilog. + verilog = """module PS7 ( +{port_defs} ); -endmodule -""".format(",\n".join(pin_strs)) + // Techmap specific parameters. +{param_defs} - with open("ps7.v", "w") as fp: + // Detect all unconnected inputs and tie them to 0. +{wire_defs} + + // Replacement cell. + PS7_VPR _TECHMAP_REPLACE_ ( +{port_conns} + ); + +endmodule +""".format( + port_defs=",\n".join(port_defs), + param_defs="\n".join(param_defs), + wire_defs="\n".join(wire_defs), + port_conns=",\n".join(port_conns) + ) + + with open("ps7_map.v", "w") as fp: fp.write(verilog) +# ============================================================================= if __name__ == "__main__": main() From 839ad1cffcd5acb70c7b9f9e2c9956f1b9369b6f Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 18 Dec 2019 12:55:27 +0100 Subject: [PATCH 3/5] Fixed techmap and XML generation. Signed-off-by: Maciej Kurc --- minitests/ps7/xtra/make_cell.py | 46 +++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/minitests/ps7/xtra/make_cell.py b/minitests/ps7/xtra/make_cell.py index 03860534..32bf7753 100644 --- a/minitests/ps7/xtra/make_cell.py +++ b/minitests/ps7/xtra/make_cell.py @@ -7,6 +7,7 @@ from collections import defaultdict # ============================================================================= + def main(): BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") @@ -89,6 +90,10 @@ def main(): name != "DDRARB": cls = "mio" + # PS7 clock/reset + elif name in ["PSCLK", "PSPORB", "PSSRSTB"]: + cls = "mio" + # "Normal" pin else: cls = "normal" @@ -180,7 +185,8 @@ def main(): port_str = " {} [{:>2d}:{:>2d}] {}".format( bus["direction"].ljust(6), bus["max"], bus["min"], name) else: - port_str = " {} {}".format(bus["direction"].ljust(6), name) + port_str = " {} {}".format( + bus["direction"].ljust(6), name) port_defs.append(port_str) @@ -214,7 +220,8 @@ endmodule port_str = " {} [{:>2d}:{:>2d}] {}".format( bus["direction"].ljust(6), bus["max"], bus["min"], name) else: - port_str = " {} {}".format(bus["direction"].ljust(6), name) + port_str = " {} {}".format( + bus["direction"].ljust(6), name) port_defs.append(port_str) @@ -226,33 +233,34 @@ endmodule if bus["direction"] == "input": # Techmap parameter definition - param_defs.append(" parameter _TECHMAP_CONSTMSK_{}_ = 0;".format(name.upper())) - param_defs.append(" parameter _TECHMAP_CONSTVAL_{}_ = 0;".format(name.upper())) + param_defs.append( + " parameter _TECHMAP_CONSTMSK_{}_ = 0;".format(name.upper())) + param_defs.append( + " parameter _TECHMAP_CONSTVAL_{}_ = 0;".format(name.upper())) # Wire definition using generate statement. Necessary for detection # of unconnected ports. - wire_defs.append(""" + wire_defs.append( + """ generate if((_TECHMAP_CONSTMSK_{name_upr}_ == {N}'d0) && (_TECHMAP_CONSTVAL_{name_upr}_ == {N}'d0)) wire [{M}:0] {name_lwr} = {N}'d0; else wire [{M}:0] {name_lwr} = {name}; - end""".format( - name=name, - name_upr=name.upper(), - name_lwr=name.lower(), - N=bus["width"], - M=bus["width"]-1 - )) + endgenerate""".format( + name=name, + name_upr=name.upper(), + name_lwr=name.lower(), + N=bus["width"], + M=bus["width"] - 1)) # Connection to the "generated" wire. - port_conns.append(" .{name:<25}({name_lwr})".format( - name=name, - name_lwr=name.lower() - )) + port_conns.append( + " .{name:<25}({name_lwr})".format( + name=name, name_lwr=name.lower())) # An output port else: - + # Direct connection port_conns.append(" .{name:<25}({name})".format(name=name)) @@ -277,12 +285,12 @@ endmodule port_defs=",\n".join(port_defs), param_defs="\n".join(param_defs), wire_defs="\n".join(wire_defs), - port_conns=",\n".join(port_conns) - ) + port_conns=",\n".join(port_conns)) with open("ps7_map.v", "w") as fp: fp.write(verilog) + # ============================================================================= if __name__ == "__main__": From 568b20252ff9fb10dc021210b6738426022b3f40 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Thu, 19 Dec 2019 13:05:41 +0100 Subject: [PATCH 4/5] Added dumping of PS7 pins grouped by direction to JSON file. Signed-off-by: Maciej Kurc --- minitests/ps7/xtra/Makefile | 1 + minitests/ps7/xtra/make_cell.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/minitests/ps7/xtra/Makefile b/minitests/ps7/xtra/Makefile index af9920e4..b9d99d3a 100644 --- a/minitests/ps7/xtra/Makefile +++ b/minitests/ps7/xtra/Makefile @@ -6,6 +6,7 @@ clean: rm -rf xtra.ok rm -rf ps7_sim.v rm -rf ps7_map.v + rm -rf ps7_pins.json rm -rf ps7.csv rm -rf *.xml rm -rf *.log diff --git a/minitests/ps7/xtra/make_cell.py b/minitests/ps7/xtra/make_cell.py index 32bf7753..307f2ef4 100644 --- a/minitests/ps7/xtra/make_cell.py +++ b/minitests/ps7/xtra/make_cell.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse import csv +import json import re from collections import defaultdict @@ -100,6 +101,27 @@ def main(): bus["class"] = cls + # ..................................................... + # Generate JSON with PS7 pins grouped by direction + + ps7_pins = {"input": [], "output": [], "inout": []} + for name in sorted(buses.keys()): + bus = buses[name] + + # Skip not relevant pins + if bus["class"] not in ["normal", "mio"]: + continue + + if bus["width"] > 1: + for i in range(bus["min"], bus["max"]+1): + pin_name = "{}{}".format(name, i) + ps7_pins[bus["direction"]].append(pin_name) + else: + ps7_pins[bus["direction"]].append(name) + + with open("ps7_pins.json", "w") as fp: + json.dump(ps7_pins, fp, sort_keys=True, indent=2) + # ..................................................... # Generate XML model pb_name = "PS7" From 45338f1af4ae806aa30e9bc79239539cb3d9e1ea Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 28 Jan 2020 12:17:16 +0100 Subject: [PATCH 5/5] Reworked the PS7 port def. extractor so instead of a minitest its now a fuzzer. Signed-off-by: Maciej Kurc --- fuzzers/076-ps7/Makefile | 30 ++ fuzzers/076-ps7/README.md | 3 + fuzzers/076-ps7/generate.sh | 6 + .../076-ps7/generate.tcl | 4 +- fuzzers/076-ps7/make_ports.py | 131 +++++++ fuzzers/Makefile | 3 + minitests/ps7/xtra/Makefile | 20 -- minitests/ps7/xtra/README.md | 3 - minitests/ps7/xtra/make_cell.py | 319 ------------------ 9 files changed, 175 insertions(+), 344 deletions(-) create mode 100644 fuzzers/076-ps7/Makefile create mode 100644 fuzzers/076-ps7/README.md create mode 100644 fuzzers/076-ps7/generate.sh rename minitests/ps7/xtra/dump_ps7.tcl => fuzzers/076-ps7/generate.tcl (84%) create mode 100644 fuzzers/076-ps7/make_ports.py delete mode 100644 minitests/ps7/xtra/Makefile delete mode 100644 minitests/ps7/xtra/README.md delete mode 100644 minitests/ps7/xtra/make_cell.py diff --git a/fuzzers/076-ps7/Makefile b/fuzzers/076-ps7/Makefile new file mode 100644 index 00000000..f58aae52 --- /dev/null +++ b/fuzzers/076-ps7/Makefile @@ -0,0 +1,30 @@ +N := 1 +SPECIMENS := $(addprefix build/specimen_,$(shell seq -f '%03.0f' $(N))) +SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) + +database: $(SPECIMENS_OK) + +pushdb: build/ps7_ports.json + mkdir -p ${XRAY_FAMILY_DIR} + cp build/ps7*.json ${XRAY_FAMILY_DIR}/ + +$(SPECIMENS_OK): + bash generate.sh $(subst /OK,,$@) + touch $@ + +build/ps7_pins.csv: $(SPECIMENS_OK) + cp build/specimen_001/ps7_pins.csv build/ + +build/ps7_ports.json: build/ps7_pins.csv + python3 make_ports.py $< $@ + +run: + $(MAKE) clean + $(MAKE) database + $(MAKE) pushdb + touch run.ok + +clean: + rm -rf build run.ok + +.PHONY: database pushdb run clean diff --git a/fuzzers/076-ps7/README.md b/fuzzers/076-ps7/README.md new file mode 100644 index 00000000..571b84dc --- /dev/null +++ b/fuzzers/076-ps7/README.md @@ -0,0 +1,3 @@ +# PS7 verilog cell definition extractor + +Extracts all pins of the PS7 bel from Vivado, groups them into ports, writes them to a JSON file. diff --git a/fuzzers/076-ps7/generate.sh b/fuzzers/076-ps7/generate.sh new file mode 100644 index 00000000..e6b0e2c6 --- /dev/null +++ b/fuzzers/076-ps7/generate.sh @@ -0,0 +1,6 @@ +#!/bin/bash -x + +source ${XRAY_GENHEADER} + +${XRAY_VIVADO} -mode batch -source $FUZDIR/generate.tcl + diff --git a/minitests/ps7/xtra/dump_ps7.tcl b/fuzzers/076-ps7/generate.tcl similarity index 84% rename from minitests/ps7/xtra/dump_ps7.tcl rename to fuzzers/076-ps7/generate.tcl index 10586c8c..f8a55556 100644 --- a/minitests/ps7/xtra/dump_ps7.tcl +++ b/fuzzers/076-ps7/generate.tcl @@ -1,8 +1,8 @@ -create_project -force -in_memory -name design -part xc7z020clg400-1 +create_project -force -name design -part $::env(XRAY_PART) set_property design_mode PinPlanning [current_fileset] open_io_design -name io_1 -set fp [open ps7.csv w] +set fp [open ps7_pins.csv w] puts $fp "name,is_input,is_output,is_bidir" set pins [get_bel_pins -of_objects [get_bels -of_objects [get_sites PS7* -of_objects [get_tiles PSS*]]]] diff --git a/fuzzers/076-ps7/make_ports.py b/fuzzers/076-ps7/make_ports.py new file mode 100644 index 00000000..1f4f169c --- /dev/null +++ b/fuzzers/076-ps7/make_ports.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +This script loads the PS7 pin dump from Vivado and groups pins into ports. Also +assigns each port a class that indicates its function. The classes are: + +- "normal": A port that connects to the PL (FPGA) +- "test": A port used for testing, not accessible from the PL. +- "debug": A debug port, not accessible. +- "mio": The "mio" ports go directly to die pads, not relevant for routing. + +Ports are then written to a JSON file. +""" +import argparse +import csv +import json +import re + +from collections import defaultdict + +# ============================================================================= + + +def main(): + + BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") + + # Parse arguments + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument("csv", type=str, help="PS7 pin dump file") + parser.add_argument( + "json", + type=str, + help="Output JSON file with PS7 pins grouped into ports") + + args = parser.parse_args() + + # Load pin dump + with open(args.csv, "r") as fp: + pin_dump = list(csv.DictReader(fp)) + + # Group pins into ports + ports = defaultdict(lambda :{ + "direction": None, + "min": None, + "max": None, + "width": 0 + }) + + for pin in list(pin_dump): + + # Get port name and signal index + match = BUS_REGEX.match(pin["name"]) + if match: + name = match.group(1) + idx = int(match.group(2)) + else: + name = pin["name"] + idx = 0 + + # Get direction + is_input = int(pin["is_input"]) + is_output = int(pin["is_output"]) + is_bidir = int(pin["is_bidir"]) + + if is_input and not is_output and not is_bidir: + direction = "input" + elif not is_input and is_output and not is_bidir: + direction = "output" + elif not is_input and not is_output and is_bidir: + direction = "inout" + else: + assert False, pin + + # Add to port + port = ports[name] + + if port["direction"] is None: + port["direction"] = direction + else: + assert port["direction"] == direction + + if port["min"] is None: + port["min"] = idx + else: + port["min"] = min(port["min"], idx) + + if port["max"] is None: + port["max"] = idx + else: + port["max"] = max(port["max"], idx) + + port["width"] = port["max"] - port["min"] + 1 + + # Sort ports by their purpose + for name, port in ports.items(): + + # A test pin (unconnected) + if name.startswith("TEST"): + cls = "test" + + # A debug pin (unconnected) + elif name.startswith("DEBUG"): + cls = "debug" + + # A MIO/DDR pin. + elif name.startswith("MIO") or name.startswith("DDR") and \ + name != "DDRARB": + cls = "mio" + + # PS7 clock/reset + elif name in ["PSCLK", "PSPORB", "PSSRSTB"]: + cls = "mio" + + # "Normal" pin + else: + cls = "normal" + + port["class"] = cls + + # Write pin ports to a JSON file + with open(args.json, "w") as fp: + json.dump(ports, fp, indent=1, sort_keys=True) + + +# ============================================================================= + +if __name__ == "__main__": + main() diff --git a/fuzzers/Makefile b/fuzzers/Makefile index b58659b9..cedde639 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -120,6 +120,9 @@ $(eval $(call fuzzer,072-ordered_wires,)) $(eval $(call fuzzer,073-get_counts,)) $(eval $(call fuzzer,074-dump_all,005-tilegrid 072-ordered_wires)) $(eval $(call fuzzer,075-pins,)) +ifeq ($(XRAY_DATABASE),zynq7) +$(eval $(call fuzzer,076-ps7,)) +endif endif endif $(eval $(call fuzzer,100-dsp-mskpat,005-tilegrid)) diff --git a/minitests/ps7/xtra/Makefile b/minitests/ps7/xtra/Makefile deleted file mode 100644 index b9d99d3a..00000000 --- a/minitests/ps7/xtra/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY: all clean - -all: xtra.ok - -clean: - rm -rf xtra.ok - rm -rf ps7_sim.v - rm -rf ps7_map.v - rm -rf ps7_pins.json - rm -rf ps7.csv - rm -rf *.xml - rm -rf *.log - rm -rf .Xil - -ps7.csv: dump_ps7.tcl - $(XRAY_VIVADO) -mode batch -source dump_ps7.tcl -nojournal -log $(basename $@).log - -xtra.ok: ps7.csv make_cell.py - python3 make_cell.py $< - touch xtra.ok diff --git a/minitests/ps7/xtra/README.md b/minitests/ps7/xtra/README.md deleted file mode 100644 index 285bc14b..00000000 --- a/minitests/ps7/xtra/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# PS7 verilog cell definition extractor - -Extracts all pins of the PS7 bel from Vivado, groups them into buses, removes those that are not connected (TEST*, DEBUG*) and creates the VPR counterpart for it. It also generates model XML, pb_type XML and techmap which handles unconnected ports correctly. diff --git a/minitests/ps7/xtra/make_cell.py b/minitests/ps7/xtra/make_cell.py deleted file mode 100644 index 307f2ef4..00000000 --- a/minitests/ps7/xtra/make_cell.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import csv -import json -import re - -from collections import defaultdict - -# ============================================================================= - - -def main(): - - BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$") - - # Parse arguments - parser = argparse.ArgumentParser() - parser.add_argument("csv", type=str, help="PS7 pin dump file") - args = parser.parse_args() - - # Load pin dump - with open(args.csv, "r") as fp: - pin_dump = list(csv.DictReader(fp)) - - # Group pins into buses - buses = defaultdict(lambda :{ - "direction": None, - "min": None, - "max": None, - "width": 0 - }) - - for pin in list(pin_dump): - - # Get bus name and signal index - match = BUS_REGEX.match(pin["name"]) - if match: - name = match.group(1) - idx = int(match.group(2)) - else: - name = pin["name"] - idx = 0 - - # Get direction - is_input = int(pin["is_input"]) - is_output = int(pin["is_output"]) - is_bidir = int(pin["is_bidir"]) - - if is_input and not is_output and not is_bidir: - direction = "input" - elif not is_input and is_output and not is_bidir: - direction = "output" - elif not is_input and not is_output and is_bidir: - direction = "inout" - else: - assert False, pin - - # Add to bus - bus = buses[name] - - if bus["direction"] is None: - bus["direction"] = direction - else: - assert bus["direction"] == direction - - if bus["min"] is None: - bus["min"] = idx - else: - bus["min"] = min(bus["min"], idx) - - if bus["max"] is None: - bus["max"] = idx - else: - bus["max"] = max(bus["max"], idx) - - bus["width"] = bus["max"] - bus["min"] + 1 - - # Sort buses by their purpose - for name, bus in buses.items(): - - # A test pin (unconnected) - if name.startswith("TEST"): - cls = "test" - - # A debug pin (unconnected) - elif name.startswith("DEBUG"): - cls = "debug" - - # A MIO/DDR pin. - elif name.startswith("MIO") or name.startswith("DDR") and \ - name != "DDRARB": - cls = "mio" - - # PS7 clock/reset - elif name in ["PSCLK", "PSPORB", "PSSRSTB"]: - cls = "mio" - - # "Normal" pin - else: - cls = "normal" - - bus["class"] = cls - - # ..................................................... - # Generate JSON with PS7 pins grouped by direction - - ps7_pins = {"input": [], "output": [], "inout": []} - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins - if bus["class"] not in ["normal", "mio"]: - continue - - if bus["width"] > 1: - for i in range(bus["min"], bus["max"]+1): - pin_name = "{}{}".format(name, i) - ps7_pins[bus["direction"]].append(pin_name) - else: - ps7_pins[bus["direction"]].append(name) - - with open("ps7_pins.json", "w") as fp: - json.dump(ps7_pins, fp, sort_keys=True, indent=2) - - # ..................................................... - # Generate XML model - pb_name = "PS7" - blif_model = "PS7_VPR" - - model_xml = """ - -""".format(blif_model) - - # Inputs - model_xml += """ -""" - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins - if bus["class"] not in ["normal"]: - continue - - if bus["direction"] != "input": - continue - - model_xml += " \n".format(name) - - # Outputs - model_xml += """ - -""" - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins - if bus["class"] not in ["normal"]: - continue - - if bus["direction"] != "output": - continue - - model_xml += " \n".format(name) - - model_xml += """ -""" - - model_xml += """ -""" - - with open("ps7.model.xml", "w") as fp: - fp.write(model_xml) - - # ..................................................... - # Generate XML pb_type - pb_xml = """ -""".format(pb_name, blif_model) - - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins - if bus["class"] not in ["normal"]: - continue - - pb_xml += " <{} name=\"{}\" num_pins=\"{}\"/>\n".format( - bus["direction"].ljust(6), name, bus["width"]) - - pb_xml += """ -""" - - with open("ps7.pb_type.xml", "w") as fp: - fp.write(pb_xml) - - # ..................................................... - # Prepare Verilog module definition for the PS7_VPR - port_defs = [] - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins (eg. MIO and DDR) - if bus["class"] not in ["normal"]: - continue - - # Generate port definition - if bus["width"] > 1: - port_str = " {} [{:>2d}:{:>2d}] {}".format( - bus["direction"].ljust(6), bus["max"], bus["min"], name) - else: - port_str = " {} {}".format( - bus["direction"].ljust(6), name) - - port_defs.append(port_str) - - verilog = """(* blackbox *) -module PS7_VPR ( -{} -); - -endmodule -""".format(",\n".join(port_defs)) - - with open("ps7_sim.v", "w") as fp: - fp.write(verilog) - - # ..................................................... - # Prepare techmap that maps PS7 to PS7_VPR and handles - # unconnected inputs (ties them to GND) - port_defs = [] - port_conns = [] - param_defs = [] - wire_defs = [] - for name in sorted(buses.keys()): - bus = buses[name] - - # Skip not relevant pins - if bus["class"] not in ["normal", "mio"]: - continue - - # Generate port definition - if bus["width"] > 1: - port_str = " {} [{:>2d}:{:>2d}] {}".format( - bus["direction"].ljust(6), bus["max"], bus["min"], name) - else: - port_str = " {} {}".format( - bus["direction"].ljust(6), name) - - port_defs.append(port_str) - - # MIO and DDR pins are not mapped as they are dummy - if bus["class"] == "mio": - continue - - # This is an input port, needs to be tied to GND if unconnected - if bus["direction"] == "input": - - # Techmap parameter definition - param_defs.append( - " parameter _TECHMAP_CONSTMSK_{}_ = 0;".format(name.upper())) - param_defs.append( - " parameter _TECHMAP_CONSTVAL_{}_ = 0;".format(name.upper())) - - # Wire definition using generate statement. Necessary for detection - # of unconnected ports. - wire_defs.append( - """ - generate if((_TECHMAP_CONSTMSK_{name_upr}_ == {N}'d0) && (_TECHMAP_CONSTVAL_{name_upr}_ == {N}'d0)) - wire [{M}:0] {name_lwr} = {N}'d0; - else - wire [{M}:0] {name_lwr} = {name}; - endgenerate""".format( - name=name, - name_upr=name.upper(), - name_lwr=name.lower(), - N=bus["width"], - M=bus["width"] - 1)) - - # Connection to the "generated" wire. - port_conns.append( - " .{name:<25}({name_lwr})".format( - name=name, name_lwr=name.lower())) - - # An output port - else: - - # Direct connection - port_conns.append(" .{name:<25}({name})".format(name=name)) - - # Format the final verilog. - verilog = """module PS7 ( -{port_defs} -); - - // Techmap specific parameters. -{param_defs} - - // Detect all unconnected inputs and tie them to 0. -{wire_defs} - - // Replacement cell. - PS7_VPR _TECHMAP_REPLACE_ ( -{port_conns} - ); - -endmodule -""".format( - port_defs=",\n".join(port_defs), - param_defs="\n".join(param_defs), - wire_defs="\n".join(wire_defs), - port_conns=",\n".join(port_conns)) - - with open("ps7_map.v", "w") as fp: - fp.write(verilog) - - -# ============================================================================= - -if __name__ == "__main__": - main()