From: Jean-Paul Chaput Date: Mon, 20 Apr 2020 12:36:34 +0000 (+0200) Subject: Optimized (datapath) placement and direct place. X-Git-Tag: partial-core-ls180-gdsii~142 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=861d43310ffc25f030a292e2b20cbca56e1f46b2;p=soclayout.git Optimized (datapath) placement and direct place. --- diff --git a/experiments7/Status.rst b/experiments7/Status.rst new file mode 100644 index 0000000..1393928 --- /dev/null +++ b/experiments7/Status.rst @@ -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?). diff --git a/experiments7/coriolis2/katana.py b/experiments7/coriolis2/katana.py index 6755b11..13768a0 100644 --- a/experiments7/coriolis2/katana.py +++ b/experiments7/coriolis2/katana.py @@ -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)' ) ) diff --git a/experiments7/coriolis2/settings.py b/experiments7/coriolis2/settings.py index b4f7c85..c95822a 100644 --- a/experiments7/coriolis2/settings.py +++ b/experiments7/coriolis2/settings.py @@ -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) diff --git a/experiments7/doAlu16.py b/experiments7/doAlu16.py index aa9dbda..70b6d5e 100755 --- a/experiments7/doAlu16.py +++ b/experiments7/doAlu16.py @@ -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[\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 index 0000000..0bdb347 --- /dev/null +++ b/experiments7/doAlu16Flat.py @@ -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) diff --git a/experiments7/utils.py b/experiments7/utils.py index 04f9cde..e575bd3 100644 --- a/experiments7/utils.py +++ b/experiments7/utils.py @@ -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. """