Xilinx Memory Interface Generator (MIG) による DDR2 SDRAM のアクセス

(4098d) 更新


公開メモ

概要

Xilinx FPGA から DDR や DDR2、DDR3 といった高速メモリにアクセスすることを目的に、

  1. Memory Interface Generator (MIG) というソフトを使ってIPコアを生成する方法
  2. 生成したIPコア経由で Spartan 3A DSP から DDR2 メモリにアクセスする方法

をまとめてみました。

詳細としては、

Core name: Xilinx MIG
Version: 3.3
Release Date: December 2, 2009

を使って、

Spartan 3A DSP 1800 Starter Platform

用のコアを作成し、その使い方について調べたものです。

入出力ポートを指定してコアを生成する

まずは MIG を使ってIPコアを生成する方法についてなのですが、 一見簡単そうに見えて、思ったよりも難しかったです。

今回は Xilinx の Starter Platform を購入したので、 それ用のコアを生成したかったのですが、 MIG で簡単にできるのは、次の2つの使い方で、

  1. Xilinx の Reference Board 用にコアを作る
  2. これから PCB を起こす段階で、FPGA のどのピンをメモリのどのピンと結線したらよいか MIG に決めてもらう

PCBがすでにできていて、FPGA とメモリが MIG が想定するのと 異なるピン配置で接続されているという今回のような場合に使える コアを生成する方法をなかなか見つけることができませんでした。

いろいろ調べたあげく、何とか方向性が見えてきたので、 以下に進捗をまとめていこうと思います。

情報源

基本は MIG の User Guid (MIG を起動した画面の左下のボタン : ug086) の

  • Chapter 1 : Using MIG
    • MIG User Interface
      • MIG Output Options
        Verify UCF and Update Design and UCF

に沿っていきます。

MIG の2ページ目で [Create Design] を指定する

MIG を起動した際の一番目の選択肢です。

mig2.png

最終的には Verify UCF and Update Design and UCF を使うのですが、 始めは .ucf に指定するための信号名が分からないので、 ここでは一旦 [Create Design] を選びます。

事前情報

ここから、ボードやメモリに合わせたパラメータ設定が必要になるので、 まずは情報を集めます。

Xilinx からは、Spartan 3A DSP Starter Platform のページ から Spartan-3A DSP FPGA ボードとキット資料 をたどると、

が手に入り、ピン配置などが分かります。

ボードには、16bit 幅 64Mbytes の DDR2 SDRAM である MT47H32M16BM が2つ載っていて、32bit 幅 128Mbytes のメモリ構成となっています。

ユーザーズガイドにメモリのスピードグレードの記述が無かったのですが、 FPGA の部屋の記事 を参考に、実機から目視で得た FBGA Code を Micron の FBGA Decoder のページ に入れたところ、MT47H32M16BN-3:D であることが分かりました。

この SDRAM チップはスピードグレードに関わらず最低動作周波数が 125MHz となっているので、今回のメモリインターフェースはこの周波数で動かすことを 考えています。

スタータープラットフォームのユーザーズガイドによれば、 FPGA <=> Memory の信号線はインピーダンスを 50Ω にしてあるので、ODT (On-dei termination) も 50Ω にするのが良い?

メモリの仕様に合わせてオプションを選択する

Spartan 3A DSP 1800 Starter Platform 用に次のように指定しました。

自信のないところもあるのですが、、、

うまく行かなかったらまた考えます。

Memory SelectionDDR2 SDRAM
Frequency8000 ps = 125MHz
Write Pipe Stages4
Memory TypeComponents
Memory PartMT47H32M16-3
Data Width32
Data MaskUncheck
Burst Length4
Burst Typesequential
Output Drive StrengthFullstrength
RTT(nominal)-ODT50ohms
DQS# EnableEnable
Use DCMUncheck
Class for Address and ControlClass II
Class for DataClass II
Debug Signals for Memory ControllerDisable

Available/Reserved ピンは指定しない

つぎにピン配置を決めるのですが・・・

[Create Design] モードというのは、新たにPCBを起こす際に MIG にお勧めの結線方法を提案してもらうというモードなので、 このモードではどんなに頑張っても1つ1つのピン配置を こちらから指定することはできません。

メモリとの接続に使ってもよいピンを MIG に教えると、 MIG がピン配置を勝手に決め、その配置でコアを生成するという 話になっています。

デフォルトでは全てのピンが Available Pins に入っていて、 MIG はその中から適当なピンを選んでコアを生成します。

もちろんそれでは困るのですが、 どうしようもないので後で直すことにして、 ここではそのままにしておきます。

ちなみに、このページの [Read UCF File] は興味を引くボタンなのですが、、、

他の用途に使っているためにメモリとの配線に使えないピンを Reserved Pins として登録するためのもので、メモリとの配線方法を指定するためのものでは無いので、 残念ながら押しても役に立ちません。

Bank は(念のため)本来のピン配置と同じものを選んでおく

MIG は上述の Available Pins から適当なピンを選んで信号線を割り当てますが、 このページでは信号線の種類毎に、使う Bank を指定することができます。

mig4.png

目的のボードに合わせてバンクを選択すればよいのだと思います。

コアを生成する

どんどんページをめくって、最後にライセンスを承諾するとコアが生成されます。

ピンを指定するための ucf ファイルを作る

ここからいよいよピン配置を指定する段階です。

私の場合 ddr2mi という名前でコアを作ったので、 できたコアは以下のディレクトリにありました。

(project folder)/ipcore_dir/ddr2mi/user_design

このフォルダの par/ddr2mi.ucf に、MIG が適当に決定したピン配置と、 そのピン配置に最適化された回路を生成するための ucf ファイルが作成されています。

このファイルから #bank というコメントが付いている行を抜き出すと、 ピン配置を指定している行だけを抽出できます。

unix ライクなコンソールが使えれば、

LANG:console
$ grep -i "#bank" ipcore_dir/ddr2mi/user_design/par/ddr2mi.ucf | sort > ddr2mi_pins.ucf

とすることで、

ddr2mi_pins.ucf

NET "cntrl0_ddr2_a[0]" LOC = "J21" ;     #bank 1
NET "cntrl0_ddr2_a[10]" LOC = "B2" ;     #bank 3
NET "cntrl0_ddr2_a[11]" LOC = "H7" ;     #bank 3
NET "cntrl0_ddr2_a[12]" LOC = "G6" ;     #bank 3
...

NET "cntrl0_ddr2_dqs_n[6]" LOC = "J4";     #bank 3
NET "cntrl0_ddr2_dqs_n[7]" LOC = "J6";     #bank 3
NET "cntrl0_rst_dqs_div_in" LOC = "G4";     #bank 3
NET "cntrl0_rst_dqs_div_out" LOC = "F5";     #bank 3

のようなファイルができます。

これを自分のボードに合わせて編集し、正しいピンを指定します。

ucf ファイルを元にコアのピン配置を変更する

上記の手順でピン配置だけを変更しても、FPGA 内部のプリミティブ配置は MIG が最初に提案したピン配置に最適化されているのでそのままでは動きません。

そこで、もう一度 MIG を起動してコアを作り直します。

Design ペインの Hierarchy で先ほど生成した ddr2mi をダブルクリックして MIG を起動します。

mig6.png

今度は [Verify UCF and Update Design and UCF] を選択し、
Load Prj File に ipcore_dir/ddr2mi/user_design/mig.prj
Load UCF File に ddr2mi_pins.ucf (ピンを指定するための自分で作ったファイル)
を指定します。

Next を連打して、もう一度ライセンス条項を許諾すると、 指定したピン配置でコアを生成できます。

正しいピン配置になっているかどうか確認する

LANG:console
$ grep -i "#bank" ipcore_dir/ddr2mi/user_design/par/ddr2mi.ucf | sort > ddr2mi_pins.ucf

としてみると、指定した通りのピン配置でコアが生成されていると思います。

オリジナルの構成と比べてみる

LANG:console
$ grep -i "#bank" ddr2mi_original.ucf | sed -e "s/#bank.*//i" -e "s/\r//" | sort > original-pins.txt
$ grep -i "#bank" ddr2mi.ucf | sed -e "s/#bank.*//i" -e "s/\r//" | sort > custom-pins.txt
$ paste *pins.txt > compare.txt

として、形式を整えたものが次の表です。

