;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Description: Infinite Metal extension on multi-crossing (Bug perfectly fixed) + HV (2x2) via options
;; Shortcut: Shift + F6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ====================================================================
;; [Option 1] Via Generation Mode (Specify direction and cut count)
;; 1 : Default 1x1 (1-cut)
;; "H" : Horizontal 2-cut (Rows=1, Cols=2)
;; "V" : Vertical 2-cut (Rows=2, Cols=1)
;; "HV" : Horizontal/Vertical 4-cut (Rows=2, Cols=2) - When intersection is wide
;; ====================================================================
defvar( AutoBusViaCutMode "HV" )
;; ====================================================================
;; [Option 2] Metal Extension (Enclosure) Forced Setting (Unit: um)
;; - Leave as nil to follow PDK default rules.
;; ====================================================================
defvar( AutoBusViaEnc1 nil ) ;; Example: list(0.02 0.01) (Bottom metal)
defvar( AutoBusViaEnc2 nil ) ;; Example: list(0.01 0.03) (Top metal)
;; ====================================================================
;; [Option 3] Metal - Via Mapping Table
;; ====================================================================
defvar( AutoBusViaMap
list(
list( list("METAL1" "METAL2") "M2_M1" )
list( list("METAL2" "METAL3") "M3_M2" )
list( list("METAL3" "METAL4") "M4_M3" )
list( list("METAL4" "METAL5") "M5_M4" )
)
)
;; Global Variable
defvar( AutoBusStrokePaths1 nil )
;; ====================================================================
;; [Helper Function] Return object orientation (Horizontal/Vertical)
;; ====================================================================
procedure( GetMetalOrientation(obj)
let((pts pt1 pt2 dx dy orientation fixedCoord)
if( obj~>objType == "pathSeg" then pts = list(obj~>beginPt obj~>endPt) else pts = obj~>points )
pt1 = car(pts)
pt2 = car(last(pts))
dx = abs(xCoord(pt1) - xCoord(pt2))
dy = abs(yCoord(pt1) - yCoord(pt2))
if( dx > dy then
orientation = "H" fixedCoord = (yCoord(pt1) + yCoord(pt2)) / 2.0
else
orientation = "V" fixedCoord = (xCoord(pt1) + xCoord(pt2)) / 2.0
)
list(orientation fixedCoord)
)
)
;; ====================================================================
;; 🎯 [Core Bug Fix] Extension algorithm that expands outward without shrinking even after multiple crossings!
;; ====================================================================
procedure( ExtendMetalToPoint(obj targetX targetY isH)
let((pts pt1 pt2 val1 val2 targetVal minVal maxVal newPt1 newPt2 midPts)
if( obj~>objType == "pathSeg" then pts = list(obj~>beginPt obj~>endPt) else pts = obj~>points )
pt1 = car(pts)
pt2 = car(last(pts))
if( isH then
val1 = xCoord(pt1)
val2 = xCoord(pt2)
targetVal = targetX
else
val1 = yCoord(pt1)
val2 = yCoord(pt2)
targetVal = targetY
)
;; Compare existing both ends with target point to extract outermost coordinates (Min, Max)
minVal = min(val1 val2 targetVal)
maxVal = max(val1 val2 targetVal)
;; Overwrite both ends to the outermost points while maintaining the original line drawing direction (start->end)
if( val1 < val2 then
if( isH then newPt1 = list(minVal yCoord(pt1)) newPt2 = list(maxVal yCoord(pt2))
else newPt1 = list(xCoord(pt1) minVal) newPt2 = list(xCoord(pt2) maxVal) )
else
if( isH then newPt1 = list(maxVal yCoord(pt1)) newPt2 = list(minVal yCoord(pt2))
else newPt1 = list(xCoord(pt1) maxVal) newPt2 = list(xCoord(pt1) minVal) )
)
;; Apply extended coordinates to the object
if( obj~>objType == "pathSeg" then
obj~>beginPt = newPt1
obj~>endPt = newPt2
else
if( length(pts) > 2 then
midPts = reverse(cdr(reverse(cdr(pts))))
obj~>points = append(list(newPt1) append(midPts list(newPt2)))
else
obj~>points = list(newPt1 newPt2)
)
)
)
)
;; ====================================================================
;; [Step 1] Start first stroke
;; ====================================================================
procedure( AutoBusStrokeStart()
printf("\n>>> [Step 1] Draw a stroke across the FIRST bus... \n")
enterLine( ?prompts list("Click start" "Click end") ?doneProc "AutoBusStrokeStep2" )
)
;; ====================================================================
;; [Step 2] Process first line
;; ====================================================================
procedure( AutoBusStrokeStep2(w done pts1)
prog((cv minX maxX minY maxY tol bBox shapes paths1)
if( !pts1 || length(pts1) < 2 then
printf(">>> [Error] Invalid stroke.\n") return(nil)
)
cv = geGetEditCellView()
minX = min(xCoord(car(pts1)) xCoord(cadr(pts1))) maxX = max(xCoord(car(pts1)) xCoord(cadr(pts1)))
minY = min(yCoord(car(pts1)) yCoord(cadr(pts1))) maxY = max(yCoord(car(pts1)) yCoord(cadr(pts1)))
tol = 0.01 bBox = list(minX-tol : minY-tol maxX+tol : maxY+tol)
shapes = dbGetOverlaps(cv bBox)
paths1 = setof(x shapes (x~>objType == "pathSeg" || x~>objType == "path") && x~>net~>name)
if( !paths1 || length(paths1) == 0 then
printf(">>> [Error] No nets found on the first stroke.\n") return(nil)
else
AutoBusStrokePaths1 = paths1
printf(">>> [Step 2] Found %d paths. Draw a stroke across the SECOND bus... \n" length(paths1))
enterLine( ?prompts list("Click start" "Click end") ?doneProc "AutoBusStrokeFinish" )
)
)
)
;; ====================================================================
;; [Step 3] Process second line and match all intersection points (1:N possible)
;; ====================================================================
procedure( AutoBusStrokeFinish(w done pts2)
prog((cv minX maxX minY maxY tol bBox shapes paths2 obj1 obj2
info1 info2 isH1 isH2 xInt yInt layer1 layer2 m1 m2 viaName viaDef
viaInst matchFound viaEntry rows cols viaParams)
if( !pts2 || length(pts2) < 2 then
printf(">>> [Error] Invalid stroke.\n") return(nil)
)
cv = geGetEditCellView()
minX = min(xCoord(car(pts2)) xCoord(cadr(pts2))) maxX = max(xCoord(car(pts2)) xCoord(cadr(pts2)))
minY = min(yCoord(car(pts2)) yCoord(cadr(pts2))) maxY = max(yCoord(car(pts2)) yCoord(cadr(pts2)))
tol = 0.01 bBox = list(minX-tol : minY-tol maxX+tol : maxY+tol)
shapes = dbGetOverlaps(cv bBox)
paths2 = setof(x shapes (x~>objType == "pathSeg" || x~>objType == "path") && x~>net~>name)
if( !paths2 || length(paths2) == 0 then
printf(">>> [Error] No nets found on the second stroke.\n") return(nil)
)
matchFound = nil
;; 🎯 Connect all lines with the same name together! (1 vertical line can cross 10 horizontal lines)
foreach( p1 AutoBusStrokePaths1
foreach( p2 paths2
if( p1~>net~>name == p2~>net~>name then
obj1 = p1
obj2 = p2
info1 = GetMetalOrientation(obj1)
info2 = GetMetalOrientation(obj2)
isH1 = (car(info1) == "H")
isH2 = (car(info2) == "H")
;; Operate only when vertical/horizontal are different (orthogonal)
if( isH1 != isH2 then
matchFound = t
if( isH1 then yInt = cadr(info1) xInt = cadr(info2)
else xInt = cadr(info1) yInt = cadr(info2) )
;; Apply improved infinite extension algorithm
ExtendMetalToPoint(obj1 xInt yInt isH1)
ExtendMetalToPoint(obj2 xInt yInt isH2)
layer1 = obj1~>layerName
layer2 = obj2~>layerName
if( listp(layer1) then layer1 = car(layer1) )
if( listp(layer2) then layer2 = car(layer2) )
viaName = nil
foreach( viaEntry AutoBusViaMap
m1 = car(car(viaEntry)) m2 = cadr(car(viaEntry))
if( (layer1 == m1 && layer2 == m2) || (layer1 == m2 && layer2 == m1) then viaName = cadr(viaEntry) )
)
if( viaName then
viaDef = techFindViaDefByName(techGetTechFile(cv) viaName)
if( viaDef then
cond(
(AutoBusViaCutMode == "H" rows = 1 cols = 2)
(AutoBusViaCutMode == "V" rows = 2 cols = 1)
(AutoBusViaCutMode == "HV" rows = 2 cols = 2)
(t rows = 1 cols = 1)
)
viaParams = list( list("cutRows" rows) list("cutColumns" cols) )
if( listp(AutoBusViaEnc1) && length(AutoBusViaEnc1) == 2 then
viaParams = cons( list("layer1Enc" AutoBusViaEnc1) viaParams ) )
if( listp(AutoBusViaEnc2) && length(AutoBusViaEnc2) == 2 then
viaParams = cons( list("layer2Enc" AutoBusViaEnc2) viaParams ) )
viaInst = dbCreateVia(cv viaDef list(xInt yInt) "R0" viaParams)
printf(">>> Connected '%s' at (%g, %g) with %dx%d Via\n" p1~>net~>name xInt yInt cols rows)
else
printf(">>> [Error] Via '%s' not found for net '%s'.\n" viaName p1~>net~>name)
)
else
printf(">>> [Error] No via map for %s and %s.\n" layer1 layer2)
)
)
)
)
)
if( !matchFound then printf(">>> [Warning] No matching nets found between strokes.\n") )
printf(">>> [Done] Stroke Routing Completed.\n")
return(t)
)
)
;; Shortcut Key Settings
hiSetBindKey("Layout" "Shift<Key>F6" "AutoBusStrokeStart()")
bus route
2026. 11. 8. 23:04

