DDR2メモリをFPGAで操作してみる

SPARTAN6には、DDR,DDR2,DDR3を操作するハードマクロが組み込まれ、MCBと呼ばれる統一されたユーザーインターフェイスが用意されています。最大400MHzのメモリクロックで操作できるので、1個の16ビット幅のDDR2,3メモリなら、最大ピーク転送レートを、1.6Gバイト/秒にすることができるようです。無論このスピードは、昨今のメモリの性能から比較するとかなり低いですが、普通のI/Oを使ってDDR2などを設計するより2倍以上のスピードアップが期待できます。XILINXの通常のI/OでDDR転送を使うと、せいぜい200MHzが限度ですが、ハードマクロでは400MHzのDDR転送ができるようです。ここで言う400MHzとは、DDR2-800 のことで、400MHzでダブルデータレートの意味です。

Digilent社のSPARTAN6の評価ボード、「ATLYS」では、DDR2メモリを300MHzで使ったデモ回路が組み込まれています。SPARTAN6のスペックでは400MHzなので、やや低めで使っているようです。

そこで、「ATLYS」を使って、DDR2のメモリ操作回路を作ってみることにしました。使用したISEは、13.1です
実際に動作させて、メモリチェック機能を追加した全ソース一式はこちら DDR2のメモリテスト
400MHzでDDR2を動作させた全ソース一式はこちら 400MHzで動かしてみる
400MHzでDDR2の性能を極限まで引き出す 100MHzクロックでMCBを操作してみる 
DDR2はどこまで遅くできるか DDR2メモリは、MCBを使ってどこまで低速なクロックで操作できるのか? 

以下が評価で使った「ATLYS]


MCBの使い方

MCBの使い方でWeb検索すると、MIGを起動して完了すると、example_design というフォルダが生成され、それを使うとDDRのメモリテストが走ってOKか否かの判定ができるらしいのですが、いろいろ設定を変えてみても、500μ秒ほど動作した後エラーになってしまうので、これをあきらめ、生成された、user_design フォルダ以下のRTLを使って、ハードマクロのMCBを操作することにしました。

ハードマクロのMCBを使うには、新しいプロジェクトを作って、そこで以下のように、メニューバーの、Project --> NewSource ... を選択してMIGを起動します。


IP (CORE Generator & Architecture Wizard) を選択し、名前を mymcb と入力し、Next で Generator がしばらく(30秒ほどかかる)走ります。


Memories & Storage Elements の Memory Interface Generator の、MIG Virtex6 and Spartan6 3.7 を選択して、Next を押します。


選択した内容の確認が出てくるので、Next を押すと、30秒ほどで新しいウインドウが出てきます。(以下の次の画面)


新しいウインドウ、Memory Interface Generator が出るので、Next に進みます。


MIG Output Option の選択画面で、Create Design を選択して、Next に進みます。ここで、Component Name を変更できるようですが、そのまま mymcb とします。


Pin Compatible の選択画面になりますが、そのまま進みます。


ATLYSでは、BANK3にDDR2が接続されているので、その選択をします。


メモリクロックは、300MHzとし、ATLYSでは、MT47H64M16HR-25E なので、MT47H64M16XX-25E を選択します。300MHzの選択で、1ピンあたり、600Mビット/秒 の転送スピードになります。


DDR2のODT(On Die Termination) などの設定をしますが、このまま進みます。


MCBでは、複数のポートからのアクセス要求に答えられるように、32ビット、64ビット、128ビットのポートが用意されていますが、バス幅の広い方が高速にアクセスできるので、また、複数のポートにすると、各ポート間のコヒーレンシの制御が必要になる場合があるので、1ポート128ビットを選択します。こうすると、コマンドFIFOに書いた順番にアクセスされるので、書いた後に読み出す場合、順序の整合性が保証されます。複数のポートの場合、各コマンドのFIFOは独立に動作しているので、たとえば各ポートのアービタをラウンドロビンに設定した場合、ポート0で書いた後、ポート1で読み出したい場合、前回のメモリアクセスがまだ実行中に、両者のコマンドがポート0、ポート1のFIFOに書かれた場合、どちらが先に実行されるか、不明だからです。

