System Verilog基本语法

本文详细介绍了SystemVerilog的基本语法,包括过程赋值与连续赋值、数组类型、函数与任务、类的使用、包的定义、约束、类型转换以及并发控制等关键概念。SystemVerilog作为Verilog的扩展,提供了更强大的验证功能,支持随机激励、功能覆盖和面向对象编程。此外,文章还探讨了如何在测试激励文件中应用这些特性,以提高验证的效率和覆盖率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

语言基本介绍

像 Verilog 和 VHDL 之类的硬件描述语言 (HDL) 主要用于描述硬件行为,以便将其转换为由组合门电路和时序元件组成的数字块。为了验证 HDL 中的硬件描述正确无误,就需要具有更多功能特性的面向对象的编程语言 (OOP) 来支持复杂的测试过程,这种语言通常被称为硬件验证语言 (HVL)。

SystemVerilog 是 Verilog 的扩展,具有诸多此类验证功能,能支持工程师在仿真中使用复杂的测试激励文件结构和随机激励来验证设计。

相比于 Verilog,SystemVerilog 的主要优势体现在它能够执行受约束的随机激励、在测试激励文件构造中使用 OOP 功能特性、功能覆盖范围、断言等等。

过程赋值和连续赋值

在 SystemVerilog 中,有两种赋值方式:过程赋值(Procedural Assignment)和连续赋值(Continuous Assignment)。它们用于在模块中定义信号的行为。

        1.过程赋值(Procedural Assignment): 过程赋值用于在 always 块内或函数、任务中对信号进行赋值。过程赋值使用 = 或 <= 运算符。

  • 使用 = 运算符进行赋值时,表示阻塞赋值。即,在当前语句执行完成之前,不会进行下一条语句的执行。具有以下特点:
    • 块结束后才完成赋值操作。
    • 值并不是立刻就改变的。
    • 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
  • 使用 <= 运算符进行赋值时,表示非阻塞赋值。即,所有的非阻塞赋值语句都会同时执行,并且不会等待其它语句的完成。具有以下特点:
    • 赋值语句执行完后,块才结束。
    • 值在赋值语句执行完后立刻就改变的。
    • 可能会产生意想不到的结果。

注:一般不会将阻塞赋值和非阻塞赋值一起使用。

下面是一个使用过程赋值的示例:

module Example;
  reg a, b, result;
  always @(posedge clk) begin
    a = in_a;     // 阻塞赋值
    b <= in_b;    // 非阻塞赋值
    
    if (a && b)
      result = 1'b1;   // 阻塞赋值
    else
      result = 1'b0;   // 阻塞赋值
  end
endmodule
在上述示例中, in_a  和  in_b  是输入信号,根据时钟上升沿触发执行 always 块内的赋值语句。使用  =  进行的赋值是阻塞赋值,需要等待上一条赋值语句执行完成后才能继续执行下一条语句。使用  <=  进行的赋值是非阻塞赋值,所有的非阻塞赋值语句都会同时执行。

        2.连续赋值(Continuous Assignment): 连续赋值用于在模块的顶层定义信号与信号之间的连接关系。它使用 assign 关键字。

下面是一个使用连续赋值的示例:

module Example(
  input wire a,
  input wire b,
  output wire result
);
  assign result = a & b;
endmodule
在上述示例中,使用  assign  关键字将  a  和  b  进行逻辑与操作,并将结果赋值给  result 。这种赋值方式是连续的,信号的赋值会随着输入信号的变化而自动更新。

需要注意的是,连续赋值只能用于顶层端口或模块内的信号赋值,不能在过程块(如 always 块)中使用。

注:线网类型与Verilog的要求相同,只能使用连续赋值语句(assign),而不能出现在过程块中(initial/always);相比于线网驱动的限制,变量(var)类型的驱动要求就很少,例如logic[3:0] a,该变量默认类型是变量,对它可以使用连续赋值、或者过程赋值

避坑指南:可以在testbench(module)中大量使用logic类型的变量,减少wire的使用。什么时候必须要使用wire?

答:多于一个驱动源,或者设计模块端口是双向(inout)的时候。具体用法就是声明某个信号为wire sigal_a;或者wire logic signal_a;就可以了。注意直接声明为logic signal_a;是不行的,会报类似如下的错误:xmelab: *E,ICDCBA: Illegal combination of driver and output clockvar to variable 'signal_a' detected (output clockvar found in ......).

数组

非组合型数组(非连续)

赋值非常的严格,必须要对齐

reg [15:0] RAM [0:4095];
logic [31:0] data [1024];
logic [31:0] data [0:1023];
/************初始化************/
int d [0:1][0:3] = '{'{7,3,2,1},'{3,2,32,1}};
byte a [0:3][0:3];
a[1][0] = 8'h5;
a[3] = '{'hF,'hA,'hC,'hE};

组合型数组(连续)

wire [3:0] select;
logic [3:0][7:0] data;

// 结构体
typedef struct packed{
    logic [7:0] crc;
    logic [63:0] data;
} data_word;
data_word [7:0] darray;

/************初始化************/
logic [3:0][7:0] a = 32'h0;
logic [3:0][7:0] b = {16'hz,16'h0};
logic [3:0][7:0] c = {16{2'b01}};

数组相关的系统函数

$dimensions(array_name)     // 用来返回数组的维度
$left(array_name, dimension)// 返回指定维度的最左索引值
$logic [1:2] [7:0] word [0:3] [4:1];
$left(word,1) // will return 0
$left(word,2) // will return 4
$left(word,3) // will return 1
$left(word,4) // will return 7

$size(array_name, dimension) // 返回指定维度的尺寸大小

动态数组

int dyn[], d2[];

initial begin
    dyn = new[5];
    foreach (dyn[j]) dyn[j] = j;
    d2 = dyn;
    d2[0] = 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Anton_wzd

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

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

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

打赏作者

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

抵扣说明:

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

余额充值