| #!/usr/bin/env python3 |
| import argparse |
| import sys |
| import csv |
| |
| import 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() |