Ejemplo n.º 1
0
    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"
Ejemplo n.º 2
0
 def py_execute(self, args, design):
     ys.log_header(design, "Plotting cell stats\n")
     cell_stats = {}
     for module in design.selected_whole_modules_warn():
         for cell in module.selected_cells():
             if cell.type.str() in cell_stats:
                 cell_stats[cell.type.str()] += 1
             else:
                 cell_stats[cell.type.str()] = 1
     plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
     plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
     plt.show()
Ejemplo n.º 3
0
 def py_execute(self, args, design):
     ys.log_header(design, "Plotting cell stats\n")
     cell_stats = {}
     for module in design.selected_whole_modules_warn():
         for cell in module.selected_cells():
             if cell.type.str() in cell_stats:
                 cell_stats[cell.type.str()] += 1
             else:
                 cell_stats[cell.type.str()] = 1
     plt.bar(range(len(cell_stats)),
             height=list(cell_stats.values()),
             align='center')
     plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
     plt.show()
Ejemplo n.º 4
0
    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"
            )
Ejemplo n.º 5
0
 def py_execute(self, args, design):
     ys.log_header(design, f"Writing JED file {args[1]}\n")
     file = args[1]
     for module in design.selected_whole_modules_warn():
         mcs = [
             cell for cell in module.selected_cells()
             if cell.type.str() == "$__macrocell"
         ]
         for mc in mcs:
             self.set_fuses_for_mc(mc)
     fuse_attrs = {
         k.str()[6:]: v.decode_string()
         for k, v in module.attributes.items()
         if k.str().startswith("$fuse.")
     }
     for k, v in fuse_attrs.items():
         self.device.set_fuse(k, v)
     self.device.commit_fuses(args[1])
Ejemplo n.º 6
0
    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"
            )
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
 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"
         )
Ejemplo n.º 9
0
    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"
            )