公開日:2023/05/20
title.png

25th Season


#25-7 乗除算命令を強化する

 FPGAボード(Sipeed Lichee Tang Primer)を使って設計しているオリジナル16ビットCPUコアMoscovium(下図)に関して、乗除算命令のバリエーションを追加して、使い勝手を向上しておこうと思います。

a153_cpublk.png

現行仕様ではダメな所

 現行の乗除算命令(mulu命令muls命令divu命令divs命令)は、状態レジスタsrのビット設定値によって、以下の様に動作が変化します。

  状態レジスタsrのDR,MLビット:

 各々1に設定すると、乗除算命令の実行に+1サイクル余分な実行時間がかかるため、ビット設定でユーザが選択可能としました。この仕様でも、1本のプログラム内で、ビット値を0/1どちらかに固定して使う場合には問題ありません。

 以下例の様に、ビット値を切り替えながら使う場合、ビット値の切り替えに1命令余計に実行することでパフォーマンスが落ちるし、ROM効率も低下してしまうことがダメなところです。

  // ================================
  // unsigned 32*32=32 bits multiply routine:
  //  r0:r1=r0:r1*r2:r3 ; disturbed r2,r3,Sr[ML]
  // ================================
  #      r0:r1
  #      r2:r3
  #    *)-------
  #      r3:r1  : 3) ML=1
  #     r3:r0    : 1) ML=0
  #     r1:r2    : 2) ML=0
  #   r0:r2     : not calculate
  #  +)-----------
  #      r0:r1  : 4)
  # ================================
  mulu32:
  clsr sreg_b_ml  // ML=0
  mulu r0,r3  // 1)
  mulu r2,r1  // 2)
  sesr sreg_b_ml  // ML=1
  mulu r1,r3  // 3)
  add r0,r2  // 4)
  add r0,r3
  rtnw


 この例では、SrのMLビットを2回、clsr命令sesr命令で切り替えています。この2命令を実行する為に、パフォーマンスとROM効率を低下させています。

 もし、MLビットの切り替えを行わずML=1に固定してしまうと、上のコードは1)2)の計算ステップに於いて、上位側16ビット計算値が格納されるR3およびR1レジスタ値が破壊されてしまって、正常動作しなくなってしまいます。それを回避するためには、破壊されてもかまわないレジスタを追加使用する必要があり、結局、パフォーマンスとROM効率を低下させることになります。


 また、汎用のコード(サブルーチン)を作ろうとすると、以下の様な「お作法」も必要となるケースが有ります。

  汎用的なサブルーチンを作ろうとした時:

 一々DR,MLビットをセット/クリアしなくてはならないため、数命令とは言え、余計な命令を実行しなくてはならず、不利でした。パフォーマンスやROM効率が低下するのみならず、使い勝手もよろしくありません。

※ちなみに、上のコード例では、disturbedリストにSr[ML]ビットが入っている通り、サブルーチンの呼び出し〜復帰間でSr[ML]ビット値は保存されません。

何で、そうなった、のか?

 乗除算命令を追加した当時「#16-2 Moscoviumで四則演算する」、命令コードが枯渇しつつありました。

 要は、命令コードをケチったがために、モード設定ビットを追加するという解決策に走り、現在の状態になっているのです。高々数命令分の命令コードをケチったがため、パフォーマンス的にも、コードサイズ的にも不利になったのは考えが不足していました……。

 結局、この段階で命令追加することになってしまいました。猛反省m(1x_x)m。

