開発日記

Erlangをダラダラ書きます。

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_STYLEを使おう

  • 同期書き込み,同期読み込み → ブロックRAM

  • 同期書き込み,非同期読み込み → 分散RAM

  • 同期読み込みのみ → ROM

  • ブロックRAMはポート2つまで.

  • 分散RAMはポート何個でも.

  • ブロックRAMは容量大きめ.

  • 分散RAMは容量少なめ.