blob: 03fe6c0c6452468add5555b1eb8a603a498ddc2d [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 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
import argparse
import sys
import csv
import f4pga.utils.eblif as eblif
# =============================================================================
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def get_cell_connection(cell, pin):
"""
Returns the name of the net connected to the given cell pin. Returns None
if unconnected
"""
# Only for subckt
assert cell["type"] == "subckt"
# Find the connection and return it
for i in range(1, len(cell["args"])):
p, net = cell["args"][i].split("=")
if p == pin:
return net
# Not found
return None
# =============================================================================
def main():
parser = argparse.ArgumentParser(description="Creates placement constraints other than IOs")
parser.add_argument(
"--input", "-i", "-I", type=argparse.FileType("r"), default=sys.stdin, help="The input constraints place file."
)
parser.add_argument(
"--output",
"-o",
"-O",
type=argparse.FileType("w"),
default=sys.stdout,
help="The output constraints place file.",
)
parser.add_argument("--map", type=argparse.FileType("r"), required=True, help="Clock pinmap CSV file")
parser.add_argument("--blif", "-b", type=argparse.FileType("r"), required=True, help="BLIF / eBLIF file.")
args = parser.parse_args()
# Load clock map
clock_to_gmux = {}
for row in csv.DictReader(args.map):
name = row["name"]
src_loc = (
int(row["src.x"]),
int(row["src.y"]),
int(row["src.z"]),
)
dst_loc = (
int(row["dst.x"]),
int(row["dst.y"]),
int(row["dst.z"]),
)
clock_to_gmux[src_loc] = (dst_loc, name)
# Load EBLIF
eblif_data = eblif.parse_blif(args.blif)
# Process the IO constraints file. Pass the constraints unchanged, store
# them.
io_constraints = {}
for line in args.input:
# Strip, skip comments
line = line.strip()
if line.startswith("#"):
continue
args.output.write(line + "\n")
# Get block and its location
block, x, y, z = line.split()[0:4]
io_constraints[block] = (
int(x),
int(y),
int(z),
)
# Analyze the BLIF netlist. Find clock inputs that go through CLOCK IOB to
# GMUXes.
clock_connections = []
IOB_CELL = {"type": "CLOCK_CELL", "ipin": "I_PAD", "opin": "O_CLK"}
BUF_CELL = {"type": "GMUX_IP", "ipin": "IP", "opin": "IZ"}
for inp_net in eblif_data["inputs"]["args"]:
# This one is not constrained, skip it
if inp_net not in io_constraints:
continue
# Search for a CLOCK cell connected to that net
for cell in eblif_data["subckt"]:
if cell["type"] == "subckt" and cell["args"][0] == IOB_CELL["type"]:
net = get_cell_connection(cell, IOB_CELL["ipin"])
if net == inp_net:
iob_cell = cell
break
else:
continue
# Get the output net of the CLOCK cell
con_net = get_cell_connection(iob_cell, IOB_CELL["opin"])
if not con_net:
continue
# Search for a GMUX connected to the CLOCK cell
for cell in eblif_data["subckt"]:
if cell["type"] == "subckt" and cell["args"][0] == BUF_CELL["type"]:
net = get_cell_connection(cell, BUF_CELL["ipin"])
if net == con_net:
buf_cell = cell
break
else:
continue
# Get the output net of the GMUX
clk_net = get_cell_connection(buf_cell, BUF_CELL["opin"])
if not clk_net:
continue
# Store data
clock_connections.append((inp_net, iob_cell, con_net, buf_cell, clk_net))
# Emit constraints for GCLK cells
for inp_net, iob_cell, con_net, buf_cell, clk_net in clock_connections:
src_loc = io_constraints[inp_net]
if src_loc not in clock_to_gmux:
eprint("ERROR: No GMUX location for input CLOCK pad for net '{}' at {}".format(inp_net, src_loc))
continue
dst_loc, name = clock_to_gmux[src_loc]
# FIXME: Silently assuming here that VPR will name the GMUX block as
# the GMUX cell in EBLIF. In order to fix that there will be a need
# to read & parse the packed netlist file.
line = "{} {} {} {} # {}\n".format(buf_cell["cname"][0], *dst_loc, name)
args.output.write(line)
# =============================================================================
if __name__ == "__main__":
main()