ユーザーインターフェイスのバススピードは、たとえば64ビットポートだと、300MHzのクロックでメモリを使った場合の最大スピード、1200Mバイト/秒を満足させるには、150MHzの転送が必要になります。制御回路を含めてこのスピードで回路を作成するのは、SPARTAN6では不安要素になってきます。このような理由で、128ビットポートが使いやすいと思われます。無論、複数のマスターからメモリアクセス要求がある場合は、アービターを作る必要があります。

MCBのユーザーインターフェイスの種別を選択します。BANK の位置選択は、下側にしておきます。


128ビットポートを選択すると、各ポート間のアービタの選択は出てきません。


次に、RZQ、ZIOピンなどの設定、クロック入力の種別(シングルか、ディファレンシャルか)などを選択しますが、ATLYSの回路図から、RZQは、L6、ZIOは、C2を選択します。RZQは100Ωの抵抗でGNDに接続され、ZIOはオープンになっています。クロック入力は、Single-Ended を選択します。ここで、クロック入力の選択があるのは、MIGが生成する回路に、infrastructure.v があり、この中で、IBUFGDS と、IBUFG の、プリミティブの選択があって、どちらかを使うようにしているからです。ところが、これをそのまま使うには、外部I/Oピンにクロックを供給しないと動作してくれません。この辺は、ちょっと不親切ですね。IPコアをそのまま使うと、外部に指定クロックを入力しないとMCBが動きません。

ここでは、IPコアをそのまま使うのではなく、生成された、user_design 以下のRTLを一部変更して使うようにして、この外部供給クロックを、内部の信号で使えるようにします。


以上でMIGの設定は終了で、以下のような、設定結果を表示してきます。


次に以下のような、Micron Technology, Inc. Simulation Model License Agreement がでてきます。エルピーダメモリを選択すると、この画面は出てきませんが、シミュレーションはできないみたいです。


UG388 を読むようにと言ってます。このファイルは、ug388.pdf で、生成されたフォルダ以下にあります。CSピンはGNDに接続とか書かれています。MCBは、シングルチップのみサポートだからです。複数のメモリを並列に接続することはできません。


ISEのバージョンなどを表示しています。これで Generete をクリックで生成されます。


十数秒で、以下の画面になり、CORE Generator で作成された、mymcb.xco ができています。このままでは使えないので、Remove して、ipcore_dir フォルダ以下の、mymcb フォルダの、user_design 以下の RTL と、サブフォルダ、mcb_controller の中の RTL のすべてを、プロジェクトソースに追加します。このままでは使えないので、mymcb.v をインスタンシエートする、TOPモジュールを作成し、追加します。


TOPモジュールなどを作成し、追加します。追加するのは、
  1. TOPモジュールの、atlyscmbmig3t.v、
  2. Custom MicroBlaze のラッパー risc2s.v、
  3. Custom MicroBlaze の本体(NGC)risc2s.ngc、
  4. Custom MicroBlaze のプログラムメモリ mainmem.v、
  5. DDR2メモリのリード、ライトバッファー mem32_128.v、
  6. UCFファイル stlyscmb.ucf
です。UCFファイルは、MIGで生成された、mymcb.ucf を元に、Custom MicroBlaze のUARTポート、テストポートを追加したものです。

上記のプロジェクトソース一式はこちら → atlyscmbmig3t.lzh これはまだインプリメントする前のものです。インプリメントすると、21Mバイトほどになり、圧縮しても6Mバイトになって、無料の FC2 ではアップできないので、以下は、別サーバーです。
インプリメント後のもの。→ atlyscmbmig3tdone.lzh 

上記のものは、DDR2のMCBのテストが目的で、機能が低いので、DDR2のメモリテストを長時間行えるように機能追加したものが以下です。最大100時間に渡って、できるだけデータパターンを変化させながら書き、読み出して一致を確認します。操作は 組み込みCPU、Custom MicroBlaze で行っていますが、メモリに書くデータの作成や、読み出して比較するのは、ハードウエアで行っています。

