Netlist
What is a Netlist view
In IPKISS almost all circuits are hierarchical. This implies that circuits (that are PCells) are themselves composed off instances of other Child PCells. It is the goal of the Netlist view to define the interconnectivity between those child cells. Here interconnectivity is to be interpreted broadly, as the interconnected PCells exchange information through optical or electrical signals. Usually, but not necessarily, this implies that the child cells will be connected with waveguides or electrical wires. The main advantage of defining the interconnectivity between hierarchical cells in a separate view, is that this information can be used in other views, such a the Layout view when connecting ports or in the CircuitModelView when doing circuit simulations. We thereby avoid having to redefine the interconnectivity between the childcells (views) in every view of the parent cell.
A Netlist is composed of 3 things:
instances: These are instances of the Netlist views of Child Cells
terms: These are the connections through which the a netlist connects to the outside world
links: These are links between terms.
In IPKISS you’ll define the netlist with its terms, instances and nets within the _generate_netlist
method of a NetlistView.
This means you’ll always have to start from the following skeleton:
class MyPCell(i3.PCell):
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
# Here you will specify which terms, nets and instances
# build up your netlist
return nl
Netlist terms
Defining terms
Netlist terms connect are the terminals through which a PCell connects with another PCell. They are added to the Netlist view within the predefined
method _generate_netlist
. A term can be created using i3.OpticalTerm
.
class Child(i3.PCell):
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
nl += i3.OpticalTerm(name="in")
nl += i3.OpticalTerm(name="out")
# or add an electrical term:
nl += i3.ElectricalTerm(name="electricalterm")
return nl
A term has two important properties:
name: This is the name that can be used to get the term from a term list.
my_netlist.terms["in"]
will return the term namedin
if it exist in the list.domain: This is the domain in which the connectivity with other components will happen.
Netlist instances
The netlists of Child cells are added as netlist instances to the netlist of the parent cell. The instances are added using the
i3.Instance
function.
class Parent(i3.PCell):
child1 = i3.ChildCellProperty()
child2 = i3.ChildCellProperty()
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
# Add the waveguide as an instance.
nl += i3.Instance(reference=self.child1, name='child1')
nl += i3.Instance(reference=self.child2, name='child2')
return insts
i3.Instance
takes two parameters:
reference: This is the reference to the netlist that is placed. Here we use
self.child1
referring to the netlist view ofchild1
because of the use ofChilCellProperty
The same is true forself.child2
.name: This is the name given to the netlist instance.
Netlist Links
Netlist links connect two terms. These terms can either be the terms of the parent cell, connecting it to the outside world, or the terms contained in the instances .
class Parent(i3.PCell):
child1 = i3.ChildCellProperty()
child2 = i3.ChildCellProperty()
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
# Instances and terms ommitted here
# ...
# Linking the terms to the term of the waveguide.
nl += i3.OpticalLink(term1=nl.terms['in'], term2=nl.instances['child1'].terms['in'], name="link_in")
nl += i3.OpticalLink(term1=nl.terms['out'], term2=nl.instances['child2'].terms['out'], name="link_out")
nl += i3.OpticalLink(term1=nl.instances['child1'].terms['in'], term2=nl.instances['child2'].terms['out'], name="link_out")
return nl
i3.OpticalLink
takes three parameters:
term1: input term of the link. Here we use
nl.terms['in']
to refer toin
of the parent cell andnl.instances['child1'].terms['in']
to refer to toin
of the netlist ofchild1
.term2: output term of the link
name: name of the link.
The Netlist object provides a convenience method ‘link’ to create links or nets more easily. You only have to provide the name of the term or the name of the instance and its term, in case you want to link to an instance term. IPKISS will then retrieve the corresponding object and infer the suitable type of Net. The example above can then be written as:
class Parent(i3.PCell):
child1 = i3.ChildCellProperty()
child2 = i3.ChildCellProperty()
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
# Instances and terms ommitted here
# ...
# Linking the terms to the term of the waveguide.
nl.link('in', 'child1:in', name='link_in')
nl.link('out', 'child2:out', name='link_out')
# Giving a name is optional but recommended
nl.link('child1:in', 'child2:out')
return nl
This is clearly more concise and easier to read, so in most cases we’ll prefer this approach.
Naming of terms, links and instances
It is highly advised to give sensitive names to every term, link and instance you create in IPKISS. Fundamentally, the only requirement is that the names should be unique within the Netlist view. This means that that, for a particular Netlist view of a component:
all the terms have to have different names.
all the links have to have different names.
all the netlist instances have to have different names.
Beyond those hard restrictions it is still advised to give the same name to matching:
Terms and Ports
Layout instances (SRefs), Netlist instances
This is best exemplified by the following piece of code.
class Parent(i3.PCell):
child1 = i3.ChildCellProperty()
child2 = i3.ChildCellProperty()
class Layout(i3.LayoutView)
# Matching the name of the ports with the terms
def _generate_ports(self, ports):
ports += i3.OpticalPort(name="in", position=..., trace_template=...)
ports += i3.OpticalPort(name="out", position=..., trace_template=...)
return ports
# Matching the name of the instances with the netlist instances
def _generate_instances(self,insts):
insts += i3.SRef(name="child1", reference=self.child1, transformation=...)
insts += i3.SRef(name="child2", reference=self.child2, transformation=...)
return insts
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
nl += i3.OpticalTerm(name="in")
nl += i3.OpticalTerm(name="out")
nl += i3.Instance(reference=self.child1, name='child1')
nl += i3.Instance(reference=self.child2, name='child2')
nl.link('in', 'child1:in')
nl.link('out', 'child2:out')
nl.link('child1:in', 'child2:out')
return nl
The reason for this matching convention is that you will be able to use this name matching convention to check if all the instances present in your netlist are also present in your layout.
For complicated designs this primitive from of Layout versus Schematic
can avoid errors.
Generate a NetlistView from a LayoutView
Instead of manually specifying the netlist, it is possible to extract the netlist from the Layout view in an automated way, using i3.NetlistFromLayout
.
Basic use
If you want to extract a netlist with terms, instances and nets from a layout with ports, instances and matching ports,
simply use i3.NetlistFromLayout
as shown in the following example.
The extraction of optical links is done by matching port location, direction and waveguide. Make sure to expose the desired ports in your layout.
import ipkiss3.all as i3
class MyCell(i3.PCell):
""" Example cell with 2 waveguides which are butt-to-butt connected"""
class Layout(i3.LayoutView):
def _generate_instances(self, insts):
wg1 = i3.RoundedWaveguide(name="{}_wg1".format(self.name))
wg1.Layout(shape=[(0.0, 0.0), (10.0, 0.0), (20.0, 20.0)], bend_radius=5.0)
wg2 = i3.RoundedWaveguide(name="{}_wg2".format(self.name))
wg2.Layout(shape=[(0.0, 0.0), (20.0, 0.0), (40.0, -20.0)], bend_radius=5.0)
insts += i3.place_and_route(
insts={'wg1': wg1,
'wg2': wg2},
specs=[i3.Place('wg1', (0.0, 0.0)),
i3.Join('wg2:in', 'wg1:out')]
)
return insts
def _generate_ports(self, ports):
ports += i3.expose_ports(self.instances, {'wg1:in': 'in',
'wg2:out': 'out'})
return ports
class Netlist(i3.NetlistFromLayout):
pass
mycell = MyCell()
layout = mycell.Layout()
layout.visualize(annotate=True)
netlist = mycell.Netlist()
print(netlist)
The code snippet above will print the following output to the console:
netlist:
--------
instances:
- wg1 : <Single instance in netlist of PCELL_2_wg1>
- wg2 : <Single instance in netlist of PCELL_2_wg2>
terms:
- in
- out
nets:
- wg1:out-wg2:in: <OpticalLink wg1:out to wg2:in>
- in-wg1:in: <OpticalLink in to wg1:in>
- out-wg2:out: <OpticalLink out to wg2:out>
Generating terms only
If you only need to create terms which correspond to the ports in the layout view, avoiding the creation of instances and nets,
you can use i3.extract_terms
and override the netlist.
In this case, any instances and their ports will be ignored.
class MyCell(i3.PCell):
class Layout(i3.LayoutView):
def _generate_ports(self, ports):
ports += i3.OpticalPort(name="out", position=(10.0 ,0.0), angle=0.0)
ports += i3.ElectricalPort(name="dc", position=(0.0, 10.0))
return ports
class Netlist(i3.NetlistFromLayout):
def _generate_netlist(self, netlist):
netlist.terms = i3.extract_terms(self.layout_view)
return netlist
Full hierarchical extraction
The netlist extraction works hierarchically. Simply use i3.NetlistFromLayout
at the different levels.
Here is a basic hierarchical example in which we just draw some rectangles representing devices and optical ports:
import ipkiss3.all as i3
device_layer = i3.Layer(0)
wg_layer = i3.Layer(1)
waveguide_windows = [i3.PathTraceWindow(layer=wg_layer,
start_offset=-0.3,
end_offset=0.3)]
waveguide_template = i3.WindowWaveguideTemplate(name="wg_template")
waveguide_template.Layout(windows=waveguide_windows)
class MyCell(i3.PCell):
""" Simple cell with an optical and an electrical port """
length = i3.PositiveNumberProperty(default=20.0)
width = i3.PositiveNumberProperty(default=5.0)
class Layout(i3.LayoutView):
def _generate_elements(self, elems):
elems += i3.Rectangle(layer=device_layer,
center=(0.0, 0.0),
box_size=(self.length, self.width))
elems += i3.Rectangle(layer=wg_layer,
center=(0.5 * self.length - 0.25, 0.0),
box_size=(0.5, 1.0))
elems += i3.Rectangle(layer=wg_layer,
center=(0.0, 0.5 * self.width - 0.25),
box_size=(1.0, 0.5))
return elems
def _generate_ports(self, ports):
ports += i3.OpticalPort(name="east", position=(0.5 * self.length ,0.0), angle=0.0, trace_template=waveguide_template)
ports += i3.ElectricalPort(name="north", position=(0.0, 0.5 * self.width))
return ports
class Netlist(i3.NetlistFromLayout):
pass
class Parent(i3.PCell):
""" Cell with 3 instances of MyCell and automated netlist extraction """
class Layout(i3.LayoutView):
def _generate_instances(self, insts):
# create 2 child cells
cell1 = MyCell(length=10.0, name="{}_cell1".format(self.name))
cell2 = MyCell(length=20.0, name="{}_cell2".format(self.name))
# place 2 instances of cell1 and one of cell2
insts += i3.SRef(name="child1", reference=cell1,
position=(0.0, 0.0))
insts += i3.SRef(name="child2", reference=cell1,
position=(0.0, 5.0),
transformation=i3.VMirror())
insts += i3.SRef(name="child3", reference=cell2,
position=(15.0, 0.0),
transformation=i3.Rotation(rotation=180.0))
return insts
def _generate_ports(self, ports):
# expose the children's ports
ports += i3.expose_ports(self.instances,
{"child2:east": "out",
"child3:north": "in"})
return ports
class Netlist(i3.NetlistFromLayout):
pass
mycell = MyCell()
mycell_layout = mycell.Layout()
mycell_layout.visualize(annotate=True)
parent = Parent()
parent_layout = parent.Layout()
parent_layout.visualize(annotate=True)
parent_netlist = parent.Netlist()
print(parent_netlist)
The code snippet above will print the following output to the console:
netlist:
--------
instances:
- child1 : <Single instance in netlist of PCELL_2_cell1>
- child2 : <Single instance in netlist of PCELL_2_cell1>
- child3 : <Single instance in netlist of PCELL_2_cell2>
terms:
- out
- in
nets:
- child1:east-child3:east: <OpticalLink child1:east to child3:east>
- child2:east-out: <OpticalLink out to child2:east>