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

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

Chisel 3.3.0-RC1で追加された非同期リセットに関する機能を試してみる

スポンサーリンク

Chiselの3.3.0-RC1が公開されたのだが、その中に1つ気になる記述があったので今回は それについて試した内容をまとめておこうと思う。

Chisel 3.3.0-RC1を使ってみる

Chiselの公式ツイッターが3/30にChisel 3.3.0-RC1が公開されたと報告していた。

でこの中に気になる記述が。。。それがこれ↓。

  • Better Asynchronous Reset

ということで、今回は”それと思われるもの”について確認してみた。断定していないのはこのツイッターに記載されている以上の情報が無く、本当に今回紹介するのがその機能なのかの確証がないからで、この後に3.3.0のリリースノートを確認して修正するかも。

なのでgithub上でも3.3.0-RC1はタグが存在するのみとなっている。

ただ3.3.0-RC1は既にMaven Repositoryに公開されているのでchisel-templateを使っていれば使用するChiselのバージョンを変更するだけで使用可能だ。 以下は参考までに自分が使っている"biuld.sbt"の記述を抜粋したもの。

// Provide a managed dependency on X if -DXVersion="" is supplied on the command line.
val defaultVersions = Map(
  "chisel3" -> "3.3.0-RC1", // バージョンを"3.3.0-RC1"にする
  "chisel-iotesters" -> "1.3.0",
  "treadle" -> "1.1.0"
  )

libraryDependencies ++= Seq("chisel3","chisel-iotesters").map {
  dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) }

scalacOptions ++= scalacOptionsVersion(scalaVersion.value)

javacOptions ++= javacOptionsVersion(scalaVersion.value)

lazy val commonSettings = Seq (
  organization := "edu.berkeley.cs",
  version := "2.0",
  //  git.remoteRepo := "git@github.com:ucb-bar/riscv-sodor.git",
  autoAPIMappings := true,
  scalaVersion := "2.11.12",
  crossScalaVersions := Seq("2.11.12", "2.12.6"),
  resolvers ++= Seq(
    Resolver.sonatypeRepo("snapshots"),
    Resolver.sonatypeRepo("releases")
  ),
  scalacOptions := Seq(
    "-deprecation",
    "-feature",
    "-language:reflectiveCalls") ++ scalacOptionsVersion(scalaVersion.value),
  javacOptions ++= javacOptionsVersion(scalaVersion.value),
  // libraryDependenciesに"3.3.0-RC1"の場所を渡せばOK
  libraryDependencies ++= Seq("chisel3", "chisel-iotesters").map {
    dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
  },
  updateOptions := updateOptions.value.withLatestSnapshots(false),
  addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full),
)

Better Asynchronous Resetってこれのこと?

そもそも本題のBetter Asynchronous Resetって何よ??って話なのだが、これはツイッターで「なにこれ、気になる」って呟いたら Chiselのメンテナさんが

  • 「3.2.0から入っているリセットの推測を改善したものだよ」

って教えてくれた。 このコメントを参考にしつつChisel3.2.0以降のコミットとの差分をチェックしてこれかしら??というものを紹介しようと思う。

RequireAsyncReset トレイト

でコミットログを眺めてて見つけたのがRequireAsyncResetというトレイト。 これを使うと自分のデザインをまるっと非同期リセットに変更できる。その意味ではPreset Resetなのかなぁ??と思ったりも。。。

使い方は簡単でトップモジュールにミックスインするだけ。なおこのトレイトと対になるRequireSyncResetも定義されている。

class SubModule extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  io.out := RegNext(io.in, false.B)
}

class BetterAsyncReset extends Module with RequireAsyncReset {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  val m_sub = Module(new SubModule)

  m_sub.io.in := RegNext(io.in, false.B)

  io.out := m_sub.io.out
}

上記のソースコードからRTLを生成すると、次のようにBetterAsyncResetモジュールとSubModuleモジュールに存在するFFが非同期リセットを使ったものになる。

// IOとレジスタの部分のみを抜粋
module SubModule(
  input   clock,
  input   reset,
  input   io_in,
  output  io_out
);
  reg  _T; // @[BetterAsyncReset.scala 14:20]

  always @(posedge clock or posedge reset) begin
    if (reset) begin
      _T <= 1'h0;
    end else begin
      _T <= io_in;
    end
  end
endmodule
module BetterAsyncReset(
  input   clock,
  input   reset,
  input   io_in,
  output  io_out
);
  reg  _T; // @[BetterAsyncReset.scala 25:25]

  always @(posedge clock or posedge reset) begin
    if (reset) begin
      _T <= 1'h0;
    end else begin
      _T <= io_in;
    end
  end
endmodule

Scalaのトレイトはモジュールのインスタンス時にもミックスインすることができるので、次のように引数 に応じて同期 or 非同期リセットを切り替えるようなことも可能になる。

// ここではModuleを継承しただけ
class BetterAsyncReset extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  val m_sub = Module(new SubModule)

  m_sub.io.in := RegNext(io.in, false.B)

  io.out := m_sub.io.out
}

object Elaborate extends App {

  // 引数に応じてRequireAsyncResetをミックスインする
  val module = args(0) match {
    case "async" => () => new BetterAsyncReset with RequireAsyncReset
    case "sync" => () => new BetterAsyncReset
  }

  Driver.execute(Array("-td=rtl", "-tn=BetterAsyncReset"), module)
}

因みにこの機能はModuleクラスの親クラスにあたるMultiIOModuleにおいてreset信号がmkResetメソッドの呼び出しに変更されたことで実現している。

trait RequireAsyncReset extends MultiIOModule {
  override private[chisel3] def mkReset: AsyncReset = AsyncReset()
}

trait RequireSyncReset extends MultiIOModule {
  override private[chisel3] def mkReset: Bool = Bool()
}

/** Abstract base class for Modules, which behave much like Verilog modules.
  * These may contain both logic and state which are written in the Module
  * body (constructor).
  * This abstract base class includes an implicit clock and reset.
  *
  * @note Module instantiations must be wrapped in a Module() call.
  */
abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions)
    extends RawModule {
  // Implicit clock and reset pins
  final val clock: Clock = IO(Input(Clock()))
  final val reset: Reset = IO(Input(mkReset)) // mkResetの戻り値

  private[chisel3] def mkReset: Reset = {
    // Top module and compatibility mode use Bool for reset
    val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset
    if (inferReset) Reset() else Bool()
  }

ということでChisel3.3.0で入るっぽいモジュールのリセットのタイプを制御するトレイトRequireAsyncResetの紹介でした。 でも正式に3.3.0が公開された時には、この記事に修正が入るかも??