;; ============================================================
;; [Environment Settings]
;; ============================================================
;; 1. Target Cell Name Settings (The skill works only for cells specified here)
;; (To apply without restriction to all cells, change to nil: defvar( MyTargetCells nil ) )
defvar( MyTargetCells list("pmos2v" "nmos2v") )
;; 2. Layer Settings
;; 2-1. New metal layer to draw and reference point (existing S/D metal) layer
defvar( MyTargetMetalLayer list("METAL1" "drawing") )
defvar( MySndMetalLayer list("METAL1" "drawing") )
;; 2-2. DIFF (Active) layer that determines the top/bottom length
;; (May be called "OD", "RX", "active", etc. depending on the PDK, so modify if it doesn't match)
defvar( MyDiffLayer list("DIFF" "drawing") )
;; 3. PathSeg Width Setting
defvar( MyPathSegWidth 0.34 )
;; ============================================================
procedure( MyAddSDMetalPathSeg()
let((cv selSet instMaster diffBox diffMinY diffMaxY sdXList uniqueSdXList lastX
p1Master p2Master p1Top p2Top newSeg successCount orient centerX centerY)
cv = geGetEditCellView()
selSet = geGetSelSet()
successCount = 0
if( !selSet then
printf(">>> [Error] No instances selected.\n")
return(nil)
)
foreach( inst selSet
if( inst~>objType == "inst" then
;; ★ Target Cell Name Filtering ★
if( MyTargetCells && !member(inst~>cellName MyTargetCells) then
printf(">>> [Skip] Instance '%s' (Cell: %s) is not in the target list.\n" inst~>name inst~>cellName)
else
orient = inst~>orient
;; Exclude 90-degree and 270-degree rotations (Only allow R0, MY, MX, R180)
if( member(orient list("R0" "MY" "MX" "R180")) then
instMaster = inst~>master
diffBox = nil
sdXList = nil
;; --------------------------------------------------------
;; Step 1. Get the full Bounding Box of the DIFF layer from the original cell (Master)
;; --------------------------------------------------------
foreach( shape instMaster~>shapes
if( shape~>lpp == MyDiffLayer && (shape~>objType == "rect" || shape~>objType == "polygon") then
if( !diffBox then
diffBox = shape~>bBox
else
;; If there are multiple DIFF pieces, merge them into the largest bounding area
diffBox = list(
list( min(caar(diffBox) caar(shape~>bBox)) min(cadar(diffBox) cadar(shape~>bBox)) )
list( max(caadr(diffBox) caadr(shape~>bBox)) max(cadadr(diffBox) cadadr(shape~>bBox)) )
)
)
)
)
if( !diffBox then
printf(">>> [Warning] DIFF layer not found in '%s'. Check 'MyDiffLayer' setting. Skipping.\n" inst~>name)
else
diffMinY = cadar(diffBox)
diffMaxY = cadadr(diffBox)
;; --------------------------------------------------------
;; Step 2. Find existing Source/Drain metals and extract X coordinates (horizontal position)
;; --------------------------------------------------------
foreach( shape instMaster~>shapes
if( shape~>lpp == MySndMetalLayer && (shape~>objType == "rect" || shape~>objType == "polygon") then
centerY = (cadar(shape~>bBox) + cadadr(shape~>bBox)) / 2.0
;; Trick logic: To avoid irrelevant metals such as gate metals,
;; consider only metals whose Y center point is inside the DIFF (Active) region as S/D metals.
if( centerY > diffMinY && centerY < diffMaxY then
centerX = (caar(shape~>bBox) + caadr(shape~>bBox)) / 2.0
sdXList = cons(centerX sdXList)
)
)
)
if( !sdXList then
printf(">>> [Warning] S/D Metal not found inside DIFF region of '%s'. Skipping.\n" inst~>name)
else
;; --------------------------------------------------------
;; Step 3. Sort X coordinates and remove duplicates (for multi-finger support)
;; --------------------------------------------------------
;; (If multiple contacts are placed, the same X coordinate is collected multiple times, so combine them into one)
sdXList = sort( sdXList 'lessp )
uniqueSdXList = nil
lastX = -10000.0
foreach( x sdXList
if( abs(x - lastX) > 0.001 then
uniqueSdXList = cons(x uniqueSdXList)
lastX = x
)
)
;; --------------------------------------------------------
;; Step 4. Draw a thick PathSeg for each extracted finger
;; --------------------------------------------------------
foreach( x uniqueSdXList
;; Master-based start and end points (from bottom to top of DIFF)
p1Master = list(x diffMinY)
p2Master = list(x diffMaxY)
;; Transform coordinates according to the current instance's location/rotation state
p1Top = dbTransformPoint( p1Master inst~>transform )
p2Top = dbTransformPoint( p2Master inst~>transform )
;; Create PathSeg (apply truncate property to cut off cleanly without sticking out of DIFF)
newSeg = dbCreatePathSeg( cv MyTargetMetalLayer p1Top p2Top MyPathSegWidth "truncate" "truncate" )
;; Attach to the instance so they move together later
if( newSeg then leAttachFig( newSeg inst ) )
)
successCount = successCount + 1
)
)
)
)
else
printf(">>> [Skip] Selected object is not an instance. Skipping.\n")
)
)
printf(">>> [Done] Added thick S/D METAL1 to %d instance(s).\n" successCount)
t
)
)
;; F7 Bindkey Assignment
hiSetBindKey("Layout" "<Key>F7" "MyAddSDMetalPathSeg()")
printf(">>> [Loaded] Source/Drain Metal PathSeg script loaded (F7)\n")
sd metal
2026. 11. 8. 22:52

