Modifying a given Circuit¶
Here are some examples on how modules can be modified. This is shown exemplarily for instances, ports and wires.
Adding and removing objects¶
- Ports can be added in two different ways, as shown in the cell below.
- Ports can either be created via
Module.create_port, which takes the attributes of the port as parameters, creates the port instance and adds it to the module. - Alternatively, already existing ports can be added to a module via
Module.add_port.
In [ ]:
Copied!
import netlist_carpentry
from netlist_carpentry import Direction, Port
circuit = netlist_carpentry.read("files/simpleAdder.v", top="simpleAdder")
top_module = circuit.top
# Version 1
top_module.create_port("new_port", direction=Direction.IN)
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
new_port2 = Port(name="new_port2", direction=Direction.OUT, module_or_instance=None)
top_module.add_port(new_port2)
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
import netlist_carpentry
from netlist_carpentry import Direction, Port
circuit = netlist_carpentry.read("files/simpleAdder.v", top="simpleAdder")
top_module = circuit.top
# Version 1
top_module.create_port("new_port", direction=Direction.IN)
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
new_port2 = Port(name="new_port2", direction=Direction.OUT, module_or_instance=None)
top_module.add_port(new_port2)
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
- Ports can also be removed via
Module.remove_port, which takes the name of the port to remove. - Execute the cell below to remove the secondly added port.
- All three methods
Module.create_port,Module.add_portandModule.remove_portreturn a Boolean value indicating whether the operation was successful.
In [ ]:
Copied!
from netlist_carpentry.core.exceptions import ObjectNotFoundError
print("Removing the port new_port2...")
is_removed = top_module.remove_port("new_port2")
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
try:
top_module.remove_port("new_port2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
from netlist_carpentry.core.exceptions import ObjectNotFoundError
print("Removing the port new_port2...")
is_removed = top_module.remove_port("new_port2")
print(f"The module now has these ports: {', '.join(top_module.ports.keys())}!")
try:
top_module.remove_port("new_port2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
- Adding and removing wires works similarly
- Execute the cell below to add two wires, one via
Module.create_wireand one viaModule.add_wire.
In [ ]:
Copied!
from netlist_carpentry import Wire
top_module.create_wire("new_wire")
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
new_wire2 = Wire(name="new_wire2", module=top_module)
top_module.add_wire(new_wire2)
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
from netlist_carpentry import Wire
top_module.create_wire("new_wire")
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
new_wire2 = Wire(name="new_wire2", module=top_module)
top_module.add_wire(new_wire2)
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
- Wires can be removed the same way ports can also be removed.
In [ ]:
Copied!
print("Removing the wire new_wire2...")
is_removed = top_module.remove_wire("new_wire2")
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
try:
top_module.remove_wire("new_wire2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
print("Removing the wire new_wire2...")
is_removed = top_module.remove_wire("new_wire2")
print(f"The module now has these wires: {', '.join(top_module.wires.keys())}!")
try:
top_module.remove_wire("new_wire2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
- Finally, instances can be added and removed again in a similar manner using
Module.create_instance,Module.add_instanceandModule.remove_instance. - Execute the cell below to see examples.
In [ ]:
Copied!
from netlist_carpentry import Instance, Module
top_module.create_instance(Module(name="some_module"),"new_instance")
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
new_instance2 = Instance(name="new_instance2", instance_type="some_instance_type", module=top_module)
top_module.add_instance(new_instance2)
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
from netlist_carpentry import Instance, Module
top_module.create_instance(Module(name="some_module"),"new_instance")
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
new_instance2 = Instance(name="new_instance2", instance_type="some_instance_type", module=top_module)
top_module.add_instance(new_instance2)
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
- Instances can also be removed, as shown below.
In [ ]:
Copied!
print("Removing the instance new_instance2...")
is_removed = top_module.remove_instance("new_instance2")
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
try:
top_module.remove_instance("new_instance2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
print("Removing the instance new_instance2...")
is_removed = top_module.remove_instance("new_instance2")
print(f"The module now has these instances: {', '.join(top_module.instances.keys())}!")
try:
top_module.remove_instance("new_instance2")
except ObjectNotFoundError as e:
print(f"Catched an ObjectNotFoundError: {e}")
Modifying existing objects¶
- Let's modify the previously added wire
new_wireto have a width of 8 bit. - Execute the cell below to increase the width of the wire using the method
Wire.create_wire_segment.
Info: The method Wire.create_wire_segment only creates a wire segment for a given index, if no wire segment for this index was present previously.
Warning: If the index provided to Wire.create_wire_segment is not consecutive to the previously highest index of the wire (i.e. the width is increased by more than one), a gap will appear between the old and the newly added wire segments in regards to index assignments.
For example, if a wire consists of only one wire segment at index 0, and Wire.create_wire_segment is called with index 3, then there will be no wire segment at index 1 and 2.
This results in missing segments in between, which will be transformed into Z values (floating wire segments, whenever they are used in the design) during Verilog write-out.
To prevent this, either create the wire segments consecutively (by setting the index to Wire.width+1) or fill in the missing segments later.
In [ ]:
Copied!
from netlist_carpentry.core.exceptions import IdentifierConflictError
new_wire = top_module.wires["new_wire"]
print(f"The wire has currently a width of {new_wire.width} bit.")
for idx in range(1, 8):
new_wire.create_wire_segment(idx)
print(f"The wire has currently a width of {new_wire.width} bit.")
try:
new_wire.create_wire_segment(1)
except IdentifierConflictError as e:
print(f"Catched an IdentifierConflictError: {e}")
from netlist_carpentry.core.exceptions import IdentifierConflictError
new_wire = top_module.wires["new_wire"]
print(f"The wire has currently a width of {new_wire.width} bit.")
for idx in range(1, 8):
new_wire.create_wire_segment(idx)
print(f"The wire has currently a width of {new_wire.width} bit.")
try:
new_wire.create_wire_segment(1)
except IdentifierConflictError as e:
print(f"Catched an IdentifierConflictError: {e}")
- Likewise, the width of the created port
new_portcan also be changed. - Execute the cell below to change the width of the created port to 8 bit using the method
Port.create_port_segment.
Info: The method Port.create_port_segment only creates a port segment for a given index, if no port segment for this index was present previously.
Warning: If the index provided to Port.create_port_segment is not consecutive to the previously highest index of the port (i.e. the width is increased by more than one), a gap will appear between the old and the newly added port segments in regards to index assignments.
For example, if a port consists of only one port segment at index 0, and Port.create_port_segment is called with index 3, then there will be no port segment at index 1 and 2.
This results in missing segments in between, which will be transformed into Z values (floating port segments, whenever they are used in the design) during Verilog write-out.
To prevent this, either create the port segments consecutively (by setting the index to Port.width+1) or fill in the missing segments later.
In [ ]:
Copied!
new_port = top_module.ports["new_port"]
print(f"The port has currently a width of {new_port.width} bit.")
for idx in range(1, 8):
new_port.create_port_segment(idx)
print(f"The port has currently a width of {new_port.width} bit.")
try:
new_port.create_port_segment(1)
except IdentifierConflictError as e:
print(f"Catched an IdentifierConflictError: {e}")
new_port = top_module.ports["new_port"]
print(f"The port has currently a width of {new_port.width} bit.")
for idx in range(1, 8):
new_port.create_port_segment(idx)
print(f"The port has currently a width of {new_port.width} bit.")
try:
new_port.create_port_segment(1)
except IdentifierConflictError as e:
print(f"Catched an IdentifierConflictError: {e}")
- A port segment (either from a module or an instance) can be connected to a wire segment via
Module.connect. - The
Module.connectmethod takes a wire segment and a port segment and interconnects them, such that both objects know about each other. - Port Segments and Wire Segments can be accessed directly via
Port.segments[<idx>]andWire.segments[<idx>]. - Alternatively, they can be accessed via
Port[<idx>]andWire[<idx>], which does the same and is thus only a shortcut. - Execute the cell below to connect the 8-bit wide port
new_portto the 8-bit wide wirenew_wire, but with inversed indices.
Info: If the PortSegment and WireSegment objects are not available, element paths can be passed instead.
However, it is important for the element paths to have the correct type.
At the position of the PortSegment object, a port segment path must be provided, e.g. PortSegmentPath(raw="module.inst.port.0").
In [ ]:
Copied!
for idx in range(8):
port_idx = idx
wire_idx = 7 - idx
port_segment = new_port[port_idx]
wire_segment = new_wire[wire_idx]
top_module.connect(wire_segment, port_segment)
# Alternative:
# >>> port_segment_path = new_port[port_idx].path
# >>> wire_segment_path = new_wire[wire_idx].path
# >>> top_module.connect(wire_segment_path, port_segment_path)
print("The port now has the following connections:")
for idx in range(8):
print(f"\tIndex {idx} of the port is connected to {new_port[idx].raw_ws_path}")
print("The wire now has the following connections:")
for idx in range(8):
print(f"\tIndex {idx} of the wire is connected to {", ".join(p.raw_path for p in new_wire[idx].port_segments)}")
for idx in range(8):
port_idx = idx
wire_idx = 7 - idx
port_segment = new_port[port_idx]
wire_segment = new_wire[wire_idx]
top_module.connect(wire_segment, port_segment)
# Alternative:
# >>> port_segment_path = new_port[port_idx].path
# >>> wire_segment_path = new_wire[wire_idx].path
# >>> top_module.connect(wire_segment_path, port_segment_path)
print("The port now has the following connections:")
for idx in range(8):
print(f"\tIndex {idx} of the port is connected to {new_port[idx].raw_ws_path}")
print("The wire now has the following connections:")
for idx in range(8):
print(f"\tIndex {idx} of the wire is connected to {", ".join(p.raw_path for p in new_wire[idx].port_segments)}")
- The instance
new_instancecurrently has no ports. - In the cell below, the instance receives two ports.
- One output port is connected to the 8-bit wide wire
new_wire, and an input port is tied to a logical 0 (i.e. connected to a TIE-cell, which makes it a constant port).
Warning: The method Instance.connect() does only establish the connection, if no port with the given name (or with the given index) already exists!
This is to prevent accidental overwriting of previous connections.
In [ ]:
Copied!
new_instance = top_module.instances["new_instance"]
for idx in range(8):
path = new_wire[idx].path
new_instance.connect(port_name="output_port", ws_path=path, direction=Direction.OUT, index=idx)
print("The instance's output port has now these connections:")
for idx in range(8):
print(f"\tIndex {idx}: {new_instance.ports["output_port"][idx]}")
# Add an input port, but set it to a constant value
new_instance.connect("input_port", None)
new_instance.tie_port('input_port', 0, sig_value="0")
input_port = new_instance.ports['input_port']
print(f"The instance has now a port '{input_port.name}' with signal: {input_port.signal}")
new_instance = top_module.instances["new_instance"]
for idx in range(8):
path = new_wire[idx].path
new_instance.connect(port_name="output_port", ws_path=path, direction=Direction.OUT, index=idx)
print("The instance's output port has now these connections:")
for idx in range(8):
print(f"\tIndex {idx}: {new_instance.ports["output_port"][idx]}")
# Add an input port, but set it to a constant value
new_instance.connect("input_port", None)
new_instance.tie_port('input_port', 0, sig_value="0")
input_port = new_instance.ports['input_port']
print(f"The instance has now a port '{input_port.name}' with signal: {input_port.signal}")
- Ports can also be tied to constant values retrospectively.
- This does only work for input ports, since output ports receive their signal values from the instance that drives the output port.
- Execute the cell below to tied the input port to
1and try to tied one bit of the output port to1as well.
Warning: The port segment must be unconnected.
For architectural reasons, a port segment does not have direct access to the wire to which it is connected.
Accordingly, the PortSegment must be disconnected separately from the Wire via Module.disconnect.
Alternatively, the wire path of the PortSegment can be unconnected directly by using PortSegment.set_wire_path('') – with an empty string as argument –, which however does not notify the wire and the wire still "thinks" it is connected to the PortSegment instance.
In [ ]:
Copied!
new_instance.tie_port('input_port', index=0, sig_value='1')
print(f"Port '{input_port.name}' has now signal: {input_port.signal}")
try:
new_instance.tie_port('output_port', index=0, sig_value='1')
except Exception as e:
print(f"Cannot tie port: {e}")
top_module.disconnect(new_instance.ports['output_port'][0])
try:
new_instance.tie_port('output_port', index=0, sig_value='1')
except Exception as e:
print(f"Cannot tie port: {e}")
new_instance.tie_port('input_port', index=0, sig_value='1')
print(f"Port '{input_port.name}' has now signal: {input_port.signal}")
try:
new_instance.tie_port('output_port', index=0, sig_value='1')
except Exception as e:
print(f"Cannot tie port: {e}")
top_module.disconnect(new_instance.ports['output_port'][0])
try:
new_instance.tie_port('output_port', index=0, sig_value='1')
except Exception as e:
print(f"Cannot tie port: {e}")
- To change an existing connection, use
Instance.modify_connectionwith the name of the port to be modified and the path to the wire segment to connect the port to, as well as the index of the port segment, where the connection should be established. - Execute the cell below to add another wire to the module
top_moduleand assign the segment 0 of the portoutput_portto it.
In [ ]:
Copied!
top_module.create_wire("another_wire")
wire = top_module.wires["another_wire"]
new_instance.modify_connection('output_port', wire[0].path, index=0)
top_module.create_wire("another_wire")
wire = top_module.wires["another_wire"]
new_instance.modify_connection('output_port', wire[0].path, index=0)
- To remove a connection entirely (keeping the port, only disconnecting it), use
Instance.disconnect. - Execute the cell below to disconnect segment 0 from the
output_portport previously connected to the newly added wireanother_wire.
In [ ]:
Copied!
output_port = new_instance.ports["output_port"]
print(f"Index 0 of the output port is connected to {output_port[0].raw_ws_path}")
new_instance.disconnect('output_port', 0)
print(f"Index 0 of the output port is now connected to {output_port[0].raw_ws_path}")
output_port = new_instance.ports["output_port"]
print(f"Index 0 of the output port is connected to {output_port[0].raw_ws_path}")
new_instance.disconnect('output_port', 0)
print(f"Index 0 of the output port is now connected to {output_port[0].raw_ws_path}")
- If it is uncertain whether a port with a given name already exists, use
Instance.connect_modify, which overwrites the previous connection (if the port existed) or creates a new port and connects it with the given wire. - Execute the cell below to overwrite the connection of segment 0 from
ouput_portto another wire.
In [ ]:
Copied!
print(f"Index 0 of the output port is connected to {output_port[0].raw_ws_path}")
new_instance.connect_modify('output_port', wire[0].path, index=0)
print(f"Index 0 of the output port is now connected to {output_port[0].raw_ws_path}")
print(f"Index 0 of the output port is connected to {output_port[0].raw_ws_path}")
new_instance.connect_modify('output_port', wire[0].path, index=0)
print(f"Index 0 of the output port is now connected to {output_port[0].raw_ws_path}")
- To check whether an instance has constant ports (i.e. ports connected to TIE cells), use
Instance.has_tied_ports,Instance.has_tied_input_portsorInstance.has_tied_output_ports. - Execute the cell below to see which of those match the given instance.
In [ ]:
Copied!
has_const_ports = new_instance.has_tied_ports()
if has_const_ports:
print(f"The instance {new_instance.name} has constant ports.")
else:
print(f"The instance {new_instance.name} does not have constant ports.")
has_const_in_ports = new_instance.has_tied_inputs()
if has_const_in_ports:
print(f"The instance {new_instance.name} has constant input ports.")
else:
print(f"The instance {new_instance.name} does not have constant input ports.")
has_const_out_ports = new_instance.has_tied_outputs()
if has_const_out_ports:
print(f"The instance {new_instance.name} has constant output ports.")
else:
print(f"The instance {new_instance.name} does not have constant output ports.")
has_const_ports = new_instance.has_tied_ports()
if has_const_ports:
print(f"The instance {new_instance.name} has constant ports.")
else:
print(f"The instance {new_instance.name} does not have constant ports.")
has_const_in_ports = new_instance.has_tied_inputs()
if has_const_in_ports:
print(f"The instance {new_instance.name} has constant input ports.")
else:
print(f"The instance {new_instance.name} does not have constant input ports.")
has_const_out_ports = new_instance.has_tied_outputs()
if has_const_out_ports:
print(f"The instance {new_instance.name} has constant output ports.")
else:
print(f"The instance {new_instance.name} does not have constant output ports.")