FPGA開発日記

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

ハードウェア記述言語Chiselイントロダクション

この記事は ハードウェア開発、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を使った簡単な回路記述の紹介と、簡単なテストプログラムを動作させた。