XC6SLX9にはブロックRAMが18kb×32個あります.これらをHDLで使用するときにはISEが持つCORE Generatorを使うと便利です.この文書では,i4002を念頭においたRAMをISE CORE Generatorで作成する方法を説明しています.
i4002の仕様
1個のi4002には4ビット幅のメモリが80個ありますので合計で320ビットの記憶容量があります.このため,作成するBlockRAMは,アドレス幅が7(80<128=27),データ幅が4となります.また,i4002にはリセット端子が備わっているのでこれもBlockRAMの機能に盛り込みます.このようなBlockRAMをCORE Generatorで作成します.
CORE Generator
ISEの一機能であるCORE Generatorを使ってBlockRAMを作成します.そのためにまずは新たなプロジェクトを作成します.i4002の作成を目指すため,プロジェクト名を「i4002」とします.下のようにISEのメニューにあるFileからNew Project...を選択してください.

次にプロジェクト名と保存先を指定します.

ターゲットデバイスとしてXC6SLX9TQG144-3,使用する言語をVerilogとなっていることを確認してください.

プロジェクトを作成する準備が整いましたらFinishボタンを押してください.

次にBlockRAMを作成するために新しいソースを作成していきます.下図のようにProjectメニューの中のNew Souce...を選択してください.

新しいソースとしてIPを選択します.IPとはIntellectual Propertyのことで,FPGAやASICに含まれる部分的な回路情報のことを指します.単にIP以外に,IPコアという言い方をする場合もあります.下のようにIPを選択し,ファイル名をBlockRAMFori4002とします.

次に,数あるIPの中からBlock Memory Generatorを選択します.Memories & Storage Elementsの中のRAMs & ROMsの中にあるものを選んでください.

新たなソースが完成しました.引き続き,BlockRAMの仕様を設定していきます.

Block Memory Generatorではメモリの種類,バス幅,アドレス幅,リセットの有無などの設定を行えます.まずはInterface TypeをデフォルトのNativeにして次に進みます.

次にメモリの種類としてSingle Port RAMを選択し,合わせてRAMのアルゴリズムを最小エリアになるようにします.

次にデータの幅とアドレスの幅を指定します.以前書いたように,今回はデータ幅4,個数を80としますので下図のようになります.

次のダイアログでは特に行うことはありませんので次へ進んでください.

次にリセット端子に関する設定を行えるダイアログになります.しかし,ここでいうリセットとは,BlockRAMから出力されるデータが0になるというだけでメモリが本当にリセットされるわけではありません.従いましてここでは下図のようになにもチェックマークをつけずに次へ進みます.

最後に,FPGAに備わるBlockRAMの使用状況を確認します.XC6SLX9には18kbのBlockRAMが32個あり,1個のBlockRAMは9kbのBlockRAMが2個あります.今回作成したi4002のためのBlockRAMはたったの320bですので,9kb(=9000b)に余裕で収まります.従いまして,下図のようにBlockRAM resouces(s)(9K BRAMs)が1となっていることが確認できるかと思います.以上でBlockRAMの作成は終了です.

IPの使用方法
作成されたIP(BlockRAM)をVerilogから呼び出す方法について説明します.まずは下図のようにBlockRAMFori4002というIPがISE上で認識されていることを確認したうえで,ファイルを開くためのアイコンを押してください.

次に,プロジェクト内に作成されているはずのipcore_dirフォルダを開き,その中にあるBlockRamFori4002.vを選択します.このように,作成されたIPはipcore_dirフォルダにでき,加えてインタフェースとして.vファイルが同フォルダにあります.

.vファイルを開きますと,下図のようにBlockRamFori4002のモジュールが現れます.あとはこのモジュールにあるポート宣言にもとづき,階層設計をしていけばよいわけです.

