Optimized (datapath) placement and direct place.
authorJean-Paul Chaput <Jean-Paul.Chaput@lip6.fr>
Mon, 20 Apr 2020 12:36:34 +0000 (14:36 +0200)
committerJean-Paul Chaput <Jean-Paul.Chaput@lip6.fr>
Mon, 20 Apr 2020 12:36:34 +0000 (14:36 +0200)
experiments7/Status.rst [new file with mode: 0644]
experiments7/coriolis2/katana.py
experiments7/coriolis2/settings.py
experiments7/doAlu16.py
experiments7/doAlu16Flat.py [new file with mode: 0644]
experiments7/utils.py

diff --git a/experiments7/Status.rst b/experiments7/Status.rst
new file mode 100644 (file)
index 0000000..1393928
--- /dev/null
@@ -0,0 +1,52 @@
+
+===============================
+Status Report on Block Building
+===============================
+
+
+April 04, 2020 -- Optimized Placement
+=====================================
+
+The placement of the ALU16 as been optimized.
+
+* The gates of the top level netlist that form a regular array (datapath)
+  have been manually placed between the add & sub blocks.
+
+* The connectors of the add & sub blocks have been put face to face to have
+  as much as possible straight lines.
+
+* The connector of the add & sub block have been placed in a "slice" 
+  way: that is a(x), b(x) & o(x) all in the same slice. A slice is one
+  horizontal row of standard cells.
+
+As a comparison, an alternate version of the script "doAlu16Flat.py" has
+been created to make an area comparison if everything is left to the
+automaic P&R. For such a small example, they clearly win. Have to confirm
+that on bigger ones.
+
+
+Open/Remaining Questions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+* The placement is manual, but the routing is automatic for the center
+  part. The router still makes some "bad choices", so it would be
+  interesting to do a manual routing too.
+
+* The layout is not completely finished: the filler cells are missing
+  at top level. Have to separate and export that lone feature from
+  Etesian (placer).
+
+* The way to "rebuild" the datapath information is not robust. It relies
+  on the way Yosys/ABC synthesis the mux+DFF, if it changes, it will fail.
+
+* Corollary: it may make senses to directly describe some part of the
+  design at cell level and not let it synthesis by Yosys, but in this
+  case how to "import" at nMigen level.
+
+* The place_datapath() and match_instance() method may be moved down to
+  the Module class.
+
+* Policy about how soon to convert into DbU.
+
+* Specify the semantic of Module. Is it an instance or a model, can they
+  be re-used in multiple blocks (should they?).
index 6755b11c4a49a5546d4a632a18ac7b463330674a..13768a0f99dd441e8109640df0d1b0b9f015af0a 100644 (file)
@@ -1 +1,7 @@
 from Hurricane import DebugSession
+
+#DebugSession.addToTrace( katana.getCell().getNet( 'add_o(1)' ) )
+#DebugSession.addToTrace( katana.getCell().getNet( 'add_o(2)' ) )
+#DebugSession.addToTrace( katana.getCell().getNet( 'b(3)' ) )
+#DebugSession.addToTrace( katana.getCell().getNet( 'b(15)' ) )
+#DebugSession.addToTrace( katana.getCell().getNet( 'add_o(12)' ) )
index b4f7c85946c7ccbaa5888f7d8899708a4398b7c4..c95822a8fc184f182379755e741bf721350bdf08 100644 (file)
@@ -18,7 +18,7 @@ Cfg.getParamBool('misc.logMode').setBool(True)
 Cfg.getParamBool('misc.verboseLevel1').setBool(True)
 Cfg.getParamBool('misc.verboseLevel2').setBool(True)
 Cfg.getParamEnumerate('etesian.effort').setInt(2)
-Cfg.getParamPercentage('etesian.spaceMargin').setPercentage(20.0)
+Cfg.getParamPercentage('etesian.spaceMargin').setPercentage(5.0)
 Cfg.getParamPercentage('etesian.aspectRatio').setPercentage(100.0)
 Cfg.getParamBool('etesian.uniformDensity').setBool(True)
 Cfg.getParamInt('anabatic.edgeLenght').setInt(24)
