2011年2月26日土曜日

DVIに表示

文字列をDDR2 SDRAMのframe bufferに書いてDVIに表示させるprogramが出来た。



左側がMain routineで、ここでData memoryから1文字づつ文字codeをloadし、
改行やNULLでなければ文字描画routine (DRAW_CHAR)をcallする。
DRAW_CHARではR1~R8までを使用するため、出入り口でこれらを
StackにPush & Popしている。

下が動作結果だ。
Fontの品位が今一だが、ちゃんと描画&表示させることが出来た。 \(^o^)/





いつものTCM8230MDの画像表示もするようにしてみた。
極短期間だが撮像Dataの書込みと文字列描画とLine bufferの読み出しの
競合が発生する筈だが問題なく動作した。



今回のprogram作成を通してAssembly言語で書くのは面倒くさいと改めて感じた。
今後はGCCの移植に挑戦してみようかと思う。
でも、それだけだとFPGAを弄れなくなっちゃうから何かと平行してやることになるかな?

2011年2月20日日曜日

Quartusで合成

先日は、zumi32のquartus 10.1での合成に挑戦したのだがmegawizardで躓いてしまった。
ubuntu でquartus 10.1のlinux版を使用したのだが、megawizardのGUIでNext buttonを
clickしても画面表示が更新されなかった。ALTERAのsiteを見たら10.1sp1があったので
これをinstallして再度挑戦してみたのだが駄目だった。
しかたがないのでWindows版で試してみた。Windows版では問題なく操作できた。
Target deviceはEP3C16F484C6、clockは100MHzでやってみたが問題なく合成できた
(Windowsは今やKVMの仮想環境に追いやってしまっていて滅多に使わないから、
たまに立ち上げるとOSの更新が始まったり、Virus対策softのpatternの更新が始まったり、
scanが始まったりと中々やりたい事に集中できない。)

合成reportのFmax値は107.38MHzだったので少なくともその位まではいけそうだ。
Cyclone3は65nm processなのでprocess的にはXilinxのVirtex5が比較対象になる
のかなと思うが、先日の試行結果だとVirtex5は-1のspeed gradeでも160MHzまで
はMetしたのでこれと比較すると107MHzは低い気がするが、そもそも私はquartus
の使い方をまだ良く知らないので設定次第ではもっと上も狙えるのかも知れない。



2011年2月19日土曜日

LCDへの文字表示

LCDに文字列を表示するprogramが出来た。
以下がそのprogramだ。



始めにLCDの初期化を行い、次にBRAM上の文字列を1文字づつ読み出して
LCDのDDRAMに書込む。文字列を全て書込んだ後はLEDを点滅させる
無限Loopに入る。
Spartan3A StartKitのBoard User GuideではLCDのI/Fは4bit modeで
使用する例のみが記載されていたがFPGAとLCDとの接続は8bitで接続
されており、それなのにわざわざ4bitで使うのも何だかなぁ~という気が
したのでこのprogramでは8bit modeで使用している。

で、肝心の結果だが以下の様にちゃんと文字列が表示された。



たったそれだけのことだが全て1から自分で作ったCPUとAssemblerと
Programでここまで実現できたと思うと嬉しい。
次はいよいよDRAMにdataを描画してDVIから出して見よう。

2011年2月17日木曜日

zumi32の性能

これまでFPGA MakerのCPUには興味がなくて関心がなかったのだが、
今日ふと本家Xilinx製CPUの性能ってどんなんだろと興味が湧いたので調べてみた。
そしたらXilinxにはMicroblazeの他にPicoblazeというのもあるんだという事が判ったが、
そっちは8bit CPUなのね。そんなことよりも、MicroblazeはVirtex-6(-3)では
DMIPS Configurationという構成では何と307MHzで動作するらしい。
http://www.xilinx.com/tools/microblaze.htm

すげぇー、さすが御本家。

これに対して、家の子はどうなんだろかと興味が湧いたので先日試したSpartan3E用の
RTL構成でDesign propertyのDevice Familyを色々変えて合成してみた。
それぞれのDevice FamilyでTimingがMetできる上限をさぐるため何回も合成を行ったので
時間がかかってしまったが、以下のような結果を得た。



300MHzには遠く及ばない。残念。

2011年2月16日水曜日

Assemblerの変更

Assemblerは変数を定義できるのだが、型はDWのみとしていた。
しかし、LCDCに文字を表示しようとするとDHやDBやSTRING(文字列)
の型も定義出来た方が良い。ということで、Assemblerを変更した。



