使用python脚本玩转古早TCAD软件(待更新)
前言
TCAD(Technology Computer Aided Design),虽然原名中没有与半导体器件有关的词汇,但这种软件便是半导体工艺模拟及器件模拟的工具,可以说是EDA软件的一种。TCAD软件同其他EDA软件一样,底层需要复杂的数学模型和数物模型支撑,能大幅减少半导体制造的研发成本,为新型半导体器件提供初步验证,是半导体器件研发中不可或缺的仿真软件。
这种软件大多使用自己开发的脚本语言进行编程仿真,少有图形化的操作界面,地球上几乎没有系统讲解TCAD软件的使用教程,即便市面上有一些教程,也只是粗略介绍了软件的基本使用,唯一的学习资源就是软件附带的文档,笔者在今年5月份有幸接触到人生中的第一款TCAD软件—Medici,使用该软件的目的是改变一个器件的某些参数后获取器件的击穿电压Bv,在使用过程中发现medici软件不仅仿真速度较慢而且需要频繁修改源文件去获取不同情况下的参数,大幅度延长了参数验证的时间,拖累科研进程,所以必须设法加快工作进程。

本文提供了一种利用python脚本让Medici根据用户所设参数自动获取结果的思路,笔者非计算机相关专业出生,脚本程序也只是笔者东平西凑的产物,但脚本确是提高了工作效率,本文也只注重讲解设计脚本的思路,不涉及器件源程序的相关知识。
实现原理
在设计python脚本的时候,将medici软件视为一个黑盒,这个黑盒是一个拥有一个输入和两个输出的盒子,当输入一个电压,这个盒子可以输出两个值,通过比对输出的两个值是否都在1附近来判断输入的电压是否正确。如下图所示:

由于判断输入的电压是否是正确的击穿电压是在仿真结束后才能进行判断,因此需要进行多次修改源文件的电压进行仿真,存在一定的试错成本,而且试错是一个不可避免的环节,所以笔者第一次设计的脚本也是一个暴力试错型的,下面是代码实现的流程图。

在上述所示的程序流程中,我最先输入了一个保守值进行仿真,为什么要输入这个保守值?由于笔者并没有深究Medici软件的数学模型,即便文档有提到也难以进行验证,在Medici软件提取电离值的时候,输出的结果是不固定的,这里的不固定说的是输出的内容不固定而不是指输出的数值不固定,当输入的电压小于一定值时,提取电离值语法会输出一些难以预料的内容,读者可以自行尝试,而即便是输出了自己想要的内容也离预期值过远,大大增加了试错的成本,所以这里我输入的保守值其实是一个“便于后续处理的数值”,如果不从一个“便于后续处理的数值(经验值)”开始仿真,从0开始仿真的话是非常浪费时间的。因此,需要先根据一定的经验先输入一个保守值进行仿真,以便后续流程的处理。
脚本以这种运行方式运行往往需要多次修改电压输入后再运行多次仿真后才能得到结果,并且仿真次数与预期电压和我们输入的保守值(经验值)有关,也就是说我们输入的保守值(经验值)越接近预期电压其仿真所需要的次数也就越少,这样做的缺点是很明显的,在不同条件下器件的击穿电压应该是不一样的,也就是说需要试错的次数也不一样,不推荐这种做法。
但是在使用上述的方法编写脚本后我发现了一些规律,如下图所示

可以看出,我是在保守值得基础上增加1伏电压或是减少一伏电压让保守值去逼近目标值,而上图数据所示我输入的保守值是小于预期值的,因此只需每次加1伏电压否判断是否符合预期结果后输出就行(这里我为了方便演示选择了每次仿真结束后都输出结果),如上图所示,为得到预期结果我的脚本一共仿真了27次,这是特别浪费时间的,是否存在更简单的仿真方式?
图中的结果已经给出了答案,在图中可以看出,我每增加1伏电压对电离值所产生的增量是比较相近的,如果我先用保守值进行一次仿真然后再在保守值的基础上增加1伏后进行仿真后,得到这两次的电离值的差值也就得到了这1伏电压增量对结果引起的电离值的增量,随后我计算目前的电离值距离预期电离值还有多少个这样的增量,再增加这些量后进行仿真是不是就能快速达到预期结果了?改进后的程序设计流程如下所示:

