进程

进程或线程是作为独立实体执行的任何代码片段。fork-join块创建并行运行的不同线程。

在下面的图-1中,我们可以看到进程的类型和进程控制。

Untitled Diagram drawio(4)

序号进程描述
1.fork-join只有所有子线程执行完毕时,父线程才会执行。
2.fork-join_any只有任何一个子线程执行完毕时,父线程才会执行。
3.fork-join_none父线程与子线程并行执行。
4.wait fork使父线程等待所有子线程执行完毕。
5.disable fork当执行disable fork时,会终止所有子线程的执行。
6.细粒度进程控制这些用于控制进程,并提供进程/线程的状态。

1. 进程或线程#

我们有3种类型的线程/进程

  1. fork-join
  2. fork-join_any
  3. fork-join_none

1.1 fork-join#

SystemVerilog通过fork-join结构支持并行线程。在fork-join进程中,只有当所有子线程完成执行时,父线程才会执行。

语法:-

fork  
   线程1  
   线程2  
   线程3
join

代码片段:-

$display("[%0t] Thread_T1: a的值为%0d,b的值为%0d,c的值为%0d,d的值为%0d",$time,a,b,c,d);

fork:FORK_F1  

   begin:BEGIN_B2  
      #1 a <= b;  
      b <= 7;  
      $monitor("[%0t] Thread-T2: a的值为%0d,b的值为%0d,c的值为%0d,d的值为%0d",$time,a,b,c,d);  
      #1 ->e1;  
      c = b;  
   end:BEGIN_B2  

   begin:BEGIN_B3  
      wait(e1.triggered);  
      $display("[%0t] 事件已触发",$time);  

      begin:BEGIN_B4  
        #1 d = c;  
      end:BEGIN_B4  

   end:BEGIN_B3  

join:FORK_F1 

$display("[%0t] Thread_T3: a的值为%0d,b的值为%0d,c的值为%0d,d的值为%0d",$time,a,b,c,d); 

输出
在图-2中,我们可以看到Thread_T1首先在#0模拟时间执行,但是Thread_T3将在所有子线程执行完毕后才执行,子线程将根据时间延迟执行。

fork_join_output

在图-3中,您可以清楚地了解关于fork-join代码的整个工作方式,以及与时间表区域相关的调度原理。

  • 变量的取样将在提前的区域中完成。
  • 所有阻塞赋值将在活动区域执行,所有非阻塞赋值将在活动区域中评估。
  • 事件将在活动区域执行。
  • $display语句将在活动区域执行。
  • 所有#0延迟语句将在非活动区域中执行。
  • 评估的非阻塞赋值将在NBA区域中执行。
  • $monitor语句将在推迟的区域中执行。

fork_join (1)

1.2 fork-join_any#

当任何一个子线程完成执行时,父线程将执行。这意味着如果您的fork-join_any块中有2个或更多线程,并且每个线程需要不同的时间完成。在这种情况下,无论哪个线程先完成,fork-join_any都将退出该块,并开始执行模拟中的下一个父线程/语句。这并不意味着剩余的子线程将被模拟自动丢弃。这些线程将在后台运行。

语法:-

fork  
   Thread 1  
   Thread 2  
   Thread 3  
join_any  

代码片段:-

$display("[%0t] Thread_T1: Starting of fork_join_any",$time);

a = "Kapu";
c = "Malpe";