命令を追加して対処

 命令コードに空きがあって、命令追加が可能な今の内に、Srのビット切り替えをしなくても2つの機能を個別に制御できるように、命令を整備することが目標です。

 命令コードに空きが有るとは言っても、8命令を追加するのは、ちょっと多すぎます。そこで少し工夫してみます。

 「r」付の新規命令は、常に、DR,MLビットの設定値と逆の動きをするので、DR,MLビットを0/1どちらに設定していたとしても、その値を変化させることなく、逆の動作を実行することができます。

 故に、SrのDR,MLビットは不要となり、廃止することが可能となります。しかし、これまた、後方互換性維持のため、仕様としては残さざるを得ません。
 ただし、事実上不要となるDR,MLビットを将来に渡って保持し続ける理由はありません。Srの空きビットは枯渇してきていますので、将来の拡張でビット追加が必要になった場合にDR,MLビットを廃止する選択肢も残す必要があるでしょう。
 従って、SrのDR,MLビットについては以下の通りとします。

 と言っても、過去のプログラム全てを修正するのは、当方としても相当な困難を伴うため、よほどのことが無い限り、後方互換性を無視してまで廃止する事は無いと思います……。

結果、どうなるの?

 新規命令を追加すると、最初に示したMoscovium用の32×32=32ビット乗算ルーチンは、以下の様に記述することが可能となります。

  今まで(命令追加前) 新規命令追加後
前提条件 復帰(rtnw命令)後、
Sr[ML]ビットは
保存されない。
必ず、
Sr[ML]=0(固定)で
使用する事。
サブルーチン mulu32:
clsr sreg_b_ml  // ML=0
mulu r0,r3  // 1)
mulu r2,r1  // 2)
sesr sreg_b_ml  // ML=1
mulu r1,r3  // 3)
add r0,r2  // 4)
add r0,r3
rtnw

mulu32:
// ML=0
mulu r0,r3  // 1)
mulu r2,r1  // 2)
// !ML=1 (ML=0)
mulur r1,r3  // 3)
add r0,r2  // 4)
add r0,r3
rtnw

 SrのMLビットをセット(sesr命令)/クリア(clsr命令)していた2命令が不要になっただけ、ではありますが、汎用的なサブルーチンなどが作り易くなったし、使い勝手は向上しています。パフォーマンスおよびROM効率も、わずかながら向上しました。

Moscoviumには、あと2命令追加

 一応、「新規追加する命令数は4個」と、言うことで話を続けてきましたが、Moscoviumには少し変則的に、32ビット除算を実行可能な命令群が有ります(以下の4命令)。

  divlu命令divls命令divlq命令divlr命令

 この内、SrのDRビットを参照して動作が変化する命令は、divlq命令とdivlr命令の2つです。この2命令に対して、以下に示す反転(reversal)版命令を追加する必要があります。

  divlqr命令divlrr命令

 動作は、「r」なしの従来命令に対して、DRビットの0/1が反転するだけです。この命令は、Nihoniumには存在しない(未定義命令、nop命令と同じ)ため、Moscovium系コアだけに追加します。

N倍化

 ここまで主に、オリジナル16ビットCPUコアMoscoviumについて議論してきました。しかし、上位互換を謳うオリジナル32ビットCPUコアNihoniumを含め、更に、実行速度を高速化したスーパスケイラ版がそれぞれに存在するため、CPUコアとしては4種類が存在しています。

オリジナル
CPU 全4コア
通常版 スーパスケイラ版
a205_cpublk.png a208_cpublk.png
16ビットCPU  Moscovium  Moscovium-SS
32ビットCPU  Nihonium  Nihonium-SS

 どれか1コアだけに新規命令を追加して、他のコアには追加しない、と言う選択肢は、基本的に無いのです……。
 う〜ん、CPUを更新して機能を強化しようとすると毎回障害になるN倍化……。何とかならない物だろうか?等と悩んでみても、現状、何ともならないので、諦めて4コア全てを修正することになります。

※この先、取り得る最善の方法は、「これ以上CPUコアの種類を増やさない」事、なのでした……。何と後ろ向きな対策である事か。

デザイン

 全ての型名に共通して使われるCPUコア自体を更新して全て置き換えるので、過去に作成した適切な型名を使って検証などを実施すればOKです。

 ただ、Nihonium系コアを搭載した型名では、内蔵メモリ構成が全てROM 4Kバイト+RAM 8Kバイトとなっていて、メモリ容量の大きい型名が有りませんでした。これを機に、タイミング的に一番苦しいと思われるNihonium-SS(スーパスケイラ版)に、内蔵メモリを増設したNSOC113MD0816を作り、これを使って検証作業を進めることとしました。

 以下リンクより、最新のデザインをダウンロードすることができます。