では、LCDCの制御programの作成にとりかかろう
って、それほど大層なもんじゃないが。

2011年2月14日月曜日

実機で動作確認

現状のDBUS ADDRESS MAPは以下のようになっている。



この中でまずは簡単な箇所からということで、LED portに
連続値を書き込む以下のprogramを実行させてみた。




結果はバッチグー(ふっ、古い)で、問題なく動作した。



次はLCDに何か文字を表示させてみよう。

2011年2月13日日曜日

zumi32の仮合成2

zumi32はどの程度の周波数までMetできるのか見てみた。
高速DRAMCの構成だと周波数を振るのは厄介なので、
以前CQ出版社の雑誌の付録についてきたSpartan3E基板用にRTLを構築し直してやって見た。
このRTLの構成要素はzumi32+UART(含むFIFO)+I2Cとした。
結果は75MHzまではMetできた。



80MHzではMetしなかったが、一番厳しかった部分はやはりbarrel shifterの経路だった。



Resourceの使用率は50%程度だ。他にGPIOとか何かIPを作って入れれば
完全originalの自分仕様のマイコンが作れそうではあるな。
そうすると、やはりzumi32用のgccは欲しいよな。。。



現在のRTLはBlock RAMを除いてXilinxのIPは使用していないので、
他社のFPGAへの移植も容易にできるだろうと思って、試しにquartus II 10.1
でCyclon向けに合成をしてみようとしたが、Memory作成(Megawizard)で
つまづいてしまった。Ubuntu上で動作させているのだが、Megawizardの
画面がNextをClickしても切り替わらない。quartusのinstallがうまく出来て
ないのかもしれないな。 今度時間ができたら再度挑戦してみよう。

2011年2月12日土曜日

zumi32の仮合成

割込みとload,store系命令の動作確認も出来たので
高速版DRAMCのRTLにzumi32を統合して合成してみた。
barrel shifterの部分で苦労するかなと思っていたのだが、
結果はtiming errorも出ること無く結構あっさりと合成できた。
やっぱ、I/Oの絡んでないRTLの合成は簡単やね~、と思ってしまった。
が、ちゃんと動作しないことには話にならないのでまだ安心は出来ない。





回路規模(resourceの使用率)はNumber of occupied Slicesで見ると、
79%になった。統合前の高速版DRAMCは67%(3988)だったので12%(680)の増加だ。
Number of Slice Flip Flopsで見ると3822から4421と599個の増加で、
32bit register約19個分になる。。。ちょっと多いかな。
BRAMは12個から19個に増加した。これは、命令用の32bit x 2048、data用の32bit x 1024
に加え、font_romとして8bit x 2048を含んでいるからだ。
(zumi32のprogramで文字を書かせてみたいと考えている。)

現状、uifのbusに命令用2port memoryとdata用2port memoryとzumi32の起動用
registerが接続されており、I2C、DRAMC、cifとvifのenable registerはzumi32の
I/O busに接続しているが、resourceにはまだ余裕があるのでこれならLCDやLED等
もzumi32から制御出きるようにしてもいいかも知れないな。

粗動作確認2

割込みとload, storeの動作を見た。

以下は割込み確認用のprogramで、Interrupt_Entryが割込み処理、
Loopがforegroundの処理になる。
programはStartでStackや変数用registerの初期化を行い、
foreground処理(Loop以降)に入る。
LoopではR4の値を0~10まで1づつ更新する処理を繰り返す。
割込みが入るとInterrupt_Entryが実行されるが、ここでは
Interrupt_Entryで使用するregisterやEPC(return address)を
stackに退避して、割込み処理を実行し、退避した値をregisterに戻し
割込み処理を抜ける。
zumi32の割込みはlevel割込みで、本来は割込み処理で(interrupt controller内の)
要因をclearして抜ける必要があるが、本試験ではtest benchでireqを一定期間だけ
1にしており、softwareでの解除処理を省略できるようにしている。
割込み処理でMAIL_BOXの値を+1して書き戻しているのは、単にdbusの内容を
loadして+1し書き戻しているだけでそれ自体には意味は無い。
foregroundで使われているR3を使って何か処理をする目的でいれている。



以下がsimulationの波形だ。
LoopではLoopの最後でR4の値が10になるかどうかを検査している。
割込みの復帰番地がずれたり、割込み処理でのstack処理が正しく行われていなければ
R4の値は最終的に10にならずzumi32はHALTするが、そういう事態は起きていない。
正しく動作している。



