使用ADS创建电感Pcell

电感是射频集成电路设计中的基础原件,也是变压器的构成部分。通常,电感越接近圆形其面积(磁通量,电感)/边长(串联损耗)比越高,因而Q值越高。但在CMOS工艺下,有时只允许使用直线和45°折线,因此设计时多数退而求其次绘制八边形电感。绘制八边形电感由于需要处理边角和格点,十分繁琐,因此可以通过编写程序省去这部分重复性工作。本文介绍了一种基于ADS AEL语言的电感Pcell设计方法。

预备

AEL绘制多边形的语法为

1
2
3
4
5
6
7
8
9
decl coors = list(
list(0, 0),
list(2, 0),
list(2, 2),
list(0, 2),
list(0, 0)
);
decl newPolygon = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM1, newPolygon);

上面的代码先定义了一个坐标的列表,列表中的每个元素代表多边形一个顶点。每个顶点又由一个2个元素的列表list(x, y)构成,分别代表为x、y的坐标。因此,上面的代码绘制了一个左下角在(0, 0)的边长为2的正方形。之后,先使用db_create_primitive_polygon将其创建成为原始多边形,再增加层信息后使用db_create_polygon将原始多边形加入设计数据库。

由于AEL不支持类型,对坐标进行修改只能通过函数进行。值得注意的是,AEL中列表的赋值是引用传递,因此当使用decl coors2 = coors;后,coors2coors指向同一个位置,对其中之一修改也会影响到另一个。我们设计了一个函数对多边形进行整体平移和翻转:

1
2
3
4
5
6
7
8
9
10
11
12
defun adjust(l, xc, yc, x, y)
{
decl i = 0;
decl len = listlen( l ); //获得列表长度
for( i = 0; i < len; i++ ) //遍历列表
{
//生成平移和翻转后的坐标,由于采用了垃圾回收机制,新生成的列表t不是局部变量
decl t = list(l[i][0] * xc + x, l[i][1] * yc + y);
//替换原有元素
repla(l, t, i);
}
}

它接受5个参数,第1个是列表,第2、3个分别是x、y的缩放系数,第4、5个是x、y的平移系数。

此外,为了防止设计的版图顶点不在格点上,我们附加了grid_ceil2grid_floor2两个相对格点取整的函数,他们以微米为单位对0.01取整。

交错部分设计

此部分由函数create_crossing实现。

首先计算几个常用量,由

可以算得

考虑版图

crossing

可以计算出各点的位置

位置 坐标
1 ( -t, 0 )
2 ( -(w + s - e) / 2, 0 )
3 ( (w + s + e) / 2, w + s )
4 ( t, w + s )
5 ( t, 2w + s )
6 ( (w + s - e) / 2, 2w + s )
7 ( -(w + s + e) / 2, w )
8 ( -t, w )

其中$e=(\sqrt{2}-1)w$,相反方向的可以通过adjust函数实现。

线圈边缘设计

此部分由函数create_coil实现。

线圈边缘如图

coil

对应坐标为

位置 坐标
1 ( t, Dh + Dd - w )
2 ( Dw - e < Dw + Dd - w ? Dw - e : Dw + Dd - w, Dh + Dd - w )
3 ( Dw + Dd - w, Dh - e < Dh + Dd - w ? Dh - e : Dh + Dd - w )
4 ( Dw + Dd - w, -Dh + e > -Dh - Dd + w ? -Dh + e : -Dh - Dd + w )
5 ( Dw - e < Dw + Dd - w ? Dw - e : Dw + Dd - w, - Dh - Dd + w )
6 ( t, - Dh - Dd + w )
7 ( t, - Dh - Dd )
8 ( Dw, - Dh - Dd )
9 ( Dw + Dd, - Dh )
10 ( Dw + Dd, Dh )
11 ( Dw, Dh + Dd )
12 ( t, Dh + Dd )

相反方向的可以通过adjust函数实现。

延展设计

此部分由函数create_tj实现。

延展部分和交错部分非常相似,其特殊之处在于两侧可宽度不同

coil

为了使图形尽可能平衡,令上、下的内侧长度均为$a_l$,中间线宽为两侧均值,有

由此可解出各点坐标。

多圈绘制

此部分由函数create_diff_inductor实现。

其核心在于计算每圈的Dw, Dd, Dh,如下:

1
2
3
4
5
6
7
8
9
10
11
12
decl ew = grid_ceil2( W * (sqrt(2) - 1) );
decl es = grid_ceil2( S * (sqrt(2) - 1) );
decl newDw = grid_floor2( Dw - ew - es );
decl newDh = grid_floor2( Dh - ew - es );
decl newDd = grid_floor2( Dd - W - S + ew + es );
// 支持矩形电感
if(newDd < 0)
{
newDw = newDw + newDd;
newDh = newDh + newDd;
newDd = 0;
}

