SV中关键字用法学习笔记

本文章是CSDN博客上收集信息集合

1. event

      event事件是静态的同步对象句柄(可以像参数一样在子程序中传递),它用来同步多个并发的进程,比如某个进程等待着事件,而另一个进程则触发这个事件。

几个特征:

  • 可以被赋值成null
  • 可以传给队列,函数和任务

可以被赋值给其它事件,这样两个事件变量(句柄)会指向同一个同步化对象,触发任意一个变量就触发这个事件, 如下所示

event eve;
event tmp;

initial begin
    tmp = eve;
    #10 -> tmp;
end
 initial begin
     wait(eve.triggered());
     $display("@%0t : event eve receive trigger",$time);
end
------------------------------------
@10 : event eve receive trigger    //执行结果
-------------------------------------

事件的触发和阻塞等待

  • 触发: -> (or ->>) event_handle

  • 等待:@ or wait(event_handle.triggered)  

两种等待条件的区别以及竞争条件的发生

    首先分析竞争的产生。如下代码所示:

module tb;
 
  // Create an event variable that processes can use to trigger and wait
  event event_a;
 
  // Thread1: Triggers the event using "->" operator at 20ns
  initial begin
    #20 ->event_a;
    $display ("[%0t] Thread1: triggered event_a", $time);
  end
 
  // Thread2: Starts waiting for the event using "@" operator at 20ns
  initial begin
    $display ("[%0t] Thread2: waiting for trigger ", $time);
    #20 @(event_a);
    $display ("[%0t] Thread2: received event_a trigger ", $time);
  end
 
  // Thread3: Starts waiting for the event using ".triggered" at 20ns
  initial begin
    $display ("[%0t] Thread3: waiting for trigger ", $time);
    #20 wait(event_a.triggered);
    $display ("[%0t] Thread3: received event_a trigger", $time);
  end
endmodule

上面module的运行结果是:

[0] Thread2: waiting for trigger 
[0] Thread3: waiting for trigger 
[20] Thread1: triggered event_a
[20] Thread3: received event_a trigger

       发现使用@的阻塞等待没有收到trigger。分析如下:事件的触发状态只会在当前时间步长保持,一但仿真开始向前运行,就会失去这个状态。也就是说,在仿真时间20的这个时间步长中(可以把它想象成1ns或1ps),事件是处于触发状态的,到21仿真时间后触发状态消失。在上面的代码中,两个阻塞等待@和.triggered都是和事件触发同时发生的,出现了竞争条件,结果是虽然触发和等待发生在同一个仿真时间,但是还有执行顺序,根据代码顺序,先执行事件的触发,由于@事件控制是边沿敏感的,等到执行@语句时,事件的触发的“上升沿”已经消失,所以@并没有被触发,而@和.triggered的不同就在于此,@是边沿敏感,后者是电平敏感,所以在整个20仿真时间步长中,wait语句都能被触发
触发和等待在同一时间开始的话就不要用@改用.triggered,当然如果触发进程在等待进程(的仿真时间)之前发生,那这两种等待都不会收到trigger。
其实将Thread2initial块移动到Thread1 initial块之前,也能让@阻塞等待被触发,这就是因为@的执行顺序在触发事件之前。

wait_order的使用

       wait_order阻塞等待多个事件的触发,并且要求这多个事件按照用户决定顺序触发。wait_order可以和else一同使用,当多个事件按顺序触发时,执行wait_order后的语句,否则执行else后的语句。

module tb;
  // Declare three events that can be triggered separately
  event a, b, c;
 
  // This block triggers each event one by one
  initial begin
    #10 -> a;
    #10 -> b;
    #10 -> c;
  end
 
  // This block waits until each event is triggered in the given order
  initial begin
 
    wait_order (a,b,c) 
      $display ("Events were executed in the correct order");
    else 
        $display ("Events were NOT executed in the correct order !");  
  end
endmodule
--------------------------------------------------------
Events were executed in the correct order //执行结果
--------------------------------------------------------------

事件的合并

     之前提到一个事件可以赋值给其它的事件变量,这样两个事件变量实际指向同一个事件对象,触发任意一个变量就是触发了这个对象

module tb;
 
  // Create event variables
  event event_a, event_b;
 
  initial begin
    fork
      // Thread1: waits for event_a to be triggered
      begin
        wait(event_a.triggered);
        $display ("[%0t] Thread1: Wait for event_a is over", $time);
      end
      // Thread2: waits for event_b to be triggered    
      begin
        wait(event_b.triggered);
        $display ("[%0t] Thread2: Wait for event_b is over", $time);
      end
 
      // Thread3: triggers event_a at 20ns
      #20 ->event_a;
 
      // Thread4: triggers event_b at 30ns
      #30 ->event_b;
 
      // Thread5: Assigns event_b to event_a at 10ns
      begin
        // Comment code below and try again to see Thread2 finish later
        #10 event_b = event_a;
      end
    join
  end
