Box In Box Pcell

edited November 2012 in Ruby Scripting
Hi everybody,

Thnaks to the Pcell tools developped by Matthias i have written a small Pcell which is used for Box In Box alignement control patterns. You can have a "standard" BiB which is 30x30 µm wide or a larger one which is 90µmx90µm (text not comprised) with alignement control better than 1µm (with optical means) and even better using SEM.
The code is probably clean, but i didn't detect bugs in it. The difference in the way Box and In Box are drawn (mainly coordinates), is on purpose (but there is probably a better way to do what i do there)

Here it is
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<prolog/>
<epilog/>
<doc/>
<format>general</format>
<autorun>true</autorun>
<autorun-early>true</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<text># PCell Pour motif alignement et contrôles
#
# This library contains
# BoxInBox
# sample PCell implements a library called "MyLib" with a single PCell that
# draws a circle. It demonstrates the basic implementation techniques for a PCell
# and how to use the "guiding shape" feature to implement a handle for the circle
# radius.
#
# NOTE: after changing the code, the macro needs to be rerun to install the new
# implementation. The macro is also set to "auto run" to install the PCell
# when KLayout is run.

module AligneControle

include RBA

# Remove any definition of our classes (this helps when
# reexecuting this code after a change has been applied)
AligneControle.constants.member?(:BoxinBox) && remove_const(:BoxinBox)
AligneControle.constants.member?(:AligneControle) && remove_const(:AligneControle)

# The PCell declaration for the circle
class BoxinBox < PCellDeclarationHelper

include RBA

def initialize

# Important: initialize the super class
super

# declare the parameters
param(:larg, TypeBoolean, "Large ?",:default=> false)
param(:bl, TypeLayer, "Layer Box", :default => LayerInfo::new(1, 0))
param(:binv,TypeBoolean, "Box polarité inverse?",:default => false)
param(:ibl, TypeLayer, "Layer InBox", :default => LayerInfo::new(1, 0))
param(:ibinv,TypeBoolean, "InBox polarité inverse?",:default => false)

# param(:s, TypeShape, "", :default => DPoint::new(0, 0))
# param(:r, TypeDouble, "Radius", :default => 0.1)
# param(:n, TypeInt, "Number of points", :default => 64)
# this hidden parameter is used to determine whether the radius has changed
# or the "s" handle has been moved
# param(:ru, TypeDouble, "Radius", :default => 0.0, :hidden => true)
# param(:inv,TypeBoolean, "inverse?",:default => false)
end

def display_text_impl
# Provide a descriptive text for the cell
"BoxInBox(L1=#{bl.to_s},L2=#{ibl.to_s})"
end

def coerce_parameters_impl

# We employ coerce_parameters_impl to decide whether the handle or the
# numeric parameter has changed (by comparing against the effective
# radius ru) and set ru to the effective radius. We also update the
# numerical value or the shape, depending on which on has not changed.
# rs = nil
# if s.is_a?(DPoint)
# compute distance in micron
# rs = s.distance(DPoint::new(0, 0))
# end
# if rs && (r-ru).abs < 1e-6
# set_ru rs
# set_r rs
#else
# set_ru r
#set_s DPoint::new(-r, 0)
#end

# n must be larger or equal than 4
#n > 4 || (set_n 4)
r =25
end

def can_create_from_shape_impl
# Implement the "Create PCell from shape" protocol: we can use any shape which
# has a finite bounding box
shape.is_box? || shape.is_polygon? || shape.is_path?
end

def parameters_from_shape_impl
# Implement the "Create PCell from shape" protocol: we set r and l from the shape's
# bounding box width and layer


set_r shape.bbox.width*layout.dbu / 2
set_ibl layout.get_info(layer)
set_bl layout.get_info(layer)
end

def transformation_from_shape_impl
# Implement the "Create PCell from shape" protocol: we use the center of the shape's
# bounding box to determine the transformation
Trans.new(shape.bbox.center)
end

def produce_impl


# This is the main part of the implementation: create the layout

# fetch the parameters


if larg
f_ib = 3.5
f_b = 3.0
else
f_b = 1.0
f_ib = 1.0
end