结果

绘制出的电感如下图

ind

可以看出,其具有电感的完整功能,且各部分可调。同时也可以支持矩形(RD=0)或者矩形/八边形混合电感(RD较小)。

ind_square

此外,将两个电感组合起来,也可以轻松实现变压器。

transformer

附录

完整代码如下

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
load("vias.ael");

defun grid_ceil2(input)
{
return ceil(100*input)/100.0;
}

defun grid_floor2(input)
{
return floor(100*input)/100.0;
}

defun adjust(l, xc, yc, x, y)
{
decl i = 0;
decl len = listlen( l );
for( i = 0; i < len; i++ )
{
decl t = list(l[i][0] * xc + x, l[i][1] * yc + y);
repla(l, t, i);
}
}

defun create_crossing(context, layerM1, w, s, t, x, y, xc, yc)
{
e = grid_ceil2( w * (sqrt(2) - 1) );
decl x1 = (w + s - e) / 2.0;
decl x2 = (w + s + e) / 2.0;
decl coors = list(
list(- t, 0),
list(- x1 , 0),
list(x2, w + s),
list(t, w + s),
list(t, 2 * w + s),
list(x1, 2 * w + s),
list(- x2, w),
list(- t, w),
list(- t, 0)
);
adjust(coors, xc, yc, x, y);
decl newPolygon = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM1, newPolygon);
}

defun create_coil(context, layerM, Dw, Dh, Dd, w, t, x, y)
{
decl e = grid_ceil2( w * (sqrt(2) - 1) );
decl coors = list(
list(t, Dh + Dd - w),
list(Dw - e < Dw + Dd - w ? Dw - e : Dw + Dd - w, Dh + Dd - w),
list(Dw + Dd - w, Dh - e < Dh + Dd - w ? Dh - e : Dh + Dd - w),
list(Dw + Dd - w, -Dh + e > -Dh - Dd + w ? -Dh + e : -Dh - Dd + w),
list(Dw - e < Dw + Dd - w ? Dw - e : Dw + Dd - w, - Dh - Dd + w),
list(t, - Dh - Dd + w),
list(t, - Dh - Dd),
list(Dw, - Dh - Dd),
list(Dw + Dd, - Dh),
list(Dw + Dd, Dh),
list(Dw, Dh + Dd),
list(t, Dh + Dd),
list(t, Dh + Dd - w)
);
adjust(coors, 1, 1, x, y);
decl newPolygon = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM, newPolygon);

adjust(coors, -1, 1, 0, 0);
decl newPolygon2 = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM, newPolygon2);
}

defun create_tj(context, layerM, w1, s1, t, w2, s2, e, x, y, dir)
{
decl d = s2 - s1;
decl ar = grid_floor2( 0.5 * ( sqrt(2)/2 * (w1 + w2) - w1 +e - s2 + s1 ) );
decl al = e - ar - s2 + s1;
decl coors = list(
list(-t, 0),
list(-s1, 0),
list(-s1, -w1 - ar),
list(-s2, -w1 - ar - (s2-s1)),
list(-s2, -w1 - e),
list(-s2 - w2, -w1 - e),
list(-s2 - w2, -w1 - al - (w2+s2-w1-s1)),
list(-s1 - w1, -w1 - al),
list(-s1 - w1, -w1),
list(-t, -w1),
list(-t, 0)
);

db_create_pin(context, x-s2-w2/2, pow(-1, dir)*(y+e+w1), pow(-1, dir)*90, layerM);
db_create_pin(context, x+s2+w2/2, pow(-1, dir)*(y+e+w1), pow(-1, dir)*90, layerM);

adjust(coors, 1, pow(-1, dir+1), x, pow(-1, dir)*y);
decl newPolygon = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM, newPolygon);

adjust(coors, -1, 1, 0, 0);
decl newPolygon2 = db_create_primitive_polygon(coors);
db_create_polygon(context, layerM, newPolygon2);
}

