2012年6月24日日曜日

cross gcc for zumi32 2

以前から自作CPU zumi32用のgccのportingに挑戦しているのだが中々捗らない。情報が乏しく無茶苦茶難しいため、少しやっては放置しそのうち忘れるといったサイクルが続いている。が、そろそろ真面目に取り組もうと思ってporting作業を再開した。 gccのCコンパイラはCソースからアセンブリ言語列への翻訳を担当し、アセンブリ言語列からマシン語列への変換はbinutilsが担当する。 binutilsのportingに関しては一応昨年の8月に終わっていたので、(ただし、今回の作業でまだ不足があることが判った。)  今回はCコンパイラ部のportingに着手した。
Cコンパイラ部のportingの本質的な部分はgcc/config/配下にzumi32用のディレクトリを作成し、そこにMachine Descriptionファイル(以下mdファイルと略す)を作成することである。 (他にconfigure関係やMakefile関係の編集も必要)
例えばCソース側で、
  a = b;
のようなプログラムがある場合に、これをcpuのどういう命令で実現するかをmdファイルに記述する。 a, b はレジスタの場合も有るし、メモリの場合も有り、其々の場合で割り当てるcpuの命令は違ってくる。 char / short / int や signed / unsigned 等の型によっても命令を使い分けなければならない場合もある。 上記のようなデータの移動は以下のような記述になる。
(define_insn "movsi_insn"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m,m,r,r,r,r")
        (match_operand:SI 1 "movsi_rhs_operand"     "m,r,r,J,K,L,U,Y"))]
  ""
  "@
   ld32     %0,%1
   mov      %0,%1
   st32     %0,%1
   st32     %0,r0
   ldsi     %0,%1
   ldui     %0,%1
   ldhi     %0,hi(%1)\n\tshli\t%0,%0,16
   ldhi     %0,hi(%1)\n\tshli\t%0,%0,16"
  [(set_attr "type" "load,arith,store,store,arith,arith,arith,arith")]
)
Machine DescriptionはRTL(Register Transfer Language)という言語というか記法で記述する。RTLの言語仕様はLispから影響を受けているらしい。 portingをする場合は、既存のcpu用のmdファイルを参考にするのが良いのは言うまでもなく資料にもそう書かれているのだが、RTLの言語仕様に関する知識がまったくない状態で、それらを見よう見まねしようとしてもほぼ無理だと思う。 各cpu固有のアーキテクチャがあり、また、書き方や構成も異なるため、定形の型が中々読み取れない。 そこで、ドキュメントを読んで学習する必要がある。ドキュメントはgccのソースツリーのgcc/doc配下にあるgccint.infoだ。 build時にmake pdfとするとgccint.pdfが作られるので、それを読むと良い。 ただ、このドキュメントが640ページもある上、ちょーーーーーー難解だ。 以前から挑戦しては挫折の繰り返しで、何回よんでも難解だ。難解だけに、・・・ぷぷっ   でも、今回はじっくり読んだこともあり、少しだけ理解が深まった気がする。また、mdファイルはLattice Mico32用のmdファイルをお手本にさせて貰った。 また、今回の作業でzumi32の命令も一部変更と追加をすることにした。 具体的には算術命令をsignedとunsigned用に分けるということと、条件分岐命令でbczとbcnzを追加等である。bcz, bnczは比較命令 ≧ や ≦ の実装の容易化の為に追加した。



悪戦苦闘の末、cc1でアセンブラソースの出力は出きるようにはなってきた。

以下ではポインタとfor ループの確認


以下では乗除算の確認、zumi32は乗除算関係の命令は持っていないので、GCCのbuiltin関数の利用しようと考えているが、自前の物に変えるかも知れない。
→ 少なくとも整数の乗除算、剰余算は自分で作る必要があるようだ。


以下はchar,short,int混在の演算の確認


以下はVRAMを単一色で塗りつぶす処理モデルをコンパイルしてみた。


zumi32(lm32等もそうだが)は32bit即値をそのままレジスタにロードすることが出来ない。これは命令を32bit固定としていることによる。そのため、32bitの即値をロードさせる場合は即値を上位と下位に分けて、ロード・合成する必要がある。上記のアセンブリ言語側のhi(...)やlo(...)はそれを表している。 上記では上位値をldsi命令でロードした後シフト命令でシフトしているが、Lattice Micoやmipsでは直接上位にロードする命令がある。 zumi32もその命令を追加すべきか思案中だ。
上記のような上位/下位に分けてロードする場合は即値は16bitの方が扱いやすいが、zumi32の即値ロード命令は20bitの即値になっており、この点も今一な仕様になってしまっている。
変えようかな。。。

