-
Notifications
You must be signed in to change notification settings - Fork 2
/
CreateCPW.lym
248 lines (192 loc) · 9.25 KB
/
CreateCPW.lym
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category>pymacros</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>python</interpreter>
<dsl-interpreter-name/>
<text>import pya
import math
"""
Usage: Click "Instance" icon. Choose "LFL_library" and then "cell".
"""
class MakeMeanderCPW(pya.PCellDeclarationHelper):
"""
The PCell declaration for a CPW
"""
def __init__(self):
# Important: initialize the super class
super(MakeMeanderCPW, self).__init__()
# declare the parameters
# self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
self.param("p", self.TypeLayer, "OUTLINE Layer", default = pya.LayerInfo(1, 2))
self.param("k", self.TypeLayer, "KEEPOUT Layer", default = pya.LayerInfo(0, 1))
self.param("c", self.TypeLayer, "IMAGE Layer for CPW", default = pya.LayerInfo(1, 0))
self.param("i", self.TypeLayer, "IMAGE BBOX Layer", default = pya.LayerInfo(0, 3))
# self.param("s", self.TypeShape, "", default = pya.Path.new())
self.param("name", self.TypeString, "CPW Name", default='CPW')
# parameter in um
self.param("cr", self.TypeDouble, "center conductor radius", default = 100)
self.param("cw", self.TypeDouble, "c.c width", default = 10)
self.param("gw", self.TypeDouble, "gap width", default = 6)
self.param("kw", self.TypeDouble, "keepout width", default = 6)
self.param("n", self.TypeInt, "Number of points per 360 degree", default = 360)
self.param("tl", self.TypeDouble, "CPW total length", default = 8000)
self.param("numq", self.TypeInt, "Number of quarter turns (even num>=6)", default =12)
self.param("bw", self.TypeDouble, "Bounding box width", default = 2000)
self.param("bh", self.TypeDouble, "Bounding box height", default = 1600)
self.param("xs", self.TypeDouble, "x-coord. of start point", default = 0)
self.param("xe", self.TypeDouble, "x-coord. of end point", default = 0)
# this hidden parameter is used to determine whether the radius has changed
# or the "s" handle has been moved
#self.param("ru", self.TypeDouble, "Radius", default = 0.0, hidden = True)
#self.param("rd", self.TypeDouble, "Double radius", readonly = True)
def display_text_impl(self):
# Provide a descriptive text for the cell
# return "MakeMeanderCPW(length=" + str(self.tl) + ",gap width=" + ('%.3f' % self.gw) + ")"
return self.name + '_' + f'{int(self.tl)}um'
# return self.name
def coerce_parameters_impl(self):
# 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 one has not changed.
# rs = None
# if isinstance(self.s, pya.DPoint):
# # compute distance in micron
# rs = self.s.distance(pya.DPoint(0, 0))
# if rs != None and abs(self.r-self.ru) < 1e-6:
# self.ru = rs
# self.r = rs
# else:
# self.ru = self.r
# self.s = pya.DPoint(-self.r, 0)
#
# self.rd = 2*self.r
# n must be larger or equal than 9
# if self.n <= 9:
# self.n = 9
# cr must be smaller than the length of each segment. segment is the line connecting two ajacent
# number of quarter turns must be even, and equal or larger than 6.
if self.numq % 2 == 1:
self.numq = self.numq -1
if self.numq <4:
self.numq = 4
# Bounding box height must be larger than N * r.
while self.numq * self.cr > self.bh:
self.numq += -2
# width of meander should be larger than 4 times radius
path_len = self.tl + (2-math.pi/2) * self.cr * self.numq
meander_height = self.numq * self.cr
extra_len = self.bh - meander_height
# find width of meander, W
W = (path_len - extra_len - self.numq * self.cr - math.fabs(self.xs) - math.fabs(self.xe)) / (self.numq/2-1)
if 4 * self.cr > W:
self.cr = W / 4
def can_create_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we can use any shape which
# has a finite bounding box
return self.shape.is_box() or self.shape.is_polygon() or self.shape.is_path()
#def parameters_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we set r and l from the shape's
# bounding box width and layer
# self.tmp = self.shape.bbox().width() * self.layout.dbu / 2
#self.l = self.layout.get_info(self.layer)
#self.s = self.shape.path # read selected path obj.
#def transformation_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we use the center of the shape's
# bounding box to determine the transformation
#return pya.Trans(self.shape.bbox().center())
def produce_impl(self):
# Here comes my code
# create a path
cc_dpath = self.create_path() # in um unit
# create two more paths : gap and keepout
gap_dpath = cc_dpath.dup()
keepout_dpath = cc_dpath.dup()
# set width of each path
cc_dpath.width = self.cw
gap_dpath.width = cc_dpath.width + self.gw * 2.0
keepout_dpath.width = gap_dpath.width + self.kw * 2.0
# round path
cc_dpath_rounded = cc_dpath.round_corners(self.cr, self.n, self.layout.dbu/2.0)
gap_dpath_rounded = gap_dpath.round_corners(self.cr, self.n, self.layout.dbu/2.0)
keepout_dpath_rounded = keepout_dpath.round_corners(self.cr, self.n, self.layout.dbu/2.0)
# turn path to region to do boolean operation
cc_region = pya.Region.new(pya.Path.new(cc_dpath_rounded.to_itype(self.layout.dbu))) # to_itype method to translate the floating-point coordinate path in micron units to an integer-coordinate path in database units.
gap_region = pya.Region.new(pya.Path.new(gap_dpath_rounded.to_itype(self.layout.dbu)))
keepout_region = pya.Region.new(pya.Path.new(keepout_dpath_rounded.to_itype(self.layout.dbu)))
# create shape
# self.cell.shapes(self.p_layer).insert(pya.Path.new(cc_dpath.to_itype(self.layout.dbu)))
self.cell.shapes(self.c_layer).insert(gap_region - cc_region)
self.cell.shapes(self.k_layer).insert(keepout_region)
# create an image bounding box in "Image BBox" layer, based on the given height and width
lower_left_x, lower_left_y = -self.bw / 2.0, -self.bh / 2.0
upper_right_x, upper_right_y = self.bw / 2.0, self.bh / 2.0
Dibbox = pya.DBox.new(lower_left_x, lower_left_y, upper_right_x, upper_right_y)
self.cell.shapes(self.i_layer).insert(Dibbox.to_itype(self.layout.dbu))
outline_region = cc_region
self.cell.shapes(self.p_layer).insert(outline_region)
def create_path(self):
# I assume the path is centered with respect to origin (0,0)
path_len = self.tl + (2.0-math.pi/2.0) * self.cr * self.numq # in um
meander_height = self.numq * self.cr
extra_len = self.bh - meander_height
# find width of meander, W
W = (path_len - extra_len - self.numq * self.cr - (math.fabs(self.xs)+ self.sign(self.xs)*(-1)**(self.numq //2)*self.xe)) / (self.numq/2-1)
# create path points
pts = []
delta = [] # delta vector
if self.xs >= 0:
startsign = -1
else:
startsign = 1
delta.append(pya.DPoint.new(0, self.cr))
delta.append(pya.DPoint.new(W /2 * startsign - self.xs, 0))
for i in range(2, self.numq - 1):
if i % 2 == 0:
delta.append(delta[0] * 2)
else:
delta.append(pya.DPoint.new(W,0) * startsign * (-1)**(i//2))
delta.append(pya.DPoint.new (self.xe - W / 2 * (-1)**(self.numq/2) * startsign, 0))
delta.append(pya.DPoint.new(0, self.cr))
# create meander path except extra length
pts.append(pya.DPoint.new(self.xs, -meander_height / 2.0))
for i in range(0, self.numq + 1):
pts.append(pts[i] + delta[i])
# add extra length
pts.insert(0, pya.DPoint.new(self.xs, -meander_height / 2.0 - extra_len / 2.0))
# pts.append(pya.Point.new(xe_dbu, meander_height_dbu/2 + extra_len_dbu/2))
pts.append(pts[-1] + pya.DPoint.new(0, extra_len / 2.0))
createdpath = pya.DPath.new(pts, 1) # in um
return createdpath
def sign(self, x):
if x>=0:
return 1
else:
return -1
"""
class MyLib(pya.Library):
# The library where we will put the PCell into
def __init__(self):
# Set the description
self.description = "LFL CPW Library"
# Create the PCell declarations
self.layout().register_pcell("MakeMeanderCPW", MakeMeanderCPW())
# 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.
self.register("CPW")
# Instantiate and register the library
MyLib()
"""</text>
</klayout-macro>