メモリチェックの方法は、1GBのメモリに、あるデータパターンを書き、それを読み出して一致を確認します。データパターンは、ハードウエアで順次変更を加えながら作成する(当然再現可能である)ので、同じ場所に同じデータを書くことも回避できます。16ビットデータバスなので、2バイト単位に見るとビットが反転しやすいパターンになるようにしており、DQn ができるだけ変化するように考慮しています。
インプリメント後のもの。→ atlyscmbmig3tmemtest.lzh

若干修正後のもの。→ atlyscmbmig3tmemtest_a.lzh こちらは、エラーがあると、LD1 を点灯し、1.5秒単位に動作中を LD2--LD7 に表示します。長時間かかる場合、チェックソフトが動作していることを確認できます。

これらの中には、チェックに使った  Custom MicroBlaze のソース一式を含んでいます。コンパイラは、EDK10.1をインストールした後から抜き出した、Cygwin プログラムです。
atlyscmbmig3tdone.lzh では、Custom MicroBlaze が50MHzで動作しましたが、atlyscmbmig3tmemtest.lzh では、さすがに回路規模が大きくなったので、33MHzに落としています。

MIGが生成したRTLの変更箇所


MIGが生成するファイルのなかで、infrastructure.v は、そのままではエラーになるので、148行目付近の以下の部分に、、IBUFG とあるのを、BUFG に変更します。

こうして、TOPから、300MHzの信号を、.c3_sys_clk(mcbclk), に与えると、メモリクロックを300MHzで操作できます。mcbclk はDCMの出力、.CLKFX() で作成します。

メモリチェックのコマンドは以下のように与えると、結果を表示します。この例では、128MBのメモリにデータを書いた後、一致を確認するもです。
CMB-Bug>ddr 4000,12345677,0,0,2,30f6a913
LOOP = 0x4000 ERROR = 0x0

ここで、0x4000 は、8kB単位の個数で、合計128M byte のテストを指示しています。
次の 0x12345677は、データパターンの加算値。32ビットで指定します。16バイトアドレス単位で、この値が加算されたデータとします。この値は、任意に指定できます。
次の 0 は、先頭の初期値で、最初の32ビットデータが、0x12345677になります。
次の 0 は、アドレス初期値。0x2000単位で指定します。128M以上は、上位アドレスが0になります。
次の 2 は、メモリテストのモードで、2は、書いて、読み出します。3だと、同じコマンド引数なら、読み出しと、比較だけ行います。
次の 0x30f6a913 は、0x4000回を超えた場合、次の回のメモリ初期値に、この値を加算します。
この場合、最初の引数を0x4000以上、たとえば0x8000とか、もっと大きく、0x2580000 とすると、1時間ほどチェックしますが、128MBが済むと、初期値を変更して始めから繰り返し、0x2580000/0x4000 で、2400回繰り返します。128MBのテストに1.5秒ほどかかるので、合計1時間になります。

以下は、上記のコマンドのスクリーンショットです。

DDR2の全領域のテストを1回した後、最後に読み出した8kBのチェック用ブロックRAMの先頭をダンプしています。


DDR2テスト回路の主なレジスタ表。Custom MicroBlaze で操作する
アドレスと回路変数名など
サイズ
機能
0xE0000034 p0_cmd_byte_addri 32 bit DDR2メモリの先頭アドレス。LSBの13ビットは無視される(0とする)
0xE0000038 loopcountb 32 bit メモリチェックのループ回数。初期の回路用で、以下のコマンドを使用するときは0とする
0xE000003C dmamode 32 bit DDR2メモリの操作コマンド。バッファーメモリのみを対象とする命令もある
0xE0000050 comperrorcount LSB 10 bit メモリ比較のエラー個数。16バイト単位の比較で異なればプラス1される
0xE0000054 genseed 32 bit メモリに書く最初のデータの初期値の元。0番地のデータは、この値が加算されたものになる
0xE0000058 genadd 32 bit 16バイト単位のアドレス更新で、この値が genseed に加算される
0xE0000080 reset_mig LSB 1 bit 1を書くと、MCBをリセットしてDDR2の操作を停止状態にする。0でMCBスタート
0xE0000084 carib_donecount 32 bit MCBがスタートしてから、キャリブレーションが終わるまでのシステムクロック数。
0xC0000000 mem32_128 mem1 8kB DDR2メモリへ書くデータバッファー。RAMB16_S9_S36 を4個使用
0xC0002000 mem32_128 mem2 8kB DDR2メモリから読み出すバッファー。RAMB16_S9_S36 を4個使用

