Verilog 学習メモ (4): シミュレーションの検証とテストベンチの作成

記事ディレクトリ


1. Verilog回路シミュレーションと検証の概要

シミュレーションは、シミュレーションとも呼ばれ、EDA シミュレーション ツールを使用して、テスト信号を入力し、出力信号 (波形、テキスト、または VCD ファイル) と期待値を比較し、設計を検証することによって、期待どおりの正しい設計結果が得られるかどうかを確認することです。正しさ。
検証は、設計アイデアがどのように実装されているかを証明し、設計が機能的に正しいことを確認するプロセスです。

検証は、Verilog 設計のプロセス全体で 4 つの段階に分かれています。

  • フェーズ 1: 機能検証。
  • フェーズ 2: 合成後の検証。
  • フェーズ 3: タイミング検証。
  • フェーズ 4: 取締役会レベルの検証。

2. Verilog テスト プログラムの設計の基礎

2.1テストベンチとその構造

シミュレーション中、テストベンチは検証中のデザイン (DUV) またはテスト中のデザイン (DUT) のテスト スティミュラスを生成するために使用されます。
ここに画像の説明を挿入
テスト プログラムの一般的な構造:
ここに画像の説明を挿入
テストベンチはテスト プラットフォームであり、信号はモジュール内に統合されており、入出力はありません。テストベンチ モジュールでは、テスト対象の設計の最上位モジュールがインスタンス化され、テスト動作のコードがその中にカプセル化され、テスト対象のシステムにテスト インセンティブを直接提供します。

例: T フリップフロップ テスト プログラム

module TFF_tb;
reg clk, rst_n, T;
wire data_out;
TFF U1(.data_out(data_out), .T(T), .clk(clk), .rst_n(rst_n)); //对被测模块实例化
	always
		#5 clk = ~clk;
	Initial 
		begin
			clk = 0;
			#3 rst_n = 0;
			#5 rst_n = 1;
			T = 1;
			#30 T = 0;
			#20 T = 1;
		end
	Initial
		begin
			$monitor($time,"T=%b, clk=%b,rst_n=%b,data_out=%b", T, clk, rst_n,data_out);
		end
endmodule

T フリップフロップの波形シミュレーションとテキスト出力:
ここに画像の説明を挿入
テストベンチの主な機能は、以下の図から明確にわかります。
(1) DUT に励起信号 0 を提供します。
(2) DUT を正しくインスタンス化します。
(3) シミュレーションデータを端末上に表示またはファイルとして保存、または波形ウィンドウに表示して解析・検査を行います。
(4) EDAツールを使用して複雑な設計を行ったり、ユーザーインターフェースを通じてシミュレーション結果を理想値と自動的に比較したり、結果の自動検査を実現したりできます。

ここに画像の説明を挿入
テストベンチを作成するときに注意する必要がある問題:
(1) テストベンチ コードは合成可能である必要はありません。
テストベンチ コードはハードウェアの動作を記述するだけであり、ハードウェア設計ではありません。
(2) 動作レベルの記述が高効率
Verilog HDL 言語には、スイッチレベル、ゲートレベル、RTL レベル、アルゴリズムレベル、システムレベルの 5 つの記述レベルがあります。
(3) 構造化・定型化された記述方法を把握する
構造化記述は設計や保守に有益であり、initial、always、assign 文により異なるテストインセンティブを分けることができます。
一般に、すべてのテストをステートメント ブロックに含めないでください。

2.2 テストプラットフォームの例

テスト プラットフォームは、クロック信号、リセット信号、および一連のシミュレーション ベクトルを生成し、DUT の応答を観察し、シミュレーション結果を確認する必要があります。
ここに画像の説明を挿入
(1) 組み合わせ論理回路のシミュレーション環境の構築
全加算器の真理値表:
ここに画像の説明を挿入

module adder1 (a, b, ci, so, co); 
input a, b, ci ; 
output so, co ; 
assign { co , so } = a + b + ci ; 
endmodule

全加算器の真理値表に従って書かれた全加算器テストプログラムは次のとおりです。

module adder1_tb ;
 wire so, co;
 reg a, b, ci; 
 adder1 U1(a, b, ci, so, co); //模块例化
 initial //测试信号产生 
 	begin
 		 a = 0; b = 0; ci = 0; 
 		 #20 a = 0; b = 0; ci = 1; 
 		 #20 a = 0; b = 1; ci = 0; 
 		 #20 a = 0; b = 1; ci = 1; 
 		 #20 a = 1; b = 0; ci = 0;
 		 #20 a = 1; b = 0; ci = 1; 
 		 #20 a = 1; b = 1; ci = 0; 
 		 #20 a = 1; b = 1; ci = 1; 
 		 #200 $finish; 
	end 
endmodule

全加算器の入力 a、b、ci は reg タイプの変数として定義され、出力 so および co はワイヤ タイプの変数として定義されます。モジュールのインスタンス化ステートメント「 adder1 U1(a, b, ci, so
, co);" すべてを入力します。加算器の設計回路はテスト シミュレーション環境にインスタンス化されます。入力変更が変更され、最初のブロック ステートメントでテスト条件が生成され、入力変更ステートメントは完全に真実に従って記述されます。全加算器の表 (2) 順序論理回路シミュレーション
ここに画像の説明を挿入
環境の構築 順序
論理回路シミュレーション環境では、タイミング、タイミング情報、グローバルリセット、セットなどの信号を考慮して定義する必要があります。

Verilog で書かれた 10 進加算カウンター:

module cnt10(cIk ,rst, ena, q, cout); 
input clk,rst,ena; 
output [3:0] q;
output cout; 
reg [3:0] q;
always@(posedge clk or posedge rst) 
	begin
		if(rst)q=4’b0000;
			else if(ena)
				begin
					if(q<9)
						q=q+1;
					else 
						q=0;
				end
	end
assign cout=q[3]&q[0];
endmodule

テストプログラムコード:

module cnt10_tb; 
reg clk, rst, ena; 
wire [3:0] q;
wire cout;
cnt10 U1(clk ,rst, ena, q, cout); 
always #50 clk = ~clk; 
initial 
	begin
		clk=0;rst=0;ena=1; 
		#1200 rst=1;
		#120 rst=0;
		#2000 ena=0;
		#200 ena=1;
		#20000 $finish; 
	end
endmodule

ステートメント「cnt10 U1(clk,rst,ena,q,cout);」をインスタンス化して、小数カウントモジュールをシミュレーション環境にインスタンス化します。ステートメント「#50
clk=~clk;」を常に使用して、100 のサイクルを生成します。 (標準時間単位) クロック方形波;
初期ブロックを使用して、リセット信号 rst およびイネーブル制御信号 ena のテスト条件を生成します。
テスト結果を図に示します。

ここに画像の説明を挿入

2.3Verilogシミュレーション結果の確認

(1) 波形を直接観察する
各信号波形の出力を直接観察し、テスト値と期待値を比較し、シミュレーション結果の正しさを判断します。
ここに画像の説明を挿入

(2) 印刷テキストの出力方法

