본문으로 바로가기

pin move

category 카테고리 없음 2026. 11. 8. 23:01


;; ====================================================================
;; [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()")