Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented a solution for issue #1790 (Support for recursive PCell i… #1803

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 66 additions & 24 deletions src/db/db/built-in-macros/pcell_declaration_helper.lym
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ module RBA
# of a PCell
class PCellDeclarationHelper < PCellDeclaration

# makes PCellDeclaration's "layout" method available
if ! self.method_defined?(:_layout_base)
alias_method :_layout_base, :layout
end

# import the Type... constants from PCellParameterDeclaration
PCellParameterDeclaration.constants.each do |c|
if !const_defined?(c)
Expand All @@ -290,18 +295,58 @@ module RBA
@cell = nil
@layer_param_index = []
@layers = nil
@state_stack = []
end

# provide accessors for the current layout and cell (for prod)
attr_reader :layout, :cell, :shape, :layer
attr_reader :cell, :shape, :layer

def layout
@layout || _layout_base()
end

# provide fallback accessors in case of a name clash with a
# parameter
def _layer; @layer; end
def _layout; @layout; end
def _layout; @layout || _layout_base(); end
def _cell; @cell; end
def _shape; @shape; end

# Starts an operation - pushes the state on the state stack

def start
@state_stack << [ @param_values, @param_states, @layers, @cell, @layout, @layer, @shape ]
self._reset_state
end

# Finishes an operation - pops the state from the state stack

def finish
if ! @state_stack.empty?
@param_values, @param_states, @layers, @cell, @layout, @layer, @shape = @state_stack.pop
else
self._reset_state
end
end

# Resets the state to default values

def _reset_state

@param_values = nil
@param_states = nil
@layers = nil
@layout = nil

# This should be here:
# @cell = nil
# @layer = nil
# @shape = nil
# but this would break backward compatibility of "display_text" (actually
# exploiting this bug) - fix this in the next major release.

end

# A helper method to access the nth parameter

def _get_param(nth, name)
Expand Down Expand Up @@ -410,11 +455,13 @@ module RBA

# implementation of display_text
def display_text(parameters)
self.start
@param_values = parameters
text = ""
begin
text = display_text_impl
ensure
@param_values = nil
self.finish
end
text
end
Expand All @@ -431,94 +478,89 @@ module RBA

# coerce parameters (make consistent)
def coerce_parameters(layout, parameters)
self.start
@param_values = parameters
@layout = layout
ret = parameters
begin
coerce_parameters_impl
ensure
@layout = nil
ret = @param_values
@param_values = nil
self.finish
end
ret
end

# parameter change callback
def callback(layout, name, states)
@param_values = nil
self.start
@param_states = states
@layout = layout
begin
callback_impl(name)
ensure
@param_states = nil
@layout = nil
self.finish
end
end

# produce the layout
def produce(layout, layers, parameters, cell)
self.start
@layers = layers
@cell = cell
@param_values = parameters
@layout = layout
begin
produce_impl
ensure
@layers = nil
@cell = nil
@param_values = nil
@layout = nil
self.finish
end
end

# produce a helper for can_create_from_shape
def can_create_from_shape(layout, shape, layer)
self.start
ret = false
@layout = layout
@shape = shape
@layer = layer
begin
ret = can_create_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
self.finish
end
ret
end

# produce a helper for parameters_from_shape
# produce a helper for transformation_from_shape
def transformation_from_shape(layout, shape, layer)
self.start
@layout = layout
@shape = shape
@layer = layer
t = nil
begin
t = transformation_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
self.finish
end
t
end

# produce a helper for parameters_from_shape
# with this helper, the implementation can use the parameter setters
def parameters_from_shape(layout, shape, layer)
self.start
@param_values = @param_decls.map { |pd| pd.default }
@layout = layout
@shape = shape
@layer = layer
begin
parameters_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
ret = @param_values
self.finish
end
@param_values
ret
end

# default implementation
Expand Down
35 changes: 31 additions & 4 deletions src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(self, *args, **kwargs):
self._param_states = None
self._layer_param_index = []
self._layers = []
self._state_stack = []
# public attributes
self.layout = None
self.shape = None
Expand Down Expand Up @@ -129,6 +130,7 @@ def display_text(self, parameters):
This function delegates the implementation to self.display_text_impl
after configuring the PCellDeclaration object.
"""
self.start()
self._param_values = parameters
try:
text = self.display_text_impl()
Expand Down Expand Up @@ -165,6 +167,7 @@ def init_values(self, values = None, layers = None, states = None):
"layers" are the layer indexes corresponding to the layer
parameters.
"""
self.start()
self._param_values = None
self._param_states = None
if states:
Expand All @@ -177,17 +180,39 @@ def init_values(self, values = None, layers = None, states = None):
self._param_values = values
self._layers = layers

def start(self):
"""
Is called to prepare the environment for an operation
After the operation, "finish" must be called.
This method will push the state onto a stack, hence implementing
reentrant implementation methods.
"""
self._state_stack.append( (self._param_values, self._param_states, self._layers, self.cell, self.layout, self.layer, self.shape) )
self._reset_state()