module adder1_tb; 
wire so,co; 
reg a,b,ci; 
adderl U1(a,b,ci,so,co);
initial
	begin
		a=0;b=0;ci=0;
		#20 a=0;b=0;ci=1;
		#20 a=0;b=1 ;ci=0;
		#20 a=0;b=1 ;ci=1;
		#20 a=1 ;b=0;ci=0;
		#20 a=1 ;b=0;ci=1;
		#20 a=1 ;b=1 ;ci=0;
		#20 a=1;b=1;ci=1;
		#200 $finish; 
	end
	$monitor($time, "%b %b %b -> %b %b”,a, b, ci, so, co);
endmodule

出力結果:

0 0 0 0 -> 0 0
20 0 0 1 -> 1 0
40 0 1 0 -> 1 0
60 0 1 1 -> 0 1
80 1 0 0 -> 1 0

システムタスク印刷タスク:
$display: 標準出力装置への直接出力、
$monitor: パラメータの変更の監視、
$fdisplay: ファイルへの出力など。

(3) シミュレーション結果の自動チェック
設計コードのキーノードにアサーションモニタを追加して、回路論理合成に関するコメントや設計機能の説明を作成することで、シミュレーション結果を自動的にチェックし、設計モジュールの可観測性を向上します。

(4) VCD ファイルの使用
Verilog は、信号値の変化を記録し、それを標準の VCD (Value Change Dump) 形式のデータベースに保存するための一連のシステム タスクを提供します。VCD ファイルは、変更された波形のみを記録する標準形式の波形記録ファイルです。

2.4 Verilog シミュレーションの効率

並列セマンティクスの変換はシリアル ソフトウェア コードを通じて完了する必要があるため、Verilog 動作レベル シミュレーション コードの実行時間は比較的長くなります。

Verilog HDLコードのシミュレーションコードの実行時間を改善します。
(1) 階層構造を減らす
シミュレーションコードが少ないほど、実行時間は短くなります。
(2) ゲートレベルのコードの使用を削減する
ゲートレベルのモデリングは構造レベルのモデリングに属するため、シミュレーション コードでは可能な限り動作レベルのステートメントを使用することをお勧めします。モデリング レベルが抽象的であればあるほど、実行時間は短くなります。時間。
(3) シミュレーション精度が高いほど効率は低下し、
タイミング単位値とタイミング精度値の差が大きいほどシミュレーション時間は長くなります。`timescale はタイムスケールをエミュレートします。
(4) プロセスが少ないほど効率が高くなります
コード内のステートメントブロックが少ないほど、エミュレータが異なるプロセスを切り替えるのに時間がかかるため、シミュレーションが高速になります。
(5) エミュレータの出力を削減する
Verilog 言語にはいくつかのシステムタスクが含まれており、一部のプロンプト情報はエミュレータのコンソール表示ウィンドウに出力できますが、エミュレータの実行効率は低下します。

3. シミュレーションに関連するシステムタスク

3.1$display と $write

文法形式:
$display("<format_specifiers>", <signal1 , signal2, ..., signaln>);
$write("<format_specifiers>", <signal1, signa12, ..., signaln>);

"<format_specifiers>" は通常 "フォーマット コントロール" と呼ばれます
"<signal1, signal12, …, signaln>" は "シグナル出力リスト"
$display は出力後に自動的にラップします
$write はラップせずに特定の情報を出力します

出力形式記述は「%」とフォーマット文字で構成され、出力データを指定された形式に変換して出力する機能を持ちます。
一般的に使用されるいくつかの出力形式は次のとおりです。
ここに画像の説明を挿入
一部の特殊文字は、表内の変換シーケンスを通じて出力できます。
ここに画像の説明を挿入
例: $display ステートメントと $write ステートメント

module disp_tb; 
reg[31 :0] rval; 
pulldown(pd);
initial
	begin
		rval=101;
		$display("\\\t%%\n\"\123”);
		$display("rval=%h hex %d decimal" rval, rval);
		$display("rval=%o otal %b binary", rval, rval);
		$display("rval has %c ascii character value",rval);
		$display("pd strength value is %v", pd);
		$display("current scope is %m");
		$display("%s is ascii value for 101",101);
		$write("simulation time is");
		$write("%t\n", $time); 
	end
endmodule

ここに画像の説明を挿入
$display では、出力リストのデータの表示幅が出力形式に応じて自動的に調整され、式の現在値は常に式の最大値が占める桁数で表示されます。

3.2 $monitor と $stobe

$monitor、$stobeともにパラメータリスト内の文字や変数の値を監視し出力する機能を提供します。
(1) $monitorの構文形式:
$monitor("<format_specifiers>", <signal1, signal2, ..., signaln>);
タスク $monitorはパラメータ内の式や変数の値を監視し出力する機能を提供します。リスト。パラメータ リスト内の変数または式の値が変更されるたびに、パラメータ リスト全体の変数または式の値が出力および表示されます。
例: $monitor($time, , “rxd=%b txd=%b”,rxd txd);
上記のステートメントでは、「, ,」は空のパラメーターを表すことに注意してください。空の引数は出力ではスペースとして表示されます。

$monitoron タスクと $monitoroff タスクの役割は、プログラマが $monitor が発生するタイミングを簡単に制御できるように、監視フラグをオンまたはオフにすることで監視タスク $monitor の開始と停止を制御することです。
$monitor と $display の違いは、$monitor は最初のブロックで呼び出されることが多く、$monitoroff が呼び出されない限り、$monitor は設定された信号を監視し続けます。

例: $monitor システムタスクの適用

module monitor_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 2;
			forever 
				begin
					#5 a = a + b;
					#5 b = a - 1;
				end
		end
	initial $monitor($time,"a = %d,b = %d",a, b);
endmodule

ここに画像の説明を挿入
(2) $strobe の構文形式:
$strobe(<functions_or_signals>);
$strobe("<string_and/or_variables>",<functions_or_signals>);
検出タスクは、特定の時点ですべての時間が処理された後、このタイム ステップの終了時に書式設定されたテキストの行を出力するために使用されます。一般的に使用されるシステム タスクは次のとおりです:
$stobe: すべての時間の処理の後、書式設定されたテキストの行を 10 進形式で出力します;
$strobeb: すべての時間の処理の後に、書式設定されたテキストの行をバイナリ形式で出力します;
$strobeo: 結局のところ時間が処理されると、書式設定されたテキストの行を 8 進数形式で出力します;
$strobeh: すべての時間が処理された後、書式設定されたテキストの行を 16 進数形式で出力します。

$strobe タスクが呼び出されると、すべての代入ステートメントが完了し、対応するテキスト情報が出力されます。$strobe タスクは、すべての代入ステートメントが実行された後にのみデータが表示されるようにする別のデータ表示メカニズムを提供します。

3.3 $time と $reltime

  • 現在のシミュレーション時間は、これら 2 つの時間システム関数を使用して取得できます。違いは、$time 関数はシミュレーション時間を 64 ビット整数値の形式で返すのに対し、$realtime 関数はシミュレーション時間を実数で返すことです。データ。
    (1) システム関数 $time の例
    : $time システムタスクの適用例
`timescale 1ns/1ns
module time_tb;
	reg ts;
	parameter dalay = 2;
	initial 
		begin
			#delay ts = 1;
			#delay ts = 0;
			#delay ts = 1;
			#delay ts = 0;
		end
	initial
		$monitor($time,,"ts = %b",ts);
	$time
endmodule

出力結果:

0 ts = x
3 ts = 1
5 ts = 0
8 ts = 1
10 ts = 0

(2)$realtimeシステム関数

$realtime によって返される時間数値は実数であり、これも時間スケールに基づいています。
例: $realtime システム タスクのアプリケーション インスタンス:

`timescalel 1ns/1ns 
module realtime_tb; 
reg set; 
parameter p=2; 
	initial
		begin
			$monitor($realtime, ,"set=b%" ,set); 
			#p set=0;
			#p set=1;
		end
endmodule

出力結果:

0セット=×
2セット=0
4セット=1

3.4 $finish と $stop

システム タスク $finish と $stop はシミュレーション プロセスを制御するために使用され、それぞれシミュレーションの終了とシミュレーションの中断を示します。文法形式:
$finish;
$fimsh(n);
$stop;
$stop(n);
このうち、n は $finish と $stop のパラメータであり、n は 0、1、または 2 のいくつかの値を取ることができます。は、下表に示すとおり、それぞれ以下の意味を表します。

ここに画像の説明を挿入
$finish の機能は、エミュレータを終了してメイン オペレーティング システムに戻ること、つまりエミュレーション プロセスを終了することです。タスク $finish はパラメーターを受け取り、パラメーターの値に従ってさまざまな機能情報を出力できます。パラメータがない場合、$finish のデフォルトのパラメータ値は 1 です。
$stop タスクの役割は、EDA ツール (エミュレータなど) を一時停止モードにし、エミュレーション環境で対話型のコマンド プロンプトを表示し、ユーザーに制御権を与えることです。このタスクはパラメータ式を受け取ることができます。パラメータの値(0、1、2)に応じて、異なる情報が出力されます。パラメータ値が大きいほど、より多くの情報が出力されます。

$finish のインスタンス

module finish_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 4;
			forever
			 begin
			 	#5 a = a + b;
			 	#5 b = a - 1;
			 end
		end
	initial #40 $finish; //程序执行到40个时间单位时退出仿真器
	initial 
		begin
			$monitor($time,"a = %d,b = %d",a,b);
		end
endmodule

$stop のインスタンス

module stop_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 4;
			forever
			 begin
			 	#5 a = a + b;
			 	#5 b = a - 1;
			 end
		end
	initial #40 $stop; //程序执行到40个时间单位时停止仿真
	initial 
		begin
			$monitor($time,"a = %d,b = %d",a,b);
		end
endmodule

3.5$readmemh と $readmem

Verilog プログラムには、ファイルからメモリにデータを読み取るために使用される 2 つのシステム タスク $readmemh および $readmem があります。2 つのシステム タスクはシミュレーション中いつでも実行でき、その文法形式は次のとおりです:
(1) $readmemb(“<file_name>”,<memory_name>);
(2) $readmemb(“<file_name>” , <memory_name>,<start_addr>);
(3) $readmemb(“<file_name>”,<memory_name>,<start_addr>,<finish_addr>); (
4) $readmemh(“<file_name>”,<memory_name> ) ;
(5) $readmemh(“<file_name>”,<memory_name>,<start_addr>);
(6) $readmemh(“<file_name>”,<memory_name>,<start_addr>,<finish_addr>);

例: $readmemh および $readmem システム タスク インスタンス

module read_mem_tb;
	reg [7:0] memory_b [0:7];
	reg [7:0] memory_h [0:31];
	integer i;
	initial
		begin
			$readmemb("init_b.txt",memory_b); //把数据文件init_b.txt读入存储器中的指定地址
			$readmemh("init_h.txt",memory_h);
			for(i = 0;i < 8; i=i+1)
				begin
					$display("memory_b[%0d] = %b",i.memory_b[i]);
					$display("memory_h[%0h] = %h",i.memory_h[i]);
				end
		end
endmodule

ファイル init_b.txt および init_h.txt には初期化データが含まれています。@<address> を使用してデータ ファイル内のアドレスを指定します。このうち、「init_b.txt」は2番地からバイナリデータを書き込むことを指定し、「init_h.txt」は16進データを1番地から書き込むことを指定しています。サンプルファイルは次のとおりです。

init_b.txt:

@002 
11111111 01010101
00000000 10101010
@001
1111zzzz 00001111

init_h.txt:

@001
00000000000000000000000000000011
00000000000000000000000000000111
00000000000000000000000000001111
00000000000000000000000000011111

3.6$ランダム

$random は乱数を生成するシステム関数です。この関数を呼び出すたびに、符号付き整数である 32 ビットの乱数が返されます。構文形式:
$random%<number>;
このシステム関数は、乱数を生成する手段を提供します。関数が呼び出されると、32 ビットの乱数を返します。符号付き整数です。
$random の一般的な使用法は次のとおりです: $ramdom%b (b>0)、(-b + 1):(b - 1) の範囲の乱数を与えます。

例:

`timescale 1ns/1ns 
module random_pulse(dout); 
output [9:0] dout;
reg dout;
integer delay1,delay2,k; 
initial
	begin
		#10 dout=0;
		for(k=0;k<100;k=k+1)
			begin
				delay1 = 20 * ({$random} % 6);
				delay2 = 20 * (1 +{$random} % 3); 
				#delay1 dout= 1 << ({$random}%10);
				$delay2 dout = 0;
			end
	end
endmodule

4. 信号時間割り当てステートメント

ここに画像の説明を挿入

4.1 時間遅延の構文説明

遅延ステートメントは、各ステートメントの実行時間を制御し、ユーザーのタイミング要件を高速に満たすために使用されます。Verilog 言語の遅延制御には 2 種類の文法形式があります:
(1) #<遅延時間> 動作ステートメント;
(2) #<遅延時間>;
ここで、記号「#」は遅延制御のキー文字、<遅延time> シミュレーション時間の単位で指定された遅延時間を直接指定できます。シミュレーション中、すべての遅延は時間単位で定義されます。

以下は、遅延を伴う代入ステートメントの例です。
#2 Sum = A ^ B; //#2 2 つの時間単位を指定した後、A XOR B の値を Sum に代入します

手続き代入文における時間制御部の位置に応じて、手続き代入文における時間制御モードは外部時間制御モードと内部時間制御モードに分けることができます。
(1) 外部時間制御方式とは、処理全体の中で代入文の一番左端に時間制御が現れる、つまり代入対象変数の左側にある時間制御方式であり、その文法構造は以下の通りです。 : シミュレーションが実行されると、次のステートメントと同等になります
#5 a=b;

initial
	begin
		#5;
		a = b;
	end

(2) 内部時間制御モードは、手続き代入文の時間制御部を「代入演算子」と「代入式」の間にも出現させることができる時間制御モードです。その文法構造は次のとおりです。
a=#5b;
時間制御部分「#5」は代入演算子「:」と代入式「b」の間に出現するため、この手続き型代入には内部時間制御モードの時間制御があります。声明。これを実行すると、次のステートメントを実行するのと同じになります。

initial
	begin
		temp=b; //先求b的值
		#5;
		a=temp;
    end

4.2 時間遅延の記述形式

ここでの時間遅延の記述形式とは、遅延制御の記述形式を指し、シリアル遅延制御、パラレル遅延制御、ブロッキング遅延制御、ノンブロッキング遅延制御の4つの形式に分けられる。異なる波形を持つ 2 セットの信号の実現を例として (図 q0_out と q1_out に示すように)、4 つの異なる時間遅延の記述形式が示されています。
ここに画像の説明を挿入
(1) シリアル遅延制御
シリアル遅延制御は、最も一般的な信号遅延制御です。処理の開始ブロックと終了ブロック、および遅延代入文で構成されます。遅延代入文は、外部時間制御メソッドまたは内部時間制御メソッドです。<遅延時間>の後に、対応する動作ステートメントを実行するかどうかを状況に応じて決定することもできます。
<遅延時間> の背後に対応する動作ステートメントがある場合、シミュレーション プロセスは、遅延制御を伴うこの動作ステートメントに遭遇した直後に、その動作ステートメントで指定された操作を実行せず、「<遅延時間>」の指定された量の動作が実行されるまで待機します。動作ステートメントで指定されたアクションが実際に開始されるまでに時間が経過します。

例: Verilog シリアル遅延制御方式で上図の信号を設計する

`timescale 1ns/1ns 
module serial_delay(q0_out, q1_out); 
output q0_out, q1 _out;
reg q0_out, q1_out;
initial
	begin
		q0_out=1'b0;
		#50 q0_out=l'b1;
		#100 q0_out=1'b0;
		#100 q0_out=1'b1;
		#50 q0_out=1'b0;
		#100 q0_out=1'b1;
		#50 q0_out=1'b0;
		#50 q0_out=1'b1;
		#50 q0_out=1'b0; 
	end
initial
	begin
		q1_out=1'b0;
		#100 q1_out=1'b1;
		#100 q1_out=1'b0;
		#50 q1_out=1'b1;
		#100 q1_out=1'b0:
		#50 q1_out=1'b1;
		#100 q1_out=1'b0;
		#50 q1_out=1'b1:
		#50 q1_out=1'b0; 
	end
endmodule

(2) 並列遅延制御 並列
遅延制御方式は、フォークジョイン処理ブロックと遅延代入文で構成されます。遅延代入文はシリアル遅延制御方式と同じであり、外部時間制御方式のどちらでもかまいません。または内部時間制御方法の方法。<遅延時間>以降、対応するアクションステートメントを実行するかどうかを状況に応じて決定することもできます。
<遅延時間> の背後には対応する動作ステートメントがあり、シミュレーション プロセスは、遅延制御を伴うこの動作ステートメントに遭遇した後、動作ステートメントで指定された操作をすぐに実行せず、「<遅延時間>」の指定された量まで待機します。動作ステートメントで指定されたアクションが実際に開始されるまでに時間が経過します。ただし、パラレル遅延制御モードとシリアル遅延制御モードの違いは、パラレル遅延制御モードでは複数の遅延ステートメントが並列に実行され、前のステートメントの実行が完了するのを待って開始する必要がないことです。現在のステートメントを実行します。

例: Verilog 並列遅延制御設計の波形図例

`timescale 1ns/1ns 
module parallel_delay (q0_out,q1_out); 
output q0_out,q1_out; 
reg q0_out,q1_out; 
initial 
	fork
		q0_out=1'b0;
		#50 q0_out=1'b1;
		#150 q0_out=1'b0;
		#250 q0_out=1'b1;
		#300 q0_out=1'b0;
		#400 q0_out=1'b1;
		#450 q0_out=1'b0;
		#500 q0_out=1'b1;
		#600 q0_out=1'b0;
	join
initial 
	fork 
		ql_out=1'b0;
		#100 q1_out=1'b1;
		#200 q1_out=1'b0;
		#250 q1_out=1'b1;
		#350 q1_out=1'b0;
		#400 q1_out=1'b1;
		#500 q1_out=1'b0;
		#550 q1_out=1'b1;
		#600 q1_out=1'b0;
	join
endmodule

(3) ブロッキング遅延制御
代入演算子「=」で特定される代入演算を「ブロッキング処理代入」と呼びます。ブロッキング遅延制御とは、ブロッキング プロセスの割り当てに基づいて遅延制御を行う状況です。次に例を示します。

initial
	begin
		a = 0;
		a = #5 1;
		a = #10 0;
		a = #15 1;
	end

各ブロッキング代入ステートメントは順番に実行され、最初のステートメントで指定された代入操作が完了するまで 2 番目のステートメントの実行は開始されません。したがって、シミュレーション プロセスの開始時に、値 "0" が a に代入され、この代入ステートメントの完了後に 2 番目の代入ステートメントが実行され、最初の代入ステートメントの完了後に値 "1" が実行されます。は 5 時間単位の遅延で a に代入されます a; 同様に、3 番目の代入ステートメントは 2 番目の代入ステートメントの完了後 10 時間単位の遅延後に実行され、「0」が a に代入されます; 最後の代入ステートメント最初の 3 つのステートメントがすべて完了し、遅延が 15 時間単位になった時点で、a に「1」を代入します。以下の図は、上記の例における信号 a の波形を示しています。上記の 2 つの例はどちらもブロッキング代入ステートメントを使用しています。
ここに画像の説明を挿入
4) ノンブロッキング遅延制御 代入
演算子「<=」で特定される代入演算を「ノンブロッキング処理代入」といいます。ノンブロッキング遅延制御は、ノンブロッキングプロセスの割り当てに基づいて遅延制御を行う場合です。次の例に示すように:

