Appearance
SWB 附录 H:Tcl 命令块
来源:
swb_ug.pdfAppendix H(W-2024.09) 原文标题:Tcl Command Blocks说明:本页按 2024 版附录 H 的原顺序整理,尽量完整保留 Tcl 命令块的写法、求值机制、适用场景与限制。
附录说明
本附录讨论如何使用 Tcl 命令块。
不过手册一开头就给出非常明确的警告:Tcl 命令块会对项目预处理和执行性能产生负面影响,而且在新的 SWB 项目里通常已经不再需要。因此,手册明确建议不要在新的 Sentaurus Workbench 项目中继续使用 Tcl 命令块。
这意味着,这篇附录更适合在以下场景中查阅:
- 维护旧项目
- 理解历史项目为什么这样写
- 需要兼容旧的 Tcl block 逻辑
创建 Tcl 命令块
手册说明,Tcl 命令块由任意数量的 Tcl 命令行组成,块边界由 !( 和 )! 符号限定。
在项目预处理的最后阶段,预处理器会把这些 Tcl 块提取出来并求值。随后,在节点输入文件中,每个 Tcl 块都会被其 Tcl 求值后的标准输出替换。
典型示例
手册用一个 Sentaurus Device 输入文件片段举例,展示 Tcl 命令块如何参与压电 / 自发极化计算,并把结果回写到 SWB 变量中。原文代码很长,这里保留其核心结构:
text
#if @<polarization>@ == "on"
!(
...
set q 1.602e-19
set Psp_AlN ...
set Psp_GaN ...
set Psp_AlGaN ...
set DPsp ...
set e33i ...
set e31i ...
set c13i ...
set c33i ...
set straini ...
set Ppz_AlGaN ...
set DPpz ...
set intCharge ...
set SWB_VARIABLES(Charge) [format %.6e $intCharge]
)!
* Spontaneous polarization for AlGaN: !(puts -nonewline [format %.2e $Psp_AlGaN])!
...
#endif
...
Physics(materialinterface="GaN/AlGaN")
{
#if @<polarization>@ == "on"
Charge( Conc= !(puts -nonewline [format %.4e $intCharge])! )
#endif
...
}这个例子的核心意思是:
- 在 Tcl 块里先完成一系列中间量计算
- 再通过
puts把需要写回节点输入文件的值输出出来 - 还可以通过
SWB_VARIABLES(...)把结果写成 SWB 变量
预处理后的结果
手册接着展示该文件预处理后的节点文件效果。也就是说,原来夹在 Tcl 块里的表达式,最终会被替换成已经求值后的具体数字。例如:
text
* Spontaneous polarization for AlGaN: -5.06e+13
* Piezopolarization for AlGaN: -1.85e+13
* Total AlGaN Polarization: -6.91e+13
* Total GaN Polarization: -1.81e+13
...
Physics(materialinterface="GaN/AlGaN")
{
Charge( Conc=1.3925e+13 )
...
}没有输出时会发生什么
手册特别提醒:如果一个 Tcl 命令块没有打印任何内容,也就是没有使用 Tcl puts 命令,那么 Tcl 求值的标准输出就是空的。
这意味着在预处理后的节点输入文件里,这个块不会产生任何文本输出。
预处理 Tcl 命令块
手册说明,Sentaurus Workbench 预处理器会在项目预处理的最终阶段自动求值 Tcl 命令块。
它的处理顺序是:
- 先完成预处理器
#命令的求值 - 再完成
@引用和@表达式的解析 - 最后才开始求值 Tcl 命令块
这也是为什么正文和附录 A 一直强调 Tcl blocks 不是“第一阶段语法”,而是最后阶段语法。
实验级求值规则
手册给出一个非常重要的规则:Tcl 命令块遵循“按实验求值”的规则。
也就是说,属于同一 experiment 的各节点输入文件中的 Tcl 块,会在同一个 Tcl 解释器上下文中,从第一个节点一直按顺序求值到最后一个节点。
这样带来的结果是:
- 如果你在 experiment 的第一个节点 Tcl 块里定义了某个 Tcl 变量
- 那么同一 experiment 中其他节点的 Tcl 块都可以继续访问它
这也是 Tcl block 能表达“按实验累积状态”的原因。
不能出现在 # 命令中
手册明确指出:Tcl 命令块不能用在预处理器 # 命令中。
原因很简单:Tcl 命令块只会在项目预处理的最终阶段求值,而 # 命令需要更早被求值。因此,如果把 Tcl 块放进 # 命令里,# 命令的求值就会失败。
手册给出的错误用法示例是:
text
#include "!(puts -nonewline "[file join /the/path myfile.cmd]")!"在这种情况下,手册建议改用 @ 表达式:
text
#include "@[file join /the/ myfile.cmd]@"显式进行 Tcl 预处理
为了缩短预处理时间,手册说明你也可以显式只做 Tcl 预处理。可用方式包括:
- 选择
Project > Operations > Preprocess Tcl Blocks - 按
Ctrl+B - 右键项目后选择
Project > Preprocess Tcl Blocks
在这种模式下,只有工具输入文件中的 Tcl 命令块会被 Tcl 求值;而所有尚未解析的 SWB 参数和表达式(@...@、@<...>@、@[...]@)都会先被替换成临时值。
手册特别指出:这种“轻量预处理模式”只适合测试用途。若要得到最终正确结果,仍必须执行完整预处理流程。
Tcl 命令块与 SWB 变量
手册接下来说明,Tcl 命令块可以用来创建或更新 Sentaurus Workbench 变量。
从效果上看,它们等价于通过 #set 和 #seth 创建的预处理变量。
要做到这一点,需要在 Tcl 块里更新一个特殊的全局 Tcl 数组:
text
SWB_VARIABLES基本写法
如果希望让 SWB 预处理器把变量 myvar 初始化成 myval,就需要在 Tcl 命令块中写:
tcl
set SWB_VARIABLES(myvar) "myval"创建多个变量的示例
手册给出的示例如下:
text
!(
...
set SWB_VARIABLES(var1) 1
set SWB_VARIABLES(var2) 2
set SWB_VARIABLES(var3) 3
...
)这个例子的结果,就是在 SWB 中创建变量 var1、var2、var3,其值分别为 1、2、3。
原文配图如下:

