Verilogの基本(1)Verilogの基本的な構文と注意事項

基本知識

 0.1モジュール(モジュール)

        Verilogのモジュールは、入力ポートと出力ポートを備えたブラックボックスと見なすことができます。ブラックボックスには入力インターフェイスと出力インターフェイス(信号)があり、ボックス内の入力に対して特定の操作を実行することで特定の機能が実現されます。(C言語の関数と同様)

図1モジュールの概略図

0.1.1モジュールの説明

図1に示されているtop-module(top_module)構造は、Verilogでは次のように記述できます。

module  top_module(
    input a,
    input b,
    output out
);

   ....... 

endmodule
  • モジュールはmoduleで始まり、endmoduleで終わります
  • top_moduleはモジュール名です
  • input:は入力ポートです
  • output:は出力ポートです
  • すべてのコードはモジュールモジュールに含まれている必要があります

 同様に、図1に示されているサブモジュール(mod_a)構造は、Verilog言語で次のように記述できます。

module  top_module(
    input in1,
    input in2,
    output out
);

   ....... 

endmodule

各モジュールは.vファイルの個別のブロックにある必要があり、モジュール名はファイル名です(正規コード!)

0.1.2モジュールの入力および出力信号

  • 出力:出力
  • 入力:入力

モジュールの入力ポートと出力ポートはモジュールの信号を見ることができます。信号タイプが書き込まれていない場合、デフォルトはワイヤタイプの信号です。

// 以下这两个语句本质是一直的
input a;

input wire a;

ワイヤータイプの信号に加えて、regタイプの信号もあります。詳細についてはセクション1.4を参照してください。

0.1.3モジュールのインスタンス化

        図1に示すように、top_moduleの2つの入力ポートは、サブモジュール(mod_a)の入力ポートに接続されています。top_moduleモジュールでmod_aモジュールの機能を使用するにはどうすればよいですか。これにはモジュールのインスタンス化が必要です。Top_moduleはC言語のメイン関数と見なすことができ、セカンダリモジュールmod_aは通常の関数と見なすことができるため、メイン関数で他の関数を呼び出して対応する関数を完成させることができます。

top_moduleでmod_aをインスタンス化する方法は次のとおりです。

モジュールインスタンス化構文:モジュール名インスタンス名(ポートに接続された信号を定義します)。

module  top_module(
    input a,
    input b,
    output out
);

	mod_a instance2 (.in1(a), .in2(b), .out(out));

endmodule
  • mod_aで定義されたポート順序でインスタンス化します。mod_ainstance1(a、b、out);
  • mod_aポート名でインスタンス化します:mod_a instance2(.in1(a)、.in2(b)、.out(out));(これをお勧めします)

0.2ロジックブロック(常に生成)

0.2.1常にロジックブロック

        alwaysブロックは、組み合わせ論理ブロックと順次論理ブロックを構築できます。if、case、forなどの複雑な論理操作は、この論理ブロックに含める必要があります。

(1)コンビネーションロジックブロック

module top_module();

    always @(*) begin
        ....
    end

endmodule
  • alwaysロジックブロック内のシグナルが変更されたらすぐにトリガーし、 begin-endの間にステートメントを実行します
  • begin-endは、複数のステートメントを1つのコードブロックに結合するために使用され、ステートメントが1つしかない場合は省略できます。

(1)シーケンシャル論理回路

module top_module();

    always @(posedge clk) begin
        ....
    end

endmodule
  • clk信号の立ち上がりエッジでトリガー
  • ポーズ:立ち上がりエッジ
  • ネガエッジ:立ち下がりエッジ

0.2.2ロジックブロックの生成

generateは、主にforループと組み合わせて使用​​されます。主な用途は次のとおりです。

  • ベクトル内の複数のビットで操作を繰り返す
  • 同じモジュールの複数の繰り返しインスタンス化(主な目的)

(1)操作ベクトル

module top_module(input [7:0] in,  output [7:0] out);
    genvar i;        // genvar i; 也可以定义在generate内部
    generate
        for(i=0; i<8; i++) begin: bit
             assign out[i]=^in[8-1:i];
        end
    endgenerate
