def make_dffs(rst_types): for rst_type in rst_types: cell = Cell(f"DFF{rst_type}") cell.add_port("D", Direction.Input) cell.add_port(rst_type, Direction.Input) cell.add_port("C", Direction.Input) cell.add_port("Q", Direction.Output) library.add_cell(cell)
def make_luts(max_size): for lut_size in range(1, max_size + 1): name = f"LUT{lut_size}" init = f"{2 ** lut_size}'h0" cell = Cell(name=name, property_map={"INIT": init}) print(name, init) in_ports = list() for port in range(lut_size): port_name = f"I{port}" cell.add_port(port_name, Direction.Input) in_ports.append(port_name) cell.add_port("O", Direction.Output) library.add_cell(cell) param = Parameter("INIT", ParameterFormat.VERILOG_HEX, init) self.device.add_parameter(name, param) self.device.add_lut_cell(name, in_ports, 'INIT')
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 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)
def make_primitives_library(self): # Primitives library library = Library("primitives") self.device.cell_libraries["primitives"] = library cell = Cell("LUT") cell.add_port("A0", Direction.Input) cell.add_port("A1", Direction.Input) cell.add_port("A2", Direction.Input) cell.add_port("A3", Direction.Input) cell.add_port("O", Direction.Output) library.add_cell(cell) cell = Cell("DFF") cell.add_port("D", Direction.Input) cell.add_port("R", Direction.Input) cell.add_port("C", Direction.Input) cell.add_port("Q", Direction.Output) library.add_cell(cell) cell = Cell("IB") cell.add_port("I", Direction.Output) cell.add_port("P", Direction.Input) library.add_cell(cell) cell = Cell("OB") cell.add_port("O", Direction.Input) cell.add_port("P", Direction.Output) library.add_cell(cell) cell = Cell("VCC") cell.add_port("V", Direction.Output) library.add_cell(cell) cell = Cell("GND") cell.add_port("G", Direction.Output) library.add_cell(cell) # Macros library library = Library("macros") self.device.cell_libraries["macros"] = library
def convert_cell(device, module_name, module_data, library, libraries, modules, verbose, errors, consts): for cell_name, cell_data in module_data['cells'].items(): # Don't import modules that are missing children, they likely aren't # important. if cell_data['type'] not in modules: errors[ module_name] = 'Failed to import cell type {} because it has a undefined cell {}'.format( module_name, cell_data['type']) return property_map = {} if 'attributes' in module_data: property_map = module_data['attributes'] cell = Cell(module_name, property_map) libraries[library].add_cell(cell) net_duplicate_names = {} def add_net(net_name, net_data, track_duplicates=False): property_map = {} if 'attributes' in net_data: property_map.update(net_data['attributes']) offset = net_data.get('offset', 0) upto = net_data.get('upto', 0) if is_bus(net_data['bits'], offset, upto): for bit_index, bit in interp_yosys_net(net_data['bits'], offset, upto): name = '{}[{}]'.format(net_name, bit_index) cell.add_net(name, property_map) if bit == '0': cell.connect_net_to_instance( name, make_gnd_cell(cell, module_data, consts), consts.GND_PORT) elif bit == '1': cell.connect_net_to_instance( name, make_vcc_cell(cell, module_data, consts), consts.VCC_PORT) else: assert isinstance(bit, int), bit if bit in net_bits: if track_duplicates: net_duplicate_names[net_bits[bit]].append(name) continue assert bit not in net_bits, (module_name, bit, net_bits.keys()) net_bits[bit] = name net_duplicate_names[name] = [] else: cell.add_net(net_name, property_map) bit = net_data['bits'][0] if bit == '0': cell.connect_net_to_instance( net_name, make_gnd_cell(cell, module_data, consts), consts.GND_PORT) elif bit == '1': cell.connect_net_to_instance( net_name, make_vcc_cell(cell, module_data, consts), consts.VCC_PORT) else: assert isinstance(bit, int) if bit in net_bits: if track_duplicates: net_duplicate_names[net_bits[bit]].append(net_name) return assert bit not in net_bits, (module_name, bit, net_bits.keys()) net_bits[bit] = net_name net_duplicate_names[net_name] = [] net_bits = {} for net_name, net_data in module_data['netnames'].items(): if net_name in module_data['ports']: continue if net_data.get('hide_name', 0): continue add_net(net_name, net_data, track_duplicates=False) for net_name, net_data in module_data['netnames'].items(): if net_name not in module_data['ports']: continue add_net(net_name, net_data, track_duplicates=True) for net_name, net_data in module_data['netnames'].items(): if net_data.get('hide_name', 0) != 1: continue add_net(net_name, net_data, track_duplicates=False) vcc_cell = None vcc_net = None gnd_cell = None gnd_net = None def get_net(bit): if bit == '0': nonlocal gnd_cell nonlocal gnd_net if gnd_cell is None: gnd_cell = make_gnd_cell(cell, module_data, consts) net_names = set(cell.nets.keys()) | set( module_data['netnames'].keys()) gnd_net = create_unique_name(net_names, '$__gnd_net') cell.add_net(gnd_net) cell.connect_net_to_instance(gnd_net, gnd_cell, consts.GND_PORT) return gnd_net elif bit == '1': nonlocal vcc_cell nonlocal vcc_net if vcc_cell is None: vcc_cell = make_vcc_cell(cell, module_data, consts) net_names = set(cell.nets.keys()) | set( module_data['netnames'].keys()) vcc_net = create_unique_name(net_names, '$__vcc_net') cell.add_net(vcc_net) cell.connect_net_to_instance(vcc_net, vcc_cell, consts.VCC_PORT) return vcc_net else: assert isinstance(bit, int) return net_bits[bit] 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) for bit_index, bit in interp_yosys_net(port_data['bits'], offset, upto): cell.connect_net_to_cell_port( get_net(bit), port_name, bit_index) else: cell.add_port( name=port_name, direction=direction, property_map=property_map) cell.connect_net_to_cell_port( get_net(port_data['bits'][0]), port_name) for cell_name, cell_data in module_data['cells'].items(): property_map = {} if 'attributes' in cell_data: property_map.update(cell_data['attributes']) if 'parameters' in cell_data: property_map.update(cell_data['parameters']) convert_parameters(device, cell_name, cell_data['type'], property_map) # Set default parameters if not already set from Yosys. device.add_default_parameters(cell_data['type'], property_map) cell.add_cell_instance( name=cell_name, cell_name=cell_data['type'], property_map=property_map) cell_type = modules[cell_data['type']] for port_name, bits in cell_data['connections'].items(): port = cell_type['ports'][port_name] offset = port.get('offset', 0) upto = port.get('upto', False) if is_bus(bits, offset, upto): for bit_index, bit in interp_yosys_net(bits, offset, upto): cell.connect_net_to_instance( get_net(bit), cell_name, port_name, bit_index) else: cell.connect_net_to_instance( get_net(bits[0]), cell_name, port_name)
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
def make_primitives_library(self): # Primitives library library = Library("primitives") self.device.cell_libraries["primitives"] = library def make_luts(max_size): for lut_size in range(1, max_size + 1): name = f"LUT{lut_size}" init = f"{2 ** lut_size}'h0" cell = Cell(name=name, property_map={"INIT": init}) print(name, init) in_ports = list() for port in range(lut_size): port_name = f"I{port}" cell.add_port(port_name, Direction.Input) in_ports.append(port_name) cell.add_port("O", Direction.Output) library.add_cell(cell) param = Parameter("INIT", ParameterFormat.VERILOG_HEX, init) self.device.add_parameter(name, param) self.device.add_lut_cell(name, in_ports, 'INIT') make_luts(4) def make_dffs(rst_types): for rst_type in rst_types: cell = Cell(f"DFF{rst_type}") cell.add_port("D", Direction.Input) cell.add_port(rst_type, Direction.Input) cell.add_port("C", Direction.Input) cell.add_port("Q", Direction.Output) library.add_cell(cell) make_dffs(["S", "R"]) cell = Cell("IB") cell.add_port("I", Direction.Output) cell.add_port("P", Direction.Input) library.add_cell(cell) cell = Cell("OB") cell.add_port("O", Direction.Input) cell.add_port("P", Direction.Output) library.add_cell(cell) cell = Cell("VCC") cell.add_port("V", Direction.Output) library.add_cell(cell) cell = Cell("GND") cell.add_port("G", Direction.Output) library.add_cell(cell) # Macros library library = Library("macros") self.device.cell_libraries["macros"] = library