SV学习笔记(二)

news2024/11/26 13:55:08

接口

什么是接口?

  • 接口 主要用作验证 ,国外有些团队会使用sv进行设计,那么接口就会用作设计。
  • 验证环境中,接口可以 使连接变得简洁而不易出错 。
  • interface和module的使用性质很像, 可以定义端口,也可以定义双向信号,可以使用initial和always,也可以定义function和task 。
  • interface可以 在硬件域和软件域间传递信息 ,也就是可以作为module的端口列表,也可以作为软件方法的形式参数。
  • 对于interface的初步认识,可以看作“插排”,DUT与TB之间的数据驱动就是靠这个“插排”来完成的。

接口的定义与使用

  • interface的定义结构与module类似。

  • interface的 端口列表只需定义时钟、复位等公共信号 ,或者不定义任何端口信号,而在变量列表中定义DUT与TB连接的各个变量, 建议用logic来定义 。

  • interface也可以依靠参数化方式提高复用性。

  • interface在例化时, 与module例化方式相同 。

  • 对于有对应interface的DUT和TB组件,在例化时,传递匹配的interface变量名也就完成了interface内变量的传递,换句话说就是两者打通,对应interface的不同组件之间变量信号实时传递。

白话一刻

在System Verilog中,interface是一个用于封装一组相关信号并简化模块间连接的重要特性。它类似于一条总线或“插排”,可以将零碎的线(即信号)整合在一起,提供给需要的模块使用。

  • DUT (Device Under Test):这是指正在被测试或验证的硬件设备或软件组件。在System Verilog的验证环境中,DUT是被测试的对象,测试人员会为其编写测试用例以检查其是否按预期工作。

  • TB (Testbench):测试平台或测试基准。在System Verilog中,TB是用于验证DUT正确性的代码集合。它通常包括DUT的实例化、接口的实例化、验证环境的实例化以及实际的测试用例。 TB的主要任务是产生激励信号并观察DUT的响应,以验证DUT是否满足预期的行为。

在interface的上下文中,从TB的角度来看,interface中的信号输入输出方向与TB中信号的输入输出方向是一致的。而从DUT的角度来看,DUT中信号的输入输出方向与interface中信号的输入输出方向是相反的。这是因为interface在TB和DUT之间起到了一个中介或桥梁的作用,确保两者之间的数据流动是正确和高效的。

总结来说,interface在System Verilog的验证环境中扮演了关键角色,它简化了DUT和TB之间的连接和数据交换,使得验证工作更加高效和准确。而DUT是被测试的对象,TB则是用于验证DUT的测试代码集合。

interface arb_interface(input bit clk);
    logic [1:0] grant, request;
    logic reset_n;
endinterface

module arb(arb_interface arb_if);
    //......
    always@(posedge arb_if.clk or negedge arb_if.reset_n)begin
        if(!arb_if.reset_n)
            arb_if.grant <= 2'b00;
        else
            arb_if.grant <= next_grant;
    end
    //......
endmodule