endmodule

(2)モジュールが複数回繰り返しインスタンス化される

module  top_module(
    input a,
    input b,
    output out
);
    genvar i;
    generate
        for(i=0; i<8; i++)  begin: gen_mod_a   //  gen_mod_a 为每个begin_end的结构的名称
            mod_a instance2 (.in1(a), .in2(b), .out(out));
        end
    endgenerate
endmodule
  • 注:モジュールが複数回インスタンス化される場合は、各begin_end構造体の名前を記述する必要があります(gen_mod_a)
  • シミュレーターは、gen_mod_aによって生成された構造を識別します:gen_mod_a [0]、gen_mod_a[1]...。

0.3割り当て方法

Verilogには、連続割り当て、ブロッキング割り当て、および非ブロッキング割り当ての3つの割り当て方法があります。

0.3.1継続的な割り当て(割り当て)

assign x = y;
  • このステートメントは、xとyの2つの信号、つまり実際の物理的な接続を接続することを意味します。
  • 常にブロックで使用することはできません

0.3.2ブロッキング割り当て(=)

// 组合块
always @(*)  begin
	out1 = a ;
    a = b ;
    out2 = a ;
end
  • 結合されたalwaysブロックでのブロック割り当て
  • 実行順序: begin_endステートメントブロックの順序に従って順番に実行します。上記の出力結果は次のとおりです。out1= a、out2 = b

 0.3.3ノンブロッキング割り当て(<=)

// 时序块
always @(posedge clk)  begin
	out1 <= a ;
    a <= b ;
    out2 <= a ;
end
  • シーケンシャルalwaysブロックで非ブロッキング割り当てを使用する
  • 実行順序: begin_endのすべてのステートメントが並行して実行され、上記の出力結果は次のようになります。out1 = a、out2 = a


第1章基本的な文法

1.1識別子

(1)目的:識別子は、定数、変数、信号、ポート、パラメーター名、モジュール名などを定義するために使用されます。

(2)構成:文字、数字、$、およびアンダースコアの任意の組み合わせ

(3)注:

  • 大文字と小文字の区別(Verilogとverilogは異なります)
  • 最初の文字は文字またはアンダースコアのみにすることができます(123demoは不正な識別子です)

1.2論理値と論理演算

1.2.1論理値

Verilogには4つの論理値があります:0、1、x、z

  • 0:低レベル
  • 1:高レベル
  • x:状態が不明であることを意味します
  • z:高抵抗状態を示します

注:ここでのzとxは大文字と小文字を区別しません(X、Zも使用できます)

1.2.2論理演算

