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
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
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
You're right :-)
Thank you,
Matthias