mirror of
https://github.com/chipsalliance/f4pga.git
synced 2025-01-03 03:43:37 -05:00
175 lines
5.8 KiB
Python
175 lines
5.8 KiB
Python
#!/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 pcf,
|
|
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,
|
|
)
|