Removing driverless Instances and Wires¶
Netlist Carpentry is capable of removing instances and wires without driver. This happens if all inputs of an instance are unconnected. In such case, the instance (or pontentially the whole instance subbranch) can be removed and the downstream load inputs will be unconnected as well. Consider the following graph:
flowchart LR
In1([Input])
Gate1{{NOT Gate 1}}
Gate2{{NOT Gate 2}}
Gate3{{AND Gate 3}}
Gate4{{AND Gate 4}}
Out1([Output])
In1 --> Gate2
Gate1 --> Gate3
Gate2 --> Gate4
Gate3 --> Gate4
Gate4 --> Out1
The second port of the AND Gate 4 (the lower arm) is driven by AND Gate 3, which itself only has one input port connected, to NOT Gate 1, which does not have a driver.
The whole "instance chain" is thus undriven.
This can be simplified down to AND Gate 4, which is the "root" of this branch and has at least one input connected to a module port.
Accordingly, both NOT Gate 1 and AND Gate 3 could be removed, and the port of AND Gate 4, where previously the output of AND Gate 3 was, will now be unconnected.
As a result, the optimized structure would look like this:
flowchart LR
In1([Input])
Gate2{{NOT Gate 2}}
Gate4{{AND Gate 4}}
Out1([Output])
In1 --> Gate2
Gate2 --> Gate4
Gate4 --> Out1
Execute the cell below to set up a structure similar to the first graph.
from netlist_carpentry import Circuit, Direction
from netlist_carpentry.utils.gate_lib_factory import and_gate, not_gate
c = Circuit(name="c")
m = c.create_module("m")
in1 = m.create_port("Input", Direction.IN)
out = m.create_port("Output", Direction.OUT)
not1 = not_gate(m, "not1")
not2 = not_gate(m, "not2", A=in1)
and3 = and_gate(m, "and3", A=not1.ports["Y"])
and4 = and_gate(m, "and4", A=not2.ports["Y"], B=and3.ports["Y"], Y=out)
m.show(interactive=True).run()
Now to start the optimization algorithm to remove the driverless instances and wires, there are two approaches.
The easiest is to execute Module.optimize, which runs a bunch of optimizations, with the removal of driverless elements being one of them.
However, to only execute the optimization task to remove driverless instances and wires, a corresponding function can be imported directly from the corresponding module routines.opt.driverless, as shown in the cell below.
The method opt_driverless will recursively remove all driverless wires and instances until no driverless wires and instances are left.
After the optimization, the shown graph will only contain two instances (the NOT Gate 2 and the AND Gate 4), and the input and output ports.
from netlist_carpentry.routines.opt import opt_driverless
print(f"Module {m.name} has these instances: {', '.join(inst for inst in m.instances)}")
for iname, inst in m.instances.items():
if all(p.is_unconnected for p in inst.input_ports):
print(f">>> Instance {iname} has no drivers and will be removed!")
else:
# Instance "and3" also has a driver, but its driver instance itself is driverless!
# So after the first optimization iteration, "and3" will become driverless as well!
print(f">>> Instance {iname} has drivers!")
opt_driverless(m)
m.show(interactive=True).run()
print(f"Module {m.name} now has these instances: {', '.join(inst for inst in m.instances)}")