这样便能快速逼近预期值,节省了不少时间,以下是改进后的脚本输出的结果图

可以看到仿真的次数大大减少了,下面讲解具体的代码实现
代码实现
引言
虽然流程图里的步骤并不复杂,但我们需要组成流程图里的组分
由于笔者不是计算机专业出生的,但也略懂些编程的思路,这里我采用“自下而上”的编程思路,简单来说也就是先实现一些小功能,再将这些功能组合成逻辑运行。
在手动仿真的过程中,我发现经常需要执行以下几个步骤:
1. 打开源文件,填入电压,关闭源文件
2. 在源文件的路径中让shell执行“medici 源文件名”的命令
3. 查看文件后缀是".out"的输出文件,判断结果是否符合预期
也就是说我的脚本需要以下功能:
1. 获得源文件路径
2. 在源文件中找到找到自己想要的信息
3. 在源文件中删除我需要更改的信息
4. 在源文件中添改我想要的信息
5. 执行仿真
6. 输出我想要的信息
下面逐一讲解代码实现,再三声明,笔者非计算机专业出生,脚本代码只是笔者东平西凑的产物,这个份产物对于专业人士而言就是所谓的”屎山“,但脚本确实提高了笔者的工作效率,不喜勿喷。
一、获得源文件路径
代码
windows环境
import os
fileDir = os.path.abspath('.') # 获取文件目录路径
FileName_Source = 'soi.txt' # 源文件名(带拓展名)
FilePath_Source = fileDir + "\\" + FileName_Source # 获取源文件路径
print(FilePath_Source)
Linux环境
import os
fileDir = os.path.abspath('.') # 获取文件目录路径
FileName_Source = 'soi.txt' # 源文件名(带拓展名)
FilePath_Source = fileDir + "/" + FileName_Source # 获取源文件路径
print(FilePath_Source)
这里需要注意两个地方
- 由于脚本是在windows环境下编写的,任何文件都需要有一个后缀名,而Linux环境下文件可以没有后缀名,程序的源文件一般为纯文本类型,这里在windows环境下选择了“.txt”后缀。
- Linux环境下文件的路径中的斜杠的方向与windows中的是相反的,如果需要在linux环境下运行则需要将"\"改成"/"
测试结果:
windows环境
C:\Users\73812\Desktop\半导体器件脚本\soi.txt
Process finished with exit code 0
Linux环境
[tcad@localhost Blog]$ python3 Auto_Sim_Script.py
/home/tcad/Medici_Project/Blog/soi.txt
小结
可以看出文件路径中的斜杆是相反的,该代码能成功获取源文件路径。至此,获得源文件路径功能的讲解结束
二、在源文件中找到找到自己想要的信息
代码
import os
fileDir = os.path.abspath('.') # 获取文件目录路径
FileName_Source = 'soi.txt' # 源文件名(带拓展名)
FilePath_Source = fileDir + "\\" + FileName_Source # 获取源文件路径
KeyWord_Nd = '$Nd' # 查找目标关键字
def seek_target_line(FilePath, KeyWord):
'''
这个函数的功能是寻找目标关键字所在的行
:param FilePath:文件路径
:param KeyWord:文件中的关键词
:return:target_line:返回查找的目标所在的行数
'''
with open(FilePath, encoding="utf8") as file:
lines = file.readlines()
count_line = 0
target_line = 0
for line in lines:
count_line += 1
if KeyWord in line:
target_line = count_line
del lines[:]
return target_line
print(FilePath_Source)
print(seek_target_line(FilePath_Source, KeyWord_Nd) + 1)
部分源文件图

