« HPC分野におけるFPGAの考察 | トップページ | 組み込み屋の為のVerilog入門 その2 »

2011年12月 1日 (木)

組み込み屋の為のVerilog入門

 従来、マンコンを載せたボードを作って云々といった、いわゆる組み込み屋にとって、FPGAがマイコンの領域をどんどん置き換えてきているのは、肌身に感じている人は多いのではないだろうか?
  今後まだまだこの流れが続くであろうし、その根拠となるトレンドもいろいろとデータ収集中である。

 ちなみに私もその一人であって、バリバリとFPGAを使っていて、Verilogを好んで使っていたりするわけだが、当然LSI開発用に開発されたHDL言語に対して、普段C言語で開発していた人間が参入してもいろいろと文化の違いに振り回される。
 が、あえて言うなら、LSI屋の常識に振り回されずに、組み込み屋の常識をもっともっとこの分野に投入して、新しい進化を起こすべきである。

  ってことで、組み込み屋的Verilog入門を考えてみる次第である。

  初心者が最初に嵌るのが多分、ノンブロッキング代入とブロッキング代入である。最近大変ためになるページを見つけたので、勝手ながらリンクを張らせてもらっておく。
http://veri.jp/ba_nba.html

  持論を述べるなら、CもVerilogも低級言語ながらどちらもチューリング完全なプログラミング言語であって本質は同じである。特に書き換えのできてしまうFPGAなんて完全にソフト屋のおもちゃである。

  C言語からの転向組みにまずオススメしたいのが  Verilog 2001 を覚えること
(SystemVerilog がよさげだが、FPGA合成ツールで対応してるのは少ない)。

  はっきり言って、K&R のCと ANSIのCぐらい違う。いまどきのFPGAでVerilog 2001 未対応なんてないので、元祖 Verilog HDL の記述なんてさっさと無視してしまおう。

  次にオススメなのが、ソースの先頭に

   `default_nettype none

  と書き、ソースの終わりに

 `default_nettype wire

  と書こう。
  C言語と違い、Verilog は、宣言の無い変数を自動で wire 認識してしまう。スペルミスなどのしょうもないミスはコンパイル時に拾うべし。
# まあC言語も勝手に int 推定しちゃうけど。
  ソースの最後で wire に戻すのも重要だ。C言語に慣れているとソースはファイル単位でそれぞれ完結しているイメージがあるが、Verilogは一緒にコンパイルする後続ファイルに 'define などありとあらゆる属性が継承されるのだ。自分のソースだけなら良いのだが、通常はベンダー固有のライブラリやら、他人のコードやらが混ざるので、戻し忘れるとタコなコードのエラーで止まってしまう。

  また、組み込み屋にオススメしたいのが always @* な構文である。ブロッキング代入の罠に嵌らないためのコーディング手法としてオススメである。
  組み込み屋なら誰しも、GPIOのソフトウェア制御で、SPIとかI2Cとか簡単なハードウェアを操作するぐらいの事はしたことがあると思う。またタイマ割り込みでいろいろと処理を進めるのも組み込み屋ならお手の物かと思う。我々には既にRTL制御の基本前提知識が備わっているのだ。
  always @(posedge clk) な文は、組み込み屋にとって理解が簡単だ。例えば100MHzで動くなら、10ns 周期のタイマ割込みハンドラと思って書けばいいのだ。
  が、ここでソフトっぽい書き方を邪魔するのがノンブロッキング代入だ。多くの場所で always @(posedge clk) の中では ノンブロッキング代入 以外使うなと論じている。

  理由は簡単で、ブロッキング代入を使うと、多分合成は意図どおり行われるが、シミュレーションが合成どおりに動かないからだ(詳細はめんどうだから割愛)。
  はっきり行ってしまえば、言語仕様の抱える問題だ。こういったトラブルの入る余地の無い言語なんて新規に作ればいくらでもできそうだが、互換性や伝統が馬鹿にできないのも良く分かるし、C言語だって似たような課題はいっぱい抱えている。

  そこで、always @* がオススメである。

  まず、reg となる変数をすべてセットで2個ずつ宣言する。1つはFFに割り当てられるもので、もうひとつは実体はwireなのだが言語仕様上、なぜか reg 宣言を強要される計算用変数だ(もっともそのおかげで1行でまとめて書けるが)。
  用途としては、クロックの立ち上がりにおいて、立ち上がる前の値の保持と、新しい値の保持の2つで使い分ける。
  こんな感じだ。

reg  [15:0]   reg_counter, next_counter;
reg            reg_puls, next_puls;
always @(posedge clk) begin
  reg_counter <= next_counter;
  reg_puls <= next_puls;
end

alwasy @* begin
  // 必ず初期化(ここ重要)
  next_counter = reg_counter;
  next_puls      = 1'b0;

  // 後はタイマ割り込みのノリですき放題書く 
  next_counter = next_counter + 1;
  if ( next_counter > 10000 ) begin
    next_puls = 1'b1;
     next_counter = 0;
  end
end

  こんな感じである。reg_xxx に古い値、next_xxx に新しい値を計算することで、クロックの立ち上がりという刹那の記述で前後の矛盾が取り払われる。
  next_xxx な変数は reg 宣言だが、wire 的な割り当てになるのでFFは消費しない(初期化を忘れるとラッチができてしまうので注意)。
  また、next_xxx は always @* の中でのみ使って、他で参照してはいけない。他に見せるのは reg_xxx の方でけである。
(このようによけいな注意事項が発生するので、このへんをケアした言語を開発するのがもっともトラブルが無いように思う。)

  次に覚えておきたいのは generate 文である。これは parameter などによって合成内容を細かくコントロールできる非常に強力な構文である。ここはC言語にも見習って欲しい。
  C言語の最大の欠点はマクロ(プリプロセッサ)の弱さである。for文など使えず #if 系しかない。プリプロセッサ構文はチューリング完全ではないのだ(その点はLISPが面白そうだがまた別の機会に)。C++のテンプレートで少しマシになったがそれでもまだまだである。
  ハードウェア記述言語という名前から固そうに見せて(実行時のやわらかさは実際無いのだが)、コンパイル時の静的構文はとても柔らかいのである。
  あと、バスの範囲を指定する [10 +: 3]  (10bit目から3bit分) とかの表記もソフト屋好みだ。というかポートとかに配列使えないので(SystemVerilogはポートでも多次元のパックド配列使えるみたいだし、構造体もある模様)、柔軟なI/Fを定義するとこの手のアクセスは generate な for 文とあわせて非常に強力だ。

  また、加えてオススメなのが、レジスタリタイミングの活用である。有無を言わさずFFを打ち込んでおけば、よきに割り振ってくれる。
  深いパイプラインを可読性良く書くのに非常に有意義である。まだまだ合成器は賢くないのでアレだが、今後に期待できる機能である。

  他にもいろいろ書きたいことがあった気はするが、今日はこれくらいで。

« HPC分野におけるFPGAの考察 | トップページ | 組み込み屋の為のVerilog入門 その2 »

FPGA」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/560384/53382220

この記事へのトラックバック一覧です: 組み込み屋の為のVerilog入門:

« HPC分野におけるFPGAの考察 | トップページ | 組み込み屋の為のVerilog入門 その2 »