FPGA RAM・ROM
RAM
- 配列になっていてビットの範囲を指定して読み出されるものをRAMで作ると資源を節約できる.
例:Instruction memory, Data memory, Register file
- RAM を構成するリソースとして何を割り当てるかを、 RAM_STYLE という合成属性を使用して制御可能. (明示的に BRAM を使用するように指定すると何らかの理由で BRAM が推定されなかった場合に Warning が発生する.予定通りの合成結果でなかったことがすぐに分かる.)
warning例:[Synth .....] Infeasible attribute ram_style = “block” set for RAM “memory_array_name”,trying to implement using LUTRAM
- RAMの初期化は$readmemh
Distributed RAM (分散RAM)
LUTを用いて構成
Verilogでは,「書き込みのタイミング」をクロックと同期すれば分散RAMとして扱われる.
非同期読み出しが可能
ポートはいくらでも増やせる.
データ幅の異なるデュアルポートは不可
容量は少ない
例:同期書き込み,非同期読み出し
module ram(clk, we, r_addr, r_data, w_addr, w_data); input clk, we; input [4:0] r_addr, w_addr; input [31:0] w_data; output [31:0] r_data; reg [31:0] mem [0:31]; // 32bitのレジスタが32個(アドレスは5bit) always @(posedge clk) begin if(we) mem[w_addr] <= w_data; // クロックと同期して書き込まれる end assign r_data = mem[r_addr]; endmodule
Block RAM(BRAM)
あらかじめFPGA上に用意されている資源
Verilogでは,「書き込みのタイミング」と「読み出しのタイミング」をクロックと同期する.
非同期読み出しは不可
ポートは2つまで.
データ幅の異なるデュアルポート可能
ECC機能(訂正機能)の割り当ても可能
同一のアドレスに対して書き込みと読み出しが同時に発生した場合に,読み出しデータには何が出力されるのかを,書き込みモードによって選択可能.らしい..
例:同期書き込み,同期読み出し
module bram (clock, we, addr, din, dout); input clock; input we; input [4:0] addr; input [15:0] din; output [15:0] dout; (* RAM_STYLE="BLOCK" *) reg [15:0] ram [0:31]; reg [15:0] dout; initial begin $readmemh("bram.data", ram, 0, 31); end always @(posedge clock) begin if (we) ram[addr] <= din; dout <= ram[addr]; end endmodule
例:バイトイネーブル付き
module bram (clock, we, addr, din, dout); parameter DATA_WIDTH = 16; parameter ADDR_WIDTH = 5; input wire [DATA_WIDTH-1:0] din; input wire [ADDR_WIDTH-1:0] addr; input wire [1:0] we; input wire clk; output reg [DATA_WIDTH-1:0] dout; wire ram_ena; assign ram_ena = 1'b1; (* RAM_STYLE="BLOCK" *) reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0]; reg [(DATA_WIDTH/2)-1:0] di0, di1; initial $readmemh("rams_20c.data", ram, 0, 31); always @(we, din) begin if (we[0]) di0 = din[(DATA_WIDTH/2)-1:0]; else di0 = ram[addr][(DATA_WIDTH/2)-1:0]; if (we[1]) di1 = din[DATA_WIDTH-1:DATA_WIDTH/2]; else di1 = ram[addr][DATA_WIDTH-1:DATA_WIDTH/2]; end always @(posedge clock) if (ram_ena) begin dout <= {di1,di0}; ram[addr] <= {di1,di0}; end endmodule
ROM
書き込み信号がないSRAMをROMのように動作
読み出しのタイミングを同期しブロックRAMで構成→ROMとして利用.
例
module rom(clk, r_addr, r_data); input clk; input [4:0] r_addr; output reg [31:0] r_data; reg [31:0] rom [0:31] always @(posedge clk) begin r_data <= rom[r_addr]; // 読み出しのタイミングを同期 end endmodule
その他参考文献
- 分散RAMとBRAMの違いについて
https://www.cqpub.co.jp/dwm/contents/0126/dwm012601570.pdf
- メモリ,エンディアン,分散RAMとBRAMの違いなど
結論
メモリを作るときはRAM_STYLEを使おう
同期書き込み,同期読み込み → ブロックRAM
同期書き込み,非同期読み込み → 分散RAM
同期読み込みのみ → ROM
ブロックRAMはポート2つまで.
分散RAMはポート何個でも.
ブロックRAMは容量大きめ.
分散RAMは容量少なめ.