HDLてにをは集
HDLてにをは集
目次
より良いグループ開発のためのHDL記述
Q&A
その他
より良いグループ開発のためのHDL記述
ここでは主にHDLの記述スタイルについて説明しています.HDLで課題をやっていくうちに,コンパイルでエラーが出ない(文法上は正しい)回路が作れるようになってくると思います.
しかしこの段階では自由度がまだ多く残っていて,人によってかなり見た目のばらばらなコードを書いてしまいます.回路規模が大きくなった場合,グループで記述スタイルを決めておかないと
相方が読めない,時間がたつと本人も読めない,デバッグが終わらない,分担できない,仕様変更できない
みたいな地獄絵図になってしまいます.
そのような事態にならないために,なるべくバグの入りにくい設計指針を1つ書いてみました.
もちろんここで書かれているスタイルはあくまでも例です.グループでの開発効率向上が目的であることを忘れずに,相方としっかりコミュニケーションをとって より良い設計を目指してください.
assign文
組み合わせ回路 の記述に使用します
組合せ回路では入力の値に応じて随時出力の値も決まるため,代入にはブロッキング代入(=) を使います
代入文の左辺はwire型 ,右辺はreg型,wire型の論理式またはfunction文を使います
マルチプレクサ構文で複雑な組み合わせ回路も可能な限りassignで書きましょう
always文
クロックによって動作する順序回路 の記述に使用します(always @ (posedge clk or negedge rst_n)が基本です)
順序回路ではクロック信号の立ち上がりタイミングでのみ入力値を取り込み記憶&出力するため,代入にはノンブロッキング代入(<=) を使います (実際にはalways文内でブロッキング代入とノンブロッキング代入を混在させて使用することも可能ですが,この場合記述順序によって合成結果が変わるため,意図しない回路が生成される可能性があります)
代入文の左辺はreg型 ,右辺はreg型,wire型の論理式またはfunction文を使います
(ちなみにalways文は後述のfunction文と同じく,複雑な組み合わせ回路の記述にも使用可能です)
function文(option)
講義ではあえて説明していません.複雑な条件分岐(if文,case文)を含む組み合わせ回路 の記述に使用できますが,一切使わなくても設計できるため,使いたい人だけ使いましょう
組合せ回路では入力の値に応じて随時出力の値も決まるため,代入にはブロッキング代入(=) を使います
代入文の左辺はwire型 ,右辺はreg型,wire型の論理式またはfunction文を使います
initial文
シミュレーションで初期値を与える際にテストベンチファイル内 で使用します(論理合成はされないので,本体のモジュールファイルに書いても意味はありません)
シミュレータ実行時に一度だけ実行される命令として,代入にはブロッキング代入(=) を使います
代入文の左辺はreg型またはwire型 ,右辺は定数を使います
Quartusではinitial文で実機ボードに対しても初期値を与えられますが,ボードの電源を入れた時のみ一度だけ実行されます(実行途中でリセットボタンを押した場合は実行されず初期化されない(要確認))
条件分岐(if文,case文)を使用する際の注意点
必ず全ての条件を網羅する(warningは出るものの一応コンパイルできてしまうため,変な回路が合成されていても気付きにくい)
全ての条件は排反であるようにする(複数のノンブロッキング代入の実行順序 でも説明していますが,同一の出力に対して複数の条件に当てはまると,後の条件の出力が前の条件の出力を上書きしてしまいます)
組み合わせ回路では,全ての入力条件に対し必ず全ての出力の値を指定する (ソフトの時と感覚が違ってミスすることが多い部分です.出力aとbおよび条件XとYがあるとして,条件Xではaの出力の値のみを指定,条件Yではaとbの出力の値を指定した場合,verilogでは条件Xにおけるbの値として,前の時点でのbの出力を参照してしまいます(ラッチになる) .なので,条件Xにおいてもbの値を明示的に指定する必要があります)
verilogにおける二項演算a op bでのビット幅の解釈は,
aとbのビット幅は大きい方に合わせてゼロ拡張する.ただしa,b両方が符号つきであれば,ゼロでなく符号拡張する
代入文では左辺の幅に合わせる
のようになります.変数はデフォルトではunsigned(符号なし)なので符号付き算術演算の場合はsigned(符号付き)として認識させる必要があります.
論理演算ではビット幅を揃える(論理演算ではオペランドのビット幅は揃っているのが普通なので,揃っていない場合は誤記の可能性が高いです)
符号付き算術演算の場合は,c = $signed(a) op $signed(b)と書くと,a,b両方が符号つきと認識され正しく符号拡張されます. (例:$signed(3'b001)+$signed(2'b10)=3'b001+3'b110=3'b111)
定数は以下のように記述します.
<ビット幅>は定数のビット幅を10進数で記述します.省略時は32bitになります.
<基数>は以下の4種類です.省略時は10進の扱いになります.
b , B : 2進(binay)
o , O : 8進(octal)
d , D : 10進(decimal)
h , H : 16進(hexadecimal)
<数値>には,基数に指定した文字と,論理値のx,zを記述することができます.ただし10進記述の場合はx,zを使用できません.
<数値>では区切り文字にアンダースコア '_' を使用することができます.(1111000011110000は1111_0000_1111_0000とも書けるし読みやすい)
(記述例)
8 : 10進 : 32bit : 00000 .... 01000
4'd5 : 10進 : 4bit : 0101
1'b0 : 2進 : 1bit : 0
16'hf0f0: 16進 : 16bit : 1111000011110000
'haa : 16進 : 32bit : 000 ... 10101010
定数を記述する時は,思わぬ誤動作を防ぐためにもなるべくビット幅を指定して記述する ことをお勧めします.
大規模回路を1モジュールで記述すると扱いにくいので,実際の設計では複数の下位モジュールに分割して,上位モジュール内で呼び出すことができます.ただ,HDLにおけるモジュール呼び出しはあくまでも別に記述したmoduleへの配線の接続 であり,ソフトウェアでの呼び出し関数とは少し違うことに注意してください.
モジュール呼び出しの記述方法&呼び出したモジュールへ接続することができる信号の種類は以下の通りです.
入力はreg型,wire型ともに接続することができる.
出力はwire型のみ接続することができる.
モジュール名 インスタンス名 (
.ポート名(信号名),
.ポート名(信号名),
...
.ポート名(信号名)
);
下はモジュール接続の例です.
module TopModule(
input clock,
input reset,
input [1:0] d_in,
output [1:0] d_out
);
...
// ポートの接続
SubModule SubModule1(.clock(clock), .reset(reset), .d_in(d_in), .d_out(d_wire));
SubModule SubModule2(.clock(clock), .reset(reset), .d_in(d_wire), .d_out(d_out));
endmodule
// 下位モジュール
module SubModule(
input clock,
input reset,
input [1:0] d_in,
output [1:0] d_out
);
...
endmodule
TopModuleを論理合成すると,下図のような回路が生成されます.
Verilogでは以下のようにパラメータを設定することができます.parameterを設定することで,モジュールをパラメタイズすることができます.
module モジュール名 #(
parameter パラメータ名 = デフォルト値,
parameter パラメータ名 = デフォルト値,
...
parameter パラメータ名 = デフォルト値
) (
// ポート宣言
);
呼び出した下位モジュールのパラメータを上書きするには,以下のように記述します.
モジュール名 #(
.パラメータ名(値),
.パラメータ名(値),
...
.パラメータ名(値)
) インスタンス名 ( /* ポートの接続 */ );
例を以下に示します.
// 下位モジュール
module SubModule #(
parameter WIDTH = 2,
parameter INIT = 2'b0)
(
input clock, reset,
input [WIDTH - 1: 0] d_in,
output reg [WIdTH - 1: 0] d_out);
...
endmodule
// 上位モジュール
module TopModule();
...
SubModule #(.WIDTH(4), .INIT(4'bF)) SubModule1 (.clock(clock), .reset(reset), .d_in(d_in), .d_out(d_out));
parameterの使い道は主に次の2通りがあります.
定数を,数値で書く代わりにparameterにする
数値の意味がわかりにくいマジックナンバー(e.g. 命令コード)をparameterで書くと可読性が上がります.
ビット幅など[ ]内の数値をparameterにする
ビット幅などはparameterで書くと設計変更や再利用が容易になります.
ただしコードが読みにくくなる弊害もあるので,どの部分を設計変更や再利用の対象にするのかを慎重に考えてから使いましょう.
コンパイル時に出たwarningは,放置せず必ず消すようにしましょう.以下では主なwarningとその対策を記述しています.
Warning (10036): Verilog HDL or VHDL warning at hoge.v(1): object "X" assigned a value but never read
内容: オブジェクトX(reg型or wire型)に値が入力されていますが,Xの出力を使用していません.
対策: 使用するはずのオブジェクトならバスのつなぎ方に問題がないか確認し,不要なら消すようにしましょう.
Warning (10230): Verilog HDL assignment warning at hoge.v(1): truncated value with size 32 to match size of target (16)
内容: 代入文などで,右辺のビット幅と左辺のビット幅が違います.
対策: 意図しない動作を防ぐためにも,算術演算の時以外はビット幅を揃えましょう.特に定数の記述でビット幅を省略すると32ビットになってしまう点に注意(定数の書き方 参照)
Warning (10236): Verilog HDL Implicit Net warning at hoge.v(1): created implicit net for "X"
内容: wire Xを宣言せずに使っています.
対策: wire Xを明示的に宣言しましょう.
Warning (10241): Verilog HDL Function Declaration warning at hoge.v(1): function "F" may return a Don't Care value because its output register may not be assigned a value in every possible path through the function
内容: function文で,全ての入力条件を網羅できていないためにドントケアな出力が存在します.
対策: 必ず全ての入力条件を網羅しましょう.
Warning (10242): Verilog HDL Function Declaration warning at hoge.v(1): variable "X" may have a Don't Care value because it may not be assigned a value in every possible path through the statements preceding its use または
Warning (10776): Verilog HDL warning at hoge.v(1): variable X in static task or function F may have unintended latch behavior
内容: 全ての入力条件に対し全ての出力の値が指定されていないため,ラッチが生成されています.
対策: 全ての入力条件に対し必ず全ての出力の値を指定するようにしましょう.
Warning (10764): Verilog HDL warning at hoge.v(1): converting signed shift amount to unsigned
内容: シフト演算で,シフト量をsigned型(符号付き)で指定しています.
対策: 負のシフト量指定は意図しないものになる可能性があります.unsigned型(符号なし)で指定しましょう.
Warning (12019): Can't analyze file -- hoge.v is missing
内容: プロジェクトにファイルhoge.vが登録されているが,ファイルが存在しません.
対策: hoge.vを作成後,移動させた場合などに発生します.不要ならばプロジェクトから外す,必要ならば対象ファイルをプロジェクトに登録し直しましょう.
Warning (12125): Using design file hoge.v, which is not specified as a design file for the current project, but contains definitions for 1 design units and 1 entities in project
内容: hoge.vを使用しているが,プロジェクトにファイルhoge.vが登録されていません.
対策: 必要ならば対象のファイルをプロジェクトに登録しましょう.
Warning (12241): 1 hierarchies have connectivity warnings - see the Connectivity Checks report folder
内容: 回路内のポートの接続関連でwarningがあります.
対策: コンパイル後,Analysis&Synthesis Compilation ReportのConnectivity Checks report folderを参照し,warning対象のポートを確認して修正しましょう.
Warning (13012): Latch X has unsafe behavior
Warning (13013): Ports D and ENA on the latch are fed by the same signal Y
内容: (おそらく意図しない) ラッチが推定され,データとイネーブルが同じ信号で駆動されているため,不安全な動作となっています.
対策: サブメッセージ(Warning (13013))を参考に,組み合わせ回路でループを作っていないかなど,実際に生成される回路をイメージしてラッチを取り除きましょう.
Warning (13024): Output pins are stuck at VCC or GND
Warning (13410): Pin "A" is stuck at VCC
内容: 出力が1または0に固定されています.
対策: 意図したものなら問題ありません(7segの回路などは,Quartusによる最適化の結果このwarningが出る可能性があります).そうでなければコードを見直して固定されている原因を探しましょう.
Warning (15706): Node “A“ is assigned to location or region, but does not exist in design
Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details
内容: ノードAが入出力ピンとして割り当てられていますが,回路内に存在しません.
対策: 回路の入出力ピンを更新した際,更新する前のピンがPinPlannerで割り当てられたまま残っているために発生します.無効なピン割り当てはPinPlannerから消しましょう.
Warning (332060): Node A was determined to be a clock but was found without an associated clock assignment.
内容: QuartusはノードAをクロックと判定しましたが,クロック割り当てがされていません.
対策: Timing Quest Analyzerを用いて,ノードAがクロックならばクロック割り当てを,そうでなければクロックから除外しましょう.(CADツールを用いた設計フロー(実験3HW編) 参照)
Critical Warning (332168): The following clock transfers have no clock uncertainty assignment. For more accurate results, apply clock uncertainty assignments or use the derive_clock_uncertainty command
Critical Warning (332169): From clock (Rise) to clock (Rise) (setup and hold)
内容: クロックに指定した信号線に対し,合成時に仮定するタイミングの揺らぎを指定していません.
対策: SDC でderive_clock_uncertainty を書くか,各クロック信号の立ち上がり,立ち下がりの組合せに対してset_clock_uncertaintyを書くとよいです.
Critical Warning (127003): Can't find Memory Initialization File or Hexadecimal (Intel-Format) File hoge.mif -- setting all initial values to 0
内容: 指定されたmifファイルが見つかりません.
対策: 指定パスを確認してください.
Critical Warning (127005): Memory depth (n) in the design file differs from memory depth (m) in the Memory Initialization File "hoge.mif" -- setting initial value for remaining addresses to 0
内容: 設計されたメモリとmifファイルに記述されているメモリサイズが異なっています.初期化できていないアドレスには0がセットされます.
対策: mifファイルのメモリサイズを変更してください.
Critical Warning (169085): No exact pin location assignment(s) for m pins of n total pins. For the list of pins please refer to the I/O Assignment Warnings table in the fitter report.
内容: 割当ができていないピンがあります.
対策: PinPlannerで該当のピンを割り当ててください.
Critical Warning (332148): Timing requirements not met
内容: クロックが早すぎてタイミングが間に合っていません.
対策: Timing Quest Analyzerの結果を参考に,周波数を落としてみましょう.
Q&A
動かないです.順序回路を生成したい場合はalways @(posegde clock) またはalways @(posegde clock or negedge reset) (非同期リセット付き)と記述しましょう.上がりと下がりで別の動作をするレジスタを作成したい場合は二つのレジスタを作るようにすれば良いでしょう.
もう一つよくある間違いがalways @(clock) という書き方です.立ち上がり,立ち下がりの両方のイベントで駆動することになるので上記と同じです.組み合わせ回路では全てのイベントで動作するのでこのように書きます.
警告が出ているのでよく見て下さい.
ファイルの最初に `default_nettype none と書いておくとエラーにできます.
(ファイルの最後で `default_nettype wire として戻しておくとより安全.)
その他
a <= b
b <= c
c <= a
a = b
b = c
c = a
上記の二つはすぐに分かると思います.では,次はどうでしょう.
a <= b
b = c
c = a
ノンブロッキング代入はブロック内の式の評価が全て終了後,同時に左辺に反映されます .
なので2行目,3行目と同時に1行目が実行されます.a = b_old, c = a_old, b = c_oldとなります.(a = c_oldではない)
以下に示す合成結果からも正しいことが理解できます.
下は同じ変数outに対するノンブロッキング代入を記述した例です.in1=1の場合は<1>と<2>の両方の条件が当てはまります.
Verilog-HDL の仕様では,ノンブロッキング代入は評価順に キューに追加され,全ての文の評価後に順に 実行されます.
in1=1の場合,1タイムステップ内でout=0,out=1が順に実行され,結果として後のout=1が有効になります.論理合成を行うと,最後の代入のみが有効な回路が生成されます.
module zikken(
input in1,clock,
output reg [3:0] out
always @(posedge clock)
begin
out <= 0;<1>
if(in1 == 1'b1) out <= 1;<2>
end
endmodule