FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chipyardで32-bit版BOOMを生成するための調査 (1. コンフィグレーション調査)

f:id:msyksphinz:20220103010108p:plain

32ビット版の乗除算器のVerilogだけ欲しくて、Chipyardで構成されるChiselの乗除算器のVerilogだけを持ってこようとしている。

通常、BOOMのコンフィグレーションを構成するためには、以下のようにしてVerilogファイルを生成する。

make CONFIG=MediumBoomConfig # 中堅サイズのコンフィグレーション作成
make CONFIG=MegaBoomConfig    # 大きめのコンフィグレーション作成

ただしここにはビットサイズに関する指定が無いので、デフォルトで64ビットのコンフィグレーションが生成されてしまうので、32ビット版の乗除算器を生成できない。BOOMのVerilog生成において32ビット版をどのように生成するのか調査してみる。

BOOMのソースコードを読んでみると、config-mixin.scalaにて以下の記述があった。

  • boom/src/main/scala/common/config-mixins.scala
/**
 * 3-wide BOOM. Try to match the Cortex-A15.
 */
class WithNLargeBooms(n: Int = 1, overrideIdOffset: Option[Int] = None) extends Config(
  new WithTAGELBPD ++ // Default to TAGE-L BPD
  new Config((site, here, up) => {
    case TilesLocated(InSubsystem) => {
      val prev = up(TilesLocated(InSubsystem), site)
      val idOffset = overrideIdOffset.getOrElse(prev.size)
      (0 until n).map { i =>
        BoomTileAttachParams(
          tileParams = BoomTileParams(
            core = BoomCoreParams(
              fetchWidth = 8,
              decodeWidth = 3,
              numRobEntries = 96,
              issueParams = Seq(
                IssueParams(issueWidth=1, numEntries=16, iqType=IQT_MEM.litValue, dispatchWidth=3),
                IssueParams(issueWidth=3, numEntries=32, iqType=IQT_INT.litValue, dispatchWidth=3),
                IssueParams(issueWidth=1, numEntries=24, iqType=IQT_FP.litValue , dispatchWidth=3)),
              numIntPhysRegisters = 100,
              numFpPhysRegisters = 96,
              numLdqEntries = 24,
              numStqEntries = 24,
              maxBrCount = 16,
              numFetchBufferEntries = 24,
              ftq = FtqParameters(nEntries=32),
              fpu = Some(freechips.rocketchip.tile.FPUParams(sfmaLatency=4, dfmaLatency=4, divSqrt=true))
            ),
            dcache = Some(
              DCacheParams(rowBits = site(SystemBusKey).beatBits, nSets=64, nWays=8, nMSHRs=4, nTLBWays=16)
            ),
            icache = Some(
              ICacheParams(rowBits = site(SystemBusKey).beatBits, nSets=64, nWays=8, fetchBytes=4*4)
            ),
            hartId = i + idOffset
          ),
          crossingParams = RocketCrossingParams()
        )
      } ++ prev
    }
    case SystemBusKey => up(SystemBusKey, site).copy(beatBytes = 16)
    case XLen => 64
  })
)
// DOC include end: LargeBoomConfig

ここで、XLenの値が決め打ちになっているため、以下のようにしてXLENのパラメータを埋め込んでみる。

diff --git a/src/main/scala/common/config-mixins.scala b/src/main/scala/common/config-mixins.scala
index fc20726c..489b4c57 100644
--- a/src/main/scala/common/config-mixins.scala
+++ b/src/main/scala/common/config-mixins.scala
@@ -129,7 +129,7 @@ class WithNSmallBooms(n: Int = 1, overrideIdOffset: Option[Int] = None) extends
 /**
  * 2-wide BOOM.
  */
-class WithNMediumBooms(n: Int = 1, overrideIdOffset: Option[Int] = None) extends Config(
+class WithNMediumBooms(xlen: Int = 64, n: Int = 1, overrideIdOffset: Option[Int] = None) extends Config(
   new WithTAGELBPD ++ // Default to TAGE-L BPD
   new Config((site, here, up) => {
     case TilesLocated(InSubsystem) => {
@@ -169,7 +169,7 @@ class WithNMediumBooms(n: Int = 1, overrideIdOffset: Option[Int] = None) extends
       } ++ prev
     }
     case SystemBusKey => up(SystemBusKey, site).copy(beatBytes = 8)
-    case XLen => 64
+    case XLen => xlen
   })
 )
 // DOC include start: LargeBoomConfig

そして、Chipyard側のコンフィグレーションを追加する。

  • generators/chipyard/src/main/scala/config/BoomConfigs.scala
