/
net_blockage.py
561 lines (490 loc) · 26.6 KB
/
net_blockage.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# Import GDSII Library
from gdsii.library import Library
from gdsii.elements import *
# Import Custom Modules
import debug_prints as dbg
from polygon import *
from net import *
from layout import *
# Other Imports
import time
import sys
import inspect
import copy
import numpy
import multiprocessing as mp
import functools as ft
# Import matplotlib
# import matplotlib.pyplot as plt
# Possible ERROR Codes:
# 1 = Error loading input load_files
# 2 = Unknown GDSII object attributes/attribute types
# 3 = Unhandled feature
# 4 = Usage Error
# ------------------------------------------------------------------
# Critical Net Blockage Metric
# ------------------------------------------------------------------
# Color adjacent layer bitmap by setting bits inside any polygon to 1
def color_bitmap_al(bitmap, offset, poly):
for row in range(bitmap.shape[0]):
for col in range(bitmap.shape[1]):
if poly.is_point_inside(Point(col + offset.x + 0.5, row + offset.y + 0.5)):
bitmap[row, col] = 1
# Color same layer bitmap by setting bits inside any polygon to 1
def color_bitmap_sl(bitmap, curr_pitch_pt, curr_overlap_pt, end_pitch_pt, nearby_polys, layout, direction):
num_sites = max(bitmap.shape[0], bitmap.shape[1])
step_size = layout.net_blockage_step
sites_ind = 0
while curr_pitch_pt != end_pitch_pt:
for poly in nearby_polys:
if poly.is_point_inside(curr_pitch_pt) or poly.is_point_inside(curr_overlap_pt):
for i in range(sites_ind, min(sites_ind + layout.net_blockage_step, num_sites)):
if direction == 'N' or direction == 'S':
bitmap[0, i] = 1
elif direction == 'E' or direction == 'W':
bitmap[i, 0] = 1
else:
print "ERROR %s: unknown scan direction (%s)." % (inspect.stack()[0][3], direction)
sys.exit(4)
break
if direction == 'N' or direction == 'S':
curr_pitch_pt.shift(step_size, 0)
curr_overlap_pt.shift(step_size, 0)
elif direction == 'E' or direction == 'W':
curr_pitch_pt.shift(0, step_size)
curr_overlap_pt.shift(0, step_size)
else:
print "ERROR %s: unknown scan direction (%s)." % (inspect.stack()[0][3], direction)
sys.exit(4)
sites_ind += step_size
return bitmap
# Computes number of bits colored
def bits_colored(bitmap):
return numpy.count_nonzero(bitmap)
def compute_windows_blocked(bitmap, layout, net_segment, offset, side, num_nearby_polys):
windows_scanned = 0
windows_blocked = 0
num_rows = bitmap.shape[0]
num_cols = bitmap.shape[1]
sl_required_open_width = layout.lef.layers[net_segment.layer_name].rogue_wire_width
# Get minimum wire spacing rules
if side == 'T':
# Get minimum wire width/spacing constraints for the adjacent layer
min_wire_width = layout.lef.layers[net_segment.layer_num + 1].min_width_db
required_open_width = layout.lef.layers[net_segment.layer_num + 1].rogue_wire_width
elif side == 'B':
# Get minimum wire width/spacing constraints for the adjacent layer
min_wire_width = layout.lef.layers[net_segment.layer_num - 1].min_width_db
required_open_width = layout.lef.layers[net_segment.layer_num - 1].rogue_wire_width
# Configure Scan Window
if side == 'N' or side == 'S':
scan_window = Window(Point(0,0), sl_required_open_width, 1, 'H')
windows_scanned_precompute = num_cols - sl_required_open_width + 1
elif side == 'E' or side == 'W':
scan_window = Window(Point(0,0), 1, sl_required_open_width, 'V')
windows_scanned_precompute = num_rows - sl_required_open_width + 1
elif side == 'T' or side == 'B':
# Get direction of wire (horizontal or vertical)
if net_segment.polygon.bbox.get_width() > net_segment.polygon.bbox.get_height():
# Horizontal Path or Boundary
if net_segment.polygon.bbox.get_width() > min_wire_width:
# Wide enough for 1 width of AL wire
scan_window = Window(Point(0, 0), required_open_width, num_rows, 'H')
windows_scanned_precompute = num_cols - required_open_width + 1
else:
# Width of entire path segment --> TODO: constrain with via enclosure rules
scan_window = Window(Point(0, 0), num_cols, num_rows, 'H')
windows_scanned_precompute = 1
elif net_segment.polygon.bbox.get_width() < net_segment.polygon.bbox.get_height():
# Vertical Path or Boundary
if net_segment.polygon.bbox.get_height() > min_wire_width:
# Tall enough for 1 width of AL wire
scan_window = Window(Point(0, 0), num_cols, required_open_width, 'V')
windows_scanned_precompute = num_rows - required_open_width + 1
else:
# Height of entire path segment --> TODO: constrain with via enclosure rules
scan_window = Window(Point(0, 0), num_cols, num_rows, 'V')
windows_scanned_precompute = 1
else:
# Path or Boundary is a Square --> get direction from LEF file
routing_direction = layout.lef.layers[net_segment.layer_name].direction
if routing_direction == "H":
# Routing Direction is HORIZONTAL
scan_window = Window(Point(0, 0), required_open_width, num_rows, 'H')
windows_scanned_precompute = num_cols - required_open_width + 1
elif routing_direction == "V":
# Routing Direction is VERTICAL
scan_window = Window(Point(0, 0), num_cols, required_open_width, 'V')
windows_scanned_precompute = num_rows - required_open_width + 1
else:
print "UNSUPPORTED %s: routing direction is not H or V." % (inspect.stack()[0][3])
sys.exit(3)
# ---------------------------------------
# Eearly Termination Optimizations
# ---------------------------------------
# CASE #1: If no nearbly polygons, skip calculation and
# return number of windows that would have been scanned.
if num_nearby_polys == 0:
if side == 'T':
net_segment.unblocked_windows[side].append(Window.from_bbox(net_segment.nearby_al_bbox, scan_window.direction))
elif side == 'B':
net_segment.unblocked_windows[side].append(Window.from_bbox(net_segment.nearby_bl_bbox, scan_window.direction))
elif side == 'N' or side == 'S':
net_segment.unblocked_windows[side].append(Window(copy.deepcopy(offset), num_cols, 1, 'H'))
elif side == 'E' or side == 'W':
net_segment.unblocked_windows[side].append(Window(copy.deepcopy(offset), 1, num_rows, 'V'))
else:
print "UNSUPPORTED %s: side to scan is invalid." % (inspect.stack()[0][3])
sys.exit(3)
return windows_scanned_precompute, 0
# ---------------------------------------
# Keep track of patching points
prev_window_blocked = True
unblocked_window = None
# print " Window Start:"
# scan_window.print_window()
# print " Num. Cols/Rows:", num_cols, "/", num_rows
# Scan bitmap rows
while scan_window.get_end_pt().y <= num_rows:
# Reset scan window X position
scan_window.reset_x_position()
# Scan bitmap cols
while scan_window.get_end_pt().x <= num_cols:
if scan_window.get_bitmap_splice(bitmap).any():
# Current Window is BLOCKED
windows_blocked += 1
prev_window_blocked = True
# If end of OPEN window, save
if unblocked_window:
# Adjust window location to real location on chip
unblocked_window.offset(offset)
# Append window to list of unblocked windows
net_segment.unblocked_windows[side].append(unblocked_window)
unblocked_window = None
elif prev_window_blocked:
# Previous Window is BLOCKED and Current Window is OPEN
prev_window_blocked = False
unblocked_window = Window(scan_window.get_start_pt_copy(), scan_window.width, scan_window.height, scan_window.direction)
else:
# Previous Window is OPEN and Current Window is OPEN
if scan_window.direction == 'H':
unblocked_window.increase_width(1)
elif scan_window.direction == 'V':
unblocked_window.increase_height(1)
else:
print "UNSUPPORTED %s: wire patching direction." % (inspect.stack()[0][3], token)
sys.exit(3)
windows_scanned += 1
scan_window.shift_horizontal(1)
scan_window.shift_vertical(1)
if unblocked_window:
# Adjust window location to real location on chip
unblocked_window.offset(offset)
# Append window to list of unblocked windows
net_segment.unblocked_windows[side].append(unblocked_window)
unblocked_window = None
# print " Window End:"
# scan_window.print_window()
# print " Windows Blocked: %d / %d" % (windows_blocked, windows_scanned)
# print " Windows Precomputed: %d" % (windows_scanned_precompute)
return windows_scanned, windows_blocked
def check_blockage_constrained(layout, net_segment):
num_same_layer_units_checked = 0
same_layer_units_blocked = 0
num_diff_layer_units_checked = 0
diff_layer_units_blocked = 0
sides_unblocked = []
sl_min_wire_spacing = layout.lef.layers[net_segment.layer_name].min_spacing_db - 1
check_distance = (layout.lef.layers[net_segment.layer_name].pitch - (0.5 * layout.lef.layers[net_segment.layer_name].width)) * layout.lef.database_units
# print " Check Distance (uM):", (layout.lef.layers[net_segment.layer_name].pitch - (0.5 * layout.lef.layers[net_segment.layer_name].width))
# Scan all 4 perimeter sides to check for blockages
for direction in ['N', 'E', 'S', 'W', 'T', 'B']:
if direction == 'N':
start_scan_coord = net_segment.polygon.bbox.ll.x - sl_min_wire_spacing
end_scan_coord = net_segment.polygon.bbox.ur.x + sl_min_wire_spacing
curr_fixed_coord_pitch = net_segment.polygon.bbox.ur.y + check_distance
curr_fixed_coord_overlap = net_segment.polygon.bbox.ur.y + 2
start_pitch_pt = Point(start_scan_coord, curr_fixed_coord_pitch)
curr_pitch_pt = Point(start_scan_coord, curr_fixed_coord_pitch)
end_pitch_pt = Point(end_scan_coord, curr_fixed_coord_pitch)
curr_overlap_pt = Point(start_scan_coord, curr_fixed_coord_overlap)
elif direction == 'E':
start_scan_coord = net_segment.polygon.bbox.ll.y - sl_min_wire_spacing
end_scan_coord = net_segment.polygon.bbox.ur.y + sl_min_wire_spacing
curr_fixed_coord_pitch = net_segment.polygon.bbox.ur.x + check_distance
curr_fixed_coord_overlap = net_segment.polygon.bbox.ur.x + 2
start_pitch_pt = Point(curr_fixed_coord_pitch, start_scan_coord)
curr_pitch_pt = Point(curr_fixed_coord_pitch, start_scan_coord)
end_pitch_pt = Point(curr_fixed_coord_pitch, end_scan_coord)
curr_overlap_pt = Point(curr_fixed_coord_overlap, start_scan_coord)
elif direction == 'S':
start_scan_coord = net_segment.polygon.bbox.ll.x - sl_min_wire_spacing
end_scan_coord = net_segment.polygon.bbox.ur.x + sl_min_wire_spacing
curr_fixed_coord_pitch = net_segment.polygon.bbox.ll.y - check_distance
curr_fixed_coord_overlap = net_segment.polygon.bbox.ll.y - 2
start_pitch_pt = Point(start_scan_coord, curr_fixed_coord_pitch)
curr_pitch_pt = Point(start_scan_coord, curr_fixed_coord_pitch)
end_pitch_pt = Point(end_scan_coord, curr_fixed_coord_pitch)
curr_overlap_pt = Point(start_scan_coord, curr_fixed_coord_overlap)
elif direction == 'W':
start_scan_coord = net_segment.polygon.bbox.ll.y - sl_min_wire_spacing
end_scan_coord = net_segment.polygon.bbox.ur.y + sl_min_wire_spacing
curr_fixed_coord_pitch = net_segment.polygon.bbox.ll.x - check_distance
curr_fixed_coord_overlap = net_segment.polygon.bbox.ll.x - 2
start_pitch_pt = Point(curr_fixed_coord_pitch, start_scan_coord)
curr_pitch_pt = Point(curr_fixed_coord_pitch, start_scan_coord)
end_pitch_pt = Point(curr_fixed_coord_pitch, end_scan_coord)
curr_overlap_pt = Point(curr_fixed_coord_overlap, start_scan_coord)
elif direction != 'T' and direction != 'B':
print "ERROR %s: unknown scan direction (%s)." % (inspect.stack()[0][3], direction)
sys.exit(4)
# Analyze blockage along the perimeter on the same layer
if direction != 'T' and direction != 'B':
curr_scan_coord = start_scan_coord
num_points_to_scan = (float(end_scan_coord - curr_scan_coord) / float(layout.net_blockage_step))
# print " Checking %.2f units along %s edge (%d/%f units/microns away)..." % (num_points_to_scan, direction, check_distance, float(check_distance / layout.lef.database_units))
# print " Start Scan Coord = %d; End Scan Coord = %d; Num Points to Scan = %d" % (curr_scan_coord, end_scan_coord, num_points_to_scan)
# Create Bitmap
if direction == 'N' or direction == 'S':
sl_bitmap = numpy.zeros(shape=(1, end_scan_coord - start_scan_coord), dtype=bool)
elif direction == 'E' or direction == 'W':
sl_bitmap = numpy.zeros(shape=(end_scan_coord - start_scan_coord, 1), dtype=bool)
# Color Bitmap
sl_bitmap = color_bitmap_sl(sl_bitmap, curr_pitch_pt, curr_overlap_pt, end_pitch_pt, net_segment.nearby_sl_polygons, layout, direction)
# Calculate windows blocked
windows_scanned, windows_blocked = compute_windows_blocked(sl_bitmap, layout, net_segment, start_pitch_pt, direction, len(net_segment.nearby_sl_polygons))
# windows_scanned, windows_blocked = compute_windows_blocked(sl_bitmap, layout, net_segment, start_pitch_pt, direction, 0)
num_same_layer_units_checked += windows_scanned
same_layer_units_blocked += windows_blocked
# Record Sides Unblocked
if windows_blocked < windows_scanned:
sides_unblocked.append(direction)
# print " Windows blocked %d/%d on %s edge" % (windows_blocked, windows_scanned, direction)
# Analyze blockage along the adjacent layers (top and bottom)
else:
# Only analyze if top/bottom adjacent layer is routable
if direction == 'T' and (net_segment.layer_num < layout.lef.top_routing_layer_num):
# Get nearby polygons to analyze
nearby_polys = net_segment.nearby_al_polygons
nearby_bbox = net_segment.nearby_al_bbox
elif direction == 'B' and (net_segment.layer_num > layout.lef.bottom_routing_layer_num):
# Get nearby polygons to analyze
nearby_polys = net_segment.nearby_bl_polygons
nearby_bbox = net_segment.nearby_bl_bbox
else:
continue
# Create bitmap
al_bitmap = numpy.zeros(shape=(nearby_bbox.get_height(), nearby_bbox.get_width()), dtype=bool)
# print " Checking (%d) nearby polygons along %s side (GDSII Layer:) ..." % (len(nearby_polys), direction)
# Color the bitmap
for poly in nearby_polys:
color_bitmap_al(al_bitmap, nearby_bbox.ll, poly)
# Calculate windows blocked
windows_scanned, windows_blocked = compute_windows_blocked(al_bitmap, layout, net_segment, nearby_bbox.ll, direction, len(nearby_polys))
# windows_scanned, windows_blocked = compute_windows_blocked(al_bitmap, layout, net_segment, nearby_bbox.ll, direction, 0)
# Updated sides unblocked
if windows_blocked < windows_scanned:
sides_unblocked.append(direction)
# Updated different layer blockage stats
num_diff_layer_units_checked += windows_scanned
diff_layer_units_blocked += windows_blocked
# Free bitmap memory
del al_bitmap
return num_same_layer_units_checked, same_layer_units_blocked, sides_unblocked, num_diff_layer_units_checked, diff_layer_units_blocked
def check_blockage(layout, net_segment):
num_same_layer_units_checked = 0
same_layer_units_blocked = 0
num_diff_layer_units_checked = 0
diff_layer_units_blocked = 0
sides_unblocked = []
check_distance = (layout.lef.layers[net_segment.layer_name].pitch - (0.5 * layout.lef.layers[net_segment.layer_name].width)) * layout.lef.database_units
# print " Check Distance (uM):", (layout.lef.layers[net_segment.layer_name].pitch - (0.5 * layout.lef.layers[net_segment.layer_name].width))
# Scan all 4 perimeter sides to check for blockages
for direction in ['N', 'E', 'S', 'W', 'T', 'B']:
if direction == 'N':
curr_scan_coord = net_segment.polygon.bbox.ll.x
end_scan_coord = net_segment.polygon.bbox.ur.x
curr_fixed_coord_overlap = net_segment.polygon.bbox.ur.y + 1
curr_fixed_coord_pitch = net_segment.polygon.bbox.ur.y + check_distance
elif direction == 'E':
curr_scan_coord = net_segment.polygon.bbox.ll.y
end_scan_coord = net_segment.polygon.bbox.ur.y
curr_fixed_coord_overlap = net_segment.polygon.bbox.ur.x + 1
curr_fixed_coord_pitch = net_segment.polygon.bbox.ur.x + check_distance
elif direction == 'S':
curr_scan_coord = net_segment.polygon.bbox.ll.x
end_scan_coord = net_segment.polygon.bbox.ur.x
curr_fixed_coord_overlap = net_segment.polygon.bbox.ll.y - 1
curr_fixed_coord_pitch = net_segment.polygon.bbox.ll.y - check_distance
elif direction == 'W':
curr_scan_coord = net_segment.polygon.bbox.ll.y
end_scan_coord = net_segment.polygon.bbox.ur.y
curr_fixed_coord_overlap = net_segment.polygon.bbox.ll.x - 1
curr_fixed_coord_pitch = net_segment.polygon.bbox.ll.x - check_distance
elif direction != 'T' and direction != 'B':
print "ERROR %s: unknown scan direction (%s)." % (inspect.stack()[0][3], direction)
sys.exit(4)
# Analyze blockage along the perimeter on the same layer
if direction != 'T' and direction != 'B':
num_points_to_scan = (float(end_scan_coord - curr_scan_coord) / float(layout.net_blockage_step))
same_side_units_blocked = 0
# print " Checking %.2f units along %s edge (%d/%f units/microns away)..." % (num_points_to_scan, direction, check_distance, float(check_distance / layout.lef.database_units))
# print " Start Scan Coord = %d; End Scan Coord = %d; Num Points to Scan = %d" % (curr_scan_coord, end_scan_coord, num_points_to_scan)
while curr_scan_coord < end_scan_coord:
for poly in net_segment.polygon.nearby_sl_polygons:
if direction == 'N' or direction == 'S':
if poly.is_point_inside(Point(curr_scan_coord, curr_fixed_coord_pitch)) or poly.is_point_inside(Point(curr_scan_coord, curr_fixed_coord_overlap)):
same_layer_units_blocked += 1
same_side_units_blocked += 1
break
else:
if poly.is_point_inside(Point(curr_fixed_coord_pitch, curr_scan_coord)) or poly.is_point_inside(Point(curr_fixed_coord_overlap, curr_scan_coord)):
same_layer_units_blocked += 1
same_side_units_blocked += 1
break
num_same_layer_units_checked += 1
curr_scan_coord += layout.net_blockage_step
if same_side_units_blocked < num_points_to_scan:
sides_unblocked.append(direction)
# Analyze blockage along the adjacent layers (top and bottom)
else:
# Create bitmap of net segment
net_segment_bitmap = numpy.zeros(shape=(net_segment.polygon.bbox.get_height(), net_segment.polygon.bbox.get_width()), data_type=bool)
# Choose nearby polygons to analyze
if direction == 'T' and (net_segment.layer_num < layout.lef.top_routing_layer_num):
nearby_polys = net_segment.nearby_al_polygons
# print " Checking (%d) nearby polygons along %s side (Layer: %d) ..." % (len(nearby_polys), direction, net_segment.layer_num + 1)
elif direction == 'B' and (net_segment.layer_num > layout.lef.bottom_routing_layer_num):
nearby_polys = net_segment.nearby_bl_polygons
# print " Checking (%d) nearby polygons along %s side (Layer: %d) ..." % (len(nearby_polys), direction, net_segment.layer_num - 1)
else:
continue
# Color the bitmap
for poly in nearby_polys:
# First check if bounding boxes overlap
if net_segment.polygon.bbox.overlaps_bbox(poly.bbox):
color_bitmap_al(net_segment_bitmap, net_segment.polygon.bbox.ll, poly)
# Calculate colored area
diff_layer_units_blocked += bits_colored(net_segment_bitmap)
num_diff_layer_units_checked += net_segment.polygon.get_area()
# Free bitmap memory
del net_segment_bitmap
return num_same_layer_units_checked, same_layer_units_blocked, sides_unblocked, num_diff_layer_units_checked, diff_layer_units_blocked
def launch_net_blockage(layout, net):
for net_segment in net.segments:
# Start Computation Timer
net_segment.nb_compute_time = time.time()
# Compute Net Blockage for each Net Segment
if layout.net_blockage_type == 1:
num_same_layer_units_checked, \
same_layer_units_blocked, \
sides_unblocked, \
num_diff_layer_units_checked, \
diff_layer_units_blocked = check_blockage_constrained(layout, net_segment)
else:
num_same_layer_units_checked, \
same_layer_units_blocked, \
sides_unblocked, \
num_diff_layer_units_checked, \
diff_layer_units_blocked = check_blockage(layout, net_segment)
# Update Net Segment Properties
net_segment.same_layer_units_blocked = same_layer_units_blocked
net_segment.diff_layer_units_blocked = diff_layer_units_blocked
net_segment.same_layer_units_checked = num_same_layer_units_checked
net_segment.diff_layer_units_checked = num_diff_layer_units_checked
net_segment.sides_unblocked = sides_unblocked
# End Computation Timer
net_segment.nb_compute_time = time.time() - net_segment.nb_compute_time
return net
def print_net_segment_blockage_info(net_segment, segment_num, verbose, layout):
# Get GDSII element type
if isinstance(net_segment.polygon.gdsii_element, Path):
gdsii_element_type = "Path"
elif isinstance(net_segment.polygon.gdsii_element, Boundary):
gdsii_element_type = "Boundary"
else:
gdsii_element_type = "Unknown"
# Report Path Segment Condition
if verbose:
print " Analyzing Net Segment", segment_num
print " Layer: ", net_segment.layer_num
print " GDSII Element: ", gdsii_element_type
print " Perimeter (dbu): ", net_segment.polygon.bbox.get_perimeter()
print " Step Size (dbu): ", layout.net_blockage_step
print " Pitch (uM): ", layout.lef.layers[net_segment.layer_name].pitch
print " Default Width (uM): ", layout.lef.layers[net_segment.layer_name].width
print " Min SL Width (uM): ", layout.lef.layers[net_segment.layer_num].min_width
if net_segment.layer_num < layout.lef.top_routing_layer_num:
print " Min TL Width (uM): ", layout.lef.layers[net_segment.layer_num + 1].min_width
if net_segment.layer_num > layout.lef.bottom_routing_layer_num:
print " Min BL Width (uM): ", layout.lef.layers[net_segment.layer_num - 1].min_width
print " Top and Bottom Area (dbu):", (net_segment.polygon.get_area() * 2)
print " BBox (M-Units): ", net_segment.polygon.bbox.get_bbox_as_list()
print " Nearby SL BBox (M-Units): ", net_segment.nearby_sl_bbox.get_bbox_as_list()
if net_segment.layer_num < layout.lef.top_routing_layer_num:
print " Nearby TL BBox (M-Units): ", net_segment.nearby_al_bbox.get_bbox_as_list()
if net_segment.layer_num > layout.lef.bottom_routing_layer_num:
print " Nearby BL BBox (M-Units): ", net_segment.nearby_bl_bbox.get_bbox_as_list()
print " Num. Nearby Polygons: ", len(net_segment.nearby_al_polygons) + len(net_segment.nearby_bl_polygons) + len(net_segment.nearby_sl_polygons)
if gdsii_element_type == "Path":
print " Klayout Query: "
print " paths on layer %d/%d of cell %s where" % (net_segment.polygon.gdsii_element.layer, net_segment.polygon.gdsii_element.data_type, layout.top_level_name)
print " shape.path.bbox.left==%d &&" % (net_segment.polygon.bbox.ll.x)
print " shape.path.bbox.right==%d &&" % (net_segment.polygon.bbox.ur.x)
print " shape.path.bbox.top==%d &&" % (net_segment.polygon.bbox.ur.y)
print " shape.path.bbox.bottom==%d" % (net_segment.polygon.bbox.ll.y)
elif gdsii_element_type == "Boundary":
print " Klayout Query: "
print " boxes on layer %d/%d of cell %s where" % (net_segment.polygon.gdsii_element.layer, net_segment.polygon.gdsii_element.data_type, layout.top_level_name)
print " shape.box.left==%d &&" % (net_segment.polygon.bbox.ll.x)
print " shape.box.right==%d &&" % (net_segment.polygon.bbox.ur.x)
print " shape.box.top==%d &&" % (net_segment.polygon.bbox.ur.y)
print " shape.box.bottom==%d" % (net_segment.polygon.bbox.ll.y)
if net_segment.sides_unblocked:
print " Sides Unblocked:", net_segment.sides_unblocked
print " Perimeter Units Blocked: %d / %d" % (net_segment.same_layer_units_blocked, net_segment.same_layer_units_checked)
print " Top/Bottom Units Blocked: %d / %d" % (net_segment.diff_layer_units_blocked, net_segment.diff_layer_units_checked)
print " Done - Time Elapsed:", net_segment.nb_compute_time, "seconds."
print " ----------------------------------------------"
def analyze_critical_net_blockage(layout, verbose):
total_perimeter_units = 0
total_top_bottom_area = 0
total_same_layer_blockage = 0
total_diff_layer_blockage = 0
# Extract all GDSII elements near security-critical nets
layout.extract_nearby_polygons()
# layout.extract_nearby_polygons_parallel()
# Distribute Workload Among Multiple Processes
worker_pool = mp.Pool(processes=layout.num_processes)
analyze_critical_nets_partial = ft.partial(launch_net_blockage, layout)
layout.critical_nets = worker_pool.map(analyze_critical_nets_partial, layout.critical_nets)
# Print/Aggregate Detailed Results
for net in layout.critical_nets:
print "Analyzing Net: ", net.fullname
segment_num = 1
for net_segment in net.segments:
# Print Blockage Results
print_net_segment_blockage_info(net_segment, segment_num, verbose, layout)
# Aggregate Blockage Results
total_same_layer_blockage += net_segment.same_layer_units_blocked
total_diff_layer_blockage += net_segment.diff_layer_units_blocked
total_perimeter_units += net_segment.same_layer_units_checked
total_top_bottom_area += net_segment.diff_layer_units_checked
segment_num += 1
# Calculate raw and weighted blockage percentages.
# Weighted accounts for area vs. perimeter blockage
# print "Total Same Layer Blockage:", total_same_layer_blockage
# print "Total Perimeter Units :", total_perimeter_units
# print "Total Diff Layer Blockage:", total_diff_layer_blockage
# print "Total Top/Bottom Area :", total_top_bottom_area
perimeter_blockage_percentage = (float(total_same_layer_blockage) / float(total_perimeter_units)) * 100.0
top_bottom_blockage_percentage = (float(total_diff_layer_blockage) / float(total_top_bottom_area)) * 100.0
raw_blockage_percentage = (float(total_same_layer_blockage + total_diff_layer_blockage) / float(total_perimeter_units + total_top_bottom_area)) * 100.0
weighted_blockage_percentage = (((float(total_same_layer_blockage) / float(total_perimeter_units)) * float(4.0/6.0)) + ((float(total_diff_layer_blockage) / float(total_top_bottom_area)) * float(2.0/6.0))) * 100.0
# Print calculations
print "Perimeter Blockage Percentage: %4.2f%%" % (perimeter_blockage_percentage)
print "Top/Bottom Blockage Percentage: %4.2f%%" % (top_bottom_blockage_percentage)
print "Raw Blockage Percentage: %4.2f%%" % (raw_blockage_percentage)
print "Weighted Blockage Percentage: %4.2f%%" % (weighted_blockage_percentage)
# Set completed flag
layout.net_blockage_done = True