利用 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数据读取工具箱。

  1. 下载Matlab相应工具包;
  2. 将工具包载入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仿真即可。

posted @ 2020-09-05 16:53  Harroy  阅读(1334)  评论(1编辑  收藏  举报