FPGA開発日記

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

SonicBOOMのデザインを読み解く(算術演算のデータパスの生成方法)

RISC-VのOoOコアであるSonicBOOMのデザインを勉強している。前回に続いて、以下のテストパタンを用いてALUのデータパスがどのようにして選択されているのかを観察している。

    .section    .text
    .global     simple_add
simple_add:
    addi    x10, x0,  1
    addi    x11, x10, 2
    addi    x12, x11, 3
    addi    x13, x12, 4
    addi    x14, x13, 5
    addi    x15, x14, 6
    addi    x16, x15, 7
    addi    x17, x16, 8
    addi    x18, x17, 9

    mv      x10, x18
    ret

データパスの構成方法について、BOOMでは以下のような記述で演算器を構築することができるようになっている。

  • src/main/scala/exu/core.scala
  // Only holds integer-registerfile execution units.
  val exe_units = new boom.exu.ExecutionUnits(fpu=false)
  • src/main/scala/exu/execution-units/execution-units.scala
/**
 * Top level class to wrap all execution units together into a "collection"
 *
 * @param fpu using a FPU?
 */
class ExecutionUnits(val fpu: Boolean)(implicit val p: Parameters) extends HasBoomCoreParameters
{
...
  if (!fpu) {
    val int_width = issueParams.find(_.iqType == IQT_INT.litValue).get.issueWidth

    for (w <- 0 until memWidth) {
      val memExeUnit = Module(new ALUExeUnit(
        hasAlu = false,
        hasMem = true))

      memExeUnit.io.ll_iresp.ready := DontCare

      exe_units += memExeUnit
    }

    for (w <- 0 until int_width) {
      def is_nth(n: Int): Boolean = w == ((n) % int_width)
      val alu_exe_unit = Module(new ALUExeUnit(
        hasJmpUnit     = is_nth(0),
        hasCSR         = is_nth(1),
        hasRocc        = is_nth(1) && usingRoCC,
        hasMul         = is_nth(2),
        hasDiv         = is_nth(3),
        hasIfpu        = is_nth(4) && usingFPU))
      exe_units += alu_exe_unit
    }
  } else {
    val fp_width = issueParams.find(_.iqType == IQT_FP.litValue).get.issueWidth
    for (w <- 0 until fp_width) {
      val fpu_exe_unit = Module(new FPUExeUnit(hasFpu = true,
                                             hasFdiv = usingFDivSqrt && (w==0),
                                             hasFpiu = (w==0)))
      exe_units += fpu_exe_unit
    }
  }
...

一見すると意味不明だが、最初のforループでメモリアドレス計算のためのALUを生成し、次のループで整数演算用のALUユニットを生成している。ALUユニットの生成においてis_nth()というのを使っているが、これはつまりn個のALUを生成するときに、

  • 0 % n番目のALUはJmpUnit用
  • 1 % n番目のALUはCSR
  • 1 % n番目のALUはRoCC用
  • 2 % n番目のALUはMultiplier用
  • 3 % n番目のALUはDivider用
  • 4 % n番目のALUはFPUの整数側用

という割り付けになっている(記事を書きながら思ったが、つまりこれは4番目以降の演算器を生成してもデフォルト(通常算術演算)用以外のALUを生成することは出来ないということか)。

f:id:msyksphinz:20201230234534p:plain

このALUに入力するためのデータ入力だが、基本的にはiregfileからの入力となっているがそれ以外にBypassからの入力も受け付けている。これらの入力オペランドの選択はiregister_readユニットによって行われており、ここでBypassデータの選択が行われている。