論理回路の基礎
VerilogHDLを用いたモジュール記述に入る前に、最低限、論理回路はどんな要素からできているのか知っている必要があります。 VerilogHDLで記述するというのは頭の中の論理回路を言語で記述する作業となります。
レジスタ(register)とは?
論理回路において、フリップフロップなどにより状態を保持する装置をレジスタと呼びます。 レジスタは高速で動作できる一方、保持できるデータ量は比較的少量です。 レジスタ (コンピュータ) - Wikipedia
レジスタの動作を(動いたほうがわかりやすいと思ったので)動画にしてみました。 レジスタには入力D、出力Q、クロックclkの3つのポートがあります。 入力Dに信号が入力されると、クロックの立ち上がりタイミングでキャプチャされ、出力Qから出力されます。 これが全てのクロック毎に繰り返されます。
入力D、出力Qのタイミングがクロックの立ち上がりから少し遅れていますが、これは、キャプチャにかかる遅延、電気信号が信号線を伝達するのに遅延が発生するためです。
組み合わせ回路と順序回路
論理回路には、組み合わせ回路と順序回路の2種類があります。 これまでの入力が現在の出力に影響するかどうかによってどちらの回路か決まります。
組み合わせ回路
その時点での入力値のみによって出力値が決まります(過去の入力は現在の出力に影響を与えない)。 そのため、回路内部に状態を持っていません。
組み合わせ回路の例として、以下のような回路が考えられます。
このような組み合わせ回路をVerilogHDLで記述する場合、function文を用いて記述できます。
\\function文テンプレート function [ビット幅-1:0] function_name; input ....; .... endfunction
順序回路
現在の入力値だけでなく、過去の入力値も現在の出力値に影響を与えます。内部に状態があり、状態が現在の出力に影響します。 内部に状態を保持する方法として、レジスタが用いられます。
順序回路の例として、以下のような回路が考えられます。 このような順序回路をVerilogHDLで記述する場合、順序回路はmodule文を使って記述します。
\\module文テンプレート module module_name #( parameter パラメータ名0 = デフォルト値0, parameter パラメータ名1 = デフォルト値1, ..., parameter パラメータ名N = デフォルト値N ) ( input [ポート幅-1:0] ポート名0, input [ポート幅-1:0] ポート名1, ..., output [ポート幅-1:0] ポート名2, output [ポート幅-1:0] ポート名3, ..., output [ポート幅-1:0] ポート名N ); 論理回路記述; endmodule
module文への入力、module文からの出力はポートを介して行われます。 パラメータ部分は省略可能です。
VerilogHDLを用いた1bitレジスタの記述
VerilogHDLでは、レジスタはalways文と呼ばれる文を用いて、記述することができます。 always文の文法は以下となります。
\\always文テンプレート always @ ( [posedge|negedge] [信号1] or [posedge|negedge] [信号1] or ...) begin レジスタの入力信号・条件記述; end
posedge、negedgeはどのタイミングでレジスタへの入力信号の取り込みを行うかを指定します。 上の動画の動作を記述する場合、クロックの立ち上がり時に信号の取り込みを行っているので、posedgeを用いることになります。
1レジスタ毎に1always文にするのが良いと思います。 (2つ以上のレジスタを1always文内に入れることもできますが、見にくいのでやらないほうがいいと思います。) 上図のレジスタをVerilogHDLに直すと下のソースコードのようになります。
module one_bit_register ( input clk, input to_regA, output from_regA ); reg regA; always @ (posedge clk) begin regA <= to_regA; end assign from_regA = regA; endmodule
ソースコード中には宣言文、always文、assign文があります。
宣言文では、使うワイヤやレジスタを宣言します。宣言はソースコード中のどこに書いてもOKです。 宣言ではワイヤ・レジスタの幅も指定できます。(指定がない場合、幅1になります。)
assign文はレジスタ-ワイヤ接続、及びワイヤ-ワイヤ接続や関係を記述するのに用います。 assign文の文法は以下となります。
\\assign文テンプレート assign wire_B = [wire_A|register_A];
4bitレジスタx3の接続
上の論理回路をVerilogHDLを用いて記述すると以下になります。
module four_bit_regx3 ( input clk, input [3:0] to_regA, output [3:0] from_regC ) \\regA reg [3:0] regA; always @ (posedge clk) begin regA <= to_regA; end assign regA_to_regB = regA; \\regB wire [3:0] regA_to_regB; reg [3:0] regB; always @ (posedge clk) begin regB <= regA_to_regB; end assign regC_to_regB = regB; \\regC wire [3:0] regB_to_regC; reg [3:0] regC; always @ (posedge clk) begin regC <= reg_B_to_regC; end assign from_regC = regC; endmodule
イネーブル付きレジスタ
毎サイクル、入力信号をキャプチャしていては使いにくいため、イネーブルポートのついたレジスタがあります。 en=1のときのみ値の取り込みを行います。
これは以下のように書きます。
wire in; wire out; wire en; reg regA; always @ (posedge clk) begin if (en) begin regA <= in; end end assign out = regA;
もちろん、上の左図の通り
wire in; wire out; wire mux_to_regA; wire en; reg regA; always @ (posedge clk) begin regA <= mux_to_regA; end assign mux_to_regA = (en) ? in : out;
と書いても同様の動作します。(が、特別の理由がない限り、このように書く必要は無いでしょう)
次の記事では、実際にシミュレータを用いて動作を動作させてみます。