• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Allegro X Scripting - Skill
  3. Skill script for checking BGA pitch

Stats

  • Replies 6
  • Subscribers 21
  • Views 540
  • Members are here 0
More Content

Skill script for checking BGA pitch

SG20260713408
SG20260713408 22 days ago

This is a skill for inspecting the pitch of BGAs on a board.

If you know of a more concise and elegant code than this, please let me know. (SKILL++, OOP...)

Thank you.


/*
============================================================================================
* Tool Name : min_pitch_bga (Enhanced Version)
* Description : Automatically checks the minimum pitch of BGA components to prevent fine-pitch routing defects.
* Features :
* 1. Support multiple symbol filtering cases (Property, SymType)
* 2. Performance Optimization: Auto-bounding box shrinkage for
* high pin-count components to prevent computation lag.
* 3. Auto-sorting and reporting with an interactive .log viewer.
* Command : min_pitch_bga [Optional Pitch Value, default "0.5"]
* To change the pitch value, simply apply the desired pitch value after the command. (min_pitch_bga 1.0 or min_pitch_bga 0.5 .......)
============================================================================================
*/

axlCmdRegister("min_pitch_bga" 'min_pitch_bga ?cmdType "interactive")

procedure(min_pitch_bga(@optional (argu_bga_pitch "0.5") "S")

let((min_bga_pitch symbols loop sym_pins_locs obj1 objPairs pitch_datas min_pitch_data

pair_count var_x var_y delta_x delta_y refdes sym_xy layer sym_rot all_x_loc all_y_loc min_x min_y max_x max_y sym_pins total_results)


min_bga_pitch = atof(argu_bga_pitch)

when(min_bga_pitch

;;========================================================================================

;;Comment : Please select and use the case that fits the situation.

;;Comment(Case1) : Use when a symbol(footprint) is given a property name("DFA_DEV_CLASS") and a property value("bga").

;; Property values are entered by the user, so they are case-sensitive.


symbols = foreach(mapcan item axlSelectByProperty("symdef" "DFA_DEV_CLASS" "bga") ~> instances item) ;; Use if applicable to Case 1

;;symbols = foreach(mapcan item axlSelectByProperty("symdef" "DFA_DEV_CLASS" "bg*" t) ~> instances item) ;; Use if applicable to Case 1

;;symbols = foreach(mapcan item axlSelectByProperty("symdef" "REF_PATTERN_NAME" "BGA*" t) ~> instances item) ;; Use if applicable to Case 1

;;========================================================================================

;; Comment(Case2) : The name given when generating the symbol(footprint).

;; In other words, when the symbol name(footprint name) starts with "BGAXXXXXXXX_XXX_XXX"


;;symbols = axlSelectByName("SYMTYPE" "BGA*" t) ;; Use if applicable to Case 2, Delete ";;" and use

;;========================================================================================

axlClearSelSet()

if(symbols then

foreach(symbol symbols

loop = t

sym_rot = nil

sym_pins = nil

sym_pins_locs = nil

all_x_loc = nil

all_y_loc = nil

objPairs = nil

pitch_datas = nil

min_pitch_data = nil

pair_count = nil

var_x = nil

var_y = nil

delta_x = nil

delta_y = nil

sym_xy = symbol -> xy

refdes = symbol -> refdes

unless(refdes refdes = "No RefDes")

layer = if(symbol -> isMirrored then "BOTTOM" else "TOP")

sym_pins = setof(e symbol -> pins e -> number != "" || e -> isMech != t)


when(sym_pins && length(sym_pins) > 1

sym_rot = symbol -> rotation

if(axlGeoEqual(sym_rot 0.0) || axlGeoEqual(sym_rot 90.0) || axlGeoEqual(sym_rot 180.0) || axlGeoEqual(sym_rot 27.0) then

sym_pins_locs = sym_pins ~> xy

else

foreach(sym_pin sym_pins

sym_pin_xy = sym_pin -> xy

rot_pin = axlGeoRotatePt(minus(sym_rot) car(sym_pin_xy):cadr(sym_pin_xy) car(sym_xy):cadr(sym_xy))

sym_pins_locs = cons(rot_pin sym_pins_locs)

)

)


all_x_loc = foreach(mapcar item sym_pins_locs car(item))

all_y_loc = foreach(mapcar item sym_pins_locs cadr(item))


var_x = apply('min all_x_loc)

sym_x = apply('max all_x_loc)

var_y = apply('min all_y_loc)

sym_y = apply('max all_y_loc)

delta_x = abs(sym_x - var_x)

delta_y = abs(sym_y - var_y)

sym_xy = list((sym_x - (delta_x / 2)) (sym_y - (delta_y / 2)))


delta_x = delta_x / 30

delta_y = delta_y / 30

sym_pins_locs = setof(e sym_pins_locs axlIsPointInsideBox(e list(sym_xy list(var_x var_y))))


when(length(sym_pins_locs) > 1

pair_count = (length(sym_pins_locs) * (length(sym_pins_locs) - 1)) / 2

when(pair_count > 1500

while(loop

var_x = var_x + delta_x

var_y = var_y + delta_y

delta_x = (car(sym_xy) - var_x) / 30

delta_y = (cadr(sym_xy) - var_y) / 30

sym_pins_locs = setof(e sym_pins_locs axlIsPointInsideBox(e list(sym_xy list(var_x var_y))))

pair_count = (length(sym_pins_locs) * (length(sym_pins_locs) - 1)) / 2

when(pair_count < 1500 loop = nil)

)

)

when(sym_pins_locs

while(sym_pins_locs

obj1 = car(sym_pins_locs)

sym_pins_locs = cdr(sym_pins_locs)

foreach(obj2 sym_pins_locs objPairs = cons(list(obj1, obj2), objPairs))

)

)

when(objPairs

foreach(objPair objPairs

pitch_datas = cons(axlDistance(car(objPair) cadr(objPair)) pitch_datas)

)

min_pitch_data = car(sort(pitch_datas 'lessp))

)

when(min_pitch_data

when(min_pitch_data < min_bga_pitch

axlHighlightObject(symbol)

total_results = cons(list(layer refdes min_pitch_data symbol -> xy symbol -> name) total_results)

)

)

)

)

)

total_results = reverse(sortcar(total_results 'axlStrcmpAlpNum))

MPB_Create_log_File(total_results min_bga_pitch)

else

printf("###### BGA cannot be found. !! ######\n")

)

)

)

)


procedure(MPB_Create_log_File(total_results min_bga_pitch)

let((err_count Date Min_Pitch_BGA_Log_File)

err_count = 0

Date = (getCurrentTime)
Date = parseString(Date " ")
Date = strcat(nth(3 Date) " " nth(0 Date) " " nth(1 Date) " " nth(2 Date))

Min_Pitch_BGA_Log_File = outfile("./Min_Pitch_BGA.log" "w")

fprintf(Min_Pitch_BGA_Log_File "\nDATE : %s\n" Date)

fprintf(Min_Pitch_BGA_Log_File "\nTarGet Pitch : %f\n" min_bga_pitch)

fprintf(Min_Pitch_BGA_Log_File "\nSyntax : nIndex, Layer, RefDes, Pitch, Symbol_XY, Symbol_Name\n")

fprintf(Min_Pitch_BGA_Log_File "\n=========================================================================\n")

if(length(total_results) == 0 then

fprintf(Min_Pitch_BGA_Log_File "\n=========================================================================\n")

fprintf(Min_Pitch_BGA_Log_File "\n Total Error Count = 0\n")

else

fprintf(Min_Pitch_BGA_Log_File "\nIndex, Layer, RefDes, Pitch, Symbol_XY, Symbol_Name\n")

foreach(total_result total_results

err_count = ++err_count

fprintf(Min_Pitch_BGA_Log_File "\n%d, %s, %s, %f, %L, %s\n" err_count nth(0 total_result) nth(1 total_result) nth(2 total_result) nth(3 total_result) nth(4 total_result))

)

fprintf(Min_Pitch_BGA_Log_File "\n=========================================================================\n")

fprintf(Min_Pitch_BGA_Log_File "\n Total Error Count = %d.\n" err_count)

)

close(Min_Pitch_BGA_Log_File)

axlShell("viewlog ./Min_Pitch_BGA.log")

)

)

  • Sign in to reply
  • Cancel
Parents
  • TL202608111936
    TL202608111936 19 days ago

    Hi,

    For BGA packages with a large number of pins, identifying all pin pairs and measuring the distance between each pair can significantly slow down the code. I suggest dividing the BGA pins into a grid based on a predefined pitch. This way, I think we can reduce the number of distance measurements by approximately 50%.

    Luan

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • SG20260713408
    SG20260713408 19 days ago in reply to TL202608111936

    Thank you for sharing your thoughts.

    Since the pitch and pin arrangement of BGAs are not all identical, it is difficult to grid them based on the input pitch.

    Thank you.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • TL202608111936
    TL202608111936 18 days ago in reply to SG20260713408

    Hi,

    Although I haven't written the code to test my idea yet, I still stand by the idea.

    Based on the initial user pitch and the pin list, I will sort the pin list by rows and columns, with each row/column separated by one user pitch and within a predefined tolerance. This will give me a matrix of the pin coordinates by row and column.

    Then, within each row and column, I will sort the pins by X or Y coordinate. Finally, I will measure the distance between two consecutive coordinates in each row or column to find gaps that are smaller than the user pitch.

    This approach should significantly reduce the number of measurements compared with the current method.

    Finally, I will filter out the pins that do not satisfy the row and column conditions described above. These pins can be considered an "exception pin list" and will be checked separately using the old method, which scans the area to find the pitch.

    If I have time, I will try coding this approach to see whether the idea is actually feasible.

    Luan

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • TL202608111936
    TL202608111936 18 days ago in reply to SG20260713408

    Hi,

    Although I haven't written the code to test my idea yet, I still stand by the idea.

    Based on the initial user pitch and the pin list, I will sort the pin list by rows and columns, with each row/column separated by one user pitch and within a predefined tolerance. This will give me a matrix of the pin coordinates by row and column.

    Then, within each row and column, I will sort the pins by X or Y coordinate. Finally, I will measure the distance between two consecutive coordinates in each row or column to find gaps that are smaller than the user pitch.

    This approach should significantly reduce the number of measurements compared with the current method.

    Finally, I will filter out the pins that do not satisfy the row and column conditions described above. These pins can be considered an "exception pin list" and will be checked separately using the old method, which scans the area to find the pitch.

    If I have time, I will try coding this approach to see whether the idea is actually feasible.

    Luan

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Children
  • SG20260713408
    SG20260713408 18 days ago in reply to TL202608111936

    I would love to see your code whenever you have time to test it.

    I'm really looking forward to your approach.

    thank you.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Cadence Guidelines

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2026 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information