def test_logical_netlist(self):
        logical_netlist = example_logical_netlist()

        interchange = Interchange(
            schema_directory=os.environ['INTERCHANGE_SCHEMA_PATH'])

        with tempfile.NamedTemporaryFile('w+b') as f:
            netlist_capnp = logical_netlist.convert_to_capnp(interchange)
            write_capnp_file(netlist_capnp, f)
            f.seek(0)

            read_logical_netlist = LogicalNetlist.read_from_capnp(
                f, interchange)

        self.assertEqual(read_logical_netlist.name, logical_netlist.name)
        self.assertEqual(read_logical_netlist.top_instance,
                         logical_netlist.top_instance)

        self.assertEqual(read_logical_netlist.libraries.keys(),
                         logical_netlist.libraries.keys())
        for library_name, library in logical_netlist.libraries.items():
            read_library = read_logical_netlist.libraries[library_name]
            self.assertEqual(library.cells.keys(), read_library.cells.keys())
            for cell_name, cell in library.cells.items():
                read_cell = read_library.cells[cell_name]

                self.assertEqual(cell.name, read_cell.name)
                self.assertEqual(cell.property_map, read_cell.property_map)
                self.assertEqual(cell.view, read_cell.view)
                self.assertEqual(cell.nets.keys(), read_cell.nets.keys())
                self.assertEqual(cell.ports.keys(), read_cell.ports.keys())
                self.assertEqual(cell.cell_instances.keys(),
                                 read_cell.cell_instances.keys())
    def test_capnp_modes(self):
        logical_netlist = example_logical_netlist()
        interchange = Interchange(
            schema_directory=os.environ['INTERCHANGE_SCHEMA_PATH'])

        for compression_format in [
                CompressionFormat.UNCOMPRESSED, CompressionFormat.GZIP
        ]:
            for packed in [True, False]:
                with tempfile.NamedTemporaryFile('w+b') as f:
                    netlist_capnp = logical_netlist.convert_to_capnp(
                        interchange)
                    write_capnp_file(netlist_capnp,
                                     f,
                                     compression_format=compression_format,
                                     is_packed=packed)
                    f.seek(0)
                    _ = LogicalNetlist.read_from_capnp(
                        f,
                        interchange,
                        compression_format=compression_format,
                        is_packed=packed)
