FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

高位合成言語Chiselイントロダクション(2)

この記事は ハードウェア開発、CPUアーキテクチャ Advent Calendar 2016 - Qiita の20日目の記事です。

ハードウェア開発、CPUアーキテクチャ Advent Calendarも、25日分ほぼ埋まってきました。あともう少し、各担当者の皆様、頑張りましょう!

Chiselをどのようにして記述して、RTLに変換されるのか、見ていこう。


0. Chiselを最初から理解したければ

RISC-Vの実装とかを眺めるんじゃなくて、まずはこちらの記事がもの凄く分かりやすいので、見てみることをお勧めしたい。

www.muo.jp

1. Z-scaleの実装を見て、Chiselを理解する

Chiselで実装された例を見るのならば、RocketChip, BOOMあたりを見るのが一番だ。 ただし、これらの実装は非常に複雑し、一つ一つの要素を見ていくのは骨が折れる作業だ。もうちょっと簡単な実装は無いだろうか?

現在は開発が中止されているようだが、さらに小さな実装としてZ-scaleを見ていくようにしよう。

github.com

  • Z-scale: Tiny 32-bit RISC-V Systems

https://riscv.org/wp-content/uploads/2015/06/riscv-zscale-workshop-june2015.pdf

Z-scaleの実装をダウンロードして中を見てみる

git clone https://github.com/ucb-bar/zscale.git
cd zscale/src/main/scala
ls
ctrl.scala  dpath.scala  package.scala  sram.scala  zscale.scala

ファイル数としては非常に少ない。zscale.scalaは以下のような構成になっていた。

class Zscale(resetSignal: Bool = null)(implicit val p: Parameters) extends Module(_reset = resetSignal)
    with HasZscaleParameters {
  val io = new Bundle {
    val imem = new HastiMasterIO
    val dmem = new HastiMasterIO
    val host = new HtifIO
  }

  val ctrl = Module(new Control)
  val dpath = Module(new Datapath)

  io.imem <> ctrl.io.imem
  io.imem <> dpath.io.imem
  io.dmem <> ctrl.io.dmem
  io.dmem <> dpath.io.dmem
  ctrl.io.dpath <> dpath.io.ctrl

  ctrl.io.host <> io.host
  dpath.io.host <> io.host
}

実装の詳細はさておき、トップモジュールでは各ユニットを接続させているだけの感じがする。

  1. imem/dmem に対するI/Oを定義する (new HastiMasterIO)
  2. hostに対するインタフェースを定義する (new HtifIO)

ここから先は各インタフェースの接続だ。上記の実装により、おそらく出力としては以下のようになると予想する。

f:id:msyksphinz:20161218153152p:plain

ZscaleのVerilogが生成できないので実際にどのように分からないけれども。。。

2. 自分でChiselを書いて、Verilogを生成させてみたい

なかなかハードルは高そうだけれども、Chiselを自分で書いてハードウェア記述を生成させてみたい。けどコンパイルする環境を生成するのも情報が少ない。。。ということであれば、Chisel-tutorialリポジトリを流用することをお勧めする。

github.com

Muxを生成してみる

チュートリアルを見てみると分かるのだが、sbtを立ち上げて以下のコマンドを実行すれば良いだけだ。

sbt
> test:run-main problems.Launcher Mux

./test_run_dir/problems.Launcher1497996405/Mux2.vVerilogファイルが生成されていることが分かる。

Verilogファイルは以下のようになっている。

module Mux2(
  input   clock,
  input   reset,
  input   io_sel,
  input   io_in0,
  input   io_in1,
  output  io_out
);
  wire  _T_10;
  wire  _T_11;
  wire  _T_12;
  wire  _T_13;
  assign io_out = _T_13;
  assign _T_10 = io_sel & io_in1;
  assign _T_11 = ~ io_sel;
  assign _T_12 = _T_11 & io_in0;
  assign _T_13 = _T_10 | _T_12;
endmodule

もう少し改造していろんな何かを作ってみたい

ちなみにこのchisel-tutorialのファイル、adder.scalaが空欄になっており、自分で作らなければならない。

class Adder(val w: Int) extends Module {
  val io = IO(new Bundle {
    val in0 = Input(UInt(1.W))
    val in1 = Input(UInt(1.W))
    val out = Output(UInt(1.W))
  })
  // fill in here
  io.out := 0.U
}

ただし、これは簡単。Counter.scalaを見てみると、普通に加算の演算子が使えるじゃないか。

class Adder(val w: Int) extends Module {
  val io = IO(new Bundle {
    val in0 = Input(UInt(w.W))
    val in1 = Input(UInt(w.W))
    val out = Output(UInt(w.W))
  })
  io.out := io.in0 + io.in1
}

Accumulatorの問題もある。これもCounterの通り、レジスタを定義すれば何とかなりそうかな。

class Accumulator extends Module {
  val io = IO(new Bundle {
    val in  = Input(UInt(1.W))
    val out = Output(UInt(8.W))
  })
  val acc = Reg(init=0.asUInt(8.W))
  acc := acc + io.in
  io.out := acc
}

これ、いまいちどのような基準で記述すれば良いのか分からないな。時間的な考え方は、Verilogじゃないんだからもちろん無いし、だからといってこのReg宣言はどこまでの有効範囲を持っているんだ? もうちょっと勉強が必要だ。さらに、Enable付きのAccumulatorはどのように記述すれば良いんだろう?

3. 終わりに

以上、Chiselを使った簡単な回路記述の紹介と、簡単なテストプログラムを動作させた。

重要なのは、

  • Chiselは高位合成とは言え、そこまでリッチな環境ではない。
  • Chiselは教育用回路ジェネレータとしての側面を持っている。遅かれ早かれ、直接回路を記述するのでななく、高位合成によって回路を作らなければならない時代がやってくるという、UC-Berkeleyの将来予測が根底にある。
  • Chiselを学ぶことが重要じゃない。如何にして自分の設計を、高位合成の技術を使って楽にするか?それが問題だ。