mirror of
https://github.com/Dasharo/f4pga.git
synced 2026-06-13 19:16:35 -07:00
f4pga/utils: all python scripts moved from xc7 arch-defs tarballs
Signed-off-by: Unai Martinez-Corral <umartinezcorral@antmicro.com>
This commit is contained in:
@@ -91,7 +91,7 @@ xc7a50t: &xc7
|
||||
params:
|
||||
stage_name: ioplace
|
||||
interpreter: '${python3}'
|
||||
script: '${shareDir}/scripts/prjxray_create_ioplace.py'
|
||||
script: ['-m', 'f4pga.utils.xc7.create_ioplace']
|
||||
outputs:
|
||||
io_place:
|
||||
mode: stdout
|
||||
@@ -107,7 +107,7 @@ xc7a50t: &xc7
|
||||
params:
|
||||
stage_name: place_constraints
|
||||
interpreter: '${python3}'
|
||||
script: '${shareDir}/scripts/prjxray_create_place_constraints.py'
|
||||
script: ['-m', 'f4pga.utils.xc7.create_place_constraints']
|
||||
outputs:
|
||||
place_constraints:
|
||||
mode: stdout
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2019-2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Top level keywords defining the begin of a cell definition.
|
||||
top_level = [
|
||||
"model",
|
||||
"inputs",
|
||||
"outputs",
|
||||
"names",
|
||||
"latch",
|
||||
"subckt",
|
||||
]
|
||||
|
||||
# Keywords defining cell attributes / parameters. Those can be specified for
|
||||
# each cell multiple times. Parameter names and values are stored in a dict
|
||||
# under the parsed blif data.
|
||||
#
|
||||
# For example: the construct ".param MODE SYNC" will add to the dict under
|
||||
# the key "param" entry "MODE":"SYNC".
|
||||
#
|
||||
sub_level = [
|
||||
"attr",
|
||||
"param",
|
||||
]
|
||||
|
||||
|
||||
def parse_blif(f):
|
||||
current = None
|
||||
|
||||
data = {}
|
||||
|
||||
def add(d):
|
||||
if d["type"] not in data:
|
||||
data[d["type"]] = []
|
||||
data[d["type"]].append(d)
|
||||
|
||||
current = None
|
||||
for oline in f:
|
||||
line = oline
|
||||
if "#" in line:
|
||||
line = line[: line.find("#")]
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if line.startswith("."):
|
||||
args = line.split(" ", maxsplit=1)
|
||||
if len(args) < 2:
|
||||
args.append("")
|
||||
|
||||
ctype = args.pop(0)
|
||||
assert ctype.startswith("."), ctype
|
||||
ctype = ctype[1:]
|
||||
|
||||
if ctype in top_level:
|
||||
if current:
|
||||
add(current)
|
||||
current = {
|
||||
"type": ctype,
|
||||
"args": args[-1].split(),
|
||||
"data": [],
|
||||
}
|
||||
elif ctype in sub_level:
|
||||
if ctype not in current:
|
||||
current[ctype] = {}
|
||||
key, value = args[-1].split(maxsplit=1)
|
||||
current[ctype][key] = value
|
||||
else:
|
||||
current[ctype] = args[-1].split()
|
||||
continue
|
||||
current["data"].append(line.strip().split())
|
||||
|
||||
if current:
|
||||
add(current)
|
||||
|
||||
assert len(data["inputs"]) == 1
|
||||
data["inputs"] = data["inputs"][0]
|
||||
assert len(data["outputs"]) == 1
|
||||
data["outputs"] = data["outputs"][0]
|
||||
return data
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2019-2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
"""
|
||||
Supported PCF commands:
|
||||
* set_io <net> <pad> - constrain a given <net> to a given physical <pad> in eFPGA pinout.
|
||||
* set_clk <pin> <net> - constrain a given global clock <pin> to a given <net>
|
||||
Every tile where <net> is present will be constrained to use a given global clock.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
import re
|
||||
|
||||
PcfIoConstraint = namedtuple("PcfIoConstraint", "net pad line_str line_num")
|
||||
PcfClkConstraint = namedtuple("PcfClkConstraint", "pin net")
|
||||
|
||||
|
||||
def parse_simple_pcf(f):
|
||||
"""Parse a simple PCF file object and yield PcfIoConstraint objects."""
|
||||
for line_number, line in enumerate(f):
|
||||
line_number += 1
|
||||
|
||||
# Remove comments.
|
||||
args = re.sub(r"#.*", "", line.strip()).split()
|
||||
|
||||
if not args:
|
||||
continue
|
||||
|
||||
# Ignore arguments.
|
||||
args = [arg for arg in args if arg[0] != "-"]
|
||||
assert len(args) == 3, args
|
||||
|
||||
if args[0] == "set_io":
|
||||
|
||||
yield PcfIoConstraint(
|
||||
net=args[1],
|
||||
pad=args[2],
|
||||
line_str=line.strip(),
|
||||
line_num=line_number,
|
||||
)
|
||||
|
||||
if args[0] == "set_clk":
|
||||
|
||||
yield PcfClkConstraint(
|
||||
pin=args[1],
|
||||
net=args[2],
|
||||
)
|
||||
@@ -0,0 +1,264 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2019-2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import print_function
|
||||
from collections import OrderedDict, namedtuple
|
||||
import itertools
|
||||
import re
|
||||
import lxml.etree as ET
|
||||
|
||||
from f4pga.utils.eblif import parse_blif
|
||||
|
||||
IoConstraint = namedtuple("IoConstraint", "name x y z comment")
|
||||
|
||||
HEADER_TEMPLATE = """\
|
||||
#{name:<{nl}} x y z pcf_line
|
||||
#{s:-^{nl}} -- -- - ----"""
|
||||
|
||||
CONSTRAINT_TEMPLATE = "{name:<{nl}} {x: 3} {y: 3} {z: 2} # {comment}"
|
||||
INOUT_REGEX = re.compile(r"^(.+)(_\$inp|_\$out)(.*)$")
|
||||
NETNAME_REGEX = re.compile(r"(.+?)(\[[0-9]+\]$|$)")
|
||||
|
||||
|
||||
class IoPlace(object):
|
||||
def __init__(self):
|
||||
self.constraints = OrderedDict()
|
||||
self.inputs = set()
|
||||
self.outputs = set()
|
||||
self.net_to_block = None
|
||||
self.net_map = {}
|
||||
self.block_to_inst = {}
|
||||
self.inout_nets = set()
|
||||
self.net_to_pad = set()
|
||||
self.net_file_io = set()
|
||||
|
||||
def read_io_loc_pairs(self, blif):
|
||||
"""
|
||||
Read IO_LOC_PAIRS parameters from eblif carrying the information
|
||||
which package pin a specified top port is constrained, e.g. O_LOC_PAIRS = "portA:D1"
|
||||
In case of differential inputs/outputs there are two pairs of the parameter,
|
||||
i.e. IO_LOC_PAIRS = "portA_p:D2,portA_n:D4"
|
||||
"""
|
||||
if "subckt" not in blif:
|
||||
return
|
||||
for attr in blif["subckt"]:
|
||||
if "param" not in attr:
|
||||
continue
|
||||
if "IO_LOC_PAIRS" in attr["param"]:
|
||||
locs = attr["param"]["IO_LOC_PAIRS"][1:-1].split(",")
|
||||
if "NONE" in locs:
|
||||
continue
|
||||
for loc in locs:
|
||||
net, pad = loc.split(":")
|
||||
self.net_to_pad.add((net, pad))
|
||||
|
||||
def read_io_list_from_eblif(self, eblif_file):
|
||||
blif = parse_blif(eblif_file)
|
||||
|
||||
self.inputs = set(blif["inputs"]["args"])
|
||||
self.outputs = set(blif["outputs"]["args"])
|
||||
|
||||
# Build a net name map that maps products of an inout port split into
|
||||
# their formet name.
|
||||
#
|
||||
# For example, an inout port 'A' is split into 'A_$inp' and 'A_$out',
|
||||
# port B[2] into 'B_$inp[2]' and 'B_$out[2]'.
|
||||
self.net_map = {}
|
||||
self.inout_nets = set()
|
||||
for net in itertools.chain(self.inputs, self.outputs):
|
||||
match = INOUT_REGEX.match(net)
|
||||
if match:
|
||||
alias = match.group(1) + match.group(3)
|
||||
self.inout_nets.add(alias)
|
||||
self.net_map[net] = alias
|
||||
else:
|
||||
self.net_map[net] = net
|
||||
self.read_io_loc_pairs(blif)
|
||||
|
||||
def load_block_names_from_net_file(self, net_file):
|
||||
"""
|
||||
.place files expect top-level block (cluster) names, not net names, so
|
||||
build a mapping from net names to block names from the .net file.
|
||||
"""
|
||||
net_xml = ET.parse(net_file)
|
||||
net_root = net_xml.getroot()
|
||||
self.net_to_block = {}
|
||||
|
||||
for block in net_root.xpath("//block"):
|
||||
instance = block.attrib["instance"]
|
||||
if instance != "inpad[0]" and instance != "outpad[0]":
|
||||
continue
|
||||
|
||||
top_block = block.getparent()
|
||||
assert top_block is not None
|
||||
while top_block.getparent() is not net_root:
|
||||
assert top_block is not None
|
||||
top_block = top_block.getparent()
|
||||
self.net_to_block[block.get("name")] = top_block.get("name")
|
||||
|
||||
# Loop over all top-level blocks. Store block name to its instance
|
||||
# correspondences.
|
||||
for block_xml in net_root.findall("block"):
|
||||
name = block_xml.attrib["name"]
|
||||
inst = block_xml.attrib["instance"]
|
||||
|
||||
assert name not in self.block_to_inst, block_xml.attrib
|
||||
self.block_to_inst[name] = inst
|
||||
|
||||
def load_net_file_ios(self, net_file):
|
||||
"""
|
||||
Loads input and outputs net names from the netlist file.
|
||||
"""
|
||||
|
||||
def get_ios(net_root, io_types):
|
||||
"Get the top level io signals from the netlist XML root."
|
||||
for io_type in io_types:
|
||||
io = net_root.xpath("/block/{}/text ()".format(io_type))
|
||||
|
||||
if len(io) == 1:
|
||||
yield io[0]
|
||||
|
||||
net_xml = ET.parse(net_file)
|
||||
net_root = net_xml.getroot()
|
||||
|
||||
for io_line in get_ios(net_root, ["inputs", "outputs"]):
|
||||
io_list = io_line.split(" ")
|
||||
for io in io_list:
|
||||
self.net_file_io.add(io.replace("out:", ""))
|
||||
|
||||
def get_top_level_block_instance_for_net(self, net_name):
|
||||
"""
|
||||
Returns a name of the top-level block instance for the given net
|
||||
name.
|
||||
"""
|
||||
assert self.is_net(net_name)
|
||||
|
||||
# VPR prefixes output constraints with "out:"
|
||||
if net_name in self.outputs:
|
||||
net_name = "out:" + net_name
|
||||
|
||||
# This is an inout net
|
||||
if net_name in self.inout_nets:
|
||||
block_names = set()
|
||||
|
||||
for prefix, suffix in zip(["", "out:"], ["_$inp", "_$out"]):
|
||||
match = NETNAME_REGEX.match(net_name)
|
||||
name = prefix + match.group(1) + suffix + match.group(2)
|
||||
block_names.add(self.net_to_block[name])
|
||||
|
||||
# Both parts of the net should point to the same block
|
||||
assert len(block_names) == 1, (net_name, block_names)
|
||||
return self.block_to_inst[list(block_names)[0]]
|
||||
|
||||
# A regular net
|
||||
else:
|
||||
if net_name in self.net_to_block:
|
||||
block_name = self.net_to_block[net_name]
|
||||
return self.block_to_inst[block_name]
|
||||
else:
|
||||
return None
|
||||
|
||||
def constrain_net(self, net_name, loc, comment=""):
|
||||
assert len(loc) == 3
|
||||
assert net_name not in self.constraints
|
||||
|
||||
assert self.is_net(net_name), "net {} not in eblif".format(net_name)
|
||||
|
||||
# VPR prefixes output constraints with "out:"
|
||||
if net_name in self.outputs:
|
||||
net_name = "out:" + net_name
|
||||
|
||||
# This is an inout net
|
||||
if net_name in self.inout_nets:
|
||||
for prefix, suffix in zip(["", "out:"], ["_$inp", "_$out"]):
|
||||
|
||||
match = NETNAME_REGEX.match(net_name)
|
||||
name = prefix + match.group(1) + suffix + match.group(2)
|
||||
|
||||
self.constraints[name] = IoConstraint(
|
||||
name=name,
|
||||
x=loc[0],
|
||||
y=loc[1],
|
||||
z=loc[2],
|
||||
comment=comment,
|
||||
)
|
||||
|
||||
# A regular net
|
||||
else:
|
||||
self.constraints[net_name] = IoConstraint(
|
||||
name=net_name,
|
||||
x=loc[0],
|
||||
y=loc[1],
|
||||
z=loc[2],
|
||||
comment=comment,
|
||||
)
|
||||
|
||||
def output_io_place(self, f):
|
||||
max_name_length = max(len(c.name) for c in self.constraints.values())
|
||||
print(HEADER_TEMPLATE.format(name="Block Name", nl=max_name_length, s=""), file=f)
|
||||
|
||||
constrained_blocks = {}
|
||||
|
||||
for vpr_net, constraint in self.constraints.items():
|
||||
name = constraint.name
|
||||
name = self.net_to_block.get(name) if self.net_to_block else name
|
||||
|
||||
# This block is already constrained, check if there is no
|
||||
# conflict there.
|
||||
if name in constrained_blocks:
|
||||
existing = constrained_blocks[name]
|
||||
|
||||
if existing.x != constraint.x or existing.y != constraint.y or existing.z != constraint.z:
|
||||
|
||||
print("Error: block '{}' has multiple conflicting constraints!".format(name))
|
||||
print("", constrained_blocks[name])
|
||||
print("", constraint)
|
||||
exit(-1)
|
||||
|
||||
# Don't write the second constraing
|
||||
continue
|
||||
|
||||
# omit if no corresponding block name for the net
|
||||
if name is not None:
|
||||
print(
|
||||
CONSTRAINT_TEMPLATE.format(
|
||||
name=name,
|
||||
nl=max_name_length,
|
||||
x=constraint.x,
|
||||
y=constraint.y,
|
||||
z=constraint.z,
|
||||
comment=constraint.comment,
|
||||
),
|
||||
file=f,
|
||||
)
|
||||
|
||||
# Add to constrained block list
|
||||
constrained_blocks[name] = constraint
|
||||
|
||||
def is_net(self, net):
|
||||
return net in self.net_map.values()
|
||||
|
||||
def is_net_packed(self, net):
|
||||
return net in self.net_file_io
|
||||
|
||||
def get_nets(self):
|
||||
for net in self.inputs:
|
||||
yield self.net_map[net]
|
||||
for net in self.outputs:
|
||||
yield self.net_map[net]
|
||||
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2019-2022 F4PGA Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
"""
|
||||
Convert a PCF file into a VPR io.place file.
|
||||
"""
|
||||
|
||||
|
||||
from argparse import ArgumentParser, FileType
|
||||
from pathlib import Path
|
||||
from csv import DictReader as csv_DictReader
|
||||
from sys import stdout, stderr, exit as sys_exit
|
||||
from json import dump as json_dump, load as json_load
|
||||
|
||||
from f4pga.utils.vpr_io_place import IoPlace
|
||||
from f4pga.utils.pcf import parse_simple_pcf
|
||||
|
||||
|
||||
def p_main(blif, map, net, pcf=None, output=stdout, iostandard_defs_file=None, iostandard="LVCMOS33", drive=12):
|
||||
io_place = IoPlace()
|
||||
io_place.read_io_list_from_eblif(blif)
|
||||
io_place.load_block_names_from_net_file(net)
|
||||
|
||||
# Map of pad names to VPR locations.
|
||||
pad_map = {}
|
||||
|
||||
for pin_map_entry in csv_DictReader(map):
|
||||
pad_map[pin_map_entry["name"]] = (
|
||||
(
|
||||
int(pin_map_entry["x"]),
|
||||
int(pin_map_entry["y"]),
|
||||
int(pin_map_entry["z"]),
|
||||
),
|
||||
pin_map_entry["is_output"],
|
||||
pin_map_entry["iob"],
|
||||
pin_map_entry["real_io_assoc"],
|
||||
)
|
||||
|
||||
iostandard_defs = {}
|
||||
|
||||
# Load iostandard constraints. This is a temporary workaround that allows
|
||||
# to pass them into fasm2bels. As soon as there is support for XDC this
|
||||
# will not be needed anymore.
|
||||
# If there is a JSON file with the same name as the PCF file then it is
|
||||
# loaded and used as iostandard constraint source NOT for the design but
|
||||
# to be used in fasm2bels.
|
||||
iostandard_constraints = {}
|
||||
|
||||
if pcf is not None:
|
||||
fname = Path(pcf.name.replace(".pcf", ".json"))
|
||||
if fname.is_file():
|
||||
with fname.open("r") as fp:
|
||||
iostandard_constraints = json_load(fp)
|
||||
net_to_pad = io_place.net_to_pad
|
||||
if pcf is not None:
|
||||
net_to_pad |= set((constr.net, constr.pad) for constr in parse_simple_pcf(pcf))
|
||||
# Check for conflicting pad constraints
|
||||
net_to_pad_map = dict()
|
||||
for (net, pad) in net_to_pad:
|
||||
if net not in net_to_pad_map:
|
||||
net_to_pad_map[net] = pad
|
||||
elif pad != net_to_pad_map[net]:
|
||||
print(
|
||||
f"ERROR: Conflicting pad constraints for net {net}:\n{pad}\n{net_to_pad_map[net]}",
|
||||
file=stderr,
|
||||
)
|
||||
sys_exit(1)
|
||||
|
||||
# Constrain nets
|
||||
for net, pad in net_to_pad:
|
||||
if not io_place.is_net(net):
|
||||
nets = "\n".join(io_place.get_nets())
|
||||
print(
|
||||
f"ERROR: Constrained net {net} is not in available netlist:\n{nets}",
|
||||
file=stderr,
|
||||
)
|
||||
sys_exit(1)
|
||||
|
||||
if pad not in pad_map:
|
||||
pads = "\n".join(sorted(pad_map.keys()))
|
||||
print(
|
||||
f"ERROR: Constrained pad {pad} is not in available pad map:\n{pads}",
|
||||
file=stderr,
|
||||
)
|
||||
sys_exit(1)
|
||||
|
||||
loc, is_output, iob, real_io_assoc = pad_map[pad]
|
||||
|
||||
io_place.constrain_net(net_name=net, loc=loc, comment="set_property LOC {} [get_ports {{{}}}]".format(pad, net))
|
||||
if real_io_assoc == "True":
|
||||
iostandard_defs[iob] = (
|
||||
iostandard_constraints[pad]
|
||||
if pad in iostandard_constraints
|
||||
else ({"DRIVE": drive, "IOSTANDARD": iostandard} if is_output else {"IOSTANDARD": iostandard})
|
||||
)
|
||||
|
||||
io_place.output_io_place(output)
|
||||
|
||||
# Write iostandard definitions
|
||||
if iostandard_defs_file is not None:
|
||||
with Path(iostandard_defs_file).open("w") as f:
|
||||
json_dump(iostandard_defs, f, indent=2)
|
||||
|
||||
|
||||
def main(
|
||||
blif,
|
||||
map,
|
||||
net,
|
||||
pcf=None,
|
||||
output=None,
|
||||
iostandard_defs_file=None,
|
||||
iostandard="LVCMOS33",
|
||||
drive=12,
|
||||
):
|
||||
p_main(
|
||||
blif=Path(blif).open("r"),
|
||||
map=Path(map).open("r"),
|
||||
net=Path(net).open("r"),
|
||||
pcf=None if pcf is None else Path(pcf).open("r"),
|
||||
output=stdout if output is None else Path(output).open("w"),
|
||||
iostandard_defs_file=iostandard_defs_file,
|
||||
iostandard=iostandard,
|
||||
drive=drive,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser(description="Convert a PCF file into a VPR io.place file.")
|
||||
parser.add_argument("--pcf", "-p", "-P", type=FileType("r"), required=False, help="PCF input file")
|
||||
parser.add_argument("--blif", "-b", type=FileType("r"), required=True, help="BLIF / eBLIF file")
|
||||
parser.add_argument("--map", "-m", "-M", type=FileType("r"), required=True, help="Pin map CSV file")
|
||||
parser.add_argument("--output", "-o", "-O", type=FileType("w"), default=stdout, help="The output io.place file")
|
||||
parser.add_argument("--iostandard_defs", help="(optional) Output IOSTANDARD def file")
|
||||
parser.add_argument(
|
||||
"--iostandard",
|
||||
default="LVCMOS33",
|
||||
help="Default IOSTANDARD to use for pins",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--drive",
|
||||
type=int,
|
||||
default=12,
|
||||
help="Default drive to use for pins",
|
||||
)
|
||||
parser.add_argument("--net", "-n", type=FileType("r"), required=True, help="top.net file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
p_main(
|
||||
blif=args.blif,
|
||||
map=args.map,
|
||||
net=args.net,
|
||||
pcf=args.pcf,
|
||||
output=args.output,
|
||||
iostandard_defs_file=args.iostandard_defs,
|
||||
iostandard=args.iostandard,
|
||||
drive=args.drive,
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,11 @@ isQuickLogic = FPGA_FAM != "xc7"
|
||||
SH_SUBDIR = "quicklogic" if isQuickLogic else FPGA_FAM
|
||||
|
||||
|
||||
if not isQuickLogic:
|
||||
from f4pga.utils.xc7.create_ioplace import main as xc7_create_ioplace
|
||||
from f4pga.utils.xc7.create_place_constraints import main as xc7_create_place_constraints
|
||||
|
||||
|
||||
# Helper functions
|
||||
|
||||
|
||||
@@ -472,26 +477,22 @@ fi
|
||||
)
|
||||
else:
|
||||
(eblif, net, part, device, arch_def) = sys_argv[1:6]
|
||||
pcf_opts = f"'--pcf' '{sys_argv[6]}'" if len(sys_argv) > 6 else ""
|
||||
ioplace_file = f"{Path(eblif).stem}.ioplace"
|
||||
p_run_bash_cmds(
|
||||
f"""
|
||||
set -e
|
||||
python3 '{F4PGA_SHARE_DIR}/scripts/prjxray_create_ioplace.py' \
|
||||
--blif '{eblif}' \
|
||||
--net '{net}' {pcf_opts} \
|
||||
--map '{F4PGA_SHARE_DIR}/arch/{device}/{part}/pinmap.csv' \
|
||||
> '{ioplace_file}'
|
||||
python3 '{F4PGA_SHARE_DIR}'/scripts/prjxray_create_place_constraints.py \
|
||||
--blif '{eblif}' \
|
||||
--net '{net}' \
|
||||
--arch '{arch_def}' \
|
||||
--part '{part}' \
|
||||
--vpr_grid_map '{F4PGA_SHARE_DIR}/arch/{device}/vpr_grid_map.csv' \
|
||||
--input '{ioplace_file}' \
|
||||
--db_root "${{DATABASE_DIR:-$(prjxray-config)}}" \
|
||||
> constraints.place
|
||||
"""
|
||||
xc7_create_ioplace(
|
||||
blif=eblif,
|
||||
map=f"{F4PGA_SHARE_DIR}/arch/{device}/{part}/pinmap.csv",
|
||||
net=net,
|
||||
output=ioplace_file,
|
||||
pcf=Path(sys_argv[6]).open("r") if len(sys_argv) > 6 else None,
|
||||
)
|
||||
xc7_create_place_constraints(
|
||||
blif=eblif,
|
||||
net=net,
|
||||
arch=arch_def,
|
||||
part=part,
|
||||
vpr_grid_map=f"{F4PGA_SHARE_DIR}/arch/{device}/vpr_grid_map.csv",
|
||||
input=ioplace_file,
|
||||
output="constraints.place",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ techmap -map $::env(TECHMAP_PATH)/carry_map.v
|
||||
clean_processes
|
||||
write_json $::env(OUT_JSON).carry_fixup.json
|
||||
|
||||
exec $::env(PYTHON3) $::env(UTILS_PATH)/fix_xc7_carry.py < $::env(OUT_JSON).carry_fixup.json > $::env(OUT_JSON).carry_fixup_out.json
|
||||
exec $::env(PYTHON3) -m f4pga.utils.xc7.fix_xc7_carry < $::env(OUT_JSON).carry_fixup.json > $::env(OUT_JSON).carry_fixup_out.json
|
||||
design -push
|
||||
read_json $::env(OUT_JSON).carry_fixup_out.json
|
||||
|
||||
|
||||
Reference in New Issue
Block a user