initial 
	begin
		a <= 0;
		a <= #5 1;
		a <= #10 0;
		a <= #15 1;
	end

上記の例では、各ノンブロッキング代入文が並列に実行されます。実行文は begin-end 直列ブロック内にありますが、その実行方法は並列遅延制御方法と一致しています。4 つの遅延代入が同時に実行されます。シミュレーションプロセスの開始時のステートメント。シミュレーション プロセスの開始時に a に値 "0" を代入し、シミュレーションの開始から 5 時間単位離れたときに a に値 "1" を代入し、10 時間経過したときに a に値 "0" を代入します。シミュレーションの開始から単位が離れた時点で、値が a に割り当てられ、最後に、シミュレーションの開始から 15 時間単位の時点で、値「1」が a に割り当てられます。以下の図は、上記の例における信号 a の波形を示しています。ここに画像の説明を挿入
例: Verilog ノンブロッキング遅延制御モードの設計

`timescale 1ns/1ns 
module non_blocking_delay(q0_out,q1_out);
output q0_out,q1_out;
reg q0_out,q1_out; 
initial
	begin
		q0_out <= 1'b0; 
		q0.out <= #50 1'b1; 
		q0_out <= #150 1'b0; 
		q0_out <= #250 1'b1;
		q0_out <= #300 1'b0; 
	 	q0_out <= #400 1'b1; 
		q0_out <= #450 1'b0;
	  	q0_out <= #500 1'b1;
	 	q0_out <= #600 1'b0;
	end
initial
	begin 
		q1_out <= 1'b0; 
		q1_out <= #100 1'b1; 
		q1_out <= #200 1'b0;
		q1_out <= #250 1'b1; 
		q1_out <= #350 1'b0; 
		q1_out <= #400 1'b1; 
		q1_out  <= #500 1'b0; 
		q1_out <= #550 1'b1; 
		q1_out <= #600 1'b0;
	end
endmodule

4.3 エッジトリガ時間制御

エッジトリガイベント制御の構文形式は以下の 4 つの形式です:
形式 1: @(<イベント式>) アクション文;
形式 2: @(<イベント式>);
形式 3: @(<イベント式 1 > or<イベント式 2>or…or<イベント式 n>) 動作ステートメント;
形式 4: @(<イベント式 1>or<イベント式 2>or…or<イベント式 n>);

1. イベント式は、
 イベント式の 3 つの形式で使用できます。
   形式 1: <信号名>
   形式 2: posedge <信号名>
   形式 3: negedge <信号名>

 ここで、「<信号名>」は、任意のデータ型のスカラーまたはベクトルにすることができます。
 形式 1 では、トリガー イベントを表す "<信号名>" は、次の例のように、指定された信号がロジックを変更したときに次のステートメントを実行します: @(in) out=in; センシティブなイベント In がロジックを変更したとき (ポジティブ ジャンプを  含む
  )
および負のジャンプ)、対応する代入ステートメントを実行し、in の値を out に代入します。

 形式 2 では、トリガー イベントを表す「posedge <信号名>」が指定された信号の正の遷移を発生すると、次の例のように次のステートメントが実行されます。敏感なイベント in が
  正で
 ある ジャンプするとき、対応する代入ステートメントを実行し、in の値を out に代入します。

 形式 3 では、トリガー イベントを表す「negedge <信号名>」が指定された信号で負の遷移を持つ場合、次の例のように、次のステートメントが実行されます。イベント in に
  負の
 遷移があります。 変更する場合は、対応する代入ステートメントを実行し、in の値を out に代入します。

 信号の論理変化 (正または負の遷移) のプロセス中に、信号の値は 4 つの値 0、1、x、z のいずれかから別の値に変化し、信号が正の遷移を起こします。このプロセスは信号山の低レベルから高レベルへの遷移であり、負のジャンプは信号山の高レベルから低レベルへの遷移です。次の表は、Verilog HDL で指定されているポジティブ遷移とネガティブ遷移を示しています。

ポジティブトランジション 負の遷移
0→× 1→×
0→z 1→z
0→1 1→0
×→1 ×→0
z→1 z→0

2. エッジ トリガー構文形式
形式 1: @(<イベント式>) 動作ステートメント;
 この構文形式の機密イベントのリストにはトリガー イベントが 1 つだけ含まれ、指定されたトリガー イベントが発生した後にのみ、後続の動作ステートメントが実行を開始します。 。イベント制御を伴うこのような動作ステートメントがシミュレーション プロセスで発生した場合、指定されたトリガー イベントが発生していない場合、シミュレーション プロセスはここに留まり、指定されたトリガー イベントが発生するまで待機してから、後続の動作ステートメント (シミュレーション プロセス) の実行を開始します。下に続いています。
例: クロックパルスカウンター

module clk_counter(clk, count_out);
input clk;
output count_out;
reg[3:0] count_out;
initial
	count_out = 0;
always@(posedge clk)
	count_out = count_out + 1; //在clk的每个正跳变边沿count_out增加1
endmodule

形式 2: @(<イベント式>);
 この構文形式の機密イベント リストにはトリガー イベントが 1 つだけ含まれており、トリガー イベントの発生時に実行される操作を指定する動作ステートメントはありません。この形式のイベント制御文の実行プロセスは、遅延制御文にアクション文がない場合と同様であり、シミュレーションプロセスは、このイベント制御文に遭遇した後に待機状態に入り、待機状態を終了しません。指定されたトリガー イベントが発生するまで、イベント制御ステートメントの実行を終了し、次のステートメントの実行を開始します。

例: 入力クロックの正レベル、負レベルの持続時間、およびクロック周期を決定するために使用されるモジュール

module clk_time(clk);
	input clk;
	time posedge_time,negegde_time;
	time high_last_time,low_last_time,last_time;
	initial
		begin
			@(posedge clk);
				posedge_time = $time;
			@(negedge clk);
				negedge_time = $time;
			@(negedge clk);
				last_time = $time - posedge_time;
				high_last_time = negedge_time - posedge_time;
				low_last_time = last_time - high_last_time;
			$display("The clk stay in High level for: %t",high_last_time);
			$display("The clk stay in Low level for: %t",low_last_time);
			$display("The clk stay in signal Period  for: %t",last_time);
		end
endmodule

形式 3: @(<イベント式 1>or<イベント式 2>…または<イベント式 n>) 動作ステートメント。
 この構文形式の「機密イベント リスト」は、「<イベント式 >」が複数のトリガー イベントを表すことを指定します。これらの「<イベント式>」はキーワード「or」と組み合わせる必要があります。これらのトリガー イベントのいずれかが発生するとすぐに、動作ステートメントの実行が開始されます。シミュレーション プロセスがこの形式のエッジ トリガー イベント制御ステートメントに遭遇したとき、トリガー イベントが何も発生しない場合、シミュレーション プロセスはトリガー イベントの 1 つが発生するまで待機状態に入り、その後次の Behavior ステートメントであるシミュレーションの実行を開始します。プロセスは下方向に続きます。

形式 4:@(<イベント式 1>or<イベント式 2>or...or<イベント式 n>) は
 3 番目の構文形式と同じであり、この構文形式で複数のトリガイベントを指定します。ただし、この形式には動作ステートメントはありません。この場合、このステートメントの実行プロセスは 2 番目の構文形式の実行プロセスと同様であり、このイベント制御ステートメントに遭遇した後、シミュレーション プロセスはイベントが発生した後、待機状態が終了するまで待機状態に入ります。イベント制御ステートメントが終了し、イベント制御ステートメントの次のステートメントの実行が開始されます。
例: トリガーイベントの発生後にイベント制御ステートメントを終了する

module display(a,b);
input a,b;
wire a,b;
always@(posedge a or negndge b);
	begin
		$display("One of a and b changed in time: %t",$time);
	end
endmodule

4.4 レベル依存イベント制御

レベルセンシティブ時間制御とは、エッジトリガーイベント制御とは異なるイベント制御方式で、指定した条件式が真の場合に実行文を開始します。レベル依存のタイミング制御は、キーワード「待機」で示されます。
レベル トリガー イベント コントロールの文法形式は、次の 2 つのタイプになります:
形式 1: wait (条件式) 動作ステートメント;
形式 2: wait (条件式);
レベル依存イベント コントロールの最初の形式には、動作ステートメントが含まれます。シリアル ブロック (begin-end) ステートメント、並列ブロック (fork-join) ステートメント、または単一の動作ステートメントです。この形式のイベント制御ステートメントでは、動作ステートメントの実行のトリガー条件は、条件式の値が「true (論理 1)」であることです。シミュレーション プロセスがこのレベル依存の制御ステートメントを実行するときに条件式の値が「true」の場合、ステートメント ブロックはすぐに実行されます。それ以外の場合、ステートメント ブロックは実行を開始する前に条件式が「true」になるまで待機する必要があります。 。

例えば:

wait(enable == 1)
begin
	d = a & b;
	d = d | c;
end

wait文の機能は、イネーブル信号enableがハイレベルになった後、つまりenable==1の文のとき、条件式の真偽に応じて後続のbegin-end文ブロックの実行を制御することです。が true の場合、a と b が実行されます。 c 間の and or 演算; イネーブル信号enable がハイレベルにならない場合、begin-end ステートメントブロックの実行は、enable がハイレベルになるまで待ってから実行を開始する必要があります。


レベル依存イベント制御の 2 番目の形式には、動作ステートメントは含まれません。この形式のレベル依存イベント制御ステートメントでは、シミュレーション プロセスが待機制御ステートメントを実行するときに条件式の値が「true」である場合、待機イベント制御ステートメントの実行は直ちに終了され、シミュレーション プロセスは続行されます。 . ; シミュレーションプロセスが待機制御文を実行するときに条件式の値が「false」の場合、シミュレーションプロセスは待機状態に入り、条件式の値が「true」になるまで待機してから待機状態を終了します。同時に終了 wait ステートメントの実行により、シミュレーション プロセスが続行されます。この形式のレベル依存のタイミング制御は、シリアル ブロック内の個々のステートメントの実行タイミングを制御するためによく使用されます。

begin
	wait(enable == 1)
	d = a & b;
	d = d | c;
end

5. タスクと機能

タスクと関数は Verilog 言語で提供されており、大規模な動作レベルの設計をより小さなコード セグメントに分割できるため、設計者は複数の場所で再利用する必要がある同じコードを抽出して、それをタスクや関数に書き込むことができます。コードがより簡潔で理解しやすくなります。

5.1 タスク

  1. タスク定義
    タスク定義の構文形式:
    task<任务名>;
    端口和类型声明
    局部变量声明
    begin
    语句1;
    语句1;
    ......
    语句n;
    end
    endtask

タスク定義はキーワード task と endtask の間に埋め込まれます。キーワード task はタスク定義構造の始まりを示し、endtask はタスク定義構造の終わりを示します。「<タスク名>」は定義したタスクの名前です。「<タスク名>」の後に入力ポートリストを指定することはできません。

メモリデータの読み取りを例として、タスク定義の動作を説明します。

task read_memory;
	input[15:0] address;
	output[31:0] data;
	reg[3:0] counter;
	reg[7:0] temp[1:4];
	begin
		for(counter=1;counter<=4;counter=counter+1)
			temp[counter]=mem[address+counter-1];
		data={temp[1],temp[2],temp[3],temp[4]};
	end
endtask

タスクを定義する場合は、以下の点に注意してください。
(1) task ステートメントの 1 行目にポート名のリストを記載することはできません。
(2) タスク内に遅延文や機密イベント制御文などのイベント制御文が存在する場合があります。
(3) タスクには、入力ポート、出力ポート、および双方向ポートがまったくないか、1 つ以上のポートが存在する場合があります。
(4) タスクは戻り値を持たないか、出力ポートまたは双方向ポートを通じて 1 つ以上の戻り値を返す場合があります。
(5) タスクは他のタスクや関数を呼び出すことも、タスク自体を呼び出すこともできます。
(6) プロセス ブロック (初期プロセス ブロックまたは常にプロセス ブロック) は、タスク定義構造内では許可されません。
(7) タスク定義構造内に終了無効化ステートメントを記述することができ、このステートメントを実行すると、実行中のタスクが中断されます。タスクが中断された後、プログラム フローは呼び出し元のタスクの実行を継続する場所に戻ります。

2. タスクの呼び出し
タスクの呼び出しは「タスク呼び出し文」によって実現されます。タスク呼び出しステートメントには、受信タスクのパラメーター値と受信結果の変数値がリストされます。タスクの呼び出し形式は次​​のとおりです。例: テスト シミュレーションでよく使用されるメソッドを使用して、タスクの呼び出しを説明します
<任务名>(端口1,端口,...,端口n);
。タスク

module task_tb;
 reg[7:0] mem[127:0];
 reg[15:0] a;
 reg[31:0] b;
 initial
	begin
		a = 0;
		read_mem(a,b);//任务的第一次任务调用
		#10;
		a = 64;
		read_mem(a,b);
	end
task read_memory;
 input[15:0] address;
 output[31:0] data;
 reg[3:0] counter;
 reg[7:0] temp[1:4];
	begin
		for(counter=1;counter<=4;counter=counter+1)
			temp[counter]=mem[address+counter-1];
		data={temp[1],temp[2],temp[3],temp[4]};
	end
endtask

タスクを使用すると、プログラムがより簡潔で理解しやすくなります。実際の信号機を例に、タスクの定義と呼び出しの特徴を説明します。

module traffic_lights(red, amber, green);
output red, amber, green; 
reg [2:1] order; 
reg clock, red, amber, green; 
parameter ON=1, OFF=0, RED_TICS=350,AMBER_TICS=30,GREEN_TICS=200;//产生时钟脉冲
always
	begin
		#100 clock = 0;
		#100 clock= 1;
end
//任务的定义,该任务用于实现交通灯的开启
task light; 
 output red; 
 output amber;
 output green;
 input [31:0] tic_time; 
 input [2:1] order; 
 	begin
		red = OFF;
		green = OFF; 
		amber = OFF; 
		case(order)
			2'1b01: red = ON;
			2'b10: green = ON;
			2'b11: amber = ON; 
		endcase
		repeat(tic_time)@(posedge clock); 
		red = OFF;
		green = OFF; 
		amber = OFF; 
	end
endtask
//任务调用,交通灯初始化
initial
	begin
		order = 2'b00;
		light(red, amber, green, 0, order);
	 end
 //任务调用,交通灯控制时序
always
	begin 
		order=2’b01;
		light(red, amber, green,RED_TICS,order);//调用开灯任务,开红灯
		order=2'b10;
		light(red, amber, green,GREEN_TICS,order);//调用开灯任务,开绿灯
		order=2'b11;
		light(red, amber, green,AMBER_TICS,order);//调用开灯任务,开黄灯
	end
endmodule

5.2 機能

1. 関数の定義 関数
function<返回值类型或位宽><函数名>;
<输入参量与类型声明>
<局部变量声明>
begin
语句1;
语句;
...
语句n;
end
endfunction
定義はキーワード function と endfunction の間に埋め込まれます。キーワード function は関数定義構造の始まりを示し、endfunction は関数定義構造の終わりを示します。「<関数名>」は、定義する関数に付けられた名前です。この関数名は関数定義構造内の内部変数も表し、関数呼び出し後の戻り値はこの関数名変数を通じて呼び出し側ステートメントに渡されます。

<戻り値の型またはビット幅> はオプションの項目で、関数呼び出しによって返されるデータの型または幅を記述するために使用されます。次の 3 つの形式があります。 (1) "[msb Isb]": this
関数名で表される戻りデータ変数はマルチビットのレジスタ変数であり、そのビット幅は関数定義ステートメントよりも大きい [msb:lsb] で指定されることを説明します。

関数[7:0]加算器;

関数「adder」が定義されており、その関数名「adder」も8ビット幅のレジスタ変数を表しており、最上位ビットが7ビット目、最下位ビットが0ビット目である。
(2) "integer": この形式は、関数名で表される戻り変数が整数変数であることを示します。
(3) "real": この形式は、関数名で表される戻り変数が実数変数であることを示します。

「<入力パラメータと型宣言>」は、関数の各入力ポートの幅と型を記述します。関数定義内には、少なくとも 1 つの入力ポート (入力) 宣言が必要であり、出力ポート (出力) 宣言は存在しません。 。データ型宣言ステートメントは、関数で使用されるローカル変数の幅と型を記述するために使用されます。この宣言ステートメントの構文は、モジュールを定義するときの対応する宣言ステートメントの構文と一致します。
「<ローカル変数の説明>」は、関数内のローカル変数の幅と型の説明です。
「begin」と「end」キーワードで定義された一連のステートメントはタスクと同じであり、関数呼び出し時に実行される操作を指定するために使用され、関数呼び出し時にこれらのステートメントがシリアルに実行されます。やり方。

例:入力データ中の「0」の数を数える

function[3:0] out;
input[7:0] x;
reg[3:0] count;
integer i;
	begin
		count=0;
		for(i=0;i<=7;i=i+1;)
			if(x[i]==1'b0)
				count=count+1;
		out0=count;
	end
endmodule

関数を定義する場合は以下の点に注意してください。
(1) タスクと同様に、関数定義構造体はモジュール内にのみ記述でき、プロセスブロック内には記述できません。
(2) 関数には少なくとも 1 つの入力ポートが必要です。
(3) 関数には、任意の種類の出力ポート (output port) および双方向ポート (inout port) を持たせることはできません。
(4) 関数定義構造体の動作文部分にはいかなる時間制御記述も記述できず、無効終了文も使用できません。
(5) タスク定義と同様に、関数定義構造体内に処理ブロックを出現させることはできません。
(6) 関数内で他の関数を呼び出すことはできますが、関数から他のタスクを呼び出すことはできません。
(7) 「function」ステートメントの最初の行にポート名のリストを含めることはできません。
(8) 関数が宣言されると、Verilog 内で function_identifier (関数識別子) というレジスタ型変数が暗黙的に宣言され、関数の出力はこのレジスタ型変数を介して返されます。

2. 関数呼び出し
関数呼び出しは、関数を式のオペランドとして使用して行われます。関数の呼び出し形式は以下のとおりです。
<函数名>(<输入表达式1>,<输入表达式2>,...,<输入表达式n>);
このうち、入力式は関数定義構造体に記述された入力ポートに一つずつ対応し、各入力ポートの入力データを表します。
関数を呼び出すときは、次の点に注意してください。
(1) 関数の呼び出しは単一のステートメントとして使用できません。呼び出しステートメントのオペランドとしてのみ使用できます。
(2) 関数呼び出しは、process ブロックだけでなく、assign 連続代入文にも出現できます。
(3) 関数定義で宣言されたすべてのローカル レジスタは静的です。つまり、関数内のローカル レジスタは、関数の複数の呼び出しにわたって値を維持します。

module tryfact_tb; 
function[31:0] factorial;
	input[3:0] operand; 
	reg[3:0] index; 
	begin
		factorial = 1;
		for(index=1;index<=operand;index=index+1)
		factorial = index * factorial; 
	end
endfunction 
reg[31:0] result;
reg[3:O] n; 
initial
	begin
		result=1;
		for(n=1;n<=9;n=n+1)
			begin
				result = factorial(n);
				$display("n= %d result= %d", n, result);
			end
	end
endmodule

上記の例は、関数定義と initia|procedure ブロックで構成されています。このブロックでは、階乗演算用の関数である、factorial という名前の関数が定義されています。4 ビットの入力ポートを持ち、同時に 32 ビットのレジスタ型の値を返します。 ; 最初のブロックでは、2 つのレジスタ変数が 32 ビットの result と 4 ビットの n として定義されており、1 ~ 9 の階乗演算を実行し、結果の値を出力します。 
ここに画像の説明を挿入

5.3 タスクと関数の違い

ここに画像の説明を挿入

6. 典型的なテストベクトルの設計

6.1 変数の初期化

Verilog言語では、変数を初期化する方法として、初期化変数を使用する方法と、変数定義時に直接変数を代入して初期化する方法があります。これら 2 つの初期化タスクは合成可能ではなく、主にシミュレーション プロセスで使用されます。
(1) 初期初期化方法
ほとんどの場合、テストベンチでの変数初期化の作業は初期プロセス ブロックを通じて完了し、豊富なシミュレーション インセンティブを生み出すことができます。
初期ステートメントは 1 回だけ実行されます。つまり、デザインが開始されてプロセスの終了まで実行をシミュレートするとき (0 回)、入力信号を初期化し、特定の信号波形を生成するために特別に使用されます。テストベンチには複数の初期プロシージャ ステートメント ブロックを含めることができ、すべての initia|procedure は同時に実行されます。初期ステートメント内の変数は reg 型である必要があることに注意してください。

例: 初期初期化メソッドを使用したテスト ベクトル出力

module counter(clk,cnt);
	output [3:0] cnt;
	reg clk;
	reg [3:0] temp;
	initial temp = 0;
	initial clk = 0;
endmodule


(2) 変数定義時の初期化 変数定義時の変数の初期化構文は非常に簡単で、reg [7:0] cnt=8'b00000000のように変数の右端に「=」を使用して値を代入するだけです。;
8 ビットのレジスタ変数 cnt は 0 に初期化されます。

6.2 データ信号テストベクタ生成

データ信号の生成には、単一の初期化ブロック内で初期化と生成を行う場合と、initial 文で初期化を完了し、always 文ブロックで生成を完了する場合の 2 つの形式があります。前者は不規則なデータ シーケンスに適しており、より短い長さが必要ですが、後者は特定の規則性を持つデータ シーケンスに適しており、長さの制限はありません。

例: ビット幅 4 の素数シーケンス {1, 2, 3, 5, 7, 11, 13} を生成し、サンプル値の間隔が 4 シミュレーション時間単位である場合にそれを 2 回繰り返します
。明らかなルールがある場合は、Initial ステートメントを使用するのが最も適切です。

`timescale 1ns/1ps 
module sequence_tb;
	reg [3:0] qout; 
	parameter sample_period = 4; 
	parameter queue_num = 2; 
	initial
		begin
			q_out =0;
			repeat( queue_num) 
				begin
					# sample_period q_out = 1;
					# sample_period q_out = 2;
					# sample_period q_out = 3;
					# sample_period q out = 5;
					# sample_period q_out = 7;
					# sample_period q_out= 11;
					# sample_period q_out = 13;
				end
	end