# find the lib
lib = RBA::Library.library_by_name("Basic")
lib || raise("Unknown lib 'Basic'")

# find the pcell
pcell_decl = lib.layout.pcell_declaration("TEXT")
pcell_decl || raise("Unknown PCell 'TEXT'")

layer = ibl.layer
datatype = ibl.datatype

if !ibinv

param = {"text" => ibl.layer.to_s, "layer" =>RBA::LayerInfo::new(layer, datatype), "mag"=>f_b*15, "inverse"=> false}
pv = pcell_decl.get_parameters.collect do |p|
param[p.name] || p.default
end


# InBox
pts3 = [ [ [-4.5*f_ib,6*f_ib],[-4.5*f_ib,8*f_ib],[4.5*f_ib,8*f_ib],[4.5*f_ib,6*f_ib] ],
[ [-4.5*f_ib,-6*f_ib],[-4.5*f_ib,-8*f_ib],[4.5*f_ib,-8*f_ib],[4.5*f_ib,-6*f_ib] ],
[ [-6*f_ib,4.5*f_ib],[-8*f_ib,4.5*f_ib],[-8*f_ib,-4.5*f_ib],[-6*f_ib,-4.5*f_ib] ],
[ [6*f_ib,4.5*f_ib],[8*f_ib,4.5*f_ib],[8*f_ib,-4.5*f_ib],[6*f_ib,-4.5*f_ib] ] ]

pts3.each { |pp|
pointlist = []
pp.each { |p| pointlist.push(Point.from_dpoint(DPoint.new( p[0]/layout.dbu, p[1]/layout.dbu ) )) }
cell.shapes(ibl_layer).insert(Polygon.new(pointlist))
}
else

param = {"text" => ibl.layer.to_s, "layer" =>RBA::LayerInfo::new(layer, datatype), "mag"=>f_b*15, "inverse"=> true}
pv = pcell_decl.get_parameters.collect do |p|
param[p.name] || p.default
end

# InBox inverse
pts3 = [ [-15.00000*f_b,-15.00000*f_b],
[-15.00000*f_b,15.00000*f_b],
[15.00000*f_b,15.00000*f_b],
[15.00000*f_b,8.00000*f_ib],
[-4.50000*f_ib,8.00000*f_ib],
[-4.50000*f_ib,6.00000*f_ib],
[4.50000*f_ib,6.00000*f_ib],
[4.50000*f_ib,8.00000*f_ib],
[15.00000*f_b,8.00000*f_ib],
[15.00000*f_b,4.50000*f_ib],
[-8.00000*f_ib,4.50000*f_ib],
[-8.00000*f_ib,-4.50000*f_ib],
[-6.00000*f_ib,-4.50000*f_ib],
[-6.00000*f_ib,4.50000*f_ib],
[6.00000*f_ib,4.50000*f_ib],
[6.00000*f_ib,-4.50000*f_ib],
[8.00000*f_ib,-4.50000*f_ib],
[8.00000*f_ib,4.50000*f_ib],
[15.00000*f_b,4.50000*f_ib],
[15.00000*f_b,-6.00000*f_ib],
[-4.50000*f_ib,-6.00000*f_ib],
[-4.50000*f_ib,-8.00000*f_ib],
[4.50000*f_ib,-8.00000*f_ib],
[4.50000*f_ib,-6.00000*f_ib],
[15.00000*f_b,-6.00000*f_ib],
[15.00000*f_b,-15.00000*f_b] ]

# pts3.each { |pp|
pointlist = []
pts3.each { |p| pointlist.push(Point.from_dpoint(DPoint.new( p[0]/layout.dbu, p[1]/layout.dbu ) )) }
cell.shapes(ibl_layer).insert(Polygon.new(pointlist))
# }
end

# create a PCell variant cell
pcell_var = cell.layout.add_pcell_variant(lib, pcell_decl.id, pv)
x = f_b*16.5/layout.dbu
y = f_b*3/layout.dbu
t = RBA::Trans::new(0,false, x, y)
pcell_inst = cell.insert(RBA::CellInstArray::new(pcell_var, t))



