Verilog-A 调试与收敛
Verilog-A 语法简单,问题多在仿真中出现。调试手段基本就是打印语句 + 良好的参数设计。
调试输出基本套路
1. initial_step 打印参数(+%m 区分实例)
parameter integer DEBUG = 0;
parameter integer par1 = 100;
parameter real par2 = 20.1;
analog begin
@(initial_step) begin
$strobe("Instance is %m, par1 = %d, par2 = %g", par1, par2);
end
V(out) <+ 1;
end
%m输出实例层次名(如AMS_test_debug.I0),多实例时能区分。
2. DEBUG 参数开关
@(initial_step) begin
if(DEBUG > 0)
$strobe("Instance is %m, par1 = %d", par1);
end
- 实例化时设 DEBUG=0 关闭调试输出;也可作为仿真参数传递。
3. 运行时输出(加时间戳)
$strobe("Sim Time is %g, Instance is %m, xout = %g", $abstime, xout);
4. 定时输出(timer 事件)
@(timer(0, 10u)) begin
if(DEBUG > 0)
$strobe("Instance is %m, time=%g, par1=%d", $abstime, par1);
end
5. 条件触发输出
- 超阈值:用
cross事件(检测变量需先赋给内部节点)。 - 变化超阈值:用
timer保存旧值比较,或always @(absdelta(...))(实时、多输出)。
6. 输出到文件(CSV 便于处理)
integer fhandle;
analog begin
@(initial_step) begin
if(DEBUG > 0) begin
fhandle = $fopen("/home/user/debug.csv","w");
$fstrobe(fhandle, "This_is_a_debug_data_table");
end
end
@(timer(0, 10u)) begin
if(DEBUG > 0)
$fstrobe(fhandle, "%m, %g, %g", $abstime, V(in));
end
V(out) <+ 0;
@(final_step) begin
if(DEBUG > 0) $fclose(fhandle);
end
end
endmodule
随机数种子技巧
问题
Verilog-A 无法获取真实时间作为种子 → 每次仿真随机数序列相同。
解决方案 1:系统随机文件(Linux)
integer seed, fhandle;
@(initial_step) begin
fhandle = $fopen("/dev/urandom", "r");
$fscanf(fhandle, "%c", seed); // %c 取一个字符(ASCII 值作种子,有告警但可用)
end
⚠️ /dev/random 可能阻塞(系统随机值不足);用 /dev/urandom 非阻塞更稳。
解决方案 2:Monte Carlo 专用(Cadence)
@(initial_step) begin
seed = $cds_get_mc_trial_number(); // 每次 MC 仿真唯一种子
end
@(timer(0, 10u))
vos_number = rdist_normal(seed, 0, 1);
V(out, gnd) <+ vos_number;
$cds_get_mc_trial_number()仅 MC 仿真返回非 0;输出均匀分布。
收敛问题排查
1. 先检查代码本身
很多"不收敛"其实是代码写错但语法合法。常见错误:
- 总线位序混乱(mode[1] 开始 vs 移位 4 开始)
- 运算次序(real/integer 转换)
- else 忘记 begin-end 对 → 输出变成电流 0(浮空)
- 误用叠加赋值特性(多分支同时赋 V → 电压求和,可能是设计者本意也可能是错误)
- 信号互相检测形成循环(cross 互触发 → 步长变小、锯齿波)
2. 初始态收敛问题
- 用
@(initial_step)初始化内部节点/变量/输出,选择系统连续非突变工作点。 - 互相连接的模块初值不匹配(如输入 0 但计算产生无穷大)→ "step larger than xxx" 错误。
- 系统函数使用不当(/dev/random 阻塞等)也会导致初始化失败。
3. 上电或不上电
- 真实器件模型(BSIM/PSP)从 0 状态上电易收敛;自写 Verilog-A 模型从 0 上电可能更难收敛(状态间不连续)。
- 为 IP 书写模型必须考虑上电次序;为自己系统的正常工况建模可简化。
4. 设定电压而非电流
- 理想电压源比理想电流源安全(短路检测明显,开路电流源浮空不易发现)。
- 电压输出型:
V(out) <+ xout;或V(out) <+ iout*rout; - 电流输出型加电压范围限制(保证连续):
// 单端限制
I(out) <+ iconst*(1-1/100^V(out));
// 两端限制
I(out) <+ iconst*(1-1/100^V(out) - 1/100^(5-V(out)));
- 对电流做微分(ddt)结果很差(电压连续、电流锯齿)——尽量用电压描述微分积分。
5. 输入输出阻抗
- 电流限定节点加电压限制函数 = 限制输出阻抗。
- 高频应用需考虑 LC 负载与 Q 值。
6. 内部限制(防开环巨大输出)
开环增益 10e6 时,1mV 失调 → 1000V 输出灾难。用 tanh 限幅:
V(sigout) <+ 0.5*(out_high - out_low)
* tanh(gain*(V(sigin, sigref) - offset))
+ (out_high + out_low)/2;
tanh 中间线性度好、两端饱和、处处连续可导,是建模限幅首选。
7. 反馈
- 把反馈放在电路结构中,不在模型内部描述。
- 模型只描述开环特性,闭环由仿真器计算 → 可验证稳定性。
- 自己写反馈需手算传递函数,无法用仿真器验证稳定性,易振荡。
调试思维导图
不收敛/不稳定
├── 审视代码(位序/begin-end/运算次序/叠加特性/自循环)
├── 初始态(initial_step 初值、模块间匹配)
├── 上电过程(真实器件 vs 自写模型差异)
├── 电压 vs 电流描述(电压优先、电流加限制)
├── 阻抗(限压即限阻、高频 LC)
├── 内部限制(tanh 限幅防 1000V 输出)
└── 反馈(放电路结构,模型只写开环)
相关笔记
- 仿真器函数 → 09 - VerilogA 仿真器函数
- 模拟事件 → 06 - VerilogA 模拟事件
- 建模方法 → 13 - VerilogA 建模与系统设计