はじめに
FPGAの授業の補助資料として入門したての頃に詰まったポイント、早く知っていればよかったなというポイントをいくつかまとめて記事にします。
今回はVerilogを書き始めたときに詰まったwireとregの使い分けです。
wireとreg
Verilogのデータ型として主に用いるのはwire(ネット型)とreg(レジスタ型)です。
wireは配線に対応し組み合わせ回路の記述に使えますが、regは記述の仕方によって組み合わせ回路になったり順序回路であるFFやラッチになったりします。
文法的には
- wire: assign文でのみ代入可能
- reg: always文やfunction文などで代入可能
という違いがあります。
個人的ですが、使い分けとしては
- 順序回路を作りたいとき
regを使いalways文で記述 - 単純な組み合わせ回路を作りたいとき
wireを使いassign文で記述 - 複雑な組み合わせ回路のうち、入力が異なるだけでロジックが同じものを複数作りたい時
wireを使い回路はfunction文で記述(もしくは4+module化) - 複雑な組み合わせ回路のうち、再利用が考えられないもの
regを使いalways文で記述
なお、3の場合で出力が複数ある場合は別のモジュールにして記述すると良いと思います。
例
以下いくつか回路記述例と合成後の実際の回路を示します。
1. 順序回路(フリップフロップやラッチ)を作りたいとき
フリップフロップの例
例えばクロックをCLK、入力をA、出力をQとして、クロック立ち上がり時にAが1の時、Qが反転するという順序回路は
module ex1(
input wire CLK,
input wire RST,
input wire A,
output reg Q
);
always @(posedge CLK) begin
if(RST) Q <= 1'd0;
else if(A) Q <= !Q;
end
endmodule
のようにFFが生成されます。
always文でクロックに同期して出力の値が変わるようにイベント式に@(posedge CLK)
などと記述するとFFが生成されます。
ラッチの例
ラッチを生成したい場合はイベント式に出力を変化させるのに必要な信号線をすべて列挙し@(A or Q or RST)
などと書くと
のようにラッチが生成されます。
なお、always文で組み合わせ回路やラッチを作成する場合、出力に必要な信号線をすべて列挙する書き方をすると漏れがある可能性があるため
@(*)
という省略記法で記述することをおすすめします。
2. 単純な組み合わせ回路を作りたいとき
組み合わせ回路 assign文の例
入力A,Bの論理積を取ったものを出力Cとするとき
module ex2_1(
input wire A,
input wire B,
output wire C
);
assign C = A & B;
endmodule
組み合わせ回路 assign文+三項演算子の例
また、好みですがwire+always+if-elseによる組み合わせ回路の代わりに三項演算子を使って条件分岐させて出力を決める組み合わせ回路もassignで記述できます。(同様の記述のalways版はこちらをご覧ください)
入力をA, B出力をC(2ビット)とし、Aが1なら0b11を、Aが0でBが1なら0b01を、そうでないなら0b00を出力するものは
module ex2_2(
input wire A,
input wire B,
output wire[1:0] C
);
assign C = A ? 2'b11 :
B ? 2'b01 :
2'b00;
endmodule
> ![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/370cfaff-c849-bd54-bcbf-9c31f1b75b93.png)
> という回路が生成されます。
## 3.複雑な組み合わせ回路のうち、入力が異なるだけでロジックが同じものを複数作りたい時
複数の信号に対して同じ組み合わせ回路を使う場合はfunction文を使うかmodule化しておくとコードが短くなりメンテナンスがしやすいです。(コピペをするとあとでバグが見つかったときに修正漏れが生じて悪化します)
### 組み合わせ回路 function文の例
ここではR,G,B 8bit入力に対して組み合わせ回路で4値化し、4値化結果をくっつけて出力O(6bit)とします。
4値化の基準は以下のとおりです
|入力|出力|
|:--:|:-:|
|8'd0|2'b00|
|8'd1~8'd63|2'b01|
|8'd64~8'd127|2'b10|
|8'd128~8'd255|2'b11|
```verilog
module ex3(
input [7:0] R,
input [7:0] G,
input [7:0] B,
output [5:0] O
);
function [1:0] convert(input [7:0] p);
begin
if(p == 8'd0) convert = 2'b00;
else if(p <= 8'd63) convert = 2'b01;
else if(p <= 8'd127) convert = 2'b10;
else convert = 2'b11;
end
endfunction
wire [1:0] r_4 = convert(R);
wire [1:0] g_4 = convert(G);
wire [1:0] b_4 = convert(B);
assign O = {r_4, g_4, b_4};
endmodule
生成さた回路を見てみると下図のように各色ごとに同じような回路が生成されていることがわかります。
4. 複雑な組み合わせ回路のうち、再利用が考えられないもの
複雑な組み合わせ回路のうち1箇所でしか使わない、もしくは複数箇所で使われるがモジュール化する場合は好みですが、個人的にはalways+regで書く事が多いです。
組み合わせ回路 always文の例
2で示した回路と同様のものをalways文で記述してみます。
module ex4_1(
input wire A,
input wire B,
output reg [1:0] C
);
always @(*) begin
if (A) C <= 2'b11;
else if(B) C <= 2'b01;
else C <= 2'b00;
end
endmodule
この方法で組み合わせ回路を記述する際は
- イベント式に内部で使うものすべてを列挙するか*を使う
- すべての条件でregに代入する(regの値を代入しない条件が生じる場合、値を保持する必要があると判定されてラッチが生成されます。)
の2点に注意してください。
2で示した回路より最適化されたものが生成されました。
組み合わせ回路 always文+module化したものを利用する例
3で示した回路の4値化部分をconvertモジュールにし、モジュールを利用する例を次に示します。
module convert(
input [7:0] p,
output reg [1:0] o
);
always @(*) begin
if(p == 8'd0) o <= 2'b00;
else if(p <= 8'd63) o <= 2'b01;
else if(p <= 8'd127) o <= 2'b10;
else o <= 2'b11;
end
endmodule
module ex4_2(
input [7:0] R,
input [7:0] G,
input [7:0] B,
output [5:0] O
);
wire [1:0] r_4;
wire [1:0] g_4;
wire [1:0] b_4;
convert r_c(R, r_4);
convert g_c(G, g_4);
convert b_c(B, b_4);
assign O = {r_4, g_4, b_4};
endmodule
のように上位モジュールでconvertモジュールを複数記述してあげて利用することも出来ます。
この場合も3で示した回路と同じような回路が生成されます。