Esempio n. 3
0
def output_interchange(top, capnp_folder, part, f_logical, f_physical, f_xdc):
    """ Output FPGA interchange from top level Module class object.

    top (Module) - Top level module.
    capnp_folder (str) - Path to the interchange capnp folder
    part (str) - Part for physical netlist.
    f_logical (file-like) - File to output logical_netlist.Netlist.
    f_physical (file-like) - File to output physical_netlist.PhysNetlist.

    """
    interchange = Interchange(capnp_folder)

    hdi_primitives = Library('hdi_primitives')
    work = Library('work')
    libraries = {hdi_primitives.name: hdi_primitives, work.name: work}

    top_cell = Cell(top.name)

    # Create source cells for constant nets.  They are required to have some
    # name, so give them one.
    #
    # TODO: Iterate net names on this?  This feels wrong/weird.  Need to
    # handle net name collisions?
    constant_nets = {
        0: "GLOBAL_LOGIC0",
        1: "GLOBAL_LOGIC1",
    }

    top_cell.add_cell_instance(name='VCC', cell_name="VCC")
    top_cell.add_net(constant_nets[1])
    top_cell.connect_net_to_instance(
        net_name=constant_nets[1], instance_name='VCC', port="P")

    top_cell.add_cell_instance(name='GND', cell_name="GND")
    top_cell.add_net(constant_nets[0])
    top_cell.connect_net_to_instance(
        net_name=constant_nets[0], instance_name='GND', port="G")

    # Parse top level port names, and convert to bussed ports as needed.
    create_top_level_ports(top_cell, top, top.root_in, Direction.Input)
    create_top_level_ports(top_cell, top, top.root_out, Direction.Output)
    create_top_level_ports(top_cell, top, top.root_inout, Direction.Inout)

    for wire, width in make_bus(top.wires):
        wire = unescape_verilog_name(wire)
        if width is None:
            top_cell.add_net(name=wire)
        else:
            for idx in range(width + 1):
                top_cell.add_net(name='{}[{}]'.format(wire, idx))

    # Update/create wire_name_net_map from the BELs.
    for site in top.sites:
        for bel in site.bels:
            bel.make_net_map(top=top, net_map=top.wire_name_net_map)

    for sink_wire, source_wire in top.wire_assigns.yield_wires():
        net_name = flatten_wires(source_wire, top.wire_assigns,
                                 top.wire_name_net_map)
        if sink_wire in top.wire_name_net_map:
            assert top.wire_name_net_map[sink_wire] == net_name
        else:
            top.wire_name_net_map[sink_wire] = net_name

    # Create a list of each primative instances to later build up a primative
    # model library.
    hdi_primitives_cells = {}

    # Create cells instances from each bel in the design.
    for site in top.sites:
        for bel in sorted(site.bels, key=lambda bel: bel.priority):
            bel.output_interchange(
                top_cell=top_cell,
                top=top,
                net_map=top.wire_name_net_map,
                constant_nets=constant_nets,
            )

            if bel.parent_cell is not None:
                continue

            if bel.module not in hdi_primitives_cells:
                hdi_primitives_cells[bel.module] = []

            hdi_primitives_cells[bel.module].append(bel)

    # Add top level cell to the work cell library.
    work.add_cell(top_cell)

    # Construct library cells based on data from top module.
    for cellname in hdi_primitives_cells:
        instances = hdi_primitives_cells[cellname]

        cell = Cell(cellname)

        ports = {}
        for instance in instances:
            _, connections, port_is_output = instance.create_connections(top)

            for port in connections:
                if port_is_output[port]:
                    # The current model doesn't handle IO at all, so add
                    # special cases for IO ports in the library.
                    if cellname.startswith('IOBUF') and port == "IO":
                        direction = Direction.Inout
                    else:
                        direction = Direction.Output
                else:
                    direction = Direction.Input

                width = connections[port].bus_width()
                if port in instance.port_width:
                    if width is not None:
                        assert width <= instance.port_width[port], port

                    width = instance.port_width[port]

                if port in ports:
                    port_dir, port_width = ports[port]
                    assert port_dir == direction, (port, direction, port_dir,
                                                   port_width)
                    if width is not None:
                        assert port_width <= width

                        if width > port_width:
                            ports[port] = (direction, width)
                    else:
                        assert port_width is None
                else:
                    ports[port] = (direction, width)

            # Add instances of unconnected ports (as needed).
            for port, direction in instance.port_direction.items():
                width = instance.port_width[port]

                if direction == "output":
                    direction = Direction.Output
                elif direction == "inout":
                    direction = Direction.Inout
                else:
                    assert direction == "input", direction
                    direction = Direction.Input

                if port in ports:
                    assert (direction, width) == ports[port]
                else:
                    ports[port] = (direction, width)

        for port, (direction, width) in ports.items():

            if width is not None:
                cell.add_bus_port(port, direction, start=width - 1, end=0)
            else:
                cell.add_port(port, direction)

        hdi_primitives.add_cell(cell)

    # Make sure VCC and GND primatives are in the library.
    if "VCC" not in hdi_primitives.cells:
        cell = Cell("VCC")
        cell.add_port("P", Direction.Output)

        hdi_primitives.add_cell(cell)

    if "GND" not in hdi_primitives.cells:
        cell = Cell("GND")
        cell.add_port("G", Direction.Output)

        hdi_primitives.add_cell(cell)

    # Logical netlist is complete, output to file now!
    logical_netlist = LogicalNetlist(
        name=top.name,
        property_map={},
        top_instance_name=top.name,
        top_instance=CellInstance(
            cell_name=top.name, view='netlist', property_map={}),
        libraries=libraries,
    ).convert_to_capnp(interchange)
    write_capnp_file(logical_netlist, f_logical)

    physical_netlist = PhysicalNetlist(part=part)

    site_type_pins = {}

    # Convert sites and bels into placement directives and physical nets.
    net_stubs = {}
    sub_cell_nets = {}
    for site in top.sites:
        physical_netlist.add_site_instance(site.site.name, site.site_type())

        for bel in site.bels:
            if bel.site is None or (bel.bel is None
                                    and len(bel.physical_bels) == 0):
                continue

            cell_instance = unescape_verilog_name(bel.get_cell(top))

            # bel.physical_bels is used to represent a transformation that
            # happens from the library cell (e.g. LUT6_2) into lower
            # primatives (LUT6_2 -> (LUT6, LUT5)).
            #
            # Rather than implement generic transformation support, for now
            # models implement the transformation by adding physical bels to
            # generate the correct placement constraints.
            #
            # TODO: Revisit this in the future?
            if len(bel.physical_bels) == 0:
                # Straight forward case, 1 logical Cell -> 1 physical Bel
                placement = Placement(
                    cell_type=bel.module,
                    cell_name=cell_instance,
                    site=bel.site,
                    bel=bel.bel,
                )

                for (bel_name,
                     bel_pin), cell_pin in bel.bel_pins_to_cell_pins.items():
                    placement.add_bel_pin_to_cell_pin(
                        bel_pin=bel_pin,
                        cell_pin=cell_pin,
                        bel=bel_name,
                    )

                physical_netlist.placements.append(placement)
            else:
                # Transformation cases, create a placement constraint for
                # each bel in the physical_bels list.
                #
                # These represent a cell within the primative, hence the "/"
                # when constructing the cell name.
                for phys_bel in bel.physical_bels:
                    placement = Placement(
                        cell_type=phys_bel.module,
                        cell_name=cell_instance + '/' + phys_bel.name,
                        site=bel.site,
                        bel=phys_bel.bel,
                    )

                    for (bel_name, bel_pin
                         ), cell_pin in phys_bel.bel_pins_to_cell_pins.items():
                        placement.add_bel_pin_to_cell_pin(
                            bel_pin=bel_pin,
                            cell_pin=cell_pin,
                            bel=bel_name,
                        )

                    physical_netlist.placements.append(placement)

        # Convert site routing to PhysicalNetlist objects (PhysicalBelPin,
        # PhysicalSitePin, PhysicalSitePip).
        #
        # Note: Calling output_site_routing must be done before
        # output_interchange_nets to ensure that Bel.final_net_names gets
        # populated, as that is computed during Site.output_site_routing.
        new_nets = site.output_site_routing(
            top=top,
            parent_cell=top_cell,
            net_map=top.wire_name_net_map,
            constant_nets=constant_nets,
            sub_cell_nets=sub_cell_nets)

        for site_pin, site_type_pin in site.site_type_pins.items():
            site_type_pins[site.site.name, site_pin] = site_type_pin

        # Extend net stubs with the site routing.
        for net_name in new_nets:
            if net_name not in net_stubs:
                net_stubs[net_name] = []

            net_stubs[net_name].extend(new_nets[net_name])

    # Convert top level routing nets to pip lists and to relevant nets
    for net_name, pips in top.output_interchange_nets(
            constant_nets=constant_nets):
        if net_name not in net_stubs:
            net_stubs[net_name] = []

        for tile, wire0, wire1 in pips:
            # TODO: Better handling of bipips?
            net_stubs[net_name].append(
                PhysicalPipForStitching(
                    tile=tile, wire0=wire0, wire1=wire1, forward=False))

    net_to_type = {}
    for val, net_name in constant_nets.items():
        if val == 0:
            net_to_type[net_name] = PhysicalNetType.Gnd
        else:
            assert val == 1
            net_to_type[net_name] = PhysicalNetType.Vcc

    cursor = top.conn.cursor()
    for net_name in net_stubs:
        sources = []
        stubs = net_stubs[net_name]
        sources, stubs = stitch_stubs(net_stubs[net_name], cursor,
                                      site_type_pins)

        physical_netlist.add_physical_net(
            net_name=sub_cell_nets.get(net_name, net_name),
            sources=sources,
            stubs=stubs,
            net_type=net_to_type.get(net_name, PhysicalNetType.Signal))

    phys_netlist_capnp = physical_netlist.convert_to_capnp(interchange)
    write_capnp_file(phys_netlist_capnp, f_physical)

    for l in top.output_extra_tcl():
        print(l, file=f_xdc)