portingに必要なGCCの情報を完全に理解しない状態で行っているのでゴールが近いのか遠いのかさえ見えていない状態だが、何とか完成させたい、それまでモチベーションが維持できればいいが。。。Cが使えればプログラムの作成効率は格段に上がるからなぁー。


2012年6月17日日曜日

Spartan6版 DDR2コントローラの作成 7


今回作成したコントローラのRTL等一式を以下に置いた。
http://www.hi-ho.ne.jp/bravo-fpga/


今回のDDR2-800用DRAMコントローラは800MHzという高速で動作させるためにSpartan6のISERDES2, OSERDES2, IODELAY2等のハードマクロを使用した。 そういう意味では800MHzで動いて当たり前と言えるかも知れないが、このコントローラの作成を通してこれらマクロのクセや使い方を学習することが出来た。その辺について幾つか書いておく。

今回作成したコントローラのPHYの基本的部の構造を以下に示す。 128bit版ではこの上位で100MHz/128bitと200MHz/64bitの乗替えがあるが、これは普通のverilog記述にしている。


今回は点線で囲っている範囲をdc_ddrioというmoduleにして、これをDDR2 SDRAM I/Fの全ての信号(クロック、コマンド系、データ系)で使用している。当初は、コマンド系は400MHzのレートの出力なのでIOB内のOFFを、クロックはODDRを、それぞれ使うつもりでいたのだが、OFFやODDRを通る経路とIODELAYを通る経路(データ系)の遅延差が大きい(つまりスキューが大きい)ことが判ったので、これを回避するために全信号でdc_ddrioを使うことにした。

OSERDES2
I/O SERDES2, IODELAY2はSDRモードとDDRモードでの動作が可能だが、本コントローラではSDRモードで使用しており、そのため、供給クロックはデータレートと同じ800MHzとなっている。
何故、DDRモードにしなかったかと言うと、OSERDES2がSTROBE信号をクロックの両エッジでサンプリングしてしまうからだ。 以下はXilinxのUG381に載っているOSERDES2のブロック図だ。



これによると、IOCE(STROBE)パルスが1の時(正確には1段F/Fを通っているが)にD1~D4, T1~T4をシフトレジスタにロードし、IOCEが0の時にシフト動作を行う。 ISEのインストールディレクトリ配下(ISE_DS/ISE/verilog/src/unisim/)にはシミュレーション用の各種モデルがあるが、そこのOSERDES2.vをシミュレーションして動作を見たところ、DDRモードの場合はI/Oクロックの両エッジでシフト動作が行われることが判ったが、(IOCEが1に時の)ロード動作もクロックの両エッジで行われることが判った。これだと先頭のビットが2シンボル分出力されることになりマズい。もしかすると、これはシミュレーションモデルの問題で実物の動作は違うのかも知れないが、UG381を見てもはっきりしないためSDRモードで使うことにした。

IODELAY2
UG381によると、IODELAY2はT~TOUTも遅延制御がされるように読めるのだが、シミュレーションモデルでシミュレーションして見たところ違った。TOUTは遅延制御されないようだ。念のため、IODELAY2.vをエディタで開いて確認してみたが遅延制御されない構造になっていた。これも、実際のハードは遅延制御されるのかも知れない。アンサーデータベースも検索してみたが、情報を見つけられなかったので、遅延制御されないものとして使うことにした。これが何に関係するかというと、ライト時のDQSとDQに関係してくる。ライト時はDQSとDQは90°位相をずらす必要があるが、これをIODELAY2の出力遅延で実現しようとする場合にT系が遅延が効かないと非常に制御が面倒くさいことになってしまった。そのため、DQS系、コマンド系のdc_ddrioモジュールには800MHzを反転して入力することで、90°位相差がつくようにした。 verilogのソース上はdc_ddrioのインスタンス部でクロックを反転する形になっているが、I/OSERDESやIODELAY2はクロック入力部で正転/反転が設定できるようになっているため、RTLを合成するとその設定が変わるだけだ。



一応、これでこのプロジェクトは終わりにしようと思う。


2012年6月11日月曜日

Spartan6版 DDR2コントローラの作成 6

一応、128bit,100MHz版も出来た。
合成は問題無し。