e ndmodule

ここに画像の説明を挿入

6.3 クロック信号テストベクタ生成

例: デューティ比 50% のクロック信号を生成する
ここに画像の説明を挿入(1) 最初のステートメントに基づく方法

module clk1(clk);
 output clk;
 parameter clk_period = 10;
 reg clk;
 initial
 	begin
 		clk = 0;
 		forever #(clk_period/2) clk = ~clk;
 	end
 endmodule

(2) Always文による方法

module clk2(clk);
 output clk;
 parameter clk_period = 10;
 reg clk;
 initial clk = 0;
 always #(clk_period/2) clk = ~clk;
endmodule

6.4 バス信号テストベクタ生成

バスは、コンピューティング コンポーネント間のデータ フローの共通チャネルです。RTL レベルの説明では、バスは、ロジック ユニット、レジスタ、メモリ、回路入力、またはその他のバスによって駆動される共有ベクトルを指します。バス関数モデルは、図に示すように、物理インターフェイスのタイミング操作をより高い抽象レベルのインターフェイスに変換するバス モデルです。 バスには、各リクエスト エンドに対して、対応する
ここに画像の説明を挿入
リクエストを選択して駆動するための入力があります。バスの終点。複数のリクエスタを選択するとバスの競合が発生し、バスのタイプに応じて異なる結果が生じる可能性があります。リクエストを送信する要求側が複数ある場合、対応する動作はバスの種類によって決まります。Verilog テストでは、バス テスト信号は通常、タスク タスクの形式でチップ セレクト信号、リード (またはライト) イネーブル信号、アドレス信号、データ信号によって記述され、バス信号テスト ベクトルを呼び出すことで完了します。バス関数に対応するタスクの形式。
以下では、タスク モードでバス信号テスト ベクトルを生成する方法を説明するために、動作周波数 100MHz の AHB バス書き込み操作を例に挙げます。次の図は、AHB バス書き込み動作のタイミング図です。データ書き込み動作が完了した後、チップ選択信号と書き込みイネーブル信号が非アクティブ (アクティブ ロー) になります。

