Verilog和SystemVerilog中的变量和线网

SystemVerilog扩展Verilog之后,很多人在使用SystemVerilog时经常使用到logic,并且认为logic和以前Verilog中的reg一样,是一种变量,这种说法不全面,不能认为是正确的。下面我们通过示例说明logic到底是不是一种变量名?并且以此示例了SystemVerilogVerilog中变量和线网的发展变化。

在使用VerilogSystemVerilog进行设计和验证环境编写的时候,经常会遇到两种数据对象变量线网。两者的主要区别在于使用过程中对其采用的赋值方式和其是否具有保持数值的功能,因此两者模拟的硬件结构也是完全不同的,变量类似于具有一定保存数值的寄存器,而线网则类似于电路连线,其本身并不能保存数值。线网常用于连续赋值语句,变量常用于过程性赋值语句中(两种赋值语句方式可参考《Verilog系列:【32】Verilog中的几种赋值语句》)。在VerilogSystemVerilog中变量和线网采用的数值系统目前主要有两种,如下表所示:

数据类型(data type

数据集合

使用语句

2-state2值类型)

0 - 逻辑0或者假

1 - 逻辑1或者真

SystemVerilog

4-state4值类型)

0 - 逻辑0或者假

1 - 逻辑1或者真

Z - 高阻态

X - 不定态

SystemVerilogVerilog


SystemVerilog之前的Verilog中只有4-state这一种数值系统,并且在数值系统和线网、变量之间并没有进行详细的区分,在Verilog中变量和线网取值都只有4-state01XZ),Verilog中常用的数据对象汇总如下表:

数据对象

类型名

数值系统

变量

reg

4-state01XZ

integer

time

real

realtime

线网

wire

wand

wor

tri

tri0

tri1

triand

trior

trireg

supply0

supply1

其实在Verilog中还有一种特殊的数据对象parameter,在Verilog中既不属于变量也不属于线网,是一种常量,parameter在程序运行的过程中是不能像变量和线网那样被改变(但是在编译阶段可以通过参数传递进行修改),并且在设计中parameter、变量和线网不能同名。关于parameter可参考Verilog系列:【18】参数三姐妹-parameter-local-specparam

【示例】
`timescale 1 ns / 1 ps
module top_tb;
reg  sig1,sig2;
wire sig3;
wire sig4;

initial begin
    #10 sig1 = 1'b0;sig2 = 1'b1;
    #10 sig1 = 1'b1;sig2 = 1'b0;
    #10 sig1 = 1'bx;sig2 = 1'bz;
    #10 sig1 = 1'bz;sig2 = 1'bx;
    #2  $stop;
end

assign sig3 = sig2;
endmodule // top_tb
【仿真结果】 

示例中,sig1sig2声明为reg,在没有对其进行操作时,其默认值为不定态“X”,sig3sig4声明为wire类型,在没有对其进行操作时,其默认值为高阻态“Z”,但是示例中sig3通过连续赋值语句被信号sig2一直驱动,所以sig3值在实际仿真时与sig2保持一致。这里需要注意的线网类型中的一个特例triregtrireg的默认值与其他线网不同,其默认值是“X”不是“Z”。

SystemVerilogVerilog进行了扩展,在数据对象(data object)和数据类型(data type)进行了更为细致的划分。其中数据对象专指变量和线网(此处不讨论parameter),数据类型指定了变量和线网所对应的数值系统,说白了就是变量或者线网可以取值的不同集合。目前SystemVerilog中主要有两种基础的数据类型:4-state4值数据类型,此与Verilog中的4-state一致)和2-state2值数据类型,01)。也就是说,在SystemVerilog中对于一个信号的描述已经不像Verilog那样,一个信号的声明将由数据对象(data object)和数据类型(data type)共同确定2-state相较于4-state使用更少的存储空间,在一些设计和验证平台中经常需要用到2-state的情况,增加了设计和构建验证平台的灵活性和丰富性。

SystemVerilog中变量的声明是通过var开始的,只是在具体使用时经常将这个关键字省略。而reglogic等则不是表示指示变量的,而是表示数据类型(data type),在SystemVerilog中常用的数据类型如下表所示:

变量(var

默认值

4-state integralintegerreglogictime

X

2-state integralbyteshortintintlongintbit

0

realshortrealrealtime

0.0

enumeration

Base type default initial value

string

“”(empty string)

class

null

chandle

null

SystemVerilog中线网并没有像变量那样通过一个通用的关键字var进行标识,而是保持了与Verilog基本一致,只是增加了一种新的线网类型uwire,其使用可参考《Verilog系列:【21】线网类型知多少》,并且所有的线网对象的数据类型都是4-state

【示例】

`timescale 1 ns / 1 ps
module top_tb;
var  bit   sig1;
bit        sig2;
var  logic sig3;
wire logic sig4;
// wire bit sig5; // illegal
wire       sig6;

