論理回路の基礎

VerilogHDLを用いたモジュール記述に入る前に、最低限、論理回路はどんな要素からできているのか知っている必要があります。 VerilogHDLで記述するというのは頭の中の論理回路を言語で記述する作業となります。

レジスタ(register)とは?

論理回路において、フリップフロップなどにより状態を保持する装置をレジスタと呼びます。 レジスタは高速で動作できる一方、保持できるデータ量は比較的少量です。 レジスタ (コンピュータ) - Wikipedia

レジスタの動作を(動いたほうがわかりやすいと思ったので)動画にしてみました。 レジスタには入力D、出力Q、クロックclkの3つのポートがあります。 入力Dに信号が入力されると、クロックの立ち上がりタイミングでキャプチャされ、出力Qから出力されます。 これが全てのクロック毎に繰り返されます。

youtu.be

入力D、出力Qのタイミングがクロックの立ち上がりから少し遅れていますが、これは、キャプチャにかかる遅延、電気信号が信号線を伝達するのに遅延が発生するためです。

組み合わせ回路と順序回路

論理回路には、組み合わせ回路と順序回路の2種類があります。 これまでの入力が現在の出力に影響するかどうかによってどちらの回路か決まります。

組み合わせ回路

その時点での入力値のみによって出力値が決まります(過去の入力は現在の出力に影響を与えない)。 そのため、回路内部に状態を持っていません。

f:id:denchu314:20180814124806p:plain
組み合わせ回路

組み合わせ回路の例として、以下のような回路が考えられます。

f:id:denchu314:20180814124954p:plain
組み合わせ回路の例

このような組み合わせ回路をVerilogHDLで記述する場合、function文を用いて記述できます。

\\function文テンプレート
function [ビット幅-1:0] function_name;
    input ....;
    ....
endfunction
順序回路

現在の入力値だけでなく、過去の入力値も現在の出力値に影響を与えます。内部に状態があり、状態が現在の出力に影響します。 内部に状態を保持する方法として、レジスタが用いられます。

f:id:denchu314:20180814134717p:plain
順序回路

順序回路の例として、以下のような回路が考えられます。

f:id:denchu314:20180814134723p:plain
順序回路の例
このような順序回路を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を用いることになります。

レジスタ毎に1always文にするのが良いと思います。 (2つ以上のレジスタを1always文内に入れることもできますが、見にくいのでやらないほうがいいと思います。)

f:id:denchu314:20180813200233p:plain
1bitレジスタ
上図のレジスタを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の接続

f:id:denchu314:20180813211850p:plain 上の論理回路を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

ネーブル付きレジスタ

毎サイクル、入力信号をキャプチャしていては使いにくいため、イネーブルポートのついたレジスタがあります。 f:id:denchu314:20180813225913p:plain 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;

と書いても同様の動作します。(が、特別の理由がない限り、このように書く必要は無いでしょう)

次の記事では、実際にシミュレータを用いて動作を動作させてみます。