FPGA開発日記

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

A Short Users Guide to Chisel 勉強中(1)

Chiselの勉強をすべく、githubのChiselプロジェクトについているWikiを読んで勉強中。

f:id:msyksphinz:20170806234951p:plain

github.com

Chiselは、「Constructing Hardware In a Scala Embedded Language」の略。 Chiselでハードウェアを設計するときは、Scalaのプログラムを利用してハードウェアのデータフローグラフを設計することになる。 本章では、Scala自身の内容については必要に応じて解説することにするが、よりChiselをシンプルかつ再利用できるような構造にするためには、Scalaの本を一冊買って、しっかり勉強することをお勧めする。

Chiselを開発したモチベーションとして、これまでのハードウェア記述言語(Verilog, VHDLなど)はハードウェアシミュレーション言語だが、後にハードウェア合成の基本となる言語となった。 これらの構文はどのようにして実際のハードウェアにマッピングされるか直感的ではなく、誤って使用すると非効率なハードウェアにマッピングされる可能性がある。

それでも、新しいハードウェア設計言語を作成しようとした動機は、エレクトロニクスシステムデザインの手法を変えようと思う我々の思いがあるからである。 学生にとっては、ハードウェア設計言語を習得することと共に、回路"ジェネレータ"を作成する手法を学ぶことも重要である。

ここでScalaを選んだ理由は、回路生成を行うのに必要ないくつかの機能をScalaが含んでいたという理由だけでなく、ドメイン特化言語のために必要な基本的な機能が搭載されていたからである。

Chiselのデータタイプ

Chiselではビットコレクションを表現するためにBitsデータタイプを持っている。 符号有り、符号なし整数はそれぞれSInt, UIntで表現される。 Boolean素はBool型にて表現される。

さらに、ChiselはBundlesというデータタイプを定義し、名前付きフィールド(他の言語ではいわゆる構造体)を定義できるようにしている。 Vecsはインデックス参照の可能な値を定義できる。

1.U       // decimal 1-bit lit from Scala Int.
"ha".U    // hexadecimal 4-bit lit from string.
"o12".U   // octal 4-bit lit from string.
"b1010".U // binary 4-bit lit from string.

5.S    // signed decimal 4-bit lit from Scala Int.
-8.S   // negative decimal 4-bit lit from Scala Int.
5.U    // unsigned decimal 3-bit lit from Scala Int.

true.B // Bool lits from Scala lits.
false.B

デフォルトでは、Chiselのコンパイラは固定数値については値を保持するための最小のビット幅を定義するようになっている。 しかし以下のようにビット幅を明示的に定義することができる。

"ha".asUInt(8.W)     // hexadecimal 8-bit lit of type UInt
"o12".asUInt(6.W)    // octal 6-bit lit of type UInt
"b1010".asUInt(12.W) // binary 12-bit lit of type UInt

5.asSInt(7.W) // signed decimal 7-bit lit of type SInt
5.asUInt(8.W) // unsigned decimal 8-bit lit of type UInt

もし定数を保持するための必要ビット幅が、指定されたビット幅よりも大きい場合、Chiselはエラーを出力する。

組み合わせ回路の記述

Chiselで組み合わせ回路を記述するためには、普通のプログラミング言語と同様に演算子を使って表現することができる。 例えば、以下のような簡単な組み合わせ回路を、以下のような記述で実現できる。

(a & b) | (~c & d)

他にも、組み合わせ回路を記述するのに、有行非巡回グラフ(Directed Acyclic Graph)を使って表現できる。 配線は変数を使って宣言することができ、以下のようなマルチプレクサの記述を定義できる。

val sel = a | b
val out = (sel & in1) | (~sel & in0)

配線

Chiselは他のノードとの接続や、定数との接続のための配線をサポートしている。

val myNode = Wire(UInt(8.W))
when (isReady) {
  myNode := 255.U
} .elsewhen {
  myNode := 0.U
}

また、Chiselでは最後接続された記述が有効である。例えば、以下の2つのChisel回路は等価である。

val myNode = Wire(UInt(8.W))
myNode := 10.U
myNode := 0.U
val myNode = Wire(UInt(8.W))
myNode := 0.U

ビルトイン演算子

Operation Explanation
ビット演算子 SInt, UInt, Bool で有効
val invertedX = ~x NOTビット演算
val hiBits = x & UInt(“h_ffff_0000”) ANDビット演算
val flagsOut = flagsIn | overflow ORビット演算
val flagsOut = flagsIn ^ toggle XORビット演算
ビットリダクション SInt and UIntで有効。Bool型を返す。
val allSet = x.andR AND リダクション
val anySet = x.orR OR リダクション
val parity = x.xorR XOR リダクション
比較演算子 SInt, UInt, and Boolで有効。Bool型を返す。
val equ = x === y 等価
val neq = x =/= y 非等価
シフト演算子 SInt and UIntで有効。
val twoToTheX = 1.S << x 論理左シフト
val hiBits = x >> 16.U 右シフト (Uintでは符号無しシフト、SIntでは符号付シフト)。
ビットフィールド操作 SInt, UInt Bool型で有効
val xLSB = x(0) 1ビット抽出。
val xTopNibble = x(15, 12) endからstartまでのビットを抽出。
val usDebt = Fill(3, “hA”.U) ビット列を複数繰り返す
val float = Cat(sign, exponent, mantissa) 左から順にビット列を接続する。
論理演算 Bool型で有効。
val sleep = !busy 論理否定
val hit = tagMatch && valid 論理AND
`val stall = src1busy || valid 論理OR
val out = Mux(sel, inTrue, inFalse) selがBool型として接続されたMux。
算術演算 SIntとUInt型で有効。
val sum = a + b or val sum = a +% b 加算(ビット拡張無し)
val sum = a +& b 加算(ビット拡張有り)
val diff = a - b or val diff = a -% b 減算(ビット拡張無し)
val diff = a -& b 減算(ビット拡張有り)
val prod = a * b 乗算
val div = a / b 除算
val mod = a % b 剰余
算術比較 SIntとUint型で有効。Boolを返す。
val gt = a > b 大なり
val gte = a >= b 以上
val lt = a < b 小なり
val lte = a <= b 以下

演算後のビット幅

Chiselの演算では、オペランドのビット幅に対して演算後のビット幅は以下のようなルールが存在する。

operation bit width
z = x + y or z = x +% y w(z) = max(w(x), w(y))
z = x +& y w(z) = max(w(x), w(y)) + 1
z = x - y or z = x -% y w(z) = max(w(x), w(y))
z = x -& y w(z) = max(w(x), w(y)) + 1
z = x & y w(z) = min(w(x), w(y))
z = Mux(c, x, y) w(z) = max(w(x), w(y))
z = w * y w(z) = w(x) + w(y)
z = x << n w(z) = w(x) + maxNum(n)
z = x >> n w(z) = w(x) - minNum(n)
z = Cat(x, y) w(z) = w(x) + w(y)
z = Fill(n, x) w(z) = w(x) * maxNum(n)

この中で、もしオペランド内にビット幅が不明な変数が入っている場合、これらの演算はエラーとなる。