# 【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)