chisel-tutorial代碼學習 - 03
- 1. 寄存器的定義
- 2. chisel的多種寄存器聲明
-
- 2.1 `Reg`
- 2.2 `RegNext`
-
- 2.2.1 `def apply[T <: Data](next: T)`
- 2.2.2 `def apply[T <: Data](next: T, init: T)`
- 2.3 `RegInit`
-
- 2.3.1 `def apply[T <: Data](init: T)`
- 2.3.2 `def apply[T <: Data](t: T, init: T)`
- 2.4 `RegEnable`
-
- 2.4.1 `def apply[T <: Data](next: T, enable: Bool)`
- 2.4.2 `def apply[T <: Data](next: T, init: T, enable: Bool)`
- 2.5 `ShiftRegister`
-
- 2.5.1 `def apply[T <: Data](in: T, n: Int, en: Bool = true.B)`
- 2.5.2 `def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool)`
- 使用寄存器實作一個累加器Accumulator
1. 寄存器的定義
在chisel中,定義一個寄存器變量,就會綜合成一個上升沿觸發的寄存器。
回憶第1篇筆記中,聲明了2個寄存器
x
和
y
,并在
when
語句塊中對這兩個寄存器進行指派,
val x = Reg(UInt())
val y = Reg(UInt())
when (io.load) {
x := io.a; y := io.b
} .otherwise {
when (x > y) {
x := x - y
} .elsewhen (x <= y) {
y := y - x
}
}
2. chisel的多種寄存器聲明
可以參考大佬寫的文章,第十八章 Chisel基礎——子產品與硬體類型
2.1 Reg
Reg
使用
Reg
可以定義一個無條件更新的寄存器,
class RegTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val x = Reg(UInt(1.W))
x := io.in.orR()
io.out := x
}
上述代碼生成的verilog如下,我這裡手動删除了生成的verilog代碼中宏定義無法綜合的部分,
module RegTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg x; // @[RegTest.scala 12:14]
assign io_out = {{3'd0}, x}; // @[RegTest.scala 17:10]
always @(posedge clock) begin
x <= |io_in;
end
endmodule
如果想給
Reg
增加條件更新的功能,那就在
when
代碼塊裡更新
Reg
值,代碼如下,
package Test
import chisel3._
class RegTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val load = Input(Bool())
val out = Output(UInt(4.W))
})
val x = Reg(UInt(1.W))
when (io.load) {
x := io.in.orR()
}
io.out := x
}
生成的verilog代碼如下,就是在
always
塊中增加了條件指派語句,實作寄存器的條件更新,
module RegTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg x; // @[RegTest.scala 12:14]
wire _T = |io_in; // @[RegTest.scala 14:19]
assign io_out = {{3'd0}, x}; // @[RegTest.scala 17:10]
always @(posedge clock) begin
if (io_load) begin
x <= _T;
end
end
endmodule
2.2 RegNext
RegNext
RegNext
與
Reg
十分類似,使用
Reg
時要先定義這個寄存器,然後再給寄存器指派,而如果使用
RegNext
,直接把被寄存的值放在
RegNext
内即可,那麼
RegNext
的輸出就是輸入延遲1拍。
RegNext
有2個
apply
方法。
2.2.1 def apply[T <: Data](next: T)
def apply[T <: Data](next: T)
這個
apply
方法隻接收1個參數,功能和
Reg
是一樣的,不帶有複位功能,代碼如下,
package Test
import chisel3._
class RegNextTest extends Module {
val io = IO(new Bundle() {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val x = RegNext(io.in)
io.out := x
}
上述代碼實作的功能就是将輸入打一拍後輸出。編譯産生的verilog如下,
module RegNextTest(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out
);
reg [3:0] x; // @[RegNextTest.scala 11:18]
assign io_out = x; // @[RegNextTest.scala 12:10]
always @(posedge clock) begin
x <= io_in;
end
endmodule
2.2.2 def apply[T <: Data](next: T, init: T)
def apply[T <: Data](next: T, init: T)
RegNext
的第二種
apply
方法,除了接收一個被寄存的值之外,還接收一個寄存器複位值,例如,
package Test
import chisel3._
class RegNextTest extends Module {
val io = IO(new Bundle() {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val x = RegNext(io.in, 5.U)
io.out := x
}
和剛才的代碼唯一的不同就是,我給
x
這個寄存器設定了複位值為5。編譯産生的verilog如下,
module RegNextTest(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out
);
reg [3:0] x; // @[RegNextTest.scala 11:18]
assign io_out = x; // @[RegNextTest.scala 12:10]
always @(posedge clock) begin
if (reset) begin
x <= 4'h5;
end else begin
x <= io_in;
end
end
endmodule
從生成的verilog看到,寄存器
x
增加了複位邏輯,高電平複位,複位值為5。
2.3 RegInit
RegInit
RegInit
就是定義寄存器時,給該寄存器設定複位值。
RegInit
有2個
apply
方法,一個隻接收寄存器複位值,另一個除了接收寄存器複位值之外還接收寄存器要儲存的變量。
2.3.1 def apply[T <: Data](init: T)
def apply[T <: Data](init: T)
第1種
apply
方法,代碼如下,
package Test
import chisel3._
class RegInitTest extends Module {
val io = IO(new Bundle() {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val x = RegInit(5.U)
x := io.in
io.out := x
}
編譯生成的verilog如下,注意和
RegNext(io.in, 5.U)
生成的verilog是一樣的,
module RegInitTest(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out
);
reg [3:0] x; // @[RegInitTest.scala 11:18]
assign io_out = x; // @[RegInitTest.scala 13:10]
always @(posedge clock) begin
if (reset) begin
x <= 4'h5;
end else begin
x <= io_in;
end
end
endmodule
2.3.2 def apply[T <: Data](t: T, init: T)
def apply[T <: Data](t: T, init: T)
這種
apply
方法為
RegNext
指定了要存儲的資料類型,例如,
package Test
import chisel3._
class RegInitTest extends Module {
val io = IO(new Bundle() {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val x = RegInit(chiselTypeOf(io.in), 5.U)
x := io.in
io.out := x
}
生成的verilog與第一個
apply
方法生成的verilog完全一樣,這個
apply
方法可以
chisel
層面保證寄存器存儲的資料類型不出錯。
module RegInitTest(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out
);
reg [3:0] x; // @[RegInitTest.scala 11:18]
assign io_out = x; // @[RegInitTest.scala 13:10]
always @(posedge clock) begin
if (reset) begin
x <= 4'h5;
end else begin
x <= io_in;
end
end
endmodule
2.4 RegEnable
RegEnable
RegEnable
定義的寄存器帶有條件更新的功能,使用
RegEnable
需要引入
chisel.util
包。有2個
apply
方法,其中一個可以指定寄存器複位值。
2.4.1 def apply[T <: Data](next: T, enable: Bool)
def apply[T <: Data](next: T, enable: Bool)
使用這個
apply
方法編譯産生的寄存器,會在時鐘上升沿判斷
enable
條件是否滿足,如果滿足,寄存器就更新資料,否則不更新,代碼如下,
package Test
import chisel3._
import chisel3.util._
class RegEnableTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val load = Input(Bool())
val out = Output(UInt(4.W))
})
val x = RegEnable(io.in, io.load)
io.out := x
}
編譯産生的verilog如下,
module RegEnableTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg [3:0] x; // @[Reg.scala 15:16]
assign io_out = x; // @[RegEnableTest.scala 13:10]
always @(posedge clock) begin
if (io_load) begin
x <= io_in;
end
end
endmodule
2.4.2 def apply[T <: Data](next: T, init: T, enable: Bool)
def apply[T <: Data](next: T, init: T, enable: Bool)
這個
apply
方法就是可以指定複位值,代碼如下,
package Test
import chisel3._
import chisel3.util._
class RegEnableTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val load = Input(Bool())
val out = Output(UInt(4.W))
})
val x = RegEnable(io.in, 5.U, io.load)
io.out := x
}
編譯産生的verilog如下,
module RegEnableTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg [3:0] x; // @[Reg.scala 27:20]
assign io_out = x; // @[RegEnableTest.scala 13:10]
always @(posedge clock) begin
if (reset) begin
x <= 4'h5;
end else if (io_load) begin
x <= io_in;
end
end
endmodule
2.5 ShiftRegister
ShiftRegister
ShiftRegister
就是一個簡單的打拍延遲,有兩個
apply
方法,一個可以指定複位值,另一個不可以。
2.5.1 def apply[T <: Data](in: T, n: Int, en: Bool = true.B)
def apply[T <: Data](in: T, n: Int, en: Bool = true.B)
注意第2個參數是scala的
Int
類型,指定将輸入延遲多少拍再輸出。舉個例子,
package Test
import chisel3._
import chisel3.util._
class ShiftRegisterTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val load = Input(Bool())
val out = Output(UInt(4.W))
})
val x = ShiftRegister(io.in, 3, io.load)
io.out := x
}
編譯産生的verilog如下,這個子產品就是将輸入延遲3拍之後輸出,并受到
io_load
控制,
module ShiftRegisterTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg [3:0] _T; // @[Reg.scala 15:16]
reg [3:0] _T_1; // @[Reg.scala 15:16]
reg [3:0] x; // @[Reg.scala 15:16]
assign io_out = x; // @[ShiftRegisterTest.scala 14:10]
always @(posedge clock) begin
if (io_load) begin
_T <= io_in;
end
if (io_load) begin
_T_1 <= _T;
end
if (io_load) begin
x <= _T_1;
end
end
endmodule
2.5.2 def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool)
def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool)
這個
apply
方法就是可以給
ShiftRegister
指定複位值,其他和第一種
apply
方法一緻。舉個例子,與上面的代碼唯一的不同就是指定了複位值,
package Test
import chisel3._
import chisel3.util._
class ShiftRegisterTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(4.W))
val load = Input(Bool())
val out = Output(UInt(4.W))
})
val x = ShiftRegister(io.in, 3, 5.U, io.load)
io.out := x
}
編譯産生的verilog如下,注意在
always
塊内,寄存器設定了複位邏輯,其他與第一種
apply
方法生成的verilog一樣的,
module ShiftRegisterTest(
input clock,
input reset,
input [3:0] io_in,
input io_load,
output [3:0] io_out
);
reg [3:0] _T; // @[Reg.scala 27:20]
reg [3:0] _T_1; // @[Reg.scala 27:20]
reg [3:0] x; // @[Reg.scala 27:20]
assign io_out = x; // @[ShiftRegisterTest.scala 14:10]
always @(posedge clock) begin
if (reset) begin
_T <= 4'h5;
end else if (io_load) begin
_T <= io_in;
end
if (reset) begin
_T_1 <= 4'h5;
end else if (io_load) begin
_T_1 <= _T;
end
if (reset) begin
x <= 4'h5;
end else if (io_load) begin
x <= _T_1;
end
end
endmodule
使用寄存器實作一個累加器Accumulator
累加器Accumulator實作是chisel-tutorial中的problem的第一個問題,給定一個1-bit的輸入,每個時鐘上升沿将該輸入累加到輸出端口上進行輸出,輸出是8-bit的。那麼直接給代碼,
// See LICENSE.txt for license details.
package problems
import chisel3._
// Problem:
//
// Count incoming trues
// (increase counter every clock if 'in' is asserted)
//
class Accumulator extends Module {
val io = IO(new Bundle {
val in = Input(UInt(1.W))
val out = Output(UInt(8.W))
})
// Implement below ----------
val accu = Reg(chiselTypeOf(io.out))
accu := accu + io.in
io.out := accu
// Implement above ----------
}
我定義了一個寄存器
accu
,用于存儲每個周期的累加值,使用
chiselTypeOf
提取
io.out
的類型,并定義
accu
存儲的就是這種類型的資料。
我們來看一眼測試用例,
// See LICENSE.txt for license details.
package problems
import chisel3.iotesters.PeekPokeTester
class AccumulatorTests(c: Accumulator) extends PeekPokeTester(c) {
var tot = 0
for (t <- 0 until 16) {
val in = rnd.nextInt(2)
poke(c.io.in, in)
step(1)
if (in == 1) tot += 1
expect(c.io.out, tot)
}
}
測試用例中,使用
rnd.nextInt(2)
産生一個僞随機數,随機範圍是0(包括)到2(不包括)的
Int
類型資料,也就是0和1,将這個随機數指派給
in
。