fork:FORK_F1  

   begin:BEGIN_B2  
      #0 $display("[%0t] Thread_T2: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);  

      begin:BEGIN_B3  
         b <= a;  
         #1 $display("[%0t] Thread_T3: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);  
      end:BEGIN_B3  

   end:BEGIN_B2  

   fork:FORK_F2  

      begin:BEGIN_B4  
         #3 -> e1;  
         $display("[%0t] Thread_T4: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);  
      end:BEGIN_B4  

   join:FORK_F2  
      
join_any:FORK_F1

#1 $display("[%0t] Thread_T5: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);
    
begin:BEGIN_B5
   wait(e1.triggered);
   d = "Kodi";
   $monitor("[%0t] Thread_T6: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);
end:BEGIN_B5

输出:-
在下图中,我们可以看到父线程Thread_T1在#0处执行,子线程Thread_T3在#1处执行,然后只有父线程Thread_T5将在#2处执行。

Untitled Diagram drawio (23)

fork join_any

1.3 fork-join_none#

父线程与子线程并行执行。这意味着在fork-join_none外部的线程不会等待fork-join_none内部的任何线程完成,它们只是并行执行。这并不意味着模拟会自动丢弃其余的子线程。这些线程将在后台运行。

语法:-

fork 
   Thread 1  
   Thread 2  
   Thread 3  
join_none 

代码片段:-

$display("[%0t] Thread_T1: Starting of fork_join_none",$time);

a = "Kapu";
c = "Malpe";

fork:FORK_F1  

   begin:BEGIN_B2  
      #1 $display("[%0t] Thread_T2: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);      
      b <= a;  
      #1 $display("[%0t] Thread_T3: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);  
   end:BEGIN_B2  

   fork:FORK_F2  
      #1 -> e1;  
      $display("[%0t] Thread_T4: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);  
   join:FORK_F2  

join_none:FORK_F1

#1 $display("[%0t] Thread_T5: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);

wait(e1.triggered);
d = "Kodi";

$monitor("[%0t] Thread_T6: Values of a =%0s,b =%0s,c =%0s,d =%0s",$time,a,b,c,d);

输出:在下图中,在#0处执行父线程Thread_T1和子线程Thread_T4,然后在#1处同时执行父线程Thread_T5和子线程Thread_T2,以此类推。

Untitled Diagram drawio (28)

fork_join_none


2. 进程控制#

System Verilog提供了允许一个进程终止或等待其他进程完成的构造。

  1. wait fork
  2. disable fork
  3. 细粒度进程控制

2.1 wait fork#

wait fork语句用于确保所有子进程(由调用进程创建的进程)都已完成执行。它将等待直到所有fork进程完成执行。

代码片段:-

#1 $display("[%0t] Thread_T1: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);

fork:FORK_F1  

   #2 b <= "Delta";  
   #0 $display("[%0t] Thread_T2: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  

   begin:BEGIN_B2  
      #1 -> e1;  
      c = "Hoode";  
      #1 $display("[%0t] Thread_T3: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  
   end:BEGIN_B2  

   fork:FORK_F2  
      wait(e1.triggered);  
      #2 $display("[%0t] Thread_T4: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  
   join:FORK_F2  

   #1 $display("[%0t] Thread_T5: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  

join_none:FORK_F1  

wait fork;  
#0 $monitor("[%0t] Thread_T6: values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  

输出:-

在下图中,我们看到在#1处,父线程Thread_T1将被执行,并且有一个#0语句将在非活动区域中工作,语句将在相应区域中执行。尽管我们使用了fork-join_none,但$monitor语句将等待所有子线程被执行。

Untitled Diagram drawio (26)

wait_fork

2.2 disable fork#

执行disable fork时,所有活动的进程都将被终止。

代码片段:-

#0 $display("[%0t] Thread_T1: Values of a = %0s,b = %0s,c = %0s",$time,a,b,c);

fork:FORK_F1  

   #3 b <= "Delta";  

   #4 $display("[%0t] Thread_T2: Values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  
             
   begin:BEGIN_B2  
      #1 -> e1;  
      c = "Hoode";  
      #1 $display("[%0t] Thread_T3: Values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  
   end:BEGIN_B2  
      
   fork:FORK_F2  
      @(e1.triggered);  
      #1 $display("[%0t] Thread_T4: Values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  
   join:FORK_F2  
      
   #1 $display("[%0t] Thread_T5: Values of a = %0s,b = %0s,c = %0s",$time,a,b,c);  

join_any:FORK_F1  

disable fork;  
#1 $display("[%0t] Thread_T6: ending of fork-join",$time);   

输出:-

在下图中,在#0处,我们正在等待事件被触发,#0语句将在活动区域中执行,因为它是$display语句。 在#1处,它正在触发事件e1,并且一个子线程Thread_T5将被执行,然后由于使用了fork-join_any,它将转到父线程并触发disable fork语句,然后所有剩余的子线程将被终止。

Untitled Diagram drawio (27)

disable_fork