index aa9dbda4b6000151bcb61212ec708cd173a3c2f3..70b6d5e584e019c86a48d65ca8d39a55504772ac 100755 (executable)
@@ -2,11 +2,14 @@
 # -*- coding: utf-8 -*-
 from __future__ import print_function
 import sys
+import re
 
 import CRL
 import Cfg
 from Hurricane import Box
 from Hurricane import Transformation
+from Hurricane import Breakpoint
+from Hurricane import Instance
 from coriolis2.settings import af
 from utils import Module, SessionManager, Config
 
@@ -48,7 +51,7 @@ class AddSub(Module):
         """ Main routine. """
 
         with SessionManager():
-            self.compute_ab()
+           #self.compute_ab()
             self.create_pins()
 
         if self.editor:
@@ -65,6 +68,69 @@ class AddSub(Module):
 
 class ALU16(Module):
 
+    @staticmethod
+    def match_instance(datapath_insts, op, plug_name, inst):
+        """
+        Guess the position of an instance from it's nets connections,
+        and put it at the right place in the datapath vector.
+
+        :param datapath_insts: vector of bit slices,
+        :param op: operator name,
+        :param inst: instance to classify,
+        :param plug_name: name of the plug to use to guess the bit index,
+        :return: boolean, True if the instance has been matched.
+        """
+        matched = False
+        re_net_index = re.compile(r'[^(]+\((?P<index>[\d]+)\)$')
+        if inst.getMasterCell().getName().startswith(op):
+            for plug in inst.getPlugs():
+                if plug.getMasterNet().getName() == plug_name:
+                    m = re_net_index.match(plug.getNet().getName())
+                    if m:
+                        bit_slice = datapath_insts[int(m.group('index'))]
+                        for column in bit_slice:
+                            if column[0] == op:
+                                column[1] = inst
+                                matched = True
+                                break
+                        break
+        return matched
+
+    def place_datapath(self, datapath_insts, x_orig, y_orig, fold):
+        channel_sff1 = self.to_dbu(60)
+        with SessionManager():
+            slice_height = self.to_dbu(50.0)
+            for i in range(len(datapath_insts)):
+                if not i%fold:
+                    x = self.to_dbu(x_orig)
+                slice = i/fold
+                if slice%2:
+                    y = self.to_dbu(y_orig) + slice_height*(slice+1)
+                    orient = Transformation.Orientation.MY
+                else:
+                    y = self.to_dbu(y_orig) + slice_height*slice
+                    orient = Transformation.Orientation.ID
+
+                for (opname, inst) in datapath_insts[i]:
+                    if opname == 'sff1':
+                        x += channel_sff1
+                    inst.setTransformation(Transformation(x, y, orient))
+                    inst.setPlacementStatus(Instance.PlacementStatus.PLACED)
+                    x += inst.getAbutmentBox().getWidth()
+
+    def place(self):
+        """ALU16.place(), manual placement overload."""
+        datapath_insts = []
+        for i in range(BIT_WIDTH):
+            datapath_insts.append([['nmx2', None], ['no2', None], ['sff1', None]])
+
+        for inst in self.cell.getInstances():
+            if ALU16.match_instance(datapath_insts, 'nmx2', 'i0', inst): continue
+            if ALU16.match_instance(datapath_insts, 'no2', 'nq', inst): continue
+            if ALU16.match_instance(datapath_insts, 'sff1', 'i', inst): continue
+
+        self.place_datapath(datapath_insts, 160.0, 50.0, 1)
+
     def save(self):
         self.name = self.name + '_r'
         self.af.saveCell(self.cell, CRL.Catalog.State.Views)
@@ -72,8 +138,8 @@ class ALU16(Module):
 
     def build(self):
 
-        h_margin = 25.0
-        v_margin = 10.0
+        h_margin = 0.0
+        v_margin = 50.0
 
         if not self.build_submodules():
             return False
