This example demonstrates advanced EDA circuit layout analysis using ZLayout. We'll create a complex circuit layout with various components and analyze it for manufacturing feasibility.
Electronic Design Automation (EDA) tools must verify that circuit layouts meet manufacturing constraints. This example shows how to use ZLayout to:
"""
Advanced EDA Circuit Layout Example
This example simulates a more realistic electronic circuit layout with:
- Various component types (chips, resistors, capacitors, traces)
- Different geometric constraints
- Comprehensive analysis for manufacturing feasibility
"""
import sys
import os
import math
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import zlayout
import matplotlib.pyplot as plt
class CircuitComponent:
"""Represents a circuit component with properties."""
def __init__(self, name: str, geometry, component_type: str = "generic"):
self.name = name
self.geometry = geometry
self.component_type = component_type
self.pins = []
def add_pin(self, point: zlayout.Point, pin_name: str = ""):
"""Add a pin to the component."""
self.pins.append((point, pin_name))
def create_microcontroller(x: float, y: float) -> CircuitComponent:
"""Create a microcontroller component with realistic dimensions."""
geometry = body.to_polygon()
mcu = CircuitComponent("MCU_STM32F4", geometry, "microcontroller")
pin_spacing = 0.65
pin_count_per_side = 16
for i in range(pin_count_per_side):
pin_x = x + 1.5 + i * pin_spacing
for i in range(pin_count_per_side):
pin_y = y + 1.5 + i * pin_spacing
for i in range(pin_count_per_side):
pin_x = x + 12.5 - i * pin_spacing
for i in range(pin_count_per_side):
pin_y = y + 12.5 - i * pin_spacing
return mcu
def create_resistor(x: float, y: float, rotation: float = 0,
value: str = "1k", package: str = "0805") -> CircuitComponent:
"""Create a resistor component with specified package size."""
packages = {
"0402": (1.0, 0.5),
"0603": (1.6, 0.8),
"0805": (2.0, 1.25),
"1206": (3.2, 1.6)
}
length, width = packages.get(package, (2.0, 1.25))
vertices = [
]
cos_r, sin_r = math.cos(rotation), math.sin(rotation)
rotated_vertices = []
for v in vertices:
new_x = v.x * cos_r - v.y * sin_r + x
new_y = v.x * sin_r + v.y * cos_r + y
resistor = CircuitComponent(f"R_{value}_{package}", geometry, "resistor")
pad_offset = length/2 * 0.8
return resistor
def create_capacitor(x: float, y: float, value: str = "10uF",
package: str = "0805") -> CircuitComponent:
"""Create a capacitor component."""
packages = {
"0402": (1.0, 0.5),
"0603": (1.6, 0.8),
"0805": (2.0, 1.25),
"1206": (3.2, 1.6),
"1210": (3.2, 2.5)
}
length, width = packages.get(package, (2.0, 1.25))
cap = CircuitComponent(f"C_{value}_{package}", geometry, "capacitor")
return cap
width: float = 0.2, layer: str = "top") -> CircuitComponent:
"""Create a PCB trace (connection line with width)."""
dx = end.x - start.x
dy = end.y - start.y
length = math.sqrt(dx*dx + dy*dy)
if length < 1e-10:
else:
perp_x = -dy * width / (2 * length)
perp_y = dx * width / (2 * length)
vertices = [
]
trace_name = f"Trace_{layer}_{width}mm"
return CircuitComponent(trace_name, geometry, "trace")
def create_realistic_circuit_layout():
"""Create a realistic circuit layout representing a microcontroller board."""
components = []
mcu = create_microcontroller(18, 13)
components.append(mcu)
processor.add_component(mcu.geometry)
power_components = [
create_resistor(8, 30, 0, "3.3V_REG", "1206"),
create_capacitor(5, 30, "22uF", "1210"),
create_capacitor(12, 30, "100nF", "0603"),
create_resistor(8, 27, 0, "10uH", "1210"),
create_resistor(8, 24, 0, "10k", "0603"),
create_resistor(11, 24, 0, "3.3k", "0603"),
]
for comp in power_components:
components.append(comp)
processor.add_component(comp.geometry)
bypass_caps = [
create_capacitor(15, 10, "100nF", "0603"),
create_capacitor(35, 10, "100nF", "0603"),
create_capacitor(15, 30, "100nF", "0603"),
create_capacitor(35, 30, "100nF", "0603"),
]
for cap in bypass_caps:
components.append(cap)
processor.add_component(cap.geometry)
crystal_components = [
create_resistor(40, 20, math.pi/2, "8MHz", "HC49"),
create_capacitor(37, 18, "22pF", "0603"),
create_capacitor(37, 22, "22pF", "0603"),
]
for comp in crystal_components:
components.append(comp)
processor.add_component(comp.geometry)
led_components = [
create_resistor(45, 35, 0, "LED_R", "0603"),
create_resistor(45, 32, 0, "LED_G", "0603"),
create_resistor(45, 29, 0, "LED_B", "0603"),
create_resistor(42, 35, 0, "330R", "0603"),
create_resistor(42, 32, 0, "330R", "0603"),
create_resistor(42, 29, 0, "330R", "0603"),
]
for comp in led_components:
components.append(comp)
processor.add_component(comp.geometry)
traces = [
]
for trace in traces:
components.append(trace)
processor.add_component(trace.geometry)
connector_vertices = [
]
components.append(CircuitComponent("Sharp_Connector", sharp_connector, "connector"))
processor.add_component(sharp_connector)
components.extend([
CircuitComponent("Test_Overlap1", overlap_rect1, "test"),
CircuitComponent("Test_Overlap2", overlap_rect2, "test")
])
processor.add_component(overlap_rect1)
processor.add_component(overlap_rect2)
return processor, components
def analyze_manufacturing_feasibility(processor):
"""Comprehensive manufacturing feasibility analysis."""
print("=== Manufacturing Feasibility Analysis ===\n")
process_constraints = {
"prototype": {
"min_trace_width": 0.1,
"min_spacing": 0.1,
"sharp_angle_limit": 20,
"description": "Prototype PCB (loose tolerances)"
},
"standard": {
"min_trace_width": 0.15,
"min_spacing": 0.15,
"sharp_angle_limit": 30,
"description": "Standard PCB manufacturing"
},
"high_density": {
"min_trace_width": 0.05,
"min_spacing": 0.05,
"sharp_angle_limit": 15,
"description": "High-density interconnect (HDI)"
}
}
analysis_results = {}
for process_name, constraints in process_constraints.items():
print(f"--- {process_name.upper()} PROCESS ---")
print(f"Description: {constraints['description']}")
analysis = processor.analyze_layout(
sharp_angle_threshold=constraints["sharp_angle_limit"],
narrow_distance_threshold=constraints["min_spacing"]
)
analysis_results[process_name] = analysis
violations = 0
warnings = []
if analysis['sharp_angles']['count'] > 0:
violations += analysis['sharp_angles']['count']
warnings.append(f"Sharp angles: {analysis['sharp_angles']['count']} violations")
for angle_info in analysis['sharp_angles']['details'][:3]:
warnings.append(f" - {angle_info['angle']:.1f}° at {angle_info['location']}")
if analysis['narrow_distances']['count'] > 0:
violations += analysis['narrow_distances']['count']
warnings.append(f"Spacing violations: {analysis['narrow_distances']['count']}")
warnings.append(f" - Minimum distance: {analysis['narrow_distances']['minimum']:.3f}mm")
if analysis['intersections']['polygon_pairs'] > 0:
violations += analysis['intersections']['polygon_pairs']
warnings.append(f"Component intersections: {analysis['intersections']['polygon_pairs']} pairs")
if violations == 0:
print("✅ MANUFACTURABLE")
print(" All constraints satisfied")
else:
print(f"❌ {violations} VIOLATIONS")
for warning in warnings:
print(f" {warning}")
yield_estimate = max(0, 100 - violations * 5)
print(f" Estimated yield: {yield_estimate}%")
print()
return analysis_results
def generate_component_report(components):
"""Generate detailed component analysis report."""
print("=== Component Analysis Report ===\n")
component_stats = {}
total_area = 0
for comp in components:
comp_type = comp.component_type
if comp_type not in component_stats:
component_stats[comp_type] = {
'count': 0,
'total_area': 0,
'components': []
}
component_stats[comp_type]['count'] += 1
component_stats[comp_type]['components'].append(comp)
area = comp.geometry.area()
component_stats[comp_type]['total_area'] += area
total_area += area
print("Component Summary:")
print(f"{'Type':<15} {'Count':<8} {'Area (mm²)':<12} {'% of Total':<10}")
print("-" * 50)
for comp_type, stats in sorted(component_stats.items()):
percentage = (stats['total_area'] / total_area * 100) if total_area > 0 else 0
print(f"{comp_type:<15} {stats['count']:<8} {stats['total_area']:<12.2f} {percentage:<10.1f}%")
print(f"\nTotal components: {len(components)}")
print(f"Total area: {total_area:.2f} mm²")
pcb_area = 50 * 40
utilization = (total_area / pcb_area) * 100
print(f"PCB utilization: {utilization:.1f}%")
if utilization > 80:
print("⚠️ High component density - consider larger PCB or double-sided layout")
elif utilization < 30:
print("ℹ️ Low component density - PCB size could be optimized")
else:
print("✅ Good component density")
return component_stats
def create_visualization(processor, components, analysis_results):
"""Create comprehensive visualization of the circuit and analysis."""
print("\n=== Creating Visualizations ===")
try:
polygons = [comp.geometry for comp in components if hasattr(comp.geometry, 'vertices')]
print("Creating circuit layout visualization...")
fig1 = visualizer.plot_layout(
polygons,
title="Microcontroller Board Layout",
show_grid=True,
grid_spacing=5.0,
component_labels=False
)
colors = {
'microcontroller': 'red',
'resistor': 'blue',
'capacitor': 'green',
'trace': 'gray',
'connector': 'orange',
'test': 'purple'
}
for comp in components:
if hasattr(comp.geometry, 'vertices'):
color = colors.get(comp.component_type, 'black')
visualizer.add_component_highlight(comp.geometry, color=color, alpha=0.6)
print("Creating analysis visualization...")
fig2 = visualizer.plot_analysis_results(
polygons,
analysis_results['standard'],
title="Design Rule Check Results (Standard Process)"
)
print("Creating process comparison...")
fig3 = visualizer.create_process_comparison_chart(analysis_results)
plt.tight_layout()
plt.show()
return [fig1, fig2, fig3]
except ImportError:
print("Matplotlib not available. Install with: pip install matplotlib")
return []
"""Main function demonstrating comprehensive EDA circuit analysis."""
print("🔧 ZLayout - Advanced EDA Circuit Analysis")
print("=" * 60)
print("❌ Failed to initialize ZLayout")
return
try:
print("Creating realistic microcontroller board layout...")
processor, components = create_realistic_circuit_layout()
print(f"✅ Created layout with {len(components)} components")
analysis_results = analyze_manufacturing_feasibility(processor)
component_stats = generate_component_report(components)
figures = create_visualization(processor, components, analysis_results)
print("\n=== Final Assessment ===")
best_process = None
min_violations = float('inf')
for process_name, analysis in analysis_results.items():
violations = (analysis['sharp_angles']['count'] +
analysis['narrow_distances']['count'] +
analysis['intersections']['polygon_pairs'])
if violations < min_violations:
min_violations = violations
best_process = process_name
print(f"Recommended process: {best_process.upper()}")
print(f"Total violations: {min_violations}")
if min_violations == 0:
print("✅ Design is ready for manufacturing!")
else:
print("⚠️ Design needs revision before manufacturing")
print("\nRecommended actions:")
print("1. Increase spacing between narrow traces")
print("2. Smooth sharp angles in connector geometries")
print("3. Check for component overlaps")
save_results = input("\nSave analysis results and visualizations? (y/n): ").lower().strip()
if save_results == 'y':
if figures:
visualizer.save_plots(figures, "eda_circuit_analysis")
with open("eda_analysis_report.txt", "w") as f:
f.write("ZLayout EDA Circuit Analysis Report\n")
f.write("=" * 50 + "\n\n")
f.write("COMPONENT SUMMARY\n")
f.write("-" * 20 + "\n")
for comp_type, stats in component_stats.items():
f.write(f"{comp_type}: {stats['count']} components, "
f"{stats['total_area']:.2f} mm²\n")
f.write("\nMANUFACTURING ANALYSIS\n")
f.write("-" * 25 + "\n")
for process_name, analysis in analysis_results.items():
violations = (analysis['sharp_angles']['count'] +
analysis['narrow_distances']['count'] +
analysis['intersections']['polygon_pairs'])
f.write(f"{process_name}: {violations} violations\n")
f.write(f"\nRECOMMENDED PROCESS: {best_process.upper()}\n")
print("✅ Results saved to files!")
except Exception as e:
print(f"❌ Error during analysis: {e}")
import traceback
traceback.print_exc()
finally:
print("\n🎉 EDA circuit analysis completed!")
if __name__ == "__main__":
2D point with high-precision coordinates and utility methods
Polygon class supporting both convex and concave polygons.
Axis-aligned rectangle for bounding boxes and simple EDA components.
bool initialize(bool enable_openmp=true)
Initialize ZLayout library.
void cleanup()
Cleanup ZLayout library resources.
This example shows how to model real electronic components with accurate dimensions:
This example provides a solid foundation for understanding how ZLayout can be used in real EDA workflows, from initial design through manufacturing verification.