0
# Verification and Results Management
1
2
Design rule checking (DRC), layout versus schematic (LVS) capabilities, and results database management for storing and analyzing verification reports in IC layout workflows.
3
4
## Capabilities
5
6
### Results Database Management
7
8
```python { .api }
9
class ReportDatabase:
10
def __init__(self, name: str):
11
"""
12
Create a results database for verification reports.
13
14
Parameters:
15
- name: Database name
16
"""
17
18
@property
19
def name(self) -> str:
20
"""Get database name."""
21
22
def set_name(self, name: str) -> None:
23
"""Set database name."""
24
25
def create_category(self, name: str) -> Category:
26
"""
27
Create a new result category.
28
29
Parameters:
30
- name: Category name (e.g., "Width Violations", "Spacing Errors")
31
32
Returns:
33
Category: New category object
34
"""
35
36
def category_by_name(self, name: str) -> Category:
37
"""Get category by name."""
38
39
def each_category(self):
40
"""Iterate over all categories."""
41
42
def num_categories(self) -> int:
43
"""Get number of categories."""
44
45
def save(self, filename: str) -> None:
46
"""
47
Save database to file.
48
49
Parameters:
50
- filename: Output file path
51
"""
52
53
def load(self, filename: str) -> None:
54
"""
55
Load database from file.
56
57
Parameters:
58
- filename: Input file path
59
"""
60
61
def clear(self) -> None:
62
"""Clear all categories and items."""
63
64
class Category:
65
def __init__(self, name: str = ""):
66
"""
67
Create a result category.
68
69
Parameters:
70
- name: Category name
71
"""
72
73
@property
74
def name(self) -> str:
75
"""Get category name."""
76
77
def set_name(self, name: str) -> None:
78
"""Set category name."""
79
80
@property
81
def description(self) -> str:
82
"""Get category description."""
83
84
def set_description(self, description: str) -> None:
85
"""Set category description."""
86
87
def create_item(self, polygon: Polygon = None) -> Item:
88
"""
89
Create a new result item in this category.
90
91
Parameters:
92
- polygon: Optional polygon representing the violation area
93
94
Returns:
95
Item: New result item
96
"""
97
98
def each_item(self):
99
"""Iterate over all items in category."""
100
101
def num_items(self) -> int:
102
"""Get number of items in category."""
103
104
def clear(self) -> None:
105
"""Clear all items from category."""
106
107
class Item:
108
def __init__(self):
109
"""Create a result item."""
110
111
def add_value(self, value: Value) -> None:
112
"""
113
Add a measurement value to the item.
114
115
Parameters:
116
- value: Measurement or property value
117
"""
118
119
def each_value(self):
120
"""Iterate over all values."""
121
122
def num_values(self) -> int:
123
"""Get number of values."""
124
125
def add_tag(self, tag: Tags) -> None:
126
"""Add a tag to categorize the item."""
127
128
def each_tag(self):
129
"""Iterate over all tags."""
130
131
def set_visited(self, visited: bool) -> None:
132
"""Mark item as visited/reviewed."""
133
134
def visited(self) -> bool:
135
"""Check if item has been visited."""
136
137
class Value:
138
def __init__(self, value=None):
139
"""
140
Create a measurement value.
141
142
Parameters:
143
- value: Numeric value, string, or geometric object
144
"""
145
146
def is_numeric(self) -> bool:
147
"""Check if value is numeric."""
148
149
def is_string(self) -> bool:
150
"""Check if value is string."""
151
152
def is_geometric(self) -> bool:
153
"""Check if value represents geometry."""
154
155
def to_s(self) -> str:
156
"""Convert value to string representation."""
157
158
class Tags:
159
def __init__(self, tag: str = ""):
160
"""
161
Create a tag for result categorization.
162
163
Parameters:
164
- tag: Tag string
165
"""
166
167
@property
168
def tag(self) -> str:
169
"""Get tag string."""
170
171
def set_tag(self, tag: str) -> None:
172
"""Set tag string."""
173
```
174
175
### Design Rule Checking Operations
176
177
```python { .api }
178
# DRC operations are primarily performed using Region methods
179
# from the shape operations module
180
181
class Region:
182
def width_check(self, d: int, whole_edges: bool = False,
183
metrics: str = "euclidean", ignore_angle: float = None) -> EdgePairs:
184
"""
185
Check minimum width rule.
186
187
Parameters:
188
- d: Minimum width requirement
189
- whole_edges: Check whole edges vs. edge segments
190
- metrics: Distance metrics ("euclidean", "square", "projected")
191
- ignore_angle: Ignore edges at this angle (degrees)
192
193
Returns:
194
EdgePairs: Width violations as edge pairs
195
"""
196
197
def space_check(self, d: int, whole_edges: bool = False,
198
metrics: str = "euclidean", ignore_angle: float = None) -> EdgePairs:
199
"""
200
Check minimum spacing rule.
201
202
Parameters:
203
- d: Minimum spacing requirement
204
- whole_edges: Check whole edges vs. edge segments
205
- metrics: Distance metrics
206
- ignore_angle: Ignore edges at this angle
207
208
Returns:
209
EdgePairs: Spacing violations as edge pairs
210
"""
211
212
def enclosing_check(self, other: Region, d: int,
213
whole_edges: bool = False) -> EdgePairs:
214
"""
215
Check enclosure rule (this region must enclose other by distance d).
216
217
Parameters:
218
- other: Region to be enclosed
219
- d: Minimum enclosure distance
220
- whole_edges: Check whole edges vs. segments
221
222
Returns:
223
EdgePairs: Enclosure violations
224
"""
225
226
def overlap_check(self, other: Region, d: int,
227
whole_edges: bool = False) -> EdgePairs:
228
"""
229
Check overlap rule (regions must overlap by at least distance d).
230
231
Parameters:
232
- other: Other region to check overlap with
233
- d: Minimum overlap distance
234
- whole_edges: Check whole edges vs. segments
235
236
Returns:
237
EdgePairs: Overlap violations
238
"""
239
240
def separation_check(self, other: Region, d: int,
241
whole_edges: bool = False) -> EdgePairs:
242
"""
243
Check separation rule (regions must be separated by at least d).
244
245
Parameters:
246
- other: Other region to check separation from
247
- d: Minimum separation distance
248
- whole_edges: Check whole edges vs. segments
249
250
Returns:
251
EdgePairs: Separation violations
252
"""
253
254
def notch_check(self, d: int) -> EdgePairs:
255
"""
256
Check for notches smaller than minimum width.
257
258
Parameters:
259
- d: Minimum notch width
260
261
Returns:
262
EdgePairs: Notch violations
263
"""
264
265
def isolated_check(self, d: int) -> EdgePairs:
266
"""
267
Check for isolated features smaller than minimum size.
268
269
Parameters:
270
- d: Minimum feature size
271
272
Returns:
273
EdgePairs: Isolated feature violations
274
"""
275
```
276
277
### Netlist and Circuit Analysis
278
279
```python { .api }
280
class Netlist:
281
def __init__(self):
282
"""Create an empty netlist."""
283
284
def create_circuit(self, name: str) -> Circuit:
285
"""
286
Create a new circuit in the netlist.
287
288
Parameters:
289
- name: Circuit name
290
291
Returns:
292
Circuit: New circuit object
293
"""
294
295
def circuit_by_name(self, name: str) -> Circuit:
296
"""Get circuit by name."""
297
298
def each_circuit(self):
299
"""Iterate over all circuits."""
300
301
def purge(self) -> None:
302
"""Remove unused circuits and nets."""
303
304
def make_top_level_pins(self) -> None:
305
"""Create top-level pins for unconnected nets."""
306
307
class Circuit:
308
def __init__(self, name: str = ""):
309
"""
310
Create a circuit.
311
312
Parameters:
313
- name: Circuit name
314
"""
315
316
@property
317
def name(self) -> str:
318
"""Get circuit name."""
319
320
def create_net(self, name: str = "") -> Net:
321
"""
322
Create a new net in the circuit.
323
324
Parameters:
325
- name: Net name (optional)
326
327
Returns:
328
Net: New net object
329
"""
330
331
def create_device(self, device_class, name: str = "") -> Device:
332
"""
333
Create a device instance.
334
335
Parameters:
336
- device_class: Device class/model
337
- name: Device instance name
338
339
Returns:
340
Device: New device instance
341
"""
342
343
def create_pin(self, name: str) -> Pin:
344
"""
345
Create a circuit pin.
346
347
Parameters:
348
- name: Pin name
349
350
Returns:
351
Pin: New pin object
352
"""
353
354
def each_net(self):
355
"""Iterate over all nets."""
356
357
def each_device(self):
358
"""Iterate over all devices."""
359
360
def each_pin(self):
361
"""Iterate over all pins."""
362
363
class Net:
364
def __init__(self, name: str = ""):
365
"""
366
Create a net.
367
368
Parameters:
369
- name: Net name
370
"""
371
372
@property
373
def name(self) -> str:
374
"""Get net name."""
375
376
def connect_pin(self, pin: Pin) -> None:
377
"""
378
Connect a pin to this net.
379
380
Parameters:
381
- pin: Pin to connect
382
"""
383
384
def each_pin(self):
385
"""Iterate over connected pins."""
386
387
def pin_count(self) -> int:
388
"""Get number of connected pins."""
389
390
class Device:
391
def __init__(self, name: str = ""):
392
"""
393
Create a device.
394
395
Parameters:
396
- name: Device name
397
"""
398
399
@property
400
def name(self) -> str:
401
"""Get device name."""
402
403
def create_terminal(self, name: str) -> Pin:
404
"""
405
Create a device terminal.
406
407
Parameters:
408
- name: Terminal name
409
410
Returns:
411
Pin: Terminal pin
412
"""
413
414
def each_terminal(self):
415
"""Iterate over device terminals."""
416
417
class Pin:
418
def __init__(self, name: str = ""):
419
"""
420
Create a pin.
421
422
Parameters:
423
- name: Pin name
424
"""
425
426
@property
427
def name(self) -> str:
428
"""Get pin name."""
429
430
def net(self) -> Net:
431
"""Get connected net."""
432
```
433
434
## Usage Examples
435
436
### Basic DRC Rule Checking
437
438
```python
439
import klayout.db as db
440
import klayout.rdb as rdb
441
442
# Load layout
443
layout = db.Layout()
444
layout.read("design.gds")
445
446
# Get shapes from specific layers
447
metal1_layer = layout.layer(db.LayerInfo(1, 0))
448
metal1_region = db.Region()
449
450
# Collect all Metal1 shapes
451
top_cell = layout.top_cell()
452
for shape in top_cell.shapes(metal1_layer).each():
453
metal1_region.insert(shape.polygon)
454
455
# Create results database
456
report_db = rdb.ReportDatabase("DRC_Report")
457
458
# Define DRC rules
459
min_width = 120 # 120nm minimum width
460
min_spacing = 140 # 140nm minimum spacing
461
462
# Check width violations
463
width_violations = metal1_region.width_check(min_width)
464
if width_violations.count() > 0:
465
width_category = report_db.create_category("Metal1 Width Violations")
466
width_category.set_description(f"Minimum width {min_width}nm")
467
468
for edge_pair in width_violations.each():
469
item = width_category.create_item()
470
# Convert edge pair to polygon for visualization
471
violation_poly = edge_pair.to_polygon(10) # 10nm marker
472
item.add_value(rdb.Value(violation_poly))
473
474
# Add measurement value
475
distance = edge_pair.distance()
476
item.add_value(rdb.Value(f"Width: {distance}nm"))
477
478
# Check spacing violations
479
space_violations = metal1_region.space_check(min_spacing)
480
if space_violations.count() > 0:
481
space_category = report_db.create_category("Metal1 Spacing Violations")
482
space_category.set_description(f"Minimum spacing {min_spacing}nm")
483
484
for edge_pair in space_violations.each():
485
item = space_category.create_item()
486
violation_poly = edge_pair.to_polygon(10)
487
item.add_value(rdb.Value(violation_poly))
488
489
distance = edge_pair.distance()
490
item.add_value(rdb.Value(f"Spacing: {distance}nm"))
491
492
# Save report
493
report_db.save("metal1_drc_report.lyrdb")
494
print(f"DRC complete: {width_violations.count()} width, {space_violations.count()} spacing violations")
495
```
496
497
### Comprehensive Multi-Layer DRC
498
499
```python
500
import klayout.db as db
501
import klayout.rdb as rdb
502
503
def run_comprehensive_drc(layout_file: str, output_report: str):
504
"""Run comprehensive DRC on a layout file."""
505
506
layout = db.Layout()
507
layout.read(layout_file)
508
509
# Define layers and rules
510
layer_rules = {
511
"NWELL": {"layer": (1, 0), "min_width": 600, "min_spacing": 600},
512
"ACTIVE": {"layer": (2, 0), "min_width": 150, "min_spacing": 150},
513
"POLY": {"layer": (3, 0), "min_width": 100, "min_spacing": 140},
514
"CONTACT": {"layer": (4, 0), "min_width": 120, "min_spacing": 140},
515
"METAL1": {"layer": (5, 0), "min_width": 120, "min_spacing": 140},
516
"VIA1": {"layer": (6, 0), "min_width": 120, "min_spacing": 140},
517
"METAL2": {"layer": (7, 0), "min_width": 140, "min_spacing": 140},
518
}
519
520
# Define enclosure rules
521
enclosure_rules = [
522
{"inner": "CONTACT", "outer": "ACTIVE", "min_enc": 60},
523
{"inner": "CONTACT", "outer": "POLY", "min_enc": 50},
524
{"inner": "VIA1", "outer": "METAL1", "min_enc": 60},
525
{"inner": "VIA1", "outer": "METAL2", "min_enc": 60},
526
]
527
528
report_db = rdb.ReportDatabase("Comprehensive_DRC")
529
530
# Extract regions for each layer
531
regions = {}
532
for layer_name, rules in layer_rules.items():
533
layer_info = db.LayerInfo(rules["layer"][0], rules["layer"][1])
534
layer_idx = layout.layer(layer_info)
535
536
region = db.Region()
537
for cell in layout.each_cell():
538
for shape in cell.shapes(layer_idx).each():
539
region.insert(shape.polygon)
540
541
regions[layer_name] = region.merged() # Merge overlapping shapes
542
543
# Run width and spacing checks
544
for layer_name, rules in layer_rules.items():
545
region = regions[layer_name]
546
547
if region.is_empty():
548
continue
549
550
# Width check
551
width_violations = region.width_check(rules["min_width"])
552
if width_violations.count() > 0:
553
category = report_db.create_category(f"{layer_name} Width Violations")
554
category.set_description(f"Minimum width {rules['min_width']}nm")
555
556
for edge_pair in width_violations.each():
557
item = category.create_item()
558
violation_poly = edge_pair.to_polygon(20)
559
item.add_value(rdb.Value(violation_poly))
560
item.add_value(rdb.Value(f"Measured: {edge_pair.distance()}nm"))
561
562
# Spacing check
563
space_violations = region.space_check(rules["min_spacing"])
564
if space_violations.count() > 0:
565
category = report_db.create_category(f"{layer_name} Spacing Violations")
566
category.set_description(f"Minimum spacing {rules['min_spacing']}nm")
567
568
for edge_pair in space_violations.each():
569
item = category.create_item()
570
violation_poly = edge_pair.to_polygon(20)
571
item.add_value(rdb.Value(violation_poly))
572
item.add_value(rdb.Value(f"Measured: {edge_pair.distance()}nm"))
573
574
# Run enclosure checks
575
for rule in enclosure_rules:
576
inner_region = regions.get(rule["inner"])
577
outer_region = regions.get(rule["outer"])
578
579
if inner_region and outer_region and not inner_region.is_empty() and not outer_region.is_empty():
580
enc_violations = inner_region.enclosing_check(outer_region, rule["min_enc"])
581
582
if enc_violations.count() > 0:
583
category = report_db.create_category(
584
f"{rule['inner']} in {rule['outer']} Enclosure Violations"
585
)
586
category.set_description(f"Minimum enclosure {rule['min_enc']}nm")
587
588
for edge_pair in enc_violations.each():
589
item = category.create_item()
590
violation_poly = edge_pair.to_polygon(20)
591
item.add_value(rdb.Value(violation_poly))
592
item.add_value(rdb.Value(f"Measured: {edge_pair.distance()}nm"))
593
594
# Generate summary
595
total_violations = sum(cat.num_items() for cat in report_db.each_category())
596
print(f"DRC Summary: {total_violations} total violations in {report_db.num_categories()} categories")
597
598
# Save report
599
report_db.save(output_report)
600
return report_db
601
602
# Run comprehensive DRC
603
report = run_comprehensive_drc("complex_design.gds", "comprehensive_drc.lyrdb")
604
```
605
606
### Netlist Extraction and LVS
607
608
```python
609
import klayout.db as db
610
611
def extract_netlist(layout: db.Layout) -> db.Netlist:
612
"""Extract netlist from layout (simplified example)."""
613
614
netlist = db.Netlist()
615
top_circuit = netlist.create_circuit("TOP")
616
617
# Get layer regions
618
active_layer = layout.layer(db.LayerInfo(2, 0))
619
poly_layer = layout.layer(db.LayerInfo(3, 0))
620
contact_layer = layout.layer(db.LayerInfo(4, 0))
621
metal1_layer = layout.layer(db.LayerInfo(5, 0))
622
623
active_region = db.Region()
624
poly_region = db.Region()
625
contact_region = db.Region()
626
metal1_region = db.Region()
627
628
top_cell = layout.top_cell()
629
630
# Collect shapes
631
for shape in top_cell.shapes(active_layer).each():
632
active_region.insert(shape.polygon)
633
for shape in top_cell.shapes(poly_layer).each():
634
poly_region.insert(shape.polygon)
635
for shape in top_cell.shapes(contact_layer).each():
636
contact_region.insert(shape.polygon)
637
for shape in top_cell.shapes(metal1_layer).each():
638
metal1_region.insert(shape.polygon)
639
640
# Find transistors (simplified: active AND poly intersections)
641
transistor_regions = active_region & poly_region
642
643
device_count = 0
644
for transistor_poly in transistor_regions.each():
645
device_count += 1
646
device = top_circuit.create_device(None, f"M{device_count}")
647
648
# Create terminals
649
gate_pin = device.create_terminal("G")
650
source_pin = device.create_terminal("S")
651
drain_pin = device.create_terminal("D")
652
bulk_pin = device.create_terminal("B")
653
654
# This is a simplified example - real extraction would:
655
# 1. Analyze connectivity through contacts and metal
656
# 2. Identify source/drain regions
657
# 3. Trace nets through the layout
658
# 4. Handle hierarchy and instances
659
660
# Extract nets by analyzing metal connectivity
661
# (This would be much more complex in a real implementation)
662
metal_connected = metal1_region.merged()
663
664
net_count = 0
665
for metal_poly in metal_connected.each():
666
net_count += 1
667
net = top_circuit.create_net(f"net{net_count}")
668
669
# Find contacts that connect to this metal
670
metal_contacts = contact_region & db.Region([metal_poly])
671
672
# Connect devices through contacts (simplified)
673
# Real implementation would trace through layout hierarchy
674
675
return netlist
676
677
def compare_netlists(layout_netlist: db.Netlist, schematic_netlist: db.Netlist) -> rdb.ReportDatabase:
678
"""Compare extracted netlist with reference schematic (simplified LVS)."""
679
680
report_db = rdb.ReportDatabase("LVS_Report")
681
682
# Compare circuit counts
683
layout_circuits = list(layout_netlist.each_circuit())
684
schematic_circuits = list(schematic_netlist.each_circuit())
685
686
if len(layout_circuits) != len(schematic_circuits):
687
category = report_db.create_category("Circuit Count Mismatch")
688
item = category.create_item()
689
item.add_value(rdb.Value(f"Layout: {len(layout_circuits)}, Schematic: {len(schematic_circuits)}"))
690
691
# Compare each circuit
692
for layout_circuit in layout_circuits:
693
schematic_circuit = schematic_netlist.circuit_by_name(layout_circuit.name)
694
695
if not schematic_circuit:
696
category = report_db.create_category("Missing Circuits")
697
item = category.create_item()
698
item.add_value(rdb.Value(f"Circuit {layout_circuit.name} not found in schematic"))
699
continue
700
701
# Compare device counts
702
layout_devices = list(layout_circuit.each_device())
703
schematic_devices = list(schematic_circuit.each_device())
704
705
if len(layout_devices) != len(schematic_devices):
706
category = report_db.create_category("Device Count Mismatch")
707
item = category.create_item()
708
item.add_value(rdb.Value(
709
f"Circuit {layout_circuit.name}: Layout {len(layout_devices)}, "
710
f"Schematic {len(schematic_devices)}"
711
))
712
713
# Compare net counts
714
layout_nets = list(layout_circuit.each_net())
715
schematic_nets = list(schematic_circuit.each_net())
716
717
if len(layout_nets) != len(schematic_nets):
718
category = report_db.create_category("Net Count Mismatch")
719
item = category.create_item()
720
item.add_value(rdb.Value(
721
f"Circuit {layout_circuit.name}: Layout {len(layout_nets)}, "
722
f"Schematic {len(schematic_nets)}"
723
))
724
725
return report_db
726
727
# Example usage
728
layout = db.Layout()
729
layout.read("extracted_design.gds")
730
731
# Extract netlist from layout
732
layout_netlist = extract_netlist(layout)
733
734
# Load reference schematic netlist (would typically be SPICE format)
735
schematic_netlist = db.Netlist()
736
# ... load schematic netlist from file ...
737
738
# Run LVS comparison
739
lvs_report = compare_netlists(layout_netlist, schematic_netlist)
740
lvs_report.save("lvs_report.lyrdb")
741
742
print(f"LVS complete: {lvs_report.num_categories()} categories, "
743
f"{sum(cat.num_items() for cat in lvs_report.each_category())} total issues")
744
```
745
746
### Advanced Verification Reporting
747
748
```python
749
import klayout.db as db
750
import klayout.rdb as rdb
751
752
def create_detailed_drc_report(violations: db.EdgePairs, rule_name: str,
753
rule_value: int, category: rdb.Category):
754
"""Create detailed DRC report with comprehensive information."""
755
756
violation_count = violations.count()
757
category.set_description(
758
f"{rule_name}: {rule_value}nm requirement, {violation_count} violations found"
759
)
760
761
# Statistics
762
distances = []
763
areas = []
764
765
for edge_pair in violations.each():
766
item = category.create_item()
767
768
# Add geometric representation
769
violation_poly = edge_pair.to_polygon(25) # 25nm marker
770
item.add_value(rdb.Value(violation_poly))
771
772
# Add measurements
773
distance = edge_pair.distance()
774
distances.append(distance)
775
item.add_value(rdb.Value(f"Measured: {distance}nm"))
776
item.add_value(rdb.Value(f"Required: {rule_value}nm"))
777
item.add_value(rdb.Value(f"Violation: {rule_value - distance}nm"))
778
779
# Add location information
780
bbox = violation_poly.bbox()
781
center_x = (bbox.left + bbox.right) // 2
782
center_y = (bbox.bottom + bbox.top) // 2
783
item.add_value(rdb.Value(f"Location: ({center_x}, {center_y})"))
784
785
# Add severity tag
786
severity = "Critical" if distance < rule_value * 0.8 else "Warning"
787
tag = rdb.Tags(severity)
788
item.add_tag(tag)
789
790
# Calculate affected area (approximate)
791
area = violation_poly.area()
792
areas.append(area)
793
item.add_value(rdb.Value(f"Affected area: {area} sq.nm"))
794
795
# Add summary statistics to first item
796
if violation_count > 0:
797
summary_item = category.create_item()
798
summary_item.add_value(rdb.Value(f"Total violations: {violation_count}"))
799
summary_item.add_value(rdb.Value(f"Min distance: {min(distances)}nm"))
800
summary_item.add_value(rdb.Value(f"Max distance: {max(distances)}nm"))
801
summary_item.add_value(rdb.Value(f"Avg distance: {sum(distances)/len(distances):.1f}nm"))
802
summary_item.add_value(rdb.Value(f"Total affected area: {sum(areas)} sq.nm"))
803
804
summary_tag = rdb.Tags("Summary")
805
summary_item.add_tag(summary_tag)
806
807
def generate_verification_dashboard(report_db: rdb.ReportDatabase):
808
"""Generate verification dashboard with key metrics."""
809
810
print("=== VERIFICATION DASHBOARD ===")
811
print(f"Report: {report_db.name}")
812
print(f"Categories: {report_db.num_categories()}")
813
814
total_items = 0
815
critical_items = 0
816
warning_items = 0
817
818
for category in report_db.each_category():
819
cat_items = category.num_items()
820
total_items += cat_items
821
822
print(f"\n{category.name}: {cat_items} items")
823
print(f" Description: {category.description}")
824
825
# Count by severity
826
cat_critical = 0
827
cat_warning = 0
828
829
for item in category.each_item():
830
for tag in item.each_tag():
831
if tag.tag == "Critical":
832
cat_critical += 1
833
critical_items += 1
834
elif tag.tag == "Warning":
835
cat_warning += 1
836
warning_items += 1
837
838
if cat_critical > 0 or cat_warning > 0:
839
print(f" Critical: {cat_critical}, Warnings: {cat_warning}")
840
841
print(f"\n=== SUMMARY ===")
842
print(f"Total violations: {total_items}")
843
print(f"Critical: {critical_items}")
844
print(f"Warnings: {warning_items}")
845
print(f"Success rate: {max(0, 100 - (critical_items + warning_items)/max(1, total_items)*100):.1f}%")
846
847
# Example: Enhanced DRC with detailed reporting
848
def run_enhanced_drc(layout_file: str):
849
"""Run DRC with enhanced reporting and analysis."""
850
851
layout = db.Layout()
852
layout.read(layout_file)
853
854
report_db = rdb.ReportDatabase("Enhanced_DRC_Report")
855
856
# Extract Metal1 layer
857
metal1_layer = layout.layer(db.LayerInfo(5, 0))
858
metal1_region = db.Region()
859
860
top_cell = layout.top_cell()
861
for shape in top_cell.shapes(metal1_layer).each():
862
metal1_region.insert(shape.polygon)
863
864
metal1_region = metal1_region.merged()
865
866
# Run checks with detailed reporting
867
min_width = 120
868
width_violations = metal1_region.width_check(min_width)
869
870
if width_violations.count() > 0:
871
width_category = report_db.create_category("Metal1 Width Violations")
872
create_detailed_drc_report(width_violations, "Minimum Width",
873
min_width, width_category)
874
875
min_spacing = 140
876
space_violations = metal1_region.space_check(min_spacing)
877
878
if space_violations.count() > 0:
879
space_category = report_db.create_category("Metal1 Spacing Violations")
880
create_detailed_drc_report(space_violations, "Minimum Spacing",
881
min_spacing, space_category)
882
883
# Generate dashboard
884
generate_verification_dashboard(report_db)
885
886
# Save enhanced report
887
report_db.save("enhanced_drc_report.lyrdb")
888
889
return report_db
890
891
# Run enhanced DRC
892
enhanced_report = run_enhanced_drc("test_design.gds")
893
```