@@ -91,20 +157,20 @@ class ALU16(Module):
             ) + 4*h_margin
             height = self.from_dbu(max([
                 self.ab.getHeight(), add.ab.getHeight(), sub.ab.getHeight()
-            ])) + 2*v_margin
+            ])) + v_margin
 
             # experiment, over-ride
-            width = 1310
-            height = 370
+            width = 580
+            #width = 1310
+            #height = 370
 
             self.ab = Box(0, 0, self.to_dbu(width), self.to_dbu(height))
 
             add_wid = self.from_dbu(add.ab.getWidth())
             sub_ht = self.from_dbu(sub.ab.getHeight())
 
-            self.place_submodule(add, 25, v_margin+add_wid)
-            self.place_submodule(sub, width-sub.ab_width-h_margin+sub_ht-35,
-                                      v_margin)
+            self.place_submodule(add, 0, v_margin)
+            self.place_submodule(sub, 400, v_margin)
 
             # TODO: replace with some form of lazy evaluation?
             y_north = self.from_dbu(self.ab.getYMax())
@@ -123,74 +189,72 @@ class ALU16(Module):
 
         # XXX this doesn't work: box is far too big, covers the entire
         # area (including "under" the add and sub)
-        self.ab = Box(
-            self.to_dbu((width-self.ab_width)/2 - h_margin),
-            self.to_dbu(v_margin),
-            self.to_dbu((width+self.ab_width)/2 + h_margin),
-            self.to_dbu(height - v_margin)
-        )
-        self.ab = Box(self.to_dbu(475), self.to_dbu(10),
-                      self.to_dbu(840), self.to_dbu(360))
+        #self.ab = Box(
+        #    self.to_dbu((width-self.ab_width)/2 - h_margin),
+        #    self.to_dbu(v_margin),
+        #    self.to_dbu((width+self.ab_width)/2 + h_margin),
+        #    self.to_dbu(height - v_margin)
+        #)
+        #self.ab = Box(self.to_dbu(475), self.to_dbu(10),
+        #              self.to_dbu(840), self.to_dbu(360))
+        self.ab = Box(0, 0, self.to_dbu(width), self.to_dbu(height))
         self.place() # place only
 
         # then route (globally)
         # this connects up not just in the remaining (little) cells,
         # it connects *to add and sub and the outside world as well*
-        self.ab = Box(0, 0, self.to_dbu(width), self.to_dbu(height))
         result = self.route()
 
         self.save()
         return result
 
 
