본문으로 바로가기

bus route

category 기타(ETC)/단축키정리 2026. 11. 8. 23:04

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

'기타(ETC) > 단축키정리' 카테고리의 다른 글

r_t key  (0) 2026.11.08
poly ext  (0) 2026.11.08
sd metal  (0) 2026.11.08
quick align  (0) 2026.11.08