2、VerilogA语言基础:语法简介、模块与端口定义、模拟行为描述

好,咱们正式开始啃VerilogA这块硬骨头。说实话,很多做数字的兄弟一看到“模拟”两个字就头大,觉得那是玄学。其实不然。VerilogA说白了就是一套描述模拟电路行为的“剧本”,你告诉仿真器:“嘿,这个电阻两端电压和电流的关系是V=I*R”,仿真器就懂了。

我个人习惯把VerilogA看作“会说话的电路图”。你不需要画版图,不需要纠结工艺角,只需要把电路的行为用数学表达式写出来。这对于SerDes均衡器的系统级建模来说,简直是神器。

2.1 VerilogA语法简介——别怕,它比你想的简单

先给大家吃颗定心丸:VerilogA的语法比Verilog数字语法要简洁得多。它没有那么多always块、wire和reg的纠结。它的核心就几个关键词:moduleendmoduleanalogVI

嗯,这里要注意: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 模块与端口定义——搭好你的“黑盒子”

模块定义,说白了就是画一个黑盒子,告诉别人:我的输入是什么,输出是什么,里面长什么样你别管。

端口类型有三种:inputoutputinout。但在模拟世界里,我们更常用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块里的表达式必须是连续的,否则仿真器无法求导。正确的做法是用tanhatan这类平滑函数来近似阶跃行为。

2.4 本章知识体系总览

为了让大家对本章内容有个整体把握,我画了一张图。这张图展示了VerilogA语言基础的三大支柱:语法基础、模块定义、行为描述。它们之间的关系就像盖房子——语法是砖块,模块是房间,行为描述是房间里的家具和功能。

VerilogA语言基础三大支柱 VerilogA语言基础 语法简介 模块与端口定义 模拟行为描述 关键词与操作符 analog块结构 端口类型定义 参数化设计 内部节点声明 V/I/ddt/idt函数 连续行为建模 平滑函数近似 三大支柱共同构成SerDes均衡器建模基础

这张图把本章的核心内容串起来了。你从语法入门,学会怎么写analog块;然后掌握模块和端口的定义方式,知道怎么封装一个功能块;最后用行为描述让这个功能块“活”起来,能算电压、算电流、算频率响应。

我个人觉得,学VerilogA最忌讳的就是死记硬背语法。你只要记住一个核心思想:模拟行为描述就是“用数学公式描述物理规律”。电阻就是V=IR,电容就是I=C*dV/dt,电感就是V=L*dI/dt。把这些基础公式组合起来,就能搭建出复杂的均衡器模型。

嗯,今天就先聊到这里。记住,动手写代码才是最好的学习方式。打开你的EDA工具,从最简单的电阻模型开始,慢慢加电容、加电感、加跨导,你会发现VerilogA其实挺有意思的。

本章要点回顾:

  • VerilogA语法核心:moduleanalog begin...end<+贡献运算符
  • 端口必须用electrical类型声明,否则仿真器不认
  • 行为描述用V()I()ddt()等函数实现
  • 避免在analog块中使用不连续的if-else,用平滑函数替代

公众号:蓝海资料掘金营,微信deep3321