initial begin
    #1 sig1 = 1'b0;sig3 = 1'b1;
    #3 sig1 = 1'b1;sig3 = 1'b0;
    #4 sig1 = 1'bx;sig3 = 1'bx;
    #2 sig1 = 1'b1;sig3 = 1'b1;
    #7 sig1 = 1'bz;sig3 = 1'bz;
    #3 sig1 = 1'b1;sig3 = 1'b1;
    #5 $stop;
end

assign sig2 = sig1;
assign sig4 = sig3;
assign sig6 = sig3;
endmodule // top_t
【仿真结果】 

示例中sig1sig2声明时均指定了数据类型为2-state,在后续的程序中虽然给其赋予了不定态和高阻态,但是因为其数据类型为2-state,所以其取值只能为0或者1,可以通过仿真观测到给sig1sig2被赋予不定态或者高阻态时,实际上其值为2-state数据类型的默认值0,即2-state数据类型的信号赋予不定态或者高阻态,该信号值将为0sig3sig4声明为4-state,其取值范围为(01XZ)的值可以是4-state中的任何一个。线网sig6并没有显式的指明其数据类型,但是此时其默认的数据类型实际是logic,即sig6的值可以是4-state中的任何一个。即如果没有明确指定线网的数据类型,那么该线网的默认数据类型为logic,即示例中的“wire sig6”与“wire logic sig6”是等价的(在没有多结构性驱动的情况下,后续示例会介绍到)。同时线网的数据类型不能为2-state,所以当在线网声明时指定其数据类型为2-state是不允许的。  

通过上述示例可以看到,线网在声明的时候必须显式的指明线网类型,否则该信号会被默认为变量。变量类型的关键字var可以省略,但是省略var时数据类型不能省略。当省略数据类型时,变量类型关键字var就不能省略,此时默认数据类型为logic。所以我们可以知道在实际的使用过程中,我们经常使用变量时是省略了关键字var的,使用的是数据类型。

在设计和构建验证环境时,经常需要在信号声明时指定初始值,但是一般建议不要在信号声明时指定初始值(综合后的结果可能与你仿真结果大相径庭)。变量声明时指定初始值后,当有其他驱动该变量时,变量的初始值将会被改变。而线网在声明时指定初始值相当于对该线网进行连续赋值,即该初始值将始终保持,并且不会被其他驱动所覆盖。

【示例】

`timescale 1 ns / 1 ps
module top_tb;
wire  sig1 = 1'b1;
logic sig2 = 1'b0;
logic sig3;

initial begin
    #10 sig3 = 1'b1;sig2 = 1'b1;
    #10 sig3 = 1'b0;sig2 = 1'b0;
    #10 sig3 = 1'b1;sig2 = 1'b1;
    #10 sig3 = 1'b0;sig2 = 1'b0;
    #5  $stop;
end

assign sig1 = sig3;
endmodule // top_tb
【仿真结果】 
sig1在声明时初始化为1,其通过连续赋值语句被sig3的驱动,在后续仿真运行的过程中,sig1的值不仅与sig1初始值有关,还与sig3的值有关,当sig1的初始值和sig3在某时刻的值相同时,sig1输出与sig3一致,但是当sig1初始值与sig3在某时刻的值不同时,此时sig1的值将会是不定态,可见sig1初始值对于sig1在整个仿真过程中都是持续有效的。sig2在声明时其值初始化为0,在没有后续驱动的情况下,sig2的值保持不变,当sig2被驱动时,sig2的值将不再是0而是驱动值,通过仿真结果可以看出,其值在运行过程中是跟随驱动变化的,其后续的值取决于驱动。这里还需要注意,因为sig2sig3前并没有指明其数据对象的类型,仅指明了所使用的数据类型(data type),即所使用的数值系统为logic,所以sig2sig3实际上是省略了关键字var的变量。虽然变量可以像线网在声明时初始化,但是声明时初始化的变量是不能用于连续赋值语句的LHS,而没有初始化的变量是可以出现在连续赋值语句的LHS,如下例:

【示例】
`timescale 1 ns / 1 ps
module top_tb;
wire  sig1;
logic sig2;
logic sig3;