Esempio n. 4
0
def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--schema_dir', required=True)
    parser.add_argument('--library', default="primitives")
    parser.add_argument('device_in')
    parser.add_argument('yosys_json')
    parser.add_argument('device_out')

    args = parser.parse_args()
    interchange = Interchange(args.schema_dir)
    with open(args.device_in, 'rb') as f:
        device = read_capnp_file(interchange.device_resources_schema.Device, f)

    device = device.as_builder()

    with open(args.yosys_json) as f:
        yosys_json = json.load(f)

    prim_lib = Library(args.library)

    assert 'modules' in yosys_json, yosys_json.keys()
    for module_name, module_data in sorted(yosys_json['modules'].items(),
                                           key=lambda x: x[0]):
        # Library should only contain blackboxes
        assert module_data['attributes'].get('blackbox', 0) or \
            module_data['attributes'].get('whitebox', 0), module_name
        property_map = {}
        if 'attributes' in module_data:
            property_map.update(module_data['attributes'])
        if 'parameters' in module_data:
            property_map.update(module_data['parameters'])
        cell = Cell(module_name, property_map)

        for port_name, port_data in module_data['ports'].items():
            if port_data['direction'] == 'input':
                direction = Direction.Input
            elif port_data['direction'] == 'output':
                direction = Direction.Output
            else:
                assert port_data['direction'] == 'inout'
                direction = Direction.Inout

            property_map = {}
            if 'attributes' in port_data:
                property_map = port_data['attributes']

            offset = port_data.get('offset', 0)
            upto = port_data.get('upto', False)

            if is_bus(port_data['bits'], offset, upto):
                end = offset
                start = offset + len(port_data['bits']) - 1

                if upto:
                    start, end = end, start

                cell.add_bus_port(name=port_name,
                                  direction=direction,
                                  start=start,
                                  end=end,
                                  property_map=property_map)
            else:
                cell.add_port(name=port_name,
                              direction=direction,
                              property_map=property_map)
        prim_lib.add_cell(cell)

    libraries = {}
    libraries[args.library] = prim_lib
    # Create the netlist
    netlist = LogicalNetlist(name=args.library,
                             property_map={},
                             top_instance_name=None,
                             top_instance=None,
                             libraries=libraries)

    str_list = [s for s in device.strList]

    netlist_capnp = netlist.convert_to_capnp(interchange,
                                             indexed_strings=str_list)

    # Patch device
    device.primLibs = netlist_capnp

    if len(device.strList) != len(str_list):
        # At least 1 string was added to the list, update the strList.
        device.init('strList', len(str_list))

        for idx, s in enumerate(str_list):
            device.strList[idx] = s

    # Save patched device
    with open(args.device_out, 'wb') as f:
        write_capnp_file(device, f)
