始めに
Seed FPGA board Tang Primerで、小さなCPUもどきを作っていたのですが、一つまともなものを作ってみようと思い、最小のRISC-V RV32I命令セットで作ってみることにしたのが約1年前。そこから論理は書いたものの、テストをせずに放置していたものを思い直して自作テストを作り、実装した命令を1回ずつテスト通ったので、公開してみました。今回公開したのは、RISC-VおよびUARTを使用したモニタのverilog RTL論理および動作環境です。一応Tang Primerを歌っておりますが、clock PLLのみIP使用であり、特殊な記述もないため他のFPGAへの移植も容易と思います。
始めに2(2021/9/5追記)
新たにXilinx Artix-7を使ったDigilentのArty A7で動作確認をしました。使用法をアップデートしました。
始めに3(2023/12/9追記)
外部割込みとmret命令、illegal operationのexceptionを追加しました。
2024/1/6追記
my-systolicサポート用にData RAMをパラメータ化し16Kwordに拡張しました。Arty A7でのみ動作確認しています。
参考URL
1. Version 0.1の制約
以下の制約があります。
- ALU周り、Load/Store、Jump系を作成。
- fence系、csr系、ecall系、uret系などは未実装。割り込みも未実装。
- メモリはinstructionとdataでセパレート。
各々1KWordsinstruction 4Kwords, data 4 or 16 Kwordsの大きさ。 - I/OはRGB LEDの3ピン×4。
2. 使い方
Tang Primerの合成環境のセットアップが済んでいることが前提となります。
上記URLを参考にしてください。
まずはgit cloneしてください。
https://github.com/yoshiki9636/my-riscv-rv32i
###2.1 RTL simulation
Intel FPGA用に配布されている、Modelsim 10.5b で動作確認しております。
基本的なverilog記述しか使用していないので、大抵のシミュレータで動作可能と思います。
(1) ssim/ディレクトリを作成し、cpu/ fpga/ io/ mon/ sim/の内容をコピーします。
(2) asm/ディレクトリで riscv-asm1.plを使い、テスト命令列test.txtを作成。ssim/にコピーします。
例:./risc-vasm1.pl lchika.asm > test.txt
(3) ssim/内でverilogシミュレータを動作させます。
###2.3 Tang Primer Synthesis & run
Tang Primer専用のIDEを使用して合成、FPGAへの書き込みを行います。詳しくは、SiPEEDのページを参照ください。
https://tang.sipeed.com/en/
(1) ssyn/ディレクトリを作成し、cpu/ fpga/ io/ mon/ syn/の内容をコピーします。fpga_top.v内のifdefはTANG_PRIMERを有効にしてください。
(2) IDEを立ち上げ、プロジェクトを作成、ssynをディレクトリに指定して、すべてのverilogファイルを指定します。
(3) PLLの追加が必要なので、Tools->IP Generatorを選択し、Create New IPから、PLLを選択します。名称はpll、入力24MHz、出力36MHzとして作成します。
(3.1)ssyn/uart_if.vの周波数設定が36MHz設定にします。
(4) 後は用法通りに合成、書き込みをします。
(5) USB - UART変換器を使って、FPGAとシリアル通信します。変換器のRxをB15、TxをB16、GndをGのどれかに接続します。
(6) Teratermを使ってシリアル通信をします。Teratermの新しい接続で、シリアルを選択し、COMを変換器のものにします。
(7) 設定->シリアルポートで、スピード:9600 データ:8bit パリティ:none ストップビット:1bit フロー制御:none
(8) 設定->端末で、 改行コード 受信:AUTO 送信:CR これでキーボードをたたくとエコーバックが帰ってくれば、動いております。
(9) プログラムを書き込みます。qを押して、状態をクリアしたのち、i 00000000と打ち込むと、改行されます。シミュレーションで作ったtest.txtの内容をコピペしてください。最後にqを押します。
(10) 書き込んだプログラムをダンプするには、p 00000000 00000100と打ち込んでください。先ほどのプログラムがダンプされます。
(11) 実行は、g 00000000で実行状態になります。lchika.asmであれば、RGB LEDが色を変化させます。そのほかのテストプログラムも項目を確認後、テストパスがわかるように同じようなLチカとなります。
(12) 実行の停止もqで停止します。それ以外のコマンドは、以下の通りです。
command format
g : goto PC address ( run program until quit ) : format: g <start addess>
q : quit from any command : format: q
w : write date to memory : format: w <start adderss> <data> ....<data> q
r : read data from memory : format: r <start address> <end adderss>
t : trashed memory data and 0 clear : format: t
s : program step execution : format: s
p : read instruction memory : format: p <start address> <end adderss>
i : write instruction memory : format: i <start adderss> <data> ....<data> q
j : print current PC value : format: j
※2021/9/5追記
2.4 Arty A7 Synthesis & Run
Digilent Arty A7を使う場合の合成方法のメモです。Xilinx Vivadoを使用します。詳しくはArty A7の設計ドキュメントを探してください。
(1) ssyn/ディレクトリを作成し、cpu/ fpga/ io/ mon/ をコピーしてください。ssyn/riscv_io_pins.xdcをコピーします。fpga_top.vのifdefをARTY_A7を有効にしてください。
(2) Vivadoを立ち上げ、projectを作成、ssynの中のRTLを指定し、constraintsとして、riscv_io_pins.xdcを指定します。
(3) MMCMの追加が必要なので、IPカタログから追加します。周波数は入力100MHz、出力90MHz85MHzで動作を確認しております。
(3.1) ssyn/uart_if.vの周波数設定を90MHz85MHzにします。
※uart_if.vの定数を85MHzに変更しました。アップデートをお願いいたします。
※定数TERMの設定は以下の式になります。HARFはTERMの1/2です。
TERM = int( (UARTビットレートの周期[us] / クロック周期[us]) )
= int ( (1,000,000/UARTビットレート[bps]) / (1 / クロック周波数[MHz]) )
(4) Vivadoのお作法で合成以下を行い、Arty A7に書き込みます。
(5) Arty A7はUSB接続がUARTとして使用できるので、特にUART変換器は必要ありません。あとはTang Primerの方法(7)からと同一です。
最後に
これからぼちぼちとTang Primerに実装できる範囲で未実装の機能を実装していこうと思います。ただTang Primerの論理容量がネックになりつつあるので、(まだ容量ありました)そのうちにFPGAをアップグレードすることになりそうです。
2021/9/5追記
Arty A7を入手して動作速度がアップいたしました。こちらをメインで作っていこうと思います。