From: Jock Tanner Date: Wed, 25 Mar 2020 05:02:32 +0000 (+0000) Subject: Replace submodule functions with Module objects. X-Git-Tag: partial-core-ls180-gdsii~157 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=405774b21b2836c3898e7c911896e7bcbc8be3e0;p=soclayout.git Replace submodule functions with Module objects. --- diff --git a/experiments7/doAlu16.py b/experiments7/doAlu16.py index 1118ec1..7f9971e 100755 --- a/experiments7/doAlu16.py +++ b/experiments7/doAlu16.py @@ -8,17 +8,17 @@ import CRL import Cfg import Etesian import Katana -import clocktree.ClockTree from Hurricane import ( - DbU, DataBase, UpdateSession, Box, Transformation, Instance, Pad, Pin, + DbU, DataBase, UpdateSession, Box, Transformation, Instance, Pin, NetExternalComponents, ) from helpers import l from plugins import RSavePlugin +from coriolis2.settings import af +from utils import Module import symbolic.cmos # do not remove -af = CRL.AllianceFramework.get() BIT_WIDTH = 16 @@ -157,158 +157,16 @@ def coriolis_setup(): Cfg.Configuration.popDefaultPriority() -def add(editor=None, **kwargs): - - db = DataBase.getDB() - print(db, dir(db)) - METAL2 = get_layer('METAL2') - METAL3 = get_layer('METAL3') - METAL5 = get_layer('METAL5') - BLOCKAGE2 = get_layer('BLOCKAGE2') - BLOCKAGE3 = get_layer('BLOCKAGE3') - BLOCKAGE4 = get_layer('BLOCKAGE4') - BLOCKAGE5 = get_layer('BLOCKAGE5') - - cell = af.getCell('add', CRL.Catalog.State.Logical) - print(cell.getNet('a(0)')) - - if not cell: - print('[ERROR] Unable to load cell "add.vst", aborting .') - return False - - kwargs['cell'] = cell - - width = 350.0 - height = 400.0 - - ab = Box(l(0.0), l(0.0), l(width), l(height)) - - cell_gauge = af.getCellGauge() - margin_as_percentage = Cfg.getParamPercentage( - 'etesian.spaceMargin' - ).asPercentage() - space_margin = (margin_as_percentage + 5) / 100.0 - aspect_ratio = margin_as_percentage / 100.0 - clocktree.ClockTree.computeAbutmentBox( - cell, space_margin, aspect_ratio, cell_gauge, - ) - ab2 = cell.getAbutmentBox() - print("box", ab, ab.getHeight(), ab.getWidth()) - print("calc box", ab2, ab2.getHeight(), ab2.getWidth()) - - UpdateSession.open() - cell.setAbutmentBox(ab) - - create_pins(cell, 'a({})', 'a({}).0', Pin.Direction.NORTH, - x=10.0, y=height, delta=20.0, repeat=BIT_WIDTH) - create_pins(cell, 'o({})', 'o({}).0', Pin.Direction.SOUTH, - x=100.0, y=0.0, delta=10.0, repeat=BIT_WIDTH) - create_pins(cell, 'b({})', 'b({}).0', Pin.Direction.NORTH, - x=20.0, y=height, delta=20.0, repeat=BIT_WIDTH) - UpdateSession.close() - - if editor: - editor.setCell(cell) - - place_and_route(cell) - - UpdateSession.open() - - ab = cell.getAbutmentBox() - ab.inflate(to_DbU(-5.0)) - - # do we really want to use net b(15)? - last_net = cell.getNet('b({})'.format(BIT_WIDTH-1)) - Pad.create(last_net, BLOCKAGE2, ab) - Pad.create(last_net, BLOCKAGE3, ab) - Pad.create(last_net, BLOCKAGE4, ab) - UpdateSession.close() - - RSavePlugin.ScriptMain(editor=editor, **kwargs) - - -def sub(editor=None, **kwargs): - - db = DataBase.getDB() - print(db, dir(db)) - METAL2 = get_layer('METAL2') - METAL3 = get_layer('METAL3') - METAL5 = get_layer('METAL5') - BLOCKAGE2 = get_layer('BLOCKAGE2') - BLOCKAGE3 = get_layer('BLOCKAGE3') - BLOCKAGE4 = get_layer('BLOCKAGE4') - BLOCKAGE5 = get_layer('BLOCKAGE5') - - cell = af.getCell('sub', CRL.Catalog.State.Logical) - print(cell.getNet('a(0)')) - - if not cell: - print('[ERROR] Unable to load cell "alu16.vst", aborting .') - return False - - kwargs['cell'] = cell - - width = 350.0 - height = 400.0 - - ab = Box(l(0.0), l(0.0), l(width), l(height)) - - cellGauge = af.getCellGauge() - margin_as_percentage = Cfg.getParamPercentage( - 'etesian.spaceMargin' - ).asPercentage() - spaceMargin = (margin_as_percentage + 5) / 100.0 - aspectRatio = margin_as_percentage / 100.0 - clocktree.ClockTree.computeAbutmentBox( - cell, spaceMargin, aspectRatio, cellGauge, - ) - ab2 = cell.getAbutmentBox() - print("box", ab, ab.getHeight(), ab.getWidth()) - print("calc box", ab2, ab2.getHeight(), ab2.getWidth()) - - UpdateSession.open() - cell.setAbutmentBox(ab) - - create_pins(cell, 'a({})', 'a({}).0', Pin.Direction.NORTH, - x=10.0, y=height, delta=20.0, repeat=BIT_WIDTH) - create_pins(cell, 'o({})', 'o({}).0', Pin.Direction.SOUTH, - x=100.0, y=0.0, delta=10.0, repeat=BIT_WIDTH) - create_pins(cell, 'b({})', 'b({}).0', Pin.Direction.NORTH, - x=20.0, y=height, delta=20.0, repeat=BIT_WIDTH) - - UpdateSession.close() - - if editor: - editor.setCell(cell) - - place_and_route(cell) - - UpdateSession.open() - - ab = cell.getAbutmentBox() - ab.inflate(to_DbU(-5.0)) - # do we really want to use net b(15)? - last_net = cell.getNet('b({})'.format(BIT_WIDTH-1)) - Pad.create(last_net, BLOCKAGE2, ab) - Pad.create(last_net, BLOCKAGE3, ab) - Pad.create(last_net, BLOCKAGE4, ab) - UpdateSession.close() - - RSavePlugin.ScriptMain(editor=editor, **kwargs) - - def alu16(editor=None, **kwargs): db = DataBase.getDB() print(db, dir(db)) METAL2 = get_layer('METAL2') - METAL3 = get_layer('METAL3') cell = af.getCell('alu16', CRL.Catalog.State.Logical) if not cell: print('[ERROR] Unable to load cell "alu16.vst", aborting .') return False - kwargs['cell'] = cell width, height = 1100.0, 600.0 ab = Box(l(0.0), l(0.0), l(width), l(height)) @@ -367,16 +225,41 @@ def alu16(editor=None, **kwargs): cell.setName(cell.getName()+'_r') af.saveCell(cell, CRL.Catalog.State.Views) - RSavePlugin.ScriptMain(editor=editor, **kwargs) + RSavePlugin.ScriptMain(editor=editor, cell=cell, **kwargs) return result -def ScriptMain(**kwargs): +def ScriptMain(editor=None, **kwargs): coriolis_setup() - add(**kwargs) - sub(**kwargs) - return alu16(**kwargs) + + add = Module( + 'add', editor, width=350.0, height=400.0, + north_pins=[ + {'net': 'a({})', 'x': 10.0, 'delta': 20.0, 'repeat': BIT_WIDTH}, + {'net': 'b({})', 'x': 20.0, 'delta': 20.0, 'repeat': BIT_WIDTH}, + ], + south_pins=[ + {'net': 'o({})', 'x': 100.0, 'delta': 10.0, 'repeat': BIT_WIDTH}, + ], + pads={'b(15)': ('BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4')}, + ) + + sub = Module( + 'sub', editor, width=350.0, height=400.0, + north_pins=[ + {'net': 'a({})', 'x': 10.0, 'delta': 20.0, 'repeat': BIT_WIDTH}, + {'net': 'b({})', 'x': 20.0, 'delta': 20.0, 'repeat': BIT_WIDTH}, + ], + south_pins=[ + {'net': 'o({})', 'x': 100.0, 'delta': 10.0, 'repeat': BIT_WIDTH}, + ], + pads={'b(15)': ('BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4')}, + ) + + add.do() + sub.do() + return alu16(editor, **kwargs) if __name__ == '__main__': diff --git a/experiments7/utils.py b/experiments7/utils.py new file mode 100644 index 0000000..1539231 --- /dev/null +++ b/experiments7/utils.py @@ -0,0 +1,315 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function + +import Anabatic +import CRL +import Etesian +import Katana +from Hurricane import ( + DbU, DataBase, UpdateSession, Box, Transformation, Instance, Pad, Pin, + NetExternalComponents, +) +from plugins import RSavePlugin + + +class Module: + + _layer_cache = {} + _instances = None + + def __init__(self, cell, editor, width=None, height=None, submodules=None, + pin_width=2.0, pin_height=2.0, pin_suffix='.0', + pin_layer=None, north_pins=None, east_pins=None, + south_pins=None, west_pins=None, pads=None, **kwargs): + """ + Creates a module. + + :param cell: cell name or Hurricane.Cell object, + :param editor: editor object when executing from cgt (or None), + :param width: module width, + :param height: module height, + :param submodules: submodules (Module objects), + :param pin_width: default pin width, + :param pin_height: default pin height, + :param pin_suffix: default pin suffix, + :param pin_layer: default layer for placing pins, + :param north_pins: list of pin configuration dictionaries for placing + pins on the north side, + :param east_pins: ditto (for the east side), + :param south_pins: ditto (for the south side), + :param west_pins: ditto (for the west side), + :param pads: dictionary of {net: list of layers} for creating pads, + :param kwargs: extra parameters to be implemented in derived classes. + """ + self.editor = editor + self.af = CRL.AllianceFramework.get() + self.db = DataBase.getDB() + self.width = width + self.height = height + + if isinstance(cell, basestring): + self.cell = self.af.getCell(cell, CRL.Catalog.State.Logical) + else: + self.cell = cell + + self.pin_width = pin_width + self.pin_height = pin_height + self.pin_suffix = pin_suffix + if pin_layer is None: + self.pin_layer = self.get_layer('METAL3') + elif isinstance(pin_layer, basestring): + self.pin_layer = self.get_layer(pin_layer) + else: + self.pin_layer = pin_layer + self.north_pins = north_pins or [] + self.east_pins = east_pins or [] + self.south_pins = south_pins or [] + self.west_pins = west_pins or [] + + self.pads = pads or {} + + self.submodules = submodules or [] + + @property + def name(self): + return self.cell.getName() + + def __str__(self): + return self.name + + @property + def ab(self): + """ The real abutment box. """ + return self.cell.getAbutmentBox() + + @ab.setter + def ab(self, ab): + self.cell.setAbutmentBox(ab) + + @property + def instances(self): + """ Cached instances. """ + if self._instances is None: + self._instances = self.cell.getInstances() + + return self._instances + + def get_layer(self, name): + """ Creates a new layer or returns it from cache. """ + if name in self._layer_cache: + return self._layer_cache[name] + + layer = self.db.getTechnology().getLayer(name) + self._layer_cache[name] = layer + return layer + + @staticmethod + def to_dbu(lmb): + """ + Convert lambdas to database units. (See Hurricane+Python Manual 3.4.) + """ + return DbU.fromLambda(lmb) + + @staticmethod + def from_dbu(dbu): + """ + Convert lambdas to database units. (See Hurricane+Python Manual 3.4.) + """ + return DbU.toLambda(dbu) + + def place_and_route(self): + """ Places and routes. """ + UpdateSession.open() + + etesian = Etesian.EtesianEngine.create(self.cell) + etesian.place() + + katana = Katana.KatanaEngine.create(self.cell) + katana.digitalInit() + katana.runGlobalRouter(Katana.Flags.NoFlags) + katana.loadGlobalRouting(Anabatic.EngineLoadGrByNet) + katana.layerAssign(Anabatic.EngineNoNetLayerAssign) + katana.runNegociate(Katana.Flags.NoFlags) + katana.finalizeLayout() + result = katana.isDetailedRoutingSuccess() + katana.destroy() + + UpdateSession.close() + return result + + def create_pin_series(self, net, direction, name=None, + status=Pin.PlacementStatus.FIXED, layer=None, + x=None, y=None, width=2.0, height=2.0, + repeat=1, delta=0.0, external=True): + """ + Creates a series of pins in a cell. + + :param net: Hurricane.Net object name or name template, taking a pin + enumeration parameter, i. e. pin number, + :param name: pin name or name template taking a pin enumeration + parameter (net name or template + suffix), + :param direction: Pin.Direction value, + :param status: Pin.PlacementStatus value (default is FIXED), + :param layer: Hurricane.Layer object or name (METAL3), + :param x: starting pin position (left to right, 0.0), + :param y: starting pin position (bottom to top, 0.0), + :param width: pin width (2,0), + :param height: pin height (2.0), + :param repeat: a number of pins to be placed or an iterable containing + pin template parameters (i. e. pin number, 1), + :param delta: next pin position offset (0.0), + :param external: mark pin as external (yes), + :return: tuple of next pin coordinates, or just (x, y), if delta + was not provided. + """ + if layer is None: + layer = self.pin_layer + elif isinstance(layer, basestring): + layer = self.get_layer(layer) + + if isinstance(repeat, int): + if repeat > 1 and delta == 0.0: + raise Warning('You are trying to place pins on each other.') + iterator = range(repeat) + else: + iterator = repeat + + if name is None: + name = net + self.pin_suffix + + for i in iterator: + pin = Pin.create(self.cell.getNet(net.format(i)), + name.format(i), direction, status, layer, + self.to_dbu(x), self.to_dbu(y), + self.to_dbu(width), self.to_dbu(height)) + if direction in (Pin.Direction.NORTH, Pin.Direction.SOUTH): + x += delta + else: + # EAST or WEST + y += delta + + if external: + pin.getNet().setExternal(True) + NetExternalComponents.setExternal(pin) + + return x, y + + def init_abutment_box(self): + """ Create the abutment box with object's initial values. """ + + UpdateSession.open() + self.ab = Box(0, 0, self.to_dbu(self.width), self.to_dbu(self.height)) + UpdateSession.close() + + def create_pins(self): + """ Creates all pins set on Module object creation. """ + + def default_x(): + if direction == Pin.Direction.EAST: + return self.from_dbu(self.ab.getWidth()) + if direction == Pin.Direction.WEST: + return 0 + return last_x + + def default_y(): + if direction == Pin.Direction.NORTH: + return self.from_dbu(self.ab.getHeight()) + if direction == Pin.Direction.SOUTH: + return 0 + return last_y + + UpdateSession.open() + + for pins, direction in ( + (self.north_pins, Pin.Direction.NORTH), + (self.east_pins, Pin.Direction.EAST), + (self.south_pins, Pin.Direction.SOUTH), + (self.west_pins, Pin.Direction.WEST), + ): + last_x = last_y = 0.0 + for pin_config in pins: + net = pin_config.pop('net') + name = pin_config.pop('name', net + self.pin_suffix) + layer = pin_config.pop('layer', self.pin_layer) + x = pin_config.pop('x', default_x()) + y = pin_config.pop('y', default_y()) + last_x, last_y = self.create_pin_series( + net, direction, name=name, layer=layer, x=x, y=y, + **pin_config) + + UpdateSession.close() + + def create_pads_for_net(self, net, layers): + """ + Creates a series of pads for a given net. + + :param net: net name or Hurricane.Net object to create pads for, + :param layers: list of layer names or Hurricane.Layer objects + to create pads on. + """ + temp_ab = self.ab + temp_ab.inflate(self.to_dbu(-5.0)) + if isinstance(net, basestring): + net = self.cell.getNet(net) + + for layer in layers: + if isinstance(layer, basestring): + layer = self.get_layer(layer) + Pad.create(net, layer, temp_ab) + + def create_pads(self): + """ Create all pads for a given Module object. """ + + UpdateSession.open() + + for net, layers in self.pads.items(): + self.create_pads_for_net(net, layers) + + UpdateSession.close() + + def do_submodules(self): + """ Execute submodules and gather their status. """ + + for submodule, x, y in self.submodules: + # execute submodule + if not submodule.do(): + return False + + return True + + def place_submodules(self): + """ Place submodules in the current module. """ + + for submodule, x, y in self.submodules: + # find instance + instance = [ + inst for inst in self.instances if inst.getName().endswith( + submodule.name + ) + ][0] + + # place submodule + instance.setTransformation(Transformation( + self.to_dbu(x), self.to_dbu(y), Transformation.Orientation.ID + )) + instance.setPlacementStatus(Instance.PlacementStatus.FIXED) + + def do(self): + """ Main routine. """ + + if not self.do_submodules(): + return False + + self.init_abutment_box() + + self.create_pins() + + if self.editor: + self.editor.setCell(self.cell) + + result = self.place_and_route() + + self.create_pads() + + RSavePlugin.ScriptMain(editor=self.editor, cell=self.cell) + return result