記事ディレクトリ
プロセス構造
次の 2 つのステートメントは、デザインの動作をモデル化するための主要なメカニズムです。
initial
声明always
声明
モジュールには、initial
または always
ステートメントをいくつでも含めることができます。これらのステートメントは互いに並列して実行されます。つまり、これらのステートメントが実行される順序は、モジュール内の順序とは無関係です。 initial
または always
ステートメントを実行すると、すべての initial
ステートメントと always
ステートメントに対して単一の制御フローが生成されます。時刻 0 で並列実行を開始します。
initial
声明
initial
ステートメントは 1 回だけ実行されます。 initial
ステートメントは、シミュレーションの開始時、つまり時間 0 に実行されます。構文は次のとおりです。
initial
[timing control] procedural_statement
procedural_statement
次のステートメントのいずれかです。
- procedural_assignment (ブロッキングまたはノンブロッキング)
- procedural_continuous_assignment
- 条件文
- ケースステートメント
- ループステートメント
- wait_statement
- 無効化ステートメント
- イベントトリガー
- シーケンシャルブロック
- 並列ブロック
- タスク有効化
シーケンシャル プロシージャ (begin...end
) は、プロセス ステートメントで最も一般的に使用されます。ここでのタイミング制御には、遅延制御、つまり特定の時間待機することも、イベント制御、つまり特定のイベントが発生するか特定の条件が真になるのを待機することもできます。
always
声明
initial
ステートメントとは異なり、always
ステートメントは繰り返し実行されます。 initial
ステートメントと同様、always
ステートメントの構文は次のとおりです。
always
[timing control] procedural_statement
たとえば、クロック周期 10 の波形を生成するには、次のようにします。
always
#5 clk = ~ clk;
次の例は、イベントによって制御されるシーケンス プロセスですalways
ステートメント:
reg[0:5] InstrReg;
reg[3:0] Accum;
wire ExecuteCycle;
always@(ExecuteCycle)
begin
case(InstrReg[0:1])
2'b00: Store(Accum, InstrReg[2:5]);
2'b11: Load(Accum, InstrReg[2:5]);
2'b01: Jump(InstrReg[2:5]);
2'b10:;
endcase
end
// Store、Load 和 Jump 是在别处定义的用户自定义的任务
イベント制御
イベント制御では、イベントに基づいてalways
の手続き文が実行されます。イベント コントロールには 2 つのタイプがあります。
- エッジトリガーイベント制御
- レベルに応じたイベント制御
エッジ トリガー イベント コントロールは次のとおりです。
@ event procedural_statement
次の例に示すように:
@(posedge clock)
curr_state = next_state
イベント制御を使用したプロセスまたはプロシージャ ステートメントの実行は、指定されたイベントが発生するまで待機する必要があります。上記の例では、clock
信号がロー レベルからハイ レベルに変化した場合 (ポジティブ エッジ)、代入ステートメントが実行されます。そうでない場合、プロセスは clock
信号は次のポジティブエッジを生成します。
レベル依存のイベント制御では、プロセス ステートメントまたはプロセス内の手続きステートメントの実行は、条件が true になるまで遅延されます。レベル依存のイベント コントロールは次の形式で提供されます。
wait(Condition)
procedural_statement
手続き型ステートメントは、条件が true の場合にのみ実行され、それ以外の場合、手続き型ステートメントは条件が true になるまで待機します。ステートメントの実行時に条件がすでに true である場合、手続き型ステートメントは直ちに実行されます。上記の表現では、手続きステートメントはオプションです。
ステートメントブロック
ステートメント ブロックは、2 つ以上のステートメントを文法的に単一のステートメントと同等に結合するメカニズムを提供します。 Verilog には、次の 2 種類のステートメント ブロックがあります。
- シーケンシャル ステートメント ブロック (
begin...end
): ステートメント ブロック内のステートメントは、指定された順序で順次実行されます。 - 並列ステートメント ブロック (
fork...join
): ステートメント ブロック内のステートメントは並列で実行されます。
シーケンスブロック
シーケンシャル ステートメント ブロック内のステートメントは、順番に実行されます。各ステートメントのレイテンシー値は、前のステートメントの実行のシミュレーション時間に関連しています。シーケンスブロックの実行が終了すると、シーケンスブロックの処理に続く次のステートメントの実行が継続されます。順次ステートメント ブロックの構文は次のとおりです。
begin
[:block_id{
declarations}]
procedural_statement(s)
end
例えば
begin
#2 Stream = 1;
#5 Stream = 0;
#3 Stream = 1;
#4 Stream = 0;
#2 Stream = 1;
#5 Stream = 0;
end
ステートメントの連続ブロックが 10 番目の時間単位で実行を開始すると仮定します。最初のステートメントは 2 時間単位後、つまり 12 番目の時間単位後に実行されます。この実行が完了すると、次のステートメントが 17 時間単位で実行されます (5 時間単位遅延)。次に、次のステートメントが 20 番目の時間単位で実行され、以下同様に続きます。
パラレルステートメントブロック
並列ステートメント ブロック内の各ステートメントは並列に実行されます。パラレル ステートメント ブロック内の各ステートメントによって指定される遅延値は、ステートメント ブロックが実行を開始する時間に関連します。並列ステートメント ブロック内の最後のアクションが実行されると (最後のアクションが最後のステートメントであるとは限りません)、シーケンシャル ステートメント ブロック内のステートメントは引き続き実行されます。つまり、パラレル ステートメント ブロック内のすべてのステートメントは、制御がステートメント ブロックの外に移される前に実行を完了する必要があります。例えば
fork
#2 Stream = 1;
#7 Stream = 0;
#10 Stream = 1;
#14 Stream = 0;
#16 Stream = 1;
#21 Stream = 0;
join
並列ステートメント ブロックがタイム ユニット 10 で実行を開始すると、すべてのステートメントが並列で実行され、すべての遅延はタイム ユニット 10 を基準とします。たとえば、3 番目の割り当ては 20 番目の時間単位で実行され、5 番目の割り当ては 26 番目の時間単位で実行されます。
手続き的な割り当て
手続き型代入は、initial
または always
ステートメント内の代入であり、 はレジスタ データにのみ使用できます。変数の割り当てをタイプします。
手続きの割り当ては、次の 2 つのカテゴリに分類されます。
- ブロッキングプロシージャの割り当て
- ノンブロッキング手続き型代入
ブロッキングプロシージャの割り当て
代入演算子は です。=
の手続き型代入はブロッキング手続き型代入です。ブロッキング プロシージャの代入は、後続のすべてのステートメントが実行される前に実行されます。つまり、次のステートメントが実行される前に代入ステートメントの実行が完了します。
ノンブロッキング手続き型代入
ノンブロッキング手続き型代入では、代入記号 <=
を使用します。
ノンブロッキング プロシージャル割り当てでは、ターゲットへの割り当ては (レイテンシのため) ノンブロッキングですが、(レイテンシに応じて) 将来のタイム ステップで実行されるようにスケジュールできます。レイテンシが 0 の場合、現在のタイム ステップで終了します。 )。
begin
Load <= 32;
RegA <= Load;
RegB <= Store;
end
ノンブロッキング手続き型代入が実行されると、右側の式が評価され、右側の値が左側のターゲットに割り当てられ、実行が次のステートメントに続きます。上の例では、ステートメントの連続ブロックが時刻 10 に実行されると想定しています。最初のステートメントにより、10 番目の時間単位の終了時に Load
に値 32 が割り当てられます。その後、2 番目のステートメントが実行され、Load
の値は残ります。変更されていない場合 (時間がまだ進んでおらず、最初の割り当てにまだ新しい値が割り当てられていないことに注意してください)、RegA
の割り当ては 10 番目のタイム ステップの終わりにスケジュールされます。すべてのイベントが 10 番目の時間単位で発生した後、左端のターゲットへのスケジュールされた割り当てがすべて完了します。
以下はブロッキングプロシージャ代入とノンブロッキングプロシージャ代入を両方使用する例ですが、両者の違いに注意してください。
reg[0:2] Q_State;
initial begin
Q_state = 3'b011;
Q_state <= 3'b100;
$display("Current value of Q_State is %b", Q_State);
#5;
$display("The delayed value of Q_State is %b", Q_State);
end
initial
ステートメントを実行すると、次の結果が生成されます:
Current value of Q_State is 011
The delayed value of Q_State is 100
if
声明
if
ステートメントの構文は次のとおりです。
if(condition_1)
procedural_statement_1
{
else if(condition_2)
procedural_statement_2}
{
else
procedural_statement_3}
条件式は常に囲む必要があることに注意してください。if-if-else
形式を使用する場合、次の例に示すようにあいまいさが生じる可能性があります。
if(clk)
if(Reset)
Q = 0;
else
Q = D;
問題は、最後の else
がどの if
に属するかということです。 Verilog は、else
を、else
を持たない最も近い if
に関連付けます。
case
声明
case
このステートメントは複数方向の条件分岐形式であり、その構文は次のとおりです。
case(case_expr)
case_item_expr{
, case_item_expr}: procedural_statement
...
...
[default: procedural_statement]
endcase
case
ステートメント 1 の例は次のとおりです。
module ALU(A, B, OpCode, Z);
input[3:0] A, B;
input[1:2] OpCode;
output[7:0] Z;
reg[7:0] Z;
parameter
ADD_INSTR = 2'b10,
SUB_INSTR = 2'b11,
MUL_INSTR = 2'b01,
DIV_INSTR = 2'b00;
always@(A or B or OpCode)
case(OpCode)
ADD_INSTR: Z = A + B;
SUB_INSTR: Z = A - B;
MUL_INSTR: Z = A * B;
DIV_INSTR: Z = A / B;
endcase
endmodule
ループ文
Verilog には 4 種類のループ ステートメントがあります。
forever
サイクルrepeat
サイクルwhile
サイクルfor
サイクル
forever
サイクル
forever
ループの構文は次のとおりです。
forever
procedural_statement
このループ ステートメントは、手続き型ステートメントを継続的に実行します。したがって、このようなループから抜け出すために、abort ステートメントを手続き型ステートメントと一緒に使用できます。また、プロシージャ ステートメントでは何らかの形式のタイミング制御を使用する必要があります。そうしないと、forever
ループは遅延 0 の後に永久にループします。
initial begin
clock = 0;
#5 forever
#10 clock = ~clock;
end
この例ではクロック波形を生成します。クロックは最初に 0 に初期化され、5 番目の時間単位までそのままになります。その後、10 時間単位ごとに clock
が反転します。
repeat
ループ文
repeat
ループステートメントの形式は次のとおりです。
repeat(loop_count)
procedural_statement
このループ ステートメントは、指定された回数のループに対して手続き型ステートメントを実行します。ループ回数式の値が不定の場合、つまりxまたはzの場合、ループ回数は0として扱われます。
while
ループ文
while
ループ ステートメントの構文は次のとおりです。
while(condition)
procedural_statement
このループ ステートメントは、指定された条件が false になるまで、プロセス代入ステートメントをループします。式の最初が false の場合、手続きステートメントは実行されません。
while(By > 0)
begin
Acc = Acc << 1;
By = by - 1;
end
for
ループ文
for
ループ ステートメントの形式は次のとおりです。
for(initial_assignment;condition;step_assignment)
procedural_statement
for
ループ ステートメントは、プロシージャ代入ステートメントの実行を指定された回数繰り返します。初期代入 initial_assignment
はループ変数の初期値を与えます。 condition
条件式は、どのような状況でループを終了する必要があるかを指定します。条件が true である限り、ループ内のステートメントは実行され、step_assignment
は、通常はループ変数の数を増減することによって、割り当てを変更します。
integer K;
for(K=0; K<MAX_RANGE; K=K+1)
begin
if(Abus[K] == 0)
Abus[K] = 1;
else if(Abus[K] == 1)
Abus[K] = 0;
else
$display("Abus[K] is an x or a z");
end
ハンドシェイクプロトコルの例
always
ステートメントは、有限状態マシンの相互作用など、対話型プロセスの動作を記述するために使用できます。これらのモジュール内のステートメントは、すべての always
ステートメントに表示されるレジスタを使用して相互に通信します。
RX 受信機と MP マイクロプロセッサという 2 つの対話プロセスの例を考えてみましょう。 RX プロセスはシリアル入力データを読み取ります。そして、 Ready
シグナルを送信して、データを MP プロセスに読み込むことができることを示します。 MP プロセスはデータを出力に割り当てた後、新しい入力データを読み取るために受信信号 Ack
を RX プロセスに送り返します。 2 つのプロセスのステートメント ブロック フローを次の図に示します。
これら 2 つの相互作用するプロセスの動作は、次の動作モデルで説明できます。
`timescale 1ns/100ps
module Interacting(Serial_In, Clk, Parallel_Out)
input Serial_In, Clk;
output[0:7] Parallel_Out;
reg[0:7] Parallel_Out;
reg Ready, Ack;
wire[0:7] Data;
`include "Read_Word.v"
always
begin:RX
Read_Word(Serial_In, Clk, Data);
// 任务 Read_Word 在每个时钟周期读取串行数据,将其转换为并行数据并存于 Data中,完成该任务需要 10ns
Ready = 1;
wait(Ack);
Ready = 0;
#40;
end:RX
always
begin:MP
#25;
Parallel_Out = Data;
Ack = 1;
#25 Ack = 0;
wait(Ready);
end:MP
endmodule
レジスタReady
と Ack
を介した 2 つのプロセス間の対話型ハンドシェイク プロトコルは、次の図に示すとおりです。