上記の表以外にも、30個ほどのレジスタがありますが、それらは、回路ソース、atlyscmbmig3t.v を参照。


400MHzで動かしてみる

「ATLYS」のデモソフトでは、300MHzのクロックで操作していますが、これを、SPARTAN6のMCBによるDDR2制御の上限である400MHzにしてみます。ISEのMIGのGUIでは、3000 ps 以下にできないので、作成後に、user_design フォルダ以下の、mymcb.v を変更します。変更箇所は、194行目付近を、localparam C3_MEM_CAS_LATENCY = 6; のように、6に変更します。スピードが速くなったので、カスレイテンシを大きくするわけです。MT47H64M16XX-25E のスペックを見ると、5となっていますが、余裕を見て6としています。

このようにしてから、TOPの( atlyscmbmig3t.v )DCMで生成する、MCB用クロックを400MHzにします。

また、TOPの以下の parameter を、400MHzなので、2500とします。
parameter C3_MEMCLK_PERIOD = 2500; // 400MHz

以上の変更で、メモリクロックが400MHzとなり、DDR2メモリのデータバスの転送レートは、1本あたり、最大800Mbit/秒となり、16本あるので、1.6Gバイト/秒の転送が実現できます。実際このスピードで、メモリテストで問題なく、数時間に渡って正常に動作しています。

インプリメント後のもの。→ atlyscmbmig3tmemtest400m.lzh

CPUクロックと非同期な、高速なクロックでMCBを操作してみる

さて、今までの回路では、MCBのユーザーインターフェイスのクロックとして、CPUと同じものを使ってきましたが、Custom MicroBlaze のクロックを高速にできないので、MCBの能力を十分引き出せません。メモリが400MHzで動作しても、MCBインターフェイスでの128ビットポートが33MHzなら、最大データ転送能力は16x33.3で、533Mバイト/秒となり、メモリの最大能力1.6Gバイト/秒の1/3になってしまいます。SPARTAN6では、100MHzのクロックでMCBを操作する回路を作成しても、十分動作するはずです。そこで、MCBインターフェイスのクロックを独立させ、これを100MHzにして操作できる回路に変更します。

むろん、非同期クロックにするには、相互にクロックの同期化を随所に追加する必要があり、回路規模は膨らみます。CPU側と、MCB側で共有してきたレジスタを分離し、相互に同期化を行います。同期化と言っても、ラッチのやり直しと、信号の微分(変化点の検出)がほとんどです。片方のクロックが十分早いとあらかじめ決まっている場合は、同期化も比較的楽に設計できます。

2種類のクロックを使う場合のクロック同期化では、余分な回路が必要になるので、また、同期化の処理でレイテンシが追加されるので、できるだけ同じクロックで回路を設計するのがFPGA回路設計での基本的な設計スタイルで、そのために、使いやすいデュアルポートメモリが実装されていると言っても過言ではありません。クロックが2種類ある場合、多少遅くてもクロックを一本化する方法にすると、レイテンシも無くなり、ゲート数も少なくなります。とは言っても、2種類のクロックに大きな差があり、どちらかに一本化すると、動作しなかったり、パフォーマンスの低下を無視できない場合、操作レジスタを2重にして、相互にクロックの同期化をするしかありません。

クロック同期化の一連の手順
  1. CPUからレジスタを変更することで、MCBにコマンドを与える部分の抽出
  2. MCB側で、CPUによって操作されるレジスタの変化を検出。微分処理
  3. MCB側での処理を実行、結果はMCBクロックで操作される側のレジスタとなっている
  4. CPU側で、MCB側で操作されるレジスタの変化を検出。微分処理
  5. CPU側で、MCBへのコマンドが終わったことを認識。
と、相互にクロックによる再ラッチや、微分処理が必要になります。
以上のような変更をして、さらに、 Custom MicroBlaze の操作プログラムも変更、追加を行ったものが以下。
全プロジェクトソース一式がこちら → atlyscmbmig3t2clk.lzh  ISE13.1のソースですが、ISEでのインプリメント前のものです。atlyscmbmig3t.bit ファイルだけ含んでいます。