(1)論理演算子:&&(and)、==(等しい)、||(または)、!=(等しくない)

  • m && nなど:mとnがすべて真であるかどうかを判断します(0が真でない場合)。真の場合は1'b1を出力し、そうでない場合は1'b0を出力します(4'b1010&4'b0101 = 1'b1
  • 最終的な出力結果はわずか1ビットです

(2)ビット演算子:&、|、〜、^、〜&、〜^、〜| 

  • m&nなど:mの各ビットとnの各ビットのビットごとのAND演算を実行します(4'b1010&4'b0101 = 4'b0000
  • 出力結果はm/nのビット数と同じです。

(3)削減演算子:&、|、〜、^、&、〜^、〜| 

  • 演算に関係するパラメーターが1つしかない場合(&は単項演算子)、それは縮小と演算、つまりベクトルの内部演算を意味します。
&a [3:0] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf)
|b [3:0] // OR: b[3]|b[2]|b[1]|b [0]相当于(b[3:0]!= 4'h0)
^c [2:0] // XOR:c[2]^c[1]^c[0]
  • 即(&4'b0101 = 0&1&0&1 = 1'b0  )
  • 最終的な出力結果はわずか1ビットです

1.3定数の表現

C言語と同様に、定数には整数、実数、文字列の3種類があります。

1.3.1整数定数を10進整数として表す

(1)正の数:ビット幅32ビットの10進整数を表す10を直接書き込みます(システムデフォルト)

(2)負の数:-10は、2の補数で表す必要があり、もう1つの符号ビット(1 1010)が必要です。

(3)科学的記数法で表現する:12.345e3は12345を意味します

1.3.2基数での整数定数の表現

[换算成二进制数后的位宽]'[数制符号][与数制对应的值]

(1)バイナリ(b):8'b1000_1100      

(2)16進数(h):8'h8c

(3)8進数(o):8'o214

(4)10進数(d):8'140

予防:

  • バイナリを表す場合は、読みやすさを向上させるために4ビットごとにアンダースコアを書き込むのが最適です。たとえば、8'b1000_1100は8'b10001100と同じです。
  • xが基数表記で検出された場合:16進数で4 x、8進数で3 x  
  • ビット幅が2進数よりも大きい場合、左側は自動的に0で埋められ、ビット幅が2進数よりも小さい場合、2は左から切り捨てられます。

1.3.3文字列(二重引用符付き)

(1)各文字は8ビットASCIIコード値で表されます。つまり、1バイトのストレージスペースが必要です。

(2)例:「   Helloworld」文字列は11個のASCII記号で構成されており、11バイトのストレージスペースが必要です。

1.3注釈方法

Verilogには、主に2つのタイプのコメントがあります。行コメント(//)とブロックコメント(/ * .... * /)です。表現方法はC言語と一致しています。

// 行注释

/*
        块注释

*/

1.4変数(wire、reg)

Verilogには、主に2つのタイプの変数があります。wireとregです。

1.4.1ワイヤ

(1)ワイヤタイプ(ワイヤ):回路間の物理的な接続を示し、ワイヤによって定義される変数は信号ポートと見なすこともできます

(2)2つの線信号が連続して割り当てられると、それらは論理ブロック内の実際の物理接続にマッピングされます。このとき、これら2つの信号ポートの変更は同期されます。

wire a;

wire b;

assign b = a;    // 表示a与b之间生成实际的物理连线

1.4.2登録

(1)レジスタタイプ(reg):抽象データストレージユニットを表します

(2)regには、ある時点での状態を維持する機能があります。

1.4.3使用法と注意事項

(1)alwaysおよびinitialステートメントで割り当てられた変数(割り当て番号の左側の変数)はすべてreg変数です

(2)assignステートメントで割り当てられた変数はワイヤー型変数です

1.5ベクトルとパラメーター(定数)

1.5.1パラメータパラメータ(定数)

(1)パラメータは一種の定数であり、通常はモジュール内に表示され、状態やデータビット幅などを定義するためによく使用されます。

parameter STATE = 1'b0;

(2)宣言されたファイルでのみ機能し、柔軟に変更できます

(3)ローカルパラメータlocalparam、このモジュールでのみ使用されます

localparam  STATE= 1'b1’;

(4)他の変数を区別するため に、パラメーターの名前は通常大文字です。

1.5.2ベクトル(ベクトル)

ベクトル(ベクトル)は、信号のグループの集合であり、ビット幅が1ビットを超える有線信号と見なすことができます。

(1)定義:

格式:input/output  wire/reg [upper:lower] vector_name

//输入输出型
input [7:0] a,b,
output reg [7:0] out

// 模块中间向量
wire [7:0] c, e;
reg [7:0] d;
  • [upper:lower]は、ビット幅を定義します。たとえば、[7:0]は、ビット幅が8ビットであることを意味します。つまり、upper = 7、lower = 0
  • vector_nameは、一度に複数のベクトルを書き込むことができます

1.5.3ベクトルチップセレクト

  • a [3:0]ベクトルaの0〜4ビットデータ
  • b[n]ベクトルbのn番目のビットデータ
  • c [-1:-2]ベクトルcの下位2ビット
  • c [0:3]ベクトルcの最上位4ビットデータ

マルチプレクサアプリケーション:256対1のセレクタを実装し、sel信号が選択信号として使用されます。sel= 0の場合はin[3:0]を選択し、sel = 1の場合はin[7:4]を選択します。の上。

module top_module (
	input [1023:0] in,
	input [7:0] sel,
	output [3:0] out
);
	assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};

	// assign out = in[sel*4 +: 4];		
	// assign out = in[sel*4+3 -: 4];	
endmodule
  • チップセレクト信号selの入力はnビットの2進数であり、操作に参加してインデックスとして機能すると、自動的に10進数に変換されます。
  • この質問で選択された信号セグメントは次のとおりです。in[sel*4 + 3:sel * 4]ですが、これはVerilogのチップ選択構文規則に準拠していないため、次のように記述します
    。in [sel * 4 +:4]   インデックスがsel*から始まることを意味します。4in
    [sel*4 + 3-:4]から始まる上位4ビット信号は 、sel * 4+3から始まる下位4ビット信号を示します。
  • または、必要な各ビットを直接選択し、{}を使用して新しいベクトルに連結します:
    {in [sel * 4 + 3]、in [sel * 4 + 2]、in [sel * 4 + 1]、in [ sel * 4 + 0]}

参考記事:HDLBits:オンラインでVerilogを学ぶ(13・問題60-64)-知る(zhihu.com)

1.6三項式

(1)C言語と同様に、Verilogにも3項式があります。

condition ? if_true : if_false

条件がtrueの場合、式はif_trueと評価されます。それ以外の場合、式はif_falseと評価されます。

(2)アプリケーション

(sel ? b : a)   // 一个二选一MUX,通过sel的值选择a或者b

always @(posedge clk)         // 一个T触发器
  q <= toggle ? ~q : q;

assign out = ena ? q : 1'bz;  // 三态缓冲器

(3)参考記事:  HDLBits:Verilog Onlineの学習(8.問題35-39)-Zhihu(zhihu.com)

1.7分岐ステートメント(if-else、case)

1.7.1if-elseステートメント

(1)最も一般的な形式:(利点:すべての可能な出力が書き込まれ、不明なレベルの出力はありません!)

if(<条件表达式 1>)
    语句或语句块 1;
else if(<条件表达式 2>)
    语句或语句块 2;
    ………
else
    语句或语句块 n;

(2)if-elseネストを使用することはお勧めしません。優先順位の問題が発生し、論理的な混乱が生じます。

(3)  すべてのif-elseステートメントは、(1)の形式で記述する必要があります

(4)条件式に従って順次比較すると、優先順位があります

1.7.2ケースステートメント

(1)書面:

case(<控制表达式>)
    <分支语句 1> : 语句块 1;
    <分支语句 2> : 语句块 2;
    <分支语句 3> : 语句块 3;
    ………
    <分支语句 n> : 语句块 n;

    default : 语句块 n+1;
endcase

<controlexpression>と<branchstatementn>の値が等しい場合、対応するステートメントが実行されます。それ以外の場合、デフォルト後のステートメントが実行されます。

(2)分岐ステートメントを実行した直後に、caseステートメントの構造から飛び出し、caseステートメントの実行を終了します。

(3)  <ブランチステートメントn>の値は互いに異なっている必要があります

(4)caseステートメントブロックをencaseで終了します

(5)ブランチステートメント間に優先順位はありません

(6)特定のアプリケーション:ユースケースステートメントを使用してマルチプレクサを構築します(例として9対1のマルチプレクサを取り上げます)

module top_module( 
    input [15:0] a, b, c, d, e, f, g, h, i,
    input [3:0] sel,
    output [15:0] out );
    always @(*) begin
        case(sel)
            4'h0:begin out = a; end
            4'h1:begin out = b; end
            4'h2:begin out = c; end
            4'h3:begin out = d; end
            4'h4:begin out = e; end
            4'h5:begin out = f; end
            4'h6:begin out = g; end
            4'h7:begin out = h; end
            4'h8:begin out = i; end
            default: out = 16'hffff;
        endcase
    end
endmodule

1.8forループステートメント

(1)書面:

integer i;
always @(*)  begin 
    for(i=0; i<n; i++)  begin: for_name
        <循环语句>
    end
end
  • <ループステートメント>をn回実行する
  • for_nameは、各ループの名前です

1.9関係演算子(>、<、> =、<=)

  • 操作の結果がtrueの場合、1を返します
  • 操作の結果がfalseの場合は0を返します
  • オペランド値が不確定(x)の場合、戻り値はxです。

2.0連結演算子({、})

2.0.1スプライシング

中括弧とコンマのペアを使用して「{、}」連結演算子を作成すると、コンマで区切られたデータが順番に新しいデータに連結されます

wire [1:0] a;
wire [3:0] b;
wire [5:0] c;
wire [11:0] d = {a, b, c} 

2.0.2スプライシングによるシフト

左にスプライシングすると右に移動し、右にスプライシングすると左に移動します。

always @(posedge clk) begin
    if(rst_n == 1'b0)
        out <= 4'b0;
    else
        out <= {in, out[3:1]};    // 右移
end

2.0.2コネクタでの繰り返し操作

構文: {繰り返し回数{ベクトル}}

{3{a}} = {a, a, a}
{3'd5, {2{3'd6}}}   // 9'b101_110_110.

