[SystemVerilog] fork join_none

SV中用的比较多的是fork…join_none,以及disable fork,wait fork;
其中,wait_fork会阻止当前线程,直到所有子线程完成;
disable fork会kill disable fork 所在的当前线程以及所有子线程
具体可看文章disable label and disable fork

The parent process continues to execute concurrently with all the processes spawned by the fork. The spawned processes do not start executing until the parent thread executes a blocking statement.
from LRM

父线程继续与fork产生的所有子线程同时执行; 生成的子线程不会开始执行,直到父线程执行阻塞语句或终止 。


上面那句话有两层含义:

automatic variables get created upon entry and initialized before executing any procedural statement within the block they are located in.

位于fork join_none中的 automatic变量的创建和初始化会比procedural statement 先执行,且与父线程同时执行。


Each statement within a fork/join_none becomes as new child process and execution of the child process does not begin until the current parent thread suspends.
位于fork join_none中的procedural statement 不会开始执行,直到父线程执行阻塞语句或终止。

sv为下列进程产生一个执行线程

  • 每一个initial块;
  • 每一个always块 ;
  • fork…join语句的每一个并行语句;
  • 每一个动态进程(fork join_any, join_none);
  • 每一个连续赋值也可以认为是它自己的线程;

对于begin…end 而言,处于initial、fork…join(join_any, join_none)中是一个线程,除此之外不是线程~~


应用场景

initial
  begin 
    N = $urandom_range(1,10);
    for( int j = 1; j <= N; ++j )
      fork
         automatic int k = j; // local copy, k, for each value of j
         start_a_thread(k);
      join_none
   end

注意以下几种场景的区别:

program automatic test; //declare automatic
  initial begin
    for(int i=0;i<16;i++)
      send(i);
    end

    task send(int j);//int j 为automatic 变量,每次call send 均会为j分配新的存储空间
      fork
        begin
          $display("Driving port %0d",j);
          #1;
        end
      join_none
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 0
Driving port 1
Driving port 2
Driving port 3
...

program test;
  initial begin
    for(int i=0;i<16;i++)
      send(i);
    end

    task send(int j); //int j 为static变量
      fork
        begin
          $display("Driving port %0d",j);
          #1;
        end
      join_none
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 15
Driving port 15
Driving port 15
Driving port 15
...

program中一个任务如果没有被标示为automatic,则默认为static。在这个任务的调用过程中就只会分配一块内存空间,所以每一次该任务的调用都会导致上一次任务的内存被重写。例子中send被调用了许多次,但是send任务里面的int j(static 变量) 这个变量其实共用了同一个地址。
再看join_none语句,它指示父线程不会被阻塞,而会继续执行,子线程会加入等待队列。也就是说i会一直被加到15跳出循环后,才会执行循环中产生的16个send任务(线程),而此时i的值已经是15了,按前面说的这个任务是static的,所以再执行display只会打印出15.
如果将task send; 改为task automatic send 就可以实现15-0的打印。

上面两个栗子是说明task中static变量 和automatic 变量的区别,下面的栗子是说明fork join_none执行的差异。


program automatic test;
  initial begin
    for(int i=0;i<16;i++)
      fork
        send(i);
      join_none
    end

    task send(int j);
        begin
          $display("Driving port %0d",j);
          #1;
        end
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 16
Driving port 16
Driving port 16
Driving port 16
...

//case 1
program automatic test;
  initial begin
    for(int i=0;i<16;i++)
      fork
        int jj = i;//variable creation and initialization;and it occurs before spawning any process within the fork/join_none
        send(jj);
      join_none
    end

    task send(int j);
        begin
          $display("Driving port %0d",j);
          #1;
        end
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 0
Driving port 1
Driving port 2
Driving port 3
...

//case 2
program automatic test;
  initial begin
    for(int i=0;i<16;i++)
      fork
        int jj ; //variable create 
        jj = i; //variable initialization as a separate procedural assignment statement
        send(jj);
      join_none
    end

    task send(int j);
        begin
          $display("Driving port %0d",j);
          #1;
        end
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 16
Driving port 16
Driving port 16
Driving port 16
...

//case 3
program automatic test;
  initial begin
    for(int i=0;i<16;i++)
      fork
        begin   //加上begin..end
          int jj = i;
          send(jj);
        end
      join_none
    end

    task send(int j);
        begin //a single statement
          $display("Driving port %0d",j);
          #1;
        end
    endtask
  end
endprogram

result :

simulation ends at time 0.
Driving port 16
Driving port 16
Driving port 16
Driving port 16
...

there will be 16 concurrent variables named index and only one named i. In case 1) & 2), the index variable gets created upon each entry into the fork/join_none block. That occurs before spawning any process within the fork/join_none. In case 1) the variable initialization also occurs before any process within the fork/join_none. The thing that you need to remember is that automatic variables get created upon entry and initialized before executing any procedural statement within the block they are located in.

  • In case 1, each index variable gets the current value of i in each loop iteration.
  • In case 2, you moved the initialization into a separate procedural assignment statement. Each statement within a fork/join_none becomes as new child process and execution of the child process does not begin until the current parent thread suspends. The for loop now spawn 16 threads before suspending at the wait fork statement when the value of i is 16. ( it is a race if send(index) sees the uninitialized value 0 or assigned value 16.)
  • In case 3, the index variable is now declared inside a begin/end block, which is a single statement for the fork/join_none. So now the index variable does not get created until all 16 processes have been spawned, and the value of i is 16.

你可能感兴趣的:(SV)