System Architectureは以下のようにした。

例によって、画像データをDRAMにダウンロードしてDVIに表示させてみた。
画像の乱れとかは無く、綺麗に表示されている。


zumi32でメモリR/Wチェックプログラムを実行させてみた様子


このプログラムを10時間程走らせてみたが問題なかった。

上の画像だけだとスピード感が無いなぁー
何か、別のプログラムを考えようかな。。。
それと、デザインの中身について殆ど書けてないので書かなきゃな。


2012年6月6日水曜日

Spartan6版 DDR2コントローラの作成 5

128bit,100MHz版に着手する前に、どの位の周波数まで動作するか見てみることにした。
Spartan6のPLLの逓倍の上限は1000MHzで、現在は800MHzなので、900MHzと1000MHzで試してみたが、両方ともタイミングがMetしなかった。そこで、FPGAのグレードを3にして(実機は2だが、3ということにして)合成してみたところ900MHzはMetした。



このbitファイルをFPGAにコンフィグレーションしメモリチェックプログラムを走らせてみたところ
エラーになったが、IODELAYの遅延を調整した結果動作するようになった。


ということで、動作マージンはありそうだ。


2012年6月5日火曜日

Spartan6版 DDR2コントローラの作成 4

800MHzでリードが上手くいかない件はバーストリードの最初の16bitが0になるというものだった。 以下はDRAMの0番地に0x12345678を書いて、IODELAY2の遅延値とリードデータのキャプチャタイミングを色々変えてみている様子だ。キャプチャタイミングは8が最適のタイミングだが、リードデータは下位16bitが0になっている。


当初はリード側を疑って上述のとおり遅延値を振ってみたりしたのだが、問題はライト側にあった。
(つまり、先頭データの書込みが出来ていない) 以下にライトのシミュレーション波形を示す。ここでデータバスにライトデータを出力する期間を正味の4バーストの期間のみとしていた。


これを以下のようにバスをONにするタイミングを早めた。
(間違えてリードの波形を貼っていたので差し替えました。2012/06/06)


その結果正常にリードできるようになった。
STA(静的タイミング解析)の結果ではトライステートコントロールパスの遅延はデータのパスとほぼ同じで問題なさそうにみえたのだが、どうも先頭データのDQSに対するセットアップが確保できていなかったようだ。 実際の信号の波形観測が出来ればいいのだが800MHzの信号を観測できる測定器など個人が持ってる訳ないので、推測するしかない。


合成も問題ない。



現状のSystem Architectureは以下のとおり。


以下はリードライトチェックプログラムを実行している様子だ。64Kワード(256KB)ブロック単位でライト&リードチェックを行っている。


ということでDDR2-800で動作できた。 やったー \(^_^)/  
うっれすぃー かあちゃん、おれ、やったよ。。。 ちょっと大袈裟か。


とはいえ、現状はローカルI/F側が64bit/200MHzとなっており、zumi32 (自作CPU)は精々100数十MHz程度でしかタイミングがMetしないので、CPUも含んだ回路にする場合は現状では周波数が高すぎて合成できない(タイミングがMetしない)可能性があるので、128bit/100MHz版も作ろうと思う。



2012年6月3日日曜日

Spartan6版 DDR2コントローラの作成 3

DDR2-800版のDDR2コントローラの作成を行っている。
この速度になると従来の設計方法ではタイミング制約を満たせない。
RTLの可搬性というか他FPGAへの移植性を失ってしまうが、PHY部はハードマクロのIODELAY2、
ISERDES, OSERDESを使わざるを得ない。しかし、このハードマクロ達がまた、使い辛い。
いやーまいった。でも、何とかPHYは出来てデバッグを行っている。
一応、PHY部800MHz (I/O SERDES部)、ローカルI/F部200MHzでタイミングはMetするが、
実機動作させると、リードデータが正しくない。  600MHzにして、IODELAY2の入力ディレイ値
を若干調整するとリードできるようになるので、800MHzでも位相調整すれば行けるかも知れないが。。。うーん、どうなんでしょう。





TE0720 No.4 (BNN-PYNQを動かしてみる 2)

TE0720でBNN-PYNQを動かすことが出来た。 以下は前回に続いてBNN-PYNQが動くまでの記録。 gdb (GNU debugger)で例外が出る原因を調べてみた。 例外が発生しているのはシェアードライブラリ(python_hw-cnv-pynq.so)の中であ...