# 【1】Scala 和 Chisel 語法簡記
contributed by AgainTW
---
# 章節
* [【1】Scala 和 Chisel 語法簡記](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/BJdW9obUa)
* [【2】組合電路、序向電路和 Control Flow](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/HyWJBxmUa)
* [【3】Generators](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/SJZ7kz7L6)
* [【4】高階函式與設計](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/rk0Ckf7Lp)
* [【5】物件導向設計](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/B1hB1aTLT)
* [【6】Generators: Types](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/SJ8tka6U6)
* [【7】FIRRTL 簡介](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/HJOjkppIT)
* [【8】Chisel 到 Verilog 的中間表示](https://hackmd.io/@nfUUgsYRTGy81y5d9AYOyg/r1URy6aIT)
---
# Outline
1. scala 語法
2. chisel 語法
3. 範例1: FIR filter
---
# 隨筆
* 程式碼重複使用的能力是 Chisel 的優點之一
* Chisel 是一種 HCL(Hardware Construction Language),它被設計用來**生成** HDL(Hardware Description Language)
* Verilog 則是一種 HDL,它可以描述數位電路的功能與行為
---
# 名詞解釋
* DSL: Domain-specific language。 Scala 就是託管 DSL 的良好語言
---
# scala 語法
* scala 的變數型態通常是宣告在右邊
```scala=
// 變數的型態定義
val max: Long = (1 << counterBits) - 1
// 函數的輸入型態和回傳值型態定義
def myMethod(count: Int, wrap: Boolean, wrapValue: Int = 24): Unit = { ... }
```
## var 和 val
* 前面有提到 ```val``` 是定義常數。相反的,```var``` 是用來定義變數
* chisel 希望盡可能使用 ```val``` 來定義,需要修改時再使用 ```:=``` 修改值。這樣是為了增加除錯的能力
## 條件句
* 這邊只記錄比較特別的語法
```scala=
val likelyCharactersSet = if (alphabet.length == 26)
"english"
else
"not english"
println(likelyCharactersSet)
```
## def
* 跟 python 蠻像的
* 在 def 內定義的 def 只能在該 def 內使用
## list
* 直接看舉例
```scala=
val x = 7
val y = 14
val list1 = List(1, 2, 3)
val list2 = x :: y :: y :: Nil // An alternate notation for assembling a list
val list3 = list1 ++ list2 // Appends the second list to the first list
val m = list2.length
val s = list2.size
val headOfList = list1.head // Gets the first element of the list
val restOfList = list1.tail // Get a new list with first element removed
val third = list1(2) // Gets the third element of a list (0-indexed)
```
```shell
// 輸出
x: Int = 7
y: Int = 14
list1: List[Int] = List(1, 2, 3)
list2: List[Int] = List(7, 14, 14)
list3: List[Int] = List(1, 2, 3, 7, 14, 14)
m: Int = 3
s: Int = 3
headOfList: Int = 1
restOfList: List[Int] = List(2, 3)
third: Int = 3
```
## for
* 第一種
```scala=
for (i <- 0 to 7) { print(i + " ") }
println()
```
```shell
0 1 2 3 4 5 6 7
```
* 第二種
```scala=
for (i <- 0 until 7) { print(i + " ") }
println()
```
```shell
0 1 2 3 4 5 6
```
* 第三種
```scala=
for(i <- 0 to 10 by 2) { print(i + " ") }
println()
```
```shell
0 2 4 6 8 10
```
---
# chisel 語法
* ```Module``` 是所有硬體模組都必須擴充的內建 Chisel 類別。
* Module 的輸入與輸出由特殊的 ```io val``` 定義。它必須被命名為 io 並且是一個 IO 物件或實例
```scala=
val io = IO(new Bundle {
val in = Input(...)
val out = Output(...)
})
```
* 使用 getVerilog() 可以將 chisel 構造好的硬體轉譯成 Verilog。這個過程稱之為 "elaboration"(闡述)
* 使用 getFirrtl() 可以將 chisel 構造好的硬體轉譯成 getFirrtl。Firrtl 是 chisel 轉譯成 verilog 前的中間描述式
## 硬體測試
* ```poke```: 用來設定測試輸入
* ```expect```: 用來**判斷**測試輸出
* ```peek```: 用來取得測試輸出
```scala=
// Scala Code: `test` runs the unit test.
// test takes a user Module and has a code block that applies pokes and expects to the
// circuit under test (c)
test(new Passthrough()) { c =>
c.io.in.poke(0.U) // Set our input to value 0
c.io.out.expect(0.U) // Assert that the output correctly has 0
c.io.in.poke(1.U) // Set our input to value 1
c.io.out.expect(1.U) // Assert that the output correctly has 1
c.io.in.poke(2.U) // Set our input to value 2
c.io.out.expect(2.U) // Assert that the output correctly has 2
}
println("SUCCESS!!") // Scala Code: if we get here, our tests passed!
```
* 可以在 chisel module 中插入 C 的 printf 用以在測試時確認輸出
```scala=
class PrintingModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
io.out := io.in
printf("Print during simulation: Input is %d\n", io.in)
// chisel printf has its own string interpolator too
printf(p"Print during simulation: IO is $io\n")
println(s"Print during generation: Input is ${io.in}")
}
test(new PrintingModule ) { c =>
c.io.in.poke(3.U)
c.clock.step(5) // circuit will print
println(s"Print during testing: Input is ${c.io.in.peek()}")
}
```
```shell
Elaborating design...
Print during generation: Input is UInt<4>(IO in unelaborated PrintingModule)
Done elaborating.
Print during simulation: Input is 3
Print during simulation: IO is AnonymousBundle(in -> 3, out -> 3)
Print during simulation: Input is 3
Print during simulation: IO is AnonymousBundle(in -> 3, out -> 3)
Print during simulation: Input is 3
Print during simulation: IO is AnonymousBundle(in -> 3, out -> 3)
Print during simulation: Input is 3
Print during simulation: IO is AnonymousBundle(in -> 3, out -> 3)
Print during simulation: Input is 3
Print during simulation: IO is AnonymousBundle(in -> 3, out -> 3)
Print during testing: Input is UInt<4>(3)
Print during simulation: Input is 0
Print during simulation: IO is AnonymousBundle(in -> 0, out -> 0)
test PrintingModule Success: 0 tests passed in 7 cycles in 0.008715 seconds 803.24 Hz
```
---
# 範例1: FIR filter
## MovingAverage3 定義
* UInt: chisel 提供的變數型態
* Input(), Output(): 提供**有方向**的 port 定義
* ```.W```: 用於定義位寬
* ```.U```: 用於將 Scala 整數轉換為 Chisel 的硬體整數
* RegNext(): reg 函數,可以在下一個 clock 將輸入吐出
```scala=
// 3-point moving average implemented in the style of a FIR filter
class MovingAverage3(bitWidth: Int) extends Module {
// 使用 Bundle(類似 C 的 struct) 定義輸入輸出介面
val io = IO(new Bundle {
val in = Input(UInt(bitWidth.W))
val out = Output(UInt(bitWidth.W))
})
val z1 = RegNext(io.in) // Create a register whose input is connected to the argument io.in
val z2 = RegNext(z1) // Create a register whose input is connected to the argument z1
io.out := (io.in * 1.U) + (z1 * 1.U) + (z2 * 1.U) // `1.U` is an unsigned literal with value 1
}
```
## 視覺化 RTL code
* 在視覺化的呈現中
* register: 以**金色**呈現
```scala=
// same 3-point moving average filter as before
visualize(() => new MovingAverage3(8))
```

## 序列化輸入輸出的 FIR filter
* chisel 比 verilog 強大的地方就是 Module 不是固定的硬體,它可以被設計成根據序列輸入產生對應的硬體
* 例如我們設計了可以根據輸入變化的硬體
* 因為輸入可以是可變長度的
* until: scala 的迴圈語法之一。[參考資料](https://www.delftstack.com/zh-tw/howto/scala/foreach-loop-in-scala/#google_vignette)
* Vec(): Chisel 集合類型的集合類型,類似矩陣,可以以矩陣的形式儲存相同類型的物件(它只能包含 Chisel 硬體元素)。但**它並不是真實儲存於記憶體的矩陣**,實際上,Vec() 收集輸入的 wire,就有點類似集線器(?)
* ```=```: 用於 val 定義,初始化變數的狀態
* ```:=```: 初始化後的 wire 不能再使用 ```=``` 賦值,引此 chisel 提供 ```:=``` 的重新賦值方法。[參考資料](https://blog.csdn.net/Frederick_Bala/article/details/108375915)
* ```<-```: scala 語法。用於迴圈中計數變量赋值
* ```List.tabulate()()```: scala list 語法。第一個參數接收**列表**,第二個參數相當於一個或多個 for 迴圈。[參考資料](https://blog.csdn.net/weixin_44941795/article/details/104991875)
* ```.reduce()```: scala list 語法。取得集合(Array、List 等)中的所有元素,並使用二元運算將它們組合起來以產生單一值。
```scala=
// Generalized FIR filter parameterized by the convolution coefficients
class FirFilter(bitWidth: Int, coeffs: Seq[UInt]) extends Module {
// 定義輸入輸出介面
val io = IO(new Bundle {
val in = Input(UInt(bitWidth.W))
val out = Output(UInt())
})
// Create the serial-in, parallel-out shift register
// chisel 強大的地方
val zs = Reg(Vec(coeffs.length, UInt(bitWidth.W)))
zs(0) := io.in
for (i <- 1 until coeffs.length) {
zs(i) := zs(i-1)
}
// Do the multiplies
val products = VecInit.tabulate(coeffs.length)(i => zs(i) * coeffs(i))
// Sum up the products
io.out := products.reduce(_ +& _)
}
```
* 因此輸入不同輸入就能產生不同的硬體
```scala=
// same 3-point moving average filter as before
visualize(() => new FirFilter(8, Seq(1.U, 1.U, 1.U)))
```

```scala=
// 5-point FIR filter with a triangle impulse response
visualize(() => new FirFilter(8, Seq(1.U, 2.U, 3.U, 2.U, 1.U)))
```

---
# 參考
* [Chisel-bootcamp](https://mybinder.org/v2/gh/freechipsproject/chisel-bootcamp/master)