ピン名称カスタムオリジナル
cntrl0_ddr2_a[0]J5M4
cntrl0_ddr2_a[1]M9M6
cntrl0_ddr2_a[2]M10M5
cntrl0_ddr2_a[3]K4N4
cntrl0_ddr2_a[4]K5N5
cntrl0_ddr2_a[5]K2N2
cntrl0_ddr2_a[6]K3N1
cntrl0_ddr2_a[7]L3N7
cntrl0_ddr2_a[8]L4N6
cntrl0_ddr2_a[9]M7P2
cntrl0_ddr2_a[10]M8P1
cntrl0_ddr2_a[11]M3W6
cntrl0_ddr2_a[12]M4W7
cntrl0_ddr2_ba[0]K6M8
cntrl0_ddr2_ba[1]J4M3
cntrl0_ddr2_cas_nL10K3
cntrl0_ddr2_ck[0]N1AD1
cntrl0_ddr2_ck[1]N5AC2
cntrl0_ddr2_ck_n[0]N2AD2
cntrl0_ddr2_ck_n[1]N4AC3
cntrl0_ddr2_ckeL7M7
cntrl0_ddr2_cs_nH2L4
cntrl0_ddr2_odtG3K5
cntrl0_ddr2_ras_nH1L3
cntrl0_ddr2_we_nL9K2
reset_in_nG8G8
cntrl0_ddr2_dq[0]U9U9
cntrl0_ddr2_dq[1]V8V8
cntrl0_ddr2_dq[2]AB1AB1
cntrl0_ddr2_dq[3]AC1AC1
cntrl0_ddr2_dq[4]Y5Y5
cntrl0_ddr2_dq[5]Y6Y6
cntrl0_ddr2_dq[6]U7U7
cntrl0_ddr2_dq[7]U8U8
cntrl0_ddr2_dq[8]AA2AA2
cntrl0_ddr2_dq[9]AA3AA3
cntrl0_ddr2_dq[10]Y1Y1
cntrl0_ddr2_dq[11]Y2Y2
cntrl0_ddr2_dq[12]T7T7
cntrl0_ddr2_dq[13]U6U6
cntrl0_ddr2_dq[14]U5U5
cntrl0_ddr2_dq[15]V5V5
cntrl0_ddr2_dq[16]R8V1
cntrl0_ddr2_dq[17]R7R7
cntrl0_ddr2_dq[18]U1R8
cntrl0_ddr2_dq[19]U2U2
cntrl0_ddr2_dq[20]P8P8
cntrl0_ddr2_dq[21]P9P9
cntrl0_ddr2_dq[22]R5R5
cntrl0_ddr2_dq[23]R6R6
cntrl0_ddr2_dq[24]P7P7
cntrl0_ddr2_dq[25]P6P6
cntrl0_ddr2_dq[26]T3T3
cntrl0_ddr2_dq[27]T4T4
cntrl0_ddr2_dq[28]N9N9
cntrl0_ddr2_dq[29]P10R2
cntrl0_ddr2_dq[30]P4P4
cntrl0_ddr2_dq[31]P3P3
cntrl0_ddr2_dqs[0]V7V7
cntrl0_ddr2_dqs[1]W3W3
cntrl0_ddr2_dqs[2]T5T5
cntrl0_ddr2_dqs[3]R3R3
cntrl0_ddr2_dqs_n[0]V6V6
cntrl0_ddr2_dqs_n[1]W4W4
cntrl0_ddr2_dqs_n[2]U4U4
cntrl0_ddr2_dqs_n[3]R4R4
cntrl0_rst_dqs_div_inT9T9
cntrl0_rst_dqs_div_outT10T10

黄色のピンがオリジナルと異なる部分です。

データ部分などはほぼぴったり同じなのですね。

気になる点

ピンをカスタマイズするために .ucf を与えた後、 Verification Report のところでかなりの数の Warning が出ました。

                      Verification Report                          

/*******************************************************/
/*                   Controller 0                           
/*******************************************************/
Checking pins allocated to Data bits ...
Checking pins allocated to Strobe bits ... 
Checking pins allocated to Mask bits ...
Checking pins allocated to Clock bits ... 
Checking pins allocated to Address bits ...
Checking pins allocated to BankAddress bits ...
WARNING: LUT delay location constraint for dqs_delayed column of dqs[3] are missing..
WARNING: LUT delay BEL constraint constraints of dqs[3] are missing..
WARNING: Fifo write address and write enable constraints of dqs[3] are missing..
WARNING: Slice location constraints of dq[31] are missing..
WARNING: Slice location constraints of dq[30] are missing..
WARNING: Slice location constraints of dq[29] are missing..
WARNING: Slice location constraints of dq[28] are missing..
WARNING: Slice location constraints of dq[27] are missing..
WARNING: Slice location constraints of dq[26] are missing..
WARNING: Slice location constraints of dq[25] are missing..
WARNING: Slice location constraints of dq[24] are missing..
WARNING: LUT delay location constraint for dqs_delayed column of dqs[2] are missing..
WARNING: LUT delay BEL constraint constraints of dqs[2] are missing..
WARNING: Fifo write address and write enable constraints of dqs[2] are missing..
WARNING: Slice location constraints of dq[23] are missing..
WARNING: Slice location constraints of dq[22] are missing..
WARNING: Slice location constraints of dq[21] are missing..
WARNING: Slice location constraints of dq[20] are missing..
WARNING: Slice location constraints of dq[19] are missing..
WARNING: Slice location constraints of dq[18] are missing..
WARNING: Slice location constraints of dq[17] are missing..
WARNING: Slice location constraints of dq[16] are missing..
WARNING: LUT delay location constraint for dqs_delayed column of dqs[1] are missing..
WARNING: LUT delay BEL constraint constraints of dqs[1] are missing..
WARNING: Fifo write address and write enable constraints of dqs[1] are missing..
WARNING: Slice location constraints of dq[15] are missing..
WARNING: Slice location constraints of dq[14] are missing..
WARNING: Slice location constraints of dq[13] are missing..
WARNING: Slice location constraints of dq[12] are missing..
WARNING: Slice location constraints of dq[11] are missing..
WARNING: Slice location constraints of dq[10] are missing..
WARNING: Slice location constraints of dq[9] are missing..
WARNING: Slice location constraints of dq[8] are missing..
WARNING: LUT delay location constraint for dqs_delayed column of dqs[0] are missing..
WARNING: LUT delay BEL constraint constraints of dqs[0] are missing..
WARNING: Fifo write address and write enable constraints of dqs[0] are missing..
WARNING: Slice location constraints of dq[7] are missing..
WARNING: Slice location constraints of dq[6] are missing..
WARNING: Slice location constraints of dq[5] are missing..
WARNING: Slice location constraints of dq[4] are missing..
WARNING: Slice location constraints of dq[3] are missing..
WARNING: Slice location constraints of dq[2] are missing..
WARNING: Slice location constraints of dq[1] are missing..
WARNING: Slice location constraints of dq[0] are missing..
WARNING: Some of the RLOC/U_SET Constraints of the LUT delay calibration circuit are missing.
WARNING: Some of the BEL constraints of the LUT delay calibration circuit are missing.
WARNING: Slice location constraint for delayed rst_dqs_div_out signal is missing.

Verification completed. Found the following warnings.
Number of warnings in the input UCF = 47.
Verification Successful.
All signals in the UCF were allocated correctly.

与えた .ucf ファイルにはピン配置制約以外書いていないので、それを検出しているものであって、 できあがった .ucf ファイルはこれら Warning の点を修正したものであると考えていたのですが、 試しに再生成された .ucf ファイルを指定して Verify UCF and Update Design and UCF してみたところ、、、

                      Verification Report                          

/*******************************************************/
/*                   Controller 0                           
/*******************************************************/
Checking pins allocated to Data bits ...
Checking pins allocated to Strobe bits ... 
Checking pins allocated to Clock bits ... 
Checking pins allocated to Address bits ...
Checking pins allocated to BankAddress bits ...
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/five-slice_x3y79" for dqs_delayed column of dqs[3] is invalid or missing.
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/four-slice_x2y78" for dqs_delayed column of dqs[3] is invalid or missing.
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/one-slice_x2y79" for dqs_delayed column of dqs[3] is invalid or missing.
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/six-slice_x3y78" for dqs_delayed column of dqs[3] is invalid or missing.
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/three-slice_x2y78" for dqs_delayed column of dqs[3] is invalid or missing.
WARNING: LUT delay location constraint "top_00/data_path0/data_read_controller0/gen_delay[3].dqs_delay_col0/two-slice_x2y79" for dqs_delayed column of dqs[3] is invalid or missing.
...

WARNING: BEL constraint "top_00/data_path0/data_read_controller0/rst_dqs_div_delayed/three-g" for delayed rst_dqs_div_in signal is not correct.
WARNING: BEL constraint "top_00/data_path0/data_read_controller0/rst_dqs_div_delayed/two-g" for delayed rst_dqs_div_in signal is not correct.

Verification completed. Found the following warnings.
Number of warnings in the input UCF = 216.
Verification Successful.
All signals in the UCF were allocated correctly.

