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
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)
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