摘要:最近,在優化程式的加鎖方式時,竟然出現了死鎖!!到底是為什麼呢?!經過仔細的分析之後,終於找到了原因。 本文分享自華為雲社區《【高併發】優化加鎖方式時竟然死鎖了!!》,作者: 冰 河。 寫在前面 最近,在優化程式的加鎖方式時,竟然出現了死鎖!!到底是為什麼呢?!經過仔細的分析之後,終於找到了原 ...
Chisel學習筆記(二)——基本類型
因為是對著chisel book學的,這篇實際上是加上我的理解的chisel book的翻譯
1.信號類型與常量
Chisel提供了三種基本的類型來描述信號、寄存器、組合邏輯:
- Bits
- SInt
- UInt
此外,還定義了邏輯類型Bool。
1.1類型定義
一個Chisel類型的定義有兩部分:位寬與類型
比如看下麵的例子:
Bits(7.W)
SInt(8.W)
UInt(9.W)
分別定義了7bits的Bits類型,8bits的有符號數,9bits的無符號數
1.2 常量定義
而一個Chisel常量類型的定義則有三部分:位寬、類型和值
比如看下麵的例子:
-3.S(4.W)
這個定義代表一個4bits的有符號數3,其中-3、4都是Scala原生的整型,而通過.S、.W轉換為Chisel定義的有符號數與寬度類型
對於上面的例子應該這樣理解,首先4.W將Scala整型4轉換為Chisel width,然後作為參數傳入.S構成Chisel四位有符號數類型,然後把Scala整型-3轉換為Chisel四位有符號數-3。
對於常量的定義,還可以使用其它的進位(16、8、2),這種情況下應該用Scala的字元串類型來表達,如:
"hff".U
"o377".U
"b1111_1111".U
都代表十進位的255
2. 組合邏輯
首先有必要介紹Scala的一個特性——類型推斷,類似C++的auto,對應的關鍵字為val。這個特性使得我們可以不用像Verilog一樣,對每一個變數顯式聲明它的類型、位寬(位寬實際上作為Bits、SInt、UInt類型的一項屬性存在)。
先看一個例子:
val logic = a & b | c
上面的代碼描述了下麵這樣一個電路:
代碼中,logic的類型為val,如前述,這不是一個實際的類型,只是表示logic是一個變數,而logic的變數由Scala推斷得出。
另外,還可以先將一個變數定義為Wire,然後再用一種持續賦值的方法來進行“連接”:
val w = Wire(UInt())
w := a & b
可以通過類似下標訪問的方法來提取某一位或一個區間:
val bit31 = x(31)
val bit0to7 = x(7, 0)
還可以進行拼接:
val word = bits1 ## bits2
下麵兩張表介紹了Chisel中定義的一些硬體運算元:
Operator | Description | Data Types |
---|---|---|
*、/、% | 乘、除、取模 | UInt、SInt |
+、- | 加、減 | UInt、SInt |
===、=/= | 等於、不等於 | UInt、SInt,返回Bool |
>、>=、<、<= | 大於、不小於、小於、不大於 | UInt、SInt,返回Bool |
<<、>> | 左移、右移(UInt邏輯移位、SInt算術移位) | UInt、SInt |
~ | 非 | UInt、SInt、Bool |
&、|、^ | 與、或、非 | UInt、SInt、Bool |
! | 邏輯非 | Bool |
&&、|| | 邏輯與、或 | Bool |
Function | Description | Data Types |
---|---|---|
v.andR、v.orR、v.xorR | AND、OR、XOR reduction | UInt、SInt、returns Bool |
v(n) | 提取1bit | UInt、SInt |
v(end, start) | 提取區間 | UInt、SInt |
Fill(n, v) | 將v複製n遍 | UInt、SInt |
a ## b | 拼接 | UInt、SInt |
Cat(a, b, ...) | 拼接 | UInt、SInt |
Mux(sel, a, b) | 多路選擇器 | sel:Bool,a、b:任何相同的Chisel類 |
3. 寄存器
Chisel提供的寄存器介面是高度抽象和封裝的。寄存器的時鐘被連接到一個全局時鐘,複位被連接到一個全局同步複位,只留下了輸入、輸出兩個介面供使用。雖然自由度變低了,但使用也相對簡化了,實際上只有初始化、連接輸入、連接輸出三件事要做:
val reg = RegInit(0.U(8.W))
reg := d
val q = reg
或者也可以這樣寫:
val reg = RegNext(d, 0.U(8.W))
val q = reg
4. Bundle和Vec
Bundle是異構的類型集合,可以通過繼承Bundle類來定義:
class MyBundle extends Bundle{
val d1 = UInt(8.W)
val d2 = Bool()
}
然後在使用前進行聲明,Bundle內數據的訪問實際上就是對類屬性的訪問:
val mb = Wire(new MyBundle())
mb.d1 := 2.U
mb.d2 := true.B
Vec是同構的類型集合,通過Vec類定義,下標訪問:
val vec = Wire(Vec(3, UInt(4.W)))
vec(0) := 1.U
vec(1) := 2.U
vec(2) := 3.U
val q = vec(0)
還可以通過初始化函數定義,這種方法直接產生Wire,不需要再轉換:
val d = 3.U(4.W)
val vec = VecInit(1.U(4.W), 2.U, d)
Vec預設是一組Wire,但也有RegFile型的:
val rf = Reg(Vec(32, 0.U(32.W)))
val rf = RegInit(VecInit(Seq.fill(32)(0.U(32.W))))
Bundle與Vec可以互相包含:
class VecBundle extends Bundle{
val d1 = UInt(8.W)
val v1 = Vec(3, UInt(4.W))
}
BundleVec = Wire(Vec(3, new VecBundle()))
另外,Bundle與Vec還有一個重要的用途。Chisel3不支持對一個變數部分賦值,比如說這樣寫是會報錯的:
val dat = Wire(UInt(8.W))
dat(7:4) := "h1".U(4.W)
dat(3:0) := "h2".U(4.W)
作為替代,可以用Vec和Bundle實現分段
5. Wire、Reg、IO
上面提到的各種類型支持了多樣的設計,但許多還不能直接對應到硬體結構。Wire、Reg、IO分別對應了連線,寄存器,輸入輸出埠。組合邏輯的信號需要用Wire顯式聲明,Reg與前述是一樣的,IO是輸入輸出埠。
Wire與IO的用法與Reg類似:
val w = Wire(UInt(8.W))
w := 8.U(8.W)
val v = WireDefault(8.U(8.W))