资讯动态

Verilog基础:数据类型

发布时间:2026/10/27 9:23:35 来源:尧图企业网站定制
相关文章Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html一、引言在Verilog中数据类型主要可以分为两大类线网(net)和变量(variable)。这两类数据类型最根本的区别在于线网表示连接关系其值由驱动源决定变量表示存储抽象其值会保持到下一次赋值。早期资料中常常把reg称为“寄存器类型”但从Verilog-2005标准开始更准确的说法是把它归入变量类型。这里需要特别注意reg这个名字很容易让初学者误以为它一定对应硬件寄存器实际上并不是这样。reg只是说明它可以在过程块中被赋值并保持赋值结果至于综合后到底是组合逻辑、锁存器还是触发器还要看具体写法。除了线网和变量之外Verilog还支持向量(vector)和数组(array)的概念。向量用于描述多位信号数组用于描述多个同类型元素的集合。它们和线网、变量并不是并列关系而更像是在已有数据类型基础上的扩展表示方式。因此学习Verilog数据类型时建议按下面这条主线理解先区分net和variable再理解标量/向量最后掌握数组/存储器这样会更容易建立完整概念。二、线网(net线网常用于表示模块之间、门级单元之间的物理连接关系。它本身一般不负责保存数值而是由外部驱动源决定当前值。常见的驱动方式有连续赋值assign语句、模块端口输出和门级原语输出。如果一个普通线网没有任何驱动那么它的默认值通常是高阻态z不过这条规则也有例外例如tri0在无驱动时默认为0tri1在无驱动时默认为1trireg在无驱动时会保持之前的驱动值。Verilog中的线网类型包括wire、wand、wor、tri、triand、trior、tri0、tri1、trireg、uwire、supply0和supply1。1、wire和triwire和tri在语法和功能上本质一致二者都表示普通线网。之所以保留两个名字更多是为了建模语义更直观wire更常用于一般的连续赋值连接而tri更强调可能存在多个驱动源的三态连接场景。例如wire a; assign a b c;当一个wire或tri被多个驱动源同时驱动时仿真器会根据驱动值和驱动强度进行解析。如果两个相同强度的驱动分别给出0和1则该线网的结果通常为x表示冲突后的未知值。2、wor、wand、trior和triand这几种线网类型主要用于处理多驱动解析问题。wor和trior多个驱动按“线或”规则解析wand和triand多个驱动按“线与”规则解析。其中wor和trior功能一致wand和triand功能一致。例如一个wor线网只要有一个驱动为1解析结果就可能为1而wand线网则要求所有相关驱动都不拉低结果才可能为1。这类类型在普通RTL编码中不算常见但在总线建模、多源驱动建模以及某些库级描述中会更有意义。3、tri0、tri1和triregtri0和tri1可以理解为带有默认弱驱动的线网。tri0在无驱动时默认下拉到0tri1在无驱动时默认上拉到1。trireg则更特殊一些它是线网类型里少数可以“保留值”的类型。当外部驱动撤销后trireg不会马上变成z而是保持之前的值。它更接近对电荷存储效应的建模因此多见于开关级或较底层建模普通RTL设计里很少使用。4、uwire、supply0、supply1与驱动强度uwire是 Verilog-2005标准新增的数据类型它要求该线网只能有一个驱动源。如果存在多个驱动编译阶段就会报错。它的价值在于帮助设计者尽早发现多驱动错误因此在一些对代码规范要求较高的场景中比较有用。supply0和supply1则通常用于建模电源网络。supply0表示强0supply1表示强1这类写法多用于标准单元库、门级网表或底层库建模普通RTL设计里很少使用。此外线网还涉及驱动强度概念。Verilog中常见驱动强度从强到弱一般可分为supply strong pull large weak medium small highz图1展示了强度建模的比例尺驱动强度主要在多驱动解析时起作用。对于日常RTL设计而言理解“多驱动会冲突强驱动会覆盖弱驱动”就已经足够如果涉及更底层建模再深入研究强度规则会更合适可以参考这篇博客Verilog基础强度建模与net型信号的多驱动问题一。图1 强度比例尺三、变量(variable)变量可以理解为“能够保存赋值结果的数据对象”。与线网不同变量不会依赖持续驱动来维持当前值而是会一直保持上一次赋的值直到下一次被重新赋值。Verilog中的线网类型包括reg、integer、time、real和realtime。1、reg的本质reg是最常见的变量类型。它只能通过过程赋值来更新也就是说只能在always和initial过程块中赋值不能像wire那样直接用连续赋值来驱动例如reg q; always (posedge clk or negedge rst_n) begin if(!rst_n) q 1b0; else q d; end这里的q是reg因为它在always块里被赋值。需要强调的是reg并不等于硬件中的寄存器。比如下面这种写法reg y; always (*) begin y a b; end虽然y被声明为reg但它描述的是组合逻辑。因此reg的含义更准确地说是“过程变量”而不是“寄存器硬件”。2、初值和赋值特性变量会保持上一次的赋值结果。若没有显式初始化reg、integer和time初始值为xreal和realtime初始值为0.0。如果写成下面这种形式其效果通常可以理解为在仿真开始时在一个initial块中对a做了一次初始化赋值。reg a 1b0;3、integer、time、real和realtime除了reg之外其余几种变量也各有用途。integer通常用于保存整数型中间变量尤其适合循环变量、计数变量、临时计算变量等。它本质上等价于reg signed [31:0]因此是一个32位有符号整数。time主要用于保存仿真时间通常配合$time使用。它本质上等价于reg [63:0]也就是64位无符号数。real和realtime都表示64位双精度浮点数。二者本质等价只是使用语义略有区别real更偏一般实数运算realtime更常与$realtime配合用于保存带小数的仿真时间。需要注意real和realtime不能进行位选和域选也不能像普通比特向量那样参与位运算建模因此它们主要用于测试平台和仿真辅助不用于综合逻辑描述。四、有符号和无符号Verilog中可以把负值赋给线网和变量但并不是所有类型都能保留“符号意义”。能够保留符号的包括integer、real、realtime、reg signed和net signed而下面这些一般按无符号处理time、默认的reg和默认的net。所以在写算术运算时如果涉及负数或符号扩展一定要明确声明signed否则很容易得到和预期不一致的结果。五、向量(vector)与数组(array)1、向量如果一个线网或变量没有声明范围那它就是标量(scalar)只有1位如果在名字前加了范围声明那它就是向量(vector)表示多位信号例如wand w; // 1位标量wand线网 wire w1, w2; // 两个1位标量wire线网 tri [15:0] busa; // 16位向量tri线网 reg a; // 1位标量变量 reg [3:0] v; // 4位向量reg变量向量声明中的范围写法很灵活既可以高位在左、低位在右也可以反过来写甚至支持负数下标。reg [4:-1] b; // 共6位 reg [0:4] c; // 共5位c[0]反而是最高位虽然语法允许范围反向或使用负数下标但在工程实践中通常还是建议保持统一风格例如统一采用[MSB:LSB]且高位在左。这样代码可读性会更高也更不容易在位拼接和位选时出错。2、数组数组表示“多个同类型元素”的集合。Verilog-2001标准对数组的支持比早期版本增强了很多现在它具有以下特点数组元素既可以是标量也可以是向量。可以是一维也可以是多维可以访问某个数组元素也可以访问数组元素中的某一位或某一段例如reg [31:0] mem [0:127];这个声明表示一个有128个元素的数组每个元素是32位宽通常称为存储器(memory)。访问方式举例如下mem[0] // 第0个元素 mem[3][7] // 第3个元素的第7位 mem[5][15:8] // 第5个元素的[15:8]位段这类数组在建模ROM、RAM、查找表、缓存时非常常见。同时Verilog还提供了 $readmemh和$readmemb系统任务可以把十六进制或二进制文本文件中的数据加载到存储器中方便仿真初始化关于这两个系统任务的更多细节可以参考下面的博客。Verilog基础$readmemb和$readmemh系统任务的使用与其中的初始化地址相关问题详细细节https://blog.csdn.net/weixin_45791458/article/details/133150967?ops_request_misc%257B%2522request%255Fid%2522%253A%2522fe305c0697ffbc35049e11d33c88b24f%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257Drequest_idfe305c0697ffbc35049e11d33c88b24fbiz_id0utm_mediumdistribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-133150967-null-null.nonecaseutm_term%24readmembspm1018.2226.3001.44503、Verilog-1995标准与Verilog-2001标准的差异在较早的Verilog-1995中数组支持比较有限主要有两个明显限制第一线网类型不能声明为数组第二数组元素只能整体访问不能继续对元素做位选和域选。到了Verilog-2001这些限制被放宽了数组使用更加灵活。不过即便如此Verilog对数组的“整体操作”仍然有限。比如不能直接对数组的一部分进行整体引用也不能像高级语言那样用一条语句对整个数组完成复杂初始化。因此在需要初始化数组时常见做法仍然是使用for语句循环逐个元素赋值或使用$readmemh和$readmemb系统任务从文件加载例如integer i; initial begin for(i 0; i 128; i i 1) mem[i] 32h0; end

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