Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cumulus/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ py.install_sources(core2chip, subdir: 'coriolis/plugins/core2chip')
py.install_sources(harness, subdir: 'coriolis/plugins/harness')
py.install_sources(macro, subdir: 'coriolis/plugins/macro')
py.install_sources(sram, subdir: 'coriolis/plugins/sram')
py.install_sources(netlist, subdir: 'coriolis/plugins/netlist')
py.install_sources(tools, subdir: 'coriolis/tools')


Expand Down
1 change: 1 addition & 0 deletions cumulus/src/plugins/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ subdir('core2chip')
subdir('harness')
subdir('macro')
subdir('sram')
subdir('netlist')

plugins = files([
'__init__.py',
Expand Down
135 changes: 135 additions & 0 deletions cumulus/src/plugins/netlist/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
.. -*- Mode: rst -*-

.. |Python wheel builds| image:: https://github.com/lip6/coriolis/actions/workflows/wheels.yml/badge.svg
:target: https://github.com/lip6/coriolis/actions/workflows/wheels.yml

.. image:: example.svg
:alt: Netlist example
:align: center

======================================
Utilities for netlist manipulation
======================================

Introduction
==============
This tutorial introduces all the notions needed to manipulate netlist throw |Coriolis| with Python. It can be useful for optimization technics which need to automatically modify the netlist.

You can refer to the Hurricane+python tutorial for more advance concepts with the |Hurricane| database

Generalities
==============
As shown with figure, a design in Coriolis is refered as a *cell*. It is composed with *instances* (dot lines), *nets* (in blue) and *plugs* (black squares). Each instance refers to a *model* (`MasterCell`) which, in this example, are `OR2` and `AND2`. A model is itself a cell with instances, nets and plugs. Each component of a design has a name which can be obtained by using the method `getName()`. Plugs, nets and instances are collections which can be obtained by the methods `getPlugs()`, `getNets()`, `getInstances()` respectively.

Using Coriolis
=================
Setting up the environment
----------------------------
Coriolis can be used throw Python3. First of all, the adequate environment has to be setup with the following command:
``crlenv.py -- shell``
Where ``shell`` is the shell used by the user (bash, zsh...):

Importing the modules
-----------------------
To use Coriolis with Python, including the Coriolis's modules is mandatory:
``from coriolis import CRL``

To make a design, we need to used a dedicated techno. The configuration of a design flow for a dedicated techno is done in the Python module ``coriolis.designflow.technos`` in which we need to import the techno. As example for SkyWater 130nm the import is:
::
from coriolis.designflow.technos import setupSky130_c4m

And after we use the ``setupSky130_c4m`` python function to run the configuration:
::
setupSky130_c4m( '../../..', '../../../pdkmaster/C4M.Sky130' )

Beginning the session
-----------------------
All the flow in Coriolis is running in a Coriolis session which has to be open:
::
af = AllianceFramework.get()
UpdateSession.open()

Netlist manipulation
======================
First of all the netlist (in blif format) have to be loaded with the following method:
::
cell = CRL.Blif.load("example")

Navigation
-----------
To navigate through the different components of the netlist (`cell`), the different following instructions can be used:
- for nets: ``for net in cell.getNets():``
- for instances: ``for inst in cell.getInstances():``
- for plugs: ``for plug in cell.getPlugs():``

Enter the hierarchy
---------------------
Each instance can also be considered as a cell and follow the same navigation capacities. For that, it is necessary to refer to the model of is instance and access it by using the method ``getMasterCell()`` on the instance. For example, considering Figure |example.svg|, accessing to the nets and instances inside ``inst1`` can be done by the following code:
::
cell1 = inst1.getMasterCell()
for net in cell1.getNets():

Components' properties
------------------------
Each component in the Hurricane database, is a class and has specific properties. The Hurricane's documentation gives all the details.

Net
^^^^^
Nets have ``Direction`` and ``Type`` which are both Hurricane's classes. A direction can be ``IN``, ``OUT``, ``TRISTATE`` etc. A type can be ``LOGICAL``, ``CLOCK`` etc. Lot of method give access to the properties (``getName()``, ``getDirection()``, etc.), test them (``isLogical()``, ``isClock()``, etc.) or set them (``setType()``, ``setExternal()``, etc.)

Instance
^^^^^^^^^^
The main methods of this class concerning netlist manipulation are ``getMasterCell()`` and ``getPlugs()``

Plug
^^^^^^
The method ``getInstance()`` allows to get the instance of the plug and ``getMasterNet()`` to get the net connected to this plug.

Examples
----------

WARNING Hurricane collections are not mutable, so change values outside iterator !!
=> the next sections have to be modify in consequence

Changing a cell
^^^^^^^^^^^^^^^^^
For example a cell from which one output drives more than ``MAX`` cells can be amplified, that mean change a ``_x2`` cell by a ``_x4`` one (i.e: ``a2_x2`` is changed by ``a2_x4`` which is amplified by 2):
::
for net in cell.getNets():
if size(net.getPlugs()) > MAX:
for plug in net.getPlugs():
if plug.getMasterNet().getDirection() == Net.Direction.OUT:
inst = plug.getInstance()
# replace _x1 or_x2 by _x4 in cell's name
cell_name = inst.getMasterCell().getName()[:-1]
cell_name += '4'
# get the corresponding cell (_x4)
new_cell = af.getCell(cell_name, Catalog.State.Views)
# change the cell
inst.setMasterCell(new_cell)

Adding a cell
^^^^^^^^^^^^^^^
For example, a buffer can be inserted to amplify a net which drives more than ``MAX`` cells:
::
for net in cell.getNets():
if size(net.getPlugs()) > MAX:
# get the buffer
buf = af.getCell('buf_x2', Catalog.State.Views)
# create its corresponding instance
buf_i = Instance.create(cell, 'buf_%s' %(net.getName()), buf)
# insert this instance to the net, i.e. cut the net into net and net_b (to be created)
buf_i.getPlug(buf.getNet('i')).setNet(net)
net_b = Net.create(cell, "%s_b" %(net.getName()))
buf_i.getPlug(buf.getNet('q')).setNet(net_b)

A complete example
====================
The ``amplify.py`` code is a concrete example of all the things described in the previous sections. It proposes 2 fonctions that can be applied to a given circuit:

- `amplify_net` that changes the cells that drive a given net
- `bufferize` that bufferizes a given net




Empty file.
122 changes: 122 additions & 0 deletions cumulus/src/plugins/netlist/adjacence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import sys, getopt

import coriolis.technos.symbolic.cmos
from coriolis.Hurricane import *
from coriolis.CRL import *

af = AllianceFramework.get()
UpdateSession.open()

# transform a given netlist to its corresponding adjacence matrix where:
# - cell is the cell corresponding to the netlist
# - tf is a boolean indicating the adjacence matrix's direction, if True (default)
# + lines are the target (to)
# + and columns the source (from)
# the opposite if False
# return:
# - the list of the nets with their index in the adjacence matrix
# - the adjacence matrix
def cell2adj(cell, tf = True):
dnets = {}
mat =[]
n = 0
# Matrix initialization
for n1 in cell.getNets():
if n1.getType() == Net.Type.LOGICAL:
mat.append([])
dnets[n1.getName()] = [n,"Input"]
n += 1
for n2 in cell.getNets():
if n2.getType() == Net.Type.LOGICAL:
mat[-1].append(0)
# adjacence's matrix filling
for ins in cell.getInstances():
target = []
source = []
for plug in ins.getPlugs():
net = plug.getMasterNet()
if net.getType() == Net.Type.LOGICAL:
if net.getDirection() == Net.Direction.IN:
source.append(dnets[plug.getNet().getName()][0])
elif net.getDirection() == Net.Direction.OUT:
target.append(dnets[plug.getNet().getName()][0])
dnets[plug.getNet().getName()][1] = ins.getMasterCell().getName()
for i in range(len(source)):
for j in range(len(target)):
if tf:
mat[target[j]][source[i]] = 1
else:
mat[source[i]][target[j]] = 1
#print(dnets)
#for i in range(len(dnets)):
# print(mat[i])
return (dnets,mat)

# get the adjassors' list of each net in the adjacence matrix mat:
# - if the adjacence matrix is to_from (tf = True) it is a predecessors list
# - if the adjacence matrix is from_to (tf = False) it is a successors list
# return a tuple with:
# - the index of the net in the adjacence matrix
# - the list of the corresponding adjassors
def adjassors(mat):
res = []
l = -1
for line in mat:
l += 1
res.append((l,[]))
for i in range(len(line)):
if line[i] == 1:
res[-1][1].append(i)
if res[-1][1] == []:
res.pop()
#for i in range(len(res)):
# print(res[i])
return res

# write the result in a json file where:
# - fname is the name of the output file
# - nets is the list of the nets and their index
# - mat is the adjacence matrix
# - adj is the list of adjassors
def write_json(fname, nets, mat, adj):
fres = open(fname+'.json', 'wt')
fres.write("{\n")
fres.write(' "Nets" : ' + str(nets))
#fres.write(',\n "Type" : ' + str(ltypes))
fres.write(',\n "Mat" : ' + str(mat))
fres.write(',\n "Adj" : ' + str(adj))
fres.write('\n}')
fres.close()

def usage():
print("python blif2graph.py [option]")
print("Gaphs' utilities on a netlist")
print("-b (blif): the name of the blif netlist")
print("-p (pred): list of predecessors of each net")
print("-s (suc): list of sucessors of each net")
print("-h (help): this message")

def main(argv):
try:
opts, args = getopt.getopt(sys.argv[1:], "hb:ps", ["help", "blif=", "pred", "suc"])
except getopt.GetoptError as err:
print(err)
usage()
sys.exit(2)

for o, a in opts:
if o == '-b':
cell = Blif.load(a)
fname = a
if o == '-p':
tf = True
if o == "-s":
tf = False
if o == "-h":
usage()
(nets,mat) = cell2adj(cell,tf)
adj = adjassors(mat)
write_json(fname,nets,mat,adj)

if __name__ == "__main__":
main(sys.argv)
Loading
Loading