プロジェクト | VHDL で書かれた Forth SoC |
---|---|
著者 | リチャード・ジェームス・ハウ |
著作権 | 2013-2019 リチャード・ハウ |
ライセンス | MIT/LGPL |
電子メール | [email protected] |
このプロジェクトは、J1 CPU に基づいて Forth を実行するように調整された小型スタック コンピューターを実装します。プロセッサは Verilog から VHDL で書き直され、わずかに拡張されています。
プロジェクトの目標は次のとおりです。
3つとも完成しました。
H2 プロセッサは、J1 と同様、FORTH に特に適した命令セットを実行するスタックベースのプロセッサです。
現在のターゲットは、Xilinx Spartan-6 XC6LX16-CS324 FPGA を搭載した Nexys3 ボードですが、このボードは製品寿命に近づいているため、将来的には新しいボードがターゲットになる予定です。 VHDL は、ハードウェア コンポーネントが明示的にインスタンス化されるのではなく推論される一般的な方法で記述されているため、コードはかなり移植可能になりますが、Nexys3 ボード コンポーネントへのインターフェイスはそのボード上のペリフェラルに固有です。
ハードウェア上で実際に実行されているプロジェクトのビデオは、ここで見ることができます。
SoC は、以下に示すように、C で記述されたシミュレーターを使用してシミュレートすることもできます。
システムアーキテクチャは次のとおりです。
プロジェクトで使用されるライセンスは混合されており、ファイル単位で使用されます。私のコードでは MIT ライセンスを使用していますので、ご自由にお使いください。使用される他のライセンスは LGPL ライセンスと Apache 2.0 ライセンスです。これらは単一モジュールに限定されているため、LGPL コードに嫌悪感がある場合は削除できます。
現時点で利用可能な唯一のターゲット ボードは Nexys3 ですが、ボードは現在サポートが終了しているため、これは将来変更されるはずです。私が次にサポートしたいと考えているボードは、その後継である Nexys 4 と myStorm BlackIce (https://mystorm.uk/) です。 myStorm ボードは、合成、配置配線、ビット ファイル生成に完全なオープン ソース ツールチェーンを使用します。
このビルドは Debian Linux バージョン 8 でテストされています。
必要なものは次のとおりです。
ハードウェア:
Xilinx ISE は無料でダウンロードできます (またはダウンロードできる) が、登録が必要です。 ISE はパス上にある必要があります。
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64;
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/lib/lin64;
C ベースのツールチェーンを作成するには:
make embed.hex
ターゲット ボードにフラッシュできるビット ファイルを作成するには:
make simulation synthesis implementation bitfile
ビットファイルをターゲットボードにアップロードするには:
make upload
「シミュレーションの作成」によって生成された波形を表示するには:
make viewer
C ベースの CLI シミュレーターは、次のコマンドで呼び出すことができます。
make run
これにより、H2 Forth ソース ファイル embed.fth がアセンブルされ、デバッガがアクティブ化された H2 シミュレータでアセンブルされたオブジェクト ファイルが実行されます。グラフィカル シミュレーターは次のもので実行できます。
make gui-run
これには freeglut と C コンパイラが必要です。
オリジナルの J1 プロジェクトは次の場所から入手できます。
このプロジェクトはオリジナルの J1 コアをターゲットにしており、eForth 実装を提供します (J1 コアへのメタコンパイル/クロス コンパイルとして Gforth を使用して記述されています)。 C で書かれたシステムのシミュレータも提供します。
メタコンパイラーの基盤となる eForth インタープリターは、次の場所にあります。
H2 プロセッサと関連ペリフェラルは現在非常に安定していますが、レジスタ マップと同様に、命令とペリフェラルがどのように動作するかについては、ソースが常に最終的なガイドとなります。
J1 CPU には次のような変更がいくつかあります。
H2 CPU は J1 CPU と非常によく似た動作をするため、このプロセッサをよりよく理解するには J1 PDF を読むことができます。プロセッサは 16 ビットで、命令は 1 クロック サイクルかかります。プリミティブ Forth ワードのほとんどは単一サイクルでも実行できますが、注目すべき例外の 1 つはストア (「!」) で、これは 2 つの命令に分割されます。
CPU 内には次の状態があります。
H2 プログラムを保持するブロック RAM へのロードおよびストアでは最下位ビットが破棄され、他のすべてのメモリ操作では下位ビットが使用されます (ジャンプ、入出力ペリフェラルへのロードおよびストアなど)。これは、アプリケーションがプログラム RAM にアクセスするときに文字操作に最下位ビットを使用できるようにするためです。
命令セットは次の方法でデコードされます。
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
| 1 | LITERAL VALUE |
+---------------------------------------------------------------+
| 0 | 0 | 0 | BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 0 | 1 | CONDITIONAL BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 0 | CALL TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 1 | ALU OPERATION |T2N|T2R|N2A|R2P| RSTACK| DSTACK|
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
T : Top of data stack
N : Next on data stack
PC : Program Counter
LITERAL VALUES : push a value onto the data stack
CONDITIONAL : BRANCHS pop and test the T
CALLS : PC+1 onto the return stack
T2N : Move T to N
T2R : Move T to top of return stack
N2A : STORE T to memory location addressed by N
R2P : Move top of return stack to PC
RSTACK and DSTACK are signed values (twos compliment) that are
the stack delta (the amount to increment or decrement the stack
by for their respective stacks: return and data)
すべての ALU 演算は T を置き換えます。
価値 | 手術 | 説明 |
---|---|---|
0 | T | スタックのトップ |
1 | N | T を N にコピー |
2 | T+N | 追加 |
3 | T&N | ビットごとの AND |
4 | TまたはN | ビットごとの OR |
5 | T ^ N | ビットごとの XOR |
6 | ~T | ビットごとの反転 |
7 | T = N | 平等性テスト |
8 | N < T | 符号付き比較 |
9 | N >> T | 論理右シフト |
10 | T-1 | デクリメント |
11 | R | リターンスタックの先頭 |
12 | [た] | アドレスからロード |
13 | N << T | 論理左シフト |
14 | 深さ | スタックの深さ |
15 | Nu符号なし比較 | |
16 | CPU 状態の設定 | 割り込みを有効にする |
17 | CPU状態の取得 | 割り込みはオンになっていますか? |
18 | 深さ | 戻り深さstk |
19 | 0= | T == 0? |
20 | CPUID | CPU識別子 |
21 | リテラル | 社内指示 |
「o」の接頭辞が付いているレジスタは出力レジスタであり、「i」の接頭辞が付いているレジスタは入力レジスタです。レジスタはレジスタの入力セクションと出力セクションに分かれており、入力レジスタと出力レジスタのアドレスは常に一致するわけではありません。
Nexys3 ボード上のデバイスとインターフェイスするために、次のペリフェラルが VHDL SoC に実装されています。
SoC は、有効または無効にできる限定された割り込みセットも備えています。
出力レジスタマップ:
登録する | 住所 | 説明 |
---|---|---|
アート | 0x4000 | UARTレジスタ |
oVT100 | 0x4002 | VT100 ターミナル書き込み |
LED | 0x4004 | LED出力 |
oタイマーコントロール | 0x4006 | タイマー制御 |
oMemDout | 0x4008 | メモリデータ出力 |
oMemControl | 0x400A | メモリ制御 / Hi アドレス |
oMemAddrLow | 0x400C | メモリLoアドレス |
o7セグLED | 0x400E | 4 x LED 7 セグメントディスプレイ |
oIrcマスク | 0x4010 | CPU割り込みマスク |
oUartBaudTx | 0x4012 | UART Tx ボークロック設定 |
oUartBaudRx | 0x4014 | UART Rx ボークロック設定 |
入力レジスタは次のとおりです。
登録する | 住所 | 説明 |
---|---|---|
iUart | 0x4000 | UARTレジスタ |
iVT100 | 0x4002 | 端末ステータスとPS/2キーボード |
iスイッチ | 0x4004 | ボタンとスイッチ |
iTimerDin | 0x4006 | 現在のタイマー値 |
アイメムディン | 0x4008 | メモリデータ入力 |
以下のレジスタの説明を順番に読み、ペリフェラルがどのように動作するかについても説明します。
SoC には、固定ボー レートとフォーマット (115200、8 ビット、1 ストップ ビット) の UART が存在します。 UART には、RX チャネルと TX チャネルの両方に深さ 8 の FIFO があります。 UART の制御は、oUart と iUart に分割されます。
UART に値を書き込むには、TXDO にデータを入れるとともに TXWE をアサートします。 FIFO の状態は、iUart レジスタを調べることで分析できます。
UART から値を読み取るには、iUart をチェックして FIFO にデータが存在するかどうかを確認できます。oUart レジスタで RXRE がアサートされている場合、次のクロック サイクルでデータが iUart レジスタに存在します。
UART のボーレートは VHDL プロジェクトを再構築することで変更できます。ビット長、パリティ ビット、ストップ ビットは uart.vhd を変更することでのみ変更できます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X |TXWE| X | X |RXRE| X | X | TXDO |
+-------------------------------------------------------------------------------+
TXWE: UART TX Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
VGA Text デバイスは、ユーザーが oVT100 レジスタに書き込むことで通信できる端末をエミュレートします。 VT100 端末機能のサブセットをサポートします。このインターフェイスは、同じビジー信号と制御信号を使用して UART に書き込むのとよく似た動作をします。入力はボード上で利用可能な PS/2 キーボードから取得され、これは UART の RX メカニズムのように動作します。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X |TXWE| X | X |RXRE| X | X | TXDO |
+-------------------------------------------------------------------------------+
TXWE: VT100 TX Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
Nexys3 ボードにはスイッチの隣に LED のバンクがあり、LEDO に書き込むことでこれらの LED をオン (1) またはオフ (0) にできます。ここでの各 LED は、その隣にあるスイッチに対応します。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | X | X | X | X | X | LEDO |
+-------------------------------------------------------------------------------+
LEDO: LED Output
タイマーは oTimerCtrl レジスタによって制御可能で、100MHz で動作する 13 ビット タイマーです。オプションで割り込みを生成でき、現在のタイマーの内部カウントは iTimerDin レジスタで読み戻すことができます。
TE ビットがアサートされるとタイマーはカウントし、TCMP 値に達するとラップアラウンドし、オプションで INTE をアサートすることで割り込みを生成できます。これにより、タイマーから出てボード上のピンに配線される Q ラインと NQ ラインも切り替わります (ピンについては制約ファイル top.ucf を参照してください)。
タイマはRSTに書き込むことでリセットできます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| TE | RST|INTE| TCMP |
+-------------------------------------------------------------------------------+
TE: Timer Enable
RST: Timer Reset
INTE: Interrupt Enable
TCMP: Timer Compare Value
H2 コアには割り込みのメカニズムがあり、命令で割り込みを有効または無効にする必要があります。 IMSK のビットを使用して各割り込みをマスクオフし、その特定の割り込みを有効にすることができます。 IMSK のビットに「1」があると、その特定の割り込みが有効になり、CPU 内で割り込みが有効になっている場合、その割り込みが CPU に送信されます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | X | X | X | X | X | IMSK |
+-------------------------------------------------------------------------------+
IMSK: Interrupt Mask
このレジスタは、送信専用のボーとサンプル クロック周波数を設定するために使用されます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BTXC |
+-------------------------------------------------------------------------------+
BTXC: Baud Clock Settings
このレジスタは受信専用のボーとサンプルクロック周波数を設定するために使用されます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BRXC |
+-------------------------------------------------------------------------------+
BRXC: Baud Clock Settings
oMemControlでライトイネーブル(WE)発行時に選択したアドレスに出力されるデータ。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Ouput |
+-------------------------------------------------------------------------------+
このレジスタには、Nexys3 ボード上のオンボード メモリの制御レジスタが含まれています。このボードには 3 つのメモリ デバイス、2 つの不揮発性メモリ デバイス、および 1 つの揮発性 RAM ベースのデバイスが含まれています。シンプルな SRAM インターフェイスでアクセスできる 2 つのデバイス (1 つは揮発性 M45W8MW16、もう 1 つは不揮発性 - NP8P128A13T1760E) は両方ともアクセス可能で、3 つ目は SPI ベースのメモリ デバイス NP5Q128A13ESFC0E) ですが、現在アクセスできません。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| OE | WE | RST|WAIT| RCS| FCS| Address Hi |
+-------------------------------------------------------------------------------+
OE: Output Enable - enable reading from current address into iMemDin
WE: Write Enable - enable writing oMemDout into ram at current address
RST: Reset the Flash memory controller
RCS: RAM Chip Select, Enable Volatile Memory
FCS: Flash Chip Select, Enable Non-Volatile Memory
Address Hi: High Bits of RAM address
OE と WE は相互に排他的であり、両方を設定しても効果はありません。
メモリ コントローラーは現在開発中であり、そのインターフェイスは変更される可能性があります。
これは RAM の下位アドレス ビットです。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Address Lo |
+-------------------------------------------------------------------------------+
Nexys3 ボードには、小数点 (実際には 8 セグメント) を備えた 7 セグメント ディスプレイのバンクがあり、数値出力に使用できます。 LED セグメントを直接アドレス指定することはできません。代わりに、L8SD に格納されている値は 16 進表示値 (または BCD 値、ただし、これには SoC の再生成と VHDL のジェネリックの変更が必要です) にマップされます。
値「0」は LED セグメントに表示されるゼロに対応し、「15」は「F」などに対応します。
ディスプレイが4つ並んでいます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| L7SD0 | L7SD1 | L7SD2 | L7SD3 |
+-------------------------------------------------------------------------------+
L7SD0: LED 7 Segment Display (leftmost display)
L7SD1: LED 7 Segment Display
L7SD2: LED 7 Segment Display
L7SD3: LED 7 Segment Display (right most display)
iUart レジスタは、oUart レジスタと連携して動作します。バイトの送信と受信の両方をバッファリングする FIFO のステータスは、受信したバイトだけでなく、iUart レジスタでも確認できます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X |TFFL|TFEM| X |RFFL|RFEM| RXDI |
+-------------------------------------------------------------------------------+
TFFL: UART TX FIFO Full
TFEM: UART TX FIFO Empty
RFFL: UART RX FIFO Full
RFEM: UART RX FIFO Empty
RXDI: UART RX Data Input
iVT100 レジスタは、oVT100 レジスタと連携して動作します。バイトの送信と受信の両方をバッファリングする FIFO のステータスは、受信したバイトだけでなく、iVT100 レジスタでも確認できます。これは、iUart/oUart レジスタと同じように機能します。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X |TFFL|TFEM| X |RFFL|RFEM| 0 | ACHR |
+-------------------------------------------------------------------------------+
TFFL: VGA VT100 TX FIFO Full
TFEM: VGA VT100 TX FIFO Empty
RFFL: PS2 VT100 RX FIFO Full
RFEM: PS2 VT100 RX FIFO Empty
ACHR: New character available on PS2 Keyboard
このレジスタには、タイマー カウンターの現在値が含まれます。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | TCNT |
+-------------------------------------------------------------------------------+
TCNT: Timer Counter Value
iSwitch には、複数のソースからの入力行が含まれています。ボタン (BUP、BDWN、BLFT、BRGH、および BCNT) は、Nexys3 ボードの D-Pad に対応します。スイッチ (TSWI) は oLed で説明されているもので、それぞれの隣に LED があります。
スイッチとボタンはハードウェアですでにデバウンスされているため、これらのレジスタから読み込まれた後はさらに処理する必要はありません。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | BUP|BDWN|BLFT|BRGH|BCNT| TSWI |
+-------------------------------------------------------------------------------+
BUP: Button Up
BDWN: Button Down
BLFT: Button Left
BRGH: Button Right
BCNT: Button Center
TSWI: Two Position Switches
SRAM またはフラッシュからのメモリ入力。oMemControl および oMemAddrLow によってインデックス付けされます。フラッシュから読み取る場合、これは実際にはステータス情報またはクエリ テーブルからの情報である可能性があります。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Input |
+-------------------------------------------------------------------------------+
次の割り込みサービス ルーチンが定義されています。
名前 | 番号 | 説明 |
---|---|---|
isrNone | 0 | 未使用 |
isrRxFifoNotEmpty | 1 | UART RX FIFO が空ではない |
isrRxFifoFull | 2 | UART RX FIFI がいっぱいです |
isrTxFifoNotEmpty | 3 | UART TX FIFO が空ではない |
isrTxFifoFull | 4 | UART TX FIFO がいっぱいです |
isrKbdNew | 5 | 新しい PS/2 キーボード キャラクター |
isrTimer | 6 | タイマーカウンター |
isrDパッドボタン | 7 | D-Pad ボタンの状態変更 |
割り込みが発生し、プロセッサ内で割り込みが有効になると、メモリ内の場所への呼び出しが実行されます。その場所は ISR 番号と同じです。たとえば、番号「4」の ISR は、メモリ内の位置「4」への呼び出し (ジャンプではなく) を実行します。
割り込みは、処理されるまでに少なくとも 4 ~ 5 サイクルのレイテンシーがあり、割り込み要求ハンドラーには 2 ~ 3 サイクルの遅延があり、その後、メモリ内の ISR 位置への呼び出しを実行する必要があり、その後、 ISR 自体を実装するワード。
2 つの割り込みが同時に発生した場合、それらは最小の割り込み番号から最大の割り込み番号まで処理されます。
同じ番号の割り込みが発生して処理されなかった場合、割り込みは失われます。
H2 用の逆アセンブラと C ベースのシミュレータは単一のプログラム内にあります (h2.c を参照)。このシミュレータは VHDL テストベンチ tb.vhd を補完するものであり、それに代わるものではありません。メタコンパイラーは eForth インタープリター上で実行され、embed.c および embed.blk ファイル内に含まれています。メタコンパイラー (クロスコンパイラーの Forth 用語) は、ターゲット上で実行される eForth イメージの作成に使用される Forth プログラムです。
ツールチェーンは現在流動的であり、今後は、セルフホスティングを作成するという長期的な目標に向けて、埋め込み仮想マシンを H2 CPU によりよく似たものに変更するとともに、h2.c と embed.c の統合がさらに進む可能性があります。システム。
両方をビルドするには、C コンパイラが必要です。ビルド ターゲット「h2」は実行可能ファイル h2 をビルドし、「embed」はメタコンパイラをビルドします。
make h2 embed
そして、make ターゲットを使用してソース ファイル embed.fth 上で実行できます。
make run
メイクファイルは必要ありません。
Linux:
cc -std=c99 h2.c -o h2 # To build the h2 executable
cc -std=c99 embed.c -o embed # To build the embed VM executable
./embed embed.blk embed.hex embed.fth # Create the target eForth image
./h2 -h # For a list of options
./h2 -r embed.hex # Run the assembled file
Windows:
gcc -std=c99 h2.c -o h2.exe # Builds the h2.exe executable
gcc -std=c99 embed.c -o embed.exe # Builds the embed.exe executable
embed.exe embed.blk embed.hex embed.fth # Create the target eForth iamge
h2.exe -h # For a list of options
h2.exe -r embed.hex # Run the assembled file
使用可能なコマンド ライン オプションのリスト:
- stop processing options, following arguments are files
-h print a help message and exit
-v increase logging level
-d disassemble input files (default)
-D full disassembly of input files
-T Enter debug mode when running simulation
-r run hex file
-L # load symbol file
-s # number of steps to run simulation (0 = forever)
-n # specify NVRAM block file (default is nvram.blk)
file* file to process
このプログラムは MIT ライセンスに基づいてリリースされていますので、ご自由に使用および変更してください。最小限の変更で、元の J1 コア用のプログラムをアセンブルできるはずです。
メタコンパイラーは、組み込み仮想マシン上で実行されます。これは、もともと H2 CPU から派生した 16 ビット仮想マシンです。このプロジェクトには、eForth イメージが変更を加えて新しい eForth イメージを生成できるようにするメタコンパイル スキームが含まれています。このシステムは H2 での使用に適応されており、C で書かれたクロス コンパイラを置き換え、H2 の最初のイメージを作成できるようになりました。
メタコンパイラーは通常の Forth プログラムであり、embed.fth 内に含まれています。次に、メタコンパイラー Forth プログラムを使用して、H2 ターゲット上で実行できる eForth イメージを構築します。
Forth のメタコンパイルの詳細については、次を参照してください。
逆アセンブラは、16 ビットの 16 進数で構成される、アセンブルされたプログラムを含むテキスト ファイルを受け取ります。次に、命令を逆アセンブルしようとします。また、アセンブラによって生成されるシンボル ファイルを供給して、ジャンプや呼び出しが指す場所の検索を試みることもできます。
逆アセンブラは GTKwave によって呼び出される tcl スクリプトによって使用され、一連の数値から H2 の命令トレースを命令とそれらが表す分岐先に変換します。これにより、VHDL のデバッグがはるかに簡単になります。
紫色のトレースは、逆アセンブルされた命令を示します。
C のシミュレーターは、H2 コアとほとんどの SoC を実装します。シミュレータの IO はサイクル精度が高くありませんが、プログラムの実行とデバッグに使用でき、ハードウェアの動作と非常に似た結果が得られます。これは、FPGA のフラッシュに使用されるビット ファイルを再構築するよりもはるかに高速です。
シミュレータには、DOS で使用できる DEBUG.COM プログラムと同様に設計されたデバッガも含まれています。デバッガを使用すると、メモリのセクションを逆アセンブルし、周辺機器のステータスを検査し、メモリのセクションを画面にダンプすることができます。また、ブレークポイントを設定し、シングルステップでブレークポイントに到達するまでコードを実行するためにも使用できます。
デバッガを実行するには、hex ファイルまたはソース ファイルを指定する必要があります。
# -T turns debugging mode on
./h2 -T -r file.hex # Run simulator
どちらの操作モードも、組み立てられたコアで変数、ラベル、関数が配置される場所をリストするシンボル ファイルを使用して拡張できます。
「-T」オプションを指定すると、シミュレーションが実行される前にデバッグ モードに入ります。プロンプトが表示され、コマンド ラインは次のようになります。
$ ./h2 -T -R h2.fth
Debugger running, type 'h' for a list of command
debug>
ブレーク ポイントは、記号的に、またはプログラムの場所によって設定できます。ブレークポイントの設定には、「b」コマンドが使用されます。
数値は、8 進数 (数値の先頭に「0」を付ける)、16 進数 (先頭に「0x」を付ける)、または 10 進数で入力できます。たとえば、次の 3 つのデバッグ コマンドはすべて同じ場所にブレークポイントを設定します。
debug> b 16
debug> b 0x10
debug> b 020
「k」は、設定されている現在のブレークポイントをリストするために使用できます。
debug> k
0x0010
これにより、関数「key?」が実行されたときにブレークポイントが設定されます。と呼ばれます:
debug> b key?
関数とラベルはどちらも停止できます。これには、シンボル ファイルをコマンド ラインで指定するか、アセンブルして実行して 16 進ファイルではなくソース ファイルで使用する必要があります。シンボル ファイルは、ソース ファイルまたは HEX ファイルで使用できます。
シングルステップには「s」コマンドを指定できますが、トレースがオフになっている場合 (デフォルトではトレースがオフになっています)、あまり起こりません。トレースは、「t」コマンドを使用してオンまたはオフに切り替えることができます。
debug> s
debug> s
debug> t
trace on
debug> s
0001: pc(089a) inst(4889) sp(0) rp(0) tos(0000) r(0000) call 889 init
debug> s
0002: pc(0889) inst(807a) sp(0) rp(1) tos(0000) r(089b) 7a
debug> s
0003: pc(088a) inst(e004) sp(1) rp(1) tos(007a) r(089b) 6004
'c' (Continue) コマンドを発行して実行するときは、トレースをオフにすることをお勧めします。
「。」コマンドを使用して、H2 コアの内部状態を表示できます。
debug> .
Return Stack:
0000: 0000 08aa 0883 017b 0000 031b 0000 ffb0 0000 02eb ffb5 0210 0167 0167
0167 0167
0010: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
Variable Stack:
tos: 0000
0001: 0000 0000 0000 0001 0004 0005 0000 ffb0 0000 0000 0000 0000 0000 0000
0000 0000
0011: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
pc: 0538
rp: 0001
dp: 0000
ie: false
また、「p」コマンドを使用して、シミュレートされたペリフェラルの状態を表示できます。
debug> p
LEDS: 00
VGA Cursor: 0005
VGA Control: 007a
Timer Control: 8032
Timer: 001b
IRC Mask: 0000
UART Input: 6c
LED 7seg: 0005
Switches: 00
LFSR: 40ba
Waiting: false
コマンドの完全なリストを表示するには、「h」コマンドを使用します。
デバッグ モードに入るその他の方法には、ソース コードに「.break」アセンブラ ディレクティブを挿入する (これは、アセンブルおよび実行コマンドが 16 進数ファイルではなくソース ファイルで使用される場合にのみ機能します)、シミュレータの実行中にエスケープ文字を押すなどがあります。シミュレートされた UART または PS/2 キーボードを介してデータを読み取ろうとしています (エスケープは引き続きシミュレーターに渡されますが、デバッグ モードもアクティブになります)。
別個のプログラムを Linux および Windows でコンパイルし、テストできます。これは、SoC がインターフェイスする Nexys3 ボード周辺機器をシミュレートしますが、コマンド ライン ユーティリティとは異なり、グラフィカル環境を提供します。デバイスと対話してデバイスが何をしているのかを確認するのは簡単ですが、デバッグ セッションはあまり制御されません。遊離過剰量が必要です。
以下は、GUI シミュレーターでの実行セッションの画像です。
構築は以下で行うことができます
make gui
そして実行中:
make gui-run
または:
./gui h2.hex (on Linux)
gui.exe h2.hex (on Windows)
Linux ビルドは、無料の Glut の開発パッケージがシステムにインストールされている場合に機能します。Windows ビルドでは、ビルド システムの変更やコンパイラ、ライブラリ、ヘッダーの手動インストールが必要になる場合があります。
現在のキーマップは次のとおりです。
Up Activate Up D-Pad Button, Release turns off
Down Activate Down D-Pad Button, Release turns off
Left Activate Left D-Pad Button, Release turns off
Right Activate Right D-Pad Button, Release turns off
F1 - F8 Toggle Switch On/Off, F1 is left most, F8 Right Most
F11 Toggle UART/PS2 Keyboard Input
F12 Toggle Debugging Information
Escape Quit simulator
他のすべてのキーボード キーは、UART または PS/2 キーボード入力にリダイレクトされます。
スイッチと方向パッド ボタンをクリックしてオンにすることができます。スイッチは左クリックでオンになり、右クリックでオフになります。 D-Pad ボタンは、ボタンの上をクリックするとオンになり、画面上の任意の場所でキーを放すとオフになります。
このシステムで使用される VHDL コンポーネントは、さまざまなツールチェーンやベンダー間で再利用可能で移植できるように設計されています。ブロック RAM などのハードウェア コンポーネントは推論され、明示的にインスタンス化されません。コンポーネントも可能な限り汎用的に作られており、ほとんどのコンポーネントの幅は選択可能です。これは極端な話ですが、残念ながら多くのベンダーはまだ VHDL-2008 標準をサポートしていません。
ファイル | ライセンス | 著者 | 説明 |
---|---|---|---|
util.vhd | マサチューセッツ工科大学 | リチャード・J・ハウ | 汎用コンポーネントのコレクション |
h2.vhd | マサチューセッツ工科大学 | リチャード・J・ハウ | H2 4 番目の CPU コア |
uart.vhd | マサチューセッツ工科大学 | リチャード・J・ハウ | UART TX/RX (実行時間カスタマイズ可能) |
vga.vhd | LGPL 3.0 | ハビエル・V・ガルシア | テキストモード VGA 80x40 ディスプレイ |
リチャード・J・ハウ | (および VT100 ターミナル エミュレータ) | ||
kbd.vhd | ??? | スコット・ラーソン | PS/2キーボード |
アセンブラとして使用される疑似 Forth のような言語については上で説明しましたが、実際に Forth コア上で実行されるアプリケーションは、それ自体が Forth インタプリタです。このセクションでは、H2 Core 上で実行される Forth インタープリターについて説明します。これは、embed.fth 内に含まれています。
TODO:
このプロジェクトではいくつかの言語が使用されていますが、それらはすべて互いに根本的に異なり、独自のコーディング標準とスタイル ガイドのセットが必要です。
一般的な信号名:
clk - The system clock
rst - A reset signal for the module
we - Write Enable
re - Read Enable
di - Data In
din - Data In
do - Data Out
dout - Data Out
control - Generally an input to a register, the documentation
for the module will need to be consulted to find out
what each bit means
signal_we - The write enable for 'signal'
signal_i - This is an input signal
signal_o - This is an output signal
一般に、「_i」および「_o」接尾辞は使用されず、モジュールは短く保たれ、その意味が明らかになるように名前が選択されます。プロジェクトが成長したら、このルールが再検討される可能性があります。
コンポーネントは次のことを行う必要があります。
constant N: positive := 4;
signal a: std_logic_vector(N - 1 downto 0) := (others => '1');
の代わりに:
signal a: std_logic_vector(3 downto 0) := x"F";
スタイルのルールは次のとおりです。
フォーマットガイドラインの例として、これは単純な任意幅のレジスターについて説明しています。
-- Lots of comments about what the unit does should go
-- here. Describe the waveforms, states and use ASCII
-- art where possible.
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- numeric_std not std_logic_arith
entity reg is -- generic and port indented one tab, their parameters two
generic (
N: positive); -- Generic parameters make for a generic component
port (
clk: in std_logic; -- standard signal names
rst: in std_logic; --
we: in std_logic;
di: in std_logic_vector(N - 1 downto 0);
do: out std_logic_vector(N - 1 downto 0)); -- note the position of ");
end entity; -- "end entity", not "end reg"
architecture rtl of reg is
signal r_c, r_n: std_logic_vector(N - 1 downto 0) := (others => '0');
begin
do <= r_c;
process(rst, clk)
begin
if rst = '1' then -- asynchronous reset
r_c <= (others => '0');
elsif rising_edge(clk) then -- rising edge, not "clk'event and clk = '1'"
r_c <= r_n;
end if;
end process;
process(r_c, di, we)
begin
r_n <= r_c;
if we = '1' then
r_n <= di;
end if;
end process;
end; -- "end" or "end architecture"
このプロジェクト内では、H2 コアのツール チェーンの作成とシステムのシミュレーションに使用される C コードが非常に多く使用されています。
ここの C コードにはそれほど驚くようなことはないので、いくつかの例外に対処する必要があります。
static const char *alu_op_to_string(uint16_t instruction) {
/* notice also that the 'case' clauses are inline with the
* switch selector */
switch (ALU_OP(instruction)) {
case ALU_OP_T: return "T";
case ALU_OP_N: return "N";
case ALU_OP_T_PLUS_N: return "T+N";
case ALU_OP_T_AND_N: return "T&N";
case ALU_OP_T_OR_N: return "T|N";
case ALU_OP_T_XOR_N: return "T^N";
case ALU_OP_T_INVERT: return "~T";
case ALU_OP_T_EQUAL_N: return "N=T";
case ALU_OP_N_LESS_T: return "T>N";
case ALU_OP_N_RSHIFT_T: return "N>>T";
case ALU_OP_T_DECREMENT: return "T-1";
case ALU_OP_R: return "R";
case ALU_OP_T_LOAD: return "[T]";
case ALU_OP_N_LSHIFT_T: return "N<N";
case ALU_OP_ENABLE_INTERRUPTS: return "seti";
case ALU_OP_INTERRUPTS_ENABLED: return "iset?";
case ALU_OP_RDEPTH: return "rdepth";
case ALU_OP_T_EQUAL_0: return "0=";
case ALU_OP_CPU_ID: return "cpu-id";
default: return "unknown";
}
}
if (foo)
bar();
else
baz();
picocom --omap delbs -b 115200 -e b /dev/ttyUSB1