目次
ちょっとFPGAやってみない?
という安易な誘いに。。
安易な気持ちで、コピペでできるかな?って思ったけど。。基礎の基礎がわかっていないので、右往左往しています。
Quartusで開発環境作って。。
ModelSimの最初のチュートリアルやって。。
演習問題の2の乗算器で。。なるほど。。いろんなRTL記述や実装方法があることがわかり。。少し勉強がてら。。まとめておこうかと思います。
知らないことばかりなので、もし間違いあったら、指摘ください。バカ丸出しのメモです。。
乗算器の記述方法
乗算器っていろいろな書き方あるんですね。
RTL記述
http://zakii.la.coocan.jp/hdl/45_mult_rtl.htm
シフト演算での記述
http://signal-process-logic.com/jp/code_book/codes/shift_mul/shift_mul.shtml
乗算器の仕組み
http://kivantium.hateblo.jp/entry/2016/12/09/000000
4ビット乗算器 DE0
乗算器のIntel(Altera)の推奨記述
どうやら。。いろんな記述方法があるようで、2進数の乗除はシフトと加算でできるので。。って思っていたら。。DSPを使ったりIPを使ったりするのが、今の基本のようですし、基本の基本がわかっていないことが分かりました(笑)
乗算自体は小難しいシフト演算と加算器の組み合わせで作らなくても。。RTL記述でかけちゃうってことも。。一応 理解(笑)
HDL・Verilog HDLの基本の基本
HDLのてにをは集
ここがよく基礎の基礎まとまっています。
http://www.lab3.kuis.kyoto-u.ac.jp/~takase/le3a/hdl/index.html
- Verilog-HDL の文法
http://cas.eedept.kobe-u.ac.jp/~arai/Verilog/chap5.html
HDLによるFPGA設計
http://zakii.la.coocan.jp/hdl/0_contents.htm
このあたりのことをまず勉強しないとな。。。っと(笑)
先人の知恵満載ですね。 このようなマトメありがたいです。
演習2の課題
always文
- あるタイミングで処理を起こしたい場合
- ループと同じ
assign文・always文とwire・regでの変数
wireで定義する変数 ネット型変数の信号への代入は,assign 文でのみ可能。
regで定義する変数 レジスタ変数の信号への代入は,always 文,initial 文,task,function の中で可能。
つまり,reg =順序回路という訳ではなく,wire や reg は上記の文法的な理由で使い分ける,と理解しておいて間違いなさそうです。いずれの変数も,式の右辺や引数としての利用(値を参照する場合)は可能。出典1
らしい。 ブロッキング文(=で代入)・ノンブロッキング文(<=で代入) の変数などについても理解が必要のようです。
Verilog HDLでは、変数は、線のつながりか、レジスターに入れた数値かってことなんですね。このあたりの使い分けは、こちら。。
always @(posegde clock) ってあるような記述は、クロック信号のポジティブエッジがきたらいつもってことみたい。。
always @(イベント式) 遅延時間 ステートメント あるいは always #定数値 ステートメント
イベント式には以下の 3 種類があります:
・ 信号名 …信号が立ち上がるときと,立ち下がるとき
・ posedge 信号名 …信号が立ち上がるとき(positive edge の略)
・ negedge 信号名 …信号が立ち下がるとき(negative edge の略)
らしい。出典1
ブロッキング文・ノンブロッキング文っていうのは、普通の変数だとブロッキング文のようにクロックの動作に関係なく保持されるようで、ノンブロッキング文は、クロックの動作にそって動く感じです。
演習問題2の課題とテストベンチ
always文を使う。 入力は a,b で4ビット幅 出力はproductで8ビット幅ってことが書かれています。
これのシミュレーションに使うテストベンチの記述を見て、まずこのテストベンチを勉強してみます。
`timescale 1 ps/ 1 ps module mult4x4_sim(); // constants // general purpose registers reg [3:0] a; reg [3:0] b; // wires wire [7:0] product; // assign statements (if any) mult4x4 u1 ( // port map - connection between master ports and signals/registers .a(a), .b(b), .product(product) ); parameter CYCLE=20000; initial begin a = 4'b0000; end always # (CYCLE) a <= a + 1; initial begin b = 4'b1111; end always # (CYCLE) b <= b - 1; endmodule
timescale
シミュレータの時刻変数。Verilog記述上は時間経過を待つ文 #(時間)で使われる。この時刻変数(正の整数)と実際の時間(実数s/ms/us/ns/ps/fs)の対応づけをテストベンチの先頭(moduleの前)で指定する。
`timescale 1ns/1ps // 単位/精度
このとき #(10) 文; は10nsたったら文を実行 #(10/3) 文;は3.333nsたったら文を実行 という意味になる |
`timescale 1 ps/ 1 ps
なので、今回は単位が1psで精度も1psってこと
変数 reg と wire
module mult4x4_sim();
で変数が定義されています。
reg [3:0] が入力変数 a,bでwire [7:0]がproductになっていますね。
[3:0]っていうのは、4ビット幅 [7:0]は8ビット幅を表すみたいです。
下層モジュールとの接続
// assign statements (if any)
ってところは、mult4x4_simとmult4x4の接続を示しているようです。
mult4x4 u1 (.a(a), .b(b), .product(product) );
下層モジュール名(mult4x4) インスタンス名(u1) パラメータリストで、()の中が、
.定義側ポート名(接続信号)
という記述のようです。 同じ名前使われているので、わかりやすいのか?わかりにくいのか?ですが。。
parameter
regやwireは回路と対応したデータなので、物理型データと呼ぶらしい。それに対して、parameterは抽象型データだそうな。。普通のプログラミングでいうと、Defineのような感じらしい。
parameter パラメータ名 = 定数;
定数を定義します。C言語でいうと #define と同じであり,宣言したモジュール以外のモジュールでも使用可能です。
- parameterの使い道は主に次の2通りがあります。
-
- 定数を、数値で書く代わりにparameterにする
- ビット幅など[ ]内の数値をparameterにする
- ビット幅などはparameterで書くと設計変更や再利用が容易になります。
-
- 数値の意味がわかりにくいマジックナンバー(e.g. 命令コード)をparameterで書くと可読性が上
- がります。
-
下で
always # (CYCLE)
と使っているので、20000ps (20ns) ごとに。。っていうことですね。
initial
initial begin a = 4'b0000; end always # (CYCLE) a <= a + 1;
initial文
- シミュレーションで初期値を与える際にテストベンチファイル内で使用します(論理合成はされないので、本体のモジュールファイルに書いても意味はありません)
- シミュレータ実行時に一度だけ実行される命令として、代入にはブロッキング代入(=)を使います
- 代入文の左辺はreg型またはwire型、右辺は定数を使います
- Quartusではinitial文で実機ボードに対しても初期値を与えられますが、ボードの電源を入れた時のみ一度だけ実行されます(実行途中でリセットボタンを押した場合は実行されず初期化されない(要確認))出典2
4ビットで0をaにブロッキングで代入して、20000psごとに、1つインクリメントするのをノンブロッキングで代入するってことですね。
bの変数は、最初に1111(十進数で15)を最初にセットされるってことであとは同じ。
演習2をもう一度みると。。
http://zakii.la.coocan.jp/hdl/45_mult_rtl.htm
のRTL記述で書いてみると。。
reg[3:0] a; reg[3:0] b; wire[7:0] product; always @(posedge a) begin a <= a; b <= b; product <= $signed(a) * $signed(b); end
って感じですかね?
Tutorialの演習2に戻りましょう(笑) Verilog-HDL_Trial_Lab_r1_download__2.zip
モジュールにして。。戻り値を入れて。。という感じで作ると。。Github
module mult4x4(a,b,product); input[3:0] a; input[3:0] b; output[7:0] product; reg[3:0] a_reg; reg[3:0] b_reg; reg[7:0] product_reg; always @(posedge a or negedge a) begin a_reg <= a; b_reg <= b; product_reg <= $signed(a_reg) * $signed(b_reg); end assign product = product_reg; endmodule
一回全部 レジスタ変数として定義して、ノンブロッキングとして代入して、最後にassignで値を返す。。。と。。
一応シミュレーションできて、moduleの中も、テストベンチ側にも信号がでて。。できるんだけど。。
1クロックというか、a のエッジ毎に計算するので、1タイミング遅れて。。計算結果がproductに反映される。。クロックを信号に入れてあると。。できるんだけどなぁ~~。。
同時にやる方法って。。どうするの?(笑) 誰かこれの回答教えて(笑)
わからないので。。おいておいて。。演習3に行くか(笑)
って。。演習3をやろうとしたら。。分かった(笑)
always @(posedge a or negedge a)
を
always @(*)
ってすると、できた(笑) ここに。。MultiplexerのModuleの書き方に。。どんな入力の変化にもする表現として。。ありました。
https://github.com/Obijuan/open-fpga-verilog-tutorial/wiki/Chapter-11:-2-to-1-multiplexor
なるほど。。
では。。続いて。。
通りすがりのLSI設計屋です。
組み合わせ回路を作るのにposedge,negedgeは使いません。
基本的にクロックに同期させる時と非同期リセットをかけるときです。
あとはテストベンチでなにかのエッジで次に行くときくらいです。
で、今回は組み合わせ回路の乗算器なので、センシビリティリストは @(a or b) で、
aもしくはbが変化したら動作ということでよいと思います。
また代入はブロッキング代入です。
module mult4x4(a,b,product);
input[3:0] a;
input[3:0] b;
output[7:0] product;
reg[7:0] product_reg;
always @(a or b)
begin
product_reg = $signed(a) * $signed(b);
end
assign product = product_reg;
endmodule
ちなみに、お仕事で書くときは always使わず
assign product = $signed(a) * $signed(b);
この1行です。
ありがとうございます。
全くの素人なので、よくわかっていません(笑)
また、通りすがったら、教えてください