;; ====================================================================
;; [Option 1] Overwrite existing pins (t: delete and create only 1 (move) / nil: infinite addition (multi-finger))
;; ====================================================================
defvar( AutoPinDeleteExisting t )
;; ====================================================================
;; [Option 2] Strict creation mode (t: allow pin generation only for Nets that already have pins in the layout / nil: create unconditionally)
;; ====================================================================
defvar( AutoPinOnlyForExisting t )
;; ====================================================================
;; [Option 3] Layer-Purpose Pair (LPP) Mapping Settings
;; ====================================================================
defvar( AutoPinLayerMap
list(
list("METAL1" list("METAL1" "drawing") list("METAL1" "pin"))
list("METAL2" list("METAL2" "drawing") list("METAL2" "pin"))
list("METAL3" list("METAL3" "drawing") list("METAL3" "pin"))
list("METAL4" list("METAL4" "drawing") list("METAL4" "pin"))
list("METAL5" list("METAL5" "drawing") list("METAL5" "pin"))
)
)
;; ====================================================================
;; [Main Execution Function]
;; ====================================================================
procedure( CreateAutoPinOnNet()
let(( cv selSet mousePt processedNets )
cv = geGetEditCellView()
selSet = geGetSelSet()
if( !selSet then
printf(">>> [Error] Please select at least one path or pathSeg.\n")
return(nil)
)
mousePt = hiGetCommandPoint()
unless( mousePt mousePt = centerBox(car(selSet)~>bBox) )
;; List of nets for which pins were already created in this hotkey execution
processedNets = nil
foreach( sel selSet
if( sel~>objType == "pathSeg" || sel~>objType == "path" then
let(( netName mLayer match pts pt1 pt2 dist1 dist2 targetPt adjPt
width ext dx dy len ux uy cx cy pinBox rot just term net
pinFig pin label labelHeight rectLPP labelLPP idx uniquePinName existTerm )
netName = sel~>net~>name
if( netName && netName != "" then
;; ====================================================================
;; [Core Bug Fix] Skip duplicate generation of the same Net in move mode (t)
;; ====================================================================
if( AutoPinDeleteExisting && member(netName processedNets) then
printf(">>> [Skip] Pin for '%s' already moved. Ignoring duplicate path.\n" netName)
else
existTerm = dbFindTermByName(cv netName)
if( AutoPinOnlyForExisting && !existTerm then
printf(">>> [Skip] Net '%s' ignored (No existing pin in cellview).\n" netName)
else
;; Proceed with LPP mapping and coordinate calculation if passed
mLayer = sel~>layerName
if( listp(mLayer) then mLayer = car(mLayer) )
match = assoc(mLayer AutoPinLayerMap)
if( match then
rectLPP = cadr(match)
labelLPP = caddr(match)
else
rectLPP = list(mLayer "pin")
labelLPP = list(mLayer "pin")
)
width = sel~>width
if( sel~>objType == "pathSeg" then pts = list(sel~>beginPt sel~>endPt) else pts = sel~>points )
pt1 = car(pts)
pt2 = car(last(pts))
dist1 = (xCoord(pt1)-xCoord(mousePt))**2 + (yCoord(pt1)-yCoord(mousePt))**2
dist2 = (xCoord(pt2)-xCoord(mousePt))**2 + (yCoord(pt2)-yCoord(mousePt))**2
if( dist1 < dist2 then
targetPt = pt1
adjPt = cadr(pts)
if( sel~>objType == "pathSeg" then ext = sel~>beginExt
else if( sel~>pathStyle == "extendExt" || sel~>pathStyle == "roundExt" then ext = width/2.0 else ext = 0.0 ) )
else
targetPt = pt2
adjPt = nth(length(pts)-2 pts)
if( sel~>objType == "pathSeg" then ext = sel~>endExt
else if( sel~>pathStyle == "extendExt" || sel~>pathStyle == "roundExt" then ext = width/2.0 else ext = 0.0 ) )
)
dx = xCoord(targetPt) - xCoord(adjPt)
dy = yCoord(targetPt) - yCoord(adjPt)
len = sqrt(dx**2 + dy**2)
if( len == 0.0 then len = 1.0 )
ux = dx / len
uy = dy / len
cx = xCoord(targetPt) + ux * (ext - width/2.0)
cy = yCoord(targetPt) + uy * (ext - width/2.0)
pinBox = list( cx - width/2.0 : cy - width/2.0 cx + width/2.0 : cy + width/2.0 )
if( abs(dx) > abs(dy) then
rot = "R0"
if( dx > 0 then just = "centerRight" else just = "centerLeft" )
else
rot = "R90"
if( dy > 0 then just = "centerRight" else just = "centerLeft" )
)
;; [Deletion Logic] Completely destroy old terminals and pins in move mode
if( AutoPinDeleteExisting && existTerm then
foreach( p existTerm~>pins
when( p~>fig
foreach( child p~>fig~>children dbDeleteObject(child) )
dbDeleteObject(p~>fig)
)
dbDeleteObject(p)
)
dbDeleteObject(existTerm)
printf(">>> Deleted old pins for '%s' to move.\n" netName)
)
;; Create pin metal shape and logical terminal
net = dbMakeNet(cv netName)
term = dbFindTermByName(cv netName)
unless( term term = dbCreateTerm(net netName "inputOutput") )
pinFig = dbCreateRect(cv rectLPP pinBox)
;; Name collision prevention logic (just in case)
pin = nil
idx = 0
while( !pin && idx < 1000
if( idx == 0 then uniquePinName = netName
else uniquePinName = sprintf(nil "%s_%d" netName idx) )
pin = car( errset( dbCreatePin(net pinFig uniquePinName term) nil ) )
idx = idx + 1
)
if( pin then
labelHeight = width * 0.8
label = dbCreateLabel(cv labelLPP targetPt netName just rot "roman" labelHeight)
label~>parent = pinFig
printf(">>> Pin '%s' successfully created.\n" uniquePinName)
;; Record in the ledger if the pin was created successfully (skip if same Net in next iteration)
processedNets = cons(netName processedNets)
else
printf(">>> [Error] Failed to create logical pin for '%s'.\n" netName)
)
)
) ;; End of duplicate skip if statement
else
printf(">>> [Skip] Selected path has NO connectivity.\n")
)
) ;; end let
else
printf(">>> [Skip] Selected object is not a path or pathSeg.\n")
)
) ;; end foreach
printf(">>> [Done] Multi-Pin operation finished.\n")
)
)
hiSetBindKey("Layout" "<Key>F5" "CreateAutoPinOnNet()")
pin move
2026. 11. 8. 23:01