ここに画像の説明を挿入
例: 書き込み操作を使用して一連の AHB バス関数モデルを生成する

module bus_wr_tb; 
reg clk;
reg cs; 
reg wr;
reg [31:0] addr; 
reg [31:0] data:
initial
	begin
		cs=1'b1;
		wr=1'b1;
		#30;
		bus_wr(32'h1100008a, 32’h11113000); 
		bus_wr(32’h1100009a, 32'h11113001);
		bus_wr(32'h110000aa, 32'h11113002); 
		bus_wr(32'h110000ba, 32’h11113003); 
		bus_wr(32'h110000ca, 32’h11113004); 
		addr=32’bx; 
		data=32’bx;
end
initial clk=1;
always #5 clk=~clk;
task bus_wr;
 input [31:0] ADDR;
 input [31:0] DATA;
 begin
 	cs=1'b0;
 	wr=1'b0;
 	addr = ADDR;
 	data = DATA;
 	#30 cs=1'b1;
 	wr=1'b1;
 end
endtask
endmodule

7. ユーザー定義コンポーネントモデル UDP

UDP を使用すると、組み合わせ論理回路や順序論理回路を UDP 内にカプセル化でき、この UDP を基本的なゲート要素として使用できます。UDP は合成できず、シミュレーションにのみ使用できることに注意してください。

7.1UDPの定義と呼び出し

UDPの定義形式は以下のとおりです。

primitive <元件名称>(<输出端口名>,<输入端口名1 >,<输入端口名2>,...,<输入端口名n>)
 输出端口类型声明(output);
 输入端口类型声明(input);
 输出端口寄存器变量说明(reg);
 元件初始状态说明(initial); 
 table
	<table表项1>;
	<table表项2>;
	...
	<table表项n>;
 endtable 
endprimitive

Verilog のモジュールと比較すると、UDP には次のような特徴があります。
(1) UDP 出力ポートは 1 つだけ存在でき、ポート リストの最初の項目である必要があります。reg タイプとして定義できるのは出力ポートのみです。
(2) UDP の入力端子は複数可能で、一般順序回路 UDP の入力ポートは最大 9 個、複合回路 UDP の入力ポートは最大 10 個可能です。
(3) すべてのポート変数のビット幅は 1 ビットである必要があります。
(4) テーブルエントリでは、0、1、x の 3 つの状態のみが出現し、z は x の状態と見なされます。

UDP に含まれる基本的な論理機能に応じて、UDP は組み合わせ回路 UDP、順序回路 UDP、およびハイブリッド回路 UDP に分類でき、これらのタイプの UDP の違いは主にテーブル エントリの記述に反映されます。
UDP の呼び出し方法は、Verilog のモジュールの呼び出し方法と似ており、ロケーション マッピングを通じて、その構文形式は次のようになります。
UDP名 例化名(连接端口1信号名,连接端口2信号名,连接端口3信号名,...);

例: UDP を使用した全加算器シミュレーション モデルの定義

primitive summ(sum, cin, a, b);//本位和
 output sum;
 input a,b,cin;
	table
  	  //cin a b : sum
		 0 0 0 : 0;
		 0 0 1 : 1;
		 0 1 0 : 1;
		 0 1 1 : 0;
	 	 1 0 0 : 1;
		 1 0 1 : 0;
		 1 1 0 : 0;
	 	 1 1 1 : 1;
	endtable
endprimitive

primitive summ(sum, cin, a, b);//进位
 output cout;
 input a,b,cin;
	table
  	  //cin a b : sum
		 0 0 0 : 0;
		 0 0 1 : 0;
		 0 1 0 : 0;
		 0 1 1 : 1;
	 	 1 0 0 : 0;
		 1 0 1 : 1;
		 1 1 0 : 1;
	 	 1 1 1 : 1;
	endtable
endprimitive

7.2UDPの応用例


(1) 組み合わせ回路 UDP コンポーネントの組み合わせ論理回路の関数リストは、異なる入力値と対応する出力値を指定する真理値表に似ており、表の各行は、output、input1、input2、という形式になります。・・・、並び順とポート 入力の組み合わせに対して定義された出力がない場合は、その場合の出力を x に設定します。

例: 3 対 1 マルチプレクサ

primitive mux3_1(Y,in0,in1,in2,s2,s1);
 input in0,in1,in2,s2,s1;
 output Y;
 table
 	//in0 in1 in2 s2 s1 : Y
 	   0   ?   ?  0  0  :  0;//当s2s1=00时,Y=in0
 	   1   ?   ?  0  0  :  1;
 	   ?   0   ?  0  1  :  0;//当s2s1=01时,Y=in1
 	   ?   1   ?  0  1  :  1;
 	   ?   ?   0  1  ?  :  0;//当s2s1=1?时,Y=in2
 	   ?   ?   1  1  ?  :  1;
 	   0   0   ?  0  ?  :  0;
 	   1   1   ?  0  ?  :  1;
 	   0   ?   0  ?  0  :  0;
 	   1   ?   1  ?  0  :  1;
 	   ?   0   0  ?  1  :  0;
 	   ?   1   1  ?  1  :  1;
 endtable
endprimitive

ここに画像の説明を挿入
(2) 順序回路 UDP コンポーネント
UDP は、レベルトリガ特性とエッジトリガ特性を備えた順序回路を記述することもできます。順序回路には内部状態シーケンスがあり、その内部状態はレジスタ変数でモデル化する必要があります。レジスタの値は順序回路の現在の状態であり、次の状態はプリミティブに配置された状態遷移テーブルによって決定されます関数リスト そしてレジスタの次の状態がこの順序回路 UDP の出力値になります。したがって、順序回路 UDP はステータス レジスタとステータス リストの 2 つの部分から構成されます。タイミング UDP を定義する作業も、ステータス レジスタの初期化とステータス リストの記述という 2 つの部分に分かれています。
順序回路の UDP 記述では、01、0x、x1 は信号の立ち上がりエッジを表します。立ち上がりエッジ D フリップフロップの UDP 開発例を以下に示します。

例: Verilog を通じて D フリップフロップの UDP 記述を与え、モジュール内の UDP コンポーネントを呼び出します。

primitive D_Edge(Q,Clk,Data);
 output Q;
 reg Q;
 input Data,Clk;
 initial Q = 0;
 table
   //Clk Data : Q(Stata): Q(next)
 	(01)   0  :    ?    :    0;
 	(01)   1  :    ?    :    1;
 	(0x)   1  :    1    :    1;
 	(0x)   0  :    0    :    0;
 	(?0)   ?  :    ?    :    -;// 忽略时钟负边沿
 	?   (??)  :    ?    :    -;// 忽略在稳定时钟上的数据变化
 endtable
endprimitive;

エントリ (01) は 0 から 1 への遷移を表し、エントリ (0x) は 0 から x への遷移を表し、エントリ (?0) は任意の値 (0、1、または x) から 0 への遷移を表します。エントリ (??) は任意の変換を意味し、出力のデフォルトは x です。D_Edge が UDP 用に定義されていると仮定すると、基本ゲートと同様にモジュールで使用できるようになります。

(3) 混合回路 UDP コンポーネントは、
同じテーブル内でレベル トリガー項目とエッジ トリガー項目を混在させることができます。この場合、エッジ変更はレベル トリガーの前に処理されます。つまり、レベル トリガー項目がエッジ トリガー項目をオーバーライドします。非同期クリアを備えた D フリップフロップの UDP 記述を以下に示します。
例: Verilog 言語を使用して、非同期クリア D フリップフロップの UDP 記述を完成させます。

primitive D_Async_FF(0, Clk, Clr, Data); 
 output Q;
 reg Q; 
 input Clk, Data, Clr;
 table 
	//Clk Clr Data : (SQtate) : Q(next)
	  (01) 0    0  :     ?    :    0;
	  (01) 0    0  :     ?    :    0;
	  (0x) 0    1  :     1    :    1;
	  (0x) 0    0  :     0    :    0;
	  (?0) 0    ?  :     ?    :    -;
	  (??) 0    ?  :     ?    :    -;
	   ?   1    ?  :     ?    :    0;
	endtable
endprimitive

8. 基本的なゲートレベルのコンポーネントとモジュールの遅延モデリング

8.1 ゲートレベルの遅延モデリング

ゲートレベル遅延は次のカテゴリに分類できます。
(1) 「立ち上がり遅延」: 信号が「x」または「z」状態から「1」状態に変化するときのゲート伝達遅延を意味します。
(2) 「立ち下がり遅延」: 信号が「1」、「x」、または「z」状態から「0」状態に変化するときのゲート伝達遅延を示します。
(3) 「不定状態までの遅延」:信号が「0」、「1」、または「z」状態から「x」状態に変化するときのゲート伝達遅延を示します。
(4) 「カットオフ遅延」: 信号が「0」、「1」、または「x」状態から「z」状態に変化するときのゲート伝達遅延を示します。

1. ゲートレベル遅延の基本的な遅延表現形式
ゲートレベルの遅延の基本的な表現形式において、「遅延」には 0 ~ 3 個の遅延値があり、次の表に異なる遅延値の数を示します。指定された、遅延の 4 つの表現。
ここに画像の説明を挿入
(1) 「遅延」でゲート遅延値を指定しない場合、デフォルトの遅延値は 0 です。これは、コンポーネント インスタンスの「立ち上がり遅延値」、「立ち下がり遅延値」、「オフ遅延値」、および「非定常状態までの遅延値」がすべて 0 であることを意味します。
例: notif1 U0(out, in, ctrl);
遅延が定義されていないため、ゲート レベルの遅延値は「0」です。「立ち上がり遅延値」、「立ち下がり遅延値」、「カットオフ遅延」コンポーネント インスタンスの「UO 時間値」と「不定状態までの遅延値」は両方とも 0 です。
(2) 「遅延」に遅延値が 1 つだけ含まれている場合、指定された遅延値「d」は、コンポーネント インスタンス時間値の「立ち上がり遅延値」、「立ち下がり遅延値」、「カットオフ遅延値」を同時に表します。および「値を不定状態に遅延する」。次の参照例では、
notif1 #20 U1(out, in, ctrl);
ゲート レベルの遅延値は「20」で、遅延値が 1 つだけ含まれており、コンポーネント インスタンス U1 のすべてのタイプのゲート レベル遅延を示しています。時間の単位は 20 です。
(3) 「遅延」に 2 つの遅延値が含まれる場合、コンポーネント インスタンスの「立ち上がり遅延値」は指定された「d1」で指定され、「立ち下がり遅延値」は指定された「d2」で指定され、「カット」 「-off 遅延値」と「不定状態までの遅延値」は「dl」と「d2」の最小値で指定します。 例:
notif1 #(10,20)U2(out, in,

(4) 「遅延」に 3 つの遅延値が含まれる場合、コンポーネント インスタンスの「立ち上がり遅延値」は指定された「dA」で指定され、「立ち下がり遅延値」は指定された「dB」で指定されます。 「オフ遅延」は特定の「dC」で指定され、「不定状態への遅延」は dA、dB、および dC の最小値で指定されます。例: notif1 #(10,20,30)U3
( out、in、ctrl);
ゲート レベルの遅延値は「00、20、30)」で、これには 3 つの遅延値 10、20、および 30 が含まれており、コンポーネント インスタンス IJ3 の立ち上がり遅延が 10 であることを示します。 「単位時間」、「立ち下がり遅延」は 20 単位、「カットオフ遅延」は 30 単位、「不定状態までの遅延」は 10、20、30 の最小値
、つまり 10 単位の時間で指定されます。

2. ゲートレベル遅延の最小遅延、代表値、最大
遅延表現 ゲートレベル遅延は、基本遅延表現に加えて、「最小遅延、代表遅延表現、最大遅延表現」も使用できます。ゲートレベルの遅延は「最小遅延」、「標準遅延」、「最大遅延」の2つの値で表され、その構文形式は次のとおりです。
#(d min:d_typ:d_max)

「最小、標準、最大」遅延式が使用される場合、次の状況のように、「遅延」には 1 ~ 3 個の遅延値を含めることができます。

#(dA_min: dA_typ: dA_max)
#(dA_min: dA_typ: dA_max, dB_min: dB_typ: dB_max) #(dA_min:dA_typ: dA_max, dB_min: dB_typ: dB_max, dC_min
: dC_typ: dC_max)

例: and #(4:5:6) U1(out,i1,i2);
「遅延」には遅延値が 1 つだけ含まれており、最小値は 4、標準値は 5、最大値は 6 です。コンポーネントインスタンスUIの「立ち上がり遅延値」、「立ち下がり遅延値」、「カットオフ遅延値」、「不定状態までの遅延値」は以下のとおりです。

最小遅延 立ち上がり遅延=4 落下遅延: 4 不定状態への遅延 = 4 オフディレイ: 4
典型的な遅延 立ち上がり遅延: 5 立ち下がり遅延=5 不定まで遅延する オフディレイ: 5
最大遅延 立ち上がり遅延=6 立ち下がり遅延=6 不定まで遅延する オフディレー = 6

例: and #(3:4:5,5:6:7)U2(out, i1,i2);
「遅延」には 2 つの遅延値が含まれており、最初の遅延値の最小値は 3 で、通常の値です。は 4、最大値は 5、2 番目の遅延値の最小値は 5、標準値は 6、最大値は 7 です。コンポーネント インスタンス U2 の「立ち上がり遅延値」は最初の遅延値で指定され、立ち下がり遅延値は 2 番目の遅延値で指定され、「不定状態までの遅延値」と「カットオフ遅延値」は両方とも指定されます。遅延時間は2つの遅延値の最小値で指定され、各値の値は以下のとおりです。

最小遅延 立ち上がり遅延=3 立ち下がり遅延=5 不定状態までの遅延=min(3,5) ターンオフ遅延=min(3,5)
典型的な遅延 立ち上がり遅延=4 立ち下がり遅延=6 不定状態までの遅延=min(4,6) ターンオフ遅延=min(4,6)
最大遅延 立ち上がり遅延=5 立ち下がり遅延=7 不定状態までの遅延=min(5,7) ターンオフ遅延=min(5,7)

および # (2:3:4,3:4:5,4:5:6) U3(out,i1,i2);

「遅延」には 3 つの遅延値が含まれます。最初の遅延値の最小値は 2、標準値は 3、最大値は 4 です。2 番目の遅延値の最小値は 3、標準値は 4、そして最大値は 5、3 番目の遅延値の最小値は 4、標準値は 5、最大値は 6 です。コンポーネント インスタンス U3 の「立ち上がり遅延値」は最初の遅延値で指定され、「立ち下がり遅延値」は 2 番目の遅延値で指定され、「カットオフ遅延値」は 3 番目の遅延値で指定され、その「不定状態への遅延」は、3 つの遅延値の最小値によって指定されます。各値の値は下表のとおりです。

最小遅延 立ち上がり遅延=2 立ち下がり遅延=3 不定状態までの遅延=min(2,3,4) オフディレイ=4
典型的な遅延 立ち上がり遅延=3 立ち下がり遅延=4 不定状態までの遅延=min(3,4,5) オフディレー = 5
最大遅延 立ち上がり遅延=4 立ち下がり遅延=5 不定状態までの遅延=min(4,5,6) オフディレー = 6

例: Verilog を使用して、モジュール D の遅延シミュレーション モジュールを構築します
。そのゲート レベルの実装は次のとおりです。モジュール D の論理図は、5 単位時間の遅延時間を持つ AND ゲートと OR を含みます。 4 単位時間の遅延時間を持つゲート。
ここに画像の説明を挿入
遅延のあるモジュール D コード:

module D(out,a,b,c);
 output out;
 input a,b,c;
 wire e;
 and #(5) a1(e,a,b); 
 or #(4) o1(out, e,c);
endmodule

時間遅延を伴うモジュール D のテスト スティミュラス モジュール:

module D_tb;
 reg A,B,C;
 wire OUT;
 D d1(.out(OUT), .a(A), .b(B), .c(C));
 initial
 	begin
 		     A=1'b0;B=1'b0;C=1'b0;
 		#10  A=1'b1;B=1'b1;C=1'b1;
 		#10  A=1'b1;B=1'b0;C=1'b0;
 		#20  $finish;
 	end
 endmodule

ここに画像の説明を挿入

8.2 モジュール遅延のモデリング

1. 遅延説明ブロック ブロック指定
モジュールの入力ピンと出力ピン間の遅延をモジュール パス遅延と呼びます。Verilog では、パス遅延はキーワード specify と endspecify の間に割り当てられ、キーワード間のステートメントがspecify ブロック (つまり、指定されたブロック) を形成します。「specify」と「endspeclfy」は、それぞれ遅延指定ブロックの開始識別子と終了識別子です。

Specify ブロックには、次の操作ステートメントが含まれています。
(1) ブロックを介したすべてのパス遅延を定義します。
(2) 回路内のタイミング チェックを設定します。
(2) specparam 定数を定義します。

ここに画像の説明を挿入
例: 上図を例に、指定ブロックを使用して途中の M モジュールのパス遅延を記述します。コードは以下のように表示されます。

module M(out,a,b,c,d);
 input a,b,c,d;
 output out;
 wire e,f;
 assign out=(a&b)|(c&b);//逻辑功能
 specify //包含路径延迟语句的specify块
 	(a=>out)=9;
 	(b=>out)=9;
 	(c=>out)=11;
 	(d=>out)=11;
 endspecify;
 endmodule

2. パス遅延の記述方法
(1) 並列接続
各パス遅延文には、ソース ドメインまたはターゲット ドメインがあります。上の例のパス遅延ステートメントでは、a、b、c、および d は送信元ドメインの場所にあり、out は宛先ドメインです。
指定ブロックでは、記号「=>」を使用して並列接続を記述します。その構文形式は次のとおりです。
(<source_field>=><destlnation_field>)=<delay_value>;
ここで、<遅延値> には 1 ~ 3 個の遅延値を含めることができ、「最小値、標準値、最大値」も使用できます。遅延表現。遅延値が複数の値で構成される場合は、
(a=>out)=(8,9,10)のように、遅延値の外側に
入力 a から出力 b を意味する括弧のペアを追加する必要があります。遅延の最小値、標準値、最大遅延はそれぞれ 8、9、10 時間単位です。

並列接続では、ソース フィールドの各ビットが宛先フィールドの対応するビットに接続されます。ソースフィールドと宛先フィールドがベクトルの場合、それらのビット数は同じでなければなりません。そうしないと不一致が発生します。したがって、並列接続では、ソース ドメインの各ビットと宛先ドメインの各ビットの間の遅延が考慮されます。
以下の図は、ソース ドメインとターゲット ドメイン間のビットがどのように並列接続されているかを示しています。同時の例では、並列接続の Verilog 記述が示されています。

ここに画像の説明を挿入
(2) フル接続
指定ブロックでは、記号「*>」を使用してフル接続を表します。その文法形式は次のとおりです。
(<source_field>*><destination_field>)=<delay_value>;
フル接続では、ソース ドメインの各ビットがソース ドメインの各ビットに接続されます。ターゲットドメイン。ソースとデスティネーションがベクトルの場合、それらのビット数は同じである必要はありません。完全な接続は、次の図に示すように、ソースのすべてのビットと宛先のすべてのビットの間の遅延を表します。

ここに画像の説明を挿入
3. spacparam 宣言ステートメント
spacparam は、spacparam ステートメントの指定ブロックの例を使用した次の例に示すように、指定ブロック内のパラメーターを定義するために使用されます。

module parallel_connected(out, a, b); 
 input a, b;
 output out; 
 wire out; 
 assign out=a&b; //逻辑功能
 //在指定块内部定义参数
specify
 specparam a_to_out = 9; 
 specparam b_to_out = 11;
 (a => out) = a_to_out;
 (b => out) = b_to_out; 
endspecify
endmodule

specparam ステートメントの形式と機能はパラメーター仕様ステートメントに似ていますが、次の点が異なります:
(1) specparam ステートメントは遅延仕様ブロック (specify ブロック) にのみ使用できますが、パラメーター ステートメントは遅延仕様には使用できません。中にブロックが出現します。
(2) specparam ステートメントで定義されるパラメータは遅延パラメータのみですが、parameter ステートメントで定義されるパラメータは任意のデータ型の定数パラメータにすることができます。
(3) specparam ステートメントで定義された遅延パラメーターは遅延記述ブロック内でのみ使用できますが、parameter ステートメントで定義されたパラメーターはモジュール内のどこでも使用できます。
指定パラメータは遅延の割り当てを容易にするためにモジュールに提供されています。遅延を示す値の代わりに指定パラメータを使用することをお勧めします。このように、回路のタイミング仕様が変更された場合、ユーザーは各パスの遅延値を 1 つずつ変更するのではなく、指定パラメータの値を変更するだけで済みます。

8.3 タイミングチェックに関連するシステムタスク

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

9. 準備されたステートメントをコンパイルする

コンパイル前処理は Verilog コンパイル システムに不可欠な部分です。つまり、コンパイル システムはいくつかの特別なコマンドを前処理し、その後、前処理結果とソース プログラムとともに通常のコンパイル プロセスを実行します。「`」 (バックティック) で始まる特定の識別子は、コンパイルされた準備済みステートメントです。Verilog 言語でコンパイルする場合、特定のコンパイラ ディレクティブは、他の異なるコンパイラ ディレクティブが検出されるまで、コンパイル プロセス全体 (コンパイル プロセスは複数のファイルにまたがる場合があります) を通じて有効です。一般的に使用されるコンパイラの前処理ステートメントは次のとおりです。
ここに画像の説明を挿入

9.1 マクロ定義

`define コマンドはマクロ定義コマンドであり、指定された識別子で文字列を表すことによって Verilog HDL コードの可読性と保守性を向上させ、パラメータまたは関数が間違っている、または許可されていない場所を見つけます。
`define ディレクティブは、C 言語の #define ディレクティブに似ており、モジュールの内部または外部で定義できます。コンパイラがコンパイル中にこのステートメントを検出すると、マクロ テキストをマクロ名で置き換えます。
`define の宣言構文形式は次のとおりです。
`define <マクロ名><テキスト>
宣言構文のコード内の適用形式は次のとおりです。マクロ名の前にある "`" を忘れないでください。
`macro_name
例:
`define MAX_BUS_SIZE 32
...
reg [`MAX_BUS_SIZE-1:0] AddReg;

`define ディレクティブがコンパイルされると、そのディレクティブはコンパイル中ずっと有効になります。たとえば、MAX_BUS_SIZE は、`別のファイルでディレクティブを定義することにより、複数のファイルで使用できます。
`undef ディレクティブは、以前に定義されたマクロをキャンセルします。例:
`define WORD 16 //置換するテキスト マクロを作成します
...
Wire[`WORD:1] Bus;
...
`undef WORD //`undef コンパイル命令の後、WORD のマクロ定義は存在しません

マクロの定義命令に関しては、以下の8つの注意事項があります。
(1) マクロ定義名は大文字でも小文字でも構いませんが、変数名と重ならないように注意してください。
(2) すべてのコンパイラ ディレクティブと同様に、マクロ定義は、単一ファイル (プロジェクト内の他のソース ファイル) の境界を超えても、次の `define、`undef、または `resetall ディレクティブによって上書きされない限り、引き続き有効です。 `define スコープによる制限はありません。
(3) マクロに変数を定義した場合、その変数をマクロ本体内で使用することができ、マクロ使用時に実際の変数式に置き換えることができます。
(4) 中央の改行文字をバックスラッシュ「\」でエスケープすると、マクロ定義を複数行にまたがることができ、改行はマクロ テキストの一部になります。
(5) マクロ定義行の最後に終了を示すセミコロンを付ける必要はありません。
(6) マクロ本体から分離できない言語トークンには、コメント、数字、文字列、予約キーワード、演算子などがあります。
(7) コンパイラ指令をマクロ名として使用することはできません。
(8) マクロ定義のテキストを式にすることもできます。

`define とparameter には違いがあります。`define とparameter はどちらもテキストの置換を完了するために使用できますが、本質的には異なります。前者はコンパイル前に前処理され、後者は通常のコンパイル中に置き換えられます。また、`defineとparameterには以下の2点の違いがあります。
(1) スコープが異なります: パラメーターは宣言されたファイルに作用します。`define は、コンパイラーがこの命令を読み取った時点からコンパイルの終了まで有効です。または、`undef コマンドに遭遇したときにプロジェクト全体に適用できます。無効にします。パラメーターをプロジェクト全体に作用させたい場合は、宣言ステートメントを別のファイルに記述し、`include を使用して各ファイルに宣言ファイルを含めることができます。
`define はコード内のどこにでも記述できますが、パラメータはアプリケーションの前に定義する必要があります。通常、コンパイラはコンパイル順序を定義したり、最下位モジュールからコンパイルを開始したりできるため、最下位レベルで記述するだけで十分です。
(2) 転送関数が異なります。パラメータは、パラメータ化された呼び出しを実現するためにモジュールのインスタンス化中にパラメータ転送として使用できます。`define ステートメントにはこの機能がありません。`define ステートメントは式を定義できますが、parameter は変数の定義にのみ使用できます。
ここに画像の説明を挿入

9.2 ファイル組み込み処理

いわゆる「ファイルインクルード処理」とは、ソースファイルに別のソースファイルの内容をすべて含めること、つまり、このファイルに別のファイルをインクルードすることを意味します。Verilog 言語には、「ファイルのインクルード」操作を実装するための `include コマンドが用意されています。その一般的な形式は次のとおりです: include "filename"。
ここに画像の説明を挿入
ここに画像の説明を挿入

9.3 シミュレーション時間のスケーリング

ここに画像の説明を挿入
このコマンドでは、時間単位パラメーターを使用して、モジュール内のシミュレーション時間と遅延時間の基本単位を定義します。時間精度パラメーターはモジュールのシミュレーション時間の精度を宣言するために使用され、このパラメーターは遅延時間値 (シミュレーション前) を丸めるために使用されるため、このパラメーターは丸め精度とも呼ばれます。同じプログラム設計内に複数の `timescale コマンドがある場合、最小の時間精度の値がシミュレーションの時間単位を決定するために使用されます。さらに、時間精度は少なくとも時間単位と同じ精度である必要があり、時間精度の値は時間単位の値を超えることはできません。
`timescale コマンドでは、時間単位と時間精度パラメータ値の説明に使用される数値は整数である必要があります。有効な数値は 1、10、100 で、単位は秒 (s)、ミリ秒 (ms)、マイクロ秒 ( us)、ナノ秒 (ns)、ピコ秒 (p 除算、ミリピコ秒 <fs)。次の例は、「timescale」コマンドの使用法を示しています。
例 シミュレーションのタイムスケールの例(1)
`timescale 1ns/1psモジュール
内のすべての時間値は、1ns の整数倍を表します。これは、`timescale コマンドで定義された時間単位が 1ns であるためです。`timescale コマンドは時間精度を 1ps として定義しているため、ブロック内の遅延時間は小数点以下 3 桁の実数として表現できます。
(2) `timescale 10us/100ns
この例では、モジュール内の時間値はすべて 10us の整数倍です。`timesacle コマンドで定義されている時間単位が 10us であるためです。遅延時間の最小分解能は 10 分の 1 マイクロ秒 (100ns) です。つまり、遅延時間は小数点第 1 位の実数で表現できます。
ここに画像の説明を挿入

9.4 条件付きコンパイルコマンド

ここに画像の説明を挿入

9.5 その他のステートメント

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

10. Verilog テスト手法の概要

集積回路テストの分野では、一般的に使用されるテスト方法には、完全テスト、ランダム テスト、および自動テストが含まれます。
(1) 完全なテスト方法 複雑な設計の場合、コードのカバレッジを確認することで検証作業が完了したかどうかを確認する重要な方法となることがよくあります。コード カバレッジは、Verilog コードで記述された機能のどの程度がシミュレーション中に検証されたかを示します。通常、コード カバレッジには以下が含まれます。
· ステートメント カバレッジ
· パス カバレッジ
· ステート マシン カバレッジ
· トリガー カバレッジ
· 式カバレッジ
(2) ランダム テスト メソッドは
、Verilog でのランダム テスト用の複数のシステム コマンドを提供します。ランダム テストのシステム関数は通常、次の目的で使用されます。通信分野で一般的に使用されるフレーム同期検索回路など、実際のアプリケーション状況をシミュレートするには、受信したデータストリームから送信側によって固定的に挿入された特別なコードパターンを検出する必要があり、データ自体にもこのパターンが含まれる場合があります。この場合、ランダム化テストの方が実際のアプリケーションに近くなります。最も一般的に使用されるシステム コマンドの 1 つは、乱数生成システム タスク $random です。
(3) 自動テスト方法
集積回路テストの分野では、包括的かつ正確なテストによって大規模集積回路の正常な動作を保証できますが、設計を完全にテストできることを保証する唯一の方法はタスクを自動化することです。通常は、対応する数のサンプリング値を使用してチェック テーブルを作成します。ソース コードを変更すると、すべてのテスト プログラムが自動的に再実行されます。ただし、自動テストを使用すると切り捨てエラーが発生する可能性があることに注意してください。

出典: Cai Jueping 教師の Verilog コース

おすすめ

転載: blog.csdn.net/KIDS333/article/details/127101378