"For example, whether you are laying out an SMT board or a 130nm polysilicon resistor targeting the SkyWater PDK, you write the physical and electrical constraints once in .hw text.
The compiler (hwc) validates the 3D material stackup and via depths, performs DRC/LVS and EM/thermal checks, and compiles out everything required downstream:
Manufacturing: Gerber X3 / Excellon (PCBs) or GDSII (Silicon)
Simulation: SPICE netlists with extracted parasitic R/C(dc.sp, ac.sp, tran.sp)
Visualization & Mechanical: 2D DXF and 3D GLB/OBJ models
Procurement: Full BOM accounting (bom.csv)
"# simple_resistor_test.hw - SKY130 P+ Poly Resistor Test (100% Relational Layout)
# Device: sky130_fd_pr__res_high_po (350 Ω/□)
# Geometry: 4μm × 1.41μm = ~1.0kΩ expected (using PDK-compliant quantized width)
#
import * from @std/primitives/units
import * from "./resistor_pdk"
module SimpleResistor:
pins: [input In, output Out]
route In to Out
space Simple_Resistor_Space implements SimpleResistor:
dimensions: 20.0um by 10.0um
profile: Resistor_3D
nets:
In: { classification: signal, potential: 1.8V, current: 1.0uA }
Out: { classification: signal, potential: 0.0V, current: 1.0uA }
GND: { classification: ground, potential: 0.0V, current: 0.0uA }
device_nets R1:
BULK: GND
# ========================================================================
# PARAMETRIC DIMENSIONS (Self-Healing Layout)
# ========================================================================
let res_length = 4.0um
let res_width = 1.41um # SKY130 quantized width (W ∈ {0.35, 0.69, 1.41, 2.85, 5.73}μm)
let contact_length = 400nm # Contact head length along current flow direction
let pad_size = 1.0um
let pad_spacing = 3.0um
let tap_size = 400nm
let tap_offset_y = 2.5um
let via_pitch = 400nm # Via pitch: diameter (170nm) + min_spacing (230nm clearance)
add pour(Polysilicon) named Resistor_Body on layer: polyres:
device: R1.A, R1.B
dimensions: res_length by res_width
at: [x: 10.0um, y: 5.0um]
add pour(Resistor_Poly_Mask) named RPM_Block on layer: rpm:
dimensions: res_length + 360nm by res_width + 360nm
align: center_x with Resistor_Body
align: center_y with Resistor_Body
add pour(Titanium_Silicide) named Contact_A_LI on layer: li1:
device: R1.A
net: In
dimensions: contact_length by res_width
align: center_x with Resistor_Body.left + contact_length / 2
align: center_y with Resistor_Body
# Right Contact B: Centered on Resistor_Body.right - half contact length
add pour(Titanium_Silicide) named Contact_B_LI on layer: li1:
device: R1.B
net: Out
dimensions: contact_length by res_width
align: center_x with Resistor_Body.right - contact_length / 2
align: center_y with Resistor_Body
for i in 0..3:
add contact(Tungsten) named Via_A_Poly_{i} spanning layer: polyres to li1:
diameter: 170nm
align: center_x with Contact_A_LI
align: center_y with Contact_A_LI.center_y + (i - 1) * via_pitch
net: In
# Terminal B via array (3 vias symmetrically centered)
for i in 0..3:
add contact(Tungsten) named Via_B_Poly_{i} spanning layer: polyres to li1:
diameter: 170nm
align: center_x with Contact_B_LI
align: center_y with Contact_B_LI.center_y + (i - 1) * via_pitch
net: Out
add pour(Aluminum) named Contact_A_Metal on layer: metal1:
dimensions: contact_length by res_width
align: center_x with Contact_A_LI
align: center_y with Contact_A_LI
net: In
add pour(Aluminum) named Contact_B_Metal on layer: metal1:
dimensions: contact_length by res_width
align: center_x with Contact_B_LI
align: center_y with Contact_B_LI
net: Out
for i in 0..3:
add contact(Tungsten) named Via_A_Metal_{i} spanning layer: li1 to metal1:
diameter: 170nm
align: center_x with Contact_A_LI
align: center_y with Contact_A_LI.center_y + (i - 1) * via_pitch
net: In
# Terminal B via array (3 vias symmetrically centered)
for i in 0..3:
add contact(Tungsten) named Via_B_Metal_{i} spanning layer: li1 to metal1:
diameter: 170nm
align: center_x with Contact_B_LI
align: center_y with Contact_B_LI.center_y + (i - 1) * via_pitch
net: Out
add pour(Aluminum) named In_Pad on layer: metal1:
dimensions: pad_size by pad_size
align: center_x with Contact_A_Metal.left - pad_spacing
align: center_y with Contact_A_Metal
net: In
add pour(Aluminum) named Out_Pad on layer: metal1:
dimensions: pad_size by pad_size
align: center_x with Contact_B_Metal.right + pad_spacing
align: center_y with Contact_B_Metal
net: Out
route In_Pad to Contact_A_Metal:
net: In
width: 300nm
layer: metal1
intent: Signal
route Contact_B_Metal to Out_Pad:
net: Out
width: 300nm
layer: metal1
intent: Signal
add pour(P_Plus_Diffusion) named Bulk_Tap_Diff on layer: pdiff:
dimensions: tap_size by tap_size
align: center_x with Resistor_Body
align: center_y with Resistor_Body.center_y + tap_offset_y
net: GND
device: R1.BULK
# 8b. LOCAL INTERCONNECT CONTACT (licon - connects pdiff to li1)
add contact(Tungsten) named Bulk_Tap_Contact spanning layer: pdiff to li1:
diameter: 170nm
align: center_x with Bulk_Tap_Diff
align: center_y with Bulk_Tap_Diff
net: GND
# 8c. LOCAL INTERCONNECT METAL (li1 layer)
add pour(Titanium_Silicide) named Bulk_Tap_LI on layer: li1:
dimensions: tap_size by tap_size
align: center_x with Bulk_Tap_Diff
align: center_y with Bulk_Tap_Diff
net: GND
test Simple_Resistor_AC_Test for Simple_Resistor_Space:
ac: { sweep: dec, points: 20, freq: 100Hz..100MHz }
tran: { step: 10ps, stop: 200ns }