btn_moscovium.png

 今回はシミュレーションのみで、FPGAボードへは書き込みませんが、必要に応じて、FPGAボード(Sipeed Lichee Tang Primer)への書き込み手順はこちらを参照してください。

コンパイル結果

 以下に、TD IDEでのコンパイル(論理合成・配置配線)結果をまとめておきます。

 タイミング余裕は無くなってきており、かなり厳しいですが、ギリギリ問題なしです。
 今回のNSOC113MD0816は、スーパスケイラ版のNihonium-SSコアです。通常版のNihoniumや、更に論理規模の小さいMoscoviumでは、もっとタイミング余裕があるはずなので、他の3コアについても「問題ない」と、しておきます。

CPUセルフテストプログラム

 いつも、CPUの機能変更や追加を実施した場合に、デグレイドが無いことなどを検証するために使っている、CPUセルフテストプログラムがあります。今回、Nihonium用のCPUセルフテストプログラムに、新規4命令のセルフテストを追加したところ、ROM容量4Kバイトを超えてしまいました。

 Moscovium/Nihoniumマイコンの最小ROM構成は4Kバイトなので、それを超えてしまうと、CPUセルフテストが実行できなくなってしまいます。機能変更した時にデグレイドが無いことの確認のため、過去の全型名で実行する可能性があるCPUセルフテストプログラムなので、「ROM容量4Kバイト以下の型名では実行不可」では、済みません。

 Nihonium用CPUセルフテストプログラムを2本に分割しました。

 Moscovium用CPUセルフテストプログラム(mcvm_self.asm)についても、4Kバイトに迫ってきていますが、まだ4Kバイト未満なので、このまま維持します。

模擬実験(シミュレーション)

 今回も、わざわざFPGAボードに書き込んで、実動作で確認するまでの事はありませんし、返って内部信号をモニタしながらサイクル毎に動作確認できるシミュレーションの方が、動作の検証をするには都合が良いです。従って、iverilogを使ったシミュレーションで動作確認を実施しました。
 iverilogを使ったシミュレーションの実行方法などについてはこちらをご確認ください。

 こちらのCPUセルフテストプログラムなどをアセンブルしておいてシミュレーションを実行、動作を検証しました。

 その他、新規命令を使った、32ビット乗算ルーチンのロードテストプログラムを作り、正常に動作することも確認しました。

  実験サンプル:【 】内はCygwinシェルウインドウからの実行コマンド
  【iverilog -f test.lst ; if ( ! $status ) vvp a.out】
  VCD info: dumpfile test_ss.vcd opened for output.

  00d0 5c0c 0003 4e62 dff2 e498
  0091 6dd1 0008 62ed a134 ac7d
  004b 7dbb 0007 eac9 ac4f a5d3
  0085 55cd 0008 4fcb 3d9d 4c8f
  :
  : (中略)
  :
  0012 21d3 000a 5bf2 d567 fa76
  0040 7ea5 000d 8033 ba8c badf
  00b5 a808 0007 54ec dad1 8760
  00a9 b13d 000a a3dd 8ab9 d8a9
  Test Pass
  CPI: 9442/ 8485( 5786)= 1112/1000
  finish: 379240 ns

 上のログデータは、各行左から32ビット毎に被乗数、乗数、乗算結果、の順です。全て期待値と照合して、合致を確認しております。
 以上で、追加した乗除算命令の動作検証が終了しました。


 デザインの修正も、そうでしたが、シミュレーションによる検証作業も4コア分のN倍化となります……。やはり、こういう事が有るから、あんまりCPUの機能改定とか、ちょくちょく小まめにはできないのですよ。ものすごくエネルギが要るので……。


btn_arduino.png

btn_prev.png btn_next.png

home