以下はRS232C経由で、 Custom MicroBlaze を使った操作例です。これは、メモリチェックパターンの初期値を0で、16バイト単位でアドレスが増加させたときの、32ビットデータ加算値を、0x12345677、全メモリに書いて読み出しチェックの後、次のチェックの初期値の加算値を0x30F6A913、0x2580000/0x4000==>0x960 から、128Mバイト全域のチェックを0x960回行う指示です。

CMB-Bug>ddr 2580000,12345677,0,0,2,30f6a913 (enter) とコマンドを与えると、約22分20秒後に、以下の結果を表示します。
LOOP = 0x2580000 ERROR = 0x0

このチェックで、DDR2メモリとFPGA間で、合計614Gバイトのデータ転送が行われるので、平均で約460Mバイト/秒の転送スピードになります。この間、チェック用のデータパターン作成、書き込み、読み出しの後全バイト一致のチェックを行っています。DDR2メモリのピークデータ転送能力の、28%となっています。回路を工夫すれば、50%ぐらいまでは上げられそうですが、これには、ハードウエアで行っているパターン作成と一致のチェックを、パイプライン処理に持ち込む工夫が必要です。 ここではそれへの変更より、もっと簡便にDDR2メモリの最大転送能力に迫るテストをするために、ちょっとした工夫をしてあります。

データチェックはやらずに書き込みのみ連続で行うための操作を行う、( Custom MicroBlaze のソフト上で変更 )以下のコマンドを用意しました。

CMB-Bug>ddr 258000,12345677,0,0,5,30f6a913,f (enter) とコマンドを与えると、書き込みだけ行います。

ここで、チェック付きと異なるのは、回数が、1桁下がって0x258000となっていること、5番目の数値が5になっていること、さらに、コマンドの引数の最後に ,f が追加されていることです。5番目の5が、書き込みのみの指示となっています。,f は、同じアドレスに同じデータブロック(8KBとなっている)を16回書くという指示です。これによって、8kBx16で、128kBの連続書き込みが行われ、CPUのオーバーへッドは1/16になって、MCBが、DDR2への書き込みに専念する時間が増えます。

この処理は、約3分42秒と、大幅に短くなり、DDR2メモリアクセス合計は、307Gバイトです。平均書き込みデータレートは、1.38Gバイト/秒 となって、DDR2メモリの上限の86%まで高められました。なぜこういうチェックを行うのかは、「ATLYS」には、電流監視モニタがあるので、各電源の消費量がリアルタイムで観察できるので、DDR2メモリ操作の消費電力の見積りに重宝するからです。DDR2、DDR3では、大容量データを高速にアクセスできるのはいいですが、組み込み基板では、高速に動作させると発熱の問題が大きくなるので、DDRメモリをどの程度のスピードでアクセスすれば、ディレーティングを満足できるのか、あらかじめ知っておくと最適なシステム設計に役立つと思います。

DDR2メモリは、MCBを使ってどこまで低速なクロックで操作できるのか?

以前から興味があったので、DDR2( DDR2に限らず、SDRAM全般 )がどこまで低速なクロックで動作するのか、実験してみます。無論、DDR2メモリのカタログでは、ATLYSに搭載されている、MT47H64M16HR-25E では125MHzが限度となっているので、125MHzが妥当な最低スピードと思われます。ここでは、このクロック下限を無視して動かしてみます。いままで紹介してきたプロジェクトソースをあまり変更しなくても、200MHz、と100MHでは、パラメータの変更で動作することを確認しています。( すべての温度範囲、電圧範囲でテストしたわけではありませんが、周囲温度25℃付近で、48時間動作させて問題は起こらなかった )

