【Chisel】2.3 控制流

前言

到目前为止,Chisel中的硬件实现都还能和软件对应上。 在控制流中,硬件和软件将开始会有很大的不同。 这一节中会介绍软件生成器中的控制流和硬件中的控制流。 例如重新连接Chisel中的线(wire)会发生什么? 怎样定义一个具有两个以上输入的多路复用器?

重复赋值

Chisel 用 := 声明连接接口。Chisel允许多个接口连接到同一个接口A的情况,在这样的情况下,接口A会和最后一个声明的接口相连。

class LastConnect extends Module {
  val io = IO(new Bundle {
    val in = Input(UInt(4.W))
    val out = Output(UInt(4.W))
  })
  io.out := 1.U
  io.out := 2.U
  io.out := 3.U
  io.out := 4.U // 最后接口out会得到值为4的信号。
}

println(getVerilog(new LastConnect))
//  Test LastConnect
test(new LastConnect) { c => c.io.out.expect(4.U) } // Assert that the output correctly has 4
println("SUCCESS!!") // Scala Code: if we get here, our tests passed!

生成的verilog代码:可见位宽为4的十六进制数4被赋给了输出。

module LastConnect(
  input        clock,
  input        reset,
  input  [3:0] io_in,
  output [3:0] io_out
);
  assign io_out = 4'h4; // @[cmd3.sc 9:10]
endmodule

基础判断语句when, else when, 和 otherwise

Chisel的基础判断语句为when, else when, and otherwise, 具体用例:

when(someBooleanCondition) {
  // 当someBooleanCondition判断语句为真,执行该句
}.elsewhen(someOtherBooleanCondition) {
  // things to do on this condition
}.otherwise {
  // 当以上的判断语句都为假,执行该句
}
//注意这里的 < . >

注意:

  • w h e n → e l s e w h e n → o t h e r w i s e when\rightarrow elsewhen\rightarrow otherwise whenelsewhenotherwise 使用顺序不能打乱。

    可以对等C的 i f → e l s e i f → e l s e if\rightarrow elseif \rightarrow else ifelseifelse , 中间想用多少个elsewhen都无所谓。

  • 与Scala中的if不同的是, when, elsewhen 和 otherwise语句并不返回值。因此以下使用是非法的:

    val result = when(squareIt) { x * x }.otherwise { x }
    

实例

// Max3 返回三个值中的最大值
class Max3 extends Module {
  val io = IO(new Bundle {
    val in1 = Input(UInt(16.W))
    val in2 = Input(UInt(16.W))
    val in3 = Input(UInt(16.W))
    val out = Output(UInt(16.W))
  })
    
  when(io.in1 >= io.in2 && io.in1 >= io.in3) {
    io.out := io.in1  
  }.elsewhen(io.in2 >= io.in3) {
    io.out := io.in2 
  }.otherwise {
    io.out := io.in3
  }
}

// Test Max3
test(new Max3) { c =>
  // verify that the max of the three inputs is correct
  c.io.in1.poke(6.U)
  c.io.in2.poke(4.U)
  c.io.in3.poke(2.U)
  c.io.out.expect(6.U)  // input 1 should be biggest
  c.io.in2.poke(7.U)
  c.io.out.expect(7.U)  // now input 2 is
  c.io.in3.poke(11.U)
  c.io.out.expect(11.U) // and now input 3
  c.io.in3.poke(3.U)
  c.io.out.expect(7.U)  // show that decreasing an input works as well
  c.io.in1.poke(9.U)
  c.io.in2.poke(9.U)
  c.io.in3.poke(6.U)
  c.io.out.expect(9.U)  // still get max with tie
}

println("SUCCESS!!") // Scala Code: if we get here, our tests passed!

Wire语句

刚才介绍了when语句并不会返回任何数值。当两两比较然后赋值给输出的时候,when语句害能解决问题,但是如果是比较后的值还需要和第三个input比较呢?这时候需要有一个暂时存储的寄存器。

wire语句可以用来生成一个寄存器存储生成的“过渡”值。

实例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LY8KuTQi-1647397625056)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0198de85-c93d-4f61-ba0e-d58c8d0976d2/Untitled.png)]

输入4个数值 i n 0 , i n 1 , i n 2 , i n 3 in_0,in_1,in_2,in_3 in0,in1,in2,in3, 比较左边的值和右边的值,当左值比右值小时,遵循红线赋值。当左值大于右值时,按照黑线赋值。

这时候可以看到,不再像上面的例子一样,单纯的把两值相比然后赋给输出,我们需要中间值来存储我们得到的结果,然后进一步比较,因此,可以用wire来声明 r o w 10 , r o w 11 , r o w 12 , r o w 13 , r o w 20 , r o w 21 , r o w 22 , r o w 23 row_{10},row_{11},row_{12},row_{13},row_{20},row_{21},row_{22},row_{23} row10,row11,row12,row13,row20,row21,row22,row23

/** Sort4 sorts its 4 inputs to its 4 outputs */
class Sort4 extends Module {
  val io = IO(new Bundle {
    val in0 = Input(UInt(16.W))
    val in1 = Input(UInt(16.W))
    val in2 = Input(UInt(16.W))
    val in3 = Input(UInt(16.W))
    val out0 = Output(UInt(16.W))
    val out1 = Output(UInt(16.W))
    val out2 = Output(UInt(16.W))
    val out3 = Output(UInt(16.W))
  })

  val row10 = Wire(UInt(16.W))
  val row11 = Wire(UInt(16.W))
  val row12 = Wire(UInt(16.W))
  val row13 = Wire(UInt(16.W))

  when(io.in0 < io.in1) {
    row10 := io.in0            // preserve first two elements
    row11 := io.in1
  }.otherwise {
    row10 := io.in1            // swap first two elements
    row11 := io.in0
  }

  when(io.in2 < io.in3) {
    row12 := io.in2            // preserve last two elements
    row13 := io.in3
  }.otherwise {
    row12 := io.in3            // swap last two elements
    row13 := io.in2
  }

  val row21 = Wire(UInt(16.W))
  val row22 = Wire(UInt(16.W))

  when(row11 < row12) {
    row21 := row11            // preserve middle 2 elements
    row22 := row12
  }.otherwise {
    row21 := row12            // swap middle two elements
    row22 := row11
  }

  val row20 = Wire(UInt(16.W))
  val row23 = Wire(UInt(16.W))
  when(row10 < row13) {
    row20 := row10            // preserve middle 2 elements
    row23 := row13
  }.otherwise {
    row20 := row13            // swap middle two elements
    row23 := row10
  }

  when(row20 < row21) {
    io.out0 := row20            // preserve first two elements
    io.out1 := row21
  }.otherwise {
    io.out0 := row21            // swap first two elements
    io.out1 := row20
  }

  when(row22 < row23) {
    io.out2 := row22            // preserve first two elements
    io.out3 := row23
  }.otherwise {
    io.out2 := row23            // swap first two elements
    io.out3 := row22
  }
}

println(getVerilog(new Sort4))
// Here's the tester
test(new Sort4) { c =>
  // verify the inputs are sorted
  c.io.in0.poke(3.U)
  c.io.in1.poke(6.U)
  c.io.in2.poke(9.U)
  c.io.in3.poke(12.U)
  c.io.out0.expect(3.U)
  c.io.out1.expect(6.U)
  c.io.out2.expect(9.U)
  c.io.out3.expect(12.U)

  c.io.in0.poke(13.U)
  c.io.in1.poke(4.U)
  c.io.in2.poke(6.U)
  c.io.in3.poke(1.U)
  c.io.out0.expect(1.U)
  c.io.out1.expect(4.U)
  c.io.out2.expect(6.U)
  c.io.out3.expect(13.U)

  c.io.in0.poke(13.U)
  c.io.in1.poke(6.U)
  c.io.in2.poke(4.U)
  c.io.in3.poke(1.U)
  c.io.out0.expect(1.U)
  c.io.out1.expect(4.U)
  c.io.out2.expect(6.U)
  c.io.out3.expect(13.U)
}
println("SUCCESS!!") // Scala Code: if we get here, our tests passed!

生成的verilog代码:

module Sort4(
  input         clock,
  input         reset,
  input  [15:0] io_in0,
  input  [15:0] io_in1,
  input  [15:0] io_in2,
  input  [15:0] io_in3,
  output [15:0] io_out0,
  output [15:0] io_out1,
  output [15:0] io_out2,
  output [15:0] io_out3
);
  wire [15:0] row10 = io_in0 < io_in1 ? io_in0 : io_in1; // @[cmd5.sc 18:25 cmd5.sc 19:11 cmd5.sc 22:11]
  wire [15:0] row11 = io_in0 < io_in1 ? io_in1 : io_in0; // @[cmd5.sc 18:25 cmd5.sc 20:11 cmd5.sc 23:11]
  wire [15:0] row12 = io_in2 < io_in3 ? io_in2 : io_in3; // @[cmd5.sc 26:25 cmd5.sc 27:11 cmd5.sc 30:11]
  wire [15:0] row13 = io_in2 < io_in3 ? io_in3 : io_in2; // @[cmd5.sc 26:25 cmd5.sc 28:11 cmd5.sc 31:11]
  wire [15:0] row21 = row11 < row12 ? row11 : row12; // @[cmd5.sc 37:23 cmd5.sc 38:11 cmd5.sc 41:11]
  wire [15:0] row22 = row11 < row12 ? row12 : row11; // @[cmd5.sc 37:23 cmd5.sc 39:11 cmd5.sc 42:11]
  wire [15:0] row20 = row10 < row13 ? row10 : row13; // @[cmd5.sc 47:23 cmd5.sc 48:11 cmd5.sc 51:11]
  wire [15:0] row23 = row10 < row13 ? row13 : row10; // @[cmd5.sc 47:23 cmd5.sc 49:11 cmd5.sc 52:11]
  assign io_out0 = row20 < row21 ? row20 : row21; // @[cmd5.sc 55:23 cmd5.sc 56:13 cmd5.sc 59:13]
  assign io_out1 = row20 < row21 ? row21 : row20; // @[cmd5.sc 55:23 cmd5.sc 57:13 cmd5.sc 60:13]
  assign io_out2 = row22 < row23 ? row22 : row23; // @[cmd5.sc 63:23 cmd5.sc 64:13 cmd5.sc 67:13]
  assign io_out3 = row22 < row23 ? row23 : row22; // @[cmd5.sc 63:23 cmd5.sc 65:13 cmd5.sc 68:13]
endmodule

可见 r o w 10 , r o w 11 , r o w 12 , r o w 13 , r o w 20 , r o w 21 , r o w 22 , r o w 23 row_{10},row_{11},row_{12},row_{13},row_{20},row_{21},row_{22},row_{23} row10,row11,row12,row13,row20,row21,row22,row23都有对应的寄存器生成来存储过渡值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值