mirror of https://github.com/openXC7/prjxray.git
Reworked the PS7 port def. extractor so instead of a minitest its now a fuzzer.
Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
parent
568b20252f
commit
45338f1af4
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash -x
|
||||||
|
|
||||||
|
source ${XRAY_GENHEADER}
|
||||||
|
|
||||||
|
${XRAY_VIVADO} -mode batch -source $FUZDIR/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]
|
set_property design_mode PinPlanning [current_fileset]
|
||||||
open_io_design -name io_1
|
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"
|
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*]]]]
|
set pins [get_bel_pins -of_objects [get_bels -of_objects [get_sites PS7* -of_objects [get_tiles PSS*]]]]
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -120,6 +120,9 @@ $(eval $(call fuzzer,072-ordered_wires,))
|
||||||
$(eval $(call fuzzer,073-get_counts,))
|
$(eval $(call fuzzer,073-get_counts,))
|
||||||
$(eval $(call fuzzer,074-dump_all,005-tilegrid 072-ordered_wires))
|
$(eval $(call fuzzer,074-dump_all,005-tilegrid 072-ordered_wires))
|
||||||
$(eval $(call fuzzer,075-pins,))
|
$(eval $(call fuzzer,075-pins,))
|
||||||
|
ifeq ($(XRAY_DATABASE),zynq7)
|
||||||
|
$(eval $(call fuzzer,076-ps7,))
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
$(eval $(call fuzzer,100-dsp-mskpat,005-tilegrid))
|
$(eval $(call fuzzer,100-dsp-mskpat,005-tilegrid))
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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 = """<models>
|
|
||||||
<model name="{}">
|
|
||||||
""".format(blif_model)
|
|
||||||
|
|
||||||
# Inputs
|
|
||||||
model_xml += """ <input_ports>
|
|
||||||
"""
|
|
||||||
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 += " <port name=\"{}\"/>\n".format(name)
|
|
||||||
|
|
||||||
# Outputs
|
|
||||||
model_xml += """ </input_ports>
|
|
||||||
<output_ports>
|
|
||||||
"""
|
|
||||||
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 += " <port name=\"{}\"/>\n".format(name)
|
|
||||||
|
|
||||||
model_xml += """ </output_ports>
|
|
||||||
"""
|
|
||||||
|
|
||||||
model_xml += """ </model>
|
|
||||||
</models>"""
|
|
||||||
|
|
||||||
with open("ps7.model.xml", "w") as fp:
|
|
||||||
fp.write(model_xml)
|
|
||||||
|
|
||||||
# .....................................................
|
|
||||||
# Generate XML pb_type
|
|
||||||
pb_xml = """<pb_type name="{}" blif_model=".subckt {}" num_pb="1">
|
|
||||||
""".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 += """</pb_type>
|
|
||||||
"""
|
|
||||||
|
|
||||||
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()
|
|
||||||
Loading…
Reference in New Issue