3バイナリ全加算器

  • a、bは入力1ビットデータです
  • cinは、前の加算器のキャリー入力です。
  • coutは、この加算器のキャリー出力です。
  • 合計=a+ b

コード:

module add1 (
	input a,
	input b,
	input cin,	
	output sum,
	output cout
);
	assign sum = a^b^cin;
	assign cout = (a&b) | (a&cin) | (b&cin);
endmodule

4つの16進全加算器

16進全加算器を上の図に示します。これは、前のセクションの16個の2進全加算器で構成できます。

Verilogに実装されている16進全加算器コードは次のとおりです。

module add16 (	
	input [15:0] a,
	input [15:0] b,
	input cin,
	
	output [15:0] sum,
	output cout
);
	wire [16:0] Add_cin;
	assign Add_cin[0] = cin;    // 上图中第一个二进制加法器进位输入为0 assign Add_cin[0] = 1b'0;

//  用 generate 进行模块多次实例化
// generate 应用范围:对矢量(vector)多个位重复操作,模块重复实例化
	genvar i;
	generate
		for(i=0; i<16; i++) begin: gen_add16		// gen_add16 为每个begin_end的结构,仿真器会通过他来标识生成结构,gen_add16[0],gen_add16[1]....
			add1 Add16(.a(a[i]), .b(b[i]), .cin(Add_cin[i]), .sum(sum[i]), .cout(Add_cin[i+1]));
		end
	
	endgenerate
	
	assign cout = Add_cin[16];

