TCAD学习笔记——CMI篇
前言
Sentaurus的Sdevice工具中可以使用Mixed-Mode来构建电路来对仿真器件进行相关电学仿真,里面也集成了很多spice模型,其中包括二极管,但是其中二极管模型是不含有反向恢复特性的,为了仿真的准确性,可以有很多解决方法:
专门跑一个一维的二极管仿真,来拟合目标参数:IV特性、反向恢复时间等等。
建立一个类似spice模型的二极管,描述其特性的电学方程包含反向恢复特性的模型。
方法一在校准上可能会比较麻烦,因为不能所见即所得地调整二极管的参数,需要调整各种掺杂浓度等参数来拟合需要的目标参数,如反向恢复时间和最大反向恢复电流。
方法二需要用到CMI(Compact Model Interface).
CMI食用方法
CMI的使用和PMI(Physical Model Interface)类似,相关的基本概念可参考该视频:
手册cm_ug.pdf也介绍了其使用方法,并且给了一个互感器件的例子,其核心就是把器件物理模型的微分方程表示出来。
对于含反向恢复的spice二极管模型,文献Practical implementation of diode SPICE model with reverse recovery用spice电路来实现了反向恢复二极管模型,并且可以使用datasheet的参数拟合该反向恢复二极管模型。
根据上述文献,描述反向恢复二极管的方程组为:
约去q_E和q_M可得微分方程:
这里用u_1-u_2代替u(t);i_1代替i(t),用于匹配CMI例子中电压节点和电流的相关定义。
根据上式就可得到其中的未知量z(t),transient right-hand side: q\left(t,z\left(t\right)\right),DC right-hand side: f\left(t,z\left(t\right)\right)以及各自的雅可比矩阵:
根据方程修改相应的C文件后编译即可,不懂C也没问题,这里关键的只是相关公式的键入。这里我把这个模型命名为RRDiode,C文件代码如下:
TL;DR
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "CMIModels.h"
#include "CMISupport.h"
class Parameters {
public:
double is;
double tau;
double tm;
double n;
double vt;
Parameters ();
void Set (const CCMBaseParam* param);
void Get (CCMBaseParam* param);
};
Parameters::Parameters () :
is (0.0004614982),
tau (5.909138e-8),
tm (3.181049e-8),
n (2.460456),
vt (0.03430948)
{
}
void Parameters::Set (const CCMBaseParam* param)
{ if (param->Defined ()) {
if (strcmp (param->Name (), "is") == 0) {
is = *param;
} else if (strcmp (param->Name (), "tau") == 0) {
tau = *param;
} else if (strcmp (param->Name (), "tm") == 0) {
tm = *param;
} else if (strcmp (param->Name (), "n") == 0) {
n = *param;
} else if (strcmp (param->Name (), "vt") == 0) {
vt = *param;
}
}
}
void Parameters::Get (CCMBaseParam* param)
{ if (strcmp (param->Name (), "is") == 0) {
*param = is;
param->Defined (1);
} else if (strcmp (param->Name (), "tau") == 0) {
*param = tau;
param->Defined (1);
} else if (strcmp (param->Name (), "tm") == 0) {
*param = tm;
param->Defined (1);
} else if (strcmp (param->Name (), "n") == 0) {
*param = n;
param->Defined (1);
} else if (strcmp (param->Name (), "vt") == 0) {
*param = vt;
param->Defined (1);
} else {
param->Defined (0);
}
}
class Device {
public:
Parameters par;
};
class PSet {
public:
Parameters par;
};
class Instance {
public:
Parameters par;
double m;
};
extern "C" void
cmi_device_create (CCMBaseDevice*const device)
{ device->device_desc = new Device;
}
extern "C" void
cmi_device_set_param (CCMBaseDevice*const device,
const CCMBaseParam*const param)
{ Device* dev = (Device*) device->device_desc;
dev->par.Set (param);
}
extern "C" void
cmi_device_initialize (CCMBaseDevice*const device)
{
}
extern "C" void
cmi_device_get_param (CCMBaseDevice*const device,
CCMBaseParam*const param)
{ Device* dev = (Device*) device->device_desc;
dev->par.Get (param);
}
extern "C" void
cmi_device_delete (CCMBaseDevice*const device)
{ delete (Device*) device->device_desc;
}
extern "C" void
cmi_pset_create (CCMBasePSet*const pset)
{ pset->pset_desc = new PSet;
}
extern "C" void
cmi_pset_set_param (CCMBasePSet*const pset,
const CCMBaseParam*const param)
{ PSet* ps = (PSet*) pset->pset_desc;
ps->par.Set (param);
}
extern "C" void
cmi_pset_initialize (CCMBasePSet*const pset)
{
}
extern "C" void
cmi_pset_get_param (CCMBasePSet*const pset,
CCMBaseParam*const param)
{ PSet* ps = (PSet*) pset->pset_desc;
ps->par.Get (param);
}
extern "C" void
cmi_pset_delete (CCMBasePSet*const pset)
{ delete (PSet*) pset->pset_desc;
}
extern "C" void
cmi_instance_create (CCMBaseInstance*const instance)
{ instance->instance_desc = new Instance;
}
extern "C" void
cmi_instance_set_param (CCMBaseInstance*const instance,
const CCMBaseParam*const param)
{ Instance* inst = (Instance*) instance->instance_desc;
inst->par.Set (param);
}
extern "C" void
cmi_instance_initialize (CCMBaseInstance*const instance)
{ Instance* inst = (Instance*) instance->instance_desc;
inst->m = inst->par.is * (inst->par.tau * inst->par.tau);
}
extern "C" void
cmi_instance_get_param (CCMBaseInstance*const instance,
CCMBaseParam*const param)
{ Instance* inst = (Instance*) instance->instance_desc;
inst->par.Get (param);
}
#define u1 (variables [0])
#define u2 (variables [1])
#define i1 (variables [2])
extern "C" void
cmi_instance_get_rhs (CCMBaseInstance*const instance,
const int dc, const double time,
const double*const variables,
double*const rhs)
{ Instance* inst = (Instance*) instance->instance_desc;
if (dc) {
rhs [0] = i1;
rhs [1] = -i1;
rhs [2] = inst->par.is * (exp((u1-u2) / inst->par.n / inst->par.vt) - 1) - (inst->par.tm / inst->par.tau + 1) * i1;
} else {
rhs [2] = inst->par.is * inst->par.tau * exp((u1-u2) / inst->par.n / inst->par.vt) - inst->par.tm * i1;
// check of callback into Sentaurus Device
double starttime = cmi_starttime ();
}
}
extern "C" void
cmi_instance_get_jacobian (CCMBaseInstance*const instance,
const int dc, const double time,
const double*const variables,
double*const*const jacobian)
{ Instance* inst = (Instance*) instance->instance_desc;
if (dc) {
jacobian[0][2] = 1.0;
jacobian[1][2] = -1.0;
jacobian[2][0] = inst->par.is / inst->par.n / inst->par.vt * exp((u1-u2) / inst->par.n / inst->par.vt);
jacobian[2][1] = -1.0 * inst->par.is / inst->par.n / inst->par.vt * exp((u1-u2) / inst->par.n / inst->par.vt);
jacobian[2][2] = -1.0 * (inst->par.tm + inst->par.tau) / inst->par.tau;
} else {
jacobian[2][0] = inst->par.is * inst->par.tau / inst->par.n / inst->par.vt * exp((u1-u2) / inst->par.n / inst->par.vt);
jacobian[2][1] = -1.0 * inst->par.is * inst->par.tau / inst->par.n / inst->par.vt * exp((u1-u2) / inst->par.n / inst->par.vt);
jacobian[2][2] = -1.0 * inst->par.tm;
}
}
extern "C" int
cmi_instance_is_physical (CCMBaseInstance*const instance,
const double time,
const double*const variables)
{ return 1;
}
extern "C" void
cmi_instance_delete (CCMBaseInstance*const instance)
{ delete (Instance*) instance->instance_desc;
}编译该c文件得到一RRDiode.so.linux64文件,除此之外还需要写一个RRDiode.ccf文件,内容参照例子写即可:
DEVICE RRDiode
ELECTRODES
u1
u2
INTERNALS
i1
PARAMETERS
double is
double tau
double tm
double n
double vt
END DEVICE
PSET RRDiode_pset
DEVICE RRDiode
END PSET将编译完后的RRDiode.so.linux64和RRDiode.ccf放在某目录下,然后在Sdevice的system中就能使用该器件了:
File {
......
CMIPATH = "PATHtoCMI"
}
......
system {
Resistor_pset rs (1 234) {resistance = 0.02805803}
RRDiode_pset Dut (234 0) {is=1.53e-5 tau=9.966e-8 tm=9.120918e-8 n=2.093419 vt=0.03215437}
}
......is, tau, tm, n, vt 这五个参数可以从开头所述的文献Practical implementation of diode SPICE model with reverse recovery中拟合得到,这里设置的参数是从IGBT器件IKW20N65ET7内封装的的二极管参数拟合而来。
最后和IGBT器件一起Mix-Mode下跑一下开通瞬态仿真,反向恢复电流这不就出来了:
