天天看点

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