RTL コードでは、組み合わせロジックを表現する場合は「=」代入を使用し、順序ロジックを表現する場合は「<=」代入を使用する場合、このルールに従って設計しないと、予期しない答えが得られることがよくあります。テストベンチでは割り当て番号の要件にはあまり注意を払っていませんが、「=」と「<=」を使用して値を割り当てることができ、どちらも結果をシミュレーションでき、最終的には実際の回路に合成されません。機能には影響しません。インターネット上のさまざまな情報チュートリアルにもさまざまな記述方法がありますが、テストベンチで「=」や「<=」の代入を何気なく使用しても、本当にテストに影響はないのでしょうか。次のテスト検証の後、予期しない答えが得られました。
テスト検証
RTLコード
まず、テスト対象の RTL コード (単純な 2 入力 1 ビット データ AND) を書き込み、レジスタを介して出力します。
//========================================================================
// module_name.v :rtl_template.v
// Created on :2023-9-27
// Author :YprgDay
// Description :用于Verilog仿真文件中的阻塞和非阻塞问题探讨
//========================================================================
module test
(
//=========================< Port Name >==============================
//input
input wire sys_clk ,
input wire sys_rst_n ,
input wire in1 ,
input wire in2 ,
//output
output reg out
);
//=========================< Always block >===========================
//sequential logic
//block description:用于两输入的与
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)begin
out <= 1'b0 ;
end
else begin
out <= in1 & in2 ;
end
end
endmodule
1. クロックの初期値は 1'b1 です
1.1. クロックは「=」、入力信号は「<=」で割り当てられます(正)
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
in1 <= 1'b0;
in2 <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 <= {$random};
always #10 in2 <= {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
シミュレーション結果は、RTL ロジック コードによって実装された関数と一致しています。
ここでは、220ns 後は 2 クロック サイクル高くなり、340ns 後は 1 クロック サイクル高になります。同時に、この場合の「|」、「+」、および「^」演算がすべて正しいこともテストされました。
1.2. クロック信号と入力信号の両方に「<=」(エラー)が割り当てられている
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk <= 1'b1;
sys_rst_n <= 1'b0;
in1 <= 1'b0;
in2 <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk <= ~sys_clk;
always #10 in1 <= {$random};
always #10 in2 <= {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
1.1との比較。ここでは、200ns 後の 1 クロック サイクルで High になり、320ns 後の 1 クロック サイクルで High になります。エラーがあり、この場合の「|」、「+」、および「^」演算もテストされましたが、すべて正しくありませんでした。
1.3. クロック信号と入力信号の両方に「=」(エラー)が割り当てられている
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
in1 = 1'b0;
in2 = 1'b0;
#200
sys_rst_n = 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 = {$random};
always #10 in2 = {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
1.1との比較。ここでは、200ns 後の 1 クロック サイクルで High になり、320ns 後の 1 クロック サイクルで High になります。エラーがあり、この場合の「|」、「+」、および「^」演算もテストされましたが、すべて正しくありませんでした。
1.4. クロックが「<=」で、入力信号が「=」で割り当てられている(エラー)
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk <= 1'b1;
sys_rst_n = 1'b0;
in1 = 1'b0;
in2 = 1'b0;
#200
sys_rst_n = 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 <= {$random};
always #10 in2 <= {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
1.1との比較。ここでは、220ns 後の 2 つのクロック サイクルが High であり、340ns 後のクロック サイクルも High であり、これは 1.1 と同じですが、この場合の「|」、「+」、および「^」演算はテストされており、エラーでした。
2. クロックの初期値は 1'b0 です
2.1. クロックは「=」、入力信号は「<=」で割り当てられます(正)
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
in1 <= 1'b0;
in2 <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 <= {$random};
always #10 in2 <= {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
シミュレーション結果は、RTL ロジック コードによって実装された関数と一致しています。
ここでは、210ns 後に 1 クロック サイクルで High になり、330ns 後に 1 クロック サイクルで High になります。同時に、この場合の「|」、「+」、および「^」演算がすべて正しいこともテストされました。
2.2. クロック信号と入力信号の両方に「<=」(エラー)が割り当てられている
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
in1 <= 1'b0;
in2 <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk <= ~sys_clk;
always #10 in1 <= {$random};
always #10 in2 <= {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
2.1との比較。ここでは、210ns 後に 2 クロック サイクルで High になり、330ns 後に 1 クロック サイクルで High になります。エラーがあり、この場合の「|」、「+」、および「^」演算もテストされましたが、すべて正しくありませんでした。
2.3. クロック信号と入力信号の両方に「=」(エラー)が割り当てられています
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
in1 = 1'b0;
in2 = 1'b0;
#200
sys_rst_n = 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 = {$random};
always #10 in2 = {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
2.1との比較。ここでは、210ns 後に 2 クロック サイクルで High になり、330ns 後に 1 クロック サイクルで High になります。エラーがあり、この場合の「|」、「+」、および「^」演算もテストされましたが、すべて正しくありませんでした。
2.4. クロックが「<=」で、入力信号が「=」で割り当てられている(エラー)
シミュレーションコード:
`timescale 1ns/1ns
module tb_test();
reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;
wire out;
//初始化
initial begin
sys_clk <= 1'b0;
sys_rst_n = 1'b0;
in1 = 1'b0;
in2 = 1'b0;
#200
sys_rst_n = 1'b1;
end
//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;
always #10 in1 = {$random};
always #10 in2 = {$random};
//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2
.out (out ) //output out
);
endmodule
シミュレーション波形の効果:
2.1との比較。ここでは、210ns 後に 2 クロック サイクルで High になり、330ns 後に 1 クロック サイクルで High になります。エラーがあり、この場合の「|」、「+」、および「^」演算もテストされましたが、すべて正しくありませんでした。
結論は
クロックの初期値が 1'b1 またはクロックの初期値が 1'b0 の場合、クロックには「=」、入力信号には「<=」が割り当てられ、エラーは発生しません。場合によってはエラーが発生します。
したがって、テストベンチを作成するときは、この状況を回避するために、クロックには「=」を割り当て、入力信号には「<=」を割り当てることをお勧めします。この問題の根本的な原因は、実際には、生成されたデータがSystem Verilog ではこのような状況は起こりませんが、これが System Verilog がシミュレーション検証言語としてより適している理由の 1 つです。クロックの初期値が0か1かは、シミュレーションの精度にはほとんど影響しませんが、データの立ち上がりで変化するようクロックの初期値を1に設定することを推奨します。これは、RTL コードとの一貫性がより高くなります。
推奨される書き込み方法:
//==========================< Reset block >============================
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
in1 <= 1'b0;
in2 <= 1'b0;
#200
sys_rst_n <= 1'b1;
end