Verilog-HDL 入門

5. Verilog-HDL の文法

5.1 識別子

 識別子とは信号線や素子を識別するためにつける名前のことです。英字かアンダースコア “_” で始めなければなりません先頭以外であれば,数字やドル記号 “$” も含めることができます。大文字と小文字は区別されます。予約語(あらかじめ決められている語。たとえば input, and 等など)は使ってはいけません。
 僕は module 2bit_counter_test; と記述してしまい,当然コンパイルが通らないのでかなり長い時間頭を抱えていました(笑)

<正しい識別子> adder2,_a, AND(大文字なので予約語 and とは区別される)
<誤った識別子> $adder2(先頭が $), 2bit_counter(先頭が数字)

5.2 数値表現

数値表現形式: ss'fnn...n

ss は,定数のビット幅を 10 進数で表します。
f は,基数を表します。b が 2 進,o が 8 進,d が 10 進,h が 16 進を表します。
 (それぞれ,binary, octal, decimal, hexadecimal の頭文字をとっています)
nn...n は,定数値を表します。各基数で許される値を書くことができます。
・ ss (ビット幅)を省略すると,32 ビットとして扱われます。
・ f (基数)を省略すると,10 進数として扱われます。
・ nn...n (定数値)は,見やすくするために “_” を区切りとして入れることが可能です。
<記述例>
Verilog ビット幅 基数 2進表現
1'b0 1 2進 0
4'b0100 4 2進 0100
4'd4 4 10進 0100
4'd7 4 10進 0111
8'hf0 8 16進 11110000
8'd1000_1111 8 2進 10001111
1 32 10進 000...0001

5.3 モジュール宣言(module, endmodule)

module モジュール名(入出力ポート名);
  
  …
  回路の記述
  …
  …
endmodule

 モジュールとは回路ブロックのことです。Verilog-HDL で回路やシミュレーションの記述を行うときには必ず宣言します。モジュールの最後は endmodule と記述します。
 module には必ずセミコロン“;”が必要ですが,endmodule には必要ありません。
 モジュール名には適当な名前(識別子)を付けることができます。入出力ポート名には,入力信号と出力信号の両方を記述してください。 

5.4 ポート宣言(input, output)

input 入力信号名;
output 出力信号名
;

 モジュール宣言の入出力ポート名で記述したものを,入力と出力に分けて宣言します。またバスの宣言も可能です。たとえば,

input a3, a2, a1, a0;

という記述を以下のように,

input [3:0] a;

と記述できます。a のそれぞれのビットを参照したいときは,a[3],a[2] などとすることで可能です。またこのとき,上位ビット(MSB)は a[3],下位ビット(LSB)は a[0] となります。つまり,

input [MSB:LSB] バス名;

となります。このバスでの宣言は output 宣言でも有効です。
 また,input,output 以外に inout という双方向のポート宣言もあります。

5.5 ネット宣言,レジスタ宣言(wire, reg)

wire 信号名;
wire [MSB:LSB] バス名;

 wire 宣言ではモジュール内で使用する信号線,配線を定義します。組合せ回路で用いる信号線などは wire 宣言を行います。ポート宣言と同様にバス記述も可能です。wire 宣言を行った変数をネット型変数と呼びます。なお,ポート宣言を行ったものは自動的に wire 宣言されます。

reg 信号名;
reg [MSB:LSB] バス名;

 reg 宣言では,値を保持する信号線を定義します。順序回路で用いるフリップ・フロップ(FF)やラッチなどは reg 宣言する必要があります。ただし,reg 宣言すれば必ず論理合成の時に FF などが生成されるというわけではありません(付録 C.2 を参照)。
 reg 宣言を行った変数をレジスタ型変数と呼びます。レジスタ型変数には reg 以外にも integer などがありますが,ここでは触れません。

 ネット型変数の信号への代入は,assign 文でのみ可能です。
 レジスタ変数の信号への代入は,always 文,initial 文,task,function の中で可能です。

 つまり,reg =順序回路という訳ではなく,wire や reg は上記の文法的な理由で使い分ける,と理解しておいて間違いなさそうです。いずれの変数も,式の右辺や引数としての利用(値を参照する場合)は可能です。

5.6 パラメータ宣言(parameter)

parameter パラメータ名 = 定数;

 定数を定義します。C言語でいうと #define と同じであり,宣言したモジュール以外のモジュールでも使用可能です。

<記述例>
 parameter SETP = 10;   // この記述以降,STEPと記述すれば STEP は 10 とみなされます。