initial begin
    #10 sig3 = 1'b1;
    #10 sig3 = 1'b0;
    #10 sig3 = 1'b1;
    #10 sig3 = 1'b0;
    // #10 sig2 = 1'bz; // illegal
    #5  $stop;
end

assign sig1 = sig3;
assign sig2 = sig3;
endmodule // top_tb
【仿真结果】 

示例中,sig2和sig3均为变量,但是sig2应用到了连续赋值语句中,如果在同一个模块内部,再将sig2应用到过程性赋值语句中那么编译将不会通过。也就是说同一个变量不能既出现在连续赋值语句中又出现在过程性赋值语句中

【示例】

`timescale 1 ns / 1 ps
module top_tb;
var logic sig1;
var logic sig2;
initial begin
    #10 sig1 = 1'b1;
    #13 sig1 = 1'b0;
    #12 sig1 = 1'b1;
    #14 sig1 = 1'b0;
    #3  sig2 = 1'b0;
    #4  $stop;
end
assign sig2 = sig1;
endmodule // top_t
【仿真结果】 

示例中,sig2既用于连续赋值语句又再initial过程块中使用,编译时会报出编译错误。

此外,在VerilogSystemVerilog中,我们知道连接inout类型的端口只能使用线网型信号,如果一个信号没有显式的指明线网,那么该信号是属于变量,不能直接连接到inout端口,这也就是为什么很多资料中都提到,当连接端口为inout时,logic不能替换wire

【示例】

`timescale 1 ns / 1 ps
module top_tb;
logic sig1,sig2,sig3;
wire sig4;

assign sig4 = sig1;
assign sig4 = sig2;

assign sig3 = sig2;
assign sig3 = sig1;

initial begin
       sig1 = 1'bz;sig2 = 1'bz;
    #3 sig1 = 1'b0;sig2 = 1'b0;
    #2 sig1 = 1'b1;sig2 = 1'b0;
    #3 sig1 = 1'bz;sig2 = 1'b0;
    #4 sig1 = 1'b0;sig2 = 1'b1;
    #2 $stop;
end
endmodule // top_tb
【仿真结果】 

当一个变量被一个结构性驱动(连续赋值)驱动时,该变量不能再被其他过程性语句或者结构性驱动再驱动,但是线网可以同时被多个结构性驱动所驱动。

通过上述示例,可以对变量和线网总结如下(本文不涉及parameter):

1)在Verilog中,变量和线网只有四值:01XZ;在SystemVerilog中变量除了可以有4-state外还可以有2-state,具体取决于变量声明时指定的数据类型,但是SystemVerilog中的线网和Verilog一样,只有4-state

2)在Verilog中,没有像SystemVerilog那样对变量、线网、数据类型进行详细的区分,仅有变量和线网的区分,并且两者都是4-state,而SystemVerilog将信号按照对象和数据类型进行了较为详细的划分,信号的声明包括数据对象和数据类型两部分,组成如下图所示:

其中变量关键字var声明时可以省略,但是当变量关键字省略时,数据类型不能省略;关键字不省略时,数据类型可以省略,此时默认的数据类型为4-state。平时我们经常使用的都是省略了变量关键字var,直接使用数据类型的方式。线网只能使用4-state数据类型。

3)在Verilog中变量不能用于连续赋值语句的LHS,但在SystemVerilog中变量不仅可以用于过程性赋值语句中,还可以用于连续赋值语句中,但是不能同时既用于连续赋值语句又用于过程性赋值语句中。

4)线网声明时指定的初值在运行的过程中一直有效,但变量声明时指定的初值当有其它驱动时该初值将会被修改覆盖。

5)线网的默认值为“Z”(除trireg之外,trireg默认值为“X”),变量的默认值取决于其数据类型。

6)在SystemVerilog中,任何使用wire的地方都可以用logic替换,但要求此时的logic不能有多结构性的驱动。






全部评论
感谢牛客让我见识增长了
点赞 回复 分享
发布于 2022-09-29 17:35 河南

相关推荐

豆泥🍀:同26届,加油,我也还没找到查看图片
点赞 评论 收藏
分享
04-29 22:35
门头沟学院 Java
牛友说改了名字能收到offer:旧图新发查看图片
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务