| #!/usr/bin/env python3 | |
| """ | |
| Utilities for cleaning circuit netlists | |
| """ | |
| import logging | |
| # ============================================================================= | |
| def absorb_buffer_luts(netlist, outputs=False): | |
| """ | |
| Performs downstream absorbtion of buffer LUTs. All buffer LUTs are absorbed | |
| except for those that drive top-level output ports not to change output | |
| net names. | |
| Returns a net map that defines the final net remapping after some nets | |
| got absorbed downstream. | |
| """ | |
| INP_PORT = "lut_in[0]" | |
| OUT_PORT = "lut_out" | |
| def is_buffer_lut(cell): | |
| """ | |
| Returns True when a cell is a buffer LUT to be absorbed. | |
| """ | |
| # A pass-through LUT | |
| if cell.type == "$lut" and cell.init == [0, 1]: | |
| # Get input and output nets | |
| assert INP_PORT in cell.ports, cell | |
| net_inp = cell.ports[INP_PORT] | |
| assert OUT_PORT in cell.ports, cell | |
| net_out = cell.ports[OUT_PORT] | |
| # Must not be driving any top-level outputs unless explicitly | |
| # allowed | |
| if (net_inp in netlist.inputs | |
| or net_out in netlist.outputs) and not outputs: | |
| return False | |
| return True | |
| return False | |
| # Identify LUT buffers | |
| buffers = { | |
| key: cell | |
| for key, cell in netlist.cells.items() | |
| if is_buffer_lut(cell) | |
| } | |
| # Merge them downstream | |
| net_map = {} | |
| for cell in buffers.values(): | |
| # Get input and output nets | |
| assert INP_PORT in cell.ports, cell | |
| net_inp = cell.ports[INP_PORT] | |
| assert OUT_PORT in cell.ports, cell | |
| net_out = cell.ports[OUT_PORT] | |
| # Cannot be a buffer connecting an input to an output directly | |
| if net_out in netlist.outputs and net_inp in netlist.inputs: | |
| continue | |
| # This cell drives a top-level output directly. Change input nets and | |
| # leave the output one | |
| if net_out in netlist.outputs: | |
| # Replace the output net in all cells with the input one | |
| for c in netlist.cells.values(): | |
| for port, net in c.ports.items(): | |
| if net == net_inp: | |
| c.ports[port] = net_out | |
| # Update net map | |
| for net in net_map: | |
| if net_map[net] == net_inp: | |
| net_map[net] = net_out | |
| net_map[net_inp] = net_out | |
| # A regular buffer | |
| else: | |
| # Replace the output net in all cells with the input one | |
| for c in netlist.cells.values(): | |
| for port, net in c.ports.items(): | |
| if net == net_out: | |
| c.ports[port] = net_inp | |
| # Update net map | |
| for net in net_map: | |
| if net_map[net] == net_out: | |
| net_map[net] = net_inp | |
| net_map[net_out] = net_inp | |
| # Remove the cell | |
| del netlist.cells[cell.name] | |
| logging.debug(" Absorbed {} buffer LUTs".format(len(buffers))) | |
| return net_map | |
| def sweep_dangling_cells(netlist): | |
| # TODO: | |
| pass |