Systemverilog スレッドとスレッド間の通信 (1)

実際のハードウェアでは計算は並行して実行されますが、Verilog では初期代入、常時代入、継続代入によってシミュレートされますが、テスト プラットフォームではこれらのステートメント ブロックを Verilog でシミュレートおよびテストするために、tb は多くの同時スレッドを使用します。

1. スレッドの定義と使用

1.1 スレッドを定義する

Initial、always、assign はすべてプロセスであり、first time 以外にも次のようなプロセスがあります。

  1. フォーク結合

    その中のステートメントは同時実行され、次のステートメントは fork-join ブロックの実行後に実行されます。

  2. フォーク結合_なし

    ブロック内のステートメントは同時実行され、ブロックの後のステートメントはブロックされません。ブロック内のステートメントとブロックの後のステートメントは同時実行されます。

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_none と似ていますが、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 

分析: fork 内のステートメントが最初に実行され、line1 と line2 が並行して実行され、line2 が最初に実行されます。このときのシミュレーション時間は 10ns です。これ以降、fork ブロックの後のステートメントも実行できるようになります。つまり、line3、line3 の出力にはまだ 15ns 必要ですが、この時点では line1 は 10ns シミュレートされており、出力にはさらに 10ns かかるため、line1 を最初に出力します。 、次に行3

1.2 動的スレッド

クラス内にスレッドを作成するには、上記の 3 つのフォーク ブロックを使用します。

各フォーク ブロックはスレッドの開始とみなすことができます。

ループ内でスレッドを開始する場合は、automatic キーワードを使用して変数を自動的に作成し、スレッドごとにメモリが個別に割り当てられるようにする必要があります。

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

1.3 すべての子スレッドが終了するまで待ちます

SVではすべてのイニシャルが実行された後にシミュレーションが終了しますが、スレッドによっては実行に時間がかかり、実行前にシミュレーションが終了してしまう場合があります。

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

wait fork は、スレッドの実行が完了するまで待機します。

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

1.4 スレッドの停止を無効にする

  1. 単一のスレッドを停止する

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

  2. 複数のスレッドを停止する

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

    timeout_fork ブロックは、無効にするフォークの範囲を制限するために使用され、上記のコードでの無効化は run_fork2() には影響しません。

  3. 複数回呼び出されるタスクを無効にする

    タスク内でプロセスが開始された場合、そのタスクが無効になると、そのタスクによって開始されたすべてのプロセスが停止されます。タスクが別の場所で呼び出された場合は、他のプロセスも無効になります。

    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

    2ns で、time_out(0) タスクを停止します。この時点で、他の 2 つのタスクも無効になり、実行できなくなります。

    すべてのタスクを無効にする場合は注意してください。

2. スレッド間通信

テスト プラットフォームのすべてのスレッドはデータを転送する必要があり、複数のスレッドが同時に同じデータにアクセスする可能性があります。テスト プラットフォームのコードは、同時に 1 つのスレッドのみがアクセスできるように設計されています。

このデータ交換と制御の同期は、スレッド間通信 (IPC) と呼ばれます。

3. イベントイベント

Verilog のイベントは SV で拡張されました。

1. イベントをパラメータとしてメソッドに渡すことができます。

2. トリガー機能の導入

Verilog では、@、-> 演算子を使用してイベントをブロックおよびトリガーします。1 つのスレッドがイベントをブロックしているときに、別のスレッドが同時にイベントを起動すると、競合が発生する可能性があります。また、起動がブロックよりも先に発生すると、起動が失敗します。

トリガー関数は SV に導入されており、現在のタイムスロット トリガー (タイムスロット) を含め、イベントがトリガーされたかどうかをクエリできます。Triggered to return 1. このようにして、 @ を使用してブロックせずに、 wait を使用してこの関数の結果を待つことができます。

@e1 はエッジに依存するブロック ステートメントであり、wait(e1.triggered()) はレベルに依存します。

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 ループ内でのイベントの使用

@e1 はエッジに依存するブロック ステートメントであり、wait(e1.triggered()) はレベルに依存します。

ループ内でイベントを使用する場合、ループの遅延が 0 の場合、少し問題が発生します。

  1. レベルに敏感なブロッキング

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

    待機は継続的に発生し、シミュレーション時間は進みませんwait がトリガーされるため、ループの実行後も、e1.triggered() は現在のタイム スライスで 1 を返し、wait がトリガーされ続けます。

    改善: ループに遅延を追加しました。

  2. エッジセンシティブブロッキング

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

    エッジ トリガーは、遅延が 0 の場合でも、トリガーは 1 回だけです。

3.2 パラメータとしてのイベント

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 複数のイベントの待機

ジェネレーターが複数ある場合は、すべてのジェネレーターのスレッドの実行が完了するまで待つ必要があります。

方法 1. 待機フォークを使用する

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

方法 2: カウンターを使用する

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

方法 3. イベントを削除し、静的変数のみを使用してカウントする

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

おすすめ

転載: blog.csdn.net/m0_38037810/article/details/125022101