# 【5】物件導向設計 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. Chisel 與物件導向 --- # 隨筆 * Chisel 由 scala 乘載,scala 又建構於 Java 之上,因此兩者都繼承了 Java 的許多物件導向的特性 --- # 物件導向設計 ## Abstract classes * 可以定義子類別必須實現的許多未實現的特徵 * 任何物件只能直接從一個父 abstract class 繼承 ```scala= abstract class MyAbstractClass { def myFunction(i: Int): Int val myValue: String } class ConcreteClass extends MyAbstractClass { def myFunction(i: Int): Int = i + 1 val myValue = "Hello World!" } val concreteClass = new ConcreteClass() ``` ## Traits * Traits 是 Scala 實現多重繼承的方式 * 跟 abstract classes 很像,但有兩個最大的不同之處 * 一個類別可以繼承多個特徵 * 特徵不能有建構函數參數 ```scala= trait HasFunction { def myFunction(i: Int): Int } trait HasValue { val myValue: String val myOtherValue = 100 } class MyClass extends HasFunction with HasValue { override def myFunction(i: Int): Int = i + 1 val myValue = "Hello World!" val myClass = new MyClass() ``` ## Objects * 針對 singleton classes * 無法實例化物件(無需呼叫 new) * 它們類似於 Java 靜態類別 ```scala= object MyObject { def hi: String = "Hello World!" def apply(msg: String) = msg } println(MyObject.hi) println(MyObject("This message is important!")) // equivalent to MyObject.apply(msg) ``` ```shell Hello World! This message is important! ``` ## Companion Objects * 當類別和物件共用相同的名稱並定義在同一檔案中時,該物件稱為伴生物件 * 當 new 在類別/物件名稱之前使用時,它將實例化該類別 * 如果不使用 new,它將引用該物件 * 使用伴生對象通常出於以下原因: * 包含與類別相關的常數 * 在類別構造函數之前/之後執行程式碼 * 為一個類別建立多個建構函數 * Chisel 使用許多伴隨對象,例如 Module。 * ```val myModule = Module(new MyModule)``` * 正在呼叫 Module 伴隨對象,因此 Chisel 可以在實例化之前和之後執行後台程式碼 MyModule。 ```scala= object Lion { def roar(): Unit = println("I'M AN OBJECT!") } class Lion { def roar(): Unit = println("I'M A CLASS!") } new Lion().roar() Lion.roar() ``` ```shell I'M A CLASS! I'M AN OBJECT! ``` ## Case Classes * Case 是一種特殊類型的 Scala 類,在 Scala 程式設計中非常常見 * 允許外部存取類別參數 * new 實例化類別時無需使用 * 自動建立一個unapply 方法,提供對所有類別參數的存取 * 不能派生自 ```scala= class Nail(length: Int) // Regular class val nail = new Nail(10) // Requires the `new` keyword // println(nail.length) // Illegal! Class constructor parameters are not by default externally visible class Screw(val threadSpace: Int) // By using the `val` keyword, threadSpace is now externally visible val screw = new Screw(2) // Requires the `new` keyword println(screw.threadSpace) case class Staple(isClosed: Boolean) // Case class constructor parameters are, by default, externally visible val staple = Staple(false) // No `new` keyword required println(staple.isClosed) ``` --- # Chisel 與物件導向 * 每當想要在 Chisel 中建立硬體物件時,它都需要作為 Module 超類別 * 有點像 verilog call module 的感覺,Chisel 透過物件導向設計和 scala 完成這件事 ## 範例1 異步 FIFO(AsyncFIFO) * 原本以為只要稍微修改就能跑啊,結果很大一個坑阿(呵呵 * 當晚就通靈出來啦!!! (葉配一下,小弟目前碩二積極找工作喔 ### 先建立一個格雷碼計數器 ```scala= import scala.math.pow // create a module class GrayCoder(bitwidth: Int) extends Module { val io = IO(new Bundle{ val in = Input(UInt(bitwidth.W)) val out = Output(UInt(bitwidth.W)) val encode = Input(Bool()) // decode on false }) when (io.encode) { //encode io.out := io.in ^ (io.in >> 1.U) } .otherwise { // decode, much more complicated io.out := Seq.fill(log2Ceil(bitwidth))(Wire(UInt(bitwidth.W))).zipWithIndex.fold((io.in, 0)){ case ((w1: UInt, i1: Int), (w2: UInt, i2: Int)) => { w2 := w1 ^ (w1 >> pow(2, log2Ceil(bitwidth)-i2-1).toInt.U) (w2, i1) } }._1 } } println(getVerilog(new GrayCoder(8))) ``` ```mips= module GrayCoder( input clock, input reset, input [7:0] io_in, output [7:0] io_out, input io_encode ); wire [7:0] _T = {{1'd0}, io_in[7:1]}; // @[cmd9.sc 12:30] wire [7:0] _T_1 = io_in ^ _T; // @[cmd9.sc 12:21] wire [7:0] _T_2 = {{4'd0}, io_in[7:4]}; // @[cmd9.sc 16:24] wire [7:0] _T_3 = io_in ^ _T_2; // @[cmd9.sc 16:18] wire [7:0] _T_4 = {{2'd0}, _T_3[7:2]}; // @[cmd9.sc 16:24] wire [7:0] _T_5 = _T_3 ^ _T_4; // @[cmd9.sc 16:18] wire [7:0] _T_6 = {{1'd0}, _T_5[7:1]}; // @[cmd9.sc 16:24] wire [7:0] _T_7 = _T_5 ^ _T_6; // @[cmd9.sc 16:18] assign io_out = io_encode ? _T_1 : _T_7; // @[cmd9.sc 11:20 cmd9.sc 12:12 cmd9.sc 14:12] endmodule ``` ### 在 AsyncFIFO 內 call 格雷碼計數器 :::info 我沒有測試過,因此不確定 AsyncFIFO 的功能是否正確,或是是否有邏輯錯誤。因此如果有人有寫 chisel 的測試,希望能將您撰寫的測試分享到網路上,並將網址提供給我,我會在本篇開頭提及您的貢獻並加入參考內容中 ::: * chisel 獲得某個硬體特定位元的方法(後來發現不會用到,但還是提一下) * verilog: ```io_in[7:1]``` * chisel: ```io_in(7,1)``` * 同理,如過要併線就用```cat()```: * ```val test = Cat(io_in(7,6), io_in(2,1))``` * chisel 中 call Module 的方式 * 要記得包在 Module() 裡面 * 像 bootcamp 範例那樣會跑不出來 ```scala val decoder = Module(new GrayCoder(read_counter.getWidth)) ``` * 異步信號設計 * 好的設計都會對非同步訊號進行同步處理,同步一般採用多層 D 觸發器級聯處理。 * 這種模型大部分資料都說的是第一級暫存器產生亞穩態後,第二級暫存器穩定輸出機率為90%,第三級暫存器穩定輸出的機率為99%,如果亞穩態跟隨電路一直傳承下去,那就會令自我修護能力較弱的系統直接崩潰 * Output 的值要嚴格宣告,因此 41 到45 行才要遍歷```io.empty```和```io.full```的輸出情況 * 不可以只寫 41 而不寫 42 遍歷的 otherwise * Scala map 中下劃線 _._2 的意義? * map(_._n): 表示任意 tuple 物件,後面的數字 n 表示取第幾個數 * 例如: ```val p = List((“hello”,35,1.50),(“nihao”,36,1.78))``` * ```p.map(_._2)```的輸出為```List(35, 36)``` * 格雷碼 * 什麼是格雷碼? * 編碼的方式定義為每個鄰近數字都只相差一個位元,因此也稱為最小差異碼 * 可以使裝置做數字步進時只更動最少的位元數以提高穩定性 * 為們麼使用格雷碼? * 避免訊號傳送錯誤的,因為格雷碼遞增時一次只會改變一個位元 * 為們麼使用移位暫存器? * 異步信號設計那邊有提到,是為了提升暫存器穩定輸出的機率,避免系統崩潰 * 使用 gray 碼進行對比,如何判斷「空」與「滿」 * 對於「空」的判斷: 二者完全相等 * 對於「滿」的判斷: 1. write_gray_counter 和同步過來的 read_gray_counter 的 MSB 不相等 2. write_gray_counter 和 read_gray_counter 的次高位不相等 3. 剩下的其餘位元完全相等 * 但因為我們有用 GrayCoder 解碼,引此空的判斷只要是```write_counter == read_counter - 1.U``` 就好 * ```ShiftRegister```移位暫存器 * 因為前面沒提到這裡解釋一下 * 移位暫存器是順序連接的一組觸發器,每個暫存器(觸發器)的輸出都作為下一個暫存器的輸入 * 講是講用了四個暫存器,但其實現實中等價於一個移位暫存器(註解中有提到 verilog 怎麼去撰寫) * ```ShiftRegister(欲傳送的值, 延遲拍數)``` ```scala= import chisel3.experimental.{withClock} class AsyncFIFO(depth: Int = 16) extends Module { val io = IO(new Bundle{ // write inputs val write_clock = Input(Clock()) val write_enable = Input(Bool()) val write_data = Input(UInt(32.W)) // read inputs/outputs val read_clock = Input(Clock()) val read_enable = Input(Bool()) val read_data = Output(UInt(32.W)) // FIFO status val full = Output(Bool()) val empty = Output(Bool()) }) // reg init val FIFO_reg = RegInit(VecInit(Seq.fill(depth)(0.U(32.W)))) // add extra bit to counter to check for fully/empty status assert(isPow2(depth), "AsyncFIFO needs a power-of-two depth!") val write_counter = withClock(io.write_clock) { Counter(io.write_enable && !io.full, depth)._1 } val read_counter = withClock(io.read_clock) { Counter(io.read_enable && !io.empty, depth)._1 } // encode val encoder = Module(new GrayCoder(write_counter.getWidth)) encoder.io.in := write_counter encoder.io.encode := true.B // synchronize val sync = withClock(io.read_clock) { ShiftRegister(encoder.io.out, 2) } // decode val decoder = Module(new GrayCoder(read_counter.getWidth)) decoder.io.in := sync decoder.io.encode := false.B when( decoder.io.out === read_counter ){ io.empty := true.B } .otherwise{ io.empty := false.B } when( decoder.io.out === read_counter - 1.U ){ io.full := true.B } .otherwise{ io.full := false.B } // write when( io.write_enable && !io.full ){ FIFO_reg(write_counter) := io.write_data } // read when( io.read_enable && !io.empty ){ io.read_data := FIFO_reg(read_counter) } .otherwise{ io.read_data := 0.U } } ``` ```mips= module GrayCoder( input [3:0] io_in, output [3:0] io_out, input io_encode ); wire [3:0] _T = {{1'd0}, io_in[3:1]}; // @[cmd86.sc 11:30] wire [3:0] _T_1 = io_in ^ _T; // @[cmd86.sc 11:21] wire [3:0] _T_2 = {{2'd0}, io_in[3:2]}; // @[cmd86.sc 15:24] wire [3:0] _T_3 = io_in ^ _T_2; // @[cmd86.sc 15:18] wire [3:0] _T_4 = {{1'd0}, _T_3[3:1]}; // @[cmd86.sc 15:24] wire [3:0] _T_5 = _T_3 ^ _T_4; // @[cmd86.sc 15:18] assign io_out = io_encode ? _T_1 : _T_5; // @[cmd86.sc 10:20 cmd86.sc 11:12 cmd86.sc 13:12] endmodule module AsyncFIFO( input clock, input reset, input io_write_clock, input io_write_enable, input [31:0] io_write_data, input io_read_clock, input io_read_enable, output [31:0] io_read_data, output io_full, output io_empty ); `ifdef RANDOMIZE_REG_INIT reg [31:0] _RAND_0; reg [31:0] _RAND_1; reg [31:0] _RAND_2; reg [31:0] _RAND_3; reg [31:0] _RAND_4; reg [31:0] _RAND_5; reg [31:0] _RAND_6; reg [31:0] _RAND_7; reg [31:0] _RAND_8; reg [31:0] _RAND_9; reg [31:0] _RAND_10; reg [31:0] _RAND_11; reg [31:0] _RAND_12; reg [31:0] _RAND_13; reg [31:0] _RAND_14; reg [31:0] _RAND_15; reg [31:0] _RAND_16; reg [31:0] _RAND_17; reg [31:0] _RAND_18; reg [31:0] _RAND_19; `endif // RANDOMIZE_REG_INIT // 預先宣告 call GrayCoder 模組的線 wire [3:0] encoder_io_in; // @[cmd94.sc 31:25] wire [3:0] encoder_io_out; // @[cmd94.sc 31:25] wire encoder_io_encode; // @[cmd94.sc 31:25] wire [3:0] decoder_io_in; // @[cmd94.sc 39:25] wire [3:0] decoder_io_out; // @[cmd94.sc 39:25] wire decoder_io_encode; // @[cmd94.sc 39:25] // 暫存器宣告 reg [31:0] FIFO_reg_0; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_1; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_2; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_3; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_4; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_5; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_6; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_7; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_8; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_9; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_10; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_11; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_12; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_13; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_14; // @[cmd94.sc 21:27] reg [31:0] FIFO_reg_15; // @[cmd94.sc 21:27] // write_counter 和 read_counter 用來計數的 reg 宣告 // 可以不用 if 就完成計數器循環的功能,用的是 verilog 的一個技巧 // 因為宣告位寬為 4 的時候,在數到 16 時就會因為溢位回到 1 wire _T_1 = io_write_enable & ~io_full; // @[cmd96.sc 25:77] reg [3:0] write_counter; // @[Counter.scala 60:40] wire [3:0] _wrap_value_T_1 = write_counter + 4'h1; // @[Counter.scala 76:24] wire _T_3 = io_read_enable & ~io_empty; // @[cmd96.sc 26:74] reg [3:0] read_counter; // @[Counter.scala 60:40] wire [3:0] _wrap_value_T_3 = read_counter + 4'h1; // @[Counter.scala 76:24] reg [3:0] r; // @[Reg.scala 15:16] reg [3:0] sync; // @[Reg.scala 15:16] wire [3:0] _T_6 = read_counter - 4'h1; // @[cmd96.sc 48:43] // 決定 reg 動作,這裡通常會用 case 寫 wire [31:0] _GEN_41 = 4'h1 == read_counter ? FIFO_reg_1 : FIFO_reg_0; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_42 = 4'h2 == read_counter ? FIFO_reg_2 : _GEN_41; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_43 = 4'h3 == read_counter ? FIFO_reg_3 : _GEN_42; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_44 = 4'h4 == read_counter ? FIFO_reg_4 : _GEN_43; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_45 = 4'h5 == read_counter ? FIFO_reg_5 : _GEN_44; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_46 = 4'h6 == read_counter ? FIFO_reg_6 : _GEN_45; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_47 = 4'h7 == read_counter ? FIFO_reg_7 : _GEN_46; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_48 = 4'h8 == read_counter ? FIFO_reg_8 : _GEN_47; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_49 = 4'h9 == read_counter ? FIFO_reg_9 : _GEN_48; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_50 = 4'ha == read_counter ? FIFO_reg_10 : _GEN_49; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_51 = 4'hb == read_counter ? FIFO_reg_11 : _GEN_50; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_52 = 4'hc == read_counter ? FIFO_reg_12 : _GEN_51; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_53 = 4'hd == read_counter ? FIFO_reg_13 : _GEN_52; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_54 = 4'he == read_counter ? FIFO_reg_14 : _GEN_53; // @[cmd94.sc 64:22 cmd94.sc 64:22] wire [31:0] _GEN_55 = 4'hf == read_counter ? FIFO_reg_15 : _GEN_54; // @[cmd94.sc 64:22 cmd94.sc 64:22] // call Module GrayCoder encoder ( // @[cmd94.sc 31:25] .io_in(encoder_io_in), .io_out(encoder_io_out), .io_encode(encoder_io_encode) ); GrayCoder decoder ( // @[cmd94.sc 39:25] .io_in(decoder_io_in), .io_out(decoder_io_out), .io_encode(decoder_io_encode) ); // 41 和 44 行控制邏輯用的線 assign io_read_data = _T_3 ? _GEN_55 : 32'h0; // @[cmd96.sc 61:40 cmd96.sc 62:22 cmd96.sc 65:22] assign io_full = decoder_io_out == _T_6; // @[cmd96.sc 48:26] assign io_empty = decoder_io_out == read_counter; // @[cmd96.sc 41:26] assign encoder_io_in = write_counter; // @[cmd96.sc 30:19] assign encoder_io_encode = 1'h1; // @[cmd96.sc 31:23] assign decoder_io_in = sync; // @[cmd96.sc 38:19] assign decoder_io_encode = 1'h0; // @[cmd96.sc 39:23] // FIFO 的 reg,透過 _T_1 和 write_counter 控制 // _T_1 = io_write_enable & ~io_full // write_counter <= _wrap_value_T_1 (寫在下面的 always 中) always @(posedge clock) begin if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_0 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h0 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_0 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_1 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h1 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_1 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_2 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h2 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_2 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_3 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h3 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_3 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_4 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h4 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_4 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_5 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h5 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_5 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_6 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h6 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_6 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_7 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h7 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_7 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_8 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h8 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_8 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_9 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'h9 == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_9 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_10 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'ha == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_10 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_11 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'hb == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_11 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_12 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'hc == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_12 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_13 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'hd == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_13 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_14 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'he == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_14 <= io_write_data; // @[cmd94.sc 59:33] end end if (reset) begin // @[cmd94.sc 21:27] FIFO_reg_15 <= 32'h0; // @[cmd94.sc 21:27] end else if (_T_1) begin // @[cmd94.sc 58:40] if (4'hf == write_counter) begin // @[cmd94.sc 59:33] FIFO_reg_15 <= io_write_data; // @[cmd94.sc 59:33] end end end always @(posedge io_write_clock) begin if (reset) begin // @[Counter.scala 60:40] write_counter <= 4'h0; // @[Counter.scala 60:40] end else if (_T_1) begin // @[Counter.scala 118:17] write_counter <= _wrap_value_T_1; // @[Counter.scala 76:15] end end always @(posedge io_read_clock) begin if (reset) begin // @[Counter.scala 60:40] read_counter <= 4'h0; // @[Counter.scala 60:40] end else if (_T_3) begin // @[Counter.scala 118:17] read_counter <= _wrap_value_T_3; // @[Counter.scala 76:15] end // 移位暫存器在 verilog 的實做,反正就傳兩次就好啦 r <= encoder_io_out; // @[Reg.scala 16:19 Reg.scala 16:23 Reg.scala 15:16] sync <= r; // @[Reg.scala 16:19 Reg.scala 16:23 Reg.scala 15:16] end endmodule ``` * 可以將 AsyncFIFO 視覺化後和[參考](https://www.cnblogs.com/streetlive/p/12872619.html)的架構圖做比較 ```scala visualize(() => new AsyncFIFO()) ``` --- # 參考 * [Chisel-bootcamp](https://mybinder.org/v2/gh/freechipsproject/chisel-bootcamp/master) * [格雷碼](https://zh.wikipedia.org/zh-tw/%E6%A0%BC%E9%9B%B7%E7%A0%81) * [chisel 移位暫存器](https://blog.csdn.net/weixin_43681766/article/details/125956135) * [Scala map 中下劃線 _._1 的意義](https://blog.csdn.net/happyfreeangel/article/details/84324716) * [AsyncFIFO](https://www.cnblogs.com/streetlive/p/12872619.html)