Esempio n. 5
0
def convert_yosys_json(device,
                       yosys_json,
                       top,
                       work_library='work',
                       verbose=False):
    """ Converts Yosys Netlist JSON to FPGA interchange logical netlist. """
    primitives = device.get_primitive_library()
    primitive_cells = primitives.get_master_cell_list()
    primitive_lib = {}
    for lib_name, lib in primitives.libraries.items():
        for cell in lib.cells.values():
            primitive_lib[cell.name] = lib_name

    consts = device.get_constants()

    name = top
    property_map = {}
    top_module = yosys_json['modules'][top]
    if 'attributes' in top_module:
        property_map.update(top_module['attributes'])

    top_instance_name = top
    top_instance = CellInstance(
        property_map=property_map, view='netlist', cell_name=top)

    libraries = {
        work_library: Library(work_library),
    }

    cells_used_in_top = find_all_cell_types_from_module(
        top, yosys_json['modules'], primitive_cells)
    cells_used_in_top.add(top)

    module_errors = {}
    for module_name, module_data in yosys_json['modules'].items():
        if module_name not in cells_used_in_top:
            if verbose:
                print('Skipping {} because it is an unused cell type'.format(
                    module_name))
            continue

        if module_name in primitive_cells:
            if verbose:
                print('Skipping {} because it is a library cell'.format(
                    module_name))
            continue

        convert_cell(device, module_name, module_data, work_library, libraries,
                     yosys_json['modules'], verbose, module_errors, consts)

    netlist = LogicalNetlist(
        name=name,
        property_map=property_map,
        top_instance_name=top_instance_name,
        top_instance=top_instance,
        libraries=libraries)

    required_cells = set()
    for lib_name, lib in netlist.libraries.items():
        for cell in lib.cells.values():
            for cell_instance in cell.cell_instances.values():
                required_cells.add(cell_instance.cell_name)

    required_cells -= set(netlist.get_master_cell_list().keys())

    for required_cell in sorted(required_cells):
        if required_cell not in primitive_cells:
            if required_cell in module_errors:
                raise RuntimeError(module_errors[required_cell])
            else:
                raise RuntimeError(
                    'Failed to find cell type {}'.format(required_cell))

        lib_name = primitive_lib[required_cell]
        if lib_name not in libraries:
            libraries[lib_name] = Library(lib_name)

        prim_cell = primitive_cells[required_cell]

        # Blackbox macro content, we only care about the primitive ports - the macro content is obtained from the chipdb
        prim_cell.cell_instances.clear()
        prim_cell.nets.clear()

        netlist.libraries[lib_name].add_cell(prim_cell)

    return netlist