module testbench(arb_interface arb_if);
    //......
    initial begin
        @(posedge arb_if.clk) arb_if.request <= 2'b01;
        $display("@%0t: Drove req=01", $time);
        repeat(2) @(posedge arb_if.clk);
        if(arb_if.grant != 2'b01) $display("@%0t: a1: grant != 2'b01", $time);
        $finish;
    end
endmodule

module tb_top;
    bit clk;
    initial clk = 1'b0;
    always #5 clk = ~clk;

    arb_interface arb_if(clk);
    arb a1(arb_if);
    test t1(arb_if);
endmodule

这段代码是使用SystemVerilog语言编写的,主要涉及到接口(interface)和模块(module)的定义。我会为你解释每个部分的功能。

    1. arb_interface 定义
interface arb_interface(input bit clk);  
    logic [1:0] grant, request;  
    logic reset_n;  
endinterface

这里定义了一个名为arb_interface的接口,它有一个输入信号clk。接口内部定义了两个2位的逻辑信号grant和request,以及一个逻辑信号reset_n。

    1. arb 模块
module arb(arb_interface arb_if);  
    //......  
    always@(posedge arb_if.clk or negedge arb_if.reset_n)begin  
        if(!arb_if.reset_n)  
            arb_if.grant <= 2'b00;  
        else  
            arb_if.grant <= next_grant;  
    end  
    //......  
endmodule

arb模块使用arb_interface作为它的接口。它有一个始终块,该块在clk的上升沿或reset_n的下降沿触发。当reset_n为假时,grant信号被设置为2’b00;否则,它被设置为next_grant的值(这里next_grant`在代码段中没有定义,可能是在模块的其他部分定义的)。

    1. testbench 模块
module testbench(arb_interface arb_if);  
    //......  
    initial begin  
        @(posedge arb_if.clk) arb_if.request <= 2'b01;  
        $display("@%0t: Drove req=01", $time);  
        repeat(2) @(posedge arb_if.clk);  
        if(arb_if.grant != 2'b01) $display("@%0t: a1: grant != 2'b01", $time);  
        $finish;  
    end  
endmodule

testbench模块也是一个使用arb_interface的模块。在initial块中,它首先等待clk的上升沿,然后将request信号设置为2’b01。接着,它等待clk的另外两个上升沿,并检查grant信号是否为2’b01`。如果不是,它会打印一条错误消息,并结束仿真。

    1. tb_top 模块
systemverilog
module tb_top;  
    bit clk;  
    initial clk = 1'b0;  
    always #5 clk = ~clk;  
  
    arb_interface arb_if(clk);  
    arb a1(arb_if);  
    test t1(arb_if);  
endmodule

tb_top是顶层模块,它定义了时钟信号clk,并通过always块每5个时间单位反转clk的值。然后,它实例化了arb_interface、arb和testbench模块,并将它们连接在一起。这样,testbench模块可以向arb`模块发送请求,并检查其响应。

总结:这段代码是一个简单的SystemVerilog验证环境,其中包含一个接口、一个仲裁器模块和一个测试基准模块。顶层模块将它们连接在一起,以测试仲裁器的行为。

这里着重解释一下tb_top 模块:

在 tb_top 模块中,test t1(arb_if); 这一行代码是实例化 testbench 模块(在这里被重命名为 test)的一个实例 t1,并将之前定义的 arb_interface 实例 arb_if 作为参数传递给 test 模块的接口。

详细解释如下:

  • 实例化模块:在SystemVerilog中,模块可以被多次实例化,以创建多个独立的模块实例。每个实例可以有其自己的内部状态和行为,但它们都基于相同的模块定义。在这里,test t1(arb_if); 就是创建 testbench 模块的一个新实例 t1。

  • 接口传递:arb_interface arb_if(clk); 这行代码在 tb_top 模块中定义了一个 arb_interface 的实例 arb_if,并将 tb_top 中定义的 clk 信号连接到该接口的 clk 输入。然后,在实例化 test 模块时,arb_if 被作为参数传递给 test 模块,这样 test 模块就可以通过 arb_if 接口与 arb 模块进行通信。

  • 连接性:由于 arb_if 被传递给了 test 和 arb 两个模块,这两个模块可以通过 arb_if 接口来交换信号。在 test 模块中,它可以通过 arb_if 发送请求(request)并观察响应(grant)。同时,arb 模块也可以通过 arb_if 接收请求并发送响应。

  • 封装性:通过使用接口,test 和 arb 模块都不需要知道彼此的内部实现细节。它们只需要知道如何通过 arb_if 接口来交换信号。这种封装性使得模块的设计和测试更加灵活和可维护。

  • 测试环境:在这种上下文中,test 模块通常用作测试环境或测试台(testbench),用于验证 arb 模块的行为是否符合预期。通过发送不同的请求并观察响应,测试人员可以验证 arb 模块的功能正确性。

综上所述,test t1(arb_if); 这行代码在 tb_top 模块中创建了一个 testbench 模块的实例 t1,并通过 arb_interface 实例 arb_if 将它与 arb 模块连接起来,从而构建了一个完整的测试环境。

module testbench(arb_interface arb_if);  
    //......  
    initial begin  
        @(posedge arb_if.clk) arb_if.request <= 2'b01;  
        $display("@%0t: Drove req=01", $time);  
        repeat(2) @(posedge arb_if.clk);  
        if(arb_if.grant != 2'b01) $display("@%0t: a1: grant != 2'b01", $time);  
        $finish;  
    end  
endmodule

接口的优势

  • 将有关信号封装在接口,对于设计和验证环境都便于维护,如需修改、添加、删除信号,只需修改interface文件即可。
  • 接口在硬件域(module)和软件域(class)都可以使用,是 硬件域和软件域交互的唯一媒介
  • 接口可例化,对于多组相同总线,通过例化可灵活使用,简化代码且便于维护。
  • 每一个agent使用对应的interface,简化验证平台结构,便于维护。
  • tb顶层例化时,无需定义信号连线,只需例化interface。

采样和数据驱动

竞争问题

  • 为了避免RTL仿真行为中发生信号竞争问题, 建议使用非阻塞赋值(<=) (简单来说阻塞赋值是顺序执行,非阻塞赋值是并发执行,硬件电路行为是并发执行)。

  • 在仿真行为中,为了避免时序电路中时钟和驱动信号的时序竞争,我们需要 尽量明确驱动时序和采样时序 。

  • 默认情况下,时钟对于组合电路的驱动会添加一个 无限最小时间(delta-cycle)的延迟 ,而该延迟无法用绝对时间单位衡量,它要比最小时间单位精度还要小。这是 仿真工具为了符合硬件电路真实行为(建立时间保持时间以及线延迟)而做出的处理 。(注意: #0并不代表0延迟,而是指延迟delta-cycle)

为了说明delta-cycle的概念,举例如下:

`timescale 1ns/1ps
module top_module ();
        bit clk1,clk2;
    bit rstn;

    logic [7:0] d1;

    initial begin
        clk1 = 0;
        forever #5 clk1 <= ~clk1;
    end

    always@(clk1) clk2<=clk1;

    initial begin
        #0 rstn <= 0;
        #10 rstn <= 1;
        #20 $finish;
    end

    always@(posedge clk1 or negedge rstn)
        if(!rstn) d1 <= 0;
        else d1 <=  d1+1;

    always@(posedge clk1)
        $display("clk1: %0t ns d1 value is 0x%0x", $time, d1);
    always@(posedge clk2)
        $display("clk2: %0t ns d1 value is 0x%0x", $time, d1);
endmodule
Running Icarus Verilog simulator...
VCD info: dumping is suppressed.
clk1: 5000 ps d1 value is 0xxx
clk2: 5000 ps d1 value is 0x0
clk1: 15000 ps d1 value is 0x0
clk2: 15000 ps d1 value is 0x1
clk1: 25000 ps d1 value is 0x1
clk2: 25000 ps d1 value is 0x2
Hint: Total mismatched samples is 0 out of 0 samples

Simulation finished at 30000 ps
Mismatches: 0 in 0 samples

白话一刻:

这是一个简单的Verilog模块,它描述了一个带有复位功能的计数器。下面是这段代码的详细解释:

时序单位

`timescale 1ns/1ps

这一行定义了时间单位和精度。在这里,时间单位是1纳秒(ns),精度是1皮秒(ps)。

模块定义

module top_module ();

定义了一个名为top_module的模块。

信号定义

bit clk1,clk2;  
bit rstn;  
logic [7:0] d1;

clk1 和 clk2 是位(bit)类型的信号,用作时钟。
rstn 是复位信号,低电平有效。
d1 是一个8位宽的逻辑信号,用作计数器。

时钟生成

initial begin  
    clk1 = 0;  
    forever #5 clk1 <= ~clk1;  
end

这个initial块生成了一个时钟信号clk1,它的周期是10个时间单位(即10ns)。

时钟同步

always@(clk1) clk2<=clk1;

这个always块确保clk2与clk1同步。每当clk1变化时,clk2都会获得clk1的当前值。

复位逻辑

initial begin  
    #0 rstn <= 0;  
    #10 rstn <= 1;  
    #20 $finish;  
end

这个initial块设置了复位信号rstn。

在仿真开始时,rstn为0(有效复位)。

在10个时间单位后,rstn变为1(解除复位)。在20个时间单位后,仿真结束。

计数器逻辑

always@(posedge clk1 or negedge rstn)  
    if(!rstn) d1 <= 0;  
    else d1 <=  d1+1;

这个always块描述了一个计数器。每当clk1的上升沿发生或rstn的下降沿发生时,都会执行这个块。

如果rstn为0(有效复位),则d1被清零。否则,d1增加1。

显示逻辑

always@(posedge clk1)  
    $display("clk1: %0t ns d1 value is 0x%0x", $time, d1);  
always@(posedge clk2)  
    $display("clk2: %0t ns d1 value is 0x%0x", $time, d1);

这两个always块在每次clk1和clk2的上升沿时显示当前时间和d1的值。

总结

这个模块生成了两个同步的时钟信号clk1和clk2,以及一个复位信号rstn。它实现了一个简单的计数器,该计数器在复位时清零,并在每个clk1的上升沿时递增。同时,它还使用$display系统任务来显示时钟信号和计数器的值。

为什么同样在25000ps时刻,d1在clk1和clk2下的采样值不同?

首先clk2是在clk1下驱动的,也就是clk2比clk1延迟一个delta-cycle时间,clk1驱动了d1,d1也比clk1延迟一个delta-cycle时间。

25000ps,当在clk1下采样时,d1的值还未更新,所以采到的是0x1;

而在clk2下采样时,d1已由clk1驱动更新,所以采到的是0x2。

如果还未完全理解,可以打开波形窗口,不过要打开delta-cycle的开关,可以看到delta-cycle的存在,对电路的数据驱动和采样时序会有更直观的理解。

,clk2 是通过 always@(clk1) 块中的语句 clk2 <= clk1; 驱动的。这个语句意味着每当 clk1 发生变化时,clk2 将被赋值为 clk1 的当前值。但是,由于 <= 是非阻塞赋值,它不会立即更新 clk2 的值,而是在当前仿真时间槽的末尾更新。

非阻塞赋值的一个关键特性是,在一个给定的仿真时间槽内,所有非阻塞赋值语句都看起来是同时执行的,即它们都在该时间槽的末尾更新其目标。因此,尽管 clk2 的赋值是基于 clk1 的变化,但由于非阻塞赋值的特性,clk2 的更新实际上会延迟一个delta-cycle。

  • 总结:

    • 如果处于各种原因,clk与被采样数据之间存在若干个delta-cycle的延迟,那么对数据的采样会存在问题。
    • 采样数据的竞争问题会成为潜在困扰仿真采样准确性的问题。
    • 避免采样的竞争问题:
      • 1)在驱动时,添加相应的人为延迟,使clk与驱动变量之间的延迟加大,提高DUT使用驱动信号时的准确度;
      • 2)在采样时,依靠采样前某段时刻进行采样,来模拟建立时间的采样要求,确保采样的可靠性。

图示:

未显示 delta-cycle

显示 delta-cycle

在仿真过程中,delta-cycle的延迟存在主要是由于以下几个原因:
首先,delta-cycle是一个无限小的时间单位,其精度比最小的时间单位还要小,无法用绝对时间单位来描述。在仿真中,当运行一个delta-cycle时,实际上是让仿真器在一个极短的时间内执行一系列的操作。这种极短的延迟有助于理解在“同一时刻”的采样或驱动的先后逻辑关系。
其次,在RTL(寄存器传输级)仿真中,一些信号变化可能在少于单位时间(如纳秒级)内发生,这使得信号的变化过程变得不确定。通过引入delta-cycle作为额外的时刻,可以更清晰地描述这些细微变化,使仿真过程更加逻辑化。
此外,delta-cycle的延迟还用于解决同一时间点的信号竞争问题。在组合逻辑电路中,由于信号竞争状态的存在,可能会导致冒险的情况产生。为了避免这种情况,通常在组合逻辑电路的驱动输出中添加一个delta-cycle的延迟。这种延迟可以确保信号在发生变化时,按照预期的先后顺序进行处理,从而避免信号竞争带来的问题。
需要注意的是,由于delta-cycle的延迟非常小,它通常不会对整体仿真结果产生显著影响。然而,在某些特定情况下,如采样数据中的竞争问题,这种微小的延迟可能会导致采样结果的不一致。因此,在进行仿真时,需要仔细考虑delta-cycle的延迟对仿真结果的影响,并采取相应的措施来避免潜在的问题。

仿真时间槽的具体长度在Verilog中并没有一个固定的值,因为它取决于当前仿真时刻发生的事件数量以及仿真器的实现。

clocking

  • 在接口中声明clocking(时序块)和采样的时钟信号,可以用来 实现信号的同步和采样 。
  • clocking块基于时钟周期对信号进行驱动或采样的方式,使testbench不再苦恼于如何准确及时地对信号驱动或采样,消除了信号竞争的问题。
clocking bus @(posedge clk1);
    default input #10ns output #2ns;
    input data, ready, enable;
    output negedge ack;
    input #1step addr;
endclocking

这行定义了一个输出信号ack,并且指定了它的有效边沿为下降沿(negedge)。这意味着ack信号的变化(从高电平到低电平)将在clk1的上升沿之后的某个时刻被输出,具体取决于可能存在的其他延迟设置或操作。

其中定义了一个名为addr的输入信号,并给它指定了一个特殊的延迟#1step。#1step通常用于描述跨时钟域的传输,表示该信号的值将在下一个时钟边沿(在本例中是clk1的下一个上升沿)时被采样。这通常用于避免亚稳态问题,确保信号在稳定后才被采样。


对上述clocking描述代码进行说明 :

  • 第一行定义clocking块bus,使用上升沿来驱动和采样。
  • 第二行指出输入信号在clk1上升沿之前5ns采样,输出信号在clk1上升沿之后2ns驱动(输入为采样,输出为驱动)。
  • 第三行声明输入信号,采用默认的输入事件(clk1上升沿5ns前采样)。
  • 第四行声明输出信号,并且指明为clk1下降沿驱动,覆盖了原有的clk1上升沿后2ns驱动。
  • 第五行定义了输入信号addr,采用了自定义的采样事件,clk1上升沿后的1 step,覆盖了原有的clk1上升沿前5ns采样,这里1 step使得采样发生在clk1上升沿的上一个时钟片采样区域,即可以保证采样到的数据是上一个时钟周期数据。

clocking块的总结 :

  • clocking块不仅可以定义在interface中,也可以定义在module和program中。
  • clocking中列举的信号不是自己定义的,而是interface或其他声明clocking的模块定义的。
  • clocking在声明完后,应该伴随着定义默认的采样事件,也就是“default input/output event”,如果没有定义,会默认使用时钟上升/下降沿前1step进行采样,时钟上升/下降沿后#0进行驱动
  • 除了定义默认的采样和驱动事件,定义信号方向时同样可以用新的采样/驱动事件对默认事件进行覆盖。

在Verilog中,clocking块用于描述在特定时钟边沿下输入和输出信号的行为。当声明一个clocking块时,确实应该伴随着定义默认的输入和输出事件,即指定输入信号的采样时刻和输出信号的驱动时刻。

如果没有在clocking块中显式定义默认的输入和输出事件,Verilog会采用一些默认的规则来确定这些事件。具体来说:

  • 默认输入事件:如果没有明确指定输入信号的采样事件,**则默认会在时钟边沿前的1个时间步长(#1step)进行采样。**这样做是为了确保在时钟边沿到来之前,输入信号的值已经稳定,从而避免可能的亚稳态问题。

  • 默认输出事件:对于输出信号,如果没有明确指定驱动事件,则默认会在时钟边沿后立即(#0)进行驱动。这意味着输出信号的新值会紧跟在时钟边沿之后被更新。

这种默认行为确保了即使在没有明确指定的情况下,clocking块内的信号也有一个清晰且合理的行为模型。然而,在实际应用中,为了增加代码的可读性和准确性,通常建议显式地定义输入和输出事件的采样和驱动时刻,而不是依赖于这些默认行为。

通过显式定义输入和输出事件,您可以更精确地控制信号的行为,确保它们符合硬件设计的预期。例如,您可以指定输入信号在时钟上升沿后的某个延迟后进行采样,或者指定输出信号在时钟下降沿前的某个时刻进行驱动。这些自定义的设置可以帮助您更准确地模拟硬件的行为,并有助于发现和解决潜在的问题。

modport

modport本身目的就是将信号分组和指明输入输出方向,在连接时工具会进行端口方向检查,防止连接出错,不过掌握clocking之后,可以忽略modport,clocking已经对信号输入输出方向进行了声明。

结论

  • 为了避免采样竞争问题,验证工程师应该在验证环境的驱动环节添加固定延迟,使得在仿真波形中更容易体现出时钟与被驱动信号之间的时序前后关系,同时这样也便于对DUT的准确处理和TB的准确采样。
  • 如果TB在采样从DUT送出的数据,在时钟与被驱动信号之间存在delta-cycle时,应该考虑在时钟采样沿的更早时间端段去模拟建立时间要求,这种方法也可以避免由于delta-cycle问题带来的采样竞争问题。
  • 当我们把clocking运用到interface中,用来声明各个接口与时钟的采样和驱动关系后,可以大大提高数据驱动和采样的准确性,从根本上消除采样竞争的可能性。

测试的开始和结束

写在前头

  • 各个设计自身可以认为是一个大的线程,内部有包含多个并行的线程,而模块之间连接即线程的通信,主要依靠信号的变化。
  • 可以想象,对于一个设计,如果在仿真开始没有任何激励,那么仿真不具备执行条件,也可以认为已经结束,因为在设计内部没有产生任何新的事件,也不会触发组合逻辑和时序逻辑。
  • 如果仿真开始后仅提供时钟和复位信号,验证会持续下去,而对设计不会产生实质的功能影响。从设计角度来看,复位信号是为了让设计进入一个确定的初始状态,而时钟就是脉搏跳动
  • verilog测试中,可以通过系统函数 “ f i n i s h ( ) ”来结束仿真,也可以通过“ finish()” 来结束仿真,也可以通过 “ finish()来结束仿真,也可以通过stop()” 来暂停仿真。

program

  • program是作为验证而提出的,可以有效控制仿真的进程,但是目前验证平台更多基于UVM,UVM有独特的控制机制,所以program在实际项目中使用并不多。
  • program的提出, 将验证部分和设计部分进行有效隔离 ,每一个program作为一个独立测试, 当testbench中所有program中最后一个initial块完成后,结束仿真 。这是program的隐式结束。
  • 有些program内的initial块无法正常结束,这时候需要使用 显示结束,使用“$exit()” 来结束program。
  • program被看做软件域,所以不可以出现always、module、interface等硬件相关语句,并且不可以例化其他program。
  • program被看做软件域,可以在program内部定义变量和发起多个initial块,并且建议使用阻塞赋值(软件方式的顺序执行)。
  • program对于数据采样也可以消除delta-cycle竞争问题,详细内容可见红宝书“SV环境构建篇之程序和模块”。(待了解)

总结

  • 硬件域(module)、软件域(program)、中间域(interface) 。

  • 不仅可以使用interface clocking来消除采样竞争问题,可以使用program(建议使用clocking)。
    program可以控制仿真的结束。

  • 使用“ s t o p ( ) ”和“ stop()”和“ stop()finish()”可以结束仿真。

调试方法

调试工具

大多工程师选择使用verdi作为调试工具,主要有三个窗口:层级列表窗口、源代码窗口、波形窗口。verdi的具体使用方法,请参考另一篇帖子【Verdi使用总结】 。

打印消息

打印消息是调试循环语句、顺序执行语句等查看路径和当前变量值的简便方式,除此之外,由于验证平台更多变量是动态的,无法在调试工具查看动态变量值,所以对与验证环境的调试,更多使用打印消息。

打印消息命令“$display()”:

  • $time代表仿真时间变量。
  • 显示格式: %x(十六进制)、%d(十进制)、%b(二进制)、%s(字符串)、%t(时间)。
  • d i s p l a y (消息级别)、 display(消息级别)、 display(消息级别)、warning(警告级别)、 e r r o r (错误级别)、 error(错误级别)、 error(错误级别)、fatal(严重错误级别)
  • 字符串变量格式化:string s = $sformatf(“Hello, %s!", name_s);

设置断点

  • 可以通过调试工具为程序设置断点。
  • 通过设置断点(breakpoint)可以查看程序执行到断点处(程序暂停)的变量数值,而设置断点要求验证工程师对程序执行顺序足够了解。
  • 设置断点可以便于查看软件程序(function、task、object)中局部变量的数值。注意:动态变量是无法添加到波形查看的。
  • 设置断点还可以方便调试程序执行的顺序,例如在顺序执行语句执行的多个位置设置断点,通过仿真执行,查看程序是否在断点处暂停,如果没有,那么程序的挂起(hang-on)原因就在上一个断点和此断点之间。通过此方法可定位可疑程序的范围。
  • 如果查看局部变量,需要使用局部变量窗口(Local Windows),继而通过断点查看变量(暂时不知verdi是否支持,待学习)。

白话一刻:

在Verilog硬件描述语言(HDL)中,理解阻塞赋值(blocking assignment)和非阻塞赋值(nonblocking assignment)是非常重要的,特别是在描述并发行为时。这两种赋值方式会影响仿真中信号值的更新顺序,并可能导致所谓的“竞争条件”。

  • 竞争条件
    竞争条件(Race Condition)指的是在仿真过程中,当多个语句或操作试图在同一仿真时间槽(time-slot)内修改同一个信号或变量时,如果这些操作的执行顺序不同,可能会导致不同的执行结果。换句话说,竞争条件是由于多个操作之间的时间依赖关系不明确导致的。

在硬件设计中,并发性是一个关键概念,因为许多事件(如信号变化)可能几乎同时发生。如果Verilog代码中的赋值操作不能正确反映这种并发性,那么仿真结果可能与实际硬件行为不一致。

  • 阻塞赋值
    阻塞赋值使用等号(=)作为操作符。当执行阻塞赋值时,赋值语句会立即计算右侧表达式(RHS)的值,并将其赋给左侧表达式(LHS)。在这个赋值完成之前,仿真器会暂停执行任何其他语句。换句话说,阻塞赋值是顺序执行的

这种赋值方式在描述组合逻辑时很有用,因为组合逻辑的输出直接依赖于其当前输入。但是,在描述时序逻辑时,如果不恰当地使用阻塞赋值,可能会导致竞争条件。

  • 非阻塞赋值
    非阻塞赋值使用小于等于号(<=)作为操作符。与阻塞赋值不同,非阻塞赋值不会立即更新LHS的值。相反,它会在当前仿真时间槽结束时更新LHS的值。这意味着在当前时间槽内,即使已经计算了RHS的值,其他语句仍然可以继续执行,并可能修改LHS的值。

非阻塞赋值允许模拟时序逻辑中的并发行为,因为所有非阻塞赋值语句在同一时间槽内看起来是同时执行的。这使得非阻塞赋值在描述寄存器传输级(RTL)时序逻辑时非常有用。

  • 使用建议
    为了避免在RTL仿真行为中发生信号竞争问题,建议在描述时序逻辑时使用非阻塞赋值。这是因为时序逻辑通常涉及状态寄存器和时钟信号,这些信号的行为在硬件中是并发的。使用非阻塞赋值可以更好地模拟这种并发行为,并减少竞争条件的风险。

相反,在描述组合逻辑时,由于输出直接依赖于输入,使用阻塞赋值通常是合适的。但是,即使在组合逻辑中,也要小心避免潜在的竞争条件,特别是当存在多个路径可以修改同一信号时。

总的来说,正确选择使用阻塞赋值还是非阻塞赋值是确保Verilog代码正确模拟硬件行为的关键。


参考资料

  • Wenhui’s Rotten Pen
  • SystemVerilog
  • chipverify

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【单片机家电产品--晶闸管】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 单片机家电产品–晶闸管 前言 记录学习单片机家电产品内容 已转载记录为主 一、知识点 晶体管和晶闸管之间的区别 晶体管和晶闸管之间的区别 什么是可控硅&#xff08;…

观察和配置MAC地址表

目录 原理概述 实验目的 实验内容 实验拓扑 ​编辑1&#xff0e;基本配置 2.观察正常状态时的MAC地址表 4.配置静态MAC地址表项 原理概述 MAC 地址表是交换机的一个核心组成部分&#xff0c;交换机主要是根据 MAC 地址表来进行帧的转发的。交换机对帧的转发操作行为一共有…

Linux 命令 top 详解

1 top命令介绍 Linux系统中&#xff0c;Top命令主要用于实时运行系统的监控&#xff0c;包括Linux内核管理的进程或者线程的资源占用情况。这个命令对所有正在运行的进程和系统负荷提供不断更新的概览信息&#xff0c;包括系统负载、CPU利用分布情况、内存使用、每个进程的内容…

Golang Context是什么

一、这篇文章我们简要讨论Golang的Context有什么用 1、首先说一下Context的基本作用&#xff0c;然后在讨论他的实现 (1)数据传递&#xff0c;子Context只能看到自己的和父Context的数据&#xff0c;子Context是不能看到孙Context添加的数据。 (2)父子协程的协同&#xff0c;比…

c++的学习之路:9、STL简介与string(1)

一、STL 1、什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 也就是说STL就是一个模板&#xff0c;这个模板就是整合了很多库让我们方…

磁盘如何分配数据数据

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 磁盘如何分配数据 数据切割&#xff1a; 按照固定长度进行切割---》编码翻译&#xff08;常用&#xff09; 计算机要求按照8bit(字节)进…

【蓝桥杯练习】tarjan算法求解LCA

还是一道比较明显的求LCA(最近公共祖先)模型的题目,我们可以使用多种方法来解决该问题&#xff0c;这里我们使用更好写的离线的tarjan算法来解决该问题。 除去tarjan算法必用的基础数组&#xff0c;我们还有一个数组d[],d[i]记录的是每个点的出度&#xff0c;也就是它的延迟时间…

学习Python第十五天:第一个程序python程序

第一个程序&#xff1a;ZIP文件口令破解机 编写ZIP文件口令破解机要从学习zipfile库的使用方法着手&#xff0c;打开pythn解释器&#xff0c;我们用help(zipfile)命令进一步了解这个库&#xff0c;并重点看一下zipfile类中的extractall()方法&#xff0c;这个类和这个方法对我…

在仿真环境中运行lio-sam

文章目录 前言LIO-SAM环境编译运行键盘控制编译lio-sam遇到的问题前言 Gazebo 仿真提供了一个高效且成本低廉的平台,使研究人员和开发者能够在安全且可控的虚拟环境中设计、测试和优化机器人系统。它允许快速原型制作和迭代,精确控制测试条件,并能模拟复杂或危险的场景,从…

2013年认证杯SPSSPRO杯数学建模B题(第二阶段)流行音乐发展简史全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 B题 流行音乐发展简史 原题再现&#xff1a; 随着互联网的发展&#xff0c;流行音乐的主要传播媒介从传统的电台和唱片逐渐过渡到网络下载和网络电台等。网络电台需要根据收听者的已知喜好&#xff0c;自动推荐并播放其它音乐。由于每个人喜好…

【解决】Unity Profile | FindMainCamera

开发平台&#xff1a;Unity 2020.3.7f1c1 关键词&#xff1a;FindMainCamera   问题背景 ModelViewer 是开发者基于 UnityEngine 编写的相机控制组件。ModelView.Update 中调度52次并触发3次GC.Collect。显然并不期望并尽可能避免 Update 造成的GC 问题。事实上 FindMainCame…

C语言第三十九弹---预处理(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 预处理 1、预定义符号 2、#define定义常量 3、#define定义宏 4、带有副作用的宏参数 5、宏替换的规则 6、宏和函数的对比 总结 在C语言中&#xff0c;预处…

经久耐用耐强腐蚀PFA材质气体洗涤瓶全氟烷氧基树脂尾气吸收瓶

PFA洗气瓶是一种常用于净化和干燥各种气体的实验室器皿&#xff0c;以去除其中的水分、油脂、颗粒物等杂质&#xff0c;从而使需要用到的气体满足实验要求。 PFA气体吸收瓶 PFA洗气瓶的工作原理&#xff1a; 主要是通过液体吸收、溶解或发生化学反应来去除气体中的杂质。在洗气…

LeetCode题练习与总结:螺旋矩阵

一、题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;ma…

物联网行业趋势——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断进步和应用场景的日益扩大&#xff0c;物联网行业呈现出迅猛发展的势头。作为当今世界最具前瞻性和战略意义的领域之一&#xff0c;物联网行业的趋势和未来发展值得深入探讨。 ​一、物联网行业正逐渐实现全面普及。随着物…

matlab使用教程(32)—求解偏微分方程(3)

1求解 PDE 方程组 此示例说明由两个偏微分方程构成的方程组的解的构成&#xff0c;以及如何对解进行计算和绘图。 以如下 PDE 方程组为例 要在 MATLAB 中求解该方程&#xff0c;您需要对方程、初始条件和边界条件编写代码&#xff0c;然后在调用求解器pdepe 之前选择合适的解…

“帮助“Java成长的世界级大师不简单!

文章目录 初探编程&#xff1a;“天啊&#xff0c;真酷&#xff0c;程序真的能学习。”哺育Java成长&#xff0c;成为Java幕后英雄出书《Effective Java》斩获Jolt图书大奖 是谁&#xff1f;作品一出版就获得著名的Jolt图书大奖&#xff0c;每一版本豆瓣评分均超9.0&#xff01…

某眼实时票房接口获取

某眼实时票房接口获取 前言解决方案1.找到veri.js2.找到signKey所在位置3.分析它所处的这个函数的内容4.index参数的获取5.signKey参数的获取运行结果关键代码另一种思路票房接口:https://piaofang.maoyan.com/dashboard-ajax https://piaofang.maoyan.com/dashboard 实时票房…

LabVIEW专栏三、探针和断点

探针和断点是LabVIEW调试的常用手段&#xff0c;该节以上一节的"测试耗时"为例 探针可以打在有线条的任何地方&#xff0c;打上后&#xff0c;经过这条线的所有最后一次的数值都会显示在探针窗口。断点可以打在程序框图的所有G代码对象&#xff0c;包括结构&#xf…