RAMのRTL記述
FPGAにはデュアルポートで使えるブロックRAMが備わっている。ブロックRAMを使うにはIP生成ツールで生成したものをインスタンシエートするのが一般的だが、ビット幅や出力レジスタ有無が見えないので可読性が悪いし、ツールバージョンやターゲットデバイスが変わるたびに生成し直さなければならず移植性もよくない。そこで、RTL記述で書いた方が可読性や移植性がよくなる。ただしIP生成するよりは贅沢にブロックRAMを使ってしまう(例えば9ビット幅のブロックRAMを8ビット幅として使ってしまう)のが欠点。
◇
例1) シングルポートRAM
○ /* シングルポートRAM - ライト前の値がリードされる */ reg[WD-1:0] ram[0:2**WA-1]; always @(posedge clk) begin if(we) ram[adr] <= din; dout <= ram[adr]; end |
○ /* シングルポートRAM - ライト後の値がリードされる */ reg[WD-1:0] ram[0:2**WA-1]; always @(posedge clk) begin if(we) ram[adr] <= din; adr_reg <= adr; end assign dout = ram[adr_reg]; |
次のブロッキング代入をつかった書き方もあるが、ノンブロッキング代入に統一した方が紛らわしくなくてよい。
× /* シングルポートRAM - ライト後の値がリードされる */ reg[WD-1:0] ram[0:2**WA-1]; always @(posedge clk) begin if(we) ram[adr] = din; dout = ram[adr]; end |
FPGAではブロックRAMの他にLUT用RAMを転用したDistributed RAMが備わっている。どのタイプのRAMを使うかは(たぶんRAMサイズによって)自動的に選択されるが、特に指定したい場合は合成制約で指定する。
// Vivadoの場合。value={block|distributed} (* ram_style="value" *) reg[WD-1:0] ram[0:2**WA-1]; // Quartusの場合。value={M20K|M9K|M144K|MLAB|logic} (* syn_ramstyle="value" *) reg[WD-1:0] ram[0:2**WA-1]; |
◇◇
例2) デュアルポートRAM
/* デュアルポートRAM - 同一ポート内ではライト前の値がリードされる */ reg[WD-1:0] ram[0:2**WA-1]; always @(posedge a_clk) begin if(a_we) ram[a_adr] <= a_din; a_dout <= ram[a_adr]; end always @(posedge b_clk) begin if(b_we) ram[b_adr] <= b_din; b_dout <= ram[b_adr]; end |