0
# Hierarchical Design and Cell Management
1
2
Cell hierarchy management, instance creation and manipulation, library organization, and parametric cell (PCell) support for reusable design components in IC layout design.
3
4
## Capabilities
5
6
### Cell Instance Management
7
8
```python { .api }
9
class CellInstArray:
10
def __init__(self, cell_index: int, trans: Trans, na: int = 1, nb: int = 1,
11
da: Vector = None, db: Vector = None):
12
"""
13
Create a cell instance or instance array.
14
15
Parameters:
16
- cell_index: Index of the cell to instantiate
17
- trans: Transformation for the instance
18
- na: Number of instances in A direction (default 1)
19
- nb: Number of instances in B direction (default 1)
20
- da: Displacement vector for A direction array
21
- db: Displacement vector for B direction array
22
"""
23
24
@property
25
def cell_index(self) -> int:
26
"""Get the instantiated cell index."""
27
28
@property
29
def trans(self) -> Trans:
30
"""Get the instance transformation."""
31
32
@property
33
def na(self) -> int:
34
"""Get number of instances in A direction."""
35
36
@property
37
def nb(self) -> int:
38
"""Get number of instances in B direction."""
39
40
@property
41
def da(self) -> Vector:
42
"""Get A direction displacement vector."""
43
44
@property
45
def db(self) -> Vector:
46
"""Get B direction displacement vector."""
47
48
def is_regular_array(self) -> bool:
49
"""Check if this is a regular array (na > 1 or nb > 1)."""
50
51
def bbox(self, layout: Layout) -> Box:
52
"""Get bounding box of the instance array."""
53
54
def transform_into(self, trans: Trans) -> CellInstArray:
55
"""Apply additional transformation to instance."""
56
57
class DCellInstArray:
58
def __init__(self, cell_index: int, trans: DCplxTrans):
59
"""Create a double precision cell instance."""
60
61
@property
62
def cell_index(self) -> int:
63
"""Get the instantiated cell index."""
64
65
@property
66
def trans(self) -> DCplxTrans:
67
"""Get the instance transformation."""
68
```
69
70
### Cell Hierarchy Navigation
71
72
```python { .api }
73
class Cell:
74
def each_inst(self):
75
"""Iterate over all instances in this cell."""
76
77
def each_parent_inst(self):
78
"""Iterate over all instances of this cell (parent instances)."""
79
80
def each_child_cell(self):
81
"""Iterate over all child cells (cells instantiated by this cell)."""
82
83
def each_parent_cell(self):
84
"""Iterate over all parent cells (cells that instantiate this cell)."""
85
86
def child_cells(self) -> int:
87
"""Get number of child cells."""
88
89
def parent_cells(self) -> int:
90
"""Get number of parent cells."""
91
92
def hierarchy_levels(self) -> int:
93
"""Get maximum hierarchy depth below this cell."""
94
95
def is_top(self) -> bool:
96
"""Check if this is a top-level cell (no parents)."""
97
98
def is_leaf(self) -> bool:
99
"""Check if this is a leaf cell (no children)."""
100
101
def is_proxy(self) -> bool:
102
"""Check if this is a proxy cell (library reference)."""
103
104
class Layout:
105
def top_cells(self):
106
"""Get all top-level cells in the layout."""
107
108
def top_cell(self) -> Cell:
109
"""Get the single top cell (if there is exactly one)."""
110
111
def each_cell(self):
112
"""Iterate over all cells in the layout."""
113
114
def cells(self) -> int:
115
"""Get total number of cells."""
116
```
117
118
### Parametric Cells (PCells)
119
120
```python { .api }
121
class PCellDeclaration:
122
def __init__(self):
123
"""Base class for parametric cell declarations."""
124
125
def display_name(self, parameters) -> str:
126
"""
127
Get display name for the PCell with given parameters.
128
129
Parameters:
130
- parameters: Dictionary of parameter values
131
132
Returns:
133
str: Display name for this parameter set
134
"""
135
136
def produce(self, layout: Layout, layers, parameters, cell: Cell) -> None:
137
"""
138
Produce the PCell geometry.
139
140
Parameters:
141
- layout: Target layout
142
- layers: Layer mapping
143
- parameters: Parameter values
144
- cell: Target cell to populate
145
"""
146
147
def get_parameters(self) -> list:
148
"""Get list of parameter declarations."""
149
150
def get_layers(self) -> list:
151
"""Get list of layer declarations."""
152
153
class PCellDeclarationHelper:
154
def __init__(self, name: str):
155
"""
156
Helper class for creating parametric cells.
157
158
Parameters:
159
- name: Name of the PCell
160
"""
161
162
def param(self, name: str, param_type: type, description: str, default=None) -> None:
163
"""
164
Declare a parameter.
165
166
Parameters:
167
- name: Parameter name
168
- param_type: Parameter type (int, float, str, bool)
169
- description: Parameter description
170
- default: Default value
171
"""
172
173
def layer(self, name: str, description: str, default: LayerInfo = None) -> None:
174
"""
175
Declare a layer parameter.
176
177
Parameters:
178
- name: Layer parameter name
179
- description: Layer description
180
- default: Default layer info
181
"""
182
183
def produce_impl(self) -> None:
184
"""
185
Implementation method to be overridden.
186
This method should create the actual geometry.
187
"""
188
189
class PCellParameterDeclaration:
190
def __init__(self, name: str, param_type: type, description: str = "",
191
default=None, choices=None):
192
"""
193
Parameter declaration for PCells.
194
195
Parameters:
196
- name: Parameter name
197
- param_type: Parameter type
198
- description: Parameter description
199
- default: Default value
200
- choices: List of valid choices (for enum parameters)
201
"""
202
203
@property
204
def name(self) -> str:
205
"""Get parameter name."""
206
207
@property
208
def type(self) -> type:
209
"""Get parameter type."""
210
211
@property
212
def description(self) -> str:
213
"""Get parameter description."""
214
215
# Parameter types
216
class PCellParameterType:
217
TypeInt = int
218
TypeDouble = float
219
TypeString = str
220
TypeBoolean = bool
221
TypeLayer = LayerInfo
222
TypeShape = object
223
```
224
225
### Library Management
226
227
```python { .api }
228
class Library:
229
def __init__(self, name: str = ""):
230
"""
231
Create a library.
232
233
Parameters:
234
- name: Library name
235
"""
236
237
@property
238
def name(self) -> str:
239
"""Get library name."""
240
241
def set_name(self, name: str) -> None:
242
"""Set library name."""
243
244
def register_pcell(self, pcell_class, name: str) -> int:
245
"""
246
Register a PCell class in the library.
247
248
Parameters:
249
- pcell_class: PCell declaration class
250
- name: PCell name in library
251
252
Returns:
253
int: PCell ID
254
"""
255
256
def layout(self) -> Layout:
257
"""Get the library layout."""
258
259
def delete(self) -> None:
260
"""Delete the library."""
261
262
class Technology:
263
def __init__(self, name: str = ""):
264
"""
265
Create a technology definition.
266
267
Parameters:
268
- name: Technology name
269
"""
270
271
@property
272
def name(self) -> str:
273
"""Get technology name."""
274
275
def load(self, tech_file: str) -> None:
276
"""Load technology from file."""
277
278
def save(self, tech_file: str) -> None:
279
"""Save technology to file."""
280
```
281
282
## Usage Examples
283
284
### Creating Cell Hierarchies
285
286
```python
287
import klayout.db as db
288
289
# Create layout with hierarchy
290
layout = db.Layout()
291
292
# Create leaf cell (basic building block)
293
nmos_cell = layout.create_cell("NMOS_TRANSISTOR")
294
layer_active = layout.layer(db.LayerInfo(1, 0))
295
layer_poly = layout.layer(db.LayerInfo(2, 0))
296
layer_contact = layout.layer(db.LayerInfo(3, 0))
297
298
# Add transistor geometry
299
nmos_cell.shapes(layer_active).insert(db.Box(0, 0, 200, 100))
300
nmos_cell.shapes(layer_poly).insert(db.Box(50, -20, 150, 120))
301
nmos_cell.shapes(layer_contact).insert(db.Box(25, 25, 75, 75))
302
nmos_cell.shapes(layer_contact).insert(db.Box(125, 25, 175, 75))
303
304
# Create intermediate cell (logic gate)
305
inverter_cell = layout.create_cell("INVERTER")
306
307
# Instantiate NMOS in inverter
308
nmos_instance = db.CellInstArray(nmos_cell.cell_index, db.Trans(db.Point(0, 0)))
309
inverter_cell.insert(nmos_instance)
310
311
# Add PMOS (simplified - just another NMOS for this example)
312
pmos_instance = db.CellInstArray(nmos_cell.cell_index,
313
db.Trans(0, True, db.Point(0, 200))) # Mirrored
314
inverter_cell.insert(pmos_instance)
315
316
# Create top-level cell
317
top_cell = layout.create_cell("LOGIC_BLOCK")
318
319
# Create array of inverters
320
for x in range(0, 1000, 250):
321
for y in range(0, 500, 300):
322
inv_instance = db.CellInstArray(inverter_cell.cell_index,
323
db.Trans(db.Point(x, y)))
324
top_cell.insert(inv_instance)
325
326
layout.write("hierarchy_example.gds")
327
```
328
329
### Instance Arrays
330
331
```python
332
import klayout.db as db
333
334
layout = db.Layout()
335
unit_cell = layout.create_cell("UNIT")
336
top_cell = layout.create_cell("TOP")
337
338
# Add content to unit cell
339
layer = layout.layer(db.LayerInfo(1, 0))
340
unit_cell.shapes(layer).insert(db.Box(0, 0, 10, 10))
341
342
# Create 1D array
343
array_1d = db.CellInstArray(
344
unit_cell.cell_index,
345
db.Trans(db.Point(0, 0)), # Base transformation
346
na=10, # 10 instances in A direction
347
nb=1, # 1 instance in B direction
348
da=db.Vector(15, 0), # 15 unit spacing in X
349
db=db.Vector(0, 0) # No B direction spacing
350
)
351
top_cell.insert(array_1d)
352
353
# Create 2D array
354
array_2d = db.CellInstArray(
355
unit_cell.cell_index,
356
db.Trans(db.Point(0, 50)), # Offset from 1D array
357
na=8, # 8 instances in A direction
358
nb=6, # 6 instances in B direction
359
da=db.Vector(15, 0), # 15 unit spacing in X
360
db=db.Vector(0, 20) # 20 unit spacing in Y
361
)
362
top_cell.insert(array_2d)
363
364
print(f"1D Array bbox: {array_1d.bbox(layout)}")
365
print(f"2D Array bbox: {array_2d.bbox(layout)}")
366
367
layout.write("array_example.gds")
368
```
369
370
### Hierarchy Navigation
371
372
```python
373
import klayout.db as db
374
375
layout = db.Layout()
376
layout.read("complex_design.gds")
377
378
# Find all top cells
379
top_cells = list(layout.top_cells())
380
print(f"Found {len(top_cells)} top-level cells")
381
382
if top_cells:
383
top_cell = top_cells[0]
384
print(f"Analyzing cell: {top_cell.name}")
385
386
# Analyze hierarchy
387
print(f"Child cells: {top_cell.child_cells()}")
388
print(f"Parent cells: {top_cell.parent_cells()}")
389
print(f"Hierarchy levels: {top_cell.hierarchy_levels()}")
390
391
# Walk through hierarchy
392
def analyze_cell(cell, level=0):
393
indent = " " * level
394
print(f"{indent}Cell: {cell.name}")
395
print(f"{indent} Instances: {sum(1 for _ in cell.each_inst())}")
396
print(f"{indent} Is leaf: {cell.is_leaf()}")
397
398
# Analyze instances
399
for inst in cell.each_inst():
400
child_cell = layout.cell(inst.cell_index)
401
print(f"{indent} -> {child_cell.name} at {inst.trans.disp}")
402
403
# Recurse for first few levels
404
if level < 3:
405
analyze_cell(child_cell, level + 1)
406
407
analyze_cell(top_cell)
408
```
409
410
### Simple Parametric Cell Example
411
412
```python
413
import klayout.db as db
414
415
class ResistorPCell(db.PCellDeclarationHelper):
416
def __init__(self):
417
super().__init__("Resistor")
418
419
# Declare parameters
420
self.param("width", db.PCellParameterType.TypeDouble, "Width", 1.0)
421
self.param("length", db.PCellParameterType.TypeDouble, "Length", 10.0)
422
self.param("num_contacts", db.PCellParameterType.TypeInt, "Number of contacts", 2)
423
424
# Declare layers
425
self.layer("poly", "Poly layer", db.LayerInfo(1, 0))
426
self.layer("contact", "Contact layer", db.LayerInfo(2, 0))
427
428
def produce_impl(self):
429
# Get parameter values
430
width = self.width
431
length = self.length
432
num_contacts = self.num_contacts
433
434
# Get layer indices
435
poly_layer = self.layout.layer(self.poly)
436
contact_layer = self.layout.layer(self.contact)
437
438
# Create resistor body
439
resistor_body = db.Box(0, 0, int(length * 1000), int(width * 1000))
440
self.cell.shapes(poly_layer).insert(resistor_body)
441
442
# Add contacts
443
contact_size = min(int(width * 200), int(length * 200 / num_contacts))
444
for i in range(num_contacts):
445
if num_contacts == 1:
446
x = int(length * 500) # Center
447
else:
448
x = int(length * 1000 * i / (num_contacts - 1))
449
450
y = int(width * 500 - contact_size / 2)
451
contact = db.Box(x - contact_size//2, y,
452
x + contact_size//2, y + contact_size)
453
self.cell.shapes(contact_layer).insert(contact)
454
455
# Register and use the PCell
456
library = db.Library("MyLibrary")
457
resistor_id = library.register_pcell(ResistorPCell(), "Resistor")
458
459
# Create layout using the PCell
460
layout = db.Layout()
461
top_cell = layout.create_cell("TOP")
462
463
# Instantiate PCell with different parameters
464
params1 = {"width": 2.0, "length": 20.0, "num_contacts": 3}
465
params2 = {"width": 1.5, "length": 15.0, "num_contacts": 2}
466
467
# Note: Actual PCell instantiation requires library integration
468
# This is a simplified example
469
```
470
471
### Cell Manipulation and Analysis
472
473
```python
474
import klayout.db as db
475
476
layout = db.Layout()
477
layout.read("design.gds")
478
479
def analyze_hierarchy(layout):
480
"""Analyze layout hierarchy and provide statistics."""
481
482
stats = {
483
"total_cells": layout.cells(),
484
"top_cells": len(list(layout.top_cells())),
485
"leaf_cells": 0,
486
"max_depth": 0,
487
"total_instances": 0
488
}
489
490
# Analyze each cell
491
for cell in layout.each_cell():
492
if cell.is_leaf():
493
stats["leaf_cells"] += 1
494
495
# Count instances
496
instance_count = sum(1 for _ in cell.each_inst())
497
stats["total_instances"] += instance_count
498
499
# Track maximum depth
500
depth = cell.hierarchy_levels()
501
if depth > stats["max_depth"]:
502
stats["max_depth"] = depth
503
504
return stats
505
506
def find_unused_cells(layout):
507
"""Find cells that are never instantiated."""
508
509
used_cells = set()
510
511
# Mark all instantiated cells as used
512
for cell in layout.each_cell():
513
for inst in cell.each_inst():
514
used_cells.add(inst.cell_index)
515
516
# Find unused cells
517
unused_cells = []
518
for cell in layout.each_cell():
519
if cell.cell_index not in used_cells and not cell.is_top():
520
unused_cells.append(cell)
521
522
return unused_cells
523
524
def flatten_hierarchy(cell, target_cell, level=1):
525
"""Flatten cell hierarchy to specified level."""
526
527
if level <= 0:
528
return
529
530
instances_to_remove = []
531
532
for inst in cell.each_inst():
533
child_cell = cell.layout().cell(inst.cell_index)
534
535
# Copy child cell content to target cell with transformation
536
for layer in range(cell.layout().layers()):
537
for shape in child_cell.shapes(layer).each():
538
transformed_shape = shape.transformed(inst.trans)
539
target_cell.shapes(layer).insert(transformed_shape)
540
541
# Recursively flatten child cells
542
flatten_hierarchy(child_cell, target_cell, level - 1)
543
544
instances_to_remove.append(inst)
545
546
# Remove flattened instances
547
for inst in instances_to_remove:
548
cell.erase(inst)
549
550
# Use the analysis functions
551
stats = analyze_hierarchy(layout)
552
print(f"Hierarchy Statistics: {stats}")
553
554
unused = find_unused_cells(layout)
555
print(f"Unused cells: {[cell.name for cell in unused]}")
556
557
# Flatten top cell by 2 levels
558
if layout.top_cell():
559
flattened_cell = layout.create_cell("FLATTENED")
560
flatten_hierarchy(layout.top_cell(), flattened_cell, 2)
561
```
562
563
### Advanced Instance Transformations
564
565
```python
566
import klayout.db as db
567
568
layout = db.Layout()
569
base_cell = layout.create_cell("BASE")
570
pattern_cell = layout.create_cell("PATTERN")
571
572
# Add content to base cell
573
layer = layout.layer(db.LayerInfo(1, 0))
574
base_cell.shapes(layer).insert(db.Box(0, 0, 100, 50))
575
576
# Create various transformed instances
577
transformations = [
578
db.Trans(0, False, db.Point(0, 0)), # Original
579
db.Trans(1, False, db.Point(200, 0)), # 90° rotation
580
db.Trans(2, False, db.Point(200, 200)), # 180° rotation
581
db.Trans(3, False, db.Point(0, 200)), # 270° rotation
582
db.Trans(0, True, db.Point(400, 0)), # X mirror
583
db.Trans(1, True, db.Point(600, 0)), # X mirror + 90° rotation
584
]
585
586
for i, trans in enumerate(transformations):
587
instance = db.CellInstArray(base_cell.cell_index, trans)
588
589
# Apply additional transformation
590
if i > 2: # Scale some instances
591
scaled_trans = db.Trans(db.Vector(50, 25)) # Additional offset
592
modified_instance = instance.transform_into(scaled_trans)
593
pattern_cell.insert(modified_instance)
594
else:
595
pattern_cell.insert(instance)
596
597
# Create complex patterns with arrays and transformations
598
spiral_cell = layout.create_cell("SPIRAL")
599
radius = 50
600
angle_step = 30
601
602
for i in range(12): # 12 positions around circle
603
angle_rad = i * angle_step * 3.14159 / 180
604
x = int(radius * cos(angle_rad))
605
y = int(radius * sin(angle_rad))
606
607
# Rotation to face outward
608
rotation = i * angle_step / 90 # Convert to 90-degree increments
609
610
trans = db.Trans(int(rotation) % 4, False, db.Point(x, y))
611
instance = db.CellInstArray(base_cell.cell_index, trans)
612
spiral_cell.insert(instance)
613
614
layout.write("advanced_instances.gds")
615
```