Dice count : gross die, yield and wafermap on a layout

edited October 2013 in Ruby Scripting

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

  • edited November -1

    Hi Laurent -

    nice script, thanks for sharing.

    Can you explain this "feature":

    if (h > w)
        h = width
        w = height
    end
    

    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

  • edited October 2013

    Hi Laurent,

    thanks from my side as well for sharing it.

    Matthias

  • edited November -1
    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
  • edited October 2013

    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

  • edited November -1
    Hi, 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
  • edited December 2013

    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 :

    layout.cell(wafer).shapes(layer_id).insert(Box.new(radius*1.05 , -radius , radius*2.2 , radius*0.95))
    

    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

  • edited November -1
    Hello 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
  • edited November -1
    Forget about my last comment Laurent - works fine.

    Thanks for sharing this script.
  • edited November 2017

    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

    #
    # 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).
    #
    # def wafermap(layout_name, layout_X, layout_Y, scribe_X, scribe_Y, waf_diam, waf_typ, PEE, defectsE, alphaE, l)
    
    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
    
        app = RBA::Application.instance
        for l in (1 .. 16) do
    
        ### 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.0 ## should be 0.1
                thick = 725
            end
            if (waf_diam.currentText() == "300 mm / 12 \" ")
                diameter = 300.0
                OFL = 0.0
                CFL = 0.0
                RE  = 0.0
                thick = 775
            end
            if (waf_diam.currentText() == "450 mm / 18 \" ")
                diameter = 450.0
                OFL = 0.0
                CFL = 0.0
                RE  = 0.0 ## should be 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
        diam_eff = diameter - 2*PE - RE
    
        if (w < diameter)
            rowmax = (Math::sqrt(diam_eff ** 2 - w ** 2) / h).to_i - 2
            columax = (Math::sqrt(diam_eff ** 2 - h ** 2) / w).to_i
    
            for row in (1..rowmax) do
                columns = (Math::sqrt((diam_eff ** 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 * (diameter - 2*PE - RE) ** 2 / diearea / 4.0
        Margin = PI * (diameter - 2*PE - RE) / Math::sqrt(2 * diearea)
        DieCount2 = (PreCount - Margin).round
    
    ### Formula 3 is Wikipedia calculation method
        DieCount3 = (PreCount * Math::exp( -2 * Math::sqrt(diearea) / diearea)).round
    
    ### Formula 4 is Wikipedia calculation method
        DieCount4 = (PreCount * (1 - 2 * Math::sqrt(diearea) / diearea) ** 2).round
    
    ### Formula 5 is Wikipedia calculation method
        DieCount5 = (PreCount - 0.58 * sqrt2 * Margin).round
    
    ### Formula 6 is Wikipedia calculation method
        DieCount6 = (PreCount * Math::exp( -2.32 * Math::sqrt(diearea) / diearea)).round
    
    ### Formula 7 is Wikipedia calculation method
        DieCount7 = (PreCount * (1 - 1.16 * Math::sqrt(diearea) / diearea) ** 2).round
    
    ### Formula 8 is TSMC calculation method
        DieCount8 = (PI * (diameter / 2.0 - 0.24 * (h + w) - PE - RE) ** 2 / diearea).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
    
    
            if (l == 1)
                dx = 0.0
                dy = 0.0
            end
            if (l == 2)
                dx = 0.0
                dy = 0.25
            end
            if (l == 3)
                dx = 0.0
                dy = 0.5
            end
            if (l == 4)
                dx = 0.0
                dy = 0.75
            end
            if (l == 5)
                dx = 0.25
                dy = 0.0
            end
            if (l == 6)
                dx = 0.25
                dy = 0.25
            end
            if (l == 7)
                dx = 0.25
                dy = 0.5
            end
            if (l == 8)
                dx = 0.25
                dy = 0.75
            end
            if (l == 9)
                dx = 0.5
                dy = 0.0
            end
            if (l == 10)
                dx = 0.5
                dy = 0.25
            end
            if (l == 11)
                dx = 0.5
                dy = 0.5
            end
            if (l == 12)
                dx = 0.5
                dy = 0.75
            end
            if (l == 13)
                dx = 0.75
                dy = 0.0
            end
            if (l == 14)
                dx = 0.75
                dy = 0.25
            end
            if (l == 15)
                dx = 0.75
                dy = 0.5
            end
            if (l == 16)
                dx = 0.75
                dy = 0.75
            end
    
    ### Draw the wafer
        mw = app.main_window
    
    #  create a new layout 
        mw.create_layout( 1 )
        layout_view = mw.current_view
    
    ###  create a new layer in that layout
        layout = layout_view.active_cellview.layout 
        layout1 = layout_view.active_cellview.layout 
        layout_view.set_config("background-color", "0xFFFFFF")
    
    ###  create a layer view for the wafer
        linfo1 = RBA::LayerInfo.new(1,0)
        linfo1.name = "wafer"
        layer_id = layout.insert_layer( linfo1 )
        ln1 = RBA::LayerPropertiesNode::new
        ln1.source_layer_index = layer_id
        ln1.dither_pattern = 1
        # ln.fill_color = 0xFFFFFF
        ln1.frame_color = 0x000000
        ln1.name = "wafer"
        ln1.width = 3
        layout_view.insert_layer( layout_view.end_layers, ln1 )
    
    ###  create a layer view for the safe area
        linfo2 = RBA::LayerInfo.new(2,0)
        linfo2.name = "safe_area"
        layer_id2 = layout.insert_layer( linfo2 )
        ln2 = RBA::LayerPropertiesNode::new
        ln2.source_layer_index = layer_id2
        ln2.dither_pattern = 1
        # ln2.fill_color = 0xFFFFFF
        ln2.frame_color = 0x00FF00
        ln2.name = "safe_area"
        ln2.width = 1
        layout_view.insert_layer( layout_view.end_layers, ln2 )
    
    ###  create a layer view for the die
        linfo3 = RBA::LayerInfo.new(3,0)
        linfo3.name = "die"
        layer_id3 = layout.insert_layer( linfo3 )
        ln3 = RBA::LayerPropertiesNode::new
        ln3.source_layer_index = layer_id3
        ln3.dither_pattern = 1
        ln3.frame_color = 0xFF0000
        ln3.name = "die"
        ln3.width = 2
        layout_view.insert_layer( layout_view.end_layers, ln3 )
    
    ###  create a layer view for the die
        linfo4 = RBA::LayerInfo.new(4,0)
        linfo4.name = "center_line"
        layer_id4 = layout.insert_layer( linfo4 )
        ln4 = RBA::LayerPropertiesNode::new
        ln4.source_layer_index = layer_id4
        ln4.dither_pattern = 1
        ln4.frame_color = 0x0000FF
        ln4.name = "center_line"
        ln4.width = 3
        layout_view.insert_layer( layout_view.end_layers, ln4 )
    
    ###  create a top cell
        wafer = layout.add_cell( "wafer_#{l}" )
        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
        RPE = radius - PE * 1000.0 * dbu
        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))
    
        string = "WaferMap center :\n #{'%.3f' %((layout_X.text.to_f + scribe_X.text.to_f)*dx)} , #{'%.3f' %((layout_Y.text.to_f + scribe_Y.text.to_f)*dy)}"
        text = RBA::Text::new(string, RBA::Trans::new(-radius.round, (radius * 0.9).round))
        text.size = (radius * 1000.0 / 25 / dbu).round
        layout.cell(wafer).shapes(layer_id4).insert(text)
    
    ### 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 + 2).to_i
        col_max = (columax - cutCFR90 + 1).to_i
    
        for j in (-row_max..row_max) do
            for i in (-col_max..col_max) do
                gBLx = (i + dx) * (xlayout + xscribe) + (xscribe/2.0)
                gBLy = (j + dy) * (ylayout + yscribe) + (yscribe/2.0)
                gTRx = (i + dx + 1) * (xlayout + xscribe) - (xscribe/2.0)
                gTRy = (j + dy + 1) * (ylayout + yscribe) - (yscribe/2.0)
    
    ### 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 / project name :  #{layout_name.text}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.95).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id3).insert(text)
        layout_size = xlayout * ylayout / 1000000 / dbu / dbu
        string = "Layout size : #{layout_X.text} x #{layout_Y.text} = #{'%.2f' %layout_size} mm2"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.84).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Scribe size :  X = #{scribe_X.text} :  Y #{scribe_Y.text}  mm"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.76).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        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"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.68).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id3).insert(text)
        if (l==1)
            string = "Shift in X: center , in Y : center"
        end
        if (l==2)
            string = "Shift in X: 1/4width , in Y : center"
        end
        if (l==3)
            string = "Shift in X: 1/2width , in Y : center"
        end
        if (l==4)
            string = "Shift in X: 3/4width , in Y : center"
        end
        if (l==5)
            string = "Shift in X: center , in Y : 1/4height"
        end
        if (l==6)
            string = "Shift in X: 1/4width , in Y : 1/4height"
        end
        if (l==7)
            string = "Shift in X: 1/2width , in Y : 1/4height"
        end
        if (l==8)
            string = "Shift in X: 3/4width , in Y : 1/4height"
        end
        if (l==9)
            string = "Shift in X: center , in Y : 1/2height"
        end
        if (l==10)
            string = "Shift in X: 1/4width , in Y : 1/2height"
        end
        if (l==11)
            string = "Shift in X: 1/2width , in Y : 1/2height"
        end
        if (l==12)
            string = "Shift in X: 3/4width , in Y : 1/2height"
        end
        if (l==13)
            string = "Shift in X: center , in Y : 3/4height"
        end
        if (l==14)
            string = "Shift in X: 1/4width , in Y : 3/4height"
        end
        if (l==15)
            string = "Shift in X: 1/2width , in Y : 3/4height"
        end
        if (l==16)
            string = "Shift in X: 3/4width , in Y : 3/4height"
        end
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.60).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id4).insert(text)
    
        string = "Die count : (method 1) =  #{DieCount1}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.52).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 2) =  #{DieCount2}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.46).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 3) =  #{DieCount3}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.40).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 4) =  #{DieCount4}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.34).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 5) =  #{DieCount5}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.28).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 6) =  #{DieCount6}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.22).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 7) =  #{DieCount7}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.16).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (method 8) =  #{DieCount8}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.10).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Die count : (counted) =  #{DieCount}  \nBest method: counted from the wafer map"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * 0.00).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id3).insert(text)
    
        string = " NegBin Yield =  #{'%.2f' %NegBinYield} %"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.08).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Poisson Yield =  #{'%.2f' %PoissonYield} %"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.15).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = " Murphy Yield =  #{'%.2f' %MurphyYield} %"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.22).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "   Seed Yield =  #{'%.2f' %SeedYield} %"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.28).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        good_die = ((NegBinYield+PoissonYield+MurphyYield)*(DieCount1+DieCount2+DieCount3+DieCount4+DieCount5+DieCount6+DieCount7+DieCount8+2*DieCount)/3000).round
        string = "Expected good die / wafer = #{good_die}\n\nDepending on design, layout and test margins,\nfoundry defects density D0 and process complexity"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.46).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id3).insert(text)
        string = "Foundry defects density =  #{defectsE.text} /cm^2\nProcess complexity assumed : #{alphaE.text}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.55).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
    
        string = "Wafer size =  #{waf_diam.currentText()}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.65).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Wafer type =  #{waf_typ.currentText()}"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.75).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
        string = "Wafer safety edge =  #{'%.2f' %(RE + PE)} mm"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.85).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id2).insert(text)
        string = "Wafer thickness =  #{thick} um-typ"
        text = RBA::Text::new(string, RBA::Trans::new((radius * 1.1).round,(radius * -0.95).round))
        text.size = (radius * 1000.0 / 50 / dbu).round
        layout.cell(wafer).shapes(layer_id).insert(text)
    
    ### 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).round,(-radius*1.0).round,(radius*1.9).round,(radius*1.00).round))
    
    ### 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)
    
        end
    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)
    
  • edited November -1

    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:

    ...
    include RBA
    
    $gross_die = Action.new
    $gross_die.title = "Gross die / Yield"
    $gross_die.on_triggered do
    
        dialog = QDialog.new(Application.instance.main_window)
    ...
    

    "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:

    module DieCount
    
    include RBA
    
    ....
    
    end
    

    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

    include RBA
    
    $gross_die = MenuAction.new( "Gross die / Yield", "" ) do 
    
        dialog = QDialog.new(Application.instance.main_window)
    

    by

    include RBA
    
    $gross_die = Action.new
    $gross_die.title = "Gross die / Yield"
    $gross_die.on_triggered do
    
        dialog = QDialog.new(Application.instance.main_window)
    

    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.

Sign In or Register to comment.