ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chiselで作ったRISC-VとUARTをArty 35Tで動かしてみた

スポンサーリンク

今回は作ってたRISC-V(dirv)とUARTを接続した簡単なシステムがFPGAで動いたので、その結果について簡単にまとめておきたいと思う。

要約

オレオレRISC-V(dirv)と自作のバスインターコネクト&UartをArty 35Tに入れて動くのが確認できたーーーー!!

f:id:diningyo-kpuku-jougeki:20191005112841g:plain

データは以下のリポジトリにあるので、ご興味あれば是非!

という記事(2019/1/5修正:リンク先をUART込み込み版のリリースタグに変更しました。)

ここまでの経緯

以前に自分で簡単なRISC-Vを作った!という記事をまとめていたが、その記事の時点では以下のような状況だった。

  • Verilatorを使ったシミュレーションでriscv-testsのRV32Iのパターンが全てPASSする
  • Vivadoでインプリしてエラー無く合成できる

その後Arty 35Tを購入したので、その上にdirvとメモリを直接接続したデザインを構築し、そのデザインをFPGA上で動かして見るところまでは確認していた。

でこれだけだと、なんだかなぁ、、、というのもあったので、その後の目標として

  • dirv + UARTで文字を出したい!!

というものを考えていた。

SysUart

作ろうとしていたシステム(と言うにはシンプルだけど)は以下のようなもの。

f:id:diningyo-kpuku-jougeki:20191005113354p:plain

最初はdirvの命令フェッチとデータアクセスをそれぞれMbusICに接続して、メモリにつなぐつもりだったけど、そもそもメモリをデュアルポートで作っていたので、データバスだけメモリとUARTにアクセスする形にした。

この中で既に作っているのはdirvとメモリだけなので、バスインターコネクトとUARTは新規で設計することになる。 各モジュールの詳細な説明はまた別の記事にするつもりなので、ここではブロック図ベースでどんなものを作った!というのを載せておく。

MbusIC

先に書いたようにデータアクセスの経路をアドレスでメモリ or UARTに分岐すればいいのでこのモジュールは実際には1-in/2-outのセレクタとして動作すればOKということにはなる。ただ単に1-in/2-outのセレクタをベタ書きで作ってもいいのだが、それだと「Chiselを使う意味!!」となるのでもう少し真面目に設計しておく。

「真面目に」の意味だが、仕様として以下のようなことが出来るように設計することを指している。

  1. インターコネクトらしくN-in/M-outのパラメタライズが可能
  2. スレーブのメモリマップをパラメータで指定可能

ブロック図的には以前にChiselで作るNICという記事で書いたものと同様で以下のようになる。

f:id:diningyo-kpuku-jougeki:20190721211443p:plain

違いは以下の2点。

  1. 各Decoder/Arbiterのバスプロトコルがdirvで設計したMbus
  2. Decoderの出力がモジュールに入力したメモリマップに従って制御される

以下の感じでメモリマップのSeqを渡すと、その情報を元にしてスレーブの数とアドレスの範囲のチェックを行う。

    Seq(
      (0x0,   32 * 1024), // MemTop (32KBytes)
      (32 * 1024, 0x100)  // Uart
    ),

このMbusICのパラメータクラスは以下のような定義なのだが、現状ではmasterInfosいらない子になってる。。。

/**
  * parameter class for MbusIC
  * @param ioAttr IO port access attribute.
  * @param slaveInfos Slave module information. It contains base address and size.
  * @param dataBits Data bus width.
  */
case class MbusICParams
(
  ioAttr: MbusIOAttr,
  masterInfos: Seq[(Int, Int)], // マスタの情報も(Int, Int)にしたが今の状態ではマスタの個数だけでよかった。。
  slaveInfos: Seq[(Int, Int)],
  dataBits: Int
) {
  val numOfMasters = masterInfos.length
  val numOfSlaves = slaveInfos.length
  val addrBits = 32
  val decParams = Seq.fill(numOfMasters)(MbusDecoderParams(MbusRW, slaveInfos, dataBits))
  val arbParams = Seq.fill(numOfSlaves)(MbusArbiterParams(MbusRW, masterInfos, dataBits))
}

きちっと拡張すると、Masterからアクセス可能なスレーブを限定したり出来るようになる、、、、はず。。。

Uart

お次はUart、なのだがこれはXilinxがVivadoで使えるIPとして公開してくれているUartLiteというものの仕様書をベースにして、とりあえず送受信を行うために必要な部分だけを実装することにした。

https://japan.xilinx.com/products/intellectual-property/axi_uartlite.html

仕様書はこちら。

ブロック図は以下のようなもの。

f:id:diningyo-kpuku-jougeki:20191005113245p:plain

図中の枠線はそれぞれ以下のような意味を持つ。

  • 青色:実装するもの
  • 赤色:バスプロトコルを変更
  • 灰色:今回は実装しないもの

要はモジュールのインターフェースをMbusに変更して、Uartの制御レジスタと送受信制御のみを作る形。

作ってみた結果

冒頭に載せたとおりArty 35T上で正常に動作してUartで接続したLinux上のターミナルに"Hello, World!!"が表示されました。嬉しい。

ここでは各種合成結果についてをまとめておきます。

リソースの使用率

以下の画像のようになりました。

f:id:diningyo-kpuku-jougeki:20191005113317p:plain

RISC-Vは最小になるように作ると大体1000LUTとか聞いた気もするので、ざっくり倍くらいのサイズ。この辺はバスプロトコルが多少複雑なこととかが影響してるか?という感じ。IFUが結構膨らんでるし。

m_mbus_ic(MbusIC)の構成がm_dec_0/1になってて、何故かマスターポートが2つあるのに気づく人もいるかもですが、これはMbusICの作りにまずいところがあって、マスターを1ポートにするとRTLの生成に失敗するからです。。ここは要修正な部分。

タイミング

こちらも画像をペタリとします。まずはクロックから。

f:id:diningyo-kpuku-jougeki:20191005113409p:plain

ご覧の通りでシステムのクロックは50MHz

タイミングレポートのサマリは以下。

f:id:diningyo-kpuku-jougeki:20191005113600p:plain

ワーストでスラックが0.246nsなので、もう少しなら余裕がある。長いパスはフェッチした命令をレジスタで受けた後のパスなので、パイプライン化を真面目にすればまだ上げられるかな、、という感じ。

今後の予定

これはリポジトリのREADMEのTODOにも書いてるのだが、3-stage/5-stageに変更してどうなるか、というのと、割り込みの実装をまずはやりたいかな。 ツイッターでやり取りをしていてMMUとかも実装したいなーとか思い始めてますが、このあたりはまずは勉強から、、という感じなのでいつになることやら。