Replace submodule functions with Module objects.
[soclayout.git] / experiments7 / utils.py
1 # -*- coding: utf-8 -*-
2 from __future__ import print_function
3
4 import Anabatic
5 import CRL
6 import Etesian
7 import Katana
8 from Hurricane import (
9 DbU, DataBase, UpdateSession, Box, Transformation, Instance, Pad, Pin,
10 NetExternalComponents,
11 )
12
13
14 class SessionManager(object):
15 """
16 Context manager for a GO update session. See Hurricane reference manual
17 for an info on Hurricane::UpdateSession class.
18 """
19
20 def __enter__(self):
21 UpdateSession.open()
22
23 def __exit__(self, *args):
24 UpdateSession.close()
25
26
27 class Module(object):
28
29 _layer_cache = {}
30 _instances = None
31
32 def __init__(self, cell, editor, width=None, height=None, submodules=None,
33 pin_width=2.0, pin_height=2.0, pin_suffix='.0',
34 pin_layer=None, north_pins=None, east_pins=None,
35 south_pins=None, west_pins=None, pads=None, **kwargs):
36 """
37 Creates a module.
38
39 :param cell: cell name or Hurricane.Cell object,
40 :param editor: editor object when executing from cgt (or None),
41 :param width: module width,
42 :param height: module height,
43 :param submodules: submodules (Module objects),
44 :param pin_width: default pin width,
45 :param pin_height: default pin height,
46 :param pin_suffix: default pin suffix,
47 :param pin_layer: default layer for placing pins,
48 :param north_pins: list of pin configuration dictionaries for placing
49 pins on the north side,
50 :param east_pins: ditto (for the east side),
51 :param south_pins: ditto (for the south side),
52 :param west_pins: ditto (for the west side),
53 :param pads: dictionary of {net: list of layers} for creating pads,
54 :param kwargs: extra parameters to be implemented in derived classes.
55 """
56 self.editor = editor
57 self.af = CRL.AllianceFramework.get()
58 self.db = DataBase.getDB()
59 self.width = width
60 self.height = height
61
62 if isinstance(cell, basestring):
63 self.cell = self.af.getCell(cell, CRL.Catalog.State.Logical)
64 else:
65 self.cell = cell
66
67 self.pin_width = pin_width
68 self.pin_height = pin_height
69 self.pin_suffix = pin_suffix
70 if pin_layer is None:
71 self.pin_layer = self.get_layer('METAL3')
72 elif isinstance(pin_layer, basestring):
73 self.pin_layer = self.get_layer(pin_layer)
74 else:
75 self.pin_layer = pin_layer
76 self.north_pins = north_pins or []
77 self.east_pins = east_pins or []
78 self.south_pins = south_pins or []
79 self.west_pins = west_pins or []
80
81 self.pads = pads or {}
82
83 self.submodules = submodules or []
84
85 @property
86 def name(self):
87 return self.cell.getName()
88
89 @name.setter
90 def name(self, name):
91 self.cell.setName(name)
92
93 def __str__(self):
94 return self.name
95
96 @property
97 def ab(self):
98 """ The real abutment box. """
99 return self.cell.getAbutmentBox()
100
101 @ab.setter
102 def ab(self, ab):
103 self.cell.setAbutmentBox(ab)
104
105 @property
106 def instances(self):
107 """ Cached instances. """
108 if self._instances is None:
109 self._instances = self.cell.getInstances()
110
111 return self._instances
112
113 def get_layer(self, name):
114 """ Creates a new layer or returns it from cache. """
115 if name in self._layer_cache:
116 return self._layer_cache[name]
117
118 layer = self.db.getTechnology().getLayer(name)
119 self._layer_cache[name] = layer
120 return layer
121
122 @staticmethod
123 def to_dbu(lmb):
124 """
125 Convert lambdas to database units. (See Hurricane+Python Manual 3.4.)
126 """
127 return DbU.fromLambda(lmb)
128
129 @staticmethod
130 def from_dbu(dbu):
131 """
132 Convert lambdas to database units. (See Hurricane+Python Manual 3.4.)
133 """
134 return DbU.toLambda(dbu)
135
136 def place(self):
137 """ Places the current cell. """
138 etesian = Etesian.EtesianEngine.create(self.cell)
139 etesian.place()
140
141 def route(self):
142 """ Routes the current cell. """
143 katana = Katana.KatanaEngine.create(self.cell)
144 katana.digitalInit()
145 katana.runGlobalRouter(Katana.Flags.NoFlags)
146 katana.loadGlobalRouting(Anabatic.EngineLoadGrByNet)
147 katana.layerAssign(Anabatic.EngineNoNetLayerAssign)
148 katana.runNegociate(Katana.Flags.NoFlags)
149 katana.finalizeLayout()
150 result = katana.isDetailedRoutingSuccess()
151 katana.destroy()
152 return result
153
154 def place_and_route(self):
155 """ Places and routes. """
156 self.place()
157 return self.route()
158
159 def create_pin_series(self, net, direction, name=None,
160 status=Pin.PlacementStatus.FIXED, layer=None,
161 x=None, y=None, width=2.0, height=2.0,
162 repeat=1, delta=0.0, external=True):
163 """
164 Creates a series of pins in a cell.
165
166 :param net: Hurricane.Net object name or name template, taking a pin
167 enumeration parameter, i. e. pin number,
168 :param name: pin name or name template taking a pin enumeration
169 parameter (net name or template + suffix),
170 :param direction: Pin.Direction value,
171 :param status: Pin.PlacementStatus value (default is FIXED),
172 :param layer: Hurricane.Layer object or name (METAL3),
173 :param x: starting pin position (left to right, 0.0),
174 :param y: starting pin position (bottom to top, 0.0),
175 :param width: pin width (2,0),
176 :param height: pin height (2.0),
177 :param repeat: a number of pins to be placed or an iterable containing
178 pin template parameters (i. e. pin number, 1),
179 :param delta: next pin position offset (0.0),
180 :param external: mark pin as external (yes),
181 :return: tuple of next pin coordinates, or just (x, y), if delta
182 was not provided.
183 """
184 if layer is None:
185 layer = self.pin_layer
186 elif isinstance(layer, basestring):
187 layer = self.get_layer(layer)
188
189 if isinstance(repeat, int):
190 if repeat > 1 and delta == 0.0:
191 raise Warning('You are trying to place pins on each other.')
192 iterator = range(repeat)
193 else:
194 iterator = repeat
195
196 if name is None:
197 name = net + self.pin_suffix
198
199 for i in iterator:
200 pin = Pin.create(self.cell.getNet(net.format(i)),
201 name.format(i), direction, status, layer,
202 self.to_dbu(x), self.to_dbu(y),
203 self.to_dbu(width), self.to_dbu(height))
204 if direction in (Pin.Direction.NORTH, Pin.Direction.SOUTH):
205 x += delta
206 else:
207 # EAST or WEST
208 y += delta
209
210 if external:
211 pin.getNet().setExternal(True)
212 NetExternalComponents.setExternal(pin)
213
214 return x, y
215
216 def init_abutment_box(self):
217 """ Create the abutment box with object's initial values. """
218
219 self.ab = Box(0, 0, self.to_dbu(self.width), self.to_dbu(self.height))
220
221 def create_pins(self):
222 """ Creates all pins set on Module object creation. """
223
224 def default_x():
225 if direction == Pin.Direction.EAST:
226 return self.from_dbu(self.ab.getWidth())
227 if direction == Pin.Direction.WEST:
228 return 0
229 return last_x
230
231 def default_y():
232 if direction == Pin.Direction.NORTH:
233 return self.from_dbu(self.ab.getHeight())
234 if direction == Pin.Direction.SOUTH:
235 return 0
236 return last_y
237
238 for pins, direction in (
239 (self.north_pins, Pin.Direction.NORTH),
240 (self.east_pins, Pin.Direction.EAST),
241 (self.south_pins, Pin.Direction.SOUTH),
242 (self.west_pins, Pin.Direction.WEST),
243 ):
244 last_x = last_y = 0.0
245 for pin_config in pins:
246 net = pin_config.pop('net')
247 name = pin_config.pop('name', net + self.pin_suffix)
248 layer = pin_config.pop('layer', self.pin_layer)
249 x = pin_config.pop('x', default_x())
250 y = pin_config.pop('y', default_y())
251 last_x, last_y = self.create_pin_series(
252 net, direction, name=name, layer=layer, x=x, y=y,
253 **pin_config)
254
255 def create_pads_for_net(self, net, layers):
256 """
257 Creates a series of pads for a given net.
258
259 :param net: net name or Hurricane.Net object to create pads for,
260 :param layers: list of layer names or Hurricane.Layer objects
261 to create pads on.
262 """
263 temp_ab = self.ab
264 temp_ab.inflate(self.to_dbu(-5.0))
265 if isinstance(net, basestring):
266 net = self.cell.getNet(net)
267
268 for layer in layers:
269 if isinstance(layer, basestring):
270 layer = self.get_layer(layer)
271 Pad.create(net, layer, temp_ab)
272
273 def create_pads(self):
274 """ Create all pads for a given Module object. """
275
276 for net, layers in self.pads.items():
277 self.create_pads_for_net(net, layers)
278
279 def do_submodules(self):
280 """
281 Execute submodules and gather their status.
282
283 :return: True if all submodules executed successfully, False otherwise.
284 """
285
286 for submodule, x, y in self.submodules:
287 if not submodule.do():
288 return False
289
290 return True
291
292 def place_submodules(self):
293 """ Place submodules in the current module. """
294
295 for submodule, x, y in self.submodules:
296 # find instance
297 instance = [
298 inst for inst in self.instances if inst.getName().endswith(
299 submodule.name
300 )
301 ][0]
302
303 # place submodule
304 instance.setTransformation(Transformation(
305 self.to_dbu(x), self.to_dbu(y), Transformation.Orientation.ID
306 ))
307 instance.setPlacementStatus(Instance.PlacementStatus.FIXED)
308
309 def do(self):
310 """ Main routine. """
311
312 raise NotImplementedError('You need to implement the `do` method.')