I would like to automate a series boolean operations, but haven't been able to get past Ruby syntax errors. It would be very helpful if you can provide a simple example.
Thanks,
Mikel
there are a lot of ways to write boolean functions. The most important application probably is to create new layers from existing ones, similar to what the menu functions do.
This is an example piece of code that performs a boolean between two layers:
# get the current layout and cell
view = RBA::Application.instance.main_window.current_view
cellview = view.cellview(view.active_cellview_index)
layout = cellview.layout
cell = cellview.cell
# prepare a shape processor (can be reused for subsequent operations)
processor = RBA::ShapeProcessor.new
# the layers to process
layer_a = 2
layer_b = 3
layer_out = 5
# the actual processor call
processor.boolean(layout, cell, layer_a, layout, cell, layer_b, cell.shapes(layer_out),
RBA::EdgeProcessor::mode_and, true, true, true)
Although this code works, it is somewhat tedious to use, because
The layers must be given as layer indices, not as layers or datatypes
The output layer is not completely cleared but rather the top level shapes are overwritten
To simplify these tasks, I have created a simple helper class. An example for using that class can be found below:
# ----------------------------------------------------------------
# A boolean processor class
class BoolProcessor
def initialize(layout, cell)
@layout = layout
@cell = cell
@proc = RBA::ShapeProcessor.new
@temp_layers = []
end
# Locate a layer for input.
# The layer is specified by a RBA::LayerInfo object which identifies the layer by
# a layer and datatype for example. If the layer does not exist, it is created.
# This method returns a layer index which can be used as input for other functions.
def input(lp)
layer_index = nil
# locate the layer if it already exists
(0..(@layout.layers-1)).each do |i|
if @layout.is_valid_layer?(i) && @layout.get_info(i).is_equivalent?(lp)
layer_index = i
break
end
end
# create a new layer if it does not exist
return layer_index || @layout.insert_layer(lp)
end
# Create a new temporary layer and remember for removal on "cleanup".
# This method returns a layer index which can be used as input for other functions.
def tmp
lp = RBA::LayerInfo.new
layer_index = @layout.insert_layer(lp)
@temp_layers.push(layer_index)
return layer_index
end
# Assign a layer as output.
# Change the layer given by the layer index to the output layer specified
# by a RBA::LayerInfo object. If the layer already exists, it is deleted.
def output(lp, layer_index)
li = nil
# locate the layer if it already exists
(0..(@layout.layers-1)).each do |i|
if @layout.is_valid_layer?(i) && @layout.get_info(i).is_equivalent?(lp)
li = i
break
end
end
# clear the layer if it already exists
if li != layer_index
if li
@layout.delete_layer(li)
end
@layout.set_info(layer_index, lp)
end
# remove the layer from the list of temporary layers
new_temp = []
@temp_layers.each do |i|
if i != layer_index
new_temp.push(i)
end
end
@temp_layers = new_temp
end
# Clean up all temporary layers
def cleanup
@temp_layers.each do |i|
@layout.delete_layer(i)
end
@temp_layers = []
end
# Size a layer by the given value (in micron).
# "input" is the input layer, given by a layer index.
def size(input, d)
res = tmp()
@proc.size(@layout, @cell, input, @cell.shapes(res), to_dbu(d),
2, true, true, true)
return res
end
# Size a layer by the given values anisotropically (in micron)
# "input" is the input layer, given by a layer index.
def size2(input, dx, dy)
res = tmp()
@proc.size(@layout, @cell, input, @cell.shapes(res), to_dbu(dx), to_dbu(dy),
2, true, true, true)
return res
end
# OR two layers
# "a" and "b" are the input layers, given by a layer index.
def or(a, b)
res = tmp()
@proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res),
RBA::EdgeProcessor::mode_or, true, true, true)
return res
end
# XOR two layers
# "a" and "b" are the input layers, given by a layer index.
def xor(a, b)
res = tmp()
@proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res),
RBA::EdgeProcessor::mode_xor, true, true, true)
return res
end
# AND two layers
# "a" and "b" are the input layers, given by a layer index.
def and(a, b)
res = tmp()
@proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res),
RBA::EdgeProcessor::mode_and, true, true, true)
return res
end
# NOT two layers
# "a" and "b" are the input layers, given by a layer index.
def not(a, b)
res = tmp()
@proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res),
RBA::EdgeProcessor::mode_anotb, true, true, true)
return res
end
# Convert a value from micron to database units
# "a" and "b" are the input layers, given by a layer index.
def to_dbu(micron)
return (0.5 + micron / @layout.dbu).floor()
end
# Run a procedure in the context of this object
def run(&action)
action.call(self)
cleanup
end
private
@layout
@cell
@proc
end
The following script employs that class to perform a simple set of boolean operations: it combines two layers, "2/0" for "active" and "3/0" for "poly" with a logical "and" to form the gate regions and combines "active" oversized by 10nm with "poly" to form gate end markers:
view = RBA::Application.instance.main_window.current_view
cv = view.cellview(view.active_cellview_index)
BoolProcessor.new(cv.layout, cv.cell).run do |p|
active = p.input(RBA::LayerInfo.new(2, 0))
poly = p.input(RBA::LayerInfo.new(3, 0))
gates = p.and(active, poly)
p.output(RBA::LayerInfo.new(10, 0), gates)
gate_ends = p.not(p.and(p.size(active, 0.01), poly), gates)
p.output(RBA::LayerInfo.new(11, 0), gate_ends)
end
# Add layers that have been created so far to the layout view
RBA::Application.instance.main_window.cm_lv_add_missing
view.update_content
Hi Matthias,
Can't say enough thanks. This is almost all the code I needed. Will be working with it this weekend.
Amazing program, even more amazing support!
Mike
I have a layout with a top cell and many sub cells. I am using your code from this page to run several sizing and boolean operations on the layers. The code runs very well, but the results are returned to the top cell destroying the hierarchy of the layout.
I've trying to modify the above code in order to return the results in child cell and not the top cell that I am executing the script, for days now.
Tried two strategies (i) iterate through the cell, (ii) use @cell.each_child_cell as the output cell. Unfortunately none works, the first one will delete the layer and if I comment out the delete part then I am filling up with temp layers. The other case it is looking for a box that I am not sure how to define.
Can you please give an example on how to change the output cell?
In general, the code above is awfully old. Now there is the DRC feature which is much more convenient to use. Please see http://www.klayout.de/doc/manual/drc.html for details.
But the DRC feature is also producing flat data. That is a general limitation and it's not easy to overcome.
In general, hierarchical processing of layout is far from being simple. You will always find a solution for a specific case. But there is no general solution, just a variety of heuristic solutions.
For example, a cell-local sizing will only work if the content of each cell is isolated and not interacting (touching, overlapping) with content from either a parent, child or sibling cell (direct or indirect ones). Only in that case you can code a simple operation to run over each cell individually. Except for rare cases this condition isn't met and this is usually when trouble starts. I'm not brave enough to deal with this topic, so my choice was to stay flat.
But there is a general flow scheme which may help to make you independent from the implementation details of the processor: If you are producing a mask, then in general it does not matter (apart from data volume maybe) whether the mask data is flat or hierarchical. But while drawing a layout, the hierarchy is important since it allows you to partition your layout and reduce the manual efforts.
So the solution is to draw your layout in a certain way and apply sizing and boolean operations in a final finishing step. Only then you convert the drawn layout into mask layout. For example you can use the DRC feature to do this. This process can be scripted, so it's no additional effort.
Matthias, If I run this code the manual way and use the heirachial way "flat" it runs slow. If I do the same thing using "semi-hierarchical" it runs fast (instant) and the file size is 100X smaller. Is there a way to run the above code in "semi-hierarchical" mode. Thanks
Comments
Hi Mikel,
there are a lot of ways to write boolean functions. The most important application probably is to create new layers from existing ones, similar to what the menu functions do.
This is an example piece of code that performs a boolean between two layers:
Although this code works, it is somewhat tedious to use, because
To simplify these tasks, I have created a simple helper class. An example for using that class can be found below:
The following script employs that class to perform a simple set of boolean operations: it combines two layers, "2/0" for "active" and "3/0" for "poly" with a logical "and" to form the gate regions and combines "active" oversized by 10nm with "poly" to form gate end markers:
Matthias
Can't say enough thanks. This is almost all the code I needed. Will be working with it this weekend.
Amazing program, even more amazing support!
Mike
Thank you for the very nice tool.
I have a layout with a top cell and many sub cells. I am using your code from this page to run several sizing and boolean operations on the layers. The code runs very well, but the results are returned to the top cell destroying the hierarchy of the layout.
I've trying to modify the above code in order to return the results in child cell and not the top cell that I am executing the script, for days now.
Tried two strategies (i) iterate through the cell, (ii) use @cell.each_child_cell as the output cell. Unfortunately none works, the first one will delete the layer and if I comment out the delete part then I am filling up with temp layers. The other case it is looking for a box that I am not sure how to define.
Can you please give an example on how to change the output cell?
Thank you,
Michael
Hi Michael,
thanks :-)
In general, the code above is awfully old. Now there is the DRC feature which is much more convenient to use. Please see http://www.klayout.de/doc/manual/drc.html for details.
But the DRC feature is also producing flat data. That is a general limitation and it's not easy to overcome.
In general, hierarchical processing of layout is far from being simple. You will always find a solution for a specific case. But there is no general solution, just a variety of heuristic solutions.
For example, a cell-local sizing will only work if the content of each cell is isolated and not interacting (touching, overlapping) with content from either a parent, child or sibling cell (direct or indirect ones). Only in that case you can code a simple operation to run over each cell individually. Except for rare cases this condition isn't met and this is usually when trouble starts. I'm not brave enough to deal with this topic, so my choice was to stay flat.
But there is a general flow scheme which may help to make you independent from the implementation details of the processor: If you are producing a mask, then in general it does not matter (apart from data volume maybe) whether the mask data is flat or hierarchical. But while drawing a layout, the hierarchy is important since it allows you to partition your layout and reduce the manual efforts.
So the solution is to draw your layout in a certain way and apply sizing and boolean operations in a final finishing step. Only then you convert the drawn layout into mask layout. For example you can use the DRC feature to do this. This process can be scripted, so it's no additional effort.
This also opens the opportunity to apply complex operations such as reverse toning (see http://klayout.de/forum/comments.php?DiscussionID=762&page=1#Item_2 for example).
Matthias
Matthias, If I run this code the manual way and use the heirachial way "flat" it runs slow. If I do the same thing using "semi-hierarchical" it runs fast (instant) and the file size is 100X smaller. Is there a way to run the above code in "semi-hierarchical" mode. Thanks
Thanks Scott
Hi Scott,
this post is pretty outdated.
I'd recommend using DRC for this purpose. For the above example:
DRC supports a real hierarchical mode. Just add the following line to the file at the beginning:
Matthias