むしろ WARNING が大幅に増えていました。
216 とか・・・(泣

対照的に、ピン配置を指定せず、Create Design で作った .ucf ファイルをそのまま指定して Verify UCF and Update Design and UCF すると、以下のように Warning は1つも出ません。

                      Verification Report                          

Reading design libraries of xc3sd1800a-fg676... successful !

/*******************************************************/
/*                   Controller 0                           
/*******************************************************/
Checking pins allocated to Data bits ...
Checking pins allocated to Strobe bits ... 
Checking pins allocated to Clock bits ... 
Checking pins allocated to Address bits ...
Checking pins allocated to BankAddress bits ...
Verification Successful.
All signals in the UCF were allocated correctly.

うまくいってない!

ここまでやっておいて何ですが、上記手順ではうまく .ucf を再生成できていないようです。

うまく行きました

で、ものは試し、と、上記のように大量の Warning が出たのをめげずに 進んで、もう一度コアを作り直し、そうしてできた .ucf ファイルを Verify UCF and Update Design and UCF に与えたところ、

                      Verification Report                          

/*******************************************************/
/*                   Controller 0                           
/*******************************************************/
Checking pins allocated to Data bits ...
Checking pins allocated to Strobe bits ... 
Checking pins allocated to Clock bits ... 
Checking pins allocated to Address bits ...
Checking pins allocated to BankAddress bits ...
Verification Successful.
All signals in the UCF were allocated correctly.

のように、WARNING が出なくなりました。

もともと、「ピン配置だけを抜き出して書き換えた」 .ucf ファイルを作ったのが間違いで、 オリジナルの .ucf ファイルを「ピン配置部分だけを書き換えた」.ucf ファイルを与えて Verify UCF and Update Design and UCF すれば良かったのかもしれません。

とりあえず、これで先に進めます。

作成された prj ファイル

CORE Generator Options:
   FPGA Family                : spartan3adsp
   FPGA Part                  : xc3sd1800a-fg676
   Speed Grade                : -4
   Synthesis Tool             : XST
   HDL                        : verilog

MIG Output Options:
   Component Name              : ddr2mi
   No of Controllers           : 1
   Selected Compatible Devices : --
    
Controller Options :
   Frequency                  : 125.00 MHz(8000 ps)
   Write Pipe Stages          : 4
   Memory                     : DDR2_SDRAM
   Memory Type                : Components
   Memory Part                : MT47H32M16XX-3
   Equivalent Part(s)         : MT47H32M16BN-3;MT47H32M16CC-3;MT47H32M16FN-3;MT47H32M16GC-3
   Row Address                : 13
   Column Address             : 10
   Bank Address               : 2
   Data Width                 : 32
   Data Mask                  : disabled

Memory Options :
   Burst Length                       : 4(010)
   Burst Type                         : sequential(0)
   CAS Latency                        : 3(011)
   DLL Reset                          : yes(1)
   Mode                               : normal(0)
   Write Recovery                     : 3(010)

   DQS# Enable                        : Enable(0)
   DLL Enable                         : Enable-Normal(0)
   OCD Operation                      : OCD Exit(000)
   Output Drive Strength              : Fullstrength(0)
   Outputs                            : Enable(0)
   RDQS Enable                        : Disable(0)
   RTT (nominal) - ODT                : 50ohms(11)

FPGA Options :
   DCM option                           : disabled
   SSTL Class for Address/Control       : Class II
   SSTL Class for Data                  : Class II
   Debug Signals for Memory Controller  : Disable
   System Clock                         : Single-Ended

Reserved Pins :
   --

Bank Selections:
    Data:               Bank 3
    Address/Control:    Bank 3
    System Control:     Bank 0
    System Clock:       Bank 0

となっていました。

MAP でエラー

(追記) この問題は、私が MI の使い方を良く読んでおらず、MIG が生成した .ucf ファイルが正しくプロジェクトに組み込まれていなかったのが原因でした。同じ問題に行き当たった方以外は、ずばっと飛ばして下記の 解決編 に進んで下さい。

上記設定で作ったコアを組み込んでプロジェクトをコンパイルしたら、 MAP 段階で以下のエラーが出ました。

ERROR:PhysDesignRules:760 - Incompatible programming for IO standard. IO
   standard LVDS_25 of comp ddr2_dqs<0> does not allow both input and output
   programming on the same comp.
ERROR:PhysDesignRules:760 - Incompatible programming for IO standard. IO
   standard LVDS_25 of comp ddr2_dqs<1> does not allow both input and output
   programming on the same comp.
ERROR:PhysDesignRules:760 - Incompatible programming for IO standard. IO
   standard LVDS_25 of comp ddr2_dqs<2> does not allow both input and output
   programming on the same comp.
ERROR:PhysDesignRules:760 - Incompatible programming for IO standard. IO
   standard LVDS_25 of comp ddr2_dqs<3> does not allow both input and output
   programming on the same comp.
ERROR:Pack:1642 - Errors in physical DRC.

そもそも .ucf には

NET  "cntrl0_ddr2_dqs[*]"                       IOSTANDARD = DIFF_SSTL18_II;
NET  "cntrl0_ddr2_dqs_n[*]"                     IOSTANDARD = DIFF_SSTL18_II;

と書かれているので、なぜ上記エラーが出るのか・・・

http://forums.xilinx.com/t5/Old-ISE-Board-no-new-thread/Error-compilation-with-MIG-DDR2-Spartan-3an/m-p/13532 によれば MIG のオプションで DQS# enable --> disable(1) の変更が必要だそうです?

仕方がないので、これを試すことにしました。
#(追記)これは恐らく間違った方針でした

で、もう一度始めから Create Design するのは面倒だったので、 .prj ファイルのパラメータを変更して Verify UCF and Update Design and UCF してみたところ、 ちゃんと Summary 画面で

DQS# Enable : Disable(1)

となっていました。

その次の画面で

ERROR: Differential dqs[3] is disabled in MIG project file but the pin out is found in UCF
ERROR: Differential dqs[2] is disabled in MIG project file but the pin out is found in UCF
ERROR: Differential dqs[1] is disabled in MIG project file but the pin out is found in UCF
ERROR: Differential dqs[0] is disabled in MIG project file but the pin out is found in UCF

なるエラーが出たので、.ucf ファイルから

NET "cntrl0_ddr2_dqs_n[0]"

などの行をすべて削除してもう一度読み込ませたところ、

                      Verification Report                          

Reading design libraries of xc3sd1800a-fg676... successful !

/*******************************************************/
/*                   Controller 0                           
/*******************************************************/
Checking pins allocated to Data bits ...
Checking pins allocated to Strobe bits ... 
Checking pins allocated to Clock bits ... 
Checking pins allocated to Address bits ...
Checking pins allocated to BankAddress bits ...
Verification Successful.
All signals in the UCF were allocated correctly.

として、正しくコアを生成できました。

生成されたコアからは cntrl0_ddr2_dqs_n というピンが無くなっていました。

これで MAP は通るようになりました。

次は PAR でエラー

(追記)この問題はどうやら MIG とは関係のないものでした。読み飛ばす場合には こちら へ進んでください。

MAP は通ったものの、次は PAR でエラーです。

Post-MAP Static Timing では All constraints were met. となっているにもかかわらず、次のエラーが出てしまいます。

ERROR:Par:228 - At least one timing constraint is impossible to meet because component delays 
  alone exceed the constraint. A timing constraint summary below shows the failing constraints 
  (preceded with an Asterisk (*)). Please use the Timing Analyzer (GUI) or TRCE (command line) 
  with the Mapped NCD and PCF files to identify which constraints and paths are failing because 
  of the component delays alone. If the failing path(s) is mapped to Xilinx components as
  expected, consider relaxing the constraint. If it is not mapped to components as expected, 
  re-evaluate your HDL and how synthesis is optimizing the path. To allow the tools to bypass 
  this error, set the environment variable XIL_TIMING_ALLOW_IMPOSSIBLE to 1. 

  For more information about the Timing Analyzer, consult the Xilinx Timing Analyzer Reference 
  manual; for more information on TRCE, consult the Xilinx Command Line Tools User Guide "TRACE" 
  chapter.

その後のメッセージから特定される箇所は、

ConstraintCheckWorst Case SlackBest Case AchievableTiming ErrorsTiming Score
* TS_clock_gen_clk_125MHz90_pre = PERIOD TIMEGRP "clock_gen_clk_125MHz90_pre" TS_clk_125MHz_in PHASE 2 ns HIGH 50%SETUP-0.926ns11.704ns42202
HOLD0.399ns00

で、これはちょうど MI コアとのインタフェース部分のクロックなのですが、 Post-MAP Static Timing では周期の 8ns に対して setup path は 3.389 と表示されており、 当然エラーにはならないはずなのです。

これでは何が悪いのか分からないので困ってしまうのですが・・・

メッセージを参考に、XIL_TIMING_ALLOW_IMPOSSIBLE という環境変数に 1 を設定して再度トライしたところ、数多く出力されるメッセージの中に

WARNING:Place:414 - The input design contains local clock signal(s). To get a better result,
we recommend users run map with the "-timing" option set before starting the placement.

というのがありました。

MIG の生成するコアは negedge clk0 に同期した信号を要求するので、 そのために local clock signals が使われているとのメッセージが出ているのだと思います。
(2010/06/03 この予想は間違いでした。下記参照)

そして、このせいで Post-MAP Static Timing で正しい見積もりが出せなかったということでしょうか。

実際 XIL_TIMING_ALLOW_IMPOSSIBLE で無理矢理通した後の、 Post-PAR Static Timing を見ても、それらしいエラーが出ていません(困

そこで、Map の Process Properties の Other Map Command Line Options に -timing を記入してトライしたところ、Phase 4.2 Initial Clock and IO Placement で ものすっごく時間が掛かるようになりました。

というか・・・CPUを100%使い切ったまま10分以上返ってこないです?!

negedge clk0 を posedge clk180 で書き直す

#結局この項の内容は後で元に戻しました

negedge clk0 のクロックドメインは後述するステートマシン部だけなので、 それほど大きなドメインではないのですが、local というには大きくて、 それが MAP に負担が掛かっている、ということでしょうか。

これらの信号を生成するためにもう1つクロック線を使って、 ユーザーコード側で negedge clk0 が必要な部分を、 180 度位相の異なる clk180 の posedge で動かすことにしました。

うーん、まだだめですね。Phase 4.2 から戻ってきません。-timing オプションを付けると map が終了しないのは、回避の仕方が分かりません。

-timing オプションを取ってみたところ、新たに PAR でエラーが出て、 たぶん BUFG が足りないと言うことのようです。

ERROR:Place:962 - A DCM / BUFGCTRL clock component pair have been found that are not placed at an optimal DCM / BUFGCTRL
  site pair. The DCM component <clock_gen/DCM_SP_inst> is locked to site <DCM_X1Y3> and the corresponding BUFGCTRL
  component <clock_gen/clk_125MHz90_bufg> is locked to site <BUFGMUX_X1Y1>. This will not allow the usage of the fast
  path between the DCM and the Clock buffer. If this sub optimal condition is acceptable for this design, you may use
  the CLOCK_DEDICATED_ROUTE constraint in the .ucf file to demote this message to a WARNING and allow your design to
  continue. However, the use of this override is highly discouraged as it may lead to very poor timing results. It is
  recommended that this error condition be corrected in the design. A list of all the COMP.PINs used in this clock
  placement rule is listed below. These examples can be used directly in the .ucf file to override this clock rule.
  < PIN "clock_gen/DCM_SP_inst.CLK90" CLOCK_DEDICATED_ROUTE = FALSE; >

ゴールは遠い感じです。

気になるメッセージ

PAR 時にいくつか気になるメッセージが出ていたので、 実際には後で対処するとして、忘れないようとりあえずここにメモしておきます。

INFO:Place:834 - Only a subset of IOs are locked. Out of 97 IOs, 39 are locked and 58 are not 
locked. If you would like to print the names of these IOs, please set the environment variable 
XIL_PAR_DESIGN_CHECK_VERBOSE to 1. 

メモリと接続されるピン配置が合ってません。 もしかして、MIG の生成した .ucf がまったく効いていない?

何か根本的な手順が抜けているのかもしれません・・・

ただ、MIG の生成した .ucf ファイルを指定して明示的にプロジェクトに追加しようとすると すでにプロジェクトに含まれるため追加できない旨のエラーメッセージが出るので、 入っていない訳じゃないみたいなんですが・・・

WARNING:Place:619 - This design is using a Side-BUFG site due to placement constraints on a BUFG, 
DCM, clock IOB or the loads of these components. It is recommended that Top and Bottom BUFG sites 
be used instead of Side-BUFG sites whenever possible because they can reach every clock region on 
the device. Side-BUFG sites can reach only clock regions on the same side of the device and also 
preclude the use of certain Top and Bottom BUFGs in the same clock region.

これも要チェック。

ISE 12.1 にバージョンアップしてプロジェクトを作り直した

# ISE のバージョンアップの前に 電気回路/HDL/Xilinx ISE 12.1で気づいた点 を # ご覧いただくと良いかもしれません

# 結局、上記の問題はバージョンアップだけでは解決しませんでした

完全に手詰まりになったので、ISE を現時点で最新版の 12.1 にバージョンアップして、 プロジェクトを作り直したところ、、、状況が改善しました?!

エラーが出るのは変わらないものの、Post-PAR Static Timing Analysis で正しそうなタイミングが表示されるようになりました。

バージョンアップとプロジェクト作り直しのどちらが効いたのかは不明です。

ピン配置その他の制約が効いて無さそうなのは変わりません。

double_ff にパラメータを持たせたら TIG が効かなくなっていた

#この項の内容は 電気回路/HDL/コード中に制約を書くときの注意点#j9993f39 にまとめました

上記のおかしな挙動は、この1つのバグから派生していたのかもしれません。

今設計中の回路には、複数のクロックドメインがあるため、 それらの間で信号を受け渡すのに、自分で作った double_ff というモジュールを多用しています。

このモジュールはとても役立ってくれていたのですが、 今回一つ不満な点が出てきてパラメータを追加する改良を行いました。

というのも、上記リンクからたどれるバージョンでは コンフィグレーション直後の出力値がゼロ固定になっているため、 初期値を 1 にしたい場合に手段がなかったのです。

そこで、宣言を

LANG:verilog
module double_ff #(INIT=1'b0) (
    input rst,
    (* IOB="FALSE", TIG="TRUE" *) input idata,
    input oclk,
    output odata
);

の形にして、INIT の値を外部から渡せるよう改良したのでした。

ところがこの改良により、なぜか idata に付けてある TIG 制約を PAR が正しく解釈しなくなってしまい、 結果として数多くのタイミング違反が出力されていました。

map はこの TIG を正しく解釈していたようなので、 正しく解釈できない par との間で齟齬が生じて 予測不可能な動作をしていたのではないかと思います。

これって、ツールのバグ・・・なんですかね。 もし本当なら、かなり影響が大きそうなのですが。 というのも、たぶん TIG だけじゃないですよね、無視されるのは。

後で再現可能なら検証用ソースファイルを上げようと思います。
電気回路/HDL/コード中に制約を書くときの注意点#j9993f39 にまとめました

さしあたり、初期値が 1 の double_ff1 を別に作って凌ぐことにしました。

理由が MIG とは関係ないところにありそうなのが分かったので、 この件については後でページを移すと思います。

で、MI コアのピン配置などの制約が効いていない問題はまだ残っています。

ucf ファイルの内容が効かない => par/readme.txt を読め

上記手順で MIG により生成したコアには ddr2mi.udf という制約ファイルが含まれていて、 ピン配置や、そのピン配置に最適化された内部プリミティブの配置指定が書かれています。

CoreGen で生成される他のコアと同様に、既存のプロジェクトへ、[New Source ...] として MI を生成すると、多数の *.v ファイルと共に、制約ファイルである ddr2mi.udf もプロジェクトに組み込まれます。

しかし、いざ合成してみるとそこに書かれた制約が正しく効いていないように思えます。 具体的には上記のように MAP 時にエラーが出たりします。

この問題については、MIG によって生成された par/readme.txt を読んだら解決しました。

MIG は、他の CoreGenerator とはかなり異なる目的のコードを生成します。
すなわち、プロジェクトの一部で使うための IP コアを生成するのではなく、 メモリインターフェースを中心に据えた "プロジェクト全体" を生成するのです。

つまり、既存のプロジェクトに MIG で作ったモジュールを組み込むのとは逆に、 MIG で作ったプロジェクトファイルに自分のモジュールを追加していく形で 使うことが想定されています。

今更ながら readme.txt を読み、この事実にかなり驚かされました。

で、おそるおそる覗いてみると、、、 やっぱり FPGA の部屋 にはちゃんと書かれていました。
http://marsee101.blog19.fc2.com/blog-entry-1451.html

先に FPGA の部屋 で勉強しておくのだった、と後悔先に立たず、です。

なんでこうなっているのか

インスタンスに対する細かい配置制約を .ucf ファイルで行おうとすると、 対象となるインスタンスを、トップモジュールからのフルパスで指定する必要があります。

したがって、MIG のように特定のインスタンスを配置制約でがちがちに固定する .ucf が必要な IP コアは、トップからの階層やインスタンス名が完全に分かっていないと 生成できない、ということみたいです?

このあたりは、verilog あるいは配置制約言語の記述力不足が非常に強く表れていて、 FPGA 開発を始めたばかりの私には、どうしてこんな状態のままになっているのか、 どうも理解できないところです。

既存のプロジェクトに無理矢理組み込む

すでに自分のコードが大きくなっているので、なんとかそちらに合わせ、 MIG の掃き出したコードを、自分のプロジェクトに組み込むため、 以下の手順を踏みました。

  • MIG で作った ddr2mi.xco をプロジェクトから Remove
  • ipcore_dir/ddr2mi/user_design をプロジェクトディレクトリ直下にコピーして ddr2mi という名前に変更
  • ddr2mi/par の ddr2mi.ucf を ddr2mi/ddr2mi.ucf にコピーし、以下の変更を加える
    • reset_in_n に関する行を2行コメントアウト
    • cntrl0_ をすべて削除
    • "top_00/ を "ddr2_sdram/ddr2mi/top_00/ に置き換え
    • "infrastructure_top0/ を "ddr2_sdram/ddr2mi/infrastructure_top0/ に置き換え
    • "clk_int" を "ddr2_sdram/ddr2mi/clk_int" に置き換え
    • "clk90_int" を "ddr2_sdram/ddr2mi/clk90_int" に置き換え
  • ddr2mi/rtl/ddr2mi_parameter_0.v を除く ddr2mi/rtl/*.v ファイルと ddr2mi/ddr2mi.ucf ファイルをプロジェクトに追加

で、その他の回路と MI モジュールとを HDL で繋いでインプリメントしたところ、 上のようにして作った .ucf についてたくさんエラーが出てしまいました。

ucf 中で NET で指定している制約、例えば

LANG:xilinx_xcf
NET "ddr2_sdram/ddr2mi/clk_int" TNM_NET = "clk0";

のような制約について、ddr2_sdram/ddr2mi/clk_int などという NET は存在しない、というようなエラーが多数出ます。

これは、ddr2_sdram/ddr2mi/clk_int は clock_gen/clk_125MHz_pre Net の一部に含まれていて、ddr2_sdram/ddr2mi/clk_int という名の Net が存在しないためです。

ucf を正しく効かせるには、

LANG:xilinx_xcf
NET "clock_gen/clk_125MHz_pre" TNM_NET = "clk0";

などと、階層構造の平坦化後、そして最適化後のネット名を書かなければなりませんが、 数が多いのでとても大変です。また、上流の回路を変更したり、 論理合成のオプションを変更するだけでも、合成後のネット名が変わってしまい、 エラーになってしまいます。

Synthesis オプションの設定

上記問題を解決するため、一度は全部手で書き直したりもしたのですが、 よくよく調べてみると実は簡単に解決する方法がありました。

Synthesis のオプションで Netlist Hierarchy を Rebuild にすれば良いのです。

こうしておくと、階層構造の平坦化や最適化によりネットの構成が内部でいろいろ変わった場合にも、 もともとの Net 名で Netlist が出力されるため、.ucf に記述する制約も、もともとの Net 名でできるようになります。

そうなっていれば手で ucf ファイルを書くときにもずいぶん楽ですので、 このオプションはデフォルトで Rebuild になっていて欲しかったです。

オプションを Rebuild にしたところ、 上記のような機械的な信号名の置き換えで ucf を正しく効かすことができました。

MIG が生成したコアを使って Spartan 3A DSP から DDR2 メモリにアクセスする

ようやくコアを使うためのコードを書く段階までこぎ着けました。
(実はコードの方を先に書いてあったので、時系列的にはここに書いたのとは逆でした)

Spartan 3A DSP 1800 Starter Platform には Micron の 16bit 幅で 512Mbit の DDR2 SDRAM である MT47H32M16BM-3 が 2つ載っていて、合計で 32bit 幅 1024Mbit の構成になっています。

DDR2 メモリには最低動作周波数が規定されていて、このチップでは 125MHz となっているため、 Spartan 3 ではそれなりに気を遣わなければならない周波数ですが、 最低限この周波数でデータの受け渡しをしなければなりません。

今回の私のアプリケーションには DDR2 SDRAM を 125MHz で回すのはオーバースペックで、 また Spartan3 で 100MHz 以上を使うには、そこそこ注意して書かないといけなくなるため、 できれば 100MHz くらいで勘弁してもらいたかったのですが、 チップのデータシートがそうなっているので、仕方なくこの周波数になりました。

また、MIG のインターフェースは元クロックと 90度 および 180度 位相の異なるクロックを利用してデータの受け渡しをするように設計されていて、 そのあたりもちょっとハードルが高いです。

まあ、考え方が分かってしまえば、高速回路設計の勉強にもなっていい感じです(強がり)

メモリの構成

合計 1024Mbit で 32bit 幅なので、アドレス空間は 0x0000000 - 0x1ffffff の 25ビット幅になります。

ddr2mi_parameter_0.v を見ると

LANG:verilog
`define   ROW_ADDRESS                              13
`define   COLUMN_ADDRESS                           10
`define   BANK_ADDRESS                             2

となっていて、合計値は正しく 25 ビットになっていました。

モードレジスタの値

SDRAM チップの動作モードを指定するために初期化時にモードレジスタに書き込まれる値です。

ddr2mi_parameter_0.v によれば、

LOAD_MODE_REGISTER                       13'b0010100110010

で、これをメモリのデータシートと突き合わせると、

mode_reg.png

項目意味
Mode Register Definition00Mode register (MR)
PD Mode0Fast exit
Write Recovery0103
DLL Reset1Yes
Test Mode0Normal Mode
CAS#0
CAS Latency (CL)113
Burst Type0Sequential
Burst Length0104

という設定であることが分かります。

リセットピンには TIG を

MI には reset_in_n というピンがあるのですが、 ここに入力する信号は clk0, clk90, clk180, clk270 の全てのクロックドメインで読まれるため、 もしクロックに同期した信号を接続するのであれば、そのパスには TIG を付けておかないと、 遅延時間解析でうまく行きません。

最近、自分で各コードはすべてクロック同期リセットを使っているので、 ここでは一旦リセット信号を FF で受けた後、TIG を付けて MI に渡しました。

LANG:verilog
input wire rst180,
...

    // リセットに TIG を付けるため余分な FF を入れた
    (* TIG="TRUE" *) reg mi_reset_in;
    always @(posedge clk180)
        mi_reset_in <= rst180;
    
    ddr2mi ddr2mi ( 
        .reset_in_n                 (!mi_reset_in),

入力ピンである rst180 に直接 TIG を付けてしまうと、 上流を含めて rst180 と同一のネットすべてに TIG が波及して、 rst180 ネットが同期リセット信号として扱われなくなってしまうと考えて、 このようにしています。=> TIG は出力ピンまで遡ってかかる

メモリの初期化

リセット直後 MI からは

  • cntrl0_sys_rst_tb
  • cntrl0_sys_rst90_tb
  • cntrl0_sys_rst180_tb

の3つのリセット信号が出力されます。

初期化コマンドの送信前にチップの初期化時間を最低限 200us 取らなければならないため、 システムリセット後 200us 程度にわたりこれらの信号は 1 の値を取ります。

これらすべてが 0 に下りたのを確認した後、 negedge clk0 に同期して mi_command に Initialize memory (3'b010) を1クロックだけ送り、 init_done が上がるのを待てば初期化が終了します。

LANG:verilog
   wire mi_rst_any_async = mi_rst0 | mi_rst90 | mi_rst180 | !dcm_lock;
   double_ff #(1) mi_rst_ff ( .idata(mi_rst_any_async), .oclk(clk180), .odata(mi_rst_any) );

   always @(posedge clk180) begin
       if( rst180 | mi_rst_any ) begin
           mi_command <= miNop;
           state <= stReset;
       end else
       case (state)

           stReset : begin
                   mi_command <= miInit;
                   state <= stInit;
               end

           stInit : begin
                   mi_command <= miNop;
                   if (mi_init_done)
                     state <= stIdle;
               end

           stIdle : begin

シミュレーション時にはこの 200us を待つのが苦痛になるため、 MI のコンパイル時に simulation という定数を `define しておけばこの 200us (実際には 125MHz 動作時に 280us 程度) を待たずに済むようになっています。

ISE から ModelSim を使ってシミュレーションするのであれば、 "Simulate Behavioral Model" などの上で右クリックして、 [Process Properties...] から [Other VLOG Command Line Options] に "+define+simulation" ("" は除く) を記入しておくのが手軽です。

読み書き

32bit のメモリにアクセスする場合、MI とは 64bit のデータをやりとりします。

書き込みでは、上位 32bit が先に、下位 32bit が後に、デバイスに送られます。

読み込みでは、先に受信した方を上位に、後に受信した方を下位に詰めて渡されます。

アドレスは clk0 の negedge に同期して、
データは clk90 の posedge に同期して、
やりとりされることになっています。

この取り決めに始め戸惑いましたが、よくよくタイミングを見てみると難しいことはないようです。

  • ステートマシンは negedge clk0 同期で作成する
    • read も write も、アドレスや burst done の出力タイミングは同じでよい
  • データを扱う posedge clk90 ドメインは
    • read では data_valid だけ見てデータを取り込めばよい
    • write では cmd_ack が立った次のクロックからデータを書き出せばよい

という単純な話でした。

write の際に、 negedge clk0 で遷移する信号を
posedge clk90 ドメインで読み取る必要が生じるのですが、
fifo をうまく使えばそのままで十分にセットアップ時間が取れるため、 普通に書いてしまって大丈夫でした。

ツールが位相違いのクロックをちゃんと認識してセットアップ・ホールド 時間解析をしてくれるので、こういう使い方ができるのですね。

クロック周期よりも短い時間単位の最適化が必要になるような 高速回路の設計について、その片鱗が見えた気がしました。

バンクアドレスをLSBに渡す

MIへのアドレス指定では、なぜかバンクアドレスが最下位に来るので、 通常のアドレス指定の最上位2ビットを最下位へ移す変換が必要でした。

LANG:verilog
   wire [ADDR_BITS-1:0] mi_addr_pre;
   assign mi_addr = { mi_addr_pre[ADDR_BITS-`BANK_ADDRESS-1:0],
                      mi_addr_pre[ADDR_BITS-1:ADDR_BITS-`BANK_ADDRESS] };

Data Mask を出力する

今回IP生成時には Data Mask を使わない設定にしていたため、 IPの入出力ピンに Data Mask が現れません。 代わりに自分で Data Mask ピンに値を出力しなければならないようです。
(始めこれに気づかず苦労しました)

メモリのマニュアルを見ると、マスクを使わない場合には Data Mask ピンに0を入れておけば良いそうです。

LANG:verilog
   output wire [4:0]    ddr2_dm
...

   assign ddr2_dm = 4'b0000;

動いているようです

Verilog コード

以下のコードで何とか動くようになりました。

と言っても、苦労したのは主にコードを書く部分以外だったわけですが。

LANG:verilog(linenumber)
`include "ddr2mi_parameters_0.v"

module ddr2_sdram #(
    parameter BURST_LENGTH      = ((`LOAD_MODE_REGISTER & 3'b111) == 3'b010) ? 4 : 8,
    parameter ADDR_BITS         = `ROW_ADDRESS+ `COLUMN_ADDRESS +`BANK_ADDRESS,
    parameter DATA_BITS         = `DATA_WIDTH,
    parameter DATA_BITS2        = `DATA_WIDTH * 2,
    parameter BLOCK_SIZE_BITS   = 9    // ワード単位
) (
    input wire           clk0,
    input wire           clk90,
    input wire           rst180,
    input wire           clk180,
    input wire           dcm_lock,

    // DDR2 interface
    inout  wire [DATA_BITS-1:0]          ddr2_dq,
    output wire [`ROW_ADDRESS-1:0]       ddr2_a,
    output wire [`BANK_ADDRESS-1:0]      ddr2_ba,
    output wire                          ddr2_cke,
    output wire                          ddr2_cs_n,
    output wire                          ddr2_ras_n,
    output wire                          ddr2_cas_n,
    output wire                          ddr2_we_n,
    output wire                          ddr2_odt,
    input  wire                          rst_dqs_div_in,
    output wire                          rst_dqs_div_out,
    inout  wire [`DATA_STROBE_WIDTH-1:0] ddr2_dqs,
    inout  wire [`DATA_STROBE_WIDTH-1:0] ddr2_dqs_n,
    output wire [`CLK_WIDTH-1:0]         ddr2_ck,
    output wire [`CLK_WIDTH-1:0]         ddr2_ck_n,

    // 外部回路とのインターフェース
    // posedge clk90 domain

    output wire                  rst,
    output wire                  clk,
    output wire                  busy,
    input wire                   read_req,
    input wire                   write_req,
    (* TIG="TRUE" *) input wire [ADDR_BITS-BLOCK_SIZE_BITS-1:0] block,
    output wire                  done,
    input wire  [DATA_BITS2-1:0] fifo_o,
    (* REGISTER_DUPLICATION="YES" *) output reg fifo_re,
    output wire [DATA_BITS2-1:0] fifo_i,
    output wire                  fifo_we
);
    wire                    mi_init_done;
    reg  [2:0]              mi_command;
    wire                    mi_command_ack;
    wire [ADDR_BITS-1:0]    mi_addr;
    wire                    mi_aref_req;
    wire                    mi_aref_done;
    wire                    mi_burst_done;
    wire                    mi_data_valid;
    wire [DATA_BITS2-1:0]   mi_odata;  // posedge mi_clk90 domain
    wire [DATA_BITS2-1:0]   mi_idata;  // posedge mi_clk90 domain
    
    wire                    mi_rst0;
    wire                    mi_rst90;
    wire                    mi_rst180;
    wire                    mi_rst_any;

    // リセットに TIG を付けるため余分な FF を入れた
    (* TIG="TRUE" *) reg mi_reset_in;
    always @(posedge clk180)
        mi_reset_in <= rst180;
    
    // MI がリセット状態にある間1
    wire rst_any = mi_rst0 | mi_rst90 | mi_rst180 | !dcm_lock;
    double_ff #(1) mi_rst_ff (
        .idata(rst_any),
        .oclk(clk180), .odata(mi_rst_any)
    );
    
    ddr2mi ddr2mi ( 
        .reset_in_n                 (!mi_reset_in),
        .clk_int                    (clk0),
        .clk90_int                  (clk90),
        .dcm_lock                   (dcm_lock),

        .cntrl0_sys_rst_tb          (mi_rst0),
        .cntrl0_sys_rst90_tb        (mi_rst90),
        .cntrl0_sys_rst180_tb       (mi_rst180),
        .cntrl0_clk_tb              (),
        .cntrl0_clk90_tb            (),

        .cntrl0_ddr2_dq             (ddr2_dq),          // to memory
        .cntrl0_ddr2_a              (ddr2_a),           // to memory
        .cntrl0_ddr2_ba             (ddr2_ba),          // to memory
        .cntrl0_ddr2_cke            (ddr2_cke),         // to memory
        .cntrl0_ddr2_cs_n           (ddr2_cs_n),        // to memory
        .cntrl0_ddr2_ras_n          (ddr2_ras_n),       // to memory
        .cntrl0_ddr2_cas_n          (ddr2_cas_n),       // to memory
        .cntrl0_ddr2_we_n           (ddr2_we_n),        // to memory
        .cntrl0_ddr2_odt            (ddr2_odt),         // to memory
        .cntrl0_rst_dqs_div_in      (rst_dqs_div_in),   // to memory
        .cntrl0_rst_dqs_div_out     (rst_dqs_div_out),  // to memory
        .cntrl0_ddr2_dqs            (ddr2_dqs),         // to memory
        .cntrl0_ddr2_dqs_n          (ddr2_dqs_n),       // to memory
        .cntrl0_ddr2_ck             (ddr2_ck),          // to memory
        .cntrl0_ddr2_ck_n           (ddr2_ck_n),        // to memory

        .cntrl0_burst_done          (mi_burst_done),
        .cntrl0_init_done           (mi_init_done),
        .cntrl0_ar_done             (mi_aref_done),
        .cntrl0_user_data_valid     (mi_data_valid),
        .cntrl0_auto_ref_req        (mi_aref_req),
        .cntrl0_user_cmd_ack        (mi_command_ack),
        .cntrl0_user_command_register(mi_command),
        .cntrl0_user_output_data    (mi_odata),
        .cntrl0_user_input_data     (mi_idata),
        .cntrl0_user_input_address  (mi_addr)      
    );

    // **** request / done のクロックドメイン間受け渡し

    wire clk180_done;
    wire clk180_read_req;
    wire clk180_write_req;
    double_ff read_req_ff  (.idata(read_req),  .oclk(clk180), .odata(clk180_read_req) );
    double_ff write_req_ff (.idata(write_req), .oclk(clk180), .odata(clk180_write_req));
    interclock_trig done_trig (
        .iclk(clk180), .itrig(clk180_done),
        .oclk(clk90), .otrig(done) );

    // **** アドレス信号

    // bank address を LSB's に出力しなければならないので、最上位を最下位に移す
    reg [BLOCK_SIZE_BITS-1:0] addr;     // ブロック内での相対アドレス
    wire [ADDR_BITS-1:0] mi_addr_pre = { block, addr };
    assign mi_addr = { mi_addr_pre[ADDR_BITS-`BANK_ADDRESS-1:0],
                       mi_addr_pre[ADDR_BITS-1:ADDR_BITS-`BANK_ADDRESS] };

    // **** MIG へのコマンド

    parameter 
        miInit      = 3'b010,
        miWrite     = 3'b100,
        miRead      = 3'b110,
        miNop       = 3'b000;
//        miPrecharge = 3'b001;
        
    /* include in udo
    radix define Ddr2SdramStates {
        0 "Reset" -color white,
        1 "Init" -color yellow,
        2 "Idle" -color yellow,
        3 "Command" -color yellow,
        4 "WaitAck1" -color yellow,
        5 "Addr0" -color yellow,
        6 "Addr1" -color yellow,
        7 "Done0" -color yellow,
        8 "Done1" -color yellow
        9 "WaitAck0" -color yellow,
        -default decimal
        -defaultcolor purple
    }
    */

    // **** ステートマシン

    parameter 
        stReset    =  0,
        stInit     =  1,
        stIdle     =  2,
        stCommand  =  3,
        stWaitAck1 =  4,
        stAddr0    =  5,
        stAddr1    =  6,
        stDone0    =  7,
        stDone1    =  8,
        stWaitAck0 =  9;

    reg [3:0] state;
    wire cmd_is_write = !mi_command[1];

    always @(posedge clk180) begin
        if(rst180 | mi_rst_any) begin
            state <= stReset;
            mi_command <= miNop;
            fifo_re <= 0;
        end else
        case (state)

            stReset : begin
                    mi_command <= miInit;
                    state <= stInit;
                end

            stInit : begin
                    mi_command <= miNop;
                    if (mi_init_done)
                      state <= stIdle;
                end

            stIdle : begin
                    addr <= 0;
                    if ( mi_aref_req && !mi_aref_done )
                        ; // wait until auto refresh is done
                    else
                    if ( clk180_write_req ) begin
                        mi_command <= miWrite;
                        state <= stCommand;
                    end else 
                    if ( clk180_read_req ) begin
                        mi_command <= miRead;
                        state <= stCommand;
                    end
                end

            stCommand:
                // 最低でも1クロック command_ack は立たないはず
                state <= stWaitAck1;
                
            stWaitAck1:
                if (mi_command_ack) begin
                    state <= stAddr0;
                    if (cmd_is_write)
                        fifo_re <= 1;
                end

            stAddr0: 
                state <= stAddr1;
                
            stAddr1: begin
                    addr <= addr + BURST_LENGTH;
                    if ( addr == 2**BLOCK_SIZE_BITS - BURST_LENGTH ) begin
                        fifo_re <= 0;
                        state <= stDone0; // 最終アドレスの転送が終了した
                    end else
                        state <= stAddr0;
                end
                
            stDone0: begin
                    mi_command <= miNop;
                    state <= stDone1;
                end
                
            stDone1:
                state <= stWaitAck0;
            
            stWaitAck0: 
                if ( !mi_command_ack ) // コマンド終了を待つ
                    state <= stIdle;
                
            default:
                if ( mi_command == miNop )
                    state <= stIdle;
                else begin
                    fifo_re <= 0;
                    state <= stDone0;
                end
        endcase
    end
    assign mi_burst_done = state == stDone0 || state == stDone1;

    // **** 外部コードとのインターフェース
    
    assign clk180_done = (state == stWaitAck0) && !mi_command_ack;
    assign clk = clk90;
    assign rst = mi_rst90;
    double_ff busy_ff (.idata(state != stIdle), .oclk(clk90), .odata(busy));

    // **** データの入出力 (clk90 ドメイン)
    
    assign fifo_i = mi_odata;
    assign fifo_we = mi_data_valid;
    assign mi_idata = fifo_o;
    // fifo_re は遅延を少なくするため reg にした
//    assign fifo_re = cmd_is_write && ( state == stWaitAck0 || state == stWaitAck0 );

endmodule

この他、トップモジュールに Data Mask 出力

LANG:verilog
   output wire [4:0]    ddr2_dm
...

   assign ddr2_dm = 4'b0000;

および、それに対応する ucf

LANG:xilinx_xcf
NET "ddr2_dm[0]" LOC = V2 | IOSTANDARD = LVCMOS18 | SLEW = FAST;	# LDM0
NET "ddr2_dm[1]" LOC = V1 | IOSTANDARD = LVCMOS18 | SLEW = FAST;	# UDM0
NET "ddr2_dm[2]" LOC = R2 | IOSTANDARD = LVCMOS18 | SLEW = FAST;	# LDM1
NET "ddr2_dm[3]" LOC = M6 | IOSTANDARD = LVCMOS18 | SLEW = FAST;	# UDM1

が必要です。

使い方

使い方は次の通りです。

  1. 内部で 電気回路/HDL/非同期信号を扱うための危ういVerilogライブラリ で紹介しているモジュール double_ff / interclock_trig を使っています。
  2. 外部の DCM で clk0, clk90 を生成し、.clk0(clk0), .clk90(clk90), .clk180(!clk0) としてクロックを与える(本当に clk180 を作って加えてしまうと逆にうまく行かないかも)
  3. clk180 つまり !clk0 に同期したリセット信号を rst180 に与える (リセット信号に時間解析が必要なければ任意のリセット信号でOK)
  4. DCM からの dcm_lock を .dcm_lock(dcm_lock) として与える
  5. モジュールの制御及びデータの入出力を行う外部回路は以下の方針で作成する
    1. clk / rst に同期した回路とする(実際には clk90 が出力される)
    2. データの書き込み・読み出しはブロック単位で行われる
    3. ブロックサイズはパラメータで指定可能で、2**BLOCK_SIZE_BITS アドレスが1ブロックとなる
    4. 読み出し・書き込みを行うブロックのアドレスを block に与える
    5. busy == 0 を確認して read_req あるいは write_req を1にすると読み出し、書き込み動作が開始する
    6. req を立てた後、busy == 1 となったら、連続してコマンドを送ってしまわないよう速やかに req を下げる
    7. 書き込み時のデータ転送は fifo_o/fifo_re を用いて行う
    8. 読み出し時のデータ転送は fifo_i/fifo_we を用いて行う
    9. 読み出し・書き込み動作が終了すると1クロックの間 done が立ち、busy が0に戻る
    10. done が立ったのを見てから req を下げたのでは連続して次の読み書きが行われてしまうので注意
  6. 上記コードは BURST_LENGTH == 4 用です。
    パラメータ部分を見ると 8 でも動きそうに見えますが、実際には手抜きをしているためできません。 手動で変えるのであれば stAddr を 0,1 の2つではなく 0,1,2,3 の4つとすれば良いですし、 addr の LSB を使ってカウントすれば、どちらでも動くコードにするのもそれほど難しく無さそうです。
  7. fifo_re => fifo_o => MIG モジュール のデータパスは clk0 ドメインから clk90 ドメインへのパスであるため、clk0 や clk90 の周期の 3/4 しか猶予が無く、 遅延量に対する要求がちょっときつめです。fifo_re を clk90 ドメインの FF とすればより外部回路に易しいモジュールになるのですが、fifo_re を下げるタイミングを作るのがちょっとだけ面倒だったので(たいしたことはないのですが) 現在の形になっています。
  8. MI からオートリフレッシュが要求されたときに、バースト転送を中断してリフレッシュの完了を待つ というロジックを組み込んでいないので、BLOCK_SIZE_BITS があまり大きいとオートリフレッシュが 正しく行えない可能性があります。
    メモリの仕様としては、オートリフレッシュは完全に周期的に行う必要は無くて、 一定時間に行わなければならない回数が決まっているだけなので、 1ブロックの転送時間がMIG のオートリフレッシュ間隔を越えない限り 問題にはならないと思っています。 BLOCK_SIZE_BITS はそのあたりを基準にして選択して下さい。

メモ

  • Invalid property "SYN_USEIOFF 1" というワーニングは XST が Synplify 用の制約である SYN_USEIOFF を認識できないために出るものなので、無視して良さそうです。

MIG とのデータのやりとりを行うための 64bit 幅 FIFO について

32bit 幅の DDR2 メモリにアクセスする MIG と効率的にデータをやりとりするには、64bit 幅の FIFO が必要になります。

64bit 幅の FIFO の実現方法としては、

  • 32bit幅 x 512長 の Block RAM を2個使い、64bit幅 x 512長 の FIFO を作る
  • 64bit幅 x 16長 の Distributed RAM をいくつか繋げて 64bit幅 x (16 x n)長 の FIFO を作る

のどちらかが一般的なのではないかと思います。

ここで議論したいのは、どちらを使っても、 それを構成するためのリソース消費量が馬鹿にならないという話です。

まず、Block RAM は単体では最大で 36bit 幅までしか取れないため、 64bit 幅の FIFO を作るには必ず2個以上を束ねて使わなければなりません。

SDRAM にアクセスする可能性のある IP が複数個あれば、その数だけ FIFO を用意して、 arbiter で切り替えながら順番に SDRAM を使うことになりますので、 それら1つ1つの接続に Block RAM を2個ずつ割り当てるとすれば・・・

例として、4つの周辺機器がそれぞれ SDRAM を読み書きすることを考えます。 4つに対して読みと書きで2つずつ、合計8本の FIFO が必要となり、 1本あたり2本の Block RAM を使いますから、全部で 8x2=16 個もの Block RAM を消費してしまいまうことになります。 これは Spartan 3A DSP - 1600A の84個の Block RAM の 19% にあたります。

かといって、これらの FIFO を Distributed RAM で作るとなると、それはそれで多くの LUT を消費することになります。Dual port の Distributed RAM は1ビットx 16深度のメモリを確保するために LUT を2個 と FF 1個を消費します。 大まかには、16ビットで1スライスを完全に占有すると考えて良いようです。 (入出力でクロックが異なる非同期 FIFO の場合には、 FF を LUT と別のスライスに入れなければならないため、 さらに消費リソース量が増えます)

FIFO の深度を最小限の 16 に取った場合、64bit x 16 = 128byte ですので、 これ以下のデータ量、例えば 64 byte を単位として SDRAM にアクセスしなければ なりません。DDR2 だと 128byte の転送に8クロックしか掛かりませんので、 この単位でメモリアクセスをしたのではかなりのオーバーヘッドが生じてしまいます。

さらに、転送帯域の問題もさることながら、DRAM ではリフレッシュを行っている間 メモリにアクセスできないため、その間にバッファがオーバーフローしないだけの バッファサイズが必要であることを考えると、もっと大きなサイズが必要となる 場合が多いのではないかと思います。

バッファサイズが 128byte ならば全部で8x64 = 512個のスライスを 消費するだけで済みますが、バッファサイズを4倍にして 512byte の FIFO を取ることにすると、2048個のスライスを消費してしまいます。 これは Spartan 3A DSP - 1800A のスライス総数 16,640 の 12.3% にあたりますので、 かなりの大盤振る舞いと言えます。

2倍クロックで動作させる?

使用するリソース量を少なくするため、次のようなことを考えました。

Block RAM 1個で 32bit x 512 の非同期 FIFO を作ります。

そして、読みか書きか、どちらか片一方を2倍のクロックで動作させれば、 1クロックで2ワード分読み出したり、書き込んだりできますので、 32bit で入れて 64bit で出すとか、その逆を行うとかいった具合に、 非対称 FIFO として使えるものができそうです。

で、良いアイディア!と思っていろいろ試してみたのですが、 メモリクロックの2倍で 64bit FIFO を動作させるのは Spartan 3A DSP にはちょっと荷が重そう、 というか・・・かなり無謀?というのが現在までに得た感触です。

Block RAM 自身はスペック通り 250MHz 程度でも十分動作するのですが、 32bit と 64bit との間で変換する所で、配線リソース不足が顕著になって、 配線遅延に足を引っ張られてしまいます。

ベースクロックを 100MHz まで落とせば (2倍クロックが 200MHz)、 まずまず余裕を持って動作させることができそうなのですが、 DDR2 のメモリコントローラは、動作クロックを 125MHz 以下にできないので、ちょっと動作周波数が足りないです。

さらに非同期 FIFO を噛ます?

ということで、現在検討中なのは 100MHz 動作 ( 内部で2倍の 200MHz 動作) の Block RAM FIFO と 125MHz で動作するメモリコントローラとの間に、 さらに 16bit 深さの非同期 Distributed RAM FIFO を噛ますという逃げ道です。

16bit 深さの FIFO 単体からデータを読み出すのであれば、 16個読み出したらデータは底を突いてしまうのですが(あたりまえ)、 後ろから 100MHz でデータが流し込まれる形になっていると、 5個のデータを取り出す間に4個が補給されるため、 空になるまでに理論的には (16-1)x5+1=76 ワードを取り出せます。1ワードが 64bit = 8byte なので、 一度に 608 バイトを転送できるはずです。 (実際にはもう少し少なくなりそうですが、512byte は行けそう、ですよね。 ) ← 行けませんでした(泣

この方法、現在開発中のシステムのメインのデータバスがたまたま 100MHz なので、かなり便利に使えそうです。システムのベースクロックが 100MHz でない場合には、反対側にも非同期 FIFO が必要になってしまいます。 反対側の非同期 FIFO は 32bit 幅でよいのですが、 それでもそこそこのリソースが追加で必要になってしまうと思います。

そんなにうまくは行かない

実際にやってみたところ、非同期 FIFO ではポインタの受け渡しに時間が掛かることが災いして、 理論値とはほど遠い成果しか得られませんでした。

上記の条件で、一度に転送可能なのは大体41〜43くらいで、 かなり早い段階で empty あるいは full のフラグが立ってしまうようです。

余裕を見つつ、切りの良い値を取れば 32 となって、 一回あたり 256 バイトを転送できることになります。 このあたいは・・・単純計算では深さ16の単純な FIFO と比べて2倍にしかなりません(泣

うーん、うまく行かない物ですね。

まあ、バッファのサイズとしては桁違いに大きくなっているため、 アクセスが集中してもバッファーオーバーフローが起きる心配がないことを考えれば、 Distributed RAM のみの時とは比べものにならない改善があるのですが、 それにしても苦労した割に報われない感はぬぐいきれません。

現状ではこれ以上良い案が浮かばないので、当面はこれでいこうと思っています。

まだシミュレーション&論理合成を試しただけなので、 実機に載せて動くかどうかは今後のお楽しみ(?!)です。

現状

上記のように、連続して転送できるデータ数に制限のある、 おかしな FIFO ができあがったわけですが、 リソースの消費利用は次の通りでした。

32bit @ 100MHz => 64bit @ 125MHz64bit @ 125MHz => 32bit @ 100MHz
Number of Slices166251
Number of Slice Flip Flops122158
Number of 4 input LUTs204259
Number of BRAMs11

Slice 数は合成の仕方でいくらでも変わるのであまり当てになりませんが、 LUT と FF の数が大体の目安になります。

もし 64bit x 32 の FIFO を作れば、 ストレージとしてだけで 64 x 2 x 2 = 256 個の LUT を消費するので、 それと比較すれば及第点と言うことで。

もっと良い方法があるでしょうか?

実はもっと良い方法がある、 というような情報があれば、是非とも教えて下さい(汗

コメント




DP-RAMぢゃダメれすか?

[アプロ] (2010-08-07 (土) 18:49:12)

DP-RAMを使って、クロックの違うデータのやり取りを行ったりしますが(^^ゞ
まあ、FIFOと同じ動作になるんですけどね(笑)
・ポインタのコントロールが自由にできる
・ポート幅が自由に設定可能
・あとなんだろう・・・

  • そうか、読み出し部分を汎用の FIFO とせず、決め打ちのロジックにすることで一度に転送可能なデータ数を増やせる可能性がありますね。うまくすれば倍まで行けるかも? -- [武内(管理人)]
  • 一方で、FIFO ではだめだけれどデュアルポートRAMならポート幅を自由にできるという話はありましたっけ?64bit 幅だとやはり Block RAM x 2 を使うしかないような?? -- [武内(管理人)]
  • 今更ですが、私が FIFO にこだわっているのは開発中の回路に測定データや計算中のデータを保持するメガバイト級の FIFO を複数本持つ必要があるためでして、言われてみればあまり一般的な状況ではないのかもしれませんね。相手が CPU などで、メモリが応答するまでウェイトを入れられるのであれば、Distributed RAM を使った小さめの DP-RAM を使うのが一般的なんでしょうか。 -- [武内(管理人)]
  • で、こんなことを書いている内に、上記 FIFO を登りと下りで4本ずつ、計8本もインプリメントしたならば、配線リソースの消費量がすごいことになって、早々に破綻しそうな気がしてきました。さて、うまく行くやら行かないやら・・・ -- [武内(管理人)]

おめでとうございます

[marsee] (2010-06-10 (木) 05:16:13)

DDR2 SDRAM動作おめでとうございます。ランダムデータのバーストテストをしていないようでしたら、することをおすすめします。

  • ありがとうございます。現時点では Ethernet 越しに書いて、読んでのテストなので、まだ 2Kバイト x 100000 回程度ですがノーミスでした。後で FPGA 内にテストロジックを組んで耐久テストも考えています。ただ、これより低い頻度でエラーが出る場合かなりデバッグが難航することが予想されるので、正直テストをするのが怖いです(苦笑 -- [武内(管理人)]

DDRx SDRAM

[アプロ] (2010-06-09 (水) 07:51:24)

がんばってください \(^o^)/ <= エールです(笑)
IPを使っているので、あまりハマらないと思いますが、実機ではタイミング(SDRAMのスペック的な、または、コマンドシーケンス)関係でトラブルので、注意してください。
・初期化がうまくできない(各信号のタイミングがあっていない等)
・ライトできるのに、リードするとデータ化け(FPGA側の受信側のタイミング等)
・データをライト/リードしていると、時々データ化け(バンク管理ミス)

  • 励ましありがとうございます。私もIPを使うのだからそれほど苦労することはないだろうと高をくくっていた(まだ括ってますw)のですが、やはりやってみるといろいろあるようで、書き込み→読み出しで結果が化ける問題と戦っています。データパターンによってほぼ毎回、かなり程度規則的に、ビット単位でおかしくなるので、ucfがらみのタイミング的な問題、あるいはODTとかdqs_nを使った方が良いとか良くないとかのシグナルインテグレーション関連の問題のように思えます。少し時間が掛かるかもしれませんが、CoreGen の吐くテストベンチを動かしてみるなど、いろいろやってみようと思っています。 -- [武内(管理人)]
  • 受信側では、DQSを90℃分遅延させないと正しくデータを取り込めないと思います(確か・・・) 昔昔、DDRで苦労しました・・・遠い目(笑) -- [アプロ]
  • お陰さまでなんとか動きました。結局、基本モジュールの整備やデバッグその他でずいぶん時間を取られてしまいました(泣 -- [武内(管理人)]

ERROR:Place:962

[marsee] (2010-06-02 (水) 19:48:00)

たくさんコメント書いてスミマセン。
ERROR:Place:962 - A DCM / BUFGCTRL clock component pairは、DCMとBUFGをロックしているんですよね?ロックしている位置が悪くて専用のFASTパスを使えないと言っていると思います。一度、配置制約を外してみてはいかがでしょうか?
ISE12.1で解消しましたか?

  • いえいえ、とてもありがたいです。DCM と BUFG には配置制約を加えてませんでした。ですので不思議だ!ということになっていました。 -- [武内(管理人)]

ERROR:Par:228

[marsee] (2010-06-02 (水) 19:40:08)

ERROR:Par:228 - At least one timing constraint is impossible to meetは、たぶんロジックだけでタイミング制約の遅延値を超えているというエラーだと思います。PARでは、ロジックの遅延値に配線の遅延値を加えて最終的な遅延値を算出できます。その時に、ロジックだけで遅延値を超えているとエラーとなると思います。この場合は、できれば、IL_TIMING_ALLOW_IMPOSSIBLE という環境変数に 1 を設定せずに、clock_gen_clk_125MHz90_preが11.704ns以上のクロック周期なるようにピリオド制約を変更してはいかがでしょうか?

  • はい、制約の意味はそうなのですが、問題は MAP の出力ではエラーになっていないことでした。そうか、確かにクロック制約を緩和するのは手軽でしたね。次からはそれを試してみます。 -- [武内(管理人)]
  • あ、でもクロック制約を緩和してしまうと別の部分を含めて全体のルーティングが大きく変わる可能性が高いので、エラーの原因を探るという意味では使い方に気をつける必要がありそうです? -- [武内(管理人)]
  • クロック制約を緩和してから、PlanAheadでインプリメントの具合を見て、HDLを修正します。修正できない場合は、ロジックの配置を変更しながらインプリメントを繰り返しています。もぐらたたきになることが多いです。できればHDLでクリティカルパスを修正することが望ましいです。 -- [marsee]
  • コメントを修正します。HDLでクリティカルパスが解消できないと、正常な制約がかけられないですね。HDLを修正するしかないです。Timing AnalyzerとクロスプローブできるFPGA Editorなどを使用して、(PlanAheadも、)原因を解明してから、制約をもとに戻します。 -- [marsee]
  • 通常であれば ERROR:Par:228 への対応は、Place & Route でエラーが出る で書いたように、MAP 時のタイミングを見れば良いのだと思います。今回の場合、MAP レポートでロジック遅延に問題がないという結果だったにもかかわらずエラーになったので当惑したという話でした。分かりづらくて済みません。 -- [武内(管理人)]
  • その通りですね。了解しました。しかし、タイミングの制約を満たすのが厳しそうでうす。メーカーでDDR2 SDRAMコントローラのサンプルプロジェクトは付いていなかったのでしょうか?それがあれば、ずいぶん助けになるような気がします。 -- [marsee]
  • あ、いえ、上記 double_ff の修正を加えたところ、他のモジュールも含めてタイミング制約は始めから満たせていました。一応、シミュレーション上はこれで動きそうな様子です。今はそういうところではなく arbiter まわりのコードにばかげたバグがあるようで、そちらの特定に苦労しています。 -- [武内(管理人)]
  • とはいえ、ビヘイビャレベルのシミュレーションでは正しく動きそうに見えているので、もしかしたら何か制約が足りていないのかもしれません(汗 -- [武内(管理人)]
  • 今更ですが記録として。上記のシミュレーションで動くのに実機で動かなかった件について。TIG の及ぶ範囲を正しく理解していなかったことが原因だったみたいです。コード中に記入した TIG 制約が思った以上に広い範囲に及んでしまい、タイミングクリティカルな部分で遅延時間解析が正しく行われていませんでした。TIG 制約は可能な限り TO で指定しないと、思わぬ問題を生じてしまいそうです。 -- [武内(管理人)]

無題

[marsee] (2010-06-02 (水) 05:01:56)

頑張っていらしゃる様子が手にとるようにわかります。結構、色々なことで失敗したりすると思いますが、頑張ってください。
私もSpartan-3A Stater KitのDDR2 SDRAMコントローラをMIGで生成してみまhした。Project NavigatorからではMIGでDDR2 SDRAMコントローラは生成できませんでしたが、CoreGenから生成したらうまくいきました。
やはり、MIGにSpartan-3A Stater Kitを選択する項目があるだけあって、動いているようです。(当たり前か?)
http://marsee101.blog19.fc2.com/blog-entry-1483.html
やはり、UCFをみると相対位置制約なので、ガチガチでした。このインスタンス名を直したくないと強く思いました。

  • 書き込みありがとうございます。現在 MIG とは関係のないアービタ周りで苦戦していて読み書きテストまで進んでいません。まだ始めたばかりなのでほぼすべてのモジュールが枯れておらず、あちらを直してはこちらが動かなくなる、というようないたちごっこを続けています。何とか2〜3日中には動かしたいものです。あ、今2ワード64ビットだけ読めました(笑 -- [武内(管理人)]

添付ファイル: filemig4.png 2139件 [詳細] filemode_reg.png 2060件 [詳細] filemig6.png 1959件 [詳細] filemig2.png 2030件 [詳細]

Counter: 55113 (from 2010/06/03), today: 5, yesterday: 6