Verilog-A/AMS建模基础

好,咱们进入第四章。这一章讲的是Verilog-A和AMS建模基础。说实话,很多做射频芯片的工程师对Verilog-A有点怵,觉得它是个新语言。其实不然,你只要有Verilog基础,上手Verilog-A就是分分钟的事。

我个人习惯把Verilog-A看作是「模拟世界的Verilog」。它保留了数字Verilog的模块化思想,但加入了连续时间、连续值的描述能力。说白了,就是让你能用代码描述一个运放、一个混频器、甚至一个完整的锁相环。

Verilog-A语言简介

Verilog-A是Verilog-AMS的一个子集,专门用来描述模拟和混合信号系统。它诞生于90年代,后来被Accellera标准化。我记得刚入行那会儿,大家还在用SPICE跑整个收发机,跑一次要三天。后来用了Verilog-A建模,仿真速度提升了两个数量级。

它的核心特点就三个:

  • 连续时间:不像数字Verilog只在时钟边沿变化,Verilog-A的信号是随时间连续变化的
  • 守恒系统:支持基尔霍夫定律,电流电压都能描述
  • 行为级描述:用数学表达式代替晶体管级电路

关键点:Verilog-A不是用来替代SPICE的,而是用来做系统级验证的。你想想看,一个完整的射频收发机,用晶体管级仿真要跑一周,用Verilog-A模型可能只要一小时。

模块定义与端口声明

模块定义这块,和数字Verilog很像,但有个关键区别——端口类型。数字Verilog只有input、output、inout,而Verilog-A多了电气端口(electrical)。

来看个例子:

// 一个简单的放大器模块
module amplifier (vin, vout);
    // 电气端口声明
    electrical vin, vout;
    
    // 参数定义
    parameter real gain = 10.0;
    parameter real rin = 1e6;    // 输入阻抗
    parameter real rout = 50;    // 输出阻抗
    
    // 内部节点
    electrical vmid;
    
    // 模拟行为描述
    analog begin
        // 输入阻抗
        I(vin) <+ V(vin) / rin;
        
        // 增益级
        V(vmid) <+ gain * V(vin);
        
        // 输出阻抗
        V(vout) <+ V(vmid) - I(vout) * rout;
    end
endmodule

这里有几个地方要注意:

  • electrical是Verilog-A特有的类型,代表一个电气节点
  • analog begin ... end是模拟行为块的标志
  • <+是贡献操作符,表示「把右边的值赋给左边的量」

我的经验:刚开始写Verilog-A时,最容易犯的错误是把<+写成=。这两个完全不一样。<+是连续贡献,=是瞬态赋值。我曾经因为这个bug查了整整两天,最后发现就是少了个尖括号。

模拟行为描述

模拟行为描述是Verilog-A最强大的地方。你可以用数学公式直接描述电路行为,而不需要关心晶体管级的实现。

基本操作符

操作符含义示例
V(net)获取节点电压V(vin) 表示输入电压
I(net)获取流入节点的电流I(vout) 表示输出电流
ddt(x)对x求时间导数ddt(V(cap)) 表示电容电流
idt(x)对x求时间积分idt(I(ind)) 表示电感磁通

一个混频器的例子

咱们写个吉尔伯特混频器的行为模型。这东西在射频里太常见了:

module mixer (rf, lo, if_out);
    electrical rf, lo, if_out;
    
    parameter real conv_gain = 1.0;
    parameter real nf = 10.0;  // 噪声系数,单位dB
    
    real gain_linear;
    real noise_power;
    
    analog begin
        // 把dB值转成线性
        gain_linear = pow(10, conv_gain / 20);
        
        // 理想混频:乘法器
        V(if_out) <+ gain_linear * V(rf) * V(lo);
        
        // 加噪声(简化模型)
        noise_power = pow(10, nf/10) * `P_K * $temperature;
        V(if_out) <+ white_noise(noise_power, "thermal");
    end
endmodule

注意:这里的噪声模型是简化的。实际项目中,噪声系数会随频率变化,还要考虑1/f噪声。我曾经在一个接收机项目中用了这个简化模型,结果系统仿真和实测差了3dB。后来加了频率相关的噪声模型才对上。

非线性行为描述

射频电路最头疼的就是非线性。Verilog-A可以用多项式或查表来描述:

// 用多项式描述放大器非线性
module pa_nonlinear (vin, vout);
    electrical vin, vout;
    
    parameter real a1 = 10.0;   // 线性增益
    parameter real a2 = 0.5;    // 二阶系数
    parameter real a3 = -0.1;   // 三阶系数(负值表示压缩)
    
    analog begin
        V(vout) <+ a1 * V(vin) + a2 * pow(V(vin), 2) + a3 * pow(V(vin), 3);
    end
endmodule

你想想看,用这个模型跑IM3仿真,几秒钟就出结果。要是用晶体管级,光DC仿真就要跑半天。

条件语句与事件检测

Verilog-A也支持条件判断,但和数字Verilog不太一样:

module comparator (vin, vref, vout);
    electrical vin, vref, vout;
    
    parameter real voh = 1.8;   // 输出高电平
    parameter real vol = 0.0;   // 输出低电平
    parameter real slew_rate = 1e6; // 压摆率
    
    real vout_int;
    
    analog begin
        // 比较器核心
        if (V(vin) > V(vref)) begin
            vout_int = voh;
        end else begin
            vout_int = vol;
        end
        
        // 限制压摆率
        V(vout) <+ slew_rate * idt(vout_int - V(vout));
    end
endmodule

避坑指南:我曾经在比较器模型里直接用阶跃函数,结果仿真器不收敛。后来加了压摆率限制,问题就解决了。记住,模拟世界里没有完美的阶跃,一定要考虑转换速率。

写在后面

Verilog-A建模的核心思想就八个字:用数学代替电路。你不需要知道每个晶体管怎么偏置,只需要知道输入输出关系。这对于系统级仿真来说,效率提升是巨大的。

下一章咱们会深入讲Verilog-AMS,也就是把数字和模拟混合在一起建模。到时候你会看到,一个完整的射频收发机,用Verilog-AMS描述起来有多优雅。

嗯,今天就到这儿。记住,多写多练,Verilog-A其实没那么神秘。