图 82:通过 Tcl 命令块创建三个 SWB 变量。
Tcl 命令块中的输入输出操作
附录 H 接着说明:如果在 Tcl 命令块里执行文件写入,必须显式刷新输出流。
也就是说,写文件后要调用 Tcl 的 flush 命令。否则,在节点真正执行之前,这个文件都不会变得可用。
手册示例如下:
tcl
set FID [open "@pwd@/tmp_n@node@_ins.cmd" w]
...
puts $FID "Hello World"
flush $FID
close $FID这个提醒非常关键,因为很多人会误以为 puts 之后文件已经可读,但在 Tcl block 场景里,如果不 flush,你后续流程里可能根本读不到它。
什么时候适合使用 Tcl 命令块
尽管手册一开始就不推荐在新项目中继续使用 Tcl 块,但它还是列出了两个“确实有用”的典型场景。
场景 1:工具原生语言难以完成的复杂计算
第一类场景是:你需要做一些复杂计算,而这些计算很难直接用工具自身语言实现,但计算结果又需要回灌到工具原生语法中。
手册举的例子是 Sentaurus Device 输入文件中,根据 @Type@ 的不同,动态设置:
SIGNHFS1HFS2DGcTempEQN0EQNS
它的核心逻辑是:
- 若
@Type@ == "nMOS",就写一套参数和方程组合 - 否则就写另一套参数和方程组合
这类逻辑如果全用工具原生语法写,往往会更难维护;而放在 Tcl 命令块里,会显得更集中。
场景 2:增强工具原生语言的表达能力
第二类场景是:借助 Tcl 命令块扩展工具语言本身不容易表达的结构。
手册示例是:在 Sentaurus Device 的预处理命令文件里,使用一个 Tcl for 循环,一次性生成 100 段具有不同浓度的 Physics 段落。
也就是类似这样的结构:
text
!(
for {set i 0} {$i < 100} {incr i} {
puts "Physics (materialInterface=\"Silicon/Oxide\")"
puts "Charge(Conc=[expr 6.0e11 + $i*1e11])"
}
)!手册把它视为“用 Tcl 模拟 for-loop 扩展工具原生语言能力”的典型例子。
对 Tcl-aware 工具的提醒
手册还有一个很重要的说明:对于像 Sentaurus Process 这样本身就支持 Tcl 的工具,虽然在语法上允许使用 Tcl 命令块,但这样做并没有额外好处。
相反,它可能导致混淆,因为:
- Tcl 命令块
- 工具原生 Tcl 流程
这两者实际上是在不同作用域中求值的。
此外,这种写法还会降低工具命令文件的可读性。
Tcl 命令块使用规则汇总
附录 H 最后给出规则汇总。按原文意思整理如下:
- Tcl 块是在一对
!(与)!之间放置任意数量的原生 Tcl 指令。 - Tcl 块不能嵌套。
- Tcl 块可以插入任何预处理文件中的任意位置,例如工具命令文件和 Sentaurus Device 的参数文件。
- 每个文件中都可以包含任意数量的 Tcl 块。
- 所有 Tcl 块都由一个单独的 Tcl 解释器求值,并且这个解释器是“每个 experiment 独有”的。
- 所有 Tcl 块都会按工具从左到右、按每个文件从头到尾的顺序被求值。
- 一个 Tcl 块可以引用前面块中设置的 Tcl 变量,而前面的块既可以在同一文件里,也可以在同一 experiment 的另一个文件里。
- Sentaurus Workbench 会用 Tcl 块的标准输出替换掉该块本身。因此,如果你想提取 Tcl 变量
aaa的值,就必须显式写puts $aaa。如果 Tcl 块本身没有任何输出,那么预处理文件中也不会出现任何文本;但该块里定义的 Tcl 变量和过程仍然会保留在当前 experiment 的解释器中,供后续使用。 - SWB 预处理器会在标准的
#...预处理和@...@、@<...>@、@[...]@表达式解析之后,才统一求值所有 Tcl 块。因此,你可以在 Tcl 块里使用这些标准预处理指令;但不能反过来在#命令和@表达式中使用 Tcl 块。
附录 H 的核心价值,不是鼓励继续写 Tcl block,而是帮你在接手旧项目时看懂三件事:
- Tcl block 什么时候求值
- 为什么它能跨节点共享 experiment 级 Tcl 变量
- 为什么它常常让预处理更慢、更难读、也更难调试