It looks like you're new here. If you want to get involved, click one of these buttons!
To package any die, you need a bonding diagram based on the pads center location and pads names. This script extract them from the layout and write a file based on :
If you find the script useful or if you have remarks, please, tell me so that I know if it is OK for you or if I should try (at least to improve it.
Note that the script may be long, so I first request all the inputs, including the filename to save the extracted list, so that you can let the script run for a long time ... at lunch or evening time.
Laurent
#############################################################################
#
# DESCRIPTION: Save the pads center location and name into a file
#
# Run the script with
# klayout -rm pads_location.rbm ...
# or put the script as "pads_location.rbm" into the installation path (on Unix for version <=0.21:
# set $KLAYOUTPATH to the installation folder).
#
# convert the layer specification into a LayerInfo structure
# format: "l", "l/d", "n(l/d)" or "n".
def string_to_layerinfo(layer_spec)
ls = nil
if (layer_spec =~ /^(\d+)$/)
ls = RBA::LayerInfo.new($1.to_i, 0)
elsif (layer_spec =~ /^(\d+)\/(\d+)$/)
ls = RBA::LayerInfo.new($1.to_i, $2.to_i)
elsif (layer_spec =~ /^(.*)\s*\((\d+)\/(\d+)\)$/)
ls = RBA::LayerInfo.new($2.to_i, $3.to_i, $1)
elsif (layer_spec =~ /^(\d+)\/(\d+)(@)(\d+)$/)
ls = RBA::LayerInfo.new($1.to_i, $2.to_i, $4)
else
raise "The format for the layer is not good."
end
return ls
end
include RBA
require 'ostruct'
def find_texts(lo, cl, layer_idx, recursion_limit)
texts = {}
nbr_pads = 0
cl.each_shape(layer_idx) do |shape|
if shape.is_text?
text = shape.text
point = text.trans.trans(Point.new(0,0))
name = ([text.string]).join('.')
texts[nbr_pads.to_s] = OpenStruct.new(:point=>point, :pad_name=>text.string)
nbr_pads = nbr_pads + 1
end
end
if (recursion_limit>0)
cl.each_inst do |inst|
child_cell = lo.cell(inst.cell_index)
cell_name = child_cell.basic_name
puts "Descending into cell #{cell_name}"
child_texts = find_texts(lo, child_cell, layer_idx, recursion_limit-1)
puts "Finished cell #{cell_name}"
child_texts.each do |pad_nbr, data|
texts[nbr_pads.to_s] = OpenStruct.new(:point=>inst.trans.trans(child_texts[pad_nbr].point), :pad_name=>data.pad_name)
nbr_pads = nbr_pads + 1
end
end
end
return texts
end
def find_polygons(lo, cl, layer_idx, recursion_limit=0)
polygons = {}
nbr_pads = 0
cl.each_shape(layer_idx) do |shape|
if shape.is_polygon?
polygons[nbr_pads.to_s] = OpenStruct.new(:point1=>shape.bbox.p1, :point2=>shape.bbox.p2)
nbr_pads = nbr_pads + 1
end
end
# if recursion_limit > 0 # the recusion limit was used only for debug, for usage it is unlimited
cl.each_inst do |inst|
child_cell = lo.cell(inst.cell_index)
cell_name = child_cell.basic_name
puts "Descending into cell #{cell_name}"
child_polygons = find_polygons(lo, child_cell, layer_idx, recursion_limit-1)
puts "Finished cell #{cell_name}"
child_polygons.each do |name, point1, point2|
polygons[nbr_pads.to_s] = OpenStruct.new(:point1=>inst.trans.trans(child_polygons[name].point1), :point2=>inst.trans.trans(child_polygons[name].point2))
nbr_pads = nbr_pads + 1
end
end
# end # the recusion limit was used only for debug, for usage it is unlimited
return polygons
end
$pads_location = MenuAction.new( "Pads name/location", "" ) do
app = RBA::Application.instance
mw = app.main_window
lv = mw.current_view
if lv == nil
raise "No view selected"
end
cv = lv.active_cellview
if !cv.is_valid?
raise "No cell or no layout found"
end
cl = cv.cell
ci = cv.cell_index
lo = cv.layout
### Layer used to draw each pad opening
pad_layer = RBA::InputDialog.get_string(" Layer of the pad opening","Enter the GDS number and datatype (ex: 1 or 1/0)\n of the layer of the pad opening :", "*")
if !pad_layer.has_value?
return
end
# The layer specification string is either "l", "l/d", "n(l/d)" or "n".
lp = string_to_layerinfo(pad_layer.to_s)
# locate the layer if it already exists
pad_layer_index = nil
lp_found = 0
(0..(lo.layers-1)).each do |i|
if lo.is_valid_layer?(i) && lo.get_info(i).is_equivalent?(lp)
pad_layer_index = i
lp_found = 1
break
end
end
if lp_found == 0
raise "Layer not found !"
end
lp_found = 0
iter = lv.begin_layers
while !iter.at_end?
lp = iter.current
# if lp.cellview == ci && lp.layer_index == pad_layer_index
if lp.layer_index == pad_layer_index
pad_layerPropertiesNode = lp
lp_found = 1
end
iter.next
end
if lp_found == 0
raise "Layer not found!"
end
### Layer used to set the names of each pad
name_layer = RBA::InputDialog.get_string(" Layer of the pad names","Enter the GDS number and datatype (ex: 1 or 1/0)\n of the layer of the pad names :", "*")
if !name_layer.has_value?
return
end
# The layer specification string is either "l", "l/d", "n(l/d)" or "n".
lp = string_to_layerinfo(name_layer.to_s)
# locate the layer if it already exists
name_layer_index = nil
lp_found = 0
(0..(lo.layers-1)).each do |i|
if lo.is_valid_layer?(i) && lo.get_info(i).is_equivalent?(lp)
name_layer_index = i
lp_found = 1
break
end
end
if lp_found == 0
raise "Layer not found !"
end
lp_found = 0
iter = lv.begin_layers
while !iter.at_end?
lp = iter.current
if lp.layer_index == name_layer_index
lp_found = 1
name_layerPropertiesNode = lp
end
iter.next
end
if lp_found == 0
raise "Layer not found!"
end
name_recursion = RBA::InputDialog.get_string(" Hierarchy of the pad names","Enter the depth in the hierarchy to find the pad names \n 0 ( = TopLevel ) should be OK :", "0")
if !name_recursion.has_value?
return
end
# Ask for the output file name
filename = RBA::FileDialog.get_save_file_name("Pads location file ", ".", "All files (*)")
if !filename.has_value?
return
end
confirm = RBA::MessageBox.info("Take your time !","Pad Layer : #{pad_layerPropertiesNode.name}\nNames layer : #{name_layerPropertiesNode.name}\n\nHierarchy to find the name : #{name_recursion.to_s}\n\nThe routine will look for the pads and their names.\nIt may need several minutes, please, be patient.", RBA::MessageBox.b_ok + RBA::MessageBox.b_cancel)
if confirm == RBA::MessageBox.b_cancel
raise "Operation aborted"
end
texts = find_texts(lo, cl, name_layer_index, name_recursion.to_s.to_i)
polygons = find_polygons(lo, cl, pad_layer_index, 1)
pad_layer = lo.get_info(pad_layer_index)
name_layer = lo.get_info(name_layer_index)
# save the data in the file :
File.open(filename.value, "w") do |file|
file.puts("*****************************************************************")
file.puts("*\n* PADS LOCATION extracted with KLayout.de")
file.puts("* Date : #{Time.now.strftime("%d/%m/%Y %H:%M")}")
file.puts("*\n* GDS filename : #{cv.name}")
file.puts("*\n* TopCell name : #{lo.cell_name(ci)}")
file.puts("*\n* Pad Layer : #{pad_layerPropertiesNode.name}")
file.puts("* Names layer : #{name_layerPropertiesNode.name}")
file.puts("*\n* Hierarchy depth to find the names : #{name_recursion.to_s}")
file.puts("*\n*****************************************************************\n\n")
pad_count = 0
polygons.each do |textpl, datapl|
find_padname = 0
pad_count = pad_count + 1
texts.each do |textnm, datanm|
if ([datapl.point1.x, datapl.point2.x].min<datanm.point.x && [datapl.point1.x, datapl.point2.x].max>datanm.point.x && [datapl.point1.y,datapl.point2.y].min<datanm.point.y && [datapl.point1.y,datapl.point2.y].max>datanm.point.y)
find_padname = 1
file.puts(" #{pad_count} : #{datanm.pad_name} : #{((datapl.point1.x+datapl.point2.x)*lo.dbu/2).round} , #{((datapl.point1.y+datapl.point2.y)*lo.dbu/2).round}")
break
end
end
if (find_padname == 0)
file.puts(" #{pad_count} : no_name : #{((datapl.point1.x+datapl.point2.x)*lo.dbu/2).round} , #{((datapl.point1.y+datapl.point2.y)*lo.dbu/2).round}")
end
end
end
end
app = RBA::Application.instance
mw = app.main_window
menu = mw.menu
menu.insert_separator("tools_menu.end", "name")
menu.insert_item("tools_menu.end", "pads_location", $pads_location)
Comments
Hi Laurent,
thanks for sharing the script with us.
I took the liberty to nicely format your nice script :-)
The forum supports Markdown formatting - if the Markdown checkbox is checked, code can be formatted by indenting it with four spaces.
Thanks,
Matthias