オリジナル16ビットCPUコアMoscoviumのスケールダウンに取り組んでいます。
FPGAボード(Sipeed Lichee Tang Primer)を使って設計しているMoscoviumシリーズマイコンでは、これまでに、性能の向上を目指して、32ビット化やスーパスケイラ化、デュアルコア化を進めてきました。性能を向上しようとする試みは全て、論理規模の増大を招いてきました。
今回は、より小さなFPGAに載せることができる事を目指して設計したオリジナル8ビットCPUコアTennessineの動作検証を実行して、完成させたいと思います。
CPUコアをTennessineに差し替え、最小限度のI/Oユニットだけを搭載したTSOC117MN0408を作りました。
以下リンクより、最新のデザインをダウンロードすることができます。
FPGAボード(Sipeed Lichee Tang Primer)への書き込み手順はこちらを参照してください。
以下に、TD IDEでのコンパイル(論理合成・配置配線)結果をまとめておきます。
UARTや汎用I/Oポート等、最小限のI/Oユニットを搭載したマイコンでも、4k[LUT]を下回るサイズに収まりました。小さ目のFPGAにも載るサイズになったと見て良いでしょう。
もう少し検証してみましょう。Tennessineおよび、MoscoviumをCPU単体でコンパイルして論理規模を比較してみました(下表)。
CPUコア | ビット幅 | LUT数(論理規模) | F/F数 | DSPマクロ | 備考 |
Tennessine | 8ビット | 1,300 | 220 | 1個 | |
Moscovium | 16ビット | 2,900 | 650 | 1個 | 乗算コプロセッサ込み |
目標だった、「Moscovium論理規模の半分以下」は、達成できました。
最小構成のマイコン(TSOC117MN0408)としても4k[LUT]を下回ることができましたので、論理規模4k[LUT]程のFPGAボードに搭載することが可能です。
FPGAを使う場合はユーザ論理を作って載せたいでしょうから、CPUコアの論理規模が小さくなって、ユーザ論理に割り当てられるLUT数が増加することは利点となるはずです。
小さいFPGAボードにも載せることができるので、FPGAボードの選択肢が広がることも利点です。
※なお、FPGAの論理規模について、メーカやチップが異なると、単純にLUT等の数だけでは比較できませんので、こちらをご理解の上、ご判断ください。
Tennessineで、複数レジスタを跨ぐ加減算処理を実行するための命令を追加しました。
adcz命令/sbbz命令/cmbz命令
8ビットCPUなので、裏レジスタとのレジスタペアQn:Rnに対する16ビット加減算を実行する機会は増すため、追加を決断しました。有って困る事は無い、ばかりか、とてつもなく有用な命令達です。
Tennessineに追加するという事は、Moscovium,Nihoniumにも追加しないとね……。Moscovium,Nihoniumに於いても、各々、32ビット,64ビットデータの加減算を実行する場合に有用な命令ですから。
すると……。またN倍化ですね?こちらの記事「#25-7 乗除算命令を強化する」では、N倍化に対して否定的な主張をしている様に見えたかもしれませんが、実は逆です。……解り難い逆説的な表現でごめんなさい。
「Moscovium/Nihonium」×「通常版/スーパスケイラ版」の、計4コアにも、命令を追加して更新しておきました。
ともあれ、まずはシミュレーションで動作を確認して行きます。
CPUが自分自身をテストするプログラムをCPUセルフテストプログラムと言います。
Tennessine用にCPUセルフテストプログラムを作成しながら、1命令毎にシミュレーション波形をチェックして、動作を確認して行きました。最初の一回目なので、「CPUセルフテストプログラムがパスしたからOK」とは、ならず、「正常に動作することを確認しながら、CPUセルフテストプログラムを作り上げていく」と言う方向です。
結果完成したプログラムを、以下にリンクしておきます。
Tennessine用CPUセルフテストプログラム:tnsn_self.asm
16ビット処理用マクロ集:tnsn_mcr_word.incl
Moscoviumシリーズ用アセンブラでアセンブルすることができます。上にリンクしておいた、マクロ集も必要になりますので、プログラム(tnsn_self.asm)と同じディレクトリに格納しておいてください。
いつもの様に、iverilogを使ってシミュレーションを実行してみましょう。
実験サンプル:【 】内はCygwinシェルウインドウからの実行コマンド
【iverilog -f test.lst ; if ( ! $status ) vvp a.out】
VCD info: dumpfile test_ts.vcd opened for output.
Test Pass
CPI: 1338/ 1292( 0)= 1035/1000
finish: 55120 ns
※iverilogを使ったシミュレーション環境の立ち上げについてはこちらをご参照ください。
無事、正常動作を確認できました。
Tennessineコアを搭載したマイコンをFPGAボードへ書き込んで、実機でも動作を確認しておこうと思います。
FTDI等のUSB⇔UART変換モジュール(3.3V定格の物)を用意して、こちらの様にFPGAボードに接続してください。以下、実験中の様子です(写真は使い回し)。
ただの「Lチカ」ですが、こちらを参照して、以下のプログラムと共に、TSOC117MN0408のデザインをFPGAボードに書き込んでください。
Lチカプログラム:mcoc_blink.asm
上にリンクしたプログラムは、Moscovium用として最初に作ったサンプルプログラム(mcoc_blink.asm)をTennessine用に移植した物です。
シリアルモニタから実験を進めます。
実験サンプル:【 】内はシリアルモニタからの送信データ
【who】
OK+ id code: 1170
OK+ version: 0148
OK+ fcpu kHz:5dc0
OK+ edition: 4d4e MN
OK+ rom top: 0000
OK+ rom size:1000
OK+ ram top: 5000
OK+ ram size:2000
OK: rgb_led_blink_ts ver.1.00
ブログラム(mcoc_blink.asm)が【who】に対して、上サンプルの様に期待通りの応答を返しました。
また、FPGAボードのRGB LEDが点滅していることも確認できました。
ブートモードに遷移して、命令ROMにプログラムコードを書き込めることも確認しました。
Tennessine用のブートプログラムが正常に動作して、移植したプログラムも正常動作を確認できた、と言う事になります。動作確認完了を持って、実験を終了します。
Tennessineコアの追加によって、8,16,32ビットのCPUコアがスケーラブルに展開できるようになりました。ビット幅に関わらず、全て命令コードレベルの後方互換性を保っています。と、なると気になる、以下の疑問。
同じオブジェクトコードがTennessine,Moscovium,Nihoniumで共通して動くのか?
以下のコードで検証してみましょう。
ldw r0,[r7] // メモリから16ビットデータを読み込む
ldwi r1,0x1234 // 期待値
cmp r0,r1 // 期待値と比較
bne fail
bra pass
エクスキューズなしで、結論だけを述べると、「動きます」。
Tennessineでも、16ビットアドレス/データを使ってメモリアクセスできるように工夫しておいたのが奏功していて、ldw命令等のメモリアクセスは、Moscovium等と同じ動作となるためです。ldw命令とldwi組み込みマクロ命令は、期待通りの動作となります。
しかし、Tennessineだけは、チェックが「ざる」になっていることに気が付きますか?
ldw命令を使って16ビットデータをr0に読み込んで、期待値r1と比較するcmp命令は、Moscovium,Nihoniumでは16ビット値を比較していますが、Tennessineは8ビットCPUなので、下位8ビットデータしか比較していません。
Tennessineで、上位8ビットを含めた16ビット値の期待値比較をするためには、以下のようなコードにしなくてはなりません。
ldw r0,[r7] // メモリから16ビットデータを読み込む
ldwi r1,0x1234 // 期待値
cmp r0,r1 // 期待値と比較(下位8ビット)
bne fail
// Tennessineの場合、ここから下、上位8ビットの比較を追加
cendw r0 // 表/裏レジスタ値を交換
cendw r1
cmb r0,r1 // 裏レジスタの比較
cendw r0 // 表/裏を元に戻す
cendw r1
bne fail
bra pass
そして、Tennessine用に作った上のコードは、逆にMoscovium,Nihoniumでは、正常動作しません。命令コード互換なのですが、正確には以下となります。
※言及し始めるときりがないのだけど、Tennessineでは0xff+1=0x00ですが、Moscoviumでは0xff+1=0x0100なので、「そのまま」では動かない場合があります。演算がビット幅を超えない範囲では、共通の動作となります。そこは理解した上で、上の文言を理解してください。
一般的なプログラムは、最低でも16ビットデータを処理することが多いため、Moscoviumで作ったプログラムは、そのままではTennessineで動作しません。命令コードは同じでも、プログラムは「移植」作業が必要となります。
実は、Moscoviumシリーズの内蔵ROMにプログラムをダウンロードするブートプログラム(mcoc_boot.asm)は、Moscovium/Nihonium用の16ビット処理なので、Tennessineでは正常動作しませんでした。現在は、ターゲットCPUを切り替えるオプションと、ユーザマクロ、Verilog-HDLの条件付きコンパイル「`ifdef」等を駆使して、ブートROMデータを差し替えてあります。
Tennessineへの移植時に障害となるのは、8ビット幅になってしまう演算系の命令群です。そこで、Tennessine用に16ビットワードデータ処理用のマクロ集を作りました。
16ビット処理用マクロ集:tnsn_mcr_word.incl
これを使うと、以下の様に移植が楽になります。
ldw r0,[r7] | |||
ldwi r1,0x1234 |
|||
cmp r0,r1 |
→ | cmpw r0,r1 | |
bne fail |
cmpwは、マクロ集(tnsn_mcr_word.incl)で定義されたユーザ定義マクロで、中身は以下の通りです。
macro cmpw rd,rs
cmp $(rd),$(rs) // 下位8ビットを比較
cendw $(rd) // 上位8ビットの比較を追加
cendw $(rs)
cmb $(rd),$(rs) // 上位8ビットを比較
cendw $(rd) // 元に戻す
cendw $(rs)
endm
他にも、addw、movw、negwなど、16ビット幅のデータ処理を容易にするマクロを準備してあります。16ビットワードデータを処理したい命令に「w」を付ければ移植完了というマクロ集と、なっています。
何なら、Moscovium,Nihoniumで使う時に以下のマクロ記述を追加すると、マクロアセンブラレベルでは共通のソースリストを使用可能です。
macro cmpw rd,rs
cmp $(rd),$(rs)
endm
ユーザマクロ機能、強烈でしょう?アセンブラでプログラムするなら、マクロは必須ですよね?絶対欲しかったんだぁ〜。
※このマクロ集は、「レジスタ内容の演算結果に限り、互換動作」であることに留意してください。状態レジスタsrは、一部動作が異なりますので、演算フラグ値(N,C,V,Z)を使用する場合は、マクロ展開後の命令配置を確認して使用してください。
また、即値の正負で左右シフトが変わるlsfti命令の様なバレルシフト演算命令を16ビットに拡張するのは、現在のマクロ機能では困難だと判ったため、以下の組み込みマクロ命令を追加しておきました。
マクロ集(tnsn_mcr_word.incl)にも、Tennessine用に16ビット処理に対応した、lslwi等のマクロを用意しておいたため、「w」を付けるだけで移植完了となります。
本ページの執筆中に、TD IDEのライセンスが切れました。はぁ。定期的にこのゴタゴタに対処しなくてはならないのは、耐えられません。老舗FPGAメーカのFPGAスタートアップキット等を購入し直して、乗り換えた方が良いかもしれません。