FC2カウンター FPGAの部屋 VerilogでXSTにBlock RAMを推論させる
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

VerilogでXSTにBlock RAMを推論させる

以前にVHDLでBlock RAMを推論させて、外部ファイルの初期化データをロードする方法をやってみたが(”VHDLでのブロックRAMや分散RAMの初期化(16進数で書かれた外部データファイル)”)、今回はVerilogで同様のことをやってみた。

XSTユーザーガイド(Virtex-4、Virtex-5、Spartan-3 および CPLD デバイス用) UG627 (v 12.4) 2010年 12月14日の254ページのv_rams_20cを参考に、バス幅などを変更した。使用したISEは13.1。FPGAはVirtex-5 LX110T-1。ソースコードを下に示す。

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, we,  addr,  din,  dout);
    input  clk;
    input  we;
    input  [4:0]    addr;
    input  [15:0]    din;
    output  [15:0]    dout;
    reg  [15:0]  ram  [0:31];
    reg  [15:0]  dout;
    
    initial begin
        $readmemh("rams_20c.data",ram,  0,  31);
    end
    
    always  @(posedge  clk) begin
        if  (we)
            ram[addr]  <=  din;
            
        dout  <=  ram[addr];
    end
endmodule


rams_20c.dataを下に示す。

AABB
CCDD
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000


これでインプリメントして、FPGA Editorで見てみた。結果は、Block RAMがインスタンスされずに、SLICEMで実装された。
Verilog_BRAM_1_110621.png

これで、困っていたところ、ツイッターで@osamu_takeuchiさんに(* RAM_STYLE="BLOCK" *)を教えていただいた。

reg [15:0] ram [0:31];



(* RAM_STYLE="BLOCK" *) reg [15:0] ram [0:31];


に変更して、もう一度インプリメントしてみた。FPGA Editorで見たところ、Block RAMがインスタンスされていた。
Verilog_BRAM_2_110621.png

初期値も代入されていた。
Verilog_BRAM_3_110621.png

VHDLでは、デフォルトでBlock RAMが推定されたが、Verilogでは、(* RAM_STYLE="BLOCK" *)制約を追加する必要があった。

ちなみに、下のようにバイトイネーブルをつけたところ、(これが本命だったのですが。。。)

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, we,  addr,  din,  dout);
    input  clk;
    input  [1:0]    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]  ram  [0:31];
    reg  [15:0]  dout;
    
    initial begin
        $readmemh("rams_20c.data",ram,  0,  31);
    end
    
    always  @(posedge  clk) begin
        dout  <=  ram[addr];
            
         if  (we[0])
             ram[addr][7:0]  <=  din[7:0];
            
         if  (we[1])
             ram[addr][15:8]  <=  din[15:8];
    end
endmodule


SLICELなどのロジックに推論されてしまった。
Verilog_BRAM_4_110621.png

(2011/06/22:追記)
ツイッターで@osamu_takeuchiさんに教えていただいたが、Project Navigatorのテンプレートにバイト・イネーブルを使用したRAMの例が載っているそうだ。たびたびありがとうございます。
EditメニューのLanguage Templates...を選ぶと出てくるLanguage TemplatesのVerilog -> Synthesis Constructs -> Coding Examples -> RAM -> Block RAM -> Single Port -> Byte-wide Write Enable -> No Change Mode w/ 2bit write enable(for device familes older than Virtex-6 and Spartan-6)を参考にして、v_rams_20c.v を書き換えてみた。
Verilog_BRAM_5_110622.png

v_rams_20c.v のVerilogソースを下に示す。

//
//  Initializing  Block  RAM  from  external  data  file
//

module  v_rams_20c  (clk, 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;

   //  The following code is only necessary if you wish to initialize the RAM 
   //  contents via an external file (use $readmemb for binary data)
   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 clk)
      if (ram_ena) begin
         dout  <= {di1,di0};
         ram[addr] <= {di1,di0};
      end
endmodule


インプリメントしたところ、Block RAMに割り当てられていた。
Verilog_BRAM_6_110622.png

Block RAMを拡大してみたところ、WEAL0~3にwe_0_IBUF, we_1_IBUFが割り当てられていた。INT_01_Lには初期化データが入っていることが確認できた。
Verilog_BRAM_7_110622.png

(もう一度追記:重要)
バイト・イネーブル付きRAMですが、Spartan-3はBlock RAMを使用しないでSLICEMで実装されました。下にBlock RAMで実装されたか、SLICEMで実装されたか?を表にします。

Spartan-3 SLICEM
Spartan-3E SLICEM
Spartan-3A Block RAM
Virtex-4 Block RAM
Virtex-5 Block RAM
Spartan-6 Block RAM
Virtex-6 Block RAM


確か、Spartan-3A辺りからBlock RAMにバイト・イネーブルが付いたんじゃなかったかな?
  1. 2011年06月21日 15:34 |
  2. 入門Verilog
  3. | トラックバック:0
  4. | コメント:2

コメント

いつも2個の8ビット入出力ブロックRAMを使ってやっていたので、こんなことができるとは知りませんでした。いつもありがとうございます。
  1. 2011/06/22(水) 20:18:38 |
  2. URL |
  3. Sim #mQop/nM.
  4. [ 編集 ]

私も知りませんでした。武内さんに感謝です。Project Navigatorのテンプレートは有用です。
(* RAM_STYLE="BLOCK" *) を抜かしてしまうと分散RAMになってしまいますので、ご注意ください。
  1. 2011/06/23(木) 04:29:49 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/1832-bb533868
この記事にトラックバックする(FC2ブログユーザー)