def finish(self):
"""
Is called at the end of an implementation of a PCellDeclaration method
"""
if len(self._state_stack) > 0:
self._param_values, self._param_states, self._layers, self.cell, self.layout, self.layer, self.shape = self._state_stack.pop()
else:
self._reset_state()

def _reset_state(self):
"""
Resets the internal state
"""
self._param_values = None
self._param_states = None
self._layers = None
self._cell = None
self._layout = None
self._layer = None
self._shape = None
self.layout = super(_PCellDeclarationHelperMixin, self).layout()
# This should be here:
# self.cell = None
# self.layer = None
# self.shape = None
# but this would break backward compatibility of "display_text" (actually
# exploiting this bug) - fix this in the next major release.

def get_layers(self, parameters):
"""
Expand Down Expand Up @@ -255,6 +280,7 @@ def can_create_from_shape(self, layout, shape, layer):
The function delegates the implementation to can_create_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.start()
self.layout = layout
self.shape = shape
self.layer = layer
Expand All @@ -271,6 +297,7 @@ def transformation_from_shape(self, layout, shape, layer):
The function delegates the implementation to transformation_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.start()
self.layout = layout
self.shape = shape
self.layer = layer
Expand Down
79 changes: 77 additions & 2 deletions testdata/python/dbPCells.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import pya
import unittest
import math
import sys

class BoxPCell(pya.PCellDeclaration):
Expand Down Expand Up @@ -77,7 +78,7 @@ def __init__(self):
sb_cell = self.layout().cell(sb_index)
sb_cell.shapes(l10).insert(pya.Box(0, 0, 100, 200))

# register us with the name "MyLib"
# register us with the name "PCellTestLib"
self.register("PCellTestLib")


Expand Down Expand Up @@ -135,9 +136,62 @@ def __init__(self):
# create the PCell declarations
self.layout().register_pcell("Box2", BoxPCell2())

# register us with the name "MyLib"
# register us with the name "PCellTestLib2"
self.register("PCellTestLib2")

# A recursive PCell

class RecursivePCell(pya.PCellDeclarationHelper):

def __init__(self):

super(RecursivePCell, self).__init__()

self.param("layer", self.TypeLayer, "Layer", default = pya.LayerInfo(0, 0))
self.param("line", self.TypeShape, "Line", default = pya.Edge(0, 0, 10000, 0))
self.param("level", self.TypeInt, "Level", default = 1)

def display_text_impl(self):
# provide a descriptive text for the cell
return "RecursivePCell(L=" + str(self.layer) + ",E=" + str(pya.CplxTrans(self.layout.dbu) * self.line) + ",LVL=" + str(self.level)

def produce_impl(self):

# fetch the parameters
l = self.layer_layer
e = self.line

if self.level <= 0:
self.cell.shapes(l).insert(e)
return

d3 = e.d() * (1.0 / 3.0)
d3n = pya.Vector(-d3.y, d3.x)

e1 = pya.Edge(e.p1, e.p1 + d3)
e2 = pya.Edge(e1.p2, e1.p2 + d3 * 0.5 + d3n * math.cos(math.pi / 6))
e3 = pya.Edge(e2.p2, e.p1 + d3 * 2.0)
e4 = pya.Edge(e3.p2, e.p2)

for e in [ e1, e2, e3, e4 ]:
t = pya.Trans(e.p1 - pya.Point())
cc = self.layout.create_cell("RecursivePCell", { "layer": self.layer, "line": t.inverted() * e, "level": self.level - 1 })
self.cell.insert(pya.CellInstArray(cc, t))

class PCellTestLib3(pya.Library):

def __init__(self):

# set the description
self.description = "PCell test lib3"

# create the PCell declarations
self.layout().register_pcell("RecursivePCell", RecursivePCell())

# register us with the name "PCellTestLib3"
self.register("PCellTestLib3")



def inspect_LayerInfo(self):
return "<" + str(self) + ">"
Expand Down Expand Up @@ -501,6 +555,27 @@ def test_8(self):

self.assertEqual(cell.begin_shapes_rec(ly.layer(5, 0)).shape().__str__(), "box (-100,-300;100,300)")

def test_9(self):

if not "PCellDeclarationHelper" in pya.__dict__:
return

# instantiate and register the library
tl = PCellTestLib3()

ly = pya.Layout(True)

li1 = find_layer(ly, "1/0")
self.assertEqual(li1 == None, True)

c1 = ly.create_cell("c1")

c2 = ly.create_cell("RecursivePCell", "PCellTestLib3", { "layer": pya.LayerInfo(1, 0), "level": 4, "line": pya.Edge(0, 0, 20000, 0) })
c1.insert(pya.CellInstArray(c2.cell_index(), pya.Trans()))

self.assertEqual(c2.display_title(), "PCellTestLib3.RecursivePCell(L=1/0,E=(0,0;20,0),LVL=4")
self.assertEqual(str(c1.dbbox()), "(0,0;20,5.774)")

# run unit tests
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests)
Expand Down
Loading
Loading