SDR SDRAMでは、出始めの頃は、66MHzでの動作に関するタイムスペックがあり、実際このスピードのメモリがあったみたいです。現在、SDR SDRAMメモリのデータシートを調べると、ハイニックスの H57V1262GFR-75 では、System Clock Cycle Time のMAXが、1000nsと記されていて、つまり1MHzでも動作保証しているようです。マイクロンの、MT48LC2M32B2 では、Clock cycle time のMAXには、記述がありません。まさかスタティック動作可能とは思いませんが、そのような使い方を想定していないと思われます。ここで、ハイニックスの1MHzではどうなのか、その意味を考察してみると、リフレッシュのインターバルが約15μ秒なので、リフレッシュの内部処理が15クロック以内に終了できるということなのかも知れません。SDR SDRAMがどこまで低速のクロックで正常に動作するのか、別の機会に調べてみようと思います。

XilinxのMCBインターフェイスを使うと、TOPの parameter の設定変更で、簡単にクロックを遅くできますが、メモリチェック回路の構成上、以下の制約があります。これらは、ユーザーTOP( atlyscmbmig3t.v )と、MCBのTOP( mymcb.v )の parameter を変更します。
  1. C3_CLKOUT2_DIVIDE は、C3_CLKOUT0_DIVIDE,C3_CLKOUT1_DIVIDE の8倍の数値として、1/8のクロック周波数とする。
  2. C3_CLKOUT3_DIVIDE は、C3_CLKOUT0_DIVIDE の4倍の数値として、1/4のクロックとする。
  3. C3_CLKFBOUT_MULT は、PLLのVCOが400MHz〜1GHzに収まるような倍率に設定する。例として、入力クロックが200MHzの場合は2が最低となる。
  4. メモリチェック回路を制御する、Custom MicroBlaze のシステムクロックは、C3_CLKOUT2_DIVIDE の設定で .c3_clk0(c3_clk0) に出力されるクロックより約1/1.5以下にする。これは、CPUの1クロック間のHigh信号を、.c3_clk0(c3_clk0) で十分サンプリングできるようにするためです。
  5. 上記 4 と関連して、Custom MicroBlaze のRS232Cのボーレート分周を115200bpsを満たすように変更する。
  6. C3_MEMCLK_PERIOD は、メモリに与えるクロックのインターバルですが、100MHzなら、10000(ps)とします。

DDR2の低速クロックでのMCB parameter 設定
メモリクロック
400MHz
200MHz
100MHz
50MHz
25MHz
C3_CLKOUT0_DIVIDE
16
C3_CLKOUT1_DIVIDE
16
C3_CLKOUT2_DIVIDE
16
32
64
C3_CLKOUT3_DIVIDE
16
32
C3_CLKFBOUT_MULT
C3_MEMCLK_PERIOD ps
2500
5000
10000
20000
40000
MCBインターフェイスクロック
100MHz
50MHz
25MHz
12.5MHz
6.25MHz
Custom MicroBlaze clk
33.3MHz
33.3MHz
16.6MHz
8.33MHz
4.17MHz

となりますが、メモリクロックが100MHzまではすんなりISEのインプリメントが通りますが、50MHs以下では、以下のようなエラーが出てしまいます。

MEM_DDR2_WRT_RECOVERY は、user_design 以下、rtl/mcb_controller フォルダの、mcb_raw_wrapper.v ファイルの、671行付近に、

という記述があって、これが、"1"になってしまって、 illegal value だということのようです。C_MEM_TWR は、mymcb.v の中で、localparam C3_MEM_TWR で設定されているので、これを、強制的に、2以上になるような数値に変更すると、ISEのエラーがなくなり、インプリメントできます。ここを、変えてしまっていいのかどうかは、DDR2メモリのスペックから判断すると、問題ないようです。C3_MEM_TWR は、Write recovery time のことで、最低値は決められていますが、大きい方は記述がないようです。( hynix 、Micron の資料 )

DDR2の低速クロックでのMCB parameter の C3_MEM_TWR 設定
 メモリクロック 
 400MHz 
 200MHz 
 100MHz 
 50MHz 
 25MHz 
C3_MEM_TWR
 15000 
 15000 
 15000 
 30000 
 60000 
Custom MicroBlaze clk
33.3MHz
33.3MHz
16.6MHz
8.33MHz
4.17MHz

ここで、メモリクロックが25MHzの場合、Custom MicroBlaze clk を、4.17MHz にしないといけませんが、DCMの分周では足らないので、12分周したあと、以下のようにもう一段クロックを遅くします。




準備中


ホームに戻る


inserted by FC2 system