# 【6】Generators: Types 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. 靜態類別 2. 類型匹配 --- # 隨筆 * Scala 是一種強型別程式語言 * 所以用 Scala 編譯的程式比 Python 那類的程式執行時錯誤要少得多 * Python 是動態類型語言 * 這一章和 chisel 設計比較不相關,但有助於理解在 chisel 中如何除錯 * 我們從 Scala 的角度去理解 chisel 是如何設計的 * 對物件導向設計比較不熟悉的話慎重閱讀 --- # 靜態類別 ## Scala 中的 type 簡介 * Scala 中的所有物件(object)都有一個類型(type) * 使用```.getClass```在 Scala 獲得物件的 type * ```class```阿還是物件導向設計那章提到的各種物件都可以```.getClass``` * 然後會回傳自定義的那個類別名稱 * 下面的範例中回傳的就是```MyClass``` ```scala= class MyClass { def myMethod = ??? } println(new MyClass().getClass) ``` ```shell class ammonite.$sess.cmd3$Helper$MyClass ``` * 不回傳任何內容的函數回傳類型為```Unit``` ```scala= var counter = 0 def increment(): Unit = { counter += 1 } ``` ### Ammonite 是甚麼? * Ammonite 讓您可以使用 Scala 語言進行腳本編寫 * 在REPL中或作為腳本 * 設計的目標是希望能將 Scala 程式碼從重量級「專案」中解放出來 ### REPL 又是甚麼? * Read–Eval–Print Loop (REPL) 是一種互動介面、交互式的編程環境,常用於表現程式語言的動作,其名稱即清楚表示了該介面的互動流程 * Read:讀取使用者輸入的指令。 * Eval:縮寫自 evaluate,即運算使用者輸入的指令。 * Print:輸出運算完該指令的結果。 * Loop:上述三種動作,會不斷地依序執行,直至使用者將此介面關閉為止。 ## Scala 與 Chisel 類型 * 前面第二章有提到 * Scala 編譯器能區分 Scala 與 Chisel 類型的差別是因為 Scala 靜態類別的特性 * 並且還能區分```if()```內期望收到的是 Scala 的 Boolean 型態 * 而```when()```內期望收到的是 Chisel 的 Bool 型態 ## Scala 中的強制轉型 * 語法: * 能將物件 x 強制轉換為類型 T * 如果給定物件無法轉換為類型 T ,它將引發異常 ```java.lang.ClassCastException``` ```scala= x.asInstanceOf[T] ``` * 像是 Chisel 的 UInt 就無法轉型成 Scala 的 Int * 但可以轉型成 Scala 的 Data,因為 UInt 就是從 Data 繼承而來 ## Chisel 中的強制轉型 * verolog 在有號數和無號數之間轉型時,使用兩個系統函數```$signed()```、```$unsigned()``` * 這兩個函數的作用是告訴編譯器所修飾的變數的二進位資料被當作有符號數或無符號數來處理。並不會對變數資料做任何轉換操作。 * Chisel 使用```.asTypeOf()```來轉型 :::warning * 這部分功能在 2023/12/26 的 chisel3 中似乎仍在維護 * Chisel 允許 * Bool/UInt to Clock * Bool/UInt to Bool/UInt (但可能會根據需要截斷/填充位元) * Bundle to Bundle (但可能會根據需要截斷/填充位元) ::: * 範例 2 和範例 3 長出來的 verilog code 完全一樣 * 是因為 Chisel 在長 verilog code 時,預設是讀入純信號,不做任何修飾 * 在讀入後有需要進行修飾時才會將修飾後的值指給其他 wire * 雖然進行轉型了,但是有 bug 產生,因此需要設計額外電路去處理 * 範例 3 的 bug * 如果輸入是```-7 = 0b1001``` * 那輸出就應該特別處理,否則輸出會是```0b1001 = 9``` * 雖然長得出電路,但這並不合理 ### 範例 * 範例 1: * bootcamp 的原始範例,會跑不出東西,因為輸出和輸入型態不同 ```scala= class TypeConvertDemo extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(SInt(4.W)) }) io.out := io.in//.asTypeOf(io.out) } println(getVerilog(new TypeConvertDemo())) ``` * 範例 2 * 有使用轉型 * 輸入是```UInt```,輸出是```SInt``` ```scala= class TypeConvertDemo extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(SInt(4.W)) }) io.out := io.in.asTypeOf(io.out) } println(getVerilog(new TypeConvertDemo())) ``` ```mips module TypeConvertDemo( input clock, input reset, input [3:0] io_in, output [3:0] io_out ); assign io_out = io_in; // @[cmd15.sc 6:29] endmodule ``` * 範例 3 * 有使用轉型 * 輸入是```SInt```,輸出是```UInt``` ```scala= class TypeConvertDemo extends Module { val io = IO(new Bundle { val in = Input(SInt(4.W)) val out = Output(UInt(4.W)) }) io.out := io.in.asTypeOf(io.out) } println(getVerilog(new TypeConvertDemo())) ``` ```mips module TypeConvertDemo( input clock, input reset, input [3:0] io_in, output [3:0] io_out ); assign io_out = io_in; // @[cmd16.sc 6:29] endmodule ``` * 範例 4 * 希望長出來的 verilog code 會用到 $signed() ```scala= class TypeConvertDemo extends Module { val io = IO(new Bundle { val in = Input(SInt(4.W)) val out = Output(UInt(4.W)) }) val temp = io.in * -1.S io.out := temp.asTypeOf(io.out) } println(getVerilog(new TypeConvertDemo())) ``` ```mips module TypeConvertDemo( input clock, input reset, input [3:0] io_in, output [3:0] io_out ); wire [4:0] _T = $signed(io_in) * 1'sh1; // @[cmd17.sc 8:28] assign io_out = _T[3:0]; // @[cmd17.sc 8:28 cmd17.sc 8:28] endmodule ``` ## Chisel 的類型結構 * chisel3.Data 是 Chisel 硬體類型的基底類別 * ```UInt```、```SInt```、```Vec```、```Bundle```等都是```Data```的實例 * ```Data```可用於 IO 和 supports ```:=```、wires、regs 等 * ```:=```是為所有```Data```定義的 * 就是 Chisel 中的```Reg``` 就是多型態的一個很好的例子 * [原始碼](https://github.com/chipsalliance/chisel/blob/v3.0.0/src/main/scala/chisel3/util/Reg.scala#L10) * 有些操作僅在```Bit```的子類型上定義,例如```+``` * 這就是為什麼```UInt```或```SInt```的類型可以相加,但不能```Bundle```或```Vec```不行 * 通常建議避免將繼承與類型泛型一起使用 * 透過對 Chisel 底層的認識,我們可以寫出以下這種程式碼 ![image](https://hackmd.io/_uploads/ry4cHB_DT.png) ```scala= class ShiftRegisterIO[T <: Data](gen: T, n: Int) extends Bundle { require (n >= 0, "Shift register must have non-negative shift") val in = Input(gen) val out = Output(Vec(n + 1, gen)) // + 1 because in is included in out override def cloneType: this.type = (new ShiftRegisterIO(gen, n)).asInstanceOf[this.type] } class ShiftRegister[T <: Data](gen: T, n: Int) extends Module { val io = IO(new ShiftRegisterIO(gen, n)) io.out.foldLeft(io.in) { case (in, out) => out := in RegNext(in) } } println(getVerilog(new ShiftRegister(SInt(6.W), 3))) ``` ```mips= module ShiftRegister( input clock, input reset, input [5:0] io_in, output [5:0] io_out_0, output [5:0] io_out_1, output [5:0] io_out_2, output [5:0] io_out_3 ); `ifdef RANDOMIZE_REG_INIT reg [31:0] _RAND_0; reg [31:0] _RAND_1; reg [31:0] _RAND_2; `endif // RANDOMIZE_REG_INIT reg [5:0] REG; // @[cmd41.sc 14:16] reg [5:0] REG_1; // @[cmd41.sc 14:16] reg [5:0] REG_2; // @[cmd41.sc 14:16] assign io_out_0 = io_in; // @[cmd41.sc 13:13] assign io_out_1 = REG; // @[cmd41.sc 13:13] assign io_out_2 = REG_1; // @[cmd41.sc 13:13] assign io_out_3 = REG_2; // @[cmd41.sc 13:13] always @(posedge clock) begin REG <= io_in; // @[cmd41.sc 14:16] REG_1 <= REG; // @[cmd41.sc 14:16] REG_2 <= REG_1; // @[cmd41.sc 14:16] end endmodule ``` ## 帶有型別類別的型別泛型 :::warning Chisel 正在從 Scala for Chisel 編譯器 (SFC) 過渡到 MLIR FIRRTL 編譯器。在此過渡中,FixedPoint 在 FIRRTL 層級被刪除,這意味著 FixPoint 需要重新實作為依賴其餘現有 Chisel 類型的東西,並使用 Chisel 的公共 API。該庫旨在提供用戶級實現。因此誕生了 "Fixed-Point User Library for Chisel" 語法: ```import fixedpoint._``` ::: :::warning 如果固定點是```Vec```,那目前這個問題還沒有可用的解決方案 ::: * 以```dsptools```函式庫提供的功能為例 * 提供了用於編寫類型參數化 DSP 生成器的工具 * 提供用於複數運算的 DspComplex[T] 類型 * MAC: multiply-accumulate * [FixedPoint](https://github.com/ucb-bar/fixedpoint/blob/master/src/main/scala/fixedpoint/FixedPoint.scala) * 需要```import chisel3.experimental._``` * ```FixedPoint(width: Width, binaryPoint: BinaryPoint)``` * 第 3 行```T <: Data : Ring``` * 意味著它 T 是 的```Data```的子類型並且也是```Ring```的子類型 * ```Ring```被定義為帶有```+```和```*```以及其他運算的 dsptools 數字 ```scala= import chisel3.experimental._ import dsptools.numbers._ class Mac[T <: Data : Ring](genIn : T, genOut: T) extends Module { val io = IO(new Bundle { val a = Input(genIn) val b = Input(genIn) val c = Input(genIn) val out = Output(genOut) }) io.out := io.a * io.b + io.c } println(getVerilog(new Mac(UInt(4.W), UInt(6.W)) )) println(getVerilog(new Mac(SInt(4.W), SInt(6.W)) )) println(getVerilog(new Mac(FixedPoint(4.W, 3.BP), FixedPoint(6.W, 4.BP)))) ``` ```mips module Mac( input clock, input reset, input [3:0] io_a, input [3:0] io_b, input [3:0] io_c, output [5:0] io_out ); wire [7:0] _T = io_a * io_b; // @[UIntTypeClass.scala 40:41] wire [7:0] _GEN_0 = {{4'd0}, io_c}; // @[UIntTypeClass.scala 19:40] wire [7:0] _T_2 = _T + _GEN_0; // @[UIntTypeClass.scala 19:40] assign io_out = _T_2[5:0]; // @[cmd27.sc 11:12] endmodule module Mac( input clock, input reset, input [3:0] io_a, input [3:0] io_b, input [3:0] io_c, output [5:0] io_out ); wire [7:0] _T = $signed(io_a) * $signed(io_b); // @[SIntTypeClass.scala 45:41] wire [7:0] _GEN_0 = {{4{io_c[3]}},io_c}; // @[SIntTypeClass.scala 19:40] wire [7:0] _T_3 = $signed(_T) + $signed(_GEN_0); // @[SIntTypeClass.scala 19:40] assign io_out = _T_3[5:0]; // @[cmd27.sc 11:12] endmodule module Mac( input clock, input reset, input [3:0] io_a, input [3:0] io_b, input [3:0] io_c, output [5:0] io_out ); wire [7:0] _T = $signed(io_a) * $signed(io_b); // @[FixedPointTypeClass.scala 43:59] wire [6:0] _GEN_0 = {$signed(io_c), 3'h0}; // @[FixedPointTypeClass.scala 21:58] wire [7:0] _GEN_1 = {{1{_GEN_0[6]}},_GEN_0}; // @[FixedPointTypeClass.scala 21:58] wire [7:0] _T_3 = $signed(_T) + $signed(_GEN_1); // @[FixedPointTypeClass.scala 21:58] assign io_out = _T_3[7:2]; // @[cmd27.sc 11:12] endmodule ``` * MAC 基於泛型更有效的寫法 ```scala= object Mac { def apply[T <: Data : Ring](a: T, b: T, c: T): T = { a * b + c } } class MacTestModule extends Module { val io = IO(new Bundle { val uin = Input(UInt(4.W)) val uout = Output(UInt()) val sin = Input(SInt(4.W)) val sout = Output(SInt()) val fin = Input(FixedPoint(16.W, 12.BP)) val fout = Output(FixedPoint()) }) // for each IO pair, do out = in * in + in io.uout := Mac(io.uin, io.uin, io.uin) io.sout := Mac(io.sin, io.sin, io.sin) io.fout := Mac(io.fin, io.fin, io.fin) } println(getVerilog(new MacTestModule)) ``` ```mips module MacTestModule( input clock, input reset, input [3:0] io_uin, output [7:0] io_uout, input [3:0] io_sin, output [7:0] io_sout, input [15:0] io_fin, output [31:0] io_fout ); wire [7:0] _T = io_uin * io_uin; // @[UIntTypeClass.scala 40:41] wire [7:0] _GEN_0 = {{4'd0}, io_uin}; // @[UIntTypeClass.scala 19:40] wire [7:0] _T_3 = $signed(io_sin) * $signed(io_sin); // @[SIntTypeClass.scala 45:41] wire [7:0] _GEN_1 = {{4{io_sin[3]}},io_sin}; // @[SIntTypeClass.scala 19:40] wire [31:0] _T_7 = $signed(io_fin) * $signed(io_fin); // @[FixedPointTypeClass.scala 43:59] wire [27:0] _GEN_2 = {$signed(io_fin), 12'h0}; // @[FixedPointTypeClass.scala 21:58] wire [31:0] _GEN_3 = {{4{_GEN_2[27]}},_GEN_2}; // @[FixedPointTypeClass.scala 21:58] assign io_uout = _T + _GEN_0; // @[UIntTypeClass.scala 19:40] assign io_sout = $signed(_T_3) + $signed(_GEN_1); // @[SIntTypeClass.scala 19:40] assign io_fout = $signed(_T_7) + $signed(_GEN_3); // @[FixedPointTypeClass.scala 21:58] endmodule ``` --- # 類型匹配 ## 泛型 * Chisel 類型通常不應進行值匹配,這邊只是介紹而已 * 使用 Data 類型,這樣丟 UInt 或是 SInt 都合法 * 但完全不建議這樣撰寫 * 在 Scala 中編寫類型泛型產生器有更好、更安全的方法 ```scala= class ConstantSum(in1: Data, in2: Data) extends Module { val io = IO(new Bundle { val out = Output(chiselTypeOf(in1)) // in case in1 is literal then just get its type }) (in1, in2) match { case (x: UInt, y: UInt) => io.out := x + y case (x: SInt, y: SInt) => io.out := x + y case _ => throw new Exception("I give up!") } } println(getVerilog(dut = new ConstantSum(3.U, 4.U))) println(getVerilog(dut = new ConstantSum(-3.S, 4.S))) println(getVerilog(dut = new ConstantSum(3.U, 4.S))) ``` ```mips module ConstantSum( input clock, input reset, output [1:0] io_out ); assign io_out = 2'h3; // @[cmd18.sc 6:43] endmodule module ConstantSum( input clock, input reset, output [2:0] io_out ); assign io_out = 3'sh1; // @[cmd18.sc 7:43] endmodule ``` ## unapply * Scala 的語法糖 * 使匹配語句能夠匹配類型並在匹配期間從這些類型中提取值 * 有許多撰寫方式,下面語句並不一定是等效的,但他們都合法 ```scala case class SomeGeneratorParameters( someWidth: Int, someOtherWidth: Int = 10, pipelineMe: Boolean = false ) { require(someWidth >= 0) require(someOtherWidth >= 0) val totalWidth = someWidth + someOtherWidth } def delay(p: SomeGeneratorParameters): Int = p match { // 下面提供的方法就是寫在這個 block 內 case SomeGeneratorParameters(_, sw, false) => sw * 2 case sg @SomeGeneratorParameters(_, _, true) => sg.totalWidth * 3 } // 法 1 case p: SomeGeneratorParameters => p.sw * 2 // 法 2 case SomeGeneratorParameters(_, sw, _) => sw * 2 // 法 3 case SomeGeneratorParameters(_, sw, true) => sw // 法 4 case sg @SomeGeneratorParameters(_, sw, true) => sw // 法 5 case s @SomeGeneratorParameters(_, sw, false) => s.sw * 2 ``` ## Partial Functions * 是指僅在其輸入的子集上定義的函數 * Partial Functions 可以用```orElse```連結在一起 * 使用未定義的輸入呼叫```PartialFunction```將導致運行時錯誤 * 例如,如果輸入是使用者定義的```PartialFunction```,則可能會發生這種情況 ```scala= // Helper function to make this cell a bit less tedious. def printAndAssert(cmd: String, result: Boolean, expected: Boolean): Unit = { println(s"$cmd = $result") assert(result == expected) } // Defined for -1, 2, 5, etc. val partialFunc1: PartialFunction[Int, String] = { case i if (i + 1) % 3 == 0 => "Something" } printAndAssert("partialFunc1.isDefinedAt(2)", partialFunc1.isDefinedAt(2), true) printAndAssert("partialFunc1.isDefinedAt(5)", partialFunc1.isDefinedAt(5), true) printAndAssert("partialFunc1.isDefinedAt(1)", partialFunc1.isDefinedAt(1), false) printAndAssert("partialFunc1.isDefinedAt(0)", partialFunc1.isDefinedAt(0), false) println(s"partialFunc1(2) = ${partialFunc1(2)}") try { println(partialFunc1(0)) } catch { case e: scala.MatchError => println("partialFunc1(0) = can't apply PartialFunctions where they are not defined") } // Defined for 1, 4, 7, etc. val partialFunc2: PartialFunction[Int, String] = { case i if (i + 2) % 3 == 0 => "Something else" } printAndAssert("partialFunc2.isDefinedAt(1)", partialFunc2.isDefinedAt(1), true) printAndAssert("partialFunc2.isDefinedAt(0)", partialFunc2.isDefinedAt(0), false) println(s"partialFunc2(1) = ${partialFunc2(1)}") try { println(partialFunc2(0)) } catch { case e: scala.MatchError => println("partialFunc2(0) = can't apply PartialFunctions where they are not defined") } val partialFunc3 = partialFunc1 orElse partialFunc2 printAndAssert("partialFunc3.isDefinedAt(0)", partialFunc3.isDefinedAt(0), false) printAndAssert("partialFunc3.isDefinedAt(1)", partialFunc3.isDefinedAt(1), true) printAndAssert("partialFunc3.isDefinedAt(2)", partialFunc3.isDefinedAt(2), true) printAndAssert("partialFunc3.isDefinedAt(3)", partialFunc3.isDefinedAt(3), false) println(s"partialFunc3(1) = ${partialFunc3(1)}") println(s"partialFunc3(2) = ${partialFunc3(2)}") ``` ```shell partialFunc1.isDefinedAt(2) = true partialFunc1.isDefinedAt(5) = true partialFunc1.isDefinedAt(1) = false partialFunc1.isDefinedAt(0) = false partialFunc1(2) = Something partialFunc1(0) = can't apply PartialFunctions where they are not defined partialFunc2.isDefinedAt(1) = true partialFunc2.isDefinedAt(0) = false partialFunc2(1) = Something else partialFunc2(0) = can't apply PartialFunctions where they are not defined partialFunc3.isDefinedAt(0) = false partialFunc3.isDefinedAt(1) = true partialFunc3.isDefinedAt(2) = true partialFunc3.isDefinedAt(3) = false partialFunc3(1) = Something else partialFunc3(2) = Something ``` --- # 範例 ## 範例 1: 累加器 * 早期 scala 和 chisel 的限制(通常當 bundle 有多個參數時)chisel 無法弄清楚如何實現複製,開發人員必須手動實現 clonetype * chisel 的最新發展幾乎消除了實現 cloneType 的需要 * 這個範例不難,可以跳過 ```scala= class Integrator[T <: Data : Ring](genIn: T, genReg: T) extends Module { val io = IO(new Bundle { val in = Input(genIn) val out = Output(genReg) }) val reg = RegInit(genReg, Ring[T].zero) // init to zero reg := reg + io.in io.out := reg } ``` ```mips= module Integrator( input clock, input reset, input [3:0] io_in, output [7:0] io_out ); `ifdef RANDOMIZE_REG_INIT reg [31:0] _RAND_0; `endif // RANDOMIZE_REG_INIT reg [7:0] reg_; // @[cmd45.sc 7:22] wire [7:0] _GEN_0 = {{4{io_in[3]}},io_in}; // @[SIntTypeClass.scala 19:40] wire [7:0] _T_2 = $signed(reg_) + $signed(_GEN_0); // @[SIntTypeClass.scala 19:40] assign io_out = reg_; // @[cmd45.sc 9:12] always @(posedge clock) begin if (reset) begin // @[cmd45.sc 7:22] reg_ <= 8'sh0; // @[cmd45.sc 7:22] end else begin reg_ <= _T_2; // @[cmd45.sc 8:9] end end endmodule ``` ## 範例 2: DspComplex * 基本宣告範例 * DspComplex 是一個型別通用容器。這意味著複數的實部和虛部可以是任何類型 * 只要它們滿足由```T <: Data : Ring```給出的類型約束即可 ```scala= class DspComplex[T <: Data:Ring](val real: T, val imag: T) extends Bundle { ... } ``` * MAC 應用 * 超酷的,它可以自動合成出實部與虛部的運算 ```scala= class Mac[T <: Data : Ring](genIn : T, genOut: T) extends Module { val io = IO(new Bundle { val a = Input(genIn) val b = Input(genIn) val c = Input(genIn) val out = Output(genOut) }) io.out := io.a * io.b + io.c } println(getVerilog(new Mac(DspComplex(SInt(4.W), SInt(4.W)), DspComplex(SInt(6.W), SInt(6.W))) )) ``` ```mips= module Mac( input clock, input reset, input [3:0] io_a_real, input [3:0] io_a_imag, input [3:0] io_b_real, input [3:0] io_b_imag, input [3:0] io_c_real, input [3:0] io_c_imag, output [5:0] io_out_real, output [5:0] io_out_imag ); wire [3:0] _T_2 = $signed(io_b_real) + $signed(io_b_imag); // @[SIntTypeClass.scala 19:40] wire [3:0] _T_5 = $signed(io_a_real) + $signed(io_a_imag); // @[SIntTypeClass.scala 19:40] wire [3:0] _T_8 = $signed(io_a_imag) - $signed(io_a_real); // @[SIntTypeClass.scala 29:50] wire [7:0] _T_9 = $signed(io_a_real) * $signed(_T_2); // @[SIntTypeClass.scala 45:41] wire [7:0] _T_10 = $signed(_T_5) * $signed(io_b_imag); // @[SIntTypeClass.scala 45:41] wire [7:0] _T_11 = $signed(_T_8) * $signed(io_b_real); // @[SIntTypeClass.scala 45:41] wire [7:0] _T_14 = $signed(_T_9) - $signed(_T_10); // @[SIntTypeClass.scala 29:50] wire [7:0] _T_17 = $signed(_T_9) + $signed(_T_11); // @[SIntTypeClass.scala 19:40] wire [7:0] _GEN_0 = {{4{io_c_real[3]}},io_c_real}; // @[SIntTypeClass.scala 19:40] wire [7:0] _T_20 = $signed(_T_14) + $signed(_GEN_0); // @[SIntTypeClass.scala 19:40] wire [7:0] _GEN_1 = {{4{io_c_imag[3]}},io_c_imag}; // @[SIntTypeClass.scala 19:40] wire [7:0] _T_23 = $signed(_T_17) + $signed(_GEN_1); // @[SIntTypeClass.scala 19:40] assign io_out_real = _T_20[5:0]; // @[cmd47.sc 8:12] assign io_out_imag = _T_23[5:0]; // @[cmd47.sc 8:12] endmodule ``` ## 範例 3: Sign-magnitude Numbers * 比上面兩個例子複雜很多,如果對 scala 還不熟悉建議跳過 ### 類型設計 * SignMagnitude: 自己設計的型態,具有 +, -, * 功能 * SignMagnitudeRing: 繼承 SignMagnitude 和 Ring 特性的 * SignMagnitudeRingImpl: 隱式的 SignMagnitudeRing ```scala= // 設計具有 +, -, * 運算的型態 SignMagnitude class SignMagnitude(val magnitudeWidth: Option[Int] = None) extends Bundle { // 就像 python class 的 self.sign 和 self.magnitude 的宣告 val sign = Bool() val magnitude = magnitudeWidth match { case Some(w) => UInt(w.W) case None => UInt() } /* 定義型態提供的運算 語法的意義類似 python 的 def +(self): 所以 this. 類似於 python 的 self. 來自輸入的則是 that. */ def +(that: SignMagnitude): SignMagnitude = { val result = Wire(new SignMagnitude()) val signsTheSame = this.sign === that.sign when (signsTheSame) { result.sign := this.sign result.magnitude := this.magnitude + that.magnitude } .otherwise { when (this.magnitude > that.magnitude) { result.sign := this.sign result.magnitude := this.magnitude - that.magnitude } .otherwise { result.sign := that.sign result.magnitude := that.magnitude - this.magnitude } } result } /* 將輸入取負丟入 +() 就解決了 */ def -(that: SignMagnitude): SignMagnitude = { this.+(-that) } def unary_-(): SignMagnitude = { val result = Wire(new SignMagnitude()) result.sign := !this.sign result.magnitude := this.magnitude result } def *(that: SignMagnitude): SignMagnitude = { val result = Wire(new SignMagnitude()) result.sign := this.sign ^ that.sign result.magnitude := this.magnitude * that.magnitude result } } // 從 SignMagnitude 和 Ring 繼承 trait SignMagnitudeRing extends Ring[SignMagnitude] { /* 因為 a + b 已經被定義在 SignMagnitude 所以可以使用 f + g */ def plus(f: SignMagnitude, g: SignMagnitude): SignMagnitude = { f + g } def times(f: SignMagnitude, g: SignMagnitude): SignMagnitude = { f * g } def one: SignMagnitude = { val one = Wire(new SignMagnitude(Some(1))) one.sign := false.B one.magnitude := 1.U one } def zero: SignMagnitude = { val zero = Wire(new SignMagnitude(Some(0))) zero.sign := false.B zero.magnitude := 0.U zero } def negate(f: SignMagnitude): SignMagnitude = { -f } // Leave unimplemented for this example // bootcamp 作者留給讀者的例題,沒有答案喔~ // 然後我也不知道這四個函式想幹嘛,所以就留在這邊了 def minusContext(f: SignMagnitude, g: SignMagnitude): SignMagnitude = ??? def negateContext(f: SignMagnitude): SignMagnitude = ??? def plusContext(f: SignMagnitude,g: SignMagnitude): SignMagnitude = ??? def timesContext(f: SignMagnitude,g: SignMagnitude): SignMagnitude = ??? } // 宣告隱式物件 SignMagnitudeRingImpl implicit object SignMagnitudeRingImpl extends SignMagnitudeRing ``` ### 輸入型態為 SignMagnitude 的 MAC ```scala= class Mac[T <: Data : Ring](genIn : T, genOut: T) extends Module { val io = IO(new Bundle { val a = Input(genIn) val b = Input(genIn) val c = Input(genIn) val out = Output(genOut) }) io.out := io.a * io.b + io.c } println(getVerilog(new Mac(new SignMagnitude(Some(4)), new SignMagnitude(Some(5))))) ``` ```mips= module Mac( input clock, input reset, input io_a_sign, input [3:0] io_a_magnitude, input io_b_sign, input [3:0] io_b_magnitude, input io_c_sign, input [3:0] io_c_magnitude, output io_out_sign, output [4:0] io_out_magnitude ); wire _T = io_a_sign ^ io_b_sign; // @[cmd48.sc 35:34] wire [7:0] _T_1 = io_a_magnitude * io_b_magnitude; // @[cmd48.sc 36:44] wire _T_2 = _T == io_c_sign; // @[cmd48.sc 9:38] wire [7:0] _GEN_4 = {{4'd0}, io_c_magnitude}; // @[cmd48.sc 12:48] wire [7:0] _T_4 = _T_1 + _GEN_4; // @[cmd48.sc 12:48] wire [7:0] _T_7 = _T_1 - _GEN_4; // @[cmd48.sc 16:52] wire [7:0] _T_9 = _GEN_4 - _T_1; // @[cmd48.sc 19:52] wire _GEN_0 = _T_1 > _GEN_4 ? _T : io_c_sign; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [7:0] _GEN_1 = _T_1 > _GEN_4 ? _T_7 : _T_9; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire [7:0] _GEN_3 = _T_2 ? _T_4 : _GEN_1; // @[cmd48.sc 10:29 cmd48.sc 12:30] assign io_out_sign = _T_2 ? _T : _GEN_0; // @[cmd48.sc 10:29 cmd48.sc 11:30] assign io_out_magnitude = _GEN_3[4:0]; // @[cmd54.sc 8:12] endmodule ``` ### 輸入型態為 DspComplex(new SignMagnitude(Some(4)), new SignMagnitude(Some(4))) 的 MAC ```scala= class Mac[T <: Data : Ring](genIn : T, genOut: T) extends Module { val io = IO(new Bundle { val a = Input(genIn) val b = Input(genIn) val c = Input(genIn) val out = Output(genOut) }) io.out := io.a * io.b + io.c } println(getVerilog(new Mac(DspComplex(new SignMagnitude(Some(4)), new SignMagnitude(Some(4))), DspComplex(new SignMagnitude(Some(5)), new SignMagnitude(Some(5)))))) ``` ```mips= module Mac( input clock, input reset, input io_a_real_sign, input [3:0] io_a_real_magnitude, input io_a_imag_sign, input [3:0] io_a_imag_magnitude, input io_b_real_sign, input [3:0] io_b_real_magnitude, input io_b_imag_sign, input [3:0] io_b_imag_magnitude, input io_c_real_sign, input [3:0] io_c_real_magnitude, input io_c_imag_sign, input [3:0] io_c_imag_magnitude, output io_out_real_sign, output [4:0] io_out_real_magnitude, output io_out_imag_sign, output [4:0] io_out_imag_magnitude ); wire _T = io_b_real_sign == io_b_imag_sign; // @[cmd48.sc 9:38] wire [3:0] _T_2 = io_b_real_magnitude + io_b_imag_magnitude; // @[cmd48.sc 12:48] wire [3:0] _T_5 = io_b_real_magnitude - io_b_imag_magnitude; // @[cmd48.sc 16:52] wire [3:0] _T_7 = io_b_imag_magnitude - io_b_real_magnitude; // @[cmd48.sc 19:52] wire _GEN_0 = io_b_real_magnitude > io_b_imag_magnitude ? io_b_real_sign : io_b_imag_sign; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [3:0] _GEN_1 = io_b_real_magnitude > io_b_imag_magnitude ? _T_5 : _T_7; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire _GEN_2 = _T ? io_b_real_sign : _GEN_0; // @[cmd48.sc 10:29 cmd48.sc 11:30] wire [3:0] _GEN_3 = _T ? _T_2 : _GEN_1; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_8 = io_a_real_sign == io_a_imag_sign; // @[cmd48.sc 9:38] wire [3:0] _T_10 = io_a_real_magnitude + io_a_imag_magnitude; // @[cmd48.sc 12:48] wire [3:0] _T_13 = io_a_real_magnitude - io_a_imag_magnitude; // @[cmd48.sc 16:52] wire [3:0] _T_15 = io_a_imag_magnitude - io_a_real_magnitude; // @[cmd48.sc 19:52] wire _GEN_4 = io_a_real_magnitude > io_a_imag_magnitude ? io_a_real_sign : io_a_imag_sign; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [3:0] _GEN_5 = io_a_real_magnitude > io_a_imag_magnitude ? _T_13 : _T_15; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire _GEN_6 = _T_8 ? io_a_real_sign : _GEN_4; // @[cmd48.sc 10:29 cmd48.sc 11:30] wire [3:0] _GEN_7 = _T_8 ? _T_10 : _GEN_5; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_16 = ~io_a_real_sign; // @[cmd48.sc 29:24] wire _T_17 = io_a_imag_sign == _T_16; // @[cmd48.sc 9:38] wire [3:0] _T_19 = io_a_imag_magnitude + io_a_real_magnitude; // @[cmd48.sc 12:48] wire _GEN_8 = io_a_imag_magnitude > io_a_real_magnitude ? io_a_imag_sign : _T_16; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [3:0] _GEN_9 = io_a_imag_magnitude > io_a_real_magnitude ? _T_15 : _T_13; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire _GEN_10 = _T_17 ? io_a_imag_sign : _GEN_8; // @[cmd48.sc 10:29 cmd48.sc 11:30] wire [3:0] _GEN_11 = _T_17 ? _T_19 : _GEN_9; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_25 = io_a_real_sign ^ _GEN_2; // @[cmd48.sc 35:34] wire [7:0] _T_26 = io_a_real_magnitude * _GEN_3; // @[cmd48.sc 36:44] wire _T_27 = _GEN_6 ^ io_b_imag_sign; // @[cmd48.sc 35:34] wire [7:0] _T_28 = _GEN_7 * io_b_imag_magnitude; // @[cmd48.sc 36:44] wire _T_29 = _GEN_10 ^ io_b_real_sign; // @[cmd48.sc 35:34] wire [7:0] _T_30 = _GEN_11 * io_b_real_magnitude; // @[cmd48.sc 36:44] wire _T_31 = ~_T_27; // @[cmd48.sc 29:24] wire _T_32 = _T_25 == _T_31; // @[cmd48.sc 9:38] wire [7:0] _T_34 = _T_26 + _T_28; // @[cmd48.sc 12:48] wire [7:0] _T_37 = _T_26 - _T_28; // @[cmd48.sc 16:52] wire [7:0] _T_39 = _T_28 - _T_26; // @[cmd48.sc 19:52] wire _GEN_12 = _T_26 > _T_28 ? _T_25 : _T_31; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [7:0] _GEN_13 = _T_26 > _T_28 ? _T_37 : _T_39; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire _GEN_14 = _T_32 ? _T_25 : _GEN_12; // @[cmd48.sc 10:29 cmd48.sc 11:30] wire [7:0] _GEN_15 = _T_32 ? _T_34 : _GEN_13; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_40 = _T_25 == _T_29; // @[cmd48.sc 9:38] wire [7:0] _T_42 = _T_26 + _T_30; // @[cmd48.sc 12:48] wire [7:0] _T_45 = _T_26 - _T_30; // @[cmd48.sc 16:52] wire [7:0] _T_47 = _T_30 - _T_26; // @[cmd48.sc 19:52] wire _GEN_16 = _T_26 > _T_30 ? _T_25 : _T_29; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [7:0] _GEN_17 = _T_26 > _T_30 ? _T_45 : _T_47; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire _GEN_18 = _T_40 ? _T_25 : _GEN_16; // @[cmd48.sc 10:29 cmd48.sc 11:30] wire [7:0] _GEN_19 = _T_40 ? _T_42 : _GEN_17; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_48 = _GEN_14 == io_c_real_sign; // @[cmd48.sc 9:38] wire [7:0] _GEN_28 = {{4'd0}, io_c_real_magnitude}; // @[cmd48.sc 12:48] wire [7:0] _T_50 = _GEN_15 + _GEN_28; // @[cmd48.sc 12:48] wire [7:0] _T_53 = _GEN_15 - _GEN_28; // @[cmd48.sc 16:52] wire [7:0] _T_55 = _GEN_28 - _GEN_15; // @[cmd48.sc 19:52] wire _GEN_20 = _GEN_15 > _GEN_28 ? _GEN_14 : io_c_real_sign; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [7:0] _GEN_21 = _GEN_15 > _GEN_28 ? _T_53 : _T_55; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire [7:0] _GEN_23 = _T_48 ? _T_50 : _GEN_21; // @[cmd48.sc 10:29 cmd48.sc 12:30] wire _T_56 = _GEN_18 == io_c_imag_sign; // @[cmd48.sc 9:38] wire [7:0] _GEN_32 = {{4'd0}, io_c_imag_magnitude}; // @[cmd48.sc 12:48] wire [7:0] _T_58 = _GEN_19 + _GEN_32; // @[cmd48.sc 12:48] wire [7:0] _T_61 = _GEN_19 - _GEN_32; // @[cmd48.sc 16:52] wire [7:0] _T_63 = _GEN_32 - _GEN_19; // @[cmd48.sc 19:52] wire _GEN_24 = _GEN_19 > _GEN_32 ? _GEN_18 : io_c_imag_sign; // @[cmd48.sc 14:52 cmd48.sc 15:34 cmd48.sc 18:34] wire [7:0] _GEN_25 = _GEN_19 > _GEN_32 ? _T_61 : _T_63; // @[cmd48.sc 14:52 cmd48.sc 16:34 cmd48.sc 19:34] wire [7:0] _GEN_27 = _T_56 ? _T_58 : _GEN_25; // @[cmd48.sc 10:29 cmd48.sc 12:30] assign io_out_real_sign = _T_48 ? _GEN_14 : _GEN_20; // @[cmd48.sc 10:29 cmd48.sc 11:30] assign io_out_real_magnitude = _GEN_23[4:0]; // @[cmd55.sc 8:12] assign io_out_imag_sign = _T_56 ? _GEN_18 : _GEN_24; // @[cmd48.sc 10:29 cmd48.sc 11:30] assign io_out_imag_magnitude = _GEN_27[4:0]; // @[cmd55.sc 8:12] endmodule ``` --- # 參考 * [Chisel-bootcamp](https://mybinder.org/v2/gh/freechipsproject/chisel-bootcamp/master) * [Ammonite-REPL](https://ammonite.io/) * [What is REPL](https://hackmd.io/@Gandolfreddy/replit_tutorial) * [Fixed-Point User Library for Chisel](https://github.com/ucb-bar/fixedpoint) * [What is Clone in Chisel](https://stackoverflow.com/questions/48105153/what-is-clone-in-chisel)