Esempio n. 6
0
def example_logical_netlist():
    hdi_primitives = Library('hdi_primitives')

    cell = Cell('FDRE')
    cell.add_port('D', Direction.Input)
    cell.add_port('C', Direction.Input)
    cell.add_port('CE', Direction.Input)
    cell.add_port('R', Direction.Input)
    cell.add_port('Q', Direction.Output)
    hdi_primitives.add_cell(cell)

    cell = Cell('IBUF')
    cell.add_port('I', Direction.Input)
    cell.add_port('O', Direction.Output)
    hdi_primitives.add_cell(cell)

    cell = Cell('OBUF')
    cell.add_port('I', Direction.Input)
    cell.add_port('O', Direction.Output)
    hdi_primitives.add_cell(cell)

    cell = Cell('BUFG')
    cell.add_port('I', Direction.Input)
    cell.add_port('O', Direction.Output)
    hdi_primitives.add_cell(cell)

    cell = Cell('VCC')
    cell.add_port('P', Direction.Output)
    hdi_primitives.add_cell(cell)

    cell = Cell('GND')
    cell.add_port('G', Direction.Output)
    hdi_primitives.add_cell(cell)

    top = Cell('top')
    top.add_port('i', Direction.Input)
    top.add_port('clk', Direction.Input)
    top.add_port('o', Direction.Output)

    top.add_cell_instance('ibuf', 'IBUF')
    top.add_cell_instance('obuf', 'OBUF')
    top.add_cell_instance('clk_ibuf', 'IBUF')
    top.add_cell_instance('clk_buf', 'BUFG')
    top.add_cell_instance('ff', 'FDRE')
    top.add_cell_instance('VCC', 'VCC')
    top.add_cell_instance('GND', 'GND')

    top.add_net('i')
    top.connect_net_to_cell_port('i', 'i')
    top.connect_net_to_instance('i', 'ibuf', 'I')

    top.add_net('i_buf')
    top.connect_net_to_instance('i_buf', 'ibuf', 'O')
    top.connect_net_to_instance('i_buf', 'ff', 'D')

    top.add_net('o_buf')
    top.connect_net_to_instance('o_buf', 'ff', 'Q')
    top.connect_net_to_instance('o_buf', 'obuf', 'I')

    top.add_net('o')
    top.connect_net_to_instance('o', 'obuf', 'O')
    top.connect_net_to_cell_port('o', 'o')

    top.add_net('clk')
    top.connect_net_to_cell_port('clk', 'clk')
    top.connect_net_to_instance('clk', 'clk_ibuf', 'I')

    top.add_net('clk_ibuf')
    top.connect_net_to_instance('clk_ibuf', 'clk_ibuf', 'O')
    top.connect_net_to_instance('clk_ibuf', 'clk_buf', 'I')

    top.add_net('clk_buf')
    top.connect_net_to_instance('clk_buf', 'clk_buf', 'O')
    top.connect_net_to_instance('clk_buf', 'ff', 'C')

    top.add_net('GLOBAL_LOGIC1')
    top.connect_net_to_instance('GLOBAL_LOGIC1', 'VCC', 'P')
    top.connect_net_to_instance('GLOBAL_LOGIC1', 'ff', 'CE')

    top.add_net('GLOBAL_LOGIC0')
    top.connect_net_to_instance('GLOBAL_LOGIC0', 'GND', 'G')
    top.connect_net_to_instance('GLOBAL_LOGIC0', 'ff', 'R')

    work = Library('work')
    work.add_cell(top)

    logical_netlist = LogicalNetlist(name='top',
                                     top_instance_name='top',
                                     top_instance=CellInstance(
                                         cell_name='top',
                                         view='netlist',
                                         property_map={},
                                     ),
                                     property_map={},
                                     libraries={
                                         'work': work,
                                         'hdi_primitives': hdi_primitives,
                                     })

    return logical_netlist