「ガレージ外」のマイクロプロセッサ

エレクトロニクスとFPGAを扱うすべての人がopencores.orgのウェブサイトに精通していることを確認してください。opencores.orgのウェブサイトには、電子機器向けの多くの便利な(そしてそうではない)ソリューションが含まれています-数十、数百、プロセッサと周辺機器の実装-両方とも既存のデバイスの元の実装、および新しい開発。 この記事では、Mars rover2ボードに基づいて作成されたオリジナルの命令システムを備えた32ビットマイクロプロセッサーについて説明します。



私たちのチームは10年にわたってL4マイクロカーネルに取り組んできましたが、ある時点で、マイクロカーネル自体をプロセッサユニットとして実装できることがわかりました。 さらに、ハードウェアに本格的なマイクロカーネルを実装することが非常に難しい場合は、少なくとも一部の機能をハードウェアにシフトすることで、ソフトウェア部分を支援できます。 最初に、簡単で最適なパスに沿って進むことにしました。既存のソリューションを検討し、適切なソリューションを選択して、マイクロカーネルに役立つファイル追加します 。 作業には約1か月かかり、オープンコアで見つかったほとんどすべてのソリューションが調査されました。 既製のソリューションを基礎として採用することで、既製のコンパイラーやさまざまなライブラリーという形で、非常に良い機会が開かれました。 ある時点で、既存のソリューションが気に入らなくなりました-複雑であることが判明したもの、最適ではないもの、未完成のもの、非表示にするものは、あまり適切なライセンスと使用条件を持っていませんでした。 勇気を得て歯を食いしばって、ギャンブルを始め、最初からプロセッサーを開発することを決めました。



マイクロプロセッサは何から始まりますか? システムプログラマーに尋ねると、彼はこれがコマンドシステムであると答えます。 RISCアーキテクチャの全体的な流行にもかかわらず、命令の長さを機械語のサイズに結び付けないことにしました。 そのため、いくつかの実験を行いました。 奇妙なことに、コマンドシステム... Microsoft Excelを設計するための非常に便利なツールであることが判明しました。 まず、いくつかの列を強調表示し、3つの計算システム(10進数、16進数、2進数)の番号付け命令に使用します。 1バイトで記述できる状態の数に応じて、結果は256行でした。 次に、デコードスキームができるだけ単純になるように、命令を論理的にグループ化しようとしました。 命令の最初のブロックは、プレフィックス、修飾子、および単純な命令のシングルバイト命令で占められていました。 次の指示ブロックは次のようになります。



画像



次の段階で、レジスタの数とタイプを決定する必要がありました。 ほとんどのタスクに最適なレジスタはいくつあると思いますか? この質問に対する答えは、応答する人の性格によって大きく異なります。32は、非登録アーキテクチャの支持者がいるため、誰かにとって十分ではありません。 16個の汎用レジスターを使用することにしました。 この量は、アセンブリ言語プログラミングに非常に便利で、アーキテクチャ上で非常にうまく機能し、HDLで簡単に実装できます。



レジスタを決定したので、完全に位置的に独立したコマンドシステムを作成することにしました。アーキテクチャには、絶対アドレスへの単一の移行コマンドはありません。すべての移行は、現在のコマンドに対して実行されます。 単純にコンパクトに取り付かれているため、すべての移行コマンドには3つの形式(1、2、3バイトの符号付きオフセット)があります。 たとえば、16ビットオフセットの遷移を以下に示します。



画像



最後に、ハードウェアスタックの概念を放棄し、「合意に基づいて」スタックを整理することにしました。 このために、特別なNOTCHプレフィックスが導入され、次のスキームが使用されます-条件付きまたは無条件のジャンプ命令にこの命令のプレフィックスが付いている場合、次の命令のアドレスがレジスタR15に配置されます。 住所を返します。 したがって、RETURN命令はレジスタR15の内容をジャンプします。 したがって、ネストされたサブプログラムの呼び出しでは、戻りアドレスの保存はプログラマーまたはコンパイラーに任されます。 一見すると、これはあまり便利で、最適で、馴染みがないように思えますが、考えてみるといくつかの利点があります-まず、ターミナルルーチン(つまり、他の関数を呼び出さないルーチン)の外部メモリにこのレジスタを保存せずに、いくつかのクロックサイクルを節約できますサブルーチン)、第二に、NOTCHプレフィックスを条件分岐命令の前に置くことができ、それにより条件付き関数呼び出しを実現できます-小さいながらも経済的です。 アセンブラでのプログラミングの複雑さについては、マクロによって隠されています。マクロは、より高いレベルのアセンブラニーモニックです。



コードの位置の独立性は別の機能を導入します-定数データへのアクセス。 コードは任意のアドレスに配置できるため、コードを使用して定数データを任意に配置することもできます。 ソリューションは非常に簡単であることが判明しました-定数をレジスタにロードするときに同じNOTCHプレフィックスを使用すると、実行可能命令に対するオフセットとして定数が使用されます。これにより、位置独立コードのデータのアドレス指定の問題が解決します。



