さてさて。前回までにマイコンと呼ぶのに必要最小限のユニットを設計して揃えてきました。ここまで、iverilogを使ったシミュレーションだけで動作を確認してきたのだけれど、今回はいよいよFPGAボード(Sipeed Lichee Tang Primer)に書き込んで、実際の動作を確認してみます。
必要な部品は完成しているので、最上位モジュールにインスタンスするだけです。以下のバナーから、ダウンロードできるようにしておきました。
プロジェクトの作り方や操作方法等については、こちらをご参照ください。
クロックは1系統しか使っていないので、タイミング制約はシンプルです。こちらと同じ制約をかけておきます。以下、タイミング制約ファイル(MCOC115.sdc)の内容です。
#Created Clock
create_clock -name clk -period 40 -waveform {0 20} [get_ports {clk}]
I/O制約については、各使用者の使い易い様に適切な端子に割り当てていただければ結構なのですが、今回の実験ではオンボードのRGB LEDを使うのと、UARTでシリアルモニタと通信する実験があるので、最低限、以下の制約をかけてください。
最上位モジュール(mcoc115mn0408.v)の信号名で記します。
以下が今回の実験で最小限必要なI/O制約ファイル(MCOC_blink.adc)の中身です。
set_pin_assignment { clk } { LOCATION = K14; }
set_pin_assignment { reset_n } { LOCATION = K16; }
set_pin_assignment { uart_rxd } { LOCATION = R7; IOSTANDARD = LVCMOS33; }
set_pin_assignment { uart_txd } { LOCATION = T7; IOSTANDARD = LVCMOS33; }
set_pin_assignment { port_out[0] } { LOCATION = P13; }
set_pin_assignment { port_out[1] } { LOCATION = R3; }
set_pin_assignment { port_out[2] } { LOCATION = J14; }
その他、明示的に割り当てなかった端子は、コンパイル時自動的に適切な空き端子に割り当てられる様です。ピンヘッダに引き出されていない端子に割り当たってしまうこともあるので、必要な端子は全てI/O制約で指定しないとなりません。
各自、必要に応じてI/O制約を追加して使ってください。
Moscoviumマイコンの全体結合テスト環境も、以下のデザインデータダウンロードページに登録しておきました。
実験サンプル:【 】内はCygwinシェルウインドウからの実行コマンド
【cd MCOC115/simulation】
【iverilog -f test.lst ; if ( ! $status ) vvp a.out】
:
VCD info: dumpfile test.vcd opened for output.
Test Pass
iverilogでシミュレーションするには、Anlogic社のセルライブラリが必要です。
Anlogic社のセルライブラリは以下のディレクトリにあるので、必要なセルを探してtest.lstファイルへ追加してください。
<TD IDE>\Anlogic\TD4.6.4\sim\
<TD IDE>は、TD IDEのインストールディレクトリ(Program Files等)です。
後日のアップデート |
後日「#20-4 32ビットに拡張するゾ(み)」にて、TD IDEのバージョンをver.5.0.4にアップデートしました。 ところが、ver.5.0.4のセルライブラリ(以下パス)を使おうとしたところ、iverilogではエラーとなってしまって、使えません。 <TD IDE>\Anlogic\TD5.0.27252\sim_release\ 「`pragma protect」の記述が嫌われてしまいます。 ※セルライブラリにプロテクト掛ける必要ある??たかが「セル」の動作記述でしょ?好かないわぁ〜。 iverilogを使ってシミュレーションを実行したい場合は、TD IDE ver.4.6.4をインストールして、セルライブラリを使う必要があります。 一方、デザインページからダウンロード可能なTD IDEのプロジェクトはver.5.0.4用なので、TD IDE ver.5.0.4のインストールも必要です。 手元の環境では、両方のバージョンを同時にインストールして保持することができました。ver.5.0.4の方を後からインストールしたのですが、プロジェクトファイルをダブルクリックすると、TD IDE ver.5.0.4が開きました。ご参考まで。 |
iverilogは、空白を含むパス名には対応していないので、TD IDEのインストールディレクトリが「Program Files」の様に空白を含んでいる場合、直接パスを指定しても、エラーとなります。Anlogicセルライブラリを、空白を含まないパスにコピーするなどして対処してください。
これでようやく、CPUセルフテストもフルに実行可能です。
テストベンチ(test.v)では、UART8N1のテスト用に、TXD → RXDのローカルループバックを設定してあります(下コード)。
reg uart_rxd;
// uart local loop back
always @uart_txd
uart_rxd<=uart_txd;
要は、UART8N1で送信したデータがそのまま、UART8N1で受信できるはずです。これで簡易的に送受信のテストが実行できます。
※このローカルループバックは、FPGAボードでも有効です。RXDに割り当てたR7端子と、TXDに割り当てたT7端子をジャンパ線で接続すると、自分が送信したデータを自分で受信して、簡易的にセルフテストを実施することが出来ます。
ありがちで申し訳ないですが、サンプルプログラムはLチカです。FPGAボードのオンボードRGB LEDをチカチカさせます。点滅パタンはこちらと同じ、「2回短く光って長めの消灯」これを1秒周期で繰り返します。更に、1周期毎にLEDの発光色を、「青 → 赤 → 紫 → 緑 → 水色 → 黄 → 白 → 黒 → 青に戻って繰り返し」の様に変えて行きます。
黒と言う光は無いので、ここは1秒間の消灯となります。
LEDの点滅には、汎用I/OポートPORT8I8OユニットとシステムタイマSYSTIMユニットを使っていますので、RGB LEDが点滅する事と点滅周期を確認すれば、PORT8I8O及びSYSTIMが正常動作していることが確かめられます。
残る非同期シリアルUART8N1ユニットのテストとして、シリアルモニタから以下のコマンドを受信して、レスポンスを返す機能を持たせておきます。
シリアルモニタから受け付けるコマンド:
シリアルモニタとの通信速度は9,600baudとしてありますが、以下の記述を変更すると、好みの通信速度に設定して使うことが出来ます。
equ baud,9600 // baud rate
equ uart_baud,fcpu/baud-1 // uartbaud setting
以下にプログラムをリンクしておきます。Olive+のスクリプト(asm_mcvm.olv)でアセンブルして、使ってください。
テスト用プログラム:mcoc_blink.asm
オリジナル16ビットCPUコアMoscoviumには何のライブラリも無いので、strcmp()関数相当のサブルーチンから作らなければならず、久々にしんどかったです。
そう言えば、strcmp()機能はじめ、jal命令でサブルーチンをコールする場合、予めスタックポインタspを有効なRAM領域のアドレスに設定しておく必要があります。普通はequ疑似命令などを使って定数としてRAM上のアドレスを指定するのですが、折角IDレジスタIDREGにRAMの情報を持たせておいたので、IDREGを読み出して自動的に設定する様にしてみました(↓)。
// set sp to the bottom of ram area
ldwi r7,idrgramt // idrg: ram top address
ldw r7,[r7]
ldwi r6,idrgrams // idrg: ram size
ldw r6,[r6]
add r7,r6
movtc sp,r7
Moscoviumのスタックはフル下降型なので、上コードの様にRAM領域の最上位アドレスに設定するのが順当だと思います。
IDREGからの読み出しで設定するのはコードが大きくて、明らかにequ等の定数で設定した方が小さい上に簡単です。ただ、異なる型番の姉妹品などに乗り換えた時に、RAMのサイズやマッピングが変わっていても追従できると言う利点があります。
前節で引用したCPUセルフテストプログラム(mcvm_self.asm)も、FPGAボードで実行できるようになっています。
シミュレーションの終了条件は、0xf028へ0x8000以上のデータ(最上位ビットが1)を書き込むことにしてあるのですが、0xf028は汎用I/OポートPORT8I8Oユニットのporoutレジスタアドレスです。
例)終了条件を発効するプログラム
equ simfnsh,0xf028
equ simpass,0x8006
equ simfail,0xfff5
#
tstpass:
ldwi r0,simpass
bra tstfnsh
tstfail:
ldwi r0,simfail
tstfnsh:
ldwi r7,simfnsh
stw [r7],r0
bra pcnt-2
poroutレジスタは下位8ビットだけが有効なので、通常、最上位ビットに1を書き込む必要はありません。従って、通常のシミュレーションでは、終了条件を満たさないことになります。また、このレジスタの最上位ビットに1を書き込んでも無視されて、動作は何も変わりません。
書き込むデータはPass表示する時に0x8006、Fail表示する時には0xfff5としてあります。poroutレジスタの下位3ビットは、FPGAボードのRGB LEDに接続しています(以下、I/O制約ファイル抜粋)。
set_pin_assignment { port_out[0] } { LOCATION = P13; } ←オンボードの青色LED
set_pin_assignment { port_out[1] } { LOCATION = R3; } ←オンボードの赤色LED
set_pin_assignment { port_out[2] } { LOCATION = J14; } ←オンボードの緑色LED
FPGAボードのRGB LEDは、LOW駆動で点灯するタイプの回路なので、Passした時は青色LED、Failした時は赤色LEDが光るようになっています。
I/O制約ファイルを変更して使う場合は、この限りではありませんが、PORT8I8Oユニット下位3ビットを上の割り当てで使用する場合には、シミュレーションで使ったプログラムをそのままFPGAボードで実行しても、ちゃんと実行結果を目視確認👀することが可能な工夫です。
最上位記述が完成、TD IDEのプロジェクトを作成、シミュレーションで動作を確認、サンプルプログラムのコーディング、と進んできました。いよいよ、コンパイル(論理合成・配置配線)です。
この時、忘れずに、ROMに書き込むプログラムをアセンブルして、ROMマクロを作成した時に指定したデータファイル(mcoc_irom.mif等)を差し替えておいてください。コンパイルした時点のデータ(MIF)ファイルがFPGAボードに書き込まれるためです。
今回試すのは前節でリンクしたプログラム(mcoc_blink.asm)のアセンブル結果(MIFファイル)なので、所定のファイルと差し替えてください。
ではコンパイルです。……以下にコンパイル結果をまとめておきます。
コンパイルして完成したBITファイルをFPGAボードに書き込むと、オンボードのRGB LEDがチカチカ点滅するはずです。
オリジナル16ビットCPUコアMoscoviumで作ったマイコンをソフトウェアで制御して光らせたLチカです。点滅周期も設計通り約1秒周期ですし、RGB LEDも周期毎に色を変えて光っています。CPU、BUSC含めて、PORT8I8OとSYSTIMが正常に動いていないとこの様な動作(期待通り)にはなりません。
シミュレーションである程度動くことはわかっていましたが、実物で動作しているのを見るとやっぱり楽しいですo(1^−^)o。
とは言っても、実は最初からこんなにしっかりと動作していたわけではありませんでした。そりゃそうです、プログラムのデバッグは必要ですよ。動かしてみて → 動作を確認し → 微調整したら → また動かしてみる、そんなイタレーションが発生します。
その度に、プログラム修正 → アセンブル → コンパイル(論理合成・配置配線) → 書き込み、を繰り返すことになります。ROMのデータを変更するだけで、回路自体には変更がないのにもかかわらず、TD IDEでは再コンパイルが必要となります。
コンパイル時間1分強、書き込み時間まで含めてもせいぜい2分、とは言えど、手順を踏む必要もあってまどろっこしいです。
そこで、TD IDEのRAMエディタ機能を試してみました。「Toolsメニュー ⇒ Debug Tools ⇒ BRAM Editor」を実行すると、以下の様なRAMエディタウインドウが開きます。
もう解りましたね。RAMエディタを使うと、プログラムを変更した時、再コンパイルと再書き込みの手順が不要となるので、ソフトウェアデバッグ時のイタレーションがだいぶ楽になります。
ただ、少し難があります。ラジオボタンの設定をLogicalにした状態だと、なぜかエラーが出てライトができませんでした。……何か筆者の操作が間違っているのかも??
ラジオボタンでPhysicalを選択しておくとライトはできるのですが、今後、ROM容量を大きくすると、ROMマクロが複数に分割されてしまって、各ROM毎に新しいデータをライトしてやる必要が生じます。
まあ、順風満帆というわけではないですが、毎回再コンパイル&再書き込みするよりは楽なはずなので、暫くはこれで我慢です。Logicalでライトできるとすごく楽なのだけれど……。
RAMエディタでROMに書き込むテキストデータですが、基本的にはVerilog-HDLの$readmemh()システムタスクで読み込み可能なDATファイル、とされています。
しかし、TD IDEが読み込み可能なDATファイルにはいろいろと制限があります。
TD IDEが読み込み可能なDATファイル:
この(↑)DATファイル出力には、asm_mcvm.olvの疑似命令で対応しています。
こうしてRAMエディタを使って書き込んだデータはvolatileなので、FPGAボードの電源を遮断すると消えてしまいます。まあ、プログラムデバッグ中の仕様制限としては許容できるでしょう。
プログラムのデバッグが済んだらアセンブルしてMIFファイルを作り、FPGAボードのFlashメモリに書き込めばnon-volatileにすることができます。
少し大きめのプログラムを組んだ時に、RAMエディタを使ってROMデータを書き換えていて気が付いたのですが、どうも、うまくデータを書き込めない場合があるみたいです。
部分的に、書き込もうとしたデータと異なるデータが書き込まれてしまうケースが有って、その場合、その部分のコードを実行しようとした時に正しい命令コードが読めないため、正常に動作しなくなってしまいます。
色々試してみたところ、どうも、MoscoviumのROMリードと、RAMエディタのROMデータ書き込みが競合すると、時々書き込みを失敗するみたいです。と、なれば、MoscoviumがROMを読み出ししていない時にRAMエディタの書き込みを実行すれば良いので、以下手順の様にして見ました。
FPGAボードのUSERボタンをMoscoviumのリセットボタンとして割り当ててある(MCOC_blink.adc)ことが条件です。USERボタンを押し続けることによって、Moscoviumにリセットがかかり、その間はROMへアクセスしないことが保証されます。この間にRAMエディタでデータを書き込みます。
まだ上の方法で使い始めたところですが、今のところ、問題ないみたいです。
少し注意して使ってみてください。
やっぱりダメ(↑の対策)みたいです。 調子よく書き込める時と、そうでない時もあるみたいで、安定しません。 お陰で、対策の効果が読みにくい……。 上手く安定して動作させる手順に辿り着けませんでした。 はあ……。 RAMエディタで書き込んで、読み出して、書き込みデータと比較したら違ってる、それだけの事だし、回路上はROMとして使っているので、回路の間違いでROMのデータを破壊している訳でもありません。明らかに、TD IDEが期待通りに動いていない。 一々MIFファイルを作って、コンパイル(論理合成・配置配線)して、書き込み、かぁ……。 ちょっと面倒だけど、AnlogicのへぼいIDEのせいなので、ごまかしながら使うしかないみたい。 |
「#16-7 Moscoviumにブートローダを」 上の記事が、最終的な対策です。 ブートローダの働きによってUARTからプログラムデータをダウンロードし、ROMと言う名前のRAMに書き込む方法で解決しました。 快適です。お試しください。 |
シリアルモニタと接続して、UART8N1とIDREGのテストも実施しておきましょう。以下図の様なUSB⇔UART変換モジュール(FTDI)を使います。
以下の様に接続してください。
FPGAボード | 接続 | FTDI |
R7(uart_rxd) | ← | TXD |
T7(uart_txd) | → | RXD |
GND | ⇔ | GND |
× | VCC(ジャンパ:3.3V側) |
プログラム(mcoc_blink.asm)のまま変更していないなら、通信速度は9,600baudなので、Arduino IDEのシリアルモニタを起動して9,600baudに設定してください。
以下、実験中の様子です。
※ご覧の様(↑)に、FPGAボードには、付属していたピンヘッダではなく、丸ピンソケットをはんだ付けしました。
実験サンプル:【 】内はシリアルモニタからの送信データ
RDY
【help】
OK+ who
OK: help
【who】
OK+ id code: 1150
OK+ version: 0100
OK+ fcpu kHz:5dc0
OK+ rom top: 0000
OK+ rom size:1000
OK+ ram top: 5000
OK+ ram size:2000
OK: rgb_led_blink ver.1.00
【who】コマンドを投げた時に、IDREGの内容を読み出して、結果をシリアルモニタに表示するようなプログラムにしてありました。IDREGから読み出した値は設計通りでした。9,600baudでのUART通信に関しても問題ない様です。
プログラムを変更して、115,200baudと、シリアルモニタで最速設定である2,000,000baudでも通信してみましたが、どちらも問題なく通信できました。
2,000,000baudだと、1ビットの送信時間が24MHzクロックで12サイクルという、非常に高速な通信だったのですが、特に問題ありませんでした。どの程度の余裕があるか?は解りません。正しく通信できるかどうか?は、接続に使用したジャンパ線の長さなど、実験する環境にも依存するはずです。
今回のお買い物は、以下の通りです。
ATmega328を追加購入した時、一緒に買っておいた部品です。普通のピンソケットよりも背が低いので、ICソケットみたいな使い方もできるかな?と思って買っておいたものを、FPGAボードにはんだ付けしてみました。
FPGAボードに付属してきたピンヘッダを取り付けた場合、FPGAボードをブレッドボードに挿して使うことになるのですが、幅が広くて長いFPGAボードを挿したところで、ブレッドボードのほとんどの穴がボードの下に隠れてしまって、有効活用できません。それに、ブレッドボードの抜き差しで、壊れやすいUSBコネクタに余計な力がかかる恐れがあったので使いませんでした。
久しぶりにアセンブラでプログラムなんか組んでしまった。やっぱ、疲れる(1へ_へ;)。