Distinguish unset submodule placement.
[soclayout.git] / experiments7 / utils.py
1 # -*- coding: utf-8 -*-
2 from __future__ import print_function
3
4 import Anabatic
5 import CRL
6 import Cfg
7 import Etesian
8 import Katana
9 from Hurricane import (
10 DbU, DataBase, UpdateSession, Box, Transformation, Instance, Pad, Pin,
11 NetExternalComponents,
12 )
13 from plugins import RSavePlugin
14
15
16 class SessionManager(object):
17 """
18 Context manager for a GO update session. See Hurricane reference manual
19 for an info on Hurricane::UpdateSession class.
20 """
21
22 def __enter__(self):
23 UpdateSession.open()
24
25 def __exit__(self, *args):
26 UpdateSession.close()
27
28
29 class Module(object):
30
31 _layer_cache = {}
32 _instances = None
33
34 def __init__(self, cell, editor, width=None, height=None, submodules=None,
35 pin_width=2.0, pin_height=2.0, pin_suffix='.0',
36 pin_layer=None, north_pins=None, east_pins=None,
37 south_pins=None, west_pins=None, pads=None, **kwargs):
38 """
39 Creates a module.
40
41 :param cell: cell name or Hurricane.Cell object,
42 :param editor: editor object when executing from cgt (or None),
43 :param width: module width,
44 :param height: module height,
45 :param submodules: submodules (Module objects)
46 or tuples of (submodule, x, y), where (x, y) is a submodule's
47 placement point in lambdas,
48 :param pin_width: default pin width,
49 :param pin_height: default pin height,
50 :param pin_suffix: default pin suffix,
51 :param pin_layer: default layer for placing pins,
52 :param north_pins: list of pin configuration dictionaries for placing
53 pins on the north side,
54 :param east_pins: ditto (for the east side),
55 :param south_pins: ditto (for the south side),
56 :param west_pins: ditto (for the west side),
57 :param pads: dictionary of {net: list of layers} for creating pads,
58 :param kwargs: extra parameters to be implemented in derived classes.
59 """
60 self.editor = editor
61 self.af = CRL.AllianceFramework.get()
62 self.db = DataBase.getDB()
63 self.width = width
64 self.height = height
65
66 if isinstance(cell, basestring):
67 self.cell = self.af.getCell(cell, CRL.Catalog.State.Logical)
68 else:
69 self.cell = cell
70
71 self.pin_width = pin_width
72 self.pin_height = pin_height
73 self.pin_suffix = pin_suffix
74 if pin_layer is None:
75 self.pin_layer = self.get_layer('METAL3')
76 elif isinstance(pin_layer, basestring):
77 self.pin_layer = self.get_layer(pin_layer)
78 else:
79 self.pin_layer = pin_layer
80 self.north_pins = north_pins or []
81 self.east_pins = east_pins or []
82 self.south_pins = south_pins or []
83 self.west_pins = west_pins or []
84
85 self.pads = pads or {}
86
87 self._submodules = []
88 if submodules is not None:
89 for submodule in submodules:
90 self._submodules.append(
91 (submodule, None, None) if isinstance(submodule, Module)
92 else submodule
93 )
94
95 @property
96 def name(self):
97 return self.cell.getName()
98
99 @name.setter
100 def name(self, name):
101 self.cell.setName(name)
102
103 def __str__(self):
104 return self.name
105
106 @property
107 def ab(self):
108 """ The real abutment box. """
109 return self.cell.getAbutmentBox()
110
111 @ab.setter
112 def ab(self, ab):
113 self.cell.setAbutmentBox(ab)
114
115 @property
116 def instances(self):
117 """ Cached instances. """
118 if self._instances is None:
119 self._instances = self.cell.getInstances()
120
121 return self._instances
122
123 def get_layer(self, name):
124 """ Creates a new layer or returns it from cache. """
125 if name in self._layer_cache:
126 return self._layer_cache[name]
127
128 layer = self.db.getTechnology().getLayer(name)
129 self._layer_cache[name] = layer
130 return layer
131
132 @staticmethod
133 def to_dbu(lmb):
134 """
135 Convert lambdas to database units. (See Hurricane+Python Manual 3.4.)
136 """
137 return DbU.fromLambda(lmb)
138
139 @staticmethod
140 def from_dbu(dbu):
141 """
142 Convert lambdas to database units. (See Hurricane+Python Manual 3.4.)
143 """
144 return DbU.toLambda(dbu)
145
146 def compute_ab(self):
147 """ Compute default abutment box without placement. """
148 etesian = Etesian.EtesianEngine.create(self.cell)
149 etesian.setDefaultAb()
150 etesian.destroy()
151
152 def place(self):
153 """ Places the current cell. """
154 etesian = Etesian.EtesianEngine.create(self.cell)
155 etesian.place()
156
157 def route(self):
158 """ Routes the current cell. """
159 katana = Katana.KatanaEngine.create(self.cell)
160 katana.digitalInit()
161 katana.runGlobalRouter(Katana.Flags.NoFlags)
162 katana.loadGlobalRouting(Anabatic.EngineLoadGrByNet)
163 katana.layerAssign(Anabatic.EngineNoNetLayerAssign)
164 katana.runNegociate(Katana.Flags.NoFlags)
165 katana.finalizeLayout()
166 result = katana.isDetailedRoutingSuccess()
167 katana.destroy()
168 return result
169
170 def place_and_route(self):
171 """ Places and routes. """
172 self.place()
173 return self.route()
174
175 def create_pin_series(self, net, direction, name=None,
176 status=Pin.PlacementStatus.FIXED, layer=None,
177 x=None, y=None, width=2.0, height=2.0,
178 repeat=1, delta=0.0, external=True):
179 """
180 Creates a pin or a series of pins in a cell.
181
182 :param net: Hurricane.Net object name or name template, taking a pin
183 enumeration parameter, i. e. pin number,
184 :param name: pin name or name template taking a pin enumeration
185 parameter (net name or template + suffix),
186 :param direction: Pin.Direction value,
187 :param status: Pin.PlacementStatus value (default is FIXED),
188 :param layer: Hurricane.Layer object or name (METAL3),
189 :param x: starting pin position (left to right, 0.0),
190 :param y: starting pin position (bottom to top, 0.0),
191 :param width: pin width (2,0),
192 :param height: pin height (2.0),
193 :param repeat: a number of pins to be placed or an iterable containing
194 pin template parameters (i. e. pin number, 1),
195 :param delta: next pin position offset (0.0),
196 :param external: mark pin as external (yes),
197 :return: tuple of next pin coordinates, or just (x, y), if delta
198 was not provided.
199 """
200 if layer is None:
201 layer = self.pin_layer
202 elif isinstance(layer, basestring):
203 layer = self.get_layer(layer)
204
205 if isinstance(repeat, int):
206 if repeat > 1 and delta == 0.0:
207 raise Warning(
208 '{}: you are trying to place pins on each other.'.format(
209 self.name
210 )
211 )
212 iterator = range(repeat)
213 else:
214 iterator = repeat
215
216 if name is None:
217 name = net + self.pin_suffix
218
219 for i in iterator:
220 pin = Pin.create(self.cell.getNet(net.format(i)),
221 name.format(i), direction, status, layer,
222 self.to_dbu(x), self.to_dbu(y),
223 self.to_dbu(width), self.to_dbu(height))
224 if direction in (Pin.Direction.NORTH, Pin.Direction.SOUTH):
225 x += delta
226 else:
227 # EAST or WEST
228 y += delta
229
230 if external:
231 pin.getNet().setExternal(True)
232 NetExternalComponents.setExternal(pin)
233
234 return x, y
235
236 def init_ab(self):
237 """ Create the abutment box with object's initial values. """
238 if self.width and self.height:
239 self.ab = Box(
240 0, 0, self.to_dbu(self.width), self.to_dbu(self.height)
241 )
242 else:
243 raise Warning('{}: Module size is not set.'.format(self.name))
244
245 def create_pins(self):
246 """ Creates all pins set on Module object creation. """
247
248 def default_x():
249 if direction == Pin.Direction.EAST:
250 return self.from_dbu(self.ab.getWidth())
251 if direction == Pin.Direction.WEST:
252 return 0
253 return last_x
254
255 def default_y():
256 if direction == Pin.Direction.NORTH:
257 return self.from_dbu(self.ab.getHeight())
258 if direction == Pin.Direction.SOUTH:
259 return 0
260 return last_y
261
262 for pins, direction in (
263 (self.north_pins, Pin.Direction.NORTH),
264 (self.east_pins, Pin.Direction.EAST),
265 (self.south_pins, Pin.Direction.SOUTH),
266 (self.west_pins, Pin.Direction.WEST),
267 ):
268 last_x = last_y = 0.0
269 for pin_config in pins:
270 net = pin_config.pop('net')
271 name = pin_config.pop('name', net + self.pin_suffix)
272 layer = pin_config.pop('layer', self.pin_layer)
273 x = pin_config.pop('x', default_x())
274 y = pin_config.pop('y', default_y())
275 last_x, last_y = self.create_pin_series(
276 net, direction, name=name, layer=layer, x=x, y=y,
277 **pin_config)
278
279 def create_pads_for_net(self, net, layers):
280 """
281 Creates a series of pads for a given net.
282
283 :param net: net name or Hurricane.Net object to create pads for,
284 :param layers: list of layer names or Hurricane.Layer objects
285 to create pads on.
286 """
287 temp_ab = self.ab
288 temp_ab.inflate(self.to_dbu(-5.0))
289 if isinstance(net, basestring):
290 net = self.cell.getNet(net)
291
292 for layer in layers:
293 if isinstance(layer, basestring):
294 layer = self.get_layer(layer)
295 Pad.create(net, layer, temp_ab)
296
297 def create_pads(self):
298 """ Create all pads for a given Module object. """
299
300 for net, layers in self.pads.items():
301 self.create_pads_for_net(net, layers)
302
303 @property
304 def submodules(self):
305 """ Submodules iterator. """
306 return iter(submodule for submodule, x, y in self._submodules)
307
308 def find_submodule(self, name):
309 """
310 Returns first submodule matching `name`. Better give your submodules
311 unique names.
312
313 :param name: submodule name to match,
314 :return: `Module` object.
315 """
316 return next(s for s in self.submodules if s.name == name)
317
318 def build_submodules(self):
319 """
320 Execute submodules and gather their status.
321
322 :return: True if all submodules executed successfully, False otherwise.
323 """
324
325 for submodule in self.submodules:
326 if not submodule.build():
327 return False
328
329 return True
330
331 def place_submodule(self, submodule, x, y):
332 """
333 Places a submodule to a given location.
334
335 :param submodule: `Module` object,
336 :param x: placement coordinate,
337 :param y: placement coordinate.
338 """
339
340 # find instance
341 instance = [
342 inst for inst in self.instances if inst.getName().endswith(
343 submodule.name
344 )
345 ][0]
346
347 # place submodule
348 instance.setTransformation(Transformation(
349 self.to_dbu(x), self.to_dbu(y), Transformation.Orientation.ID
350 ))
351 instance.setPlacementStatus(Instance.PlacementStatus.FIXED)
352
353 def place_submodules(self):
354 """
355 Places the submodules in the current module using their initial
356 placement points, if set.
357 """
358 for submodule, x, y in self._submodules:
359 if x is not None and y is not None:
360 self.place_submodule(submodule, x, y)
361 else:
362 raise Warning((
363 '{}: cannot place {}, because its '
364 'initial placement point is not set.'
365 ).format(self.name, submodule.name))
366
367 def save(self):
368 """ Saves cell. """
369 RSavePlugin.ScriptMain(editor=self.editor, cell=self.cell)
370
371 def build(self):
372 """ Main routine. """
373
374 raise NotImplementedError('You need to implement the `build` method.')
375
376
377 class Config:
378
379 def __init__(self, priority=None):
380 self._priority = priority
381
382 def __enter__(self):
383 if self._priority is not None:
384 Cfg.Configuration.pushDefaultPriority(self._priority)
385 return self
386
387 def __setattr__(self, attr, val):
388 if attr.startswith("_"):
389 self.__dict__[attr] = val
390 return
391 attr = attr.replace("_", ".")
392 if isinstance(val, bool):
393 Cfg.getParamBool(attr).setBool(val)
394 elif isinstance(val, int):
395 p = Cfg.getParamInt(attr) # all params have a type
396 if p.type == 'Enumerate':
397 Cfg.getParamEnumerate(attr).setInt(val)
398 else:
399 Cfg.getParamInt(attr).setInt(val)
400 elif '%' in val:
401 Cfg.getParamPercentage(attr).setPercentage(float(val[:-1]))
402 else:
403 Cfg.getParamString(attr).setString(val)
404
405 def __exit__(self, *args):
406 if self._priority is not None:
407 Cfg.Configuration.popDefaultPriority()
408