def py_execute(self, args, design): ys.log_header(design, "Coalescing $_NOT_ gates with $sop outputs\n") for module in design.selected_whole_modules_warn(): # Find all the $_NOT_ cells that are fed by $sop cells, but where the # $sop only feeds that $_NOT_ cell, remove the $_NOT_ cell, and add an # inversion attribute to the $sop cell. cell_inputs = {} # input wire name -> Set[Cell] not_inputs = {} # input wire name -> Cell sop_outputs = {} # output wire name -> Cell for cell in [cell for cell in module.selected_cells()]: inputs = get_cell_port(cell, ID_PORT_A).to_sigbit_vector() for s in inputs: if not s.is_wire(): continue # It's probably a const name = f"{s.wire.name.str()}[{s.offset}]" if name not in cell_inputs: cell_inputs[name] = set() cell_inputs[name].update([cell]) if cell.type.str() == "$_NOT_": name = f"{inputs[0].wire.name.str()}[{s.offset}]" not_inputs[name] = cell if cell.type.str() == "$sop": output = get_cell_port(cell, ID_PORT_Y).to_sigbit_vector()[0] name = f"{output.wire.name.str()}[{output.offset}]" sop_outputs[name] = cell inversions = [] # List[Tuple[$sop Cell, $_NOT_ Cell]] for wire_name, cell in not_inputs.items(): if len(cell_inputs[wire_name]) == 1: if wire_name in sop_outputs: sop = sop_outputs[wire_name] inversions.append((sop, cell)) cells_to_remove = [] for sop, not_cell in inversions: sop.set_bool_attribute(ID_ATTR_INV_OUTPUT, True) # Replace the output wire in the $sop with the output wire from the $_NOT_. sop.setPort(ID_PORT_Y, get_cell_port(not_cell, ID_PORT_Y)) cells_to_remove.append(not_cell) for cell in cells_to_remove: module.remove(cell) ys.log( f"Coalesced {len(cells_to_remove)} $_NOT_ cells with $sop cells.\n" )
def py_execute(self, args, design): ys.log_header(design, "Replacing $sop cells equivalent to $_NOT_\n") for module in design.selected_whole_modules_warn(): cells_to_remove = [] for cell in [ cell for cell in module.selected_cells() if cell.type.str() == "$sop" ]: sop_depth = cell.parameters.get( ys.IdString("\\DEPTH")).as_int() sop_width = cell.parameters.get( ys.IdString("\\WIDTH")).as_int() sop_table = cell.parameters.get( ys.IdString("\\TABLE")).as_int() sop_input = get_cell_port(cell, ID_PORT_A) sop_output = get_cell_port(cell, ID_PORT_Y) if sop_depth != 1 or sop_width != 1 or sop_table != 1: continue not_cell = module.addCell( module.uniquify(ys.IdString("$notsop")), ys.IdString("$_NOT_")) not_cell.setPort(ys.IdString("\\A"), sop_input) not_cell.setPort(ID_PORT_Y, sop_output) ys.log( f"$sop cell {cell.name.str()} replaced with $_NOT_ cell {not_cell.name.str()}\n" ) cells_to_remove.append(cell) for cell in cells_to_remove: module.remove(cell) ys.log( f"Replaced {len(cells_to_remove)} $sop cells with $_NOT_ cells.\n" )
def py_clear_flags(self): ys.log("Clear Flags - CellStatsPass\n")
def py_help(self): ys.log("This pass uses the matplotlib library to display cell stats\n")
def py_help(self): ys.log("Writes a JED file.\n")
def py_help(self): ys.log("Sets UIM multiplexer and product term fuses.\n")
def py_help(self): ys.log("Allocates $__macrocell cells for $sop cells.\n")
def py_execute(self, args, design): ys.log_header(design, "Adding IO macrocells\n") for module in design.selected_whole_modules_warn(): tbufs = [ cell for cell in module.selected_cells() if cell.type.str() == "$_TBUF_" ] for tbuf in tbufs: tbuf_output = get_cell_port(tbuf, ID_PORT_Y) tbuf_enable = get_cell_port(tbuf, ID_PORT_E) print( f"TBUF {sigspec_str(tbuf_output)}, enable = {sigspec_str(tbuf_enable)}" ) print(f" -- Number of TBUFS: {len(tbufs)}") for wire in module.wires_.values(): print( f"Wire {wire.name.str()} offset {wire.start_offset} port_id {wire.port_id} input {wire.port_input} output {wire.port_output}" ) output_wires = [ wire for wire in module.wires_.values() if wire.port_output ] print(f" -- Number of output wires (ports): {len(output_wires)}") i_wires = [ wire for wire in module.wires_.values() if self.is_input_pin(wire) ] o_wires = [ wire for wire in module.wires_.values() if self.is_output_pin(wire) ] print(f" -- Input wires: {len(i_wires)}") print(f" -- Output wires: {len(o_wires)}") i_ports = [] o_ports = [] for conn_from, conn_to in module.connections_: if not conn_from.is_wire() or not conn_to.is_wire(): continue conn_from_name = conn_from.as_wire().name.str() conn_to_name = conn_to.as_wire().name.str() if self.is_input_pin(conn_from.as_wire()): i_ports.append(conn_to_name) if self.is_output_pin(conn_from.as_wire()): o_ports.append(conn_to_name) if self.is_input_pin(conn_to.as_wire()): i_ports.append(conn_from_name) if self.is_output_pin(conn_to.as_wire()): o_ports.append(conn_from_name) print(f" -- Input ports: {i_ports}") print(f" -- Output ports: {o_ports}") for wire in output_wires: n = wire.name.str().split("_")[1] mc = self.device.gpiobuf_to_mc[int(n)] new_cell = module.addCell( module.uniquify(ys.IdString(f"$mc{n}")), ys.IdString("$__macrocell")) new_cell.set_string_attribute(ys.IdString("$gpiobuf"), n) new_cell.set_string_attribute(ID_ATTR_MC, mc) if wire.name.str() in i_ports: new_cell.setPort(ID_PORT_PAD, ys.SigSpec(wire)) new_cell.setPort(ID_PORT_IO_EN, ys.SigSpec(ys.State.S0)) new_cell.set_string_attribute(ID_ATTR_IO_FUNC, "input") self.device.input_mcs[wire.name.str()] = mc self.device.input_sigs[wire.name.str()] = f"M{mc}_PAD" else: new_cell.setPort(ID_PORT_IO_A, ys.SigSpec(wire)) new_cell.setPort(ID_PORT_IO_EN, ys.SigSpec(ys.State.S1)) new_cell.set_string_attribute(ID_ATTR_IO_FUNC, "output") self.device.input_mcs[wire.name.str()] = mc # Remove this MC from the list of available MCs self.device.available_mcs.remove(mc) ys.log( f"Added {len(output_wires)} macrocells ({len(i_ports)} inputs, {len(o_ports)} outputs)\n" )
def py_help(self): ys.log("Adds $__macrocell cells for inputs and outputs.\n")
def py_execute(self, args, design): ys.log_header(design, "Splitting overly large $sop cells\n") threshold = 5 if len(args) > 1: threshold = int(args[1]) new_cells = 0 cells_to_remove = [] for module in design.selected_whole_modules_warn(): sops = [ cell for cell in module.selected_cells() if cell.type.str() == "$sop" ] for sop in sops: # Depth is the number of product terms sop_depth = sop.parameters.get(ID_PARAM_DEPTH).as_int() if sop_depth <= threshold: continue cells_to_remove.append(sop) # Width is the number of inputs sop_width = sop.parameters.get(ID_PARAM_WIDTH) # for (int i = 0; i < sop_depth; i++) { # for (int j = 0; j < sop_width; j++) # { # if (sop_table[2 * (i * sop_width + j) + 0]) # { # and_in_comp.insert(sop_inputs[j]); # } # if (sop_table[2 * (i * sop_width + j) + 1]) # { # and_in_true.insert(sop_inputs[j]); # } # } sop_table = sop.parameters.get(ID_PARAM_TABLE) sop_inputs = get_cell_port(sop, ID_PORT_A) sop_output = get_cell_port(sop, ID_PORT_Y) table = sop_table.as_string() product_terms = [] print(f"Table is {table}") for i in range(sop_depth): offset = 2 * i * sop_width.as_int() size = 2 * sop_width.as_int() product_term = table[offset:offset + size] product_terms.append(product_term) print(f" Term {i}: {product_term}") # Rather than create $sops with 5 inputs each and then ORing # them together in another $sop, we ripple $sops together. new_output = None offset = 0 while offset < sop_depth: is_first = offset == 0 is_last = sop_depth - offset < threshold # We can fit N inputs into the first $sop, but only # N-1 inputs into the subsequent $sops because they # ripple their outputs. if is_first: new_inputs = sop_inputs new_product_terms = product_terms[:threshold] offset += threshold else: new_inputs = sop_inputs new_product_terms = [ f"{p}00" for p in product_terms[offset:offset + threshold - 1] ] additional_product_term = f"{'00'*sop_inputs.size()}01" new_inputs.append(new_output) new_product_terms.append(additional_product_term) offset += threshold - 1 new_width = new_inputs.size() new_depth = len(new_product_terms) print(f"New width: {new_width}") print(f"New depth: {new_depth}") print(f"New product terms: {''.join(new_product_terms)}") new_table = ys.Const.from_string( "".join(new_product_terms)) print(f"new inputs for sop: {sigspec_str(new_inputs)}") if is_last: new_output = sop_output print(f"final output: {sigspec_str(new_output)}") else: new_output = ys.SigSpec( module.addWire( module.uniquify( ys.IdString("$smaller_sop_out")))) print(f"new ripple output: {sigspec_str(new_output)}") table = new_table.as_string() print(f"New table: {table}") for i in range(new_depth): off = 2 * i * new_width size = 2 * new_width product_term = table[off:off + size] print(f" Term {i}: {product_term}") new_cell = module.addCell( module.uniquify(ys.IdString("$smaller_sop")), ys.IdString("$sop")) new_cell.setPort(ys.IdString("\\A"), new_inputs) new_cell.setPort(ID_PORT_Y, new_output) new_cell.setParam(ys.IdString("\\TABLE"), new_table) new_cell.setParam(ys.IdString("\\DEPTH"), ys.Const(new_depth, 32)) new_cell.setParam(ys.IdString("\\WIDTH"), ys.Const(new_width, 32)) new_cells += 1 for cell in cells_to_remove: module.remove(cell) ys.log( f"Removed {len(cells_to_remove)} overly large $sop cells and replaced with {new_cells} cells.\n" )
def py_help(self): ys.log("Splits $sop cells with more product terms than desired.\n") ys.log(" Usage: split_large_sop [threshold]\n") ys.log(" The default threshold is 5.\n")
def py_help(self): ys.log("Coalesces $_NOT_ gates with $sop outputs\n")
def py_help(self): ys.log("Replaces $sop cells that implement not with $_NOT_ cells\n")