Verilog reg, Verilog wire, SystemVerilog logic. What’s the difference?

Verilog reg 和 Verilog wire 之间的区别经常让许多刚开始学习这种语言的程序员感到困惑(我也曾困惑过!)。作为初学者,我被告知要遵循以下准则,这些准则似乎普遍有效:

  • 在始终块内部分配的信号左侧 (LHS) 使用 Verilog 规范
  • 使用 Verilog wire 处理分配在 always 块外的信号左端 (LHS)

后来,当我采用 SystemVerilog 编写 RTL 设计时,我被告知现在一切都可以是 “类型逻辑”。这在一般情况下也行得通,但时不时会遇到关于变量、网和赋值的令人费解的错误信息。

于是,我决定找出这些数据类型的确切工作原理,来撰写这篇文章。我翻阅了语言参考手册,搜索了现已失效的 Verilog-2005 标准文件,并上了一堂历史课。请继续阅读我发现的 Verilog reg、Verilog wire 和 SystemVerilog 逻辑之间的区别。

Verilog data types, Verilog reg, Verilog wire

Verilog 数据类型分为两大类:网和变量。它们的区别在于它们如何表示不同的硬件结构。

网数据类型表示结构实体之间的物理连接(想象一根普通的导线),如门之间或模块之间。它不存储任何值。它的值来自其驱动程序的驱动内容。Verilog 线可能是最常见的网络数据类型,尽管还有许多其他网络数据类型,如 tri、wand、supply0。

变量数据类型通常代表一块存储空间。在下一次赋值之前,它可以保持一个赋给它的值。Verilog reg 可能是最常见的变量数据类型。Verilog reg 通常用于模拟硬件寄存器(尽管它也可以表示组合逻辑,如 always@(*) 块内部)。其他变量数据类型包括整数、时间、real、realtime。

几乎所有 Verilog 数据类型都是 4 态的,这意味着它们可以取 4 个值:

  • 0 表示逻辑零,或假条件
  • 1 代表逻辑 1,或真条件
  • X 代表未知逻辑值
  • Z 代表高阻抗状态

Verilog 经验法则 1:要表示存储空间时使用 Verilog reg,要表示物理连接时使用 Verilog wire。

Assigning values to Verilog reg, Verilog wire

Verilog 网络数据类型只能通过连续赋值来赋值。这意味着要使用连续赋值语句(赋值语句)等结构,或从输出端口驱动。连续赋值驱动网络的方式与门驱动网络的方式类似。右侧的表达式可以看作是连续驱动网络的组合电路。

Verilog 变量数据类型只能通过过程赋值来赋值。这意味着要在始终块、初始块、任务和函数中进行赋值。赋值发生在某种触发器上(如时钟的摆位),之后变量将保留其值,直到下一次赋值(在下一个触发器上)。这使得变量成为模拟触发器等存储元件的理想选择。

Verilog 第 2 条规则:用赋值语句或端口输出驱动 Verilog 线,并从 always 块中驱动 Verilog reg。如果要在 always@(*) 块内用组合逻辑驱动物理连接,则必须将物理连接声明为 Verilog reg。

SystemVerilog logic, data types, and data objects

SystemVerilog 引入了一种新的 2 状态数据类型(只允许逻辑 0 和逻辑 1,不允许 X 或 Z),用于测试台建模。为了区别旧的 Verilog 4 状态行为,增加了一种新的 SystemVerilog 逻辑数据类型来描述通用的 4 状态数据类型。

过去在 Verilog 中的数据类型,如 wire、reg、wand,现在在 SystemVerilog 中称为数据对象。wire、reg、wand(以及几乎所有以前的 Verilog 数据类型)都是 4 态数据对象。Bit、byte、shortint、int、longint 是新的 SystemVerilog 2 态数据对象。

仍然有两大类数据对象:nets和variables。我们熟悉的所有 Verilog 数据类型(现在的数据对象)都是 4 态数据对象,因此现在也应正确地包含 SystemVerilog 逻辑关键字。

wire my_wire; // implicitly means “wire logic my_wire”
wire logic my_wire; // you can also declare it this way
wire [7:0] my_wire_bus; // implicitly means “wire logic[15:0] my_wire_bus”
wire logic [7:0] my_wire_logic_bus; // you can also declare it this way
reg [15:0] my_reg_bus; // implicitly means “reg logic[15:0] my_reg_bus”
//reg logic [15:0] my_reg_bus; // but if you declare it fully, VCS 2014.10 doesn’t like it

