Systemverilog thread and communication between threads (1)

In the actual hardware, calculations are performed concurrently. In Verilog, it is simulated through initial, always, and continuous assignment. In the test platform, in order to simulate and test these statement blocks in Verilog, tb uses many concurrent threads.

1. Definition and use of threads

1.1 Define threads

initial, always, and assign are all processes. Besides the first time, there are:

  1. fork join

    The statements in it are concurrent, and the following statements are executed after the fork-join block is executed.

  2. fork join_none

    The statements in it are concurrent and will not block the statements after the block. The statements in the block and the statements after the block are concurrent.

initial begin
    语句1;
    #10;
    fork
        语句2;
        语句3;
    join_none
    语句4;
    语句5;
end
// 执行顺序
// #0 语句1
// #10 语句2,语句3,语句4并发执行
// #10 语句4执行完之后才执行语句5。4执行完之后,即使2,3没执行完,也会接着执行5,因为fork块内语句与之后的语句是并行的,不会阻塞之后的语句
  1. fork join_any

    Similar to fork Join_none, except that the process in a block must be executed before continuing to execute the statement block after fork..join_any.

initial begin
  fork
      #20 $display("@ %0t [line1] seq 20",$time);
      #10 $display("@ %0t [line2] seq 10",$time);
  join_any
    #15  $display("@ %0t [line3] after seq 15",$time);
end
//结果
@ 10 [line2] seq 10
@ 20 [line1] seq 20
@ 25 [line3] after seq 15 

Analysis: The statement in fork must be executed first, line1 and line2 are executed in parallel, and line2 is executed first, and the simulation time at this time is 10ns. From now on, the statement after the fork block can also be executed, that is, line3, line3 still needs 15ns to output, and at this time line1 has been simulated for 10ns, and it takes another 10ns to output, so output line1 first, then line3

1.2 Dynamic threads

To create a thread in a class, use the above three fork blocks.

Each fork block can be regarded as starting a thread.

If you start threads in a loop, you need to use the automatic keyword to automatically create variables, so that memory is allocated separately for each thread.

initial begin
    for(int i=0;i<3;i++)
        fork
            automatic int k=i;
            $display(k);
        join_none
end

1.3 Wait for all child threads to end

In SV, the simulation ends after all the initials are executed. Some threads take a long time to execute, and the simulation may end before they are executed.

initial begin
    ....
    run_fork(); //调用run_fork()之后,继续执行a<=b,最后一条语句结束,仿真结束,此时
                    // run_fork()可能还没执行完。
    a <= b;
    ....
end

wait fork to wait for the threads to finish executing.

task run();
    fork:fork_all
        run_fork0(); //线程0 ;任务内有fork
        run_fork1();//任务内有fork
        run_fork2();//任务内有fork
        ...
    join_none
    wait fork; // 等待fork_all中的所有线程都执行完
endtask

1.4 stop thread disable

  1. stop a single thread

    initial begin
        fork:timeout_fork  //fork有标识符
            run_fork0();//任务内有fork
            run_fork1();//任务内有fork
        join_none
        #100;
        disable timeout_fork; // disable+标识符
    end

  2. stop multiple threads

    initial begin
        run_fork2();
        fork // timeout_fork
            begin
                run_fork0();//任务内有fork
                run_fork1();//任务内有fork
                #100 disable fork;// 将disable_fork进程中所有的子进程都停止,不需要标识符。
            end
        join
    end

    The timeout_fork block is used to limit the range of the fork to be disabled, and the disable in the above code has no effect on run_fork2().

  3. Disable tasks that are called multiple times

    If a process is started in a task, when the task is disabled, all processes started by the task will be stopped; if the task is called elsewhere, other processes will also be disabled.

    task time_out(input int i);
        if(i==0)
            #2 disable time_out; // 如果i等于0,那么2ns之后停止任务
        ....
    endtaks
    initial begin
        time_out(0);
        time_out(1);
        time_out(2);
    end

    At 2ns, stop the time_out(0) task, and at this time, the other two tasks will also be disabled, so that they cannot be executed.

    Be careful with all disable tasks.

2. Inter-thread communication