layerb = bl.layer
datatypeb = bl.datatype

if !binv

param = {"text" => bl.layer.to_s, "layer" =>RBA::LayerInfo::new(layerb, datatypeb), "mag"=>f_b*15, "inverse"=> false}
pv = pcell_decl.get_parameters.collect do |p|
param[p.name] || p.default
end

#Box

pts2 = [ [ [-7.5,10],[-7.5,12],[7.5,12],[7.5,10] ],
[ [-7.5,-10],[-7.5,-12],[7.5,-12],[7.5,-10] ],
[ [-10,7.5],[-12,7.5],[-12,-7.5],[-10,-7.5] ],
[ [10,7.5],[12,7.5],[12,-7.5],[10,-7.5] ] ]

pts2.each { |pp|
pointlist = []
pp.each { |p| pointlist.push(Point.from_dpoint(DPoint.new( f_b*p[0]/layout.dbu, f_b*p[1]/layout.dbu ) )) }
cell.shapes(bl_layer).insert(Polygon.new(pointlist))
}

else

param = {"text" => bl.layer.to_s, "layer" =>RBA::LayerInfo::new(layerb, datatypeb), "mag"=>f_b*15, "inverse"=> true}
pv = pcell_decl.get_parameters.collect do |p|
param[p.name] || p.default
end

#Box inverse

pts2 = [ [-15.00000,-15.00000],
[-15.00000,15.00000],
[-10.00000,15.00000],
[-10.00000,7.500000],
[-12.00000,7.500000],
[-12.00000,-7.500000],
[-10.00000,-7.500000],
[-10.00000,15.00000],
[7.500000,15.00000],
[7.500000,12.00000],
[-7.500000,12.00000],
[-7.500000,10.00000],
[7.500000,10.00000],
[7.500000,-10.00000],
[-7.500000,-10.00000],
[-7.500000,-12.00000],
[7.500000,-12.00000],
[7.500000,15.00000],
[12.00000,15.00000],
[12.00000,7.500000],
[10.00000,7.500000],
[10.00000,-7.500000],
[12.00000,-7.500000],
[12.00000,15.00000],
[15.00000,15.00000],
[15.00000,-15.00000]
]

# pts3.each { |pp|
pointlist = []
pts2.each { |p| pointlist.push(Point.from_dpoint(DPoint.new( f_b*p[0]/layout.dbu,f_b*p[1]/layout.dbu ) )) }
cell.shapes(bl_layer).insert(Polygon.new(pointlist))
# }

end
pcell_var = cell.layout.add_pcell_variant(lib, pcell_decl.id, pv)
x = 16.5*f_b/layout.dbu
y = -13.5*f_b/layout.dbu
t = RBA::Trans::new(0,false, x, y)
pcell_inst = cell.insert(RBA::CellInstArray::new(pcell_var, t))
end


# cell.shapes(l_layer).insert(Polygon.new(pointlist))

end
# The library where we will put the PCell into
class AligneControle < Library

def initialize

# Set the description
self.description = "Librairie alignement et controles"

# Create the PCell declarations
layout.register_pcell("BoxinBox", BoxinBox::new)
# That would be the place to put in more PCells ...

# Register us with the name "MyLib".
# If a library with that name already existed, it will be replaced then.
register("AligneControle")

end

end

# Instantiate and register the library
AligneControle::new

end
</text>
</klayout-macro>

Comments

  • edited November 2012

    Hi gwondaleya,

    that's amazing, thank you very much :-)

    I have put the script on the server. You can download it here: bib_pcell.lym.

    The "server" is actually a SVN repository already. I plan to give write access to forum users, so that could be a chance for further development of that library. Please let me know in case you object to that. I thought the code is intended to become public domain - otherwise you would not have posted it here :-)

    Thank and best regards,

    Matthias

  • edited November 2012
    Hi Matthias

    Was indeed intented to become public. That's part of the spirit of GPL :-) isnt'it? You have written and released a master piece of software, so it appears to me normal to contribute when i can and with my modest ruby knowledge.

    Joël
  • edited November -1

    You're right :-)

    Thank you,

    Matthias

Sign In or Register to comment.