如果未指定数据类型(2 态或 4 态),则隐式声明为逻辑变量。下面是一些变量声明示例。尽管有些工具似乎并不完全支持。

// From the SV-2012 LRM Section 6.8
var byte my_byte; // byte is 2-state, so this is a variable
// var v; // implicitly means “var logic v;”, but VCS 2014.10 doesn’t like this
var logic v; // this is okay
// var [15:0] vw; // implicitly means “var logic [15:0] vw;”, but VCS 2014.10 doesn’t like this
var logic [15:0] vw; // this is okay
var enum bit {clear, error} status; // variable of enumerated type
var reg r; // variable reg

不用太在意 var 关键字。添加这个关键字是为了保证语言的精确性(随着语言的发展,语言大师们会努力保持向后兼容性),在 RTL 设计中很可能看不到这个关键字。

I’m confused… Just tell me how I should use SystemVerilog logic!

说了这么多技术规范的废话,如果您正在使用 SystemVerilog 进行 RTL 设计,我有一个好消息要告诉您。在 RTL 设计的日常使用中,你几乎可以忘掉所有这些!

SystemVerilog 逻辑关键字 standalone 将声明一个变量,但规则已经改写,在 RTL 设计中几乎可以随处使用变量。因此,在其他文章的示例代码中,我使用 SystemVerilog 逻辑来声明变量和端口。

module my_systemverilog_module
(
  input  logic       clk,
  input  logic       rst_n,
  input  logic       data_in_valid,
  input  logic [7:0] data_in_bus,
  output logic       data_out_valid, // driven by always_ff, it is a variable
  output logic [7:0] data_out_bus,   // driven by always_comb, it is a variable
  output logic       data_out_err    // also a variable, driven by continuous assignment (allowed in SV)
);

  assign data_out_err = 1'b1; // continuous assignment to a variable (allowed in SV)
//  always_comb data_out_err = 1'b0; // multiple drivers to variable not allowed, get compile time error

  always_comb data_out_bus = <data_out_bus logic expression>;
  always_ff @(posedge clk, negedge rst_n)
    if (!rst_n)
      data_out_valid <= 1'b0;
    else
      data_out_valid <= <data_out_valid logic expression>;
  
endmodule

当您以这种方式独立使用 SystemVerilog 逻辑时,还有另一个好处,即改进了对意外多重驱动的检查。SystemVerilog 变量的多重赋值或连续赋值与过程(总是块)赋值的混合是一个错误,这意味着很可能会出现编译错误。在网络中允许混合赋值和多重赋值。因此,如果您真的需要一个多重驱动的网,您需要将其声明为线。

Verilog 中,从模块外部向模块输出端口(声明为 Verilog wireVerilog reg)赋值,或在模块内部向声明为输入端口的网络赋值都是合法的。这两种情况都是经常发生的非预期布线错误,会造成争用。对于 SystemVerilog,声明为 SystemVerilog 逻辑变量的输出端口禁止多重驱动,而对声明为 SystemVerilog 逻辑变量的输入端口进行赋值也是非法的。因此,如果您犯了这种布线错误,很可能再次出现编译错误。

SystemVerilog 经验法则 1:如果使用 SystemVerilog 进行 RTL 设计,则应使用 SystemVerilog 逻辑进行声明:
- 所有点对点网络。如果特别需要多驱动网络,则使用传统网络类型之一,如 wire
- 所有变量(由始终块驱动的逻辑)
- 所有输入端口
- 所有输出端口

如果遵循这一规则,您就可以基本忘记 Verilog reg 和 Verilog wire 之间的区别!(好吧,大多数时候)

Conclusion

当我最初想知道为什么可以始终使用 SystemVerilog 逻辑关键字编写 RTL 时,我从未料到这会成为一项重大的工作,需要阅读和解释两种不同的规范,理解复杂的语言规则,并弄清其中的细微差别。至少我可以说,这些建议很容易记住。

我希望这篇文章能为您很好地总结 Verilog regVerilog wireSystemVerilog 逻辑、它们的历史,以及一套有用的 RTL 编码建议。

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值