From 507202b92aa1f250e951a59f7b409162780f76a4 Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:41:48 +0200 Subject: [PATCH 1/7] utilities to amplify nets in a netlist using bufferization or cells' amplification --- cumulus/meson.build | 1 + cumulus/src/plugins/meson.build | 1 + cumulus/src/plugins/netlist/README.rst | 100 +++++ cumulus/src/plugins/netlist/__init__.py | 0 cumulus/src/plugins/netlist/amplify.py | 192 ++++++++++ cumulus/src/plugins/netlist/example.svg | 464 ++++++++++++++++++++++++ cumulus/src/plugins/netlist/meson.build | 5 + 7 files changed, 763 insertions(+) create mode 100644 cumulus/src/plugins/netlist/README.rst create mode 100644 cumulus/src/plugins/netlist/__init__.py create mode 100644 cumulus/src/plugins/netlist/amplify.py create mode 100644 cumulus/src/plugins/netlist/example.svg create mode 100644 cumulus/src/plugins/netlist/meson.build diff --git a/cumulus/meson.build b/cumulus/meson.build index 4492d5afc..da5a7410a 100644 --- a/cumulus/meson.build +++ b/cumulus/meson.build @@ -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') diff --git a/cumulus/src/plugins/meson.build b/cumulus/src/plugins/meson.build index b929b5012..3eea0e633 100644 --- a/cumulus/src/plugins/meson.build +++ b/cumulus/src/plugins/meson.build @@ -4,6 +4,7 @@ subdir('core2chip') subdir('harness') subdir('macro') subdir('sram') +subdir('netlist') plugins = files([ '__init__.py', diff --git a/cumulus/src/plugins/netlist/README.rst b/cumulus/src/plugins/netlist/README.rst new file mode 100644 index 000000000..9c9b4de08 --- /dev/null +++ b/cumulus/src/plugins/netlist/README.rst @@ -0,0 +1,100 @@ +# 1 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 + +# 2 Generalities +[[example.svg]] +![[example.svg]] +As shown with Figure |example.svg|, 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. + +# 3 Using Coriolis +## 3.1 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...): +## 3.2 Importing the modules +To use Coriolis with Python, including the Coriolis's modules is mandatory: +`from coriolis import CRL` +`Hurricane` is the database on which Coriolis is build and `CRL` is the main part of Coriolis. + +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' )` +### 3.3 Beginning the session +All the flow in Coriolis is running in a Coriolis session which has to be open: +`af = AllianceFramework.get()` +`UpdateSession.open()` + +# 4 Netlist manipulation +First of all the netlist (in blif format) have to be loaded with the following method: +`cell = CRL.Blif.load("example")` + +## 4.1 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():` + +## 4.2 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():` + +## 4.3 Components' properties +Each component in the Hurricane database, is a class and has specific properties. The Hurricane's documentation gives all the details. + +### 4.3.1 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.) + +### 4.3.2 Instance +The main methods of this class concerning netlist manipulation are `getMasterCell()` and `getPlugs()` + +### 4.3.3 Plug +The method `getInstance()` allows to get the instance of the plug and `getMasterNet()` to get the net connected to this plug. + +## 4.4 Examples + +WARNING Hurricane collections are not mutable, so change values outside iterator !! +=> the next sections have to be modify in consequence +### 4.4.1 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) + +### 4.4.2 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) +``` + +# 5 A complete example +The following 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(nb)` that changes the cells that drive more than `nb` cells +`- bufferize(nb)` that bufferizes the nets that drive more than `nb` cells + + + + diff --git a/cumulus/src/plugins/netlist/__init__.py b/cumulus/src/plugins/netlist/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py new file mode 100644 index 000000000..fe8e2bd27 --- /dev/null +++ b/cumulus/src/plugins/netlist/amplify.py @@ -0,0 +1,192 @@ +import getopt + +from coriolis.designflow.technos import setupSky130_c4m +from coriolis import CRL +from coriolis import Hurricane + +setupSky130_c4m( '../../..', '../../../pdkmaster/C4M.Sky130' ) + +# Buffer's cell in the library +# TODO: get it in the library +AF = CRL.AllianceFramework.get() +BUF = AF.getCell('buf_x2', CRL.Catalog.State.Views) +Hurricane.UpdateSession.open() + +# Arrange the cells's nets in a dictonary where: +# - keys are the number of cells drived by nets +# - values are lists of nets which have the key drived cells +# parameter: the cell to analyze +# return: the corresponding dictionary +def net_by_load(cell): + #lnets = list(cell.getNets()) + #lnets.sort(key=lambda net: size(net.getPlugs()), reverse=True) + #for net in lnets: + # print("net: %s (%d)" %(net.getName(), size(net.getPlugs()))) + sorted_dict = {} + for net in cell.getNets(): + try: + sorted_dict[size(net.getPlugs())].append(net) + except KeyError: + sorted_dict[size(net.getPlugs())] = [net] + return sorted_dict + +# TODO: move this function to a common place as it is usefull for lots of operations +# Return the number of elements (size) of a given collection +def size(collection): + total = 0 + for c in collection: + total += 1 + return total + +# TODO: see if this function is not already written +# Return if a net is a clock or not based on its name +def isClock(net): + return net.getName() in ['ck', 'CK', 'clk', 'CLK'] + +# Amplify a net by: +# - adding a buffer (tech == 'buf') +# - or amplifying the previous cell (tech == 'amp') +# parameters: +# - net is the net to amplify +# - cell is the cell in which the net is defined (TODO: try to use MasterCell) +# - tech is the technic used (buffer or cell amplification) +# raise ValueError if the technic is not defined +def amplify_net(net, cell, tech): + # find the source plug + for p in net.getPlugs(): + if p.getMasterNet().getDirection() == Hurricane.Net.Direction.OUT: + p_found = p + break + # modify the source plug net + if tech == 'buf': + # Add a buffer + p_found.setNet(bufferize(net,cell)) + elif tech == 'amp': + # Amplify the source cell + amplify_source(p_found) + else: + raise ValueError(f'Unknown technic {tech}') + +# Amplify the cell of a given plug +def amplify_source(plug): + inst = plug.getInstance() + newModel = find_bigger_cell(inst.getMasterCell()) + inst.setMasterCell(newModel) + +# Return the biggest (in term of load) cell equivalent of a given cell +# TODO: try to access cells by technology file +# TODO: support the other load for cells +def find_bigger_cell(model): + equivalence = { + 'a2_x2': ['a2_x4'], + 'a3_x2': ['a3_x4'], + 'a4_x2': ['a4_x4'], + 'an12_x1': ['an12_x4'], + #'ao22_x2': ['ao22_x4'], + 'ao22_x2': ['a3_x2'], # POUR C4M sky essais + 'ao2o22_x2': ['ao2o22_x4'], + 'buf_x2': ['buf_x4', 'buf_x8'], + 'inv_x1': ['inv_x2', 'inv_x4', 'inv_x8'], + 'mx2_x2': ['mx2_x4'], + 'mx3_x2': ['mx3_x4'], + 'na2_x1': ['na2_x4'], + 'na3_x1': ['na3_x4'], + 'na4_x1': ['na4_x4'], + 'nao22_x1': ['nao22_x4'], + 'nao2o22_x1': ['nao2o22_x4'], + 'nmx2_x1': ['nmx2_x4'], + #'nmx3_x1': + 'nor2_x0': ['nxr2_x1'], # POUR C4M sky essais + 'no2_x1': ['no2_x4'], + 'no3_x1': ['no3_x4'], + 'no4_x1': ['no4_x4'], + 'noa22_x1': ['noa22_x4'], + 'noa2a22_x1': ['noa2a22_x4'], + 'noa2a2a23_x1': ['noa2a2a23_x4'], + 'noa2a2a2a24_x1': ['noa2a2a2a24_x4'], + 'noa2ao222_x1': ['noa2ao222_x4'], + 'noa3ao322_x1': ['noa3ao322_x4'], + 'nts_x1': ['nts_x2'], + 'nxr2_x1': ['nxr2_x4'], + 'o2_x2': ['o2_x4'], + 'o3_x2': ['o3_x4'], + 'o4_x2': ['o4_x4'], + 'oa22_x2': ['oa22_x4'], + 'oa2a22_x2': ['oa2a22_x4'], + 'oa2a2a23_x2': ['oa2a2a23_x4'], + 'oa2a2a2a24_x2': ['oa2a2a2a24_x4'], + 'oa2ao222_x2': ['oa2ao222_x4'], + 'oa3ao322_x2': ['oa3ao322_x4'], + 'on12_x1': ['on12_x4'], + #'one_x0': + #'powmid_x0': + #'rowend_x0': + #'sff1_x4': + #'sff1r_x4': + #'sff2_x4': + #'tie_x0': + 'ts_x4': ['ts_x8'], + 'xr2_x1': ['xr2_x4'] + #'zero_x0': + } + return AF.getCell(equivalence[model.getName()][0], CRL.Catalog.State.Views) + +# Add a buffer to a given net to amplify it +# parameters: +# - net is the net to amplify +# - cell is the cell in which the net is defined (TODO: try to use MasterCell) +def bufferize(net, cell): + # create the buffer instance + # TODO: try to get the cell from the net (masterCell ?) + buf_i = Hurricane.Instance.create(cell, f'buf_{net.getName()}', BUF) + # insert this instance to the net, i.e. cut the net into net_b (source to be created) and net + buf_i.getPlug(BUF.getNet('q')).setNet(net) + net_b = Hurricane.Net.create(cell, "%s_b" %(net.getName())) + buf_i.getPlug(BUF.getNet('i')).setNet(net_b) + return net_b + +# Amplify a given cell using the corresponding technic +# Parameters: +# - cell is the cell to amplify +# - tech is the technic used (buffer or cell amplification) +# - threshold: if net has a load > threshold then bufferize +# - nb_max: bufferize all the nets with nb_max maximal thresholds +def amplify(cell, tech, threshold=0, nb_max=0): + lnets = net_by_load(cell) + max_keys = sorted(lnets.keys(), reverse=True) + for i in max_keys[0:nb_max]: + if i >= threshold: + for net in lnets[i]: + if not isClock(net): + amplify_net(net,cell,tech) + +def usage(): + print("python utilities.py [option]") + print("Utility functions to amplify nets from a netlist") + print("-n (blif): the name of the blif netlist") + print("-a (tech:th:nb): the amplification technique used (buf to bufferize the nets and amp to amplify the cells)") + print(" - th is the threshold corresponding to the load of a signal (in number of targetting cells)") + print(" - nb is the maximal number of thresholds to treat") + print("-h (help): this message") + +if __name__ == '__main__': + tech = "" + try: + opts, args = getopt.getopt(sys.argv[1:], "hn:a:", ["help", "blif=", "amp", "buf"]) + except getopt.GetoptError as err: + print(err) + usage() + sys.exit(2) + + for o, a in opts: + if o == "-h": + usage() + if o == '-n': + cell = CRL.Blif.load(a) + fname = a + if o == "-a": + tech,th,nb = a.split(':') + + print(f'Amplify the {nb} nets of {fname} with a threshold of {th} with {tech}') + amplify(cell,tech,int(th),int(nb)) + AF.saveCell(cell,CRL.Catalog.State.Logical) diff --git a/cumulus/src/plugins/netlist/example.svg b/cumulus/src/plugins/netlist/example.svg new file mode 100644 index 000000000..caf01c75b --- /dev/null +++ b/cumulus/src/plugins/netlist/example.svg @@ -0,0 +1,464 @@ + + + + + + + + Or Gate + + + + And Gate + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cell + + inst3 + + inst1 + + inst2 + + OR2 + + AND2 + + OR2 + + + + + + + + + + + + + + + + + diff --git a/cumulus/src/plugins/netlist/meson.build b/cumulus/src/plugins/netlist/meson.build new file mode 100644 index 000000000..d532d85bb --- /dev/null +++ b/cumulus/src/plugins/netlist/meson.build @@ -0,0 +1,5 @@ +netlist = files([ + '__init__.py', + 'amplify.py', + 'adjacence.py', +]) From 2b7e2f1694f787cd8633d75b8ee84579021bb13d Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:45:05 +0200 Subject: [PATCH 2/7] Functions to transform a netlist to an adjacence matrix for graphs' manipulation --- cumulus/src/plugins/netlist/adjacence.py | 122 +++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 cumulus/src/plugins/netlist/adjacence.py diff --git a/cumulus/src/plugins/netlist/adjacence.py b/cumulus/src/plugins/netlist/adjacence.py new file mode 100644 index 000000000..00c610bd6 --- /dev/null +++ b/cumulus/src/plugins/netlist/adjacence.py @@ -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) From bc3862fd9d89d12c99d28c73454f614999cb4367 Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:49:23 +0200 Subject: [PATCH 3/7] use liberty file to get the biggest cell --- cumulus/src/plugins/netlist/amplify.py | 132 +++++++++++++------------ 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py index fe8e2bd27..a293de1da 100644 --- a/cumulus/src/plugins/netlist/amplify.py +++ b/cumulus/src/plugins/netlist/amplify.py @@ -1,17 +1,64 @@ import getopt -from coriolis.designflow.technos import setupSky130_c4m +#from coriolis.designflow.technos import setupSky130_c4m +from coriolis.designflow.technos import setupSky130_nsx2 from coriolis import CRL from coriolis import Hurricane -setupSky130_c4m( '../../..', '../../../pdkmaster/C4M.Sky130' ) +from liberty.parser import parse_liberty +from liberty.types import * +from sympy import parse_expr + +#setupSky130_c4m( '../../..', '../../../pdkmaster/C4M.Sky130' ) +setupSky130_nsx2( checkToolkit='../../..' ) # Buffer's cell in the library # TODO: get it in the library +LIB = '/dsk/l1/misc/roselyne/coriolis/src/alliance-check-toolkit/cells/nsxlib/nsxlib.lib' AF = CRL.AllianceFramework.get() BUF = AF.getCell('buf_x2', CRL.Catalog.State.Views) Hurricane.UpdateSession.open() +# Read liberty file to create a dictionary where: +# - key is the canonical logic function of the cell (thanks to sympy read_expr) +# - value is a list of dictionaries (cell's name,capa) +# This dictionary helps to choose the appropriate cell for amplification +# return: the created dictionary +# TODO: add decorator to cells +def read_liberty(): + # TODO: read from setup + liberty_file = LIB + library = parse_liberty(open(liberty_file).read()) + fdict = {} + for cell_group in library.get_groups('cell'): + #print("\n" + cell_group.args[0]) + #print(cell_group.__repr__() + for pin_group in cell_group.get_groups('pin'): + #print(" " + pin_group.args[0]) + pin = select_pin(cell_group, pin_group.args[0]) + #print(pin) + #print(pin.__repr__()) + if pin['direction'] == 'output': + f = str(parse_expr(str(pin['function']).replace("!","~").replace("\"",""))) + val = (cell_group.args[0],float(pin['capacitance'])) + #print(" f: " + f) + #print(" cap: " + str(pin['capacitance'])) + try: + fdict[f].append(val) + except KeyError: + fdict[f] = [val] + return fdict + +# Find a cell given by its model's name in a liberty file +# return the corresponding (key,value), False if not found +def find_in_liberty(cell): + lib = read_liberty() + for (k,v) in lib.items(): + for l in v: + if l[0] == cell: + return (k,v) + return False + # Arrange the cells's nets in a dictonary where: # - keys are the number of cells drived by nets # - values are lists of nets which have the key drived cells @@ -52,85 +99,45 @@ def isClock(net): # - tech is the technic used (buffer or cell amplification) # raise ValueError if the technic is not defined def amplify_net(net, cell, tech): + print(net.getName()) # find the source plug + p_found = False # keep False if the net is an input TODO: how to bufferize? for p in net.getPlugs(): if p.getMasterNet().getDirection() == Hurricane.Net.Direction.OUT: p_found = p break + # not output plug found mean net is an input of the circuit then exit + # TODO: think how to do on inputs + if not p_found: + return # modify the source plug net if tech == 'buf': # Add a buffer p_found.setNet(bufferize(net,cell)) elif tech == 'amp': - # Amplify the source cell - amplify_source(p_found) + # Get the model of the instance of the found plug + model = p_found.getInstance().getMasterCell().getName() + # Find the cell in the library + res = find_in_liberty(model) + new_cell = choose_cell(res) + # Amplify the source cell + p_found.getInstance().setMasterCell(AF.getCell(new_cell, CRL.Catalog.State.Views)) else: raise ValueError(f'Unknown technic {tech}') +# Choose the remplacing cell (biggest one) +# TODO: think about a searching technic +def choose_cell(dict_elem): + # search the cell with the max capacitance + dict_elem[1].sort(key=lambda capa: capa[1]) + return dict_elem[1][-1][0] + # Amplify the cell of a given plug def amplify_source(plug): inst = plug.getInstance() newModel = find_bigger_cell(inst.getMasterCell()) inst.setMasterCell(newModel) -# Return the biggest (in term of load) cell equivalent of a given cell -# TODO: try to access cells by technology file -# TODO: support the other load for cells -def find_bigger_cell(model): - equivalence = { - 'a2_x2': ['a2_x4'], - 'a3_x2': ['a3_x4'], - 'a4_x2': ['a4_x4'], - 'an12_x1': ['an12_x4'], - #'ao22_x2': ['ao22_x4'], - 'ao22_x2': ['a3_x2'], # POUR C4M sky essais - 'ao2o22_x2': ['ao2o22_x4'], - 'buf_x2': ['buf_x4', 'buf_x8'], - 'inv_x1': ['inv_x2', 'inv_x4', 'inv_x8'], - 'mx2_x2': ['mx2_x4'], - 'mx3_x2': ['mx3_x4'], - 'na2_x1': ['na2_x4'], - 'na3_x1': ['na3_x4'], - 'na4_x1': ['na4_x4'], - 'nao22_x1': ['nao22_x4'], - 'nao2o22_x1': ['nao2o22_x4'], - 'nmx2_x1': ['nmx2_x4'], - #'nmx3_x1': - 'nor2_x0': ['nxr2_x1'], # POUR C4M sky essais - 'no2_x1': ['no2_x4'], - 'no3_x1': ['no3_x4'], - 'no4_x1': ['no4_x4'], - 'noa22_x1': ['noa22_x4'], - 'noa2a22_x1': ['noa2a22_x4'], - 'noa2a2a23_x1': ['noa2a2a23_x4'], - 'noa2a2a2a24_x1': ['noa2a2a2a24_x4'], - 'noa2ao222_x1': ['noa2ao222_x4'], - 'noa3ao322_x1': ['noa3ao322_x4'], - 'nts_x1': ['nts_x2'], - 'nxr2_x1': ['nxr2_x4'], - 'o2_x2': ['o2_x4'], - 'o3_x2': ['o3_x4'], - 'o4_x2': ['o4_x4'], - 'oa22_x2': ['oa22_x4'], - 'oa2a22_x2': ['oa2a22_x4'], - 'oa2a2a23_x2': ['oa2a2a23_x4'], - 'oa2a2a2a24_x2': ['oa2a2a2a24_x4'], - 'oa2ao222_x2': ['oa2ao222_x4'], - 'oa3ao322_x2': ['oa3ao322_x4'], - 'on12_x1': ['on12_x4'], - #'one_x0': - #'powmid_x0': - #'rowend_x0': - #'sff1_x4': - #'sff1r_x4': - #'sff2_x4': - #'tie_x0': - 'ts_x4': ['ts_x8'], - 'xr2_x1': ['xr2_x4'] - #'zero_x0': - } - return AF.getCell(equivalence[model.getName()][0], CRL.Catalog.State.Views) - # Add a buffer to a given net to amplify it # parameters: # - net is the net to amplify @@ -153,6 +160,7 @@ def bufferize(net, cell): # - nb_max: bufferize all the nets with nb_max maximal thresholds def amplify(cell, tech, threshold=0, nb_max=0): lnets = net_by_load(cell) + #print(lnets) max_keys = sorted(lnets.keys(), reverse=True) for i in max_keys[0:nb_max]: if i >= threshold: From 01ebee6496c9bed1eb78dc80e417ac8cab2a1bd3 Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:51:16 +0200 Subject: [PATCH 4/7] change amplification technic, now based on a threshold on the load of the net --- cumulus/src/plugins/netlist/amplify.py | 51 +++++++------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py index a293de1da..abafefb8c 100644 --- a/cumulus/src/plugins/netlist/amplify.py +++ b/cumulus/src/plugins/netlist/amplify.py @@ -1,6 +1,5 @@ import getopt -#from coriolis.designflow.technos import setupSky130_c4m from coriolis.designflow.technos import setupSky130_nsx2 from coriolis import CRL from coriolis import Hurricane @@ -9,7 +8,6 @@ from liberty.types import * from sympy import parse_expr -#setupSky130_c4m( '../../..', '../../../pdkmaster/C4M.Sky130' ) setupSky130_nsx2( checkToolkit='../../..' ) # Buffer's cell in the library @@ -24,7 +22,7 @@ # - value is a list of dictionaries (cell's name,capa) # This dictionary helps to choose the appropriate cell for amplification # return: the created dictionary -# TODO: add decorator to cells +# TODO: add decorator to cells? def read_liberty(): # TODO: read from setup liberty_file = LIB @@ -59,24 +57,6 @@ def find_in_liberty(cell): return (k,v) return False -# Arrange the cells's nets in a dictonary where: -# - keys are the number of cells drived by nets -# - values are lists of nets which have the key drived cells -# parameter: the cell to analyze -# return: the corresponding dictionary -def net_by_load(cell): - #lnets = list(cell.getNets()) - #lnets.sort(key=lambda net: size(net.getPlugs()), reverse=True) - #for net in lnets: - # print("net: %s (%d)" %(net.getName(), size(net.getPlugs()))) - sorted_dict = {} - for net in cell.getNets(): - try: - sorted_dict[size(net.getPlugs())].append(net) - except KeyError: - sorted_dict[size(net.getPlugs())] = [net] - return sorted_dict - # TODO: move this function to a common place as it is usefull for lots of operations # Return the number of elements (size) of a given collection def size(collection): @@ -99,7 +79,6 @@ def isClock(net): # - tech is the technic used (buffer or cell amplification) # raise ValueError if the technic is not defined def amplify_net(net, cell, tech): - print(net.getName()) # find the source plug p_found = False # keep False if the net is an input TODO: how to bufferize? for p in net.getPlugs(): @@ -156,25 +135,21 @@ def bufferize(net, cell): # Parameters: # - cell is the cell to amplify # - tech is the technic used (buffer or cell amplification) -# - threshold: if net has a load > threshold then bufferize -# - nb_max: bufferize all the nets with nb_max maximal thresholds -def amplify(cell, tech, threshold=0, nb_max=0): - lnets = net_by_load(cell) - #print(lnets) - max_keys = sorted(lnets.keys(), reverse=True) - for i in max_keys[0:nb_max]: - if i >= threshold: - for net in lnets[i]: - if not isClock(net): - amplify_net(net,cell,tech) +# - threshold: if net has a load (in number of target cells) > threshold then amplify +def amplify(cell, tech, threshold=0): + for net in cell.getNets(): + if not isClock(net): + s = size(net.getPlugs()) + if s > threshold: + print(net.getName() + " th: " + str(s)) + amplify_net(net,cell,tech) def usage(): print("python utilities.py [option]") print("Utility functions to amplify nets from a netlist") print("-n (blif): the name of the blif netlist") - print("-a (tech:th:nb): the amplification technique used (buf to bufferize the nets and amp to amplify the cells)") + print("-a (tech:th): the amplification technique used (buf to bufferize the nets and amp to amplify the cells)") print(" - th is the threshold corresponding to the load of a signal (in number of targetting cells)") - print(" - nb is the maximal number of thresholds to treat") print("-h (help): this message") if __name__ == '__main__': @@ -193,8 +168,8 @@ def usage(): cell = CRL.Blif.load(a) fname = a if o == "-a": - tech,th,nb = a.split(':') + tech,th = a.split(':') - print(f'Amplify the {nb} nets of {fname} with a threshold of {th} with {tech}') - amplify(cell,tech,int(th),int(nb)) + print(f'Amplify the nets of {fname} with a threshold of {th} with {tech}') + amplify(cell,tech,int(th)) AF.saveCell(cell,CRL.Catalog.State.Logical) From cfc29458e8e6f14238e0dc757e314e9652eecffd Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:52:30 +0200 Subject: [PATCH 5/7] get liberty file from Yosys rule --- cumulus/src/plugins/netlist/amplify.py | 72 ++++++++++++++------------ 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py index abafefb8c..f60094680 100644 --- a/cumulus/src/plugins/netlist/amplify.py +++ b/cumulus/src/plugins/netlist/amplify.py @@ -1,20 +1,20 @@ import getopt from coriolis.designflow.technos import setupSky130_nsx2 +from coriolis.designflow.yosys import Yosys from coriolis import CRL from coriolis import Hurricane from liberty.parser import parse_liberty from liberty.types import * -from sympy import parse_expr +from sympy import parse_expr, Id setupSky130_nsx2( checkToolkit='../../..' ) -# Buffer's cell in the library -# TODO: get it in the library -LIB = '/dsk/l1/misc/roselyne/coriolis/src/alliance-check-toolkit/cells/nsxlib/nsxlib.lib' +#LIB = '/dsk/l1/misc/roselyne/coriolis/src/alliance-check-toolkit/cells/nsxlib/nsxlib.lib' +LIB = Yosys.liberty # doesn't work ??? +print("LIB = " + str(LIB)) AF = CRL.AllianceFramework.get() -BUF = AF.getCell('buf_x2', CRL.Catalog.State.Views) Hurricane.UpdateSession.open() # Read liberty file to create a dictionary where: @@ -25,7 +25,9 @@ # TODO: add decorator to cells? def read_liberty(): # TODO: read from setup - liberty_file = LIB + #liberty_file = LIB + liberty_file = Yosys.liberty + print(liberty_file) library = parse_liberty(open(liberty_file).read()) fdict = {} for cell_group in library.get_groups('cell'): @@ -37,20 +39,22 @@ def read_liberty(): #print(pin) #print(pin.__repr__()) if pin['direction'] == 'output': - f = str(parse_expr(str(pin['function']).replace("!","~").replace("\"",""))) + f = parse_expr(str(pin['function']).replace("!","~").replace("\"","")) + #print(f.args,f.func) val = (cell_group.args[0],float(pin['capacitance'])) - #print(" f: " + f) + #print(" f: " + str(f)) #print(" cap: " + str(pin['capacitance'])) + if (type(f) == sympy.core.symbol.Symbol) and (str(f) != 'IQ'): # buffer + f = 'buf' try: - fdict[f].append(val) + fdict[str(f)].append(val) except KeyError: - fdict[f] = [val] + fdict[str(f)] = [val] return fdict -# Find a cell given by its model's name in a liberty file +# Find a cell given by its model's name in a given liberty librairy # return the corresponding (key,value), False if not found -def find_in_liberty(cell): - lib = read_liberty() +def find_in_liberty(cell,lib): for (k,v) in lib.items(): for l in v: if l[0] == cell: @@ -66,6 +70,7 @@ def size(collection): return total # TODO: see if this function is not already written +# Yes it is but doesn't work # Return if a net is a clock or not based on its name def isClock(net): return net.getName() in ['ck', 'CK', 'clk', 'CLK'] @@ -75,29 +80,29 @@ def isClock(net): # - or amplifying the previous cell (tech == 'amp') # parameters: # - net is the net to amplify -# - cell is the cell in which the net is defined (TODO: try to use MasterCell) # - tech is the technic used (buffer or cell amplification) +# - lib is the liberty library # raise ValueError if the technic is not defined -def amplify_net(net, cell, tech): +def amplify_net(net, tech, lib): # find the source plug p_found = False # keep False if the net is an input TODO: how to bufferize? for p in net.getPlugs(): if p.getMasterNet().getDirection() == Hurricane.Net.Direction.OUT: p_found = p break - # not output plug found mean net is an input of the circuit then exit + # no output plug found mean net is an input of the circuit then exit # TODO: think how to do on inputs if not p_found: return # modify the source plug net if tech == 'buf': # Add a buffer - p_found.setNet(bufferize(net,cell)) + p_found.setNet(bufferize(net,lib)) elif tech == 'amp': # Get the model of the instance of the found plug model = p_found.getInstance().getMasterCell().getName() # Find the cell in the library - res = find_in_liberty(model) + res = find_in_liberty(model,lib) new_cell = choose_cell(res) # Amplify the source cell p_found.getInstance().setMasterCell(AF.getCell(new_cell, CRL.Catalog.State.Views)) @@ -110,25 +115,27 @@ def choose_cell(dict_elem): # search the cell with the max capacitance dict_elem[1].sort(key=lambda capa: capa[1]) return dict_elem[1][-1][0] - -# Amplify the cell of a given plug -def amplify_source(plug): - inst = plug.getInstance() - newModel = find_bigger_cell(inst.getMasterCell()) - inst.setMasterCell(newModel) # Add a buffer to a given net to amplify it # parameters: # - net is the net to amplify -# - cell is the cell in which the net is defined (TODO: try to use MasterCell) -def bufferize(net, cell): +# - cell is the cell in which the net is defined +# - lib is the liberty library +def bufferize(net, lib): # create the buffer instance - # TODO: try to get the cell from the net (masterCell ?) - buf_i = Hurricane.Instance.create(cell, f'buf_{net.getName()}', BUF) + # take a buffer cell in the liberty library + # TODO: method to find a better one + buf_m = AF.getCell(lib['buf'][0][0], CRL.Catalog.State.Views) + buf_i = Hurricane.Instance.create(net.getCell(), f'buf_{net.getName()}', buf_m) + for n in buf_m.getExternalNets(): + if (n.getDirection() == Hurricane.Net.Direction.IN) and not n.isSupply(): + buf_in = n.getName() + elif n.getDirection() == Hurricane.Net.Direction.OUT: + buf_out = n.getName() # insert this instance to the net, i.e. cut the net into net_b (source to be created) and net - buf_i.getPlug(BUF.getNet('q')).setNet(net) - net_b = Hurricane.Net.create(cell, "%s_b" %(net.getName())) - buf_i.getPlug(BUF.getNet('i')).setNet(net_b) + buf_i.getPlug(buf_m.getNet(buf_out)).setNet(net) + net_b = Hurricane.Net.create(net.getCell(), "%s_b" %(net.getName())) + buf_i.getPlug(buf_m.getNet(buf_in)).setNet(net_b) return net_b # Amplify a given cell using the corresponding technic @@ -137,12 +144,13 @@ def bufferize(net, cell): # - tech is the technic used (buffer or cell amplification) # - threshold: if net has a load (in number of target cells) > threshold then amplify def amplify(cell, tech, threshold=0): + lib = read_liberty() for net in cell.getNets(): if not isClock(net): s = size(net.getPlugs()) if s > threshold: print(net.getName() + " th: " + str(s)) - amplify_net(net,cell,tech) + amplify_net(net,tech,lib) def usage(): print("python utilities.py [option]") From 3d2904e5365c53d93dc4e3b497f58d150397a53c Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 14:55:36 +0200 Subject: [PATCH 6/7] improve cells' categorization to be more efficient and add generalization on library mangement --- cumulus/src/plugins/netlist/amplify.py | 132 ++++++++++++++-------- cumulus/src/plugins/netlist/tryliberty.py | 37 ++++++ 2 files changed, 125 insertions(+), 44 deletions(-) create mode 100644 cumulus/src/plugins/netlist/tryliberty.py diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py index f60094680..1afdadc84 100644 --- a/cumulus/src/plugins/netlist/amplify.py +++ b/cumulus/src/plugins/netlist/amplify.py @@ -1,63 +1,81 @@ -import getopt +# example of standalone use: +# python amplify.py -l gf180mcu -n arlet6502 -a amp:30 -o vlog -from coriolis.designflow.technos import setupSky130_nsx2 -from coriolis.designflow.yosys import Yosys -from coriolis import CRL -from coriolis import Hurricane +from coriolis import Hurricane, CRL from liberty.parser import parse_liberty from liberty.types import * from sympy import parse_expr, Id - -setupSky130_nsx2( checkToolkit='../../..' ) - -#LIB = '/dsk/l1/misc/roselyne/coriolis/src/alliance-check-toolkit/cells/nsxlib/nsxlib.lib' -LIB = Yosys.liberty # doesn't work ??? -print("LIB = " + str(LIB)) -AF = CRL.AllianceFramework.get() -Hurricane.UpdateSession.open() +from coriolis.helpers.overlay import UpdateSession # Read liberty file to create a dictionary where: # - key is the canonical logic function of the cell (thanks to sympy read_expr) -# - value is a list of dictionaries (cell's name,capa) +# - value is a list of tuples (cell's name,capa) # This dictionary helps to choose the appropriate cell for amplification # return: the created dictionary -# TODO: add decorator to cells? -def read_liberty(): - # TODO: read from setup - #liberty_file = LIB - liberty_file = Yosys.liberty - print(liberty_file) +# TODO: add decorator to Hurricane library? +def read_liberty(liberty_file): + #print("LIBERTY="+liberty_file) library = parse_liberty(open(liberty_file).read()) fdict = {} for cell_group in library.get_groups('cell'): - #print("\n" + cell_group.args[0]) - #print(cell_group.__repr__() + #print(cell_group.__repr__()) for pin_group in cell_group.get_groups('pin'): #print(" " + pin_group.args[0]) pin = select_pin(cell_group, pin_group.args[0]) #print(pin) #print(pin.__repr__()) if pin['direction'] == 'output': - f = parse_expr(str(pin['function']).replace("!","~").replace("\"","")) + #print(pin['function']) + # lower() because I is considered as Imaginary by sympy + # and replace for coherence with sympy operators + f = parse_expr(str(pin['function']).lower().replace("!","~").replace("\"","")) #print(f.args,f.func) - val = (cell_group.args[0],float(pin['capacitance'])) + #print(cell_group.args,pin) + # replace to be coherent with Coriolis cells' naming + #val = (cell_group.args[0].replace("__","_"),float(pin['max_capacitance'])) + val = (cell_group.args[0],float(pin['max_capacitance'])) #print(" f: " + str(f)) - #print(" cap: " + str(pin['capacitance'])) + #print(" cap: " + str(pin['max_capacitance'])) if (type(f) == sympy.core.symbol.Symbol) and (str(f) != 'IQ'): # buffer f = 'buf' + # threestate + if pin['three_state']: f="ts" + # latch + if cell_group.get_groups('latch'): f="latch" + # clock gating + if cell_group['clock_gating_integrated_cell']: f="clkg" + # flip-flop + ff = cell_group.get_groups('ff') + if ff: + f = "ff" + #print(ff.__repr__()) + if cell_group.get_groups('test_cell'): f+='_test' + if ff[0]['clear']: f+='_r' + if ff[0]['preset']: f+='_s' try: fdict[str(f)].append(val) except KeyError: fdict[str(f)] = [val] + # sort by capicitance values + for v in fdict.values(): + v.sort(key=lambda gate: gate[1]) return fdict +# Pretty display for the dictionary returned by read_liberty +def pretty_display(libdict): + for k in libdict.keys(): + print(k,':') + for e in libdict[k]: + print(" ",e) + # Find a cell given by its model's name in a given liberty librairy # return the corresponding (key,value), False if not found def find_in_liberty(cell,lib): for (k,v) in lib.items(): for l in v: - if l[0] == cell: + # replace to be coherent with Coriolis cells' naming + if l[0].replace("__","_") == cell: return (k,v) return False @@ -82,8 +100,9 @@ def isClock(net): # - net is the net to amplify # - tech is the technic used (buffer or cell amplification) # - lib is the liberty library +# - hlib is the Hurricane library # raise ValueError if the technic is not defined -def amplify_net(net, tech, lib): +def amplify_net(net, tech, lib, hlib): # find the source plug p_found = False # keep False if the net is an input TODO: how to bufferize? for p in net.getPlugs(): @@ -97,36 +116,37 @@ def amplify_net(net, tech, lib): # modify the source plug net if tech == 'buf': # Add a buffer - p_found.setNet(bufferize(net,lib)) + p_found.setNet(bufferize(net,lib,hlib)) elif tech == 'amp': # Get the model of the instance of the found plug model = p_found.getInstance().getMasterCell().getName() # Find the cell in the library res = find_in_liberty(model,lib) new_cell = choose_cell(res) - # Amplify the source cell - p_found.getInstance().setMasterCell(AF.getCell(new_cell, CRL.Catalog.State.Views)) + # Amplify the source cell + p_found.getInstance().setMasterCell(hlib.getCell(new_cell)) else: raise ValueError(f'Unknown technic {tech}') # Choose the remplacing cell (biggest one) # TODO: think about a searching technic def choose_cell(dict_elem): - # search the cell with the max capacitance - dict_elem[1].sort(key=lambda capa: capa[1]) + # search the cell with the max capacitance: the last one according to the sorting creation of the dictionary return dict_elem[1][-1][0] # Add a buffer to a given net to amplify it # parameters: # - net is the net to amplify # - cell is the cell in which the net is defined -# - lib is the liberty library -def bufferize(net, lib): +# - lib is the liberty library +# - hlib is the Hurricane library +def bufferize(net, lib, hlib): # create the buffer instance # take a buffer cell in the liberty library # TODO: method to find a better one - buf_m = AF.getCell(lib['buf'][0][0], CRL.Catalog.State.Views) - buf_i = Hurricane.Instance.create(net.getCell(), f'buf_{net.getName()}', buf_m) + buf_m = hlib.getCell(lib['buf'][2][0]) + with UpdateSession(): + buf_i = Hurricane.Instance.create(net.getCell(), f'buf_{net.getName()}', buf_m) for n in buf_m.getExternalNets(): if (n.getDirection() == Hurricane.Net.Direction.IN) and not n.isSupply(): buf_in = n.getName() @@ -134,23 +154,25 @@ def bufferize(net, lib): buf_out = n.getName() # insert this instance to the net, i.e. cut the net into net_b (source to be created) and net buf_i.getPlug(buf_m.getNet(buf_out)).setNet(net) - net_b = Hurricane.Net.create(net.getCell(), "%s_b" %(net.getName())) + with UpdateSession(): + net_b = Hurricane.Net.create(net.getCell(), "%s_b" %(net.getName())) buf_i.getPlug(buf_m.getNet(buf_in)).setNet(net_b) return net_b # Amplify a given cell using the corresponding technic # Parameters: +# - lib is the liberty dictionary provided by read_liberty # - cell is the cell to amplify # - tech is the technic used (buffer or cell amplification) # - threshold: if net has a load (in number of target cells) > threshold then amplify -def amplify(cell, tech, threshold=0): - lib = read_liberty() +def amplify(lib, hlib, cell, tech, threshold=0): for net in cell.getNets(): if not isClock(net): s = size(net.getPlugs()) if s > threshold: print(net.getName() + " th: " + str(s)) - amplify_net(net,tech,lib) + amplify_net(net,tech,lib,hlib) + def usage(): print("python utilities.py [option]") @@ -158,12 +180,16 @@ def usage(): print("-n (blif): the name of the blif netlist") print("-a (tech:th): the amplification technique used (buf to bufferize the nets and amp to amplify the cells)") print(" - th is the threshold corresponding to the load of a signal (in number of targetting cells)") + print("-o (output format): vlog or vst") print("-h (help): this message") - + +# main for standalone usage if __name__ == '__main__': + import getopt, sys + tech = "" try: - opts, args = getopt.getopt(sys.argv[1:], "hn:a:", ["help", "blif=", "amp", "buf"]) + opts, args = getopt.getopt(sys.argv[1:], "hn:a:o:l:", ["help", "blif=", "amp", "buf", "vlog", "vst", "lib="]) except getopt.GetoptError as err: print(err) usage() @@ -177,7 +203,25 @@ def usage(): fname = a if o == "-a": tech,th = a.split(':') + if o == '-o': + output = a + if o == '-l': + from coriolis.designflow.yosys import Yosys + from pathlib import Path + import importlib + mod = importlib.import_module("pdks."+a) + + mod.setup( useHV=True ) + liberty = Yosys._liberty + libdict = read_liberty(liberty) + # stem[0:-1] due to bug in Coriolis: mcu9t5v insteed mcu9t5v0 + hlib = Hurricane.DataBase.getDB().getRootLibrary().getLibrary(Path(liberty).stem[0:-1]) print(f'Amplify the nets of {fname} with a threshold of {th} with {tech}') - amplify(cell,tech,int(th)) - AF.saveCell(cell,CRL.Catalog.State.Logical) + amplify(libdict,hlib,cell,tech,int(th)) + if output == 'vlog': + CRL.Verilog.save(cell, True) + elif output == 'vst': + AF = CRL.AllianceFramework.get() + AF.saveCell(cell,CRL.Catalog.State.Logical) + else: raise ValueError("not implemented format " + output) diff --git a/cumulus/src/plugins/netlist/tryliberty.py b/cumulus/src/plugins/netlist/tryliberty.py new file mode 100644 index 000000000..c083b73a2 --- /dev/null +++ b/cumulus/src/plugins/netlist/tryliberty.py @@ -0,0 +1,37 @@ +from liberty.parser import parse_liberty +from liberty.types import * +from sympy import parse_expr + +liberty_file = '/dsk/l1/misc/roselyne/coriolis/src/alliance-check-toolkit/cells/nsxlib/nsxlib.lib' +library = parse_liberty(open(liberty_file).read()) + +fdict = {} + +# cell = select_cell(library, 'nmx2_x4') +# pins = cell.get_groups('pin') +# pinq = select_pin(cell, 'nq') +# f = pinq['function'] +# expr = str(f).replace("!","~").replace("\"","") +# print(parse_expr(expr)) + +for cell_group in library.get_groups('cell'): + print("\n" + cell_group.args[0]) + #print(cell_group.__repr__() + + for pin_group in cell_group.get_groups('pin'): + print(" " + pin_group.args[0]) + pin = select_pin(cell_group, pin_group.args[0]) + #print(pin) + #print(pin.__repr__()) + if pin['direction'] == 'output': + f = str(parse_expr(str(pin['function']).replace("!","~").replace("\"",""))) + val = (cell_group.args[0],float(pin['capacitance'])) + print(" f: " + f) + print(" cap: " + str(pin['capacitance'])) + try: + fdict[f].append(val) + except KeyError: + fdict[f] = [val] + +for (k,v) in fdict.items(): + print(k,v) From 3f7788dc0de03d4420f384a6239e978394df9759 Mon Sep 17 00:00:00 2001 From: Roselyne Chotin Date: Wed, 26 Aug 2026 15:29:41 +0200 Subject: [PATCH 7/7] update due to changing __ replacement in Coriolis --- cumulus/src/plugins/netlist/README.rst | 139 ++++++++++++++++--------- cumulus/src/plugins/netlist/amplify.py | 5 +- 2 files changed, 89 insertions(+), 55 deletions(-) diff --git a/cumulus/src/plugins/netlist/README.rst b/cumulus/src/plugins/netlist/README.rst index 9c9b4de08..6db141f52 100644 --- a/cumulus/src/plugins/netlist/README.rst +++ b/cumulus/src/plugins/netlist/README.rst @@ -1,66 +1,100 @@ -# 1 Introduction +.. -*- 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 -# 2 Generalities -[[example.svg]] -![[example.svg]] -As shown with Figure |example.svg|, 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. +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. -# 3 Using Coriolis -## 3.1 Setting up the environment +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...): -## 3.2 Importing the modules +``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` -`Hurricane` is the database on which Coriolis is build and `CRL` is the main part of Coriolis. - -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' )` -### 3.3 Beginning the session +``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()` +:: + af = AllianceFramework.get() + UpdateSession.open() -# 4 Netlist manipulation +Netlist manipulation +====================== First of all the netlist (in blif format) have to be loaded with the following method: -`cell = CRL.Blif.load("example")` +:: + cell = CRL.Blif.load("example") -## 4.1 Navigation +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():` - -## 4.2 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():` - -## 4.3 Components' properties +- 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. -### 4.3.1 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.) +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.) -### 4.3.2 Instance -The main methods of this class concerning netlist manipulation are `getMasterCell()` and `getPlugs()` +Instance +^^^^^^^^^^ +The main methods of this class concerning netlist manipulation are ``getMasterCell()`` and ``getPlugs()`` -### 4.3.3 Plug -The method `getInstance()` allows to get the instance of the plug and `getMasterNet()` to get the net connected to this plug. +Plug +^^^^^^ +The method ``getInstance()`` allows to get the instance of the plug and ``getMasterNet()`` to get the net connected to this plug. -## 4.4 Examples +Examples +---------- WARNING Hurricane collections are not mutable, so change values outside iterator !! => the next sections have to be modify in consequence -### 4.4.1 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): +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(): @@ -74,11 +108,11 @@ For example a cell from which one output drives more than `MAX` cells can be amp # change the cell inst.setMasterCell(new_cell) -### 4.4.2 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(): +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) @@ -88,12 +122,13 @@ for net in cell.getNets(): 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) -``` -# 5 A complete example -The following 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(nb)` that changes the cells that drive more than `nb` cells -`- bufferize(nb)` that bufferizes the nets that drive more than `nb` cells +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 diff --git a/cumulus/src/plugins/netlist/amplify.py b/cumulus/src/plugins/netlist/amplify.py index 1afdadc84..eab86518f 100644 --- a/cumulus/src/plugins/netlist/amplify.py +++ b/cumulus/src/plugins/netlist/amplify.py @@ -69,13 +69,12 @@ def pretty_display(libdict): for e in libdict[k]: print(" ",e) -# Find a cell given by its model's name in a given liberty librairy +# Find a cell given by its model's name in a given liberty library # return the corresponding (key,value), False if not found def find_in_liberty(cell,lib): for (k,v) in lib.items(): for l in v: - # replace to be coherent with Coriolis cells' naming - if l[0].replace("__","_") == cell: + if l[0] == cell: return (k,v) return False