-def ScriptMain(editor=None, **kwargs):
+def scriptMain(editor=None, **kwargs):
     coriolis_setup()
 
     add = AddSub(
         'add', editor,
-        north_pins=[
-            {'net': 'a({})', 'x': 165.0, 'delta': -10.0, 'repeat': BIT_WIDTH},
-            {'net': 'b({})', 'x': 160.0, 'delta': -10.0, 'repeat': BIT_WIDTH},
-            {'net': 'o({})', 'x': 340.0, 'delta': -10.0, 'repeat': BIT_WIDTH},
-        ],
-        south_pins=[
+        east_pins=[
+            {'net': 'a({})', 'y': 15.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
+            {'net': 'b({})', 'y': 25.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
+            {'net': 'o({})', 'y': 35.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
         ],
         pads={
             'b({})'.format(BIT_WIDTH-1): (
                 'BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4',
             ),
         },
-        orientation=Transformation.Orientation.R3,
+        orientation=Transformation.Orientation.ID,
     )
+    add.set_ab(160.0, 800.0)
     sub = AddSub(
         'sub', editor,
-        north_pins=[
-            {'net': 'a({})', 'x': 180.0, 'delta': 10.0, 'repeat': BIT_WIDTH},
-            {'net': 'b({})', 'x': 185.0, 'delta': 10.0, 'repeat': BIT_WIDTH},
-            {'net': 'o({})', 'x': 10.0, 'delta': 10.0, 'repeat': BIT_WIDTH},
-        ],
-        south_pins=[
+        west_pins=[
+            {'net': 'a({})', 'y': 15.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
+            {'net': 'b({})', 'y': 25.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
+            {'net': 'o({})', 'y': 35.0, 'delta': 50.0, 'repeat': BIT_WIDTH, 'layer': 'METAL2'},
         ],
         pads={
             'b({})'.format(BIT_WIDTH-1): (
                 'BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4',
             ),
         },
-        orientation=Transformation.Orientation.R1,
+        orientation=Transformation.Orientation.ID,
     )
+    sub.set_ab(180.0, 800.0)
 
     alu16 = ALU16(
         'alu16', editor, submodules=[add, sub],
         north_pins=[
-            {'net': 'o({})', 'x': 500.0, 'delta': 20.0, 'repeat': BIT_WIDTH},
+            {'net': 'o({})', 'x': 365.0, 'delta': -5.0, 'repeat': BIT_WIDTH},
             {'net': 'op'},
         ],
         south_pins=[
-            {'net': 'a({})', 'x': 500.0, 'delta': 10.0, 'repeat': BIT_WIDTH},
-            {'net': 'b({})', 'x': 700.0, 'delta': 10.0, 'repeat': BIT_WIDTH},
+            {'net': 'a({})', 'x': 205.0, 'delta': 5.0, 'repeat': BIT_WIDTH},
+            {'net': 'b({})', 'x': 295.0, 'delta': 5.0, 'repeat': BIT_WIDTH},
         ],
         west_pins=[
-            {'net': 'rst', 'y': 140.0, 'layer': 'METAL2'},
+            {'net': 'rst', 'y': 10.0, 'layer': 'METAL2'},
         ],
     )
     return alu16.build()
@@ -198,7 +262,7 @@ def ScriptMain(editor=None, **kwargs):
 
 if __name__ == '__main__':
     kwargs = {}
-    success = ScriptMain(**kwargs)
+    success = scriptMain(**kwargs)
     shellSuccess = 0
     if not success:
         shellSuccess = 1
diff --git a/experiments7/doAlu16Flat.py b/experiments7/doAlu16Flat.py
new file mode 100644 (file)
index 0000000..0bdb347
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+import sys
+import re
+
+import CRL
+import Cfg
+from Hurricane import Box
+from Hurricane import Transformation
+from Hurricane import Breakpoint
+from Hurricane import Instance
+from coriolis2.settings import af
+from utils import Module, SessionManager, Config
+
+import symbolic.cmos  # do not remove
+
+BIT_WIDTH = 16
+
+
+def coriolis_setup():
+    with Config(Cfg.Parameter.Priority.UserFile) as cfg:
+        cfg.misc_catchCore = False
+        cfg.misc_info = False
+        cfg.misc_paranoid = False
+        cfg.misc_bug = False
+        cfg.misc_logMode = True
+        cfg.misc_verboseLevel1 = True
+        cfg.misc_verboseLevel2 = True
+        cfg.etesian_effort = 2
+        cfg.etesian_spaceMargin = "5.0%"
+        cfg.etesian_aspectRatio = "100.0%"
+        cfg.etesian_uniformDensity = True
+        cfg.anabatic_edgeLenght = 24
+        cfg.anabatic_edgeWidth = 8
+        cfg.anabatic_topRoutingLayer = 'METAL5'
+        cfg.katana_searchHalo = 30
+        cfg.katana_eventsLimit = 1000000
+        cfg.katana_hTracksReservedLocal = 7
+        cfg.katana_vTracksReservedLocal = 6
+
+        env = af.getEnvironment()
+        env.setCLOCK('^clk$|m_clock')
+        env.setPOWER('vdd')
+        env.setGROUND('vss')
+
+
+class AddSub(Module):
+
+    def build(self):
+        """ Main routine. """
+
+        return True
+
+
+class ALU16(Module):
+
+    def save(self):
+        self.name = self.name + '_r'
+        self.af.saveCell(self.cell, CRL.Catalog.State.Views)
+        super(ALU16, self).save()
+
+    def build(self):
+
+        h_margin = 0.0
+        v_margin = 50.0
+
+        if not self.build_submodules():
+            return False
+
+        # at this point we have the (auto-calculated) submodules' dimensions
+        # in their `ab` properties.
+
+        add, sub = self.submodules
+
+        with SessionManager():
+            # TODO: replace with some form of lazy evaluation?
+            y_north = self.from_dbu(self.ab.getYMax())
+            for pin_conf in self.north_pins:
+                pin_conf['y'] = y_north
+
+            self.create_pins()
+
+        if self.editor:
+            self.editor.setCell(self.cell)
+
+        self.place() # place only
+        Breakpoint.stop(1, 'After ALU16 placement.')
+        result = self.route()
+
+        self.save()
+        return result
+
+
+def scriptMain(editor=None, **kwargs):
+    coriolis_setup()
+
+    add = AddSub(
+        'add', editor,
+        pads={
+            'b({})'.format(BIT_WIDTH-1): (
+                'BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4',
+            ),
+        },
+        orientation=Transformation.Orientation.ID,
+    )
+    sub = AddSub(
+        'sub', editor,
+        pads={
+            'b({})'.format(BIT_WIDTH-1): (
+                'BLOCKAGE2', 'BLOCKAGE3', 'BLOCKAGE4',
+            ),
+        },
+        orientation=Transformation.Orientation.ID,
+    )
+
+    alu16 = ALU16(
+        'alu16', editor, submodules=[add, sub],
+        north_pins=[
+            {'net': 'o({})', 'x': 365.0, 'delta': -5.0, 'repeat': BIT_WIDTH},
+            {'net': 'op'},
+        ],
+        south_pins=[
+            {'net': 'a({})', 'x': 205.0, 'delta': 5.0, 'repeat': BIT_WIDTH},
+            {'net': 'b({})', 'x': 295.0, 'delta': 5.0, 'repeat': BIT_WIDTH},
+        ],
+        west_pins=[
+            {'net': 'rst', 'y': 10.0, 'layer': 'METAL2'},
+        ],
+    )
+    alu16.set_ab( 600, 650 )
+    return alu16.build()
+
+
+if __name__ == '__main__':
+    kwargs = {}
+    success = scriptMain(**kwargs)
+    shellSuccess = 0
+    if not success:
+        shellSuccess = 1
+
+    sys.exit(shellSuccess)
index 04f9cdea684e6051f48d7919230a4b8af616e91f..e575bd33afff19ea64933c8caeaec05019b86dc7 100644 (file)
@@ -7,10 +7,10 @@ import Cfg
 import Etesian
 import Katana
 from Hurricane import (
-    DbU, DataBase, UpdateSession, Box, Transformation, Instance,  Pad, Pin,
+    Breakpoint, DbU, DataBase, UpdateSession, Box, Transformation, Instance,  Pad, Pin,
     NetExternalComponents,
 )
-from plugins import RSavePlugin
+from plugins import rsave
 
 
 class SessionManager(object):
@@ -170,6 +170,14 @@ class Module(object):
         etesian.setDefaultAb()
         etesian.destroy()
 
+    def set_ab(self, width, height):
+        """
+        Let the user specify the abutment box. Bottom left corner always
+        sets to (0,0). 
+        """
+        self.cell.setAbutmentBox(Box(self.to_dbu(0.0), self.to_dbu(0.0),
+                                     self.to_dbu(width), self.to_dbu(height)))
+
     def place(self):
         """ Places the current cell. """
         etesian = Etesian.EtesianEngine.create(self.cell)
@@ -183,6 +191,7 @@ class Module(object):
         katana.loadGlobalRouting(Anabatic.EngineLoadGrByNet)
         katana.layerAssign(Anabatic.EngineNoNetLayerAssign)
         katana.runNegociate(Katana.Flags.NoFlags)
+        Breakpoint.stop(0, 'After routing {0}'.format(self.cell))
         katana.finalizeLayout()
         result = katana.isDetailedRoutingSuccess()
         katana.destroy()
@@ -387,7 +396,7 @@ class Module(object):
 
     def save(self):
         """ Saves cell. """
-        RSavePlugin.ScriptMain(editor=self.editor, cell=self.cell)
+        rsave.scriptMain(editor=self.editor, cell=self.cell)
 
     def build(self):
         """ Main routine. """