endmodule
--------------------------------------------------------------------
[20] Thread1: Wait for event_a is over
[20] Thread2: Wait for event_b is over
---------------------------------------------------------------------

上面结果可见,触发event_a后也触发了event_b,因为这两个句柄指向同一个事件对象。

 

2. fork

     fork jion

          fork...join内部的线程都会并行执行,直至处理完内部所有线程后才会结束块语句。fork join 例子:

module fork_join;
 
  initial begin
    $display("-----------------------------------------------------------------");
    fork
      //-------------------
      //Process-1
      //-------------------
      begin
        $display($time,"\tProcess-1 Started");
        #5;
        $display($time,"\tProcess-1 Finished");
      end
 
      //-------------------
      //Process-2
      //-------------------
      begin
        $display($time,"\tProcess-2 Started");
        #20;
        $display($time,"\tProcess-2 Finished");
      end
    join
 
    $display($time,"\tOutside Fork-Join");
    $display("-----------------------------------------------------------------");
    $finish;
  end
endmodule

Process-1和Process-2将会同时开始运行,Process-1在5ns完成,Process-2在20ns完成.运行结果如下:

  fork join_any

      fork...join_any语句块会阻塞statement-3的执行,直至fork..join_any语句块中的任意一个线程执行完(如上图中的proceess-2)后才能开始执行statement-3。

fork join any 例子:

module fork_join;
  initial begin
    $display("-----------------------------------------------------------------");
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 Started");
        #5;
        $display($time,"\tProcess-1 Finished");
      end
 
      //Process-2
      begin
        $display($time,"\tProcess-2 Started");
        #20;
        $display($time,"\tProcess-2 Finished");
      end
    join_any
 
    $display($time,"\tOutside Fork-Join");
    $display("-----------------------------------------------------------------");
  end
endmodule

 Process-1和Process-2将会同时开始运行,Process-1在5ns完成,Process-2在20ns完成.在执行fork..join_any语句块的同时,语句块后的$display语句也在执行。运行结果如下:

 fork join_none

     fork...join_none语句块不会阻塞statement-3的执行,即在执行fork...join_none语句块的同时也在执行statement-3.

 fork join none 例子:

module fork_join_none;
  initial begin
    $display("-----------------------------------------------------------------");
 
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 Started");
        #5;
        $display($time,"\tProcess-1 Finished");
      end
      //Process-2
      begin
        $display($time,"\tProcess-2 Startedt");
        #20;
        $display($time,"\tProcess-2 Finished");
      end
    join_none
  
    $display($time,"\tOutside Fork-Join_none");
    $display("-----------------------------------------------------------------");
  end
endmodule

在执行fork块语句的同时,语句块后面的$display语句也在执行,运行结果如下:

wait fork

       直至处理完fork块中的线程,程序才会往下执行,也就是将程序阻塞在fork块这里,直到fork块执行完。例子如下:

module wait_fork;
 
  initial begin
    $display("-----------------------------------------------------------------");
 
 
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 Started");
        #5;
        $display($time,"\tProcess-1 Finished");
      end
 
      //Process-2
      begin
        $display($time,"\tProcess-2 Started");
        #20;
        $display($time,"\tProcess-2 Finished");
      end
    join_any
 
   $display("-----------------------------------------------------------------");
$finish; //ends the simulation
  
  end
endmodule

     上面的例子在5ns执行完process-1后,fork块将不会阻塞,在执行fork块内的process-2的同时,开始执行块后的$display语句,当遇到$finish时会结束程序的执行,导致 process-2被迫中途停止执行。运行结果如下:

      解决上述问题的办法就是在fork块语句后添加wait fork语句,它会等待fork语句块内的所有线程执行完毕后,才会继续往下执行。修改后的代码如下:  

module wait_fork;
 
  initial begin
    $display("-----------------------------------------------------------------");
 
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 Started");
        #5;
        $display($time,"\tProcess-1 Finished");
      end
 
      //Process-2
      begin
        $display($time,"\tProcess-2 Started");
        #20;
        $display($time,"\tProcess-2 Finished");
      end
    join_any
    wait fork; //waiting for the completion of active fork threads
     
    $display("-----------------------------------------------------------------");
    $finish; //ends the simulation
  end
endmodule

运行结果如下:

disable fork

终止正在执行的fork语句块。看下面的例子:

module disable_fork;
 
  initial begin
    $display("-----------------------------------------------------------------");
 
    //fork-1
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 of fork-1 Started");
        #5;
        $display($time,"\tProcess-1 of fork-1 Finished");
      end
       
      //Process-2
      begin
        $display($time,"\tProcess-2 of fork-1 Started");
        #20;
        $display($time,"\tProcess-2 of fork-1 Finished");
      end
    join_any
 
    //fork-2
    fork
      //Process-1
      begin
        $display($time,"\tProcess-1 of fork-2 Started");
        #5;
        $display($time,"\tProcess-1 of fork-2 Finished");
      end
      //Process-2
      begin
        $display($time,"\tProcess-2 of fork-2 Started");
        #20;
        $display($time,"\tProcess-2 of fork-2 Finished");
      end
    join_none   
 
    disable fork;
     
    $display("-----------------------------------------------------------------");
      $display($time,"\tAfter disable-fork");
    $display("-----------------------------------------------------------------");
  end
endmodule

在执行完fork-1的process-1后开始执行后面的fork-2,由于fork-2为fork...join_none语句,不会发生阻塞,所以会继续执行下面的disable fork语句,在执行此语句后,中断所有的fork块,包括 fork-1的process-2和fork-2的process-1以及process-1的执行都被终止。运行结果如下:

3. TASK

1.任务定义 
任务定义的形式如下: 
task task_id; 
    [declaration] 
    procedural_statement 
endtask 
其中,关键词 task 和 endtask 将它们之间的内容标志成一个任务定义,task 标志着一个
任务定义结构的开始;task_id 是任务名;可选项 declaration 是端口声明语句和变量声明语
句,任务接收输入值和返回输出值就是通过此处声明的端口进行的;procedural_statement
是一段用来完成这个任务操作的过程语句,如果过程语句多于一条,应将其放在语句块内;
endtask 为任务定义结构体结束标志。下面给出一个任务定义的实例。

:定义一个任务。 
task task_demo;                //任务定义结构开头,命名为 task_demo 
    input  [7:0] x,y;           //输入端口说明 
    output [7:0] tmp;           //输出端口说明 
 
    if(x>y)                  //给出任务定义的描述语句 
      tmp = x; 
  else 
    tmp = y;

endtask 
上述代码定义了一个名为“task_demo”的任务,求取两个数的最大值。在定义任务时,
有下列六点需要注意: 
(1)在第一行“task”语句中不能列出端口名称; 
(2)任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及
双向端口。 
(3)在任务定义的描述语句中,可以使用出现不可综合操作符合语句(使用最为频繁
的就是延迟控制语句) ,但这样会造成该任务不可综合。 
(4)在任务中可以调用其他的任务或函数,也可以调用自身。 
(5)在任务定义结构内不能出现 initial和 always过程块。 
(6)在任务定义中可以出现“disable 中止语句” ,将中断正在执行的任务,但其是不
可综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。

2.任务调用 
虽然任务中不能出现 initial 语句和 always 语句语句, 但任务调用语句可以在 initial 语句
和 always 语句中使用,其语法形式如下: 
task_id[(端口1,  端口 2, ........,  端口 N)]; 
其中 task_id是要调用的任务名,端口 1、端口 2,…是参数列表。参数列表给出传入任
务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果) 。
任务调用语句中,参数列表的顺序必须与任务定义中的端口声明顺序相同。任务调用语句是
过程性语句,所以任务调用中接收返回数据的变量必须是寄存器类型。下面给出一个任务调
用实例。

例:通过 Verilog HDL 的任务调用实现一个 4 比特全加器。

module EXAMPLE (A, B, CIN, S, COUT); 
 
input [3:0] A, B; 
input CIN; 
output [3:0] S; 
output COUT; 
 
reg [3:0] S; 
reg COUT; 
reg [1:0] S0, S1, S2, S3; 
 
task ADD; 
 
input A, B, CIN; 
output [1:0] C; 
 
reg [1:0] C; 
reg S, COUT; 
 
begin

S = A ^ B ^ CIN; 
COUT = (A&B) | (A&CIN) | (B&CIN); 
C = {COUT, S}; 
end 
endtask 
 
always @(A or B or CIN) begin 
ADD (A[0], B[0], CIN, S0); 
ADD (A[1], B[1], S0[1], S1); 
ADD (A[2], B[2], S1[1], S2); 
ADD (A[3], B[3], S2[1], S3); 
S = {S3[0], S2[0], S1[0], S0[0]}; 
COUT = S3[1]; 
end 
endmodule

在调用任务时,需要注意以下几点: 
(1)任务调用语句只能出现在过程块内; 
(2)任务调用语句和一条普通的行为描述语句的处理方法一致; 
(3)当被调用输入、输出或双向端口时,任务调用语句必须包含端口名列表,且信号
端口顺序和类型必须和任务定义结构中的顺序和类型一致。需要说明的是,任务的输出端口
必须和寄存器类型的数据变量对应。 
(4)可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为“0” 。而在面
向仿真的任务中可以带有时序控制,如时延,因此面向仿真的任务的调用时间不为“0” 

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值