All threads in the test platform need to transfer data, and multiple threads may access the same data at the same time. The code of the test platform is such that only one thread can access it at the same time.

This synchronization of data exchange and control is called inter-thread communication (IPC).

3. Event event

The event in Verilog has been extended in SV:

1. event can be passed as a parameter to the method.

2. Introduced the triggered function

In Verilog, @, -> operators are used to block and trigger events. If one thread is blocking an event while another thread fires an event at the same time, a race can occur, and if the fire precedes the block, the fire is missed.

The triggered function is introduced in SV, which can query whether an event is triggered, including the current time slot trigger (time slot). Triggered to return 1. In this way, you can use wait to wait for the result of this function without using @ to block.

@e1 is an edge-sensitive blocking statement; wait(e1.triggered()) is level-sensitive.

event e1,e2;
initial begin:i1
    @e1; // 先执行i1块,发现阻塞
    $display(....);
    ->e2; //执行完代码后触发e2,开始执行i2
end
initial begin:i2
    #1; // 1ns后触发e1,并且阻塞在e2
    ->e1;
    @e2;
    $display(...);  
end
event e1,e2;
initial begin
    wait(e1.triggered());
    $display(....);
    ->e2;
end
initial begin
    #1;
    ->e1;
    wait(e2.triggered());
    $display(...);
end

3.1 Using events in loops

@e1 is an edge-sensitive blocking statement; wait(e1.triggered()) is level-sensitive.

Using events in a loop, if the loop is 0 delay, then there will be a bit of a problem:

  1. level sensitive blocking

    initial begin
        forever begin
            wait(e1.triggered());
            $display(...);
        end
    end

    The wait will fire continuously, and the simulation time will not advance . Because wait is triggered, after executing a loop, e1.triggered() still returns 1 in the current time slice, and wait continues to trigger.

    Improvement: added delay in loop.

  2. edge sensitive blocking

    initial begin
        forever begin
            @e1;
            $display(...);
        end
    end

    Edge triggering, even with 0 delay, only triggers once.

3.2 Events as parameters

class Generator;
    event e;
    function new(event e1) //传入事件
        this.e = e1;
    endfunction
    task run()
        ...
        ->e; //触发
    endtask
endclass
class Driver;
    event e;
    function new(event e2);//传入事件
        this.e=e2;
    endfunction
    task run();
        @e;  //等待触发
        // wait(e.triggered());
        ...
    endtask
endclass
program test;
    Generator gen;
    Driver drv;
    event e;
    initial begin
        gen=new(e);
        drv=new(e);
        fork
            gen.run();
            drv.run();
        join
    end
endpragram

3.3 Waiting for multiple events

If there are multiple generators, it is necessary to wait for the threads of all generators to finish executing.

Method 1. Use wait fork

event done[N];// N是发生器数目
initial begin
    foreach (gen[i])begin
        gen[i]=new(done[i]);
        gen[i].run();
    end
    foreach(gen[i]) fork
        automatic int k=i;
        wait(done[k].triggered());
    join_none
    wait fork; //等待所有的fork执行完
end

Method 2: Use a counter

event done[N];// N是发生器数目
int cnt;
initial begin
    foreach (gen[i])begin
        gen[i]=new(done[i]);
        gen[i].run();
    end
    foreach(gen[i]) fork
        automatic int k=i;
        begin //begin块
            wait(done[k].triggered());
            cnt++; //触发一个,计数加一。
        end
    join_none
    wait(cnt==N); //等待计数到N。说明所有的fork执行完毕,所有的事件都触发
end

Method 3. Get rid of events and only use static variables to count

class Generator ;
    static int cnt=0;
    task run();
        cnt++; // 调用run计数加一
        fork
            begin
               ....
                cnt--; //代码执行完毕,cnt减一。
            end
        join_none
    endtask
endclass
initial begin
    foreach (gen[i])  gen[i]=new();
    foreach (gen[i])  gen[i].run();
    wait(Generator::cnt == 0); //gen启动时都+1,结束时都-1,最终结果0.
end

Guess you like

Origin blog.csdn.net/m0_38037810/article/details/125022101