FPGAボード(Sipeed Lichee Tang Primer)を使って設計しているオリジナル16ビットCPUコアMoscoviumには、半精度浮動小数点演算ユニット(Half precision FPU、以降HFPUと呼びます)が搭載できます。
「#16-5 半精度浮動小数点演算ユニット」
「#16-6 半精度浮動小数点演算ユニット(弐)」
半精度浮動小数点型(16ビットhalf型)は、16ビット幅の汎用レジスタを持つMoscoviumには最適でした。一方、有効数字は10進数換算で約3桁強と、満足できる精度ではありませんでした。
その後、汎用レジスタや演算器を32ビット幅に拡張したオリジナル32ビットCPUコアNihoniumを設計しました。現時点で、NihoniumのコプロセッサとしてHFPUを接続することは可能なRTLにしてあります。
折角、汎用レジスタが32ビットになったので、浮動小数点型も32ビット長にすることができます。32ビット長の浮動小数点型としては、half型と同様、IEEE754で規定されているbinary32(単精度浮動小数点、以降float型と呼びます)が挙げられます。
今回は、単精度浮動小数点演算ユニット(Single precision FPU、以降SFPUと呼びます)を設計します。
half型とfloat型は同じIEEE754で規定されているだけあって、下図の様にバイナリフォーマットは、ほぼ同じです。
float型では、全体のビット幅が16ビット→32ビットに拡張されたのに伴って、指数部Ex’と仮数部Fx’のビット幅も拡張されています。
手元には既に、HFPUのRTLが有りますので、そこからチョチョイと、ビット幅を広げる設計変更をすれば、SFPUが完成するはずです。従って、改めて作図し直すまでもなく、ブロック図レベルとしては、HFPUのブロック図と同じ形となります(下図)。
※half→single、h***→s***等と読み替えてください。内部バスや演算器のビット幅が拡張となります。
以下、代表的なブロックについて、float型への設計変更を進めて行きます。
32ビットコプロセッサバスA[31:0]、B[31:0]から受け取った2数を加減算(A±B)します。演算アルゴリズムについては、half型と同じです。以下、half型との差分についてのみ、考察します。
float型で表現可能な、非有限値(NaN、INF、ゼロ等)との加減算は、half型と同様です。
32ビットコプロセッサバスA[31:0]、B[31:0]から受け取った2数を乗算(A×B)します。演算アルゴリズムについては、half型と同じです。以下、half型との差分についてのみ、考察します。
float型で表現可能な、非有限値(NaN、INF、ゼロ等)との乗算は、half型と同様です。
TD IDEのIP Generatorを使って、以下図の様に乗算器マクロを作成してください。
TD IDEの乗算器ハードマクロは18ビット長であるのに対して、必要なのは24ビット長の乗算器なので、4個の乗算器ハードマクロで部分積を作って、加算して求めるような論理を生成するはずです。
HFPUの乗算器は11ビット長だったので、18ビット乗算器ハードマクロ1個で充足していました。HFPUに比べて、論理規模と伝搬遅延時間が増大しますが、HFPUと同様、1サイクルで実行することにします。クリティカルパスは強化されますが、多分、問題ないでしょう(後ほどSTAで確認)。
32ビットコプロセッサバスA[31:0]、B[31:0]から受け取った2数を除算(A÷B)します。演算アルゴリズムについては、half型と同じです。以下、half型との差分についてのみ、考察します。
float型で表現可能な、非有限値(NaN、INF、ゼロ等)との除算は、half型と同様です。
float型からint型への型変換だけでなく、floor()関数やceil()関数の実装等に於いて、float型の数値を整数化する必要が生じます。
floor()関数やceil()関数等を実装するためには、float型のまま、整数化する処理が必要となります。
この処理を実行する回路だけ、新規設計して追加します。
NihoniumにHFPUを接続することは可能なので、HFPU命令を備えています。HFPU命令は、hadd命令,hmul命令などの様に、先頭文字「h」で始まる命令です。
では、SFPU用として「f」で始まる、fadd命令、fmul命令などを作りましょう。
ここで一考。
そこで、HFPU命令と、SFPU命令の命令コード(オペコード)を同一コードに割り当ててしまいます。すると、何という事でしょう!従来のHFPU命令コードのまま、SFPUを制御できてしまいます。
命令数に変動が無いので、論理規模は変わらず、伝搬遅延時間も変わりありません。
※NHビットの設定だけで、16ビットモードと32ビットモードを切り替える、Nihoniumの基本方針と同じ考え方です。
「欠点」とまで、言えるかどうかわかりませんが、考え得る欠点としては、Nihoniumに対して、HFPUとSFPUを同時に接続することは、できなくなります。でも、それだけのことです。普通、SFPUを搭載したら、並行してHFPUを使いたいケースは無いでしょうから、問題になることはないでしょう。
従来通り、HFPUだけを接続して使うことは可能ですし、新たにSFPUだけを接続すれば、Nihoniumで単精度浮動小数点float型の演算を実行することができます。
アセンブラとしては、HFPU命令(h***命令)とSFPU命令(f***命令)を別名として、同じ命令コードにアセンブルします。アセンブラレベルでは、別命令扱いとなることに留意してください。アセンブルリストの一例を以下に示しておきます。
// @adr code // adr*2 instruction
@0000 7001 // 0000 fadd r0,r1
@0001 7001 // 0002 hadd r0,r1
アセンブラレベルでは、fadd命令とhadd命令は別扱いですが、共に同じ命令コード0x7001にアセンブルされていることが判ります。
Nihoniumは、状態レジスタsrのNHビットによって、動作モードが変化します。
コプロセッサとして接続するSFPUには、32ビット幅の汎用レジスタや内部バスが必要なので、NH=1を設定した場合だけSFPU命令を使用することができます。
プログラム実行時、NihoniumにHFPUまたはSFPUが接続されているかどうか?検出したい場合には、以下の命令列を実行して、判定することができます。
ldbiu r0,1
hneg r0,r0 // fneg r0,r0 でも良い
fneg命令とhneg命令は同じ命令コードに展開されるので、どちらを使っても結果は同じです。実行後の汎用レジスタr0の値で、以下の様に判定してください。
検出したFPUに従って処理を変えたり、場合によってはエラー出力してプログラムを停止するなどの処置に使用できます。
HFPUからSFPUへの設計変更で必要となった、float型のまま整数化する機能について、CPUにも新規に命令を追加します。
動作は、「ゼロ方向への丸め(round down to zero)」なので、fdown命令とします。
HFPUでは、以下のようにすると同じ動作をエミュレートできるため、命令は追加しません。
fdown r0,r1
↓ HFPUで相当する動作
huint r0,r1
hhalf r0,r0
hmvsg r0,r1
Moscovium+HFPUにも追加すると便利かもしれませんが、今回は追加しません。余計な修正がCPUに入ると、検証などの工数が馬鹿にならないので、必要に迫られない限り手を付けないのが賢明だという判断です。
今回設計したSFPUを搭載したマイコンNHOC113FS0408を作成しました。
以下リンクより、最新のデザインをダウンロードすることができます。
FPGAボード(Sipeed Lichee Tang Primer)への書き込み手順はこちらを参照してください。
以下に、TD IDEでのコンパイル(論理合成・配置配線)結果をまとめておきます。
SFPU単体でコンパイルしてみると、3,300LUT程の論理規模でした。Moscoviumコア+乗除算器が約3,600LUTですので、CPU(Moscovium)ほどの大きさがあります。いくつか並べて比較してみましょう。
ユニット | 論理規模[LUT] | 乗算器マクロ数 |
Moscovium | 2,600 | 0 |
Moscovium+乗除算器 | 3,600 | 1 |
Nihonium | 5,600 | 4 |
HFPUコプロセッサ | 1,100 | 1 |
SFPUコプロセッサ | 3,300 | 4 |
SFPUは、HFPUから比べると×3倍ほどの論理規模でした。浮動小数点型のビット幅では×2倍なのですが……。まあまあ、大きい回路ですね……。演算精度が許すなら、HFPUを使った方が、お得感があります。
iverilogを使って、論理検証を行います。検証方針は、HFPUの時と同じです(下図)。
half型(16ビット)→float型(32ビット)などに読み替えてください。
ただし、1点だけ、HFPUの時とは異なる点がありました。HFPUの時はピッタリ期待値を一致させることができたのですが、SFPUでは、どうしてもfloat型最下位ビット(LSB)の値だけ、うまく合わせることができませんでした。
常に上例の様になるわけではなくて、完全に一致する場合もあり、合わせ込むことは断念しました。一々エラーを表示しても、目視で最下位ビットだけの不一致だと確認するのは手間なので、最下位ビットだけの相違であれば、パス(合致)として検証を実行しました。
加減乗除算数10,000パタンと、その他のFPUコマンドによる処理結果を検証した結果、問題ありませんでした。勿論、確認した数10,000パタンの範囲内です。本HPの回路やプログラムは全て無保証ですので、ご理解の上、ご利用ください。
SFPU命令をシミュレーション実行して見て、各命令の実行サイクル数を調べました。以下表にまとめて、解説します。
HFPU | SFPU | メモ | ||||
命令 | min | max | 命令 | min | max | |
hadd | 3 | 8 | fadd | 3 | 8 |
仮数部ビット数増加で+1、 指数差≧0x20の対処で−1サイクル。 |
hsub | 3 | 8 | fsub | 3 | 8 | |
hcmp | 3 | 8 | fcmp | 3 | 8 | |
hmul | 3 | 4 | fmul | 3 | 3 | 正規化を演算に押し込んで−1サイクル。 |
hdiv | 6 | 7 | fdiv | 3 | 7 | 除算ステップ数+2、ロード−1、正規化−1サイクル。 |
huint | 3 | 7 | fuint | 3 | 8 | uint16→32に伴って、+1サイクル。 |
hfrac | 3 | 7 | ffrac | 3 | 8 | |
hhalf | 3 | 5 | fsngl | 3 | 6 | |
hmvsg | 2 | 2 | fmvsg | 2 | 2 | 変わらず。 |
hneg | 2 | 2 | fneg | 2 | 2 | |
hsat | 2 | 2 | fsat | 2 | 2 | |
fdown | 2 | 2 | 新設命令。 |
解説:
上に記載の通り、HFPUのサイクル数を検証したところ、何ヶ所か無駄なサイクルが有りました。SFPUへ設計変更する際に見直したので、HFPUと比べてそん色ない速度で処理可能なコプロセッサとなりました。10進数換算で有効数字3桁強しか無かったHFPUに比べると十分実用的な、有効数字7桁弱のSFPUが、HFPUとそん色無い速度で実行できるので、浮動小数点演算が必要になった場合には、SFPU一択で悩まずに済みます。
なお、HFPUでも適応可能な見直し箇所もあるのですが、修正すると論理検証までやり直しで無駄な工数🏭が発生するため、HFPUへの適応は見送ります。SFPUを使った応用は、次回です。