Skip to content

Commit

Permalink
Merge pull request f4pga#1519 from antmicro/io_loc_eblif
Browse files Browse the repository at this point in the history
Support for IO constraints read from XDC
  • Loading branch information
litghost committed Jul 8, 2020
2 parents c5c86dd + 8dc555d commit f0d7e4b
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 103 deletions.
27 changes: 18 additions & 9 deletions common/cmake/devices.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ function(ADD_FPGA_TARGET)

if(NOT "${ADD_FPGA_TARGET_INPUT_XDC_FILE}" STREQUAL "")
get_file_location(INPUT_XDC_FILE ${ADD_FPGA_TARGET_INPUT_XDC_FILE})
append_file_dependency(YOSYS_IO_DEPS ${ADD_FPGA_TARGET_INPUT_XDC_FILE})
endif()

#
Expand Down Expand Up @@ -1113,15 +1114,15 @@ function(ADD_FPGA_TARGET)

set(YOSYS_IO_DEPS "")

if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "" OR NOT ${ADD_FPGA_TARGET_INPUT_XDC_FILE} STREQUAL "")
get_target_property_required(PINMAP_FILE ${BOARD} PINMAP)

get_file_location(INPUT_IO_FILE ${ADD_FPGA_TARGET_INPUT_IO_FILE})
get_file_location(PINMAP ${PINMAP_FILE})

append_file_dependency(YOSYS_IO_DEPS ${ADD_FPGA_TARGET_INPUT_IO_FILE})
append_file_dependency(YOSYS_IO_DEPS ${PINMAP_FILE})
endif()

if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
get_file_location(INPUT_IO_FILE ${ADD_FPGA_TARGET_INPUT_IO_FILE})
append_file_dependency(YOSYS_IO_DEPS ${ADD_FPGA_TARGET_INPUT_IO_FILE})
endif()

if(NOT ${ADD_FPGA_TARGET_NO_SYNTHESIS})
Expand Down Expand Up @@ -1342,7 +1343,7 @@ function(ADD_FPGA_TARGET)
# -------------------------------------------------------------------------
set(FIX_PINS_ARG "")

if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "" OR NOT ${ADD_FPGA_TARGET_INPUT_XDC_FILE} STREQUAL "")
get_target_property_required(NO_PINS ${ARCH} NO_PINS)
if(${NO_PINS})
message(FATAL_ERROR "Arch ${ARCH} does not currently support pin constraints.")
Expand All @@ -1361,12 +1362,17 @@ function(ADD_FPGA_TARGET)

# Add complete dependency chain
set(IO_DEPS ${VPR_DEPS})
append_file_dependency(IO_DEPS ${ADD_FPGA_TARGET_INPUT_IO_FILE})
if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
append_file_dependency(IO_DEPS ${ADD_FPGA_TARGET_INPUT_IO_FILE})
endif()
append_file_dependency(IO_DEPS ${PINMAP_FILE})
append_file_dependency(IO_DEPS ${OUT_NET_REL})

set_target_properties(${NAME} PROPERTIES
INPUT_IO_FILE ${ADD_FPGA_TARGET_INPUT_IO_FILE})
if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
set_target_properties(${NAME} PROPERTIES
INPUT_IO_FILE ${ADD_FPGA_TARGET_INPUT_IO_FILE})
set(PCF_INPUT_IO_FILE "--pcf ${INPUT_IO_FILE}")
endif()