次にload, storeの8bit, 16bit, 32bit命令の動作確認を行った。
以下がprogramで、BRAMに32bit値0x76543210をstore後32bit load,
8bit load, 16bit loadで期待通りにreadできるかを見ている。
次に8bit store後8bit load, 32bit loadでそれぞれが期待通りにreadできるか、
同様に16bit storeを見ている。
(今回は結果は波形を見て確認することにして、programでの合否判定はしていない。)



以下がsimulationの結果の波形だ。
期待通りだ。

2011年2月11日金曜日

粗動作確認

zumi32を下図の様な構成にし、simulationで粗動作確認を始めた。



programは下図の様に機能検査を行い結果が正常なら次の検査へ分岐する。
結果が期待通りでなければ、HALT命令を実行して停止する。


上図の部分の波形は以下のようになる。


ib_renはinstruction memoryのread strobe, ib_adrはaddress, ib_datはread dataだ。
fe_enはfetch unitからexec unitへのenable、ef_rdyはexec_unitからfetch_unitへの
ready信号でこの2信号でhand-shakeを行いながら動作する。
fe_pcはaddress tagで、ef_pcはPC (program counter)の値、fe_abrkはaddress tagと
PCの値が不一致になった場合にinstruction queueをclearするための信号だ。
上記のAssemble listの通りprogramは始めに0x100に分岐するが、波形のef_pcの値も
その通りの変化をしていることが判る。


上図はADD命令によるregister間のdata copy checkだ。
これの波形は以下の通りだ。

 

以下はload, store, swap命令だ。



ということで、これまでの動作は問題なさそうだ。
引き続きload, store系の8bit, 16bit accessや割込み動作等をみて
良ければ合成してみようと思う。


2011年2月6日日曜日

Assemblerの作成

ゆくゆくはgccを移植したいと考えているが、
(出来るかも知れないな、、
出来るといいな、、
ま、チョット覚悟はしておけ、、、
今のところその程度のノリで考えている。)
いきなりgccでは難易度が高すぎるのでとりあえずは簡易AssemblerをC言語で作成した。
簡易版とは言っても、可読性の良いprogramを書きたいのでLABELや
定数の別名定義等は使えるようにした。
また、このAssembleはsimulation用のprogram作成にも使うので、
出力はBinary objectではなく数値文字列を出力するようにした。


例えば上図の左端のようなsourceをAssembleするとその右のようなListと
右端のようなobject fileを出力する。
Assembler programの行数はcommentや空行や構造体型宣言や配列定義等
ひっくるめて802行になった。programのcode正味では500行程度だろう。


2011年2月5日土曜日

CPUの設計 2

先のblogに書いたように、instruction queueは2 stage FIFOが元になっている。
下図で左側が2 stage FIFOの概略を示している。このFIFOはpipeline型の構造に
なっており、instruction queueではstg1の入力部にdecorderを追加している。



stg1出力のridx1,2,3はregister 番号で命令のREG1,2,3である。opcは命令の
operation code (bit31~28)の値、xx_opcは各命令のsub operation code
だ。tadrは現在出力している命令の番地であり、命令のtagとして使用してい
て、exec unitのPC (program counter)の値と常に比較されている。分岐命
令実行等でPCの値が更新された場合は、tadrとの比較結果が不一致になる。
tadrとPCの不一致が検出されるとinstruction queueの内容は破棄されると
共にaddress counterにPCの値がloadされ、IBUSのその番地の命令が
instruction queueにreadされる。つまり、refillが行われる。instruction
queueのrefillが完了するまでexec unitは待ち状態になる。f_xxxはone hot
のflag情報であり、f_regは演算対象がregisterであることを示す。f_r1pr3,
 f_r2pr3はridx1またはridx2が直前の命令のridx3と同じ場合に1になる。
exec unitのALU部を下図に示す。各演算の結果をそのままregister fileへの
入力とすると、組合せ論理が深くなりtimingをmetさせるのが厳しくなって
しまうため、F/Fで受けて、その出力をregister fileの入力としている。



この場合、latencyが2に増加してしまうため、例えば

   add r3,r2,r1
   add r5,r4,r3

のように直前の演算結果を次の命令で使用するような場合に不都合がでてしまう。
f_r1pr3, f_r2pr3はその様な場合に使用される信号で上図に示すとおり、信号が1
の場合、register fileのr1v, (r2v)ではなく、演算結果F/Fの値をr1v(r2v)として
ALUの入力に戻す。f_r1pc, f_r2pcはridx1またはridx2がPCを指している事を示
す信号である。通常動作ではPCとtadrの値は一致しているのでf_r1pcまたは
f_r2pcの値が1の場合はPCの代わりにtadrの値をr1vまたはr2vに出力するように
して、PCのfanoutを増加させないようにしている。