defun create_diff_inductor(context, MT, MB, D, W, S, C, RH, RD, s1, w2, s2, e, x, y, dir, combine)
{
decl AP = db_get_layerid(context, "AP", "drawing");
decl M9 = db_get_layerid(context, "M9", "drawing");
decl M8 = db_get_layerid(context, "M8", "drawing");
decl M7 = db_get_layerid(context, "M7", "drawing");
decl M6 = db_get_layerid(context, "M6", "drawing");
decl M5 = db_get_layerid(context, "M5", "drawing");
decl M4 = db_get_layerid(context, "M4", "drawing");
decl M3 = db_get_layerid(context, "M3", "drawing");
decl M2 = db_get_layerid(context, "M2", "drawing");
decl M1 = db_get_layerid(context, "M1", "drawing");
decl Metals = list(M1, M1, M2, M3, M4, M5, M6, M7, M8, M9, AP);
decl via9 = db_get_layerid(context, "via9", "drawing");
decl via8 = db_get_layerid(context, "via8", "drawing");
decl via7 = db_get_layerid(context, "via7", "drawing");
decl via6 = db_get_layerid(context, "via6", "drawing");
decl via5 = db_get_layerid(context, "via5", "drawing");
decl via4 = db_get_layerid(context, "via4", "drawing");
decl via3 = db_get_layerid(context, "via3", "drawing");
decl via2 = db_get_layerid(context, "via2", "drawing");
decl via1 = db_get_layerid(context, "via1", "drawing");
decl Vias = list(via1, via1, via2, M3, M4, M5, M6, M7, M8, M9, AP);

decl layerM1 = Metals[MT];
decl layerM2 = Metals[MB];

decl i = 0;
decl Dw = grid_ceil2( D / 2 );
decl Dh = grid_ceil2( D / 2 * RH );
decl Dd = grid_ceil2( D * RD / sqrt(2) );
decl ew = grid_ceil2( W * (sqrt(2) - 1) );
decl es = grid_ceil2( S * (sqrt(2) - 1) );

decl iDW = Dw;
for(i = 0; i < C - 1; i++)
iDW = grid_floor2( iDW - ew - es );
decl wv = grid_floor(iDW - (1 + (W + S + ew) / 2));
if(wv > 2 * W)
wv = 2 * W;
decl t = grid_floor( wv + 1 + (W + S + ew) / 2 );

create_tj(context, layerM1, W, s1, t, w2, s2, e, x, y+Dh+Dd-W, dir);

for(i = 0; i < C; i++)
{
create_coil(context, layerM1, Dw, Dh, Dd, W, t, x, y);
decl newDw = grid_floor2( Dw - ew - es );
decl newDh = grid_floor2( Dh - ew - es );
decl newDd = grid_floor2( Dd - W - S + ew + es );
if(newDd < 0)
{
newDw = newDw + newDd;
newDh = newDh + newDd;
newDd = 0;
}
decl cs = Dh + Dd - newDh - newDd - W;
if(i != C - 1)
{
if(dir == 0)
{
create_crossing(context, layerM1, W, cs, t, x, y-Dh-Dd, 1, 1);
create_crossing(context, layerM2, W, cs, t, x, y-Dh-Dd, -1, 1);
via_fill7to9(MB, MT, wv, W, 0.3, 0.3, 0.3, 0.3, x-t, y-(Dh+Dd)+W+cs, combine);
via_fill7to9(MB, MT, wv, W, 0.3, 0.3, 0.3, 0.3, x+t-wv, y-(Dh+Dd), combine);
}
if(dir == 1)
{
create_crossing(context, layerM1, W, cs, t, x, y+Dh+Dd, 1, -1);
create_crossing(context, layerM2, W, cs, t, x, y+Dh+Dd, -1, -1);
via_fill7to9(MB, MT, wv, W, 0.3, 0.3, 0.3, 0.3, x-t, y+Dh+Dd-2*W-cs, combine);
via_fill7to9(MB, MT, wv, W, 0.3, 0.3, 0.3, 0.3, x+t-wv, y+Dh+Dd-W, combine);
}
}
else
db_add_rectangle(context, layerM1, x-t, y+pow(-1, dir+1)*(Dh+Dd), x+t, y+pow(-1, dir+1)*(Dh+Dd-W));
Dw = newDw;
Dh = newDh;
Dd = newDd;
dir = 1 - dir;
}
}

defun create_diff_inductor2(context, MT, MB, D, W, S, C, RH, RD, s1, w2, s2, e, x, y, dir, combine)
{
D = grid_ceil2( D * RH );
RH = 1 / RH;
RD = RD * RH;
create_diff_inductor(context, MT, MB, D, W, S, C, RH, RD, s1, w2, s2, e, x, y, dir, combine);
}

defun diff_inductor(MT, MB, D, W, S, C, RH, RD, s1, w2, s2, e, dir, combine)
{
decl designContext = de_get_current_design_context();
decl mks2uu = db_get_mks_to_uu_factor(designContext);

D = D * mks2uu;
W = W * mks2uu;
S = S * mks2uu;
C = floor(C);
s1 = s1 * mks2uu;
w2 = w2 * mks2uu;
s2 = s2 * mks2uu;
e = e * mks2uu;
dir = floor(dir);
combine = floor(combine);

create_diff_inductor2(designContext, MT, MB, D, W, S, C, RH, RD, s1, w2, s2, e, 0, 0, dir, combine);
}
0%