この記事は ハードウェア開発、CPUアーキテクチャ Advent Calendar 2016 - Qiita の20日目の記事です。
ハードウェア開発、CPUアーキテクチャ Advent Calendarも、25日分ほぼ埋まってきました。あともう少し、各担当者の皆様、頑張りましょう!
Chiselをどのようにして記述して、RTLに変換されるのか、見ていこう。
0. Chiselを最初から理解したければ
RISC-Vの実装とかを眺めるんじゃなくて、まずはこちらの記事がもの凄く分かりやすいので、見てみることをお勧めしたい。
1. Z-scaleの実装を見て、Chiselを理解する
Chiselで実装された例を見るのならば、RocketChip, BOOMあたりを見るのが一番だ。 ただし、これらの実装は非常に複雑し、一つ一つの要素を見ていくのは骨が折れる作業だ。もうちょっと簡単な実装は無いだろうか?
現在は開発が中止されているようだが、さらに小さな実装としてZ-scaleを見ていくようにしよう。
- 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 }
実装の詳細はさておき、トップモジュールでは各ユニットを接続させているだけの感じがする。
- imem/dmem に対するI/Oを定義する (new HastiMasterIO)
- hostに対するインタフェースを定義する (new HtifIO)
ここから先は各インタフェースの接続だ。上記の実装により、おそらく出力としては以下のようになると予想する。
ZscaleのVerilogが生成できないので実際にどのように分からないけれども。。。
2. 自分でChiselを書いて、Verilogを生成させてみたい
なかなかハードルは高そうだけれども、Chiselを自分で書いてハードウェア記述を生成させてみたい。けどコンパイルする環境を生成するのも情報が少ない。。。ということであれば、Chisel-tutorialリポジトリを流用することをお勧めする。
Muxを生成してみる
チュートリアルを見てみると分かるのだが、sbtを立ち上げて以下のコマンドを実行すれば良いだけだ。
sbt > test:run-main problems.Launcher Mux
./test_run_dir/problems.Launcher1497996405/Mux2.v
にVerilogファイルが生成されていることが分かる。
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を使った簡単な回路記述の紹介と、簡単なテストプログラムを動作させた。