通常約1年かかったコマンドシステムを設計した後、QauartusおよびIcarus Verilog環境で武装し、急いでいることに気付きました。 Verilogのコマンドシステムの実装は非常に複雑であることが判明しました。 知識のある人は、通常のCでデコーダーやその他の機能デバイスを記述することにより、ソフトウェアモデルでソリューションを実行することを勧めました。 存在しないプロセッサのエミュレータを実装し、その上でテストプログラムを実行すると、事態は改善されました。 Verilogにプロセッサを実装するには、さらに6か月が必要でした。 初心者向けのFPGAプログラミングは非常に複雑になる可能性があり、高水準言語での長年のプログラミング経験はタスクを複雑にすることさえあると言わなければなりません。 この場合、モデリングツールが役立ちます。 最初の段階では、信号を表示するプログラムであるGTKWaveに付属する無料の回路シミュレーションツールであるIcarus Verilogが非常に有用であることが判明しました。 これらのツールを使用すると、いつでもデバイスに何が起こるかを確認できます。 ある段階で、Icarus Verilogの機能が少なくなり、非常に強力な商用ツールであるMentorGraphis ModelSimシミュレーターを使用しました。このツールは、アルテラQuartusで無料でインストールできます。



デバッグプロセスについて長い間話すことができます。 そしてある時点で、FPGAリソースが3分の1に占有されたときに、突然、結果のプロセッサがいくつかのプロジェクトで使用できるという理解がありました。







プロセッサの機能を示すために、起動時にリモート端末の画面に次のメニューを表示する最も単純なファームウェアを作成しました。



ようこそ───────────────────────>エベレストコアへようこそ<─────────────────── ─────────┐
 │1-X-modemプロトコル経由でバイナリファイルをロード│
 │2-以前にロードされたバイナリファイルを実行する│
 │3-RAMを表示(0x100000-0x100140)│
 │4-メッセージレジスタのテスト│
 │5-以前にロードしたANSI画像を表示│
 │6-組み込みANSI写真#1を表示│
 │7-組み込みのANSI写真#2を表示│
 ────────────└───────────────────────── ───────────────────────────────┘




1を押し、端末がXモデムプロトコルを使用したファイル転送をサポートしている場合、最大4 KBのサイズのファイルをダウンロードできます。 テキストまたはANSI画像を使用できます。この場合、キー5を押すと、テキストまたは画像が画面に表示されます。 しかし、これに関する記事を書く価値はありますか? したがって、もちろん、ターミナルで2キーを押すと、最初のメニュー項目を使用してロードされたコードに制御が移ります。 ダウンロードしたテキストまたはANSIピクチャに制御を移すと、数ステップ後にプロセッサは存在しない(まだ不定のコマンド)か、存在しないメモリにアクセスします。 この場合、プロセッサはステップバイステップモードになります。ターミナルから受信した各コードは、リモートターミナルへのバスステータスの出力で1つのプロセッサ命令を実行します。



画像



リセットキーを押す時間です。 Mars rover2ボードの左ボタンを「リセット」と呼びました。



デバイスに意味のあることをさせるには、 マクロアセンブラーが必要です。 このアーカイブでは、アセンブラ自体といくつかの例に加えて、プロセッサマイクロコードのソースコードを配置しました。 以下は、アセンブラを使用してバイナリファイルに変換し、プロセッサにロードできる単純なユーザープログラムの例です。



function user_main load r14, 0x2000 push r15 loop: call _get_sysclock load r2, 0x05F5E100 call _div64 call _print_dec lea r1, $shw_str call _puts call _uart_status rcr r0, 2 ;  RCV_RDY   jc done ;        load r0, 0x01000000 call _delay jmp loop done: pop r15 return end include tty.asm include delay.asm include mul.asm include div.asm include print_dec.asm include sysclock.asm $shw_str db ' seconds since boot',13,10,0
      
      







このプログラムは、端末のいずれかのキーを押す前のサイクルで、デバイスが起動またはリモート端末にリセットされてからの秒数に関する情報を表示します。 実際に確認するには、生成されたusr_demo2.binファイルが必要です。



プログラムの簡単な説明。 _get_sysclockサブルーチンは、デバイスがオンまたはリセットされた瞬間から水晶発振器のパルス数を返します。 サブルーチンダンプの例:

 ; ------------------- _get_sysclock ------------------ 0198: 37 e3 ; DEC R14, 4 019a: 60 e3 ; MOV (R14), R3 019c: e3 ff fe ff f8 ; LOAD R3, 0xfffefff8 01a1: 68 03 ; MOV R0, (R3) 01a3: 36 33 ; INC R3, 4 01a5: 68 13 ; MOV R1, (R3) 01a7: 68 3e ; MOV R3, (R14) 01a9: 36 e3 ; INC R14, 4 01ab: 05 ; RETURN
      
      







_get_sysclockサブルーチンを終了するときレジスタR0には下位32ビットが含まれ、レジスタR1には結果の上位32ビットが含まれます。

定数0x05F5E100は、1秒間のクロックのパルス数です。



Mars rover2ボードの最新ファームウェアはこちらからダウンロードできます。



私たちのプロジェクトからのニュースが聞こえない場合は、L4マイクロカーネルをFPGAに転送する作業を行っていることを知っておく必要があります。

ご清聴ありがとうございました。



All Articles