利用 Spectre 生成 Gmid 数据库
学习Murmann《Systematic Design of Analog CMOS Circuits》过程中,需要首先仿真得到关于Gmid参数的数据库。该数据库在Matlab环境下生成。
我的仿真环境为:CentOS7、Matlab2018a、CadenceIC617。
该数据库是四维的数据库,四个维度分别为\(V_{DS}\)、\(V_{SB}\)、\(V_{GS}\)、\(L\)。
包含的参数有:
类别 | 详细内容 |
---|---|
电容信息C | CGG,CGS,CSG,CGD,CDG,CGB,CDD,CSS |
电流信息I | ID,IGD,IGS |
跨导信息GM | GM,GMB |
阈值电压VT | VT |
噪声信息S | STH,SFL |
利用书中提供的工具箱,利用该数据库即可完成相应设计。
下载/安装Matlab相应工具包
我将相应工具包上传到了这里,请点击下载。该工具箱包括《Systematic Design of Analog CMOS Circuits》书配套工具箱和Spectre数据读取工具箱。
- 下载Matlab相应工具包;
- 将工具包载入Matlab工作目录即可;
配置仿真.m脚本
仿真使用的脚本有两个,一个用来配置仿真参数,名字为cortemsweep_config.m;另一个用来执行仿真,名字为cortemsweep_spectre_run.m。
脚本中有一些路径需要修改成为自己电脑中的相应路径。
需要修改的地方我会写做“< XXX… > “,修改时请去掉左右尖括号< >。
例如:'< 修改为你模型所在的路径,例如:/home/allmodel.scs >' 修改为‘/home/allmodel.scs’。
cortemsweep_config.m脚本整体如下:
% Harroy 2020.09.02
% 该.m文件用来配置数据库仿真时的一些参数,注意输入参数需要输入相应工艺角和温度(摄氏度)
function c = cortemsweep_config(corner, temp)
% 仿真模型路径
c.modelfile = '< 修改为你模型所在的路径,例如:/home/allmodel.scs >';
c.modelinfo = '< 此处填写数据库信息,方便查阅,不对数据库数据产生影响,例如:180nm CMOS>';
c.corner = corner;
c.temp = temp;
% 模型名字,对应Spectre相应仿真中器件的模型名。
c.modeln = 'nch';
c.modelp = 'pch';
% 数据库存储名字和位置
c.savefilen = sprintf("< NMOS数据库存储名字和位置,例如:/home/180_nch_%d_%s >",c.temp, c.corner);
c.savefilep = sprintf("< PMOS数据库存储名字和位置,例如:/home/180_pch_%d_%s >",c.temp, c.corner);
% 该仿真是通过在matlab中调用spectre的方式实现的,下面是关于调用的命令,配合system函数使用。
c.simcmd = '< Spectre软件所在路径,若环境变量已配置好,直接输入Spectre即可 > -64 < 本.m文件自动生成仿真网表文件的路径,在本.m文件最后位置更改或查看 > +log < 填写log文件存在位置,可以查看仿真中遇到的问题,例如:/home/techsweep.out >';
c.outfile = '< 仿真网表文件存放路径,后面会读取该文件,建议和数据库放一起。例如:/home/techsweep.raw >';
c.sweep = 'sweepvds_sweepvgs-sweep';
c.sweep_noise = 'sweepvds_noise_sweepvgs_noise-sweep';
% 扫描仿真的信息,可以自行修改自己为所需数据
c.VGS_step = 100e-3;
c.VDS_step = 100e-3;
c.VSB_step = 0.1;
c.VGS_max = 1.8;
c.VDS_max = 1.8;
c.VSB_max = 1.0;
c.VGS = 0:c.VGS_step:c.VGS_max;
c.VDS = 0:c.VDS_step:c.VDS_max;
c.VSB = 0:c.VSB_step:c.VSB_max;
c.LENGTH = [(0.18:0.02:0.5) (0.6:0.1:2.0)];
c.WIDTH = 4;
c.NFING = 4;
% 变量排布,以便后面读取数据,不熟悉请不要修改
c.outvars = {'ID','VT','IGD','IGS','GM','GMB','GDS','CGG','CGS','CSG','CGD','CDG','CGB','CDD','CSS'};
c.n{1}= {'mn:ids','A', [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.n{2}= {'mn:vth','V', [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.n{3}= {'mn:igd','A', [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.n{4}= {'mn:igs','A', [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 ]};
c.n{5}= {'mn:gm','S', [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 ]};
c.n{6}= {'mn:gmbs','S', [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 ]};
c.n{7}= {'mn:gds','S', [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 ]};
c.n{8}= {'mn:cgg','F', [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ]};
c.n{9}= {'mn:cgs','F', [0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 ]};
c.n{10}={'mn:cgd','F', [0 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 ]};
c.n{11}={'mn:cgb','F', [0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 ]};
c.n{12}={'mn:cdd','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]};
c.n{13}={'mn:cdg','F', [0 0 0 0 0 0 0 0 0 0 0 -1 0 0 0 ]};
c.n{14}={'mn:css','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]};
c.n{15}={'mn:csg','F', [0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 ]};
c.n{16}={'mn:cjd','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]};
c.n{17}={'mn:cjs','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]};
%
% {'ID','VT','IGD','IGS','GM','GMB','GDS','CGG','CGS','CSG','CGD','CDG','CGB','CDD','CSS'};
c.p{1}= {'mp:ids','A', [-1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.p{2}= {'mp:vth','V', [0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.p{3}= {'mp:igd','A', [0 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 ]};
c.p{4}= {'mp:igs','A', [0 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 ]};
c.p{5}= {'mp:gm','S', [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 ]};
c.p{6}= {'mp:gmbs','S', [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 ]};
c.p{7}= {'mp:gds','S', [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 ]};
c.p{8}= {'mp:cgg','F', [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ]};
c.p{9}= {'mp:cgs','F', [0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 ]};
c.p{10}={'mp:cgd','F', [0 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 ]};
c.p{11}={'mp:cgb','F', [0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 ]};
c.p{12}={'mp:cdd','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]};
c.p{13}={'mp:cdg','F', [0 0 0 0 0 0 0 0 0 0 0 -1 0 0 0 ]};
c.p{14}={'mp:css','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]};
c.p{15}={'mp:csg','F', [0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 ]};
c.p{16}={'mp:cjd','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]};
c.p{17}={'mp:cjs','F', [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]};
%
c.outvars_noise = {'STH','SFL'};
c.n_noise{1}= {'mn:id', ''};
c.n_noise{2}= {'mn:fn', ''};
%
c.p_noise{1}= {'mp:id', ''};
c.p_noise{2}= {'mp:fn', ''};
% 自动生成Spectre仿真网表,不熟悉请不要修改,注意其中有个路径需要修改
netlist = sprintf([...
'//techsweep.scs \n'...
'include "%s" section=%s\n'...
'include "< 仿真执行时的扫描网表位置,注意和另一脚本对应路径应该相同,例如:/home/techsweep_params.scs >\n'...
'save mn \n'...
'save mp \n'...
'parameters gs=0 ds=0 \n'...
'vnoi (vx 0) vsource dc=0 \n'...
'vdsn (vdn vx) vsource dc=ds \n'...
'vgsn (vgn 0) vsource dc=gs \n'...
'vbsn (vbn 0) vsource dc=-sb \n'...
'vdsp (vdp vx) vsource dc=-ds \n'...
'vgsp (vgp 0) vsource dc=-gs \n'...
'vbsp (vbp 0) vsource dc=sb \n'...
'\n'...
'//NOTE: YOUR MODELS SHOULD BE SET UP SUCH THAT THE STRESS PARAMS (SA, SB, etc.) ARE AUTOMATICALLY COMPUTED\n'...
'mn (vdn vgn 0 vbn) %s l=length*1e-6 w=%de-6 nf=%d \n'...
'mp (vdp vgp 0 vbp) %s l=length*1e-6 w=%de-6 nf=%d \n'...
'\n'...
'simOptions options gmin=1e-13 reltol=1e-4 vabstol=1e-6 iabstol=1e-10 temp=%d tnom=27 rawfmt=psfbin rawfile="%s" \n'...
'sweepvds sweep param=ds start=0 stop=%d step=%d { \n'...
' sweepvgs dc param=gs start=0 stop=%d step=%d \n'...
'}\n'...
'sweepvds_noise sweep param=ds start=0 stop=%d step=%d { \n'...
' sweepvgs_noise noise freq=1 oprobe=vnoi param=gs start=0 stop=%d step=%d \n'...
'}\n'...
], c.modelfile, c.corner, ...
c.modeln, c.WIDTH, c.NFING, ...
c.modelp, c.WIDTH, c.NFING, ...
c.temp, c.outfile,...
c.VDS_max, c.VDS_step, ...
c.VGS_max, c.VGS_step, ...
c.VDS_max, c.VDS_step, ...
c.VGS_max, c.VGS_step);
% 将网表写入文件保存
fid = fopen('<仿真网表文件存放路径,例如:/home/techsweep.scs' >, 'w');
fprintf(fid, netlist);
fclose(fid);
return
仿真执行脚本cortemsweep_spectre_run.m中也有一个路径需要修改:
% Harroy @ 2020.09.02
clearvars;
close all;
% corner表示当前仿真工艺角,temp表示摄氏温度,自行修改
corner = 'tt';
temp = 27;
% 载入配置脚本
c = cortemsweep_config(corner, temp);
% 载入扫描信息,无需修改
nch.INFO = c.modelinfo;
nch.CORNER = c.corner;
nch.TEMP = c.temp;
nch.NFING = c.NFING;
nch.L = c.LENGTH';
nch.W = c.WIDTH;
nch.VGS = c.VGS';
nch.VDS = c.VDS';
nch.VSB = c.VSB';
%
pch.INFO = c.modelinfo;
pch.CORNER = c.corner;
pch.TEMP = c.temp;
pch.NFING = c.NFING;
pch.L = c.LENGTH';
pch.W = c.WIDTH;
pch.VGS = c.VGS';
pch.VDS = c.VDS';
pch.VSB = c.VSB';
% 开始仿真
for i = 1:length(c.LENGTH)
str=sprintf('L = %2.3f', c.LENGTH(i));
disp(str);
tic
for j = 1:length(c.VSB)
% 写入扫描参数
fid=fopen('< 仿真执行时的扫描网表位置,注意和另一脚本对应路径应该相同,例如:/home/techsweep_params.scs >', 'w');
fprintf(fid,'parameters length = %d\n', c.LENGTH(i));
fprintf(fid,'parameters sb = %d\n', c.VSB(j));
fclose(fid);
pause(5)
% 调用Spectre
[status,result] = system(c.simcmd);
if(status)
disp('Simulation did not run properly. Check techsweep.out.')
return;
end
% 初始化数据库
for m = 1:length(c.outvars)
nch.(c.outvars{m})(i,:,:,j) = zeros(length(c.VGS), length(c.VDS));
pch.(c.outvars{m})(i,:,:,j) = zeros(length(c.VGS), length(c.VDS));
end
% 读取并存储常规数据
for k = 1:length(c.n)
params_n = c.n{k};
struct_n = cds_srr(c.outfile, c.sweep, params_n{1});
values_n = struct_n.(params_n{2});
params_p = c.p{k};
struct_p = cds_srr(c.outfile, c.sweep, params_p{1});
values_p = struct_p.(params_p{2});
for m = 1:length(c.outvars)
nch.(c.outvars{m})(i,:,:,j) = squeeze(nch.(c.outvars{m})(i,:,:,j)) + values_n*params_n{3}(m);
pch.(c.outvars{m})(i,:,:,j) = squeeze(pch.(c.outvars{m})(i,:,:,j)) + values_p*params_p{3}(m);
end
end
% 读取并存储噪声数据
for k = 1:length(c.n_noise)
params_n = c.n_noise{k};
% note: using cds_innersrr, since cds_srr is buggy for noise
struct_n = cds_innersrr(c.outfile, c.sweep_noise, params_n{1},0);
field_names = fieldnames(struct_n);
values_n = struct_n.(field_names{4});
params_p = c.p_noise{k};
% note: using cds_innersrr, since cds_srr is buggy for noise
struct_p = cds_innersrr(c.outfile, c.sweep_noise, params_p{1},0);
field_names = fieldnames(struct_p);
values_p = struct_p.(field_names{4});
nch.(c.outvars_noise{k})(i,:,:,j) = squeeze(values_n);
pch.(c.outvars_noise{k})(i,:,:,j) = squeeze(values_p);
end
end
toc
end
save(c.savefilen, 'nch');
save(c.savefilep, 'pch');
脚本修改完成后,即可执行cortemsweep_spectre_run.m脚本开始仿真。
仿真可能遇到的问题
Matlab输出
“Simulation did not run properly. Check techsweep.out.“
查看Spectre生成的Log文件发现如下提示
“lmStatus: ERROR (LMC-01902): License call failed. The license server search path is defined as
. Can't find license file.“
- 此处提示没有正确载入Spectre的Licenses,一般是由于没有正确载入环境变量导致,从桌面快捷方式打开的Matlab仿真时会出现这种位问题。建议在终端Terminal中输入matlab执行仿真。
运行时提示缺少“libsrr.so“
-
由于我的Cadence软件是直接拷贝过来的,而不是正常的安装,因此会导致关于Cadence 的动态连接库无法找到。此时需要将Cadence/Spectre中的动态链接库放入自己系统的环境中。
# 1. 首先找到libsrr.so所在的位置 locate libsrr.so # 2. 在结果中复制该文件所在文件夹的位置,例如复制/opt/cadence/installs/SPECTRE181/tools.lnx86/lib # 3. 进入系统动态连接库所在位置 cd /etc/ld.so.conf.d/ # 4. 新建配置文件或复制配置文件 sudo cp libiscsi-x86_64.conf spectre.conf # 5. 将之前复制的位置/opt/cadence/installs/SPECTRE181/tools.lnx86/lib替换进去 # 6. 重新配置动态连接库 sudo ldconfig
此时动态连接库配置完成,重启Matlab仿真即可。