実際のハードウェアでは計算は並行して実行されますが、Verilog では初期代入、常時代入、継続代入によってシミュレートされますが、テスト プラットフォームではこれらのステートメント ブロックを Verilog でシミュレートおよびテストするために、tb は多くの同時スレッドを使用します。
1. スレッドの定義と使用
1.1 スレッドを定義する
Initial、always、assign はすべてプロセスであり、first time 以外にも次のようなプロセスがあります。
-
フォーク結合
その中のステートメントは同時実行され、次のステートメントは fork-join ブロックの実行後に実行されます。
-
フォーク結合_なし
ブロック内のステートメントは同時実行され、ブロックの後のステートメントはブロックされません。ブロック内のステートメントとブロックの後のステートメントは同時実行されます。
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块内语句与之后的语句是并行的,不会阻塞之后的语句
-
フォーク結合_任意
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 スレッドの停止を無効にする
-
単一のスレッドを停止する
initial begin fork:timeout_fork //fork有标识符 run_fork0();//任务内有fork run_fork1();//任务内有fork join_none #100; disable timeout_fork; // disable+标识符 end
-
複数のスレッドを停止する
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() には影響しません。
-
複数回呼び出されるタスクを無効にする
タスク内でプロセスが開始された場合、そのタスクが無効になると、そのタスクによって開始されたすべてのプロセスが停止されます。タスクが別の場所で呼び出された場合は、他のプロセスも無効になります。
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 の場合、少し問題が発生します。
-
レベルに敏感なブロッキング
initial begin forever begin wait(e1.triggered()); $display(...); end end
待機は継続的に発生し、シミュレーション時間は進みません。wait がトリガーされるため、ループの実行後も、e1.triggered() は現在のタイム スライスで 1 を返し、wait がトリガーされ続けます。
改善: ループに遅延を追加しました。
-
エッジセンシティブブロッキング
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