Replace submodule functions with Module objects.
authorJock Tanner <tanner.of.kha@gmail.com>
Wed, 25 Mar 2020 05:02:32 +0000 (05:02 +0000)
committerJock Tanner <tanner.of.kha@gmail.com>
Thu, 26 Mar 2020 06:58:43 +0000 (06:58 +0000)
experiments7/doAlu16.py
experiments7/utils.py [new file with mode: 0644]

index 1118ec136199719e87282b46920bb70381677c37..7f9971ed52070b60bb958955ae5d341e75f9a192 100755 (executable)
@@ -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 (file)
index 0000000..1539231
--- /dev/null
@@ -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