# Set variables for the string(CONFIGURE) below.
set(OUT_IO ${OUT_LOCAL}/${TOP}_io.place)
Expand Down Expand Up @@ -1671,6 +1677,9 @@ function(ADD_FPGA_TARGET)
set(OUT_BIT_VERILOG ${OUT_LOCAL}/${TOP}_bit.v)
get_target_property_required(BIT_TO_V ${ARCH} BIT_TO_V)
get_target_property_required(BIT_TO_V_CMD ${ARCH} BIT_TO_V_CMD)
if(NOT ${ADD_FPGA_TARGET_INPUT_IO_FILE} STREQUAL "")
set(PCF_INPUT_IO_FILE "--pcf ${INPUT_IO_FILE}")
endif()
string(CONFIGURE ${BIT_TO_V_CMD} BIT_TO_V_CMD_FOR_TARGET)
separate_arguments(
BIT_TO_V_CMD_FOR_TARGET_LIST UNIX_COMMAND ${BIT_TO_V_CMD_FOR_TARGET}
Expand Down
22 changes: 22 additions & 0 deletions utils/vpr_io_place.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ def __init__(self):
self.net_to_block = None
self.net_map = {}
self.inout_nets = set()
self.net_to_pad = 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 = eblif.parse_blif(eblif_file)
Expand All @@ -46,6 +67,7 @@ def read_io_list_from_eblif(self, eblif_file):
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):
"""
Expand Down
4 changes: 2 additions & 2 deletions xc/common/cmake/arch_define.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function(ADD_XC_ARCH_DEFINE)
\${PYTHON3} \${PLACE_TOOL} \
--map \${PINMAP} \
--blif \${OUT_EBLIF} \
--pcf \${INPUT_IO_FILE} \
\${PCF_INPUT_IO_FILE} \
--net \${OUT_NET}"
PLACE_CONSTR_TOOL
${symbiflow-arch-defs_SOURCE_DIR}/xc/common/utils/prjxray_create_place_constraints.py
Expand Down Expand Up @@ -165,7 +165,7 @@ function(ADD_XC_ARCH_DEFINE)
--fasm_file \${OUT_BIN}.fasm \
--iostandard ${DEFAULT_IOSTANDARD} \
--drive ${DEFAULT_DRIVE} \
--pcf \${INPUT_IO_FILE} \
\${PCF_INPUT_IO_FILE} \
--eblif \${OUT_EBLIF} \
--top \${TOP} \
\${OUT_BIT_VERILOG} \${OUT_BIT_VERILOG}.tcl"
Expand Down
61 changes: 38 additions & 23 deletions xc/common/utils/prjxray_create_ioplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def main():
'-p',
"-P",
type=argparse.FileType('r'),
required=True,
required=False,
help='PCF input file'
)
parser.add_argument(
Expand Down Expand Up @@ -96,46 +96,61 @@ def main():
# to be used in fasm2bels.
iostandard_constraints = {}

fname = args.pcf.name.replace(".pcf", ".json")
if os.path.isfile(fname):
with open(fname, "r") as fp:
iostandard_constraints = json.load(fp)

if args.pcf:
fname = args.pcf.name.replace(".pcf", ".json")
if os.path.isfile(fname):
with open(fname, "r") as fp:
iostandard_constraints = json.load(fp)
net_to_pad = io_place.net_to_pad
if args.pcf:
pcf_constraints = parse_simple_pcf(args.pcf)
net_to_pad |= set(
(constr.net, constr.pad) for constr in pcf_constraints
)
# 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(
"""ERROR:
Conflicting pad constraints for net {}:\n{}\n{}""".format(
net, pad, net_to_pad_map[net]
),
file=sys.stderr
)
sys.exit(1)
# Constrain nets
for pcf_constraint in parse_simple_pcf(args.pcf):
if not io_place.is_net(pcf_constraint.net):
for net, pad in net_to_pad:
if not io_place.is_net(net):
print(
"""ERROR:
PCF constraint "{}" from line {} constraints net {} which is not in available netlist:\n{}"""
.format(
pcf_constraint.line_str, pcf_constraint.line_num,
pcf_constraint.net, '\n'.join(io_place.get_nets())
Constrained net {} is not in available netlist:\n{}""".format(
net, '\n'.join(io_place.get_nets())
),
file=sys.stderr
)
sys.exit(1)