PC (program counter)
zumi32はおそらく世の中の全てのCPUがそうであるように命令空間の番地の小さ
い方から大きい方へと命令を実行していく、つまりPCの値は連続して単調増加する。
BR等命令で明示的にPCの値を書換える場合や、外部からの割込みやaddress error
等の例外時は不連続な値になる。割込みの場合は現在実行中の命令は破棄され、
PCの現在値がEPC registerに転送されPCはinterrupt vector address値に更新さ
れる。割込み処理を行うsoftwareは処理の最後でPCにEPCの値を戻すことで、
zumi32は破棄された番地から処理を再開する。こう書くと簡単な感じがするが、
実行中の命令がPCの値を更新するような命令だったり、load/store系の命令だった
り、分岐の遅延slotの命令実行時だったりした場合は、単純に行かない。例えば、
load/store系で、DBUS空間内に何らかの要因を表すstatus registerがあり、
それをreadすることで要因が0に解除されるような仕様の場合、そのregisterの
load命令実行中の割込み時、zumi32はその命令を破棄するとは言っても、DBUSで
はそのregisterに対するread transactionは実行されているため割込みから復帰
して処理を再開した時点では要因は既に解除されてしまっているといったことに
なる恐れがある。遅延slotの命令実行中の割込みも厄介だ。遅延slotの命令は言
わば幻のようなもので、その時点でPCの値は分岐先に更新されているから、ここ
で割込みを受け付けてしまうと、割込みからの復帰は分岐先であり破棄された命
令の再実行は果たせない。
以上の理由から、実行中の命令がPCの値を更新するような命令だったり、load/
store系の命令だったり、分岐の遅延slotの命令実行時だったりする場合は割込み
を受け付けないようにしている。これは割込み要求信号をmaskするのではなくて、
PCの記述でinterrupt vector addressの代入部の優先順位を他よりも低くすること
で実現している。















2011年2月2日水曜日

CPUの設計 1

命令表に一部間違いがあったので思い切って全部定義し直した。


乗除算命令は持っていない。
FPGAのDSPを使えば乗算命令の実装は出来るが、
excution unitのlatencyが(多分)1に出来なくなる事と、
FPGAのdevice依存になってしまうのが嫌だったので、
FPGAのDSPを使う案も考えられるが、DSPは入出力にF/Fを入れると
高周波数で動作できるがそれだとlatencyが1に出来なくなるし、
F/Fがない構成で使用すると遅延が大きくて駆動周波数を上げられない
といった経験を過去にしているので使用しないこととした。
また、除算はどう頑張っても1cycleで演算を行うことは出来ない気が
するので、除算器も実装しないことにした。
その代わりに32bit barrel shifterを持ち、これを利用して
乗除算はsoftwareで行う事にした。

zumi32は17個のregisterを持ちその内訳は以下のとおりだ。
  • R0 ... 常に値0を持つ
  • R1~14 ... 汎用
  • R15 ... program counter
  • EPC ... 割込み、例外発生時の戻り番地を格納する
また、flagはCarry flagとZero flagを持つ。

構造は以下のとおりだ。




















zumi32はcpu core部のみでありcacheは含んでいない。
(cacheは別設計)
zumi32内部はFETCH UNITとEXEC UNITから成り、
FETCH UNIT内部はregister file, instruction queue, address counterから成る。
instruction queueはDRAMCでも使用している2 stage FIFOを応用した物で、
後段のF/Fの入力部で命令のdecodeを行っており、後段のF/Fはこれらdecodeした
結果をone hot信号として保持したりしている。
register fileはR1からR14迄のregisterの配列だ。
execution unitは命令の実行unitでlatencyは1、つまりinstruction queueと
合わせて全体のpipeline段数は3段だ。
zumi32はload/store/swapを除いて1命令/1cycleで実行する。
分岐時は最小でも2cycle pipelineがstallする。
また、分岐は遅延分岐になる。

このzumi32は先日の133MHz版DRAMCまたは240MHz版DRAMCのdesignに統合して
動作させたいと考えていて、目標の動作周波数は60MHzだ。

しっかし、俺って文章書くの下手糞だな。もっと国語の勉強をしとくんだった。
ニポン語は苦手だ。
Verilogなら少しは出来るんだけどな。
おーるうぇいずー、あっとまーくー、ぽじえっじー、あっいけね括弧忘れた。。

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

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