def py_execute(self, args, design): ys.log_header(design, "Allocating macrocells for SOPs\n") for module in design.selected_whole_modules_warn(): sops = [ cell for cell in module.selected_cells() if cell.type.str() == "$sop" ] mcs = [ cell for cell in module.selected_cells() if cell.type.str() == "$__macrocell" ] print(mcs) mcs_by_output = { get_cell_port_wire_name(mc, ID_PORT_IO_A): mc for mc in mcs if io_func_is(mc, "output") } print(mcs_by_output) for sop in sops: sop_output = get_cell_port_wire_name(sop, ID_PORT_Y) if sop_output in mcs_by_output: new_cell = mcs_by_output[sop_output] print( f"Allocate mc {new_cell.get_string_attribute(ys.IdString('$mc'))} for sop {sop.name.str()} (output wire {sop_output})" ) else: if len(self.device.available_mcs) == 0: raise SystemExit("Ran out of macrocells") mc = self.device.available_mcs[-1] self.device.available_mcs = self.device.available_mcs[:-1] new_cell = module.addCell( module.uniquify(ys.IdString(f"$mc{mc}")), ys.IdString("$__macrocell")) new_cell.set_string_attribute(ID_ATTR_MC, mc) new_cell.set_string_attribute(ID_ATTR_IO_FUNC, "input") new_cell.set_string_attribute(ID_ATTR_LOGIC_FUNC, "sop") new_cell.set_string_attribute(ID_ATTR_SOP, sop.name.str()) new_cell.setPort(ID_PORT_XT, get_cell_port(sop, ID_PORT_Y)) new_cell.setPort(ID_PORT_FB, get_cell_port(sop, ID_PORT_Y)) new_cell.setPort(ID_PORT_A, get_cell_port(sop, ID_PORT_A)) new_cell.setParam(ID_PARAM_DEPTH, get_cell_param(sop, ID_PARAM_DEPTH)) new_cell.setParam(ID_PARAM_WIDTH, get_cell_param(sop, ID_PARAM_WIDTH)) new_cell.setParam(ID_PARAM_TABLE, get_cell_param(sop, ID_PARAM_TABLE)) new_cell.setParam( ID_PARAM_ST_INV, ys.Const( ys.State.S1 if sop.has_attribute(ID_ATTR_INV_OUTPUT) else ys.State.S0, 1)) self.device.input_sigs[get_cell_port_wire_name( sop, ID_PORT_Y )] = f"MC{new_cell.get_string_attribute(ID_ATTR_MC)}_FB"
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_execute(self, args, design): ys.log_header(design, "Setting UIM multiplexer and product term fuses\n") for module in design.selected_whole_modules_warn(): mcs = [ cell for cell in module.selected_cells() if cell.type.str() == "$__macrocell" ] mcs = [mc for mc in mcs if mc.has_attribute(ID_ATTR_LOGIC_FUNC)] mcs = [ mc for mc in mcs if mc.get_string_attribute(ID_ATTR_LOGIC_FUNC) == "sop" ] mc_num_to_cell = { mc.get_string_attribute(ID_ATTR_MC): mc for mc in mcs } block_to_mcs = {block: [] for block in self.device.blocks} for mc in mc_num_to_cell.keys(): block_to_mcs[self.device.mc_to_block[mc]].append(mc) print(block_to_mcs) print(self.device.input_sigs) for blk, mcs in block_to_mcs.items(): print(f"Constructing set of signals in block {blk}") # Construct the set of needed signals. sigs = set() for mc in mcs: inputs = get_cell_port(mc_num_to_cell[mc], ID_PORT_A).to_sigbit_vector() sigs.update(set(i.wire.name.str() for i in inputs)) # Convert to ordered array of signames sigs = [self.device.input_sigs[s] for s in sigs] if len(sigs) == 0: print(f"No used signals in block {blk}") continue print(f"Used signals in block {blk}: {sigs}") # Construct the set of candidate switches for those signals. candidate_switches = set() for sig in sigs: candidate_switches.update( set(s for s in self.device.sig_to_uim[blk][sig])) # Convert to ordered array candidate_switches = [s for s in candidate_switches] print( f"Candidate switches in block {blk}: {candidate_switches}") # Construct the cost matrix. We assign an different cost per candidate # switch to help the algorithm be stable. matrix = [[DISALLOWED for _ in range(len(candidate_switches))] for _ in range(len(sigs))] for row, sig in enumerate(sigs): cost = 1 for candidate_switch in self.device.sig_to_uim[blk][sig]: col = candidate_switches.index(candidate_switch) matrix[row][col] = cost cost += 1 cost_matrix = make_cost_matrix( matrix, lambda cost: cost if cost != DISALLOWED else DISALLOWED) # Assign signals to switches. m = Munkres() indexes = m.compute(cost_matrix) sig_to_switch = {} print("Setting UIM fuses") for r, c in indexes: v = matrix[r][c] print(f"Set {candidate_switches[c]} to {sigs[r]}") module.set_string_attribute( ys.IdString(f"$fuse.{candidate_switches[c]}"), sigs[r]) sig_to_switch[sigs[r]] = candidate_switches[c] print(f"Setting product term fuses:") for mc in mcs: cell = mc_num_to_cell[mc] if not cell.hasParam(ID_PARAM_TABLE): continue depth = cell.parameters.get(ID_PARAM_DEPTH).as_int() # Width is the number of inputs width = cell.parameters.get(ID_PARAM_WIDTH).as_int() # 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]); # } # } table = cell.parameters.get(ID_PARAM_TABLE).as_string() inputs = get_cell_port(cell, ID_PORT_A).to_sigbit_vector() product_terms = [] print(f" Table for mc {mc} is {table}") for i in range(depth): offset = 2 * i * width size = 2 * width product_term = table[offset:offset + size] product_terms.append(product_term) for i, term in enumerate(product_terms): print(f" Term {i}: {term}") fuses = [] for j in range(0, len(term) // 2): offset = 2 * j pattern = term[offset:offset + 2] sig = inputs[j].wire.name.str() signame = self.device.input_sigs[sig] uim = sig_to_switch[signame] print( f" Input {sig} ({signame}) is {pattern}") if pattern[0] == "1": # negative fuses.append(f"{uim}_N") if pattern[1] == "1": # positive fuses.append(f"{uim}_P") cell.set_string_attribute( ys.IdString(f"$fuse.MC{mc}.PT{i+1}"), ",".join(fuses))
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_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" )
from nmigen.build import Platform, Resource, Pins, Attrs from nmigen.build.run import BuildPlan from nmigen.back import rtlil from pass_register import * # !!!!!!!!!!!!!!!!!!!!!NOTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # You must have bitarray, munkres, and nmigen installed via pip3, and also you must # have checked out the prjbureau repo (https://github.com/whitequark/prjbureau) # and added the repo to your PYTHONPATH. # prjbureau stuff: from util.fuseconv import read_jed, write_jed from util.fusecli import FuseTool, parse_filters ID_ATTR_IO_FUNC = ys.IdString("$io_func") ID_PORT_A = ys.IdString("\\A") ID_PORT_E = ys.IdString("\\E") ID_PORT_Y = ys.IdString("\\Y") ID_PORT_PAD = ys.IdString("\\PAD") ID_PORT_IO_A = ys.IdString("\\IO.A") ID_PORT_IO_EN = ys.IdString("\\IO.EN") ID_PORT_XT = ys.IdString("\\XT") ID_PORT_FB = ys.IdString("\\FB") ID_PARAM_TABLE = ys.IdString("\\TABLE") ID_PARAM_DEPTH = ys.IdString("\\DEPTH") ID_PARAM_WIDTH = ys.IdString("\\WIDTH") ID_PARAM_ST_INV = ys.IdString("\\ST.INV") ID_ATTR_INV_OUTPUT = ys.IdString("$inv_output") ID_ATTR_MC = ys.IdString("$mc") ID_ATTR_LOGIC_FUNC = ys.IdString("$logic_func")