2019年12月29日 修正 XilinxのFPGA(XC7A35TICSG324-1L)を搭載したArtyに実装した結果の更新
概要
新型の8bit CPU(ICF3-Z)を設計してFPGAに実装。仮想マシンの加速支援機構を使った、簡単なスタックマシンで、サンプルプログラムがXilinx FPGA Artix-7(-1)の実機上で動作した。たった約400LUTの小さいCPUで。Intel 8051マイコンのFREEな実装にlight52があります。軽量なマイコンですがCPU部だけで約800LUTあるようです。light52は1命令、6サイクルですが、このICF3-Zは1命令、1サイクルが基本で、時に1サイクルに複数命令の実行ができます。周波数比較もICF3-Zが、おおよそ2倍のようで、圧倒的に勝ったのかも。他にあまり比較できるものがなかったという話もありますが。
仮想マシンの加速支援機構とは
新型8bit CPU(ICF3-Z)なのですが命令コードが32bitで、一般的な8bit CPUと比べてメモリ効率が悪い。そこで16bitの圧縮命令の機能を追加した。この機能が仮想マシンの加速支援機構なのです。 圧縮命令のために作られたもので、加速支援機構として、最終的に成立するのかは、わからないですが、需要があって無理がなければ、加速支援機構として考えていく方向です。
加速支援機構が何の役に立つのか?
超低スペックなCPUでは、ソフトウエアで仮想マシンを実装しても性能的な問題で用途が狭められることがあります。この加速支援機構があれば実用の範囲が広がります。
2019年11月23日 追記 仮想マシンの命令列を大容量の安価なプログラムメモリに置けるので、容量の少ない高価なデータメモリを圧迫しないということも利点です。
なぜ低スペックなCPUで仮想マシンなのか?
仮想マシンをJavaのようなスタックマシンにしたり、レジスタ1個のマシンにしたりできます。 CPUのアーキテクチャの違いを吸収して、いろいろな言語のコンパイラの開発を容易にします。 非C言語が利用できるようになることで、より多くの人がIoTデバイスなどを開発できるようになります。
試作プログラム
プログラムの上部で簡単なスタックマシンの命令を実装して、仮想マシンの命令列をFPGAの実機で動作させたものです。独自のアセンブラなので多少、わかりにくかもしれません。自作したアセンブラでバイナリを生成して、FPGAのプログラムメモリに、置きます。 即値の5をPUSH、メモリの0番の値をPUSH、メモリの1番の値をPUSH、乗算、加算、LEDに結果を出力。 メモリ0番には、最初のところで2を書き込んでいます。メモリ1番には3。つまり3*2+5 = 11がLEDに出力されます。(確認しました)
######################################################## # vm (Virtual Machie) # # C Register : SP(Stack Pointer) # D Register : PRINT (LED x 8) # MEM[0-255] : Memory # # PUSHI: SP=SP-1 , MEM[SP]=nn (5cyc) # PUSHR: SP=SP-1 , MEM[SP]=MEM[nn] (5cyc) # POP : MEM[nn]=MEM[SP] , SP=SP+1 (4cyc) # ADD : MEM[SP+1]=MEM[SP]+MEM[SP+1] , SP=SP+1 (7cyc) # MUL : MEM[SP+1]=MEM[SP]*MEM[SP+1] , SP=SP+1 (16cyc) # PRINT: PRINT MEM[SP] ######################################################## # Initial MEM[0]=0x02 , MEM[1]=0x03 ######################################################## ADDL=N;N=0x02;A=ANS STORE;Z=0 ADDL=N;N=0x03;A=ANS B(START) STORE;Z=1;C=ANS;D=ANS ######################################################## %NOP: JRETURN %PUSHI: ADDR=C;ADDL=N;N=0xFF;C=ANS RETURN;COPR;ADDL=N;A=ANS ADDR=C;R(ADDR);STORE %PUSHR: ADDR=C;ADDL=N;N=0xFF;C=ANS RETURN;R(N);COPR MOV;ADDR=C;R(ADDR);STORE %POP: RETURN;R(ADDR);ADDR=C;ONE;C=ANS MOV;COPR;R(N);STORE %ADD: R(ADDR);ADDR=C;ONE;C=ANS B=REG;R(ADDR);ADDR=C B=REG;ADDR=B;A=ANS RETURN;ADDL=A;ADDR=B;A=ANS R(ADDR);ADDR=C;STORE %MUL: R(ADDR);ADDR=C;ONE;C=ANS;D=ANS C=REG;R(ADDR);ADDR=C C=REG;ADDR=C;A=ANS B=ANS;I=X;X=7 MUL;ADDL=A;ADDR=B;B=RSH;C=RSH;WAIT RETURN;ADDR=C;A=ANS R(ADDR);ADDR=D;STORE;C=ANS %PRINT: RETURN;R(ADDR);ADDR=C D=REG START: _%PUSHI,0x05,%PUSHR,0x00 _%PUSHR,0x01,%MUL,0x00 _%ADD,0x00,%PRINT,0x00 EXIT(0);@D END: B(END) NOP
ちなみに
割込みの実装もついていて約400LUTです。2つの割込みを受けられます。
2019年12月29日更新
XilinxのFPGA(XC7A35TICSG324-1L)に搭載したArtyに実装して面積を確認しています。 論理合成のオプションで結果が違うので、面積重視、性能重視の結果です。
合成方針 | LUT数 | FF数 | BRAM数 | LUT-RAM | 周波数 |
---|---|---|---|---|---|
面積重視 | 390 | 197 | 1.5 | 10 | 150MHz |
面積重視 | 406 | 197 | 1.5 | 10 | 160MHz |
性能重視 | 481 | 197 | 1.5 | 10 | 175MHz |
BRAM 1.5の内訳はプログラムメモリ 4KBとデータメモリ 2KB
データメモリの先頭32バイトがレジスタ、先頭256バイトがスクラッチパッドメモリ。この合成結果はデータのアドレス線を8bitにしたものです。11bitにすればBRAMを消費することなく2KBのデータメモリをすべて使えます。データのアドレス線を16bitまで設定可能ですがBRAMを消費します。圧縮命令(仮想マシン)を利用する場合はプログラムメモリの先頭の領域を消費します。圧縮命令の命令数によって消費する領域が変化します。
割込みを使ったデバッグ機能の検証をしました。 プログラムのコード中に、ブレークポイントを設定して、そこに、たどり着いた回数を計算して、設定した値(パスカウント)のとき、ブレークさせるみたいな機能が実現できます。 具体的には毎サイクル割込みを入れて、アドレスを確認して、設定されたアドレスなら、指定された回数に到達したかをチェック。 指定された回数の場合は、LEDを点灯させるというプログラムを作成して、FPGAの実機でテストしました。ちゃんと動いているようです。 ただプログラムコード中の一部の命令ではパスカウントを正しく計算できません。遅延分岐で遅延スロットをキャンセルする命令などです。 分岐命令の直後の命令にブレークポイントを設定した場合は、パスカウントは正しくありませんという仕様にしようかと考えています。 割込み処理中でキャンセル信号をチェックしてもいいのですが、 デバッグ機能のためにキャンセル信号をチェックする論理を追加するのは面積最小を追求するプロセッサでは、あまり良くないと考えてのことです。 (面倒とかではなくて)
約400 LUTの面積の小さいCPUで、追加のデバッグモジュールなしにパスカウントを 使ったブレークポイントを設定できるのは、良くできるほうなんだろうと思うが、 他のCPUの面積とかわからないから、なんとも。