2、VerilogA语言基础:语法简介、模块与端口定义、模拟行为描述
好,咱们正式开始啃VerilogA这块硬骨头。说实话,很多做数字的兄弟一看到“模拟”两个字就头大,觉得那是玄学。其实不然。VerilogA说白了就是一套描述模拟电路行为的“剧本”,你告诉仿真器:“嘿,这个电阻两端电压和电流的关系是V=I*R”,仿真器就懂了。
我个人习惯把VerilogA看作“会说话的电路图”。你不需要画版图,不需要纠结工艺角,只需要把电路的行为用数学表达式写出来。这对于SerDes均衡器的系统级建模来说,简直是神器。
2.1 VerilogA语法简介——别怕,它比你想的简单
先给大家吃颗定心丸:VerilogA的语法比Verilog数字语法要简洁得多。它没有那么多always块、wire和reg的纠结。它的核心就几个关键词:module、endmodule、analog、V、I。
嗯,这里要注意:VerilogA是区分大小写的。我当年刚入门时,因为把V写成了小写v,整整查了一下午的bug。后来发现仿真器直接把小写v当成了变量名,而不是电压函数。这种坑,踩过一次就记住了。
来看一个最简单的例子——一个理想电阻的模型:
// 理想电阻模型
module resistor(p, n);
inout p, n;
electrical p, n;
parameter real R = 1k;
analog begin
V(p,n) <+ R * I(p,n);
end
endmodule
看到了吗?analog begin ... end就是核心。里面写的是连续时间的行为。<+这个符号是贡献运算符,意思是“把右边的值赋给左边的电气量”。
关键点:VerilogA中的analog块是连续执行的,不像数字Verilog的always块需要时钟触发。它每时每刻都在计算,这就是模拟世界的“实时性”。
2.2 模块与端口定义——搭好你的“黑盒子”
模块定义,说白了就是画一个黑盒子,告诉别人:我的输入是什么,输出是什么,里面长什么样你别管。
端口类型有三种:input、output、inout。但在模拟世界里,我们更常用electrical这个类型。它代表一个电气节点,有电压和电流属性。
我曾经在做一个CTLE(连续时间线性均衡器)模型时,把输出端口定义成了output,结果仿真死活不收敛。后来发现,模拟端口必须用electrical,否则仿真器不知道这个节点的电压是多少。你想想看,一个没有电压信息的节点,仿真器怎么算?
正确的端口定义方式:
module ctle_stage(inp, inn, outp, outn);
inout inp, inn, outp, outn;
electrical inp, inn, outp, outn;
// 内部参数
parameter real gm = 1e-3; // 跨导
parameter real Rload = 1k; // 负载电阻
parameter real Cload = 1e-12; // 负载电容
// 内部节点
electrical mid;
analog begin
// 差分输入转电流
I(mid) <+ gm * (V(inp) - V(inn));
// 负载网络
V(outp, mid) <+ Rload * I(outp, mid);
I(outp) <+ Cload * ddt(V(outp));
// 对称的另一半
V(outn, mid) <+ Rload * I(outn, mid);
I(outn) <+ Cload * ddt(V(outn));
end
endmodule
小技巧:我建议在定义端口时,把电源和地也显式地作为端口传进来。虽然可以用ground关键字,但显式传电源地可以让模块更清晰,也方便做不同电压域的仿真。
2.3 模拟行为描述——让电路“活”起来
模拟行为描述,就是告诉仿真器:“这个电路在某个时刻应该表现出什么特性”。VerilogA提供了丰富的操作符和函数来支持这种描述。
常用的操作符包括:
V(a,b):获取节点a和b之间的电压I(a,b):获取从a流向b的电流ddt(x):对x求时间导数(电容电感必备)idt(x):对x求时间积分abs(x)、sqrt(x)、exp(x):数学函数
举个例子,一个简单的RC低通滤波器,用行为级描述就是:
module rc_lpf(in, out);
inout in, out;
electrical in, out;
parameter real R = 1k;
parameter real C = 1e-12;
analog begin
// 电阻上的电流等于电容上的电流
I(in, out) <+ (V(in) - V(out)) / R;
I(out) <+ C * ddt(V(out));
end
endmodule
你看,这就是行为描述的魅力。你不需要画版图,不需要跑EM仿真,几行代码就把一个滤波器的行为描述清楚了。对于SerDes均衡器来说,我们经常用这种思路来建模CTLE、DFE(判决反馈均衡器)等模块。
避坑指南:我曾经在建模一个高速比较器时,直接用if-else语句来描述迟滞特性。结果仿真器报错说“不连续”。后来才明白,analog块里的表达式必须是连续的,否则仿真器无法求导。正确的做法是用tanh或atan这类平滑函数来近似阶跃行为。
2.4 本章知识体系总览
为了让大家对本章内容有个整体把握,我画了一张图。这张图展示了VerilogA语言基础的三大支柱:语法基础、模块定义、行为描述。它们之间的关系就像盖房子——语法是砖块,模块是房间,行为描述是房间里的家具和功能。
这张图把本章的核心内容串起来了。你从语法入门,学会怎么写analog块;然后掌握模块和端口的定义方式,知道怎么封装一个功能块;最后用行为描述让这个功能块“活”起来,能算电压、算电流、算频率响应。
我个人觉得,学VerilogA最忌讳的就是死记硬背语法。你只要记住一个核心思想:模拟行为描述就是“用数学公式描述物理规律”。电阻就是V=IR,电容就是I=C*dV/dt,电感就是V=L*dI/dt。把这些基础公式组合起来,就能搭建出复杂的均衡器模型。
嗯,今天就先聊到这里。记住,动手写代码才是最好的学习方式。打开你的EDA工具,从最简单的电阻模型开始,慢慢加电容、加电感、加跨导,你会发现VerilogA其实挺有意思的。
本章要点回顾:
- VerilogA语法核心:
module、analog begin...end、<+贡献运算符 - 端口必须用
electrical类型声明,否则仿真器不认 - 行为描述用
V()、I()、ddt()等函数实现 - 避免在
analog块中使用不连续的if-else,用平滑函数替代
公众号:蓝海资料掘金营,微信deep3321