RISC-Vベースの自作CPUに自作OSを実装(helloコマンドしかできない)

こんにちは,めぶきぶです.
この記事は,大学院生の春休みに就活をほったらかして作成した,自作CPUに自作OSを実装する,その記録になります.


タイトルは「RISC-Vベースの自作CPUに自作OSを実装(helloコマンドしかできない)」ですが,もう少し詳しく説明すると,「RISC-VとChiselで学ぶ はじめてのCPU自作」の内容をVerilogに移植し,「Writing an OS in 1,000 lines」という記事中のhelloコマンドまでを,移植したCPU上で動作させるという意味です.完全に自作というわけではないです.

 

 

1.完成したもの

TeraTermを用いてUART通信を行っています.

コマンドを入力し,その応答が返ってきます.

↓動画です(私のポスト)

https://x.com/MebuKibu/status/1773163575554592995?s=20

 

Github

github.com

 

2.システムの全体像

構築したシステムは図のようになっています.

動作の流れ

C言語にてOSを作成

RISC-VコンパイラでOSをコンパイルし,バイナリファイルを作成

③バイナリファイルから,命令部分のみを抽出し,16進数に変換

④CPUのmemory.vに16進数形式のファイルを書き込む

⑤Vivadoを用いて,Verilogで作成したCPUをbitstreamファイルに変換

FPGAボードに書き込み,CPUを起動

TeraTermを用いてUART通信を行い,helloコマンドを入力し「Hello World

 

 

2.1.FPGAボード

今回使用したFPGAボードはDigilent社の「ARTY S7-50」です.

digilent.com

 

数年前に,秋月電子通商で購入しました.そのときは,13,000円くらいだったと記憶していますが,今見たら22,440円になってました.びっくり

購入理由としては,「作ろう!CPU」のサポートページ において,このボードを用いたサンプルを見つけたためです.

Vivadoのインストールからシミュレーションの方法,FPGA書き込みまでサポートしているので,初心者の方にはおすすめです.

 

2.2.CPU

自作CPUは,「RISC-VとChiselで学ぶ はじめてのCPU自作――オープンソース命令セットによるカスタムCPU実装への第一歩」という本で作成するCPUをVerilogに移植したものです.

gihyo.jp

私の認識では,ChiselはVerilogよりも高位なハードウェア記述言語です.おそらく,ChiselコードをVerilogコードに変換することも可能ですが,より低位な視点で1からCPUを作成したいと思ったので,Verilogで書くことにしました.

この本は,かなりボリューミーで,簡単なCPUの実装から,パイプライン処理,ベクトル命令,カスタム命令について書かれています.その中から,「簡単なCPUの実装」の部分だけ使用し,Verilogに移植しました.

自作したCPUの命令セットは,RV32Iです.これは,RISC-V 32bitの基本整数命令セットIであり,乗算,除算命令は含みません.

 

2.3.OS

自作OSは,「Writing an OS in 1,000 lines」という記事のhelloコマンドまでを自作CPUに合うように変更しました.

operating-system-in-1000-lines.vercel.app

この記事は,全18章で構成され,章ごとに少しずつ実装していく形式になっています.一歩ずつ進めていくので,OSを勉強する上でおすすめです.

全18章のうち,15章のシステムコールまでを対象としました.16章以降は,ホストOS(自分のPCのOS)との通信するという内容でした.私は,FPGAボードに実装が目標だったため,内容が違うなと思い,16章以降は見送りました.

 

「Writing an OS in 1,000 lines」のOSと私の自作OSの変更点は以下です.

・OpenSBIを用いない

OpenSBIとは,RISC-VのオープンソースなSモードインターフェースです.自作CPUは,一から構築するため,これは使用できません.QEMUでのシミュレーションでは,-bias none としています.

 

・文字入出力はメモリマップドなUARTモジュール

OpenSBIが使用できないと,文字の入出力も一苦労です.ここでは,xv6-riscvで使用されているUARTモジュールを参考にして,実装することにしました.

xv6は,MIT(マサチューセッツ工科大学)の授業で用いられているOSです.元々x86 CPU用に実装されたxv6をRISC-V CPU用に実装し直したものがxv6-riscvです.ソースコードは,GitHubで公開されています.

github.com

 

・ページングSV32が実装できなかったので,ページングなし

ページングSV32に設定するには,CSRのSATPの32bit目の立てればよいと思っていたのですが,この処理を行うと例外が発生してしまいます.色々調べたのですが,解決方法が見つからず,ページングはなしとなりました.

shell.cはユーザアプリケーションで,ベースアドレスは仮想アドレスを指定しています.しかし,ページングがないため,仮想アドレスから物理アドレスへの変換ができません.そのため,一度コンパイルしてから,その結果をダンプしてアプリケーションのベース物理アドレスを確認し,その物理アドレスリンカスクリプトで指定してから,再びコンパイルするという方法でなんとか実装しました.(アプリケーションとは)

そもそも,自作CPUではCSRは存在するだけで,ほぼ機能していないので,ユーザモードとか意味ないです(泣)

 

2.4.UART通信

メモリマップドなUARTモジュールについて少し説明します.

メモリマップドとは,あるアドレスに対するメモリの読み書きが,他のデバイスの読み書きと対応する形式のことを指します.

私の自作CPUの場合だと,アドレス0x1000に対してメモリの読み書きを行うと,そのアドレスにはUARTモジュールが接続されているので,UARTモジュールに対して読み書きが行われます.

この手法の利点は,CPU側はメモリの読み書きをするだけでよく,他のデバイスのことを考える必要がないことです.

UARTのOS側動作(C言語)は,前述のようにxv6-riscvを参考にしています.

一方,CPU側(Verilog)では,主に以下の記事を参考にしました.

www.acri.c.titech.ac.jp

この記事は,UARTの送信側だけですが,受信側も検索すれば沢山出てきます.

 

3.まとめ

今回,RISC-Vベースの自作CPUに,helloコマンドができる自作OSを実装し,FPGAボードで動作させました.

今後は,まずページングをなんとかすることと,ファイルシステムにも挑戦してみたいと思います.