测试结果
[tcad@localhost Blog]$ python3 Auto_Sim_Script.py
/home/tcad/Medici_Project/Blog/soi.txt
3
小结
我的目的是为了修改第源文件中第3行后进行仿真,所以我在上一行也就是第2行增加了一个注释便于我去定位第3行,通过运行结果可以看出名为"seek_target_line"的函数成功返回看关键字所在行。至此,在源文件中找到找到自己想要的信息讲解结束。
三、在源文件中删除我需要更改的信息
代码
import os
fileDir = os.path.abspath('.') # 获取文件目录路径
FileName_Source = 'soi.txt' # 源文件名(带拓展名)
FilePath_Source = fileDir + "/" + FileName_Source # 获取源文件路径
KeyWord_Nd = '$Nd' # 查找目标关键字
def seek_target_line(FilePath, KeyWord):
'''
这个函数的功能是寻找目标关键字所在的行
:param FilePath:文件路径
:param KeyWord:文件中的关键词
:return:target_line:返回查找的目标所在的行数
'''
with open(FilePath, encoding="utf8") as file:
lines = file.readlines()
count_line = 0
target_line = 0
for line in lines:
count_line += 1
if KeyWord in line:
target_line = count_line
del lines[:]
return target_line
def delete_line(FilePath, TargetLine):
'''
这个函数的功能是删除目标行
:param FilePath:文件路径
:param TargetLine:目标行
'''
with open(FilePath, 'r', encoding="utf8") as file:
lines = file.readlines()
del lines[TargetLine]
with open(FilePath, 'w', encoding="utf8") as file:
for line in lines:
file.write(line)
del lines[:]
delete_line(FilePath_Source, seek_target_line(FilePath_Source, KeyWord_Nd)+1)
执行程序的结果图
执行程序前:

执行程序后:

小结
我的目的是为了删除源文件的第3行,所以我在第3行的上方也就是第二行添加了一个注释以便于我去定位我需要删除的行,执行程序以后可以看到成功删除了第3行的内容。至此,在源文件中找到找到自己想要的信息讲解结束。
四、在源文件中添改我想要的信息
import os
fileDir = os.path.abspath('.') # 获取文件目录路径
FileName_Source = 'soi.txt' # 源文件名(带拓展名)
FilePath_Source = fileDir + "/" + FileName_Source # 获取源文件路径
KeyWord_Nd = '$Nd' # 查找目标关键字
Content_Nd = 'assign name=Nepi n.val=1e15'+ '\n'
def seek_target_line(FilePath, KeyWord):
'''
这个函数的功能是寻找目标关键字所在的行
:param FilePath:文件路径
:param KeyWord:文件中的关键词
:return:target_line:返回查找的目标所在的行数
'''
with open(FilePath, encoding="utf8") as file:
lines = file.readlines()
count_line = 0
target_line = 0
for line in lines:
count_line += 1
if KeyWord in line:
target_line = count_line
del lines[:]
return target_line
def delete_line(FilePath, TargetLine):
'''
这个函数的功能是删除目标行
:param FilePath:文件路径
:param TargetLine:目标行
'''
with open(FilePath, 'r', encoding="utf8") as file:
lines = file.readlines()
del lines[TargetLine]
with open(FilePath, 'w', encoding="utf8") as file:
for line in lines:
file.write(line)
del lines[:]
def Insert_String(FileName, TargetLine, Content):
lines = []
with open(FileName, "r", encoding="utf8") as y:
for line in y:
lines.append(line)
y.close()
lines.insert(TargetLine, Content)
s = ''.join(lines)
with open(FileName, "w", encoding="utf8") as z:
z.write(s)
z.close()
# 清空列表:
del lines[:]
Insert_String(FileName_Source, seek_target_line(FilePath_Source, KeyWord_Nd), Content_Nd)
执行程序的结果图
执行程序前:


【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)