diff --git a/generators/chipyard/src/main/scala/config/BoomConfigs.scala b/generators/chipyard/src/main/scala/config/BoomConfigs.scala
index 9e1f558a..2a1922b1 100644
--- a/generators/chipyard/src/main/scala/config/BoomConfigs.scala
+++ b/generators/chipyard/src/main/scala/config/BoomConfigs.scala
@@ -11,7 +11,11 @@ class SmallBoomConfig extends Config(
   new chipyard.config.AbstractConfig)

 class MediumBoomConfig extends Config(
-  new boom.common.WithNMediumBooms(1) ++                         // medium boom config
+  new boom.common.WithNMediumBooms(64, 1) ++                         // medium boom config
+  new chipyard.config.AbstractConfig)
+
+class MediumBoom32Config extends Config(
+  new boom.common.WithNMediumBooms(32, 1) ++                         // medium boom config
   new chipyard.config.AbstractConfig)

 class LargeBoomConfig extends Config(

これでVerilog生成に挑戦してみる。少し生成に時間がかかるので、結果は後日確認する。

make CONFIG=MediumBoom32Config debug

WavedromをCLIで動かすためのパッケージインストール

WaveDromが便利すぎるのだが、CLIのインタフェースが無くて少し困っていた。 調べてみるとCLIを構築するためリポジトリがあるので、それを使う必要がある。

github.com

自分はフロントエンドについて全く詳しくないので、Node.jsのアップデートの方法が分からず苦労した。 nvm, npiとかよく分からない。

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
$ export NVM_DIR="$HOME/.nvm"\n[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" \n[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

WaveDrom-CLIをインストールする。

$ git clone https://github.com/wavedrom/cli.git
$ cd cli
$ nvm install stable --latest-npm
$ nvm alias default stable
$ npm i wavedrom-cli 

これでWaveDromがCLIで使えるようになった。

$ wavedrom-cli
Options:
  -i, --input    path to the source                                   [required]
  -s, --svg      path to the generated SVG
  -p, --png      path to the generated PNG
      --version  Show version number                                   [boolean]
      --help     Show help                                             [boolean]

すこしソースコードに改造を加えてみたいのだが、これでどのように変更すればいいのか分からない...

あけましておめでとうございます 2022

f:id:msyksphinz:20211231234930p:plain

あけましておめでとうございます。今年も、FPGA開発日記をよろしくお願いします。

去年は、自分のなかではかなりハードウェア開発寄りに活動を寄せていった一年だった。 もちろんソフトウェアもいろいろ見ていたけど、自作CPUとか、ハードウェア開発とか、SystemVerilogとかの調査が中心だったかな。

いろんなプロジェクトが中途半端になっているので、今年こそは区切りを付けなければ...!

  • 引き続きRISC-Vを中心としたエコシステムについて調査していきたい。RISC-Vをベースとしたハードウェア開発、ソフトウェアエコシステムに貢献していきたい。
  • 昔から興味のあるソフトウェアインフラストラクチャ、言語処理系LLVMなどについての勉強を進めていきたい。これも中途半端になっているので何とかしなければ。
  • 新しいことを始めたいな。新しいプログラミング言語を覚えるのでもいいし、全く知らなかった方面についてやっていきたい。例えばソフトウェアでもAndroidなどのアプリ開発とかどうかな?ハードウェア系のネタを使いつつアプリ開発とかできたら面白いかもしれない。

という訳でいろいろ夢は広がるけれども、一つ一つ地道にこなしていきたいと思う。今年もよろしくお願いします。

年末なので今年のまとめをしよう2021

f:id:msyksphinz:20211231012743p:plain FPGA開発日記は2015年1月4日開始から、おおよそ7年が経過しました。 年末年始なので、2021年をいろいろと振り返っていこうと思う。

今年の最初には、以下のような目標を設定していました。あんまり見直したくねえなあ...

  • 引き続きRISC-Vをベースとしたエコシステムの勉強。自分でOSSへの貢献をしていきたい。
    • これはRISC-Vの実装も含む。ソフトウェアエコシステム・ハードウェアエコシステムを含めて、RISC-V界隈への貢献を続けたい。
  • コンパイラの知識も付いたので、さらに昇華させたい。
    • 特にLLVMに関する知識はかなり詳しくなった。これを何らかの形でまとめ上げたい。
  • ハードウェアと言語処理実装について興味がある。
    • FIRRTLのPassの勉強はかなり進んだ。FIRRTLを題材に、自分で何か取り組みたい。

RISC-Vのエコシステムの勉強については毎年目標に設定しているのだが、毎年OSSへのコントリビューションという所で 目標が達成できていない。思い切りが必要なのだろうが、まとまった時間が取れないという所で言い訳が続いてしまっている。

特に今年はプライベートで目が回るような忙しさが続いてしまい、ブログの執筆も年末に向かうにつれて速度が落ちてしまった。 これについては仕方がない。とりあえずは業務や当面の計画を最優先にしなければならないということもあり、趣味の開発や執筆は かなり優先度を落として対応していた。

ブログの記事数

今年は特に目の回るような忙しさにより、ブログの執筆数は大幅に減退。まあこれは仕方がない。ブログ開設以降過去最低となった。 ここまで来ると、もはやブログの更新頻度はあまり指標としては役に立たない。

Jan Feb Mar Apr Mar Jun Jul Aug Sep Oct Nov Dec Total
2021 24 28 30 30 32 28 29 26 25 22 15 13 302
2020 31 29 31 28 31 30 31 31 30 31 27 30 360
2019 29 28 28 31 29 29 31 31 30 31 29 32 358
2018 33 29 29 29 30 31 27 26 27 30 28 29 348
2017 27 21 30 24 33 30 30 29 29 31 33 32 349
2016 32 31 22 28 25 20 23 19 23 30 29 26 308
2015 20 27 17 6 28 29 35 31 44 31 34 27 329

対外発表

ありがたいことに、対外発表については依頼をもらうことが多く(私が出不精なのであまり自分で出たがらない)、 今年も数件発表をさせてもらった。

Rustで作るフルスクラッチQEMU型エミュレータ - Speaker Deck

2020年終盤に触っていた自作QEMUをまとめて発表した。実はこのプロジェクトの最終目標はKernel/VM勉強会に設定しており、募集が始まった段階ですぐに応募していた(自分にしては珍しい)。

  • ~Verilator解析を通じて感じる「最近面白そうと思っている技術」~

Verilatorの内部構造解析 - FPGA開発日記 カテゴリ別記事インデックス

趣味でVerilatorの内部解析をしていたので、そのことについて発表させてもらった。Verilatorの解析は最近は止まっているが、自作CPUの開発などで活用させてもらっている。

次世代を担うオープン命令セットアーキテクチャRISC-Vの最新動向 - Speaker Deck

研究会のような場所で私のような人間が発表をさせてもらうのは非常に申し訳ない気分になるし、 なにせアカウント名が非常に読みにくいので毎回ご迷惑をおかけしている。そろそろアカウント名を読みやすいものに変更するべきかしら...

ブログのコンテンツについて

今年は久しぶりにRISC-Vを題材に自作CPUの開発を始めた。ブログに内容をちょこちょこ書いているが、性能を上げるために色々試行錯誤してはベンチマークリグレッションテストを走らせている。 Verilatorを使っているのだが、ここまで規模の大きなコアを一人で作っているとコンパイルが非常に遅くなってしまいとてもつらい。 商用のシミュレータが使いたいな...

自作CPUの話って、傍から見ていると結構単純なものに限定しているケースがあったり、高性能側に振り切った実装の解説とかってあまりない。 こういうコンテンツって、ハードウェアを知るためには結構重要なコンテンツだと思うので、自作CPUを組み上げる中で内部実装解説の書籍などを作れればいいかなと思っている。

プライベート

とにかく2021年は忙しかった。業務においても新しい技術をかなり勉強した(といってもハードウェア実装がほとんどだが)。これについてはかなり実践的な技術が身についてきたと思う。自作CPUの開発にもかなりつながっている部分がある。

いくつか水面下でプロジェクトを走らせているのだが、これらについてもあまり手が出せないでいる。今年中に終わらせたかったプロジェクトも来年に持ち越しになってしまった。私自身の時間画家確保できるのが2月以降になりそうなのでこれもかなりまずい...

とにかく2021年も非常に忙しい年になるに違いない。体力的にもつらいところがあるが、自分で決めた道なので諦めずに頑張っていこうと思う。

自作RISC-V CPUコア実装(RASの動作確認2)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードのボトルネックを解決した。 そうこうしているうちにいつの間にかRET命令のRAS予測が怪しくなってきているので修正しておきたい。 RASの修正を行って、簡単なテストケースならば想定通りに動作するようになったのだが、Dhrystoneではほとんど性能が向上しなかったので、もう少し複雑なテストケースでRASの動作を確認する。

func1func2で交互に呼び出しながらRASを埋めていくようにする。

0000000080000018 <func1>:
    80000018:   02050663                beqz    a0,80000044 <_func_fin1>
    8000001c:   fe010113                addi    sp,sp,-32 # 7ffe0 <_start-0x7ff80020>
    80000020:   00a13023                sd      a0,0(sp)
    80000024:   00113423                sd      ra,8(sp)
    80000028:   fff50513                addi    a0,a0,-1
    8000002c:   01c000ef                jal     ra,80000048 <func2>
    80000030:   00813083                ld      ra,8(sp)
    80000034:   00013683                ld      a3,0(sp)
    80000038:   02010113                addi    sp,sp,32
    8000003c:   00d50533                add     a0,a0,a3
    80000040:   00008067                ret

0000000080000044 <_func_fin1>:
    80000044:   00008067                ret

0000000080000048 <func2>:
    80000048:   02050663                beqz    a0,80000074 <_func_fin2>
    8000004c:   fe010113                addi    sp,sp,-32
    80000050:   00a13023                sd      a0,0(sp)
    80000054:   00113423                sd      ra,8(sp)
    80000058:   fff50513                addi    a0,a0,-1
    8000005c:   fbdff0ef                jal     ra,80000018 <func1>
    80000060:   00813083                ld      ra,8(sp)
    80000064:   00013683                ld      a3,0(sp)
    80000068:   02010113                addi    sp,sp,32
    8000006c:   00d50533                add     a0,a0,a3
    80000070:   00008067                ret

0000000080000074 <_func_fin2>:
    80000074:   00008067                ret

RASの積み上がり具合を確認していく。一応、積みあがっているように見える。

f:id:msyksphinz:20211230010725p:plain

ただし、予測器は殆どMispreditionを出している。これはどこかに不具合があるに違いない。

自作RISC-V CPUコア実装(RASの動作確認)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードのボトルネックを解決した。 そうこうしているうちにいつの間にかRET命令のRAS予測が怪しくなってきているので修正しておきたい。

RASの実装を確認するために、とりあえず以下のようなアセンブリを組み上げてみた。 再帰的に関数を呼び出すような形で  f(x) = f(x-1) + x (ただし x=0 ならば  f(x) = 0) で実装している。 これでひたすらRASが埋められていき、RET命令実行時にスタックされた情報がPOPされていくはずだ。

_start:
    li      sp, 0x80001000
    li      x10, 10
    jal     func

    j       fin

func:

    beq     x10, zero, _func_fin

    addi    sp, sp, -0x20
    sd      x10, 0(sp)
    sd      ra , 8(sp)

    addi    x10, x10, -1
    jal     func

    ld      ra , 8(sp)
    ld      x13, 0(sp)
    addi    sp, sp, 0x20

    add     x10, x10, x13

    ret

_func_fin:
    ret

これにより、RASの情報が増えていき、そしてRET命令時にPOPされるのを波形上で確認した。シミュレーション結果としても、CALL/RET時にフラッシュが起きていないので良い感じだ。次はこれをDhrystoneで流してみよう。

f:id:msyksphinz:20211228010452p:plain

自作RISC-V CPUコア実装(LDQ/STQ間のフラッシュインタフェースの性能確認)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードが性能ボトルネックになっているのを見た。Dhrystoneの実行に当たりしばらくデバッグしていたのだが、いろいろ試行錯誤してようやくまともに動作するようになってきた気がする。

LDQ/STQ間フラッシュインタフェースを適用することで、もともとの実装に対してどれくらい性能が向上したのかを見てみよう。

  • LDQ/STQフラッシュインタフェース実装前
$ grep "ISS MCYCLE" dhrystone.riscv.log
ISS MCYCLE is updated to RTL = 000000000000075b
ISS MCYCLE is updated to RTL = 00000000000007a8
ISS MCYCLE is updated to RTL = 0000000000069437
ISS MCYCLE is updated to RTL = 0000000000069457

STARTからSTOPまでのサイクル数は0x69457-0x7a8 = 0x68caf = 429,231 (10進数) だった。

これをフラッシュインタフェース実装した場合で同様に性能測定してみる。

$ grep "ISS MCYCLE" dhrystone.riscv.log
ISS MCYCLE is updated to RTL = 000000000000036e
ISS MCYCLE is updated to RTL = 00000000000003a2
ISS MCYCLE is updated to RTL = 000000000002dd13
ISS MCYCLE is updated to RTL = 000000000002dd2d

STARTからSTOPまでのサイクル数は0x2dd13 - 0x3a2 = 0x2d971 = 186,737 (10進数) だった。 おお、かなり速くなったなあ。

次の課題は、それ以外のテストケースのデバッグと、CALL/RETの最適化が未完なのでこれを見てみよう。