ブログ記事の焼き直しですみませんが、私が Verilog HDL を書き始めて一番ハマった(バグった)部分です。
Veritakのたっくさんに教えて頂いたバグでした。わからなかったので、とっても助かりました。ありがとうございました。
(書き方が全くわからないので、平文で書きます。ご容赦を。。。)
Spartan-3E Starter Kit (http://japan.xilinx.com/products/boards-and-kits/HW-SPAR3E-SK-US-G.htm) のDDR SDRAMのコントローラを作った時の話です。フロムスクラッチで練習のために書いてまして、FIFOも自分で書きました。その時に、Xilinx社のプリミティブの中で、Dual Port RAMライブラリのRAM16XDをメモリとして使用しています。Write Pointer(wp) と Read Pointer(rp) を定義し、wp と rp のポインタを使ったリングバッファとして、FIFOを構成していました。
なお、wp, rp 共に4ビットとして定義していました。FIFOのFULLを判定する式はrp-1 == wp となるのはお分かりかと思います。それで、FIFOのFULLを判定する式を下に示すように書いていました。
assign fifo_full = (rp-1==wp) ? 1'b1: 1'b0;
これでバグりました。fifo_full が 1 になりませんでした。判定条件が真になりませんでした。
この記述がなぜダメかというと、rp-1 の -1 はビット幅を指定していないので、32ビット幅に拡張されてしまうそうです。つまり符号が拡張されるので、32'hFFFF_FFFFになります。左辺は当然32ビット符号拡張されますが、== の比較のときに右辺も32ビットに拡張されてしまいます。
これでも通常の場合はOKなのですが、rp が 0、wp が F の時(ポインタは4ビットに定義してあるので)当然ながらFIFOはFULLなのですが、左辺は32'hFFFF_FFFF、右辺は32'h0000_000Fとなってイコールになりませんでした。当然式は真にならず、fifo_full が 1 になることはありませんでした。
これを避けるためには rp-1 の -1 のビット幅を制限する必要があるそうです。ここではポインタは4ビットなので、4ビットに制限します。つまり 1 は 4'b0001 か 4'h1 と書く必要があります。
よって、下のように、上式を書き換えると、fifo_full が正常に 1 になるようになりました。
assign fifo_full = (rp-4'h1==wp) ? 1'b1: 1'b0;
これは、皆さんには常識かもしれませんが、知らなかった人のために書いておきます。コレにハマるとしばらく原因がわかりません。。。