BlockRAMのインタフェース
作成されたBlockRAMには下表の端子が備わっています.特徴的なのはデータバスの取り扱いです.SRAMのICの場合,データバスの端子はinoutになっており,入力と出力を共用にしてあります.しかしこのBlockRAMでは入力端子と出力端子を別に持っています.
| 信号名 | 幅 | 通称 | 用途 |
| clka | 1 | クロック | 立ち上がりエッジで動作する. |
| wea | 1 | ライトイネーブル | Lowのときには読み込み,Highのときには書き込む |
| addra | 7 | アドレス | BlockRAMのアドレス |
| dina | 4 | データイン | BlockRAMにデータが入力するときに使う |
| douta | 4 | データアウト | BlockRAMからデータを出力するときに使う |
動作の確認
作成したBlockRAMの動作を確認するためにテストベンチを作成するとともに,作成したIPを使用する方法を示すため,トップモジュールを作成します.まずはトップモジュールです.下図のようにISEのProjectメニューにあるNew Source...を選択します.

次にトップモジュールTopModuleを追加します.今回作成するトップモジュールは,先ほど作成したBlockRAMの動作を確認するためだけのものです.i4002の中に含まれるRAMのようにするには別のトップモジュールを作成してください.

ポート宣言は下記のとおりです.名称は異なるもののBlockRAMと同じになっています.

最後に確認するためのダイアログが現れます.

作成されたモジュールにBlockRamFori4002を接続するため,下記のように追記します.以上でトップモジュールができました.

次にテストベンチを作成します.今回は次のような動作をするテストベンチにします.
- 番地1に数値3を記憶する
- 番地5に数値2を記憶する
- 番地0に数値10を記憶する
- 番地1の値をoDoutから出力する
- 番地0の値をoDoutから出力する
- 番地5の値をoDoutから出力する
以上の条件に合致するテストベンチ(t_TopModule.v)を追加・作成します.
module t_TopModule;
// Inputs
reg iClk;
reg iWe;
reg [6:0] iAddr;
reg [3:0] iDin;
// Outputs
wire [3:0] oDout;
// Instantiate the Unit Under Test (UUT)
TopModule uut (
.iClk(iClk),
.iWe(iWe),
.iAddr(iAddr),
.iDin(iDin),
.oDout(oDout)
);
initial begin
// Initialize Inputs
iClk = 0;
iWe = 0;
iAddr = 0;
iDin = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
iWe = 1;
iDin = 3;
#5
iAddr = 1;
#25
iDin = 2;
#5
iAddr = 5;
#25
iDin = 10;
#5
iAddr = 0;
#50
iWe = 0;
iAddr = 1;
#30
iAddr = 0;
#30
iAddr = 5;
end
initial begin
forever
#10 iClk = ~iClk;
end
endmodule
テストベンチができましたらISimによるシミュレーションをしましょう.下の図のようにt_TopModuleを選択し,Simulate Behavioral Modelをダブルクリックします.

ISimが起動されると最初に0~1000[ns]までシミュレーションしてくれます.今回作成したテストベンチですと,主に見たいところは0~300[ns]あたりなのでそのあたりを拡大してください.下図だけでも動作の様子を確認できますが,これだけではBlockRAMの中身が本当に確かに変わっているのか分かりません.

そこで次のようにBlockRAMの中身をみえるようにしましょう.まず,左側にあるInstance and Process Nameにあるツリーをたどっていき,native_mem_...というものを選択してください.次にObject Nameにあるmemory[0:79,3...]というものを選択し,タイミングチャートへドラッグ&ドロップしましょう.

memoryという信号を追加しましたらもう一度シミュレーションします.下図のようにRestartボタンをまず押し,次に時間限定実行をしてください.その後,0~300[ns]付近を中心に見てください.iWeがHighになっている100~215[ns]までの間でCLKが立ち上がったとき,iAddrにiDinの値がmemoryへ記憶されていることが確認できます.また,215[ns]以降ではiAddrにある値がoDoutに出力されているのも確認できます.なお,oDoutはiWeがHigh,つまり書き込み時にも値を出力しています.従って,oDoutはiAddrに位置づけられた値を常に出力しているといえます.

以上でBlockRAMの使い方の説明を終わります.Spartan6にあるBlockRAMを使えば,分散RAMを消費することなしに開発できますのでぜひとも活用してください.
