From 5d140296d5948acd4884885085fee46be81379cb Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Mon, 9 Dec 2019 17:15:39 +0100 Subject: [PATCH] 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()