5.7 組合せ回路(assign 文)

assign ネット型変数 = 論理式など;

 簡単な組合せ回路を記述するときには assign 文を用います。 assgin 文の左辺は,ネット型変数( ポート宣言,あるいは wire 宣言された変数)でなければなりません。

<記述例>
 wire a, b, c, d;
 wire [3:0] s, t, u;

 assign c = a | b;        // 2入力OR
 assign u = s & t;        // 4ビット2入力AND
 assign c = (d == 1)? a: b;   // セレクタ(d が1のとき a を,1でないとき b を,c に出力)

5.8 always 文

always @(イベント式) 遅延時間 ステートメント あるいは always #定数値 ステートメント
イベント式には以下の 3 種類があります:
  ・ 信号名       …信号が立ち上がるときと,立ち下がるとき
  ・ posedge 信号名 …信号が立ち上がるとき(positive edge の略)
  ・ negedge 信号名 …信号が立ち下がるとき(negative edge の略)

 順序回路は always 文で記述します。なお遅延時間はステートメントを実行するまでの遅延時間ですが,省略可能です。ステートメントはたいてい 1 文で収まらないので,その場合は begin〜end 文を用います。
 always #定数値 ステートメント は,「定数値時間を経過するごとに」という意味になります。
 always 文は,イベント式に信号名だけを指定した場合,信号に変化があった場合ステートメントが実行されます。イベント式は “or” で区切って複数記述することが可能です。
 また,always 文でも組合せ回路を記述することが可能です。詳しくは,付録 C.2 を参照してください。

<記述例 (非同期 2bit カウンタ)>

 module counter_2bit ( ck, res, q );

  input ck, res;
  output [1:0] q;

  reg [1:0] q;

  always @(posedge ck or posedge res)
   begin
    if (res)
     q <= 0;    // ここを,q <= 2'b00; としても OK
    else
     q <= q + 1;  // ここを,q <= q + 2'b01; としても OK
   end

 endmodule

<シミュレーション結果 (2bit カウンタ)>

D:\My Document\Verilog>vvp 2bit_counter
      
0: ck = 0, res = 0, out = xx
      5: ck = 1, res = 0, out = xx
      10: ck = 0, res = 1, out = 00
      15: ck = 1, res = 1, out = 00
      20: ck = 0, res = 0, out = 00
      25: ck = 1, res = 0, out = 01
      30: ck = 0, res = 0, out = 01
      35: ck = 1, res = 0, out = 10
      40: ck = 0, res = 1, out = 00
      45: ck = 1, res = 1, out = 00
      50: ck = 0, res = 0, out = 00
      55: ck = 1, res = 0, out = 01
      60: ck = 0, res = 0, out = 01
      65: ck = 1, res = 0, out = 10
      70: ck = 0, res = 0, out = 10
      75: ck = 1, res = 0, out = 11
      80: ck = 0, res = 0, out = 11
      85: ck = 1, res = 0, out = 00
      90: ck = 0, res = 0, out = 00
      95: ck = 1, res = 0, out = 01
      100: ck = 0, res = 0, out = 01
      105: ck = 1, res = 0, out = 10


D:\My Document\Verilog>

5.9 ブロッキング代入文とノンブロッキング代入文

 Verilog-HDL では並列性のある命令の流れを記述することができます。それを実現するのがノンブロッキング代入文です。一方で,通常のプログラム言語のように普通の代入文をブロッキング代入文と呼びます。

ブロッキング代入文 ノンブロッキング代入文
initial
 begin
  a = 3;
  b = 5;

  #10
  a = b + 1;
  b = a + 1;
 end
initial
 begin
  a <= 3;
  b <= 5;

  #10
  a <= b + 1;
  b <= a + 1;
 end

 上記の例で,10 サイクル経過したときの a, b それぞれの値は以下のようになります。

ブロッキング代入文 ノンブロッキング代入文
a = 6, b = 7 a = 6, b = 4

 これは,ブロッキング代入文が「代入処理が終了して次の文を実行する」のに対し,ノンブロッキング代入文では「同時刻の代入文は,右辺を評価してから代入する」ためです。上記の例では,10 サイクルになる直前,a = 3, b = 5 であるので, 10 サイクル目では b + 1 = 6, a + 1 = 4 となり,それらが a と b に代入されるため, a = 6, b = 4 となるのです。このように,ノンブロッキング代入文により並列性のある記述が可能となるのです。


Verilog-HDL 入門のトップへ戻る
サポートページのトップへ戻る