if pcf_constraint.pad not in pad_map:
if pad not in pad_map:
print(
"""ERROR:
PCF constraint "{}" from line {} constraints pad {} which is not in available pad map:\n{}"""
.format(
pcf_constraint.line_str, pcf_constraint.line_num,
pcf_constraint.pad, '\n'.join(sorted(pad_map.keys()))
Constrained pad {} is not in available pad map:\n{}""".format(
pad, '\n'.join(sorted(pad_map.keys()))
),
file=sys.stderr
)
sys.exit(1)

loc, is_output, iob = pad_map[pcf_constraint.pad]
loc, is_output, iob = pad_map[pad]
io_place.constrain_net(
net_name=pcf_constraint.net,
net_name=net,
loc=loc,
comment=pcf_constraint.line_str
comment="set_property LOC {} [get_ports {{{}}}]".format(pad, net)
)

if pcf_constraint.pad in iostandard_constraints:
iostandard_defs[iob] = iostandard_constraints[pcf_constraint.pad]
if pad in iostandard_constraints:
iostandard_defs[iob] = iostandard_constraints[pad]
else:
if is_output:
iostandard_defs[iob] = {
Expand Down
42 changes: 31 additions & 11 deletions xc/xc7/fasm2bels/fasm2bels.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import os.path
import sqlite3
import subprocess
import sys
import tempfile
import json

Expand All @@ -46,6 +47,7 @@
import lib.rr_graph_capnp.graph2 as capnp_graph2
from lib.parse_pcf import parse_simple_pcf
import eblif
import vpr_io_place


def null_process(conn, top, tile, tiles):
Expand Down Expand Up @@ -204,23 +206,40 @@ def bit2fasm(db_root, db, grid, bit_file, fasm_file, bitread, part):
)


def load_io_sites(db_root, part, pcf):
""" Load map of sites to signal names from pcf and part pin definitions.
def load_io_sites(db_root, part, pcf, eblif):
""" Load map of sites to signal names from pcf or eblif and part pin definitions.
Args:
db_root (str): Path to database root folder
part (str): Part name being targeted.
pcf (str): Full path to pcf file for this bitstream.
eblif (str): Parsed contents of EBLIF file.
Returns:
Dict from pad site name to net name.
"""
pin_to_signal = {}
with open(pcf) as f:
for pcf_constraint in parse_simple_pcf(f):
assert pcf_constraint.pad not in pin_to_signal, pcf_constraint.pad
pin_to_signal[pcf_constraint.pad] = pcf_constraint.net
if pcf:
with open(pcf) as f:
for pcf_constraint in parse_simple_pcf(f):
assert pcf_constraint.pad not in pin_to_signal, pcf_constraint.pad
pin_to_signal[pcf_constraint.pad] = pcf_constraint.net
if eblif:
io_place = vpr_io_place.IoPlace()
io_place.read_io_loc_pairs(eblif)
for net, pad in io_place.net_to_pad:
if pad not in pin_to_signal:
pin_to_signal[pad] = net
elif net != pin_to_signal[pad]:
print(
"""ERROR:
Conflicting pin constraints for pad {}:\n{}\n{}""".format(
pad, net, pin_to_signal[pad]
),
file=sys.stderr
)
sys.exit(1)

site_to_signal = {}

Expand Down Expand Up @@ -341,9 +360,13 @@ def main():
maybe_get_wire = create_maybe_get_wire(conn)

top = Module(db, grid, conn, name=args.top)
if args.pcf:
if args.eblif:
with open(args.eblif) as f:
parsed_eblif = eblif.parse_blif(f)

if args.eblif or args.pcf:
top.set_site_to_signal(
load_io_sites(args.db_root, args.part, args.pcf)
load_io_sites(args.db_root, args.part, args.pcf, parsed_eblif)
)

if args.route_file:
Expand All @@ -360,9 +383,6 @@ def main():
top.set_io_banks(part_data['iobanks'])

if args.eblif:
with open(args.eblif) as f:
parsed_eblif = eblif.parse_blif(f)

top.add_to_cname_map(parsed_eblif)
top.make_iosettings_map(parsed_eblif)

Expand Down

0 comments on commit f0d7e4b

Please sign in to comment.