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

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

Chisel Bootcamp - Module 2.1(1) - 最初のモジュール

スポンサーリンク

前回の記事でChisel BootcampのModule1がやっと終わった。 徐ろに、題名につけてた”Chiselの勉強”を省いてスタート。

www.tech-diningyo.info

ということで今日からはModule2に入っていよいよChiselを使ったHW設計について学んでいく。

Module 2.1: 最初のChiselモジュール

モチベーション

とりあえずモチベーションを見てみる。

Module1でScalaにある程度なれたので、ついにハードウェアを掘り始める時だ!ChiselはConstructing Hardware In a Scala Embedded Languageの頭文字をとったものだ。これはChiselがScalaDSLであり、あなたにScalaとChiselの良いところを同じコード内でどちらも享受することを認めることを意味する。どのコードが”Scala"のものでどのコードが”Chisel"のものであるかを理解することは大切だが、これについてはまた後ほど議論することにする。まず今はChiselとModule2のコードがVerilogを書くことよりも優れた方法であることについて考えていこう。このModuleではChisel Module全体とテスターについてをあなたに教えていく。この後に多くの例題を見ることになるので、まずはgistからコードを取ってこよう。

まあ、なんか全体的な話をするよーって感じでしょうかね。

セットアップ

以下のようなコードを実行することで、Chisel一式のダウンロードが行われる。

val path = System.getProperty("user.dir") + "/source/load-ivy.sc"
interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))

上記のコードはJupyter notebook上で実行されるので、実行結果が出力される部分に様々なモジュールのダウンロードログが表示される。

ログはgistに置いてあるので、必要があれば参照してもらえれば。

ダウンロードが終わると以下のようなメッセージが出力されてセットアップが終わったぽい雰囲気になる。

Compiling chisel-bootcamp/Main.sc
path: String = "chisel-bootcamp/source/load-ivy.sc"

この表示が出ると、importが可能になる。

import chisel3._
import chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}

上記の3つのimportを実行すると、Chiselを使ったHW設計を行うためのセットアップが完了だ。

最初のモジュール

このセクションではいよいよChiselで最初のモジュールを作ることになるが、ここでは最初のモジュールも含めて以下のことを知ることが出来る。

  • モジュールのコード
  • テストケースのコード
  • どのように実行するか

例題:モジュール

Verilogと同様にChiselでもモジュールの宣言を行うことが出来る。例題ではPassthroughという名前のModuleが取り上げられている。ざっとまとめると以下のような仕様になる(まあ、何するモジュールなのかはお察しのとおりだ)

  • モジュール名:Passthrough
  • in : 4-bit input
  • out: 4-bit output
  • 処理:組み合わせ回路でinoutに接続されているので、inoutを駆動する(パススルー)
// Chisel Code: Declare a new module definition
class Passthrough extends Module {
  val io = IO(new Bundle {
    val in = Input(UInt(4.W))
    val out = Output(UInt(4.W))
  })
  io.out := io.in
}

これくらいの物だと、見ただけでもなんとなくわかるがChiselとしての基本的なことが色々詰まっているようなので、解説を読み進めていく。

モジュール宣言

まずはクラスを使ってChiselのModule宣言。

class Passthrough extends Module {

見てわかるようにModuleを継承して作られている。このModuleはChiselのビルトイン・クラスで全てのハードウェアモジュールはこれを継承しなければならない。

IO宣言

全ての入力/出力ポートは特別なio valの中に宣言する。

val io = IO(...)

このIO宣言については以下の制約が存在する。

  • 変数名はioでなければならない
  • IOオブジェクト、またはIO(_instantiated_bundle_)のような形式のインスタンスでなければならない

ポート用変数ioの中に新しいハードウェア構造の型(Bundle)を宣言し、その中に名前のついた信号in/outをそれぞれ入力/出力として宣言する。

new Bundle {
    val in = Input(...)
    val out = Output(...)
}
ビット幅指定

下記のコードで信号のハードウェアタイプを宣言する。

UInt(4.W)

この例ではin/out共に4-bit幅のunsinged intになる。

ハードウェアの処理 - 方向指示演算子(directioned operator)

val ioの下に続くのこのモジュールの処理になる。ここでは入力ポートの信号を出力ポートに接続しているのでパススルー処理が実装されていることになる。

io.out := io.in

ここで使用されている:=演算子はChiselの演算子

  • 右辺の信号で左辺の信号をドライブする

こと意味する。

この演算子を方向指示演算子(directioned operator)と呼ぶ。

エラボレーション

きちんとしたハードウェア構築言語ではその基礎となる言語をスクリプト言語のように扱うことが出来る。例を示すと、Chiselでモジュールを宣言した後、ScalaでChiselのコンパイラーを呼びだし、ChiselのPassthroughモジュールをVerilogPassthroughモジュールに変換する。このプロセスをエラボレーションという。

// Scala Code: Elaborate our Chisel design by translating it to Verilog
// Don't worry about understanding this code; it is very complicated Scala
println(getVerilog(new Passthrough))

上記を実行すると以下の出力が得られる

[info] [0.001] Elaborating design...
[info] [0.741] Done elaborating.
Total FIRRTL Compile Time: 235.4 ms
module cmd2HelperPassthrough( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input  [3:0] io_in, // @[:@6.4]
  output [3:0] io_out // @[:@6.4]
);
  assign io_out = io_in;
endmodule

なお、上記でモジュール名がcmd<#>HelperPassthroughとなっているのは、このチュートリアルをJupyter上で動かしたためのものだ。Chiselではモジュールの名前と他のコンポーネントのを可能な限り保持してくれるとのことだ。

上記の出力を見るとわかるが、printlnで出力された結果にVeirlogのPassthroughモジュールが出力されている。

module cmd2HelperPassthrough( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input  [3:0] io_in, // @[:@6.4]
  output [3:0] io_out // @[:@6.4]
);
  assign io_out = io_in;
endmodule

処理もChiselで書いたものが確かに反映されている。

ということで、とりあえず最初のモジュールが出来たので今日はここまで。