天天看點

03-chisel-tutorial代碼學習之寄存器的使用1. 寄存器的定義2. chisel的多種寄存器聲明

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

可以定義一個無條件更新的寄存器,

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

Reg

十分類似,使用

Reg

時要先定義這個寄存器,然後再給寄存器指派,而如果使用

RegNext

,直接把被寄存的值放在

RegNext

内即可,那麼

RegNext

的輸出就是輸入延遲1拍。

RegNext

有2個

apply

方法。

2.2.1

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)

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

有2個

apply

方法,一個隻接收寄存器複位值,另一個除了接收寄存器複位值之外還接收寄存器要儲存的變量。

2.3.1

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)

這種

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

需要引入

chisel.util

包。有2個

apply

方法,其中一個可以指定寄存器複位值。

2.4.1

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)

這個

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

就是一個簡單的打拍延遲,有兩個

apply

方法,一個可以指定複位值,另一個不可以。

2.5.1

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)

這個

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