CPU+コンパイラ自作キャンプの手引き
「CPU+コンパイラ自作キャンプ」は数ヶ月の事前準備+数日の泊まり込みを想定したイベント
本ページは自作キャンプの全行程を案内することを目的とする
事前準備
Verilogの練習
6ビットのLEDを好きなビットパターンで光らせる
1秒程度の間隔で点滅させる
脱線ネタ:PWMを使って滑らかに明るさを変えて明滅させる ボタンを押すたびに1ビットずつ乱数を生成する
logic [15:0] lfsr;
6ビットのLEDを1ビットずらし、空いた1ビットに乱数を入れる
メタステーブル
チャタリング
UART受信回路を書き、受信した8ビット(の下位6ビット)をLEDに表示させる オンボードUSB-UARTを使えば、変換モジュールを配らなくて済む
FPGA_TX:PIN17
FPGA_RX:PIN18
UART送信回路を書き、エコーバック(PCへ送り返す)
単にエコーバックする
脱線ネタ
何らかの変換をしてエコーバックする
a~zをA~Zに変換して送り返す
文字コードを1だけずらして送り返す(Aを送ったらBを返す)
UARTで送りつけた2つの8ビット値を加算する仕組みを作る
例:0A 05 を送ると 0F が返ってくる
出力先はUART
脱線ネタ
加算器をゲートロジックで組む
XORで半加算器を作り
2個の半加算器で全加算器を作り
7個の全加算器と1個の半加算器で8ビットの加算器を作る
加算と減算を選択できるようにする
「整数」「整数」「演算命令」という3バイトを続けて送る
「演算命令」を受信したら、すでに受信した2つの整数を用いて計算し、結果を出力する
コラム:減算と2の補数
演算結果の出力先としてUARTかLEDを指定する仕組みを作る
「整数」「整数」「演算命令」「書き込み命令」という4バイトを続けて送る
「整数」と「命令」を混同しないための仕組みを考える
例えば「整数」は最上位ビット(bit7)を1、「命令」は最上位ビットを0とする、など。
こうすると、整数の幅が7ビットになってしまうが、仕方ない
プログラムをBRAMに貯めて一気に実行するようにする
命令用のBRAMを導入
BRAMに命令列をため、命令列を受信し終えたら一気に実行する
「命令列の受信完了」を知るための仕組みを考える
例えばこんなやり方がある
A. 命令列の後に送信完了マークを送る
B. 命令列を送る前に命令数を送る
C. 一定時間受信がなければそこで終わったと見なす
何度も加算と減算を繰り返せるようにする
1~10の総和を求めてみる
整数1、整数2、…、整数10、加算命令、出力命令 という命令列を送る
加減算のコンパイラを作る
ここで作るコンパイラは、直接機械語(16進数の文字列)を生成すれば良い
一般的にはコンパイラとアセンブラ(とリンカ)を別々に作ることが多い
コンパイラがアセンブリ言語を出力し、アセンブラが機械語を出力する
乗算をサポートする
演算子の優先順位を意識する
1 + 2 * 3 は (1 + 2) * 3 ではなく 1 + (2 * 3) である
乗算の優先順位を保つ仕組みを考える
途中状態を保存しておく必要性
一般の電卓は前から順に計算してしまう(1+2*3と入力すると結果が9になる)
スタック型か、レジスタ型か
スタックの枯渇に対するケア(枯渇したらどうなるか)
デバッガ(GDBなど)を使ってコンパイラの処理を追いかける
ここまでが事前準備の目安
特別な変数 _SW2 を用意
LEDの表示を変数化
特別な変数 _LED を用意
複文に対応
send文に対応
C言語にはないが、UARTにデータを送る専用の構文として send <expr>; を導入する
変数の読み書きに対応
データ用BRAM
ノイマン型かハーバード型か
命令長の自由度
セキュリティ
動的プログラム生成
暗黙に定義される1文字変数
ベースボードが手に入ってからやる
自動テストを導入
外付けUSB-UART変換モジュールが欲しいのでベースボード入手後にやる
何をテストしているか・していないかを理解する
if文に対応
CPUにどの命令を追加するか、しないか
LTに加えてGTは必要か?
関連話題
NEG命令は必要か?
複合代入文
フラグレジスタとスタック
関係演算子に対応
論理演算子に対応
while文に対応
タイマーによる時間待ちを可能にする
プログラムからLEDを点滅させる
インクリメント演算子に対応
for文に対応
関数定義に対応
変数の定義に対応
LCDに対応する
キーパッド入力に対応する
プログラム上からのUART送受信に対応する
2台の自作CPUでチャットする
USB-UART変換モジュールを取り外せるようにしておく