endmodule

5モジュールでのパラメータの受け渡し

5.1パラメータを渡すことができるモジュールの定義

module counter
// 参数传递
#(
    parameter COUNT_MAX = 25'd24_999_999,
    parameter STATE     = 1'b0            // 多个参数用逗号隔开
)
(
    input  wire  sys_clk,
    output reg led_out
);
// 代码主体
endmodule

5.2パラメータを使用したブロックのインスタンス化

// 参数传递
#(
   .COUNT_NUM( 25'd24_999_999),        // 传入参数
   .STATE(1'b0)
)
counter1_init      // 实例化模块的名称位置
(
    .sys_clk   (sys_clk),
    .led_out(led_out)
); 

 

参照:

[1] Wildfire "FPGA Verilog Development Practical Guide":[Wildfire] FPGA Verilog Development Practical Guide-Altera EP4CE10JourneyPro開発ボードに基づく-[Wildfire]FPGAVerilog Development Practical Guide-Altera EP4CE10 Journey Pro Development Board Documentation(embedfireに基づく) .com)https://doc.embedfire.com/fpga/altera/ep4ce10_pro/en/latest/index.html

[2] HDLBits中国語ガイド:HDLBits中国語ガイド-zhihu(zhihu.com)

おすすめ

転載: blog.csdn.net/jac_chao/article/details/123744724