--- /dev/null
+
+===============================
+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?).
# -*- 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
""" Main routine. """
with SessionManager():
- self.compute_ab()
+ #self.compute_ab()
self.create_pins()
if self.editor:
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)
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
) + 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())
# 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()
if __name__ == '__main__':
kwargs = {}
- success = ScriptMain(**kwargs)
+ success = scriptMain(**kwargs)
shellSuccess = 0
if not success:
shellSuccess = 1
--- /dev/null
+#!/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)