It looks like you're new here. If you want to get involved, click one of these buttons!
Based on your layout size, this script will give you the dice count : gross die, yield and draw a wafermap.
Save the script under the install directory of klayout as : gross_die.rbm
You will get a "gross die / yield" command in the tools menu.
It even draws correctly the flat for each silicon orientation for small wafers (< 8").
Enjoy it, regards,
Laurent
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# DESCRIPTION: dice count : gross die , yield estimation and wafermap
#
# Run the script with
# klayout -rm gross_die.rbm ...
# or put the script as "gross_die.rbm" into the installation path (on Unix for version <=0.21:
# set $KLAYOUTPATH to the installation folder).
#
include RBA
$gross_die = MenuAction.new( "Gross die / Yield", "" ) do
dialog = QDialog.new(Application.instance.main_window)
dialog.windowTitle = "WAFER & DIE characteritics"
layout = QVBoxLayout::new(dialog)
dialog.setLayout(layout)
### Die name
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the die or project name :"
layout_name = QLineEdit.new(dialog)
layout.addWidget(layout_name)
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "\n"
### Layout width : X
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the layout width X (mm) :"
layout_X = QLineEdit.new(dialog)
layout.addWidget(layout_X)
### Layout heigth : Y
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the layout heigth Y (mm) :"
layout_Y = QLineEdit.new(dialog)
layout.addWidget(layout_Y)
### Scribe width : X
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the scribe width X (mm) :"
scribe_X = QLineEdit.new(dialog)
layout.addWidget(scribe_X)
scribe_X.text = "0.1"
### Scribe heigth : Y
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the scribe heigth Y (mm) :"
scribe_Y = QLineEdit.new(dialog)
layout.addWidget(scribe_Y)
scribe_Y.text = "0.1"
### Wafer diameter
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the wafer diameter (mm) / (inches)"
waf_diam = QComboBox.new(dialog)
waf_diam.addItem("50 mm / 2 \" ")
waf_diam.addItem("75 mm / 3 \" ")
waf_diam.addItem("100 mm / 4 \" ")
waf_diam.addItem("125 mm / 5 \" ")
waf_diam.addItem("150 mm / 6 \" ")
waf_diam.addItem("200 mm / 8 \" ")
waf_diam.addItem("300 mm / 12 \" ")
waf_diam.addItem("450 mm / 18 \" ")
# waf_diam.setSizeAdjustPolicy(QComboBox::AdjustToContents)
waf_diam.setCurrentIndex(5) # default wafer size = 200mm
layout.addWidget(waf_diam)
### Wafer type : P-100
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the wafer type"
waf_typ = QComboBox.new(dialog)
waf_typ.addItem("N-100")
waf_typ.addItem("N-111")
waf_typ.addItem("P-100")
waf_typ.addItem("P-111")
layout.addWidget(waf_typ)
# waf_typ.setSizeAdjustPolicy(QComboBox::AdjustToContents)
### Edging loss of the wafer
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the edging loss (mm) :"
PEE = QLineEdit.new(dialog)
layout.addWidget(PEE)
PEE.text = "3"
### defects / cm^2
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the defects density D0 (/cm^2) :"
defectsE = QLineEdit.new(dialog)
layout.addWidget(defectsE)
defectsE.text = "0.12"
### measure of manufacturing process complexity
label = QLabel.new(dialog)
layout.addWidget(label)
label.text = "Enter the manufacturing process complexity :"
alphaE = QLineEdit.new(dialog)
layout.addWidget(alphaE)
alphaE.text = "2.5"
# OK button
buttonOK = QPushButton.new(dialog)
layout.addWidget(buttonOK)
buttonOK.text = " OK "
buttonOK.clicked do
if (layout_X.text.to_f == 0.0)
confirm = RBA::MessageBox.info("WRONG INPUTS !!!","Set the layout width X", RBA::MessageBox.b_ok + RBA::MessageBox.b_cancel)
if confirm == RBA::MessageBox.b_cancel
raise "Operation aborted"
end
elsif (layout_Y.text.to_f == 0.0)
confirm = RBA::MessageBox.info("WRONG INPUTS !!!","Set the layout heigth Y", RBA::MessageBox.b_ok + RBA::MessageBox.b_cancel)
if confirm == RBA::MessageBox.b_cancel
raise "Operation aborted"
end
else
dialog.accept()
end
end
dialog.exec # input data
### Input data from string to float
xlayout = layout_X.text.to_f
ylayout = layout_Y.text.to_f
xscribe = scribe_X.text.to_f
yscribe = scribe_Y.text.to_f
PE = PEE.text.to_f
defects = defectsE.text.to_f
alpha = alphaE.text.to_f
if (waf_diam.currentText() == "50 mm / 2 \" ")
diameter = 50.8
OFL = 15.88 ## Wafer primary slice length
CFL = 8.0 ## Length of the secondary side of the wafer
RE = 2.0 ## Resist damage on the side of the wafer
thick = 279
end
if (waf_diam.currentText() == "75 mm / 3 \" ")
diameter = 76.2
OFL = 22.22
CFL = 11.18
RE = 2.0
thick = 381
end
if (waf_diam.currentText() == "100 mm / 4 \" ")
diameter = 100.0
OFL = 32.5
CFL = 18.0
RE = 2.0
thick = "525 or 625"
end
if (waf_diam.currentText() == "125 mm / 5 \" ")
diameter = 125.0
OFL = 42.5
CFL = 27.5
RE = 2.0
thick = 625
end
if (waf_diam.currentText() == "150 mm / 6 \" ")
diameter = 150.0
OFL = 57.5
CFL = 37.5
RE = 2.0
thick = 675
end
if (waf_diam.currentText() == "200 mm / 8 \" ")
diameter = 200.0
OFL = 0.0
CFL = 0.0
RE = 0.1
thick = 725
end
if (waf_diam.currentText() == "300 mm / 12 \" ")
diameter = 300.0
OFL = 0.0
CFL = 0.0
RE = 0.1
thick = 775
end
if (waf_diam.currentText() == "450 mm / 18 \" ")
diameter = 450.0
OFL = 0.0
CFL = 0.0
RE = 0.1
thick = 925
end
### Define common constants
PI = 3.14159
sqrt2 = Math::sqrt(2)
lne = 2.7183
inchtomm = 25.4
radius = diameter / 2.0
height = xlayout + xscribe
width = ylayout + yscribe
### Formula One round in the die landed calculated the number of ways to calculate the diagonal
### Formula1 refer to Anderson School at UCLA
DieCount1 = 0
h = height
w = width
if (h > w)
h = width
w = height
end
if (w < diameter)
rowmax = (Math::sqrt(diameter ** 2 - w ** 2) / h).to_i
columax = (Math::sqrt(diameter ** 2 - h ** 2) / w).to_i
for row in (1..rowmax) do
columns = (Math::sqrt((diameter ** 2) - (row * h) ** 2) / w).to_i
DieCount1 = DieCount1 + columns
end
end
### Formula 2 is calculated in terms of area and remove the edge of the die number
### Formula 2 refers to www.cse.psu.edu/~mji
diearea = height * width
PreCount = PI * radius ** 2 / diearea
Margin = PI * diameter / Math::sqrt(2 * diearea)
DieCount2 = (PreCount-Margin).round
### Four yield models formula
NegBinYield = 100.0 *(1.0 +(defects * diearea * 1e-2) / alpha) **(alpha * -1.0)
PoissonYield = 100.0 * 1.0 /(lne **(diearea * 1e-2 * defects))
MurphyYield = 100.0 *((1.0 -(lne **(-1.0 * diearea * 1e-2 * defects))) /(diearea * 1e-2 * defects)) ** 2
SeedYield = 100.0 * 1.0 /(lne ** Math::sqrt(diearea * 1e-2 * defects))
if (waf_typ.currentText() == "N-100")
OFR = radius - Math::sqrt(radius ** 2.0 -(OFL/2.0) ** 2)
CFR = radius - Math::sqrt(radius ** 2.0 -(CFL/2.0) ** 2)
cutOFR = (OFR / h + 1.0).round
cutCFR45 = 0.0
cutCFR90 = 0.0
cutCFR180 = (CFR / w +1).round
end
if (waf_typ.currentText() == "N-111")
OFR = radius - Math::sqrt(radius ** 2.0 -(OFL/2.0) ** 2)
CFR = radius - Math::sqrt(radius ** 2.0 -(CFL/2.0) ** 2)
cutOFR = (OFR / h + 1.0).round
cutCFR45 = 1.0
cutCFR90 = 0.0
cutCFR180 = 0.0
end
if (waf_typ.currentText() == "P-100")
OFR = radius - Math::sqrt(radius ** 2.0 -(OFL/2.0) ** 2)
CFR = radius - Math::sqrt(radius ** 2.0 -(CFL/2.0) ** 2)
cutOFR = (OFR / h + 1.0).round
cutCFR45 = 0.0
cutCFR90 = (CFR / w + 1.0).round
cutCFR180 = 0.0
end
if (waf_typ.currentText() == "P-111")
OFR = radius - Math::sqrt(radius ** 2.0 -(OFL/2.0) ** 2)
cutOFR = (OFR / h +1.0).round
cutCFR45 = 0.0
cutCFR90 = 0.0
cutCFR180 = 0.0
end
### Draw the wafer
app = RBA::Application.instance
mw = app.main_window
# create a new layout
mw.create_layout( 0 )
layout_view = mw.current_view
### create a new layer in that layout
layout = layout_view.cellview( 0 ).layout
layout_view.set_config("background-color", "0xFFFFFF")
linfo = RBA::LayerInfo.new
### create a layer view for the wafer
layer_id = layout.insert_layer( linfo )
ln = RBA::LayerPropertiesNode::new
ln.dither_pattern = 1
# ln.fill_color = 0xFFFFFF
ln.frame_color = 0x000000
ln.width = 3
ln.source_layer_index = layer_id
layout_view.insert_layer( layout_view.end_layers, ln )
### create a layer view for the safe area
layer_id2 = layout.insert_layer( linfo )
ln2 = RBA::LayerPropertiesNode::new
ln2.dither_pattern = 1
# ln2.fill_color = 0xFFFFFF
ln2.frame_color = 0x00FF00
ln2.width = 1
ln2.source_layer_index = layer_id2
layout_view.insert_layer( layout_view.end_layers, ln2 )
### create a layer view for the safe area
layer_id3 = layout.insert_layer( linfo )
ln3 = RBA::LayerPropertiesNode::new
ln3.dither_pattern = 1
# ln3.fill_color = 0xFFFFFF
ln3.frame_color = 0xFF0000
ln3.width = 2
ln3.source_layer_index = layer_id3
layout_view.insert_layer( layout_view.end_layers, ln3 )
### create a top cell
wafer = layout.add_cell( "wafer" )
dbu = 1.0/layout.dbu
RPE = radius - (RE + PE)
### convert to micron
radius *= 1000.0 * dbu
RPE *= 1000.0 * dbu
xlayout *= 1000.0 * dbu
ylayout *= 1000.0 * dbu
xscribe *= 1000.0 * dbu
yscribe *= 1000.0 * dbu
### draw wafers
pts = []
n = 128
da = Math::PI * 2 / n
if ((waf_diam.currentText() == "200 mm / 8 \" ") || (waf_diam.currentText() == "300 mm / 12 \" ") || (waf_diam.currentText() == "450 mm / 18 \" "))
n.times do |i|
if (i==0)
pts.push(Point.from_dpoint(DPoint.new(radius * Math::sin(i * da) + 1000*dbu, - radius * Math::cos(i * da))))
else
pts.push(Point.from_dpoint(DPoint.new(radius * Math::sin(i * da), - radius * Math::cos(i * da))))
end
end
pts.push(Point.from_dpoint(DPoint.new(- 1000*dbu, - radius))) # draw the wafer notch
pts.push(Point.from_dpoint(DPoint.new(- 1000*dbu, - radius + 1000*dbu)))
pts.push(Point.from_dpoint(DPoint.new(0, - radius + 1500*dbu)))
pts.push(Point.from_dpoint(DPoint.new(+ 1000*dbu, - radius + 1000*dbu)))
else
flat = 0
n.times do |i|
if ((- Math::cos(i * da) > 0) || (radius * Math::sin(i * da) > OFL*500*dbu) || (radius * Math::sin(i * da) < -OFL*500*dbu))
if ((waf_typ.currentText() == "N-111") && ((i * da) < (Math::asin((OFL*500*dbu) / radius) + 2 * Math::asin((CFL*500*dbu) / radius))))
if (flat==0)
theta = Math::asin((OFL*500*dbu) / radius) + 2 * Math::asin((CFL*500*dbu) / radius)
pts.push(Point.from_dpoint(DPoint.new( radius * Math::sin(theta), - radius * Math::cos(theta))))
end
flat = 1
elsif ((waf_typ.currentText() == "P-100") && (Math::sin(i * da) > 0) && (radius * Math::cos(i * da) < CFL*500*dbu) && (radius * Math::cos(i * da) > -CFL*500*dbu))
flat = 1
elsif ((waf_typ.currentText() == "P-111") && (- Math::cos(i * da) > 0) && (radius * Math::sin(i * da) < CFL*500*dbu) && (radius * Math::sin(i * da) > -CFL*500*dbu))
flat = 1
else
pts.push(Point.from_dpoint(DPoint.new(radius * Math::sin(i * da), - radius * Math::cos(i * da))))
end
end
end
pts.push(Point.from_dpoint(DPoint.new(-OFL*500*dbu, - Math::sqrt(radius**2 - (OFL*500*dbu)**2 ))))
pts.push(Point.from_dpoint(DPoint.new( OFL*500*dbu, - Math::sqrt(radius**2 - (OFL*500*dbu)**2 ))))
end
layout.cell(wafer).shapes(layer_id).insert(Polygon.new(pts))
### draw the safe circle
pts = []
n.times do |i|
pts.push(Point.from_dpoint(DPoint.new(RPE * Math::cos(i * da), RPE * Math::sin(i * da))))
end
layout.cell(wafer).shapes(layer_id2).insert(Polygon.new(pts))
### line function Calculating an angle of 45 N-111 Cutaway
linem = 1
lineb1 = - radius * sqrt2
lineb2 = sqrt2 * (radius - Math::sqrt(radius**2 - (CFL/2.0)**2))
lineb = lineb1 + lineb2
R0x = 0.0
R0y = linem * R0x + lineb
R1y = 0.0
R1x = (R1y - lineb) / linem
P2 = -1
DieCount = 0
row_min = cutOFR.to_i
row_max = (rowmax - cutCFR180).to_i
col_max = (columax - cutCFR90).to_i
for j in (row_min..row_max) do
for i in (0..2*col_max) do
gBLx = i * (xlayout + xscribe) + (xscribe/2.0) - radius
gBLy = j * (ylayout + yscribe) + (yscribe/2.0) - radius
gTRx = (i +1) * (xlayout + xscribe) - (xscribe/2.0) - radius
gTRy = (j +1) * (ylayout + yscribe) - (yscribe/2.0) - radius
### point inside wafer Calculation falls in the circle die
ptdisc1 = Math::sqrt(gBLx**2 + gBLy**2)
ptdisc2 = Math::sqrt(gTRx**2 + gTRy**2)
ptdisc3 = Math::sqrt(gBLx**2 + gTRy**2)
ptdisc4 = Math::sqrt(gTRx**2 + gBLy**2)
### in angle 45 45 Ø cutting angle calculation die within
if (cutCFR45 == 1)
Rnx = gBLx
Rny = gBLy
P0 = (R1x-Rnx) * (R0y-Rny)
P1 = (R0x-Rnx) * (R1y-Rny)
P2 = P0-P1
end
### create die rectangle - Create die in the cellview
if (ptdisc1<RPE && ptdisc2<RPE && ptdisc3<RPE && ptdisc4<RPE && P2<0)
layout.cell(wafer).shapes(layer_id3).insert(Box.new(gBLx,gBLy,gTRx,gTRy))
DieCount += 1
end
end # for i
end # for j
### Add text on the layout
txt_size = (radius / 50 / dbu).round.to_s # set the text display size proportional to the wafer radius
layout_view.set_config("default-text-size", txt_size)
string = "Die / poject name : #{layout_name.text}"
layout.cell(wafer).shapes(layer_id3).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.85)))
layout_size = xlayout * ylayout / 1000000 / dbu / dbu
string = "Layout size : #{layout_X.text} x #{layout_Y.text} = #{'%.2f' %layout_size} mm2"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.72)))
string = "Scribe size : X = #{scribe_X.text} : Y #{scribe_Y.text} mm"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.62)))
die_size = (xlayout+xscribe) * (ylayout+yscribe) / 1000000 / dbu / dbu
string = "Die size : #{'%.3f' %(layout_X.text.to_f+scribe_X.text.to_f)} x #{'%.3f' %(layout_Y.text.to_f+scribe_Y.text.to_f)} = #{'%.2f' %die_size} mm2"
layout.cell(wafer).shapes(layer_id3).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.52)))
string = "Die count : (method 1) = #{DieCount1}"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.37)))
string = "Die count : (method 2) = #{DieCount2}"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.27)))
string = "Die count : (counted) = #{DieCount} \nBest method: counted from the wafer map"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.15)))
string = " NegBin Yield = #{'%.2f' %NegBinYield} %"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * 0.07)))
string = "Poisson Yield = #{'%.2f' %PoissonYield} %"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.03)))
string = " Murphy Yield = #{'%.2f' %MurphyYield} %"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.13)))
string = " Seed Yield = #{'%.2f' %SeedYield} %"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.23)))
good_die = ((NegBinYield+PoissonYield+MurphyYield)*DieCount/300).round
string = "Expected good die / wafer = #{good_die}\n\nDepending on design, layout and test margins,\nfoundry defects density D0 and process complexity"
layout.cell(wafer).shapes(layer_id3).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.43)))
string = "Foundry defects density = #{defectsE.text} /cm^2\nProcess complexity assumed : #{alphaE.text}"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.55)))
string = "Wafer size = #{waf_diam.currentText()}"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.65)))
string = "Wafer type = #{waf_typ.currentText()}"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.75)))
string = "Wafer safety edge = #{'%.2f' %(RE + PE)} mm"
layout.cell(wafer).shapes(layer_id2).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.85)))
string = "Wafer thickness = #{thick} um-typ"
layout.cell(wafer).shapes(layer_id).insert(RBA::Text::new(string, RBA::Trans::new(radius * 1.1, radius * -0.95)))
### Text box : the third parameter : radius*2.2 need to be adjusted depending on your screen resolution : it adjust the box width
layout.cell(wafer).shapes(layer_id).insert(Box.new(radius*1.05 , -radius , radius*2.2 , radius*0.95))
### adjust layout view to fit the drawings
layout_view.select_cell(wafer, 0)
layout_view.update_content
layout_view.add_missing_layers
layout_view.zoom_fit
layout_view.max_hier_levels=(2)
### Die count and Yield message
# RBA::MessageBox::info("Die count and Yield", "Gross die (count1) = #{DieCount1}\nGross die (count2) = #{DieCount2}\nBest one from drawing: \nGross die (counted) = #{DieCount}\n\nNegBin Yield #{'%.2f' %NegBinYield}%\nPoisson Yield #{'%.2f' %PoissonYield}%\nMurphy Yield #{'%.2f' %MurphyYield}%\nSeed Yield #{'%.2f' %SeedYield}%", RBA::MessageBox::b_ok)
end
### add the command in the tools menu
app = RBA::Application.instance
mw = app.main_window
menu = mw.menu
menu.insert_separator("tools_menu.end", "name")
menu.insert_item("tools_menu.end", "gross_die", $gross_die)
Comments
Hi Laurent -
nice script, thanks for sharing.
Can you explain this "feature":
If die height is larger than its width - a large portion of the wafer (on the right hand side) is not utilized - not covered by dies... Why?
You may be interested in reading this paper, talking about optimization of dies placement on wafer:
https://www.pdf.com/upload/File/Publications/WAMA.pdf
Max
Hi Laurent,
thanks from my side as well for sharing it.
Matthias
The script works well under windows, but on Linux, I got the Ruby error message :
NameError: uninitialized constant QDialog in Action::triggered
/my_path/klayout-0.22.9/bin.linux-64-gcc-release/gross_die.rbm:24:
/my_path/klayout-0.22.9/bin.linux-64-gcc-release/write_childcells.rbm:31 in `call'
/my_path/klayout-0.22.9/bin.linux-64-gcc-release/write_childcells.rbm:31 in `trigered'
For your information, I have compiled it on RHEL6 installation.
Any idea on how to debug this error ?
Thanks, Rgds,
Laurent
Hi Laurent,
That is probably because the Qt binding is not enabled on Linux by default. If you build with -with-qtbinding the Qt classes are available for Ruby as well. But building will take considerably longer then.
Regards,
Matthias
Due the advanced or MEMS Silicon process, the chip direction is need fixed for match the Si wafer crystal orientation. So the automatic roate 90 degrees in height > width case could be a option for choice.
Thanks!
regards,
arided
Dear Arided,
.
There was a bug when Y > X, it is now fixed in the script ABOVE. So you can set the X and Y as you need.
There is a line you will have to tune :
This is the Text box : the third parameter ( radius*2.2 ) needs to be adjusted depending on your screen resolution : it adjusts the box width.
Hope it feed your needs, if you have other request, do not hezitate to ask, improvements are welcome.
Happy new year, Best regards,
Laurent
I'd like to try your script but fail with a comment MenuAction not defined.
I assume the root cause has to do with the QT installation. I tried the windows version too, through installer and the zip version.
The QT enablement in windows is done automatic while in Linux you need the use qt binding switch with build.sh.
Did you try the latest windows release?
Thanks
Andy
Thanks for sharing this script.
I have updated my script to calculate the wafermap and number of die per wafer.
It is now compatible and as complete as my Android Application "Wafer gross die yield and cost".
It considers 16 different positions of the wafermap, furthermore, it can save it in GDS format.
Due to the very recent 0.25 version of KLayout, I also updated it to be compatible.
Laurent
Hi Laurent,
thanks a lot for sharing it!
Regards,
Matthias
Could this be adjusted to be saved as a Macro?
Hi Laurent,
Thanks for sharing the script.
My computer is Windows 11.
I am trying to run the script - import as a Ruby macro, and getting the error message at line 24: "uninitialized constant MenuAction".
I tried versions 0.28.9, 0.28.11 - both stuck at the same point.
Any idea on how to fix this and get the script running?
Thanks,
Victor
"MenuAction" is not part of the KLayout API .. I assume it got there initially by namespace pollution from another package.
The API way of doing this is:
"namespace pollution" means that all top-level scripts share the same global name space. So if one script defines a class called "MenuAction", other script will see it too, but they will not execute without the first one. Do prevent namespace pollution in general, script code can be placed inside a specific module:
I'd also suggest to save the script as a Macro (.lym file) and configure it as "Autorun". Then it is automatically loaded and you do not need to specify it as "-rm ..." on the command line.
Matthias
Hey Matthias,

Thanks for helping me.
I am confused with your snapshot. Is it Ruby script or Python script?
I was able to import script from a file as a Ruby script only, with .rb extention. Saving it under gross_die.lym name as you suggested, gives an error when importing it back to Klayout (snapshot attached).
I was trying to debug it, setting breakpoints. It stops on the lines you added only. The next line "dialog = QDialog.new(Application.instance.main_window)" never returns to the debugger.
Victor
This seems very useful (at one or more points in the tapeout
series of events) and I'd like to suggest that when it's "ready
for prime time", it be plugged into a contrib utilities menu all
fill-form-GUI-like?
I think there's enough good contrib stuff going on that some
of it ought to be "curated" and made part of the family, not
just stashed in the file-pile or discussion archive with DIY
instructions. Like, anything that seems "universal layout
workflow accessory" (not necessarily -every- contrib widget,
but if it looks like it deserves a pulldown menu button, ...).
@vspitsyn You cannot simply rename .rb to .lym - that is a different format (XML).
In order to make the script work, go to "Tools/Macro Development", create a new Ruby macro (.lym) and paste the code from above.
Substitute
by
Then set the "Autorun on Startup" flag.
However, I found that some more things are not compatible with recent versions of KLayout, so I patched the script and provide a final version for download in the attachment.
This generates this view:
Matthias
Hi Matthias,
Probably, my Klayout is not properly initialized.
I have downloaded .exe file from the site, run it and got Klayout (Editor) button in Start menu.
Klayout Editor is running, and I can open up example gds, show and hide layers - all seems to be working properly.
But your script is silently ending, doing nothing. As so as previous versions in the discussion do (after fixing MenuAction issue).
My expectation is that there should new window open up, where I can enter parameters for calculation. But nothing happens.
One more observation - when I open up Macro Properties form, it has always been in the Interpreter Ruby mode, I see no way to change it to the Interpreter Python.
Thanks,
Victor
Hello,
Just tested: it works via the menu "Tools > gros_die" but not from within the "Macro Development" window...
Can you try?
You can also try the attached version: menu entry is "Tools > Generate > Wafer Map Calculation"...
Cheers,
Tomas
Thanks Tomas.
Finally, I figured out the way to run Klayout from a command line, with -rm key. It works fine.
Hi Sir,
I have some idea , hope it can help for you.
in bumpping team , we have to check and keep out our Gross DIE without wafer ID.
that area is depend on fab laser technology and need to make some buffer for that .
Another point is , some of photo machine can't procoess the field center releated wafer center by a small value (such as 0.005um)
so , we will try to make the field /shot center to wafer center as a integer value.
Hope it is helpful for your code.