当前位置:网站首页>Chisel tutorial - 04 Control flow in chisel
Chisel tutorial - 04 Control flow in chisel
2022-07-07 23:41:00 【github-3rr0r】
control flow
motivation
So far in this series ,Chisel There is a strong correspondence between the software and hardware in . But after the introduction of control flow, it is different , There should be great differences on the views of software and hardware .
This section will introduce control flow in both generator software and hardware . If you reconnect to a Chisel What will happen to the connection ? How to make a multiplexer have more than two inputs ? This section will give the answers to these two questions .
Finally, connect semantics
Mentioned earlier ,Chisel Through operators :=
To connect components , For various reasons , It is allowed to emit multiple connection statements to the same component .
For multiple statements re assigned after assignment , The last connection statement will take effect :
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
}
// 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!
when
,elsewhen
and otherwise
Chisel The main conditional logic implementation in is when
,elsewhen
and otherwise
structure , It usually looks like this :
when(someBooleanCondition) {
// things to do when true
}.elsewhen(someOtherBooleanCondition) {
// things to do on this condition
}.otherwise {
// things to do if none of th boolean conditions are true
}
The three of them must appear in the above order , The following can be omitted , There can be as many elsewhen
Clause .
Any true clause condition will terminate execution , The actions performed by clause can be complex statement blocks or nested when
.
And Scala Medium if
The difference is ,when
The clause in does not return a value . So it can't be used like that :
val result = when(squareIt) {
x * x}.otherwise {
x}
The solution will be in the later connection (Wire) Section mentions .
Here is a when
structure Chisel Examples of conditional statements :
import chisel3._
import chisel3.util._
class Max3 extends Module {
val io = IO(new Bundle {
val in_a = Input(UInt(16.W))
val in_b = Input(UInt(16.W))
val in_c = Input(UInt(16.W))
val out = Output(UInt(16.W))
})
when(io.in_a >= io.in_b && io.in_a >= io.in_c) {
io.out := io.in_a
}.elsewhen(io.in_b >= io.in_c) {
io.out := io.in_b
}.otherwise {
io.out := io.in_c
}
}
object Max3 extends App {
println(getVerilogString(new Max3()))
}
Output is :
module Max3(
input clock,
input reset,
input [15:0] io_in_a,
input [15:0] io_in_b,
input [15:0] io_in_c,
output [15:0] io_out
);
wire [15:0] _GEN_0 = io_in_b >= io_in_c ? io_in_b : io_in_c; // @[MyModule.scala 14:34 15:12 17:12]
assign io_out = io_in_a >= io_in_b & io_in_a >= io_in_c ? io_in_a : _GEN_0; // @[MyModule.scala 12:50 13:12]
endmodule
Test part :
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class MyModuleTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "MyOperators"
it should "get right results" in {
test(new Max3) {
c =>
// verify that the max of the three inputs is correct
c.io.in_a.poke(6.U)
c.io.in_b.poke(4.U)
c.io.in_c.poke(2.U)
c.io.out.expect(6.U) // input 1 should be biggest
c.io.in_b.poke(7.U)
c.io.out.expect(7.U) // now input 2 is
c.io.in_c.poke(11.U)
c.io.out.expect(11.U) // and now input 3
c.io.in_c.poke(3.U)
c.io.out.expect(7.U) // show that decreasing an input works as well
c.io.in_a.poke(9.U)
c.io.in_b.poke(9.U)
c.io.in_c.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!
}
}
The test passed .
Wire
structure
Mentioned earlier when
Cannot return value ,Chisel Medium Wire
Structure is one of the ways to solve this problem .Wire
Defines a circuit component , He can show up :=
Left or right of the operator .
The following is a four input use Wire
For example, the sorting circuit of :
To demonstrate , First, design a small combinatorial sorter , Accept four numerical inputs and get four numerical outputs , Consider the following graph :
The red line indicates that the value on the left is smaller than that on the right , Just copy the value directly , The black line indicates that when the value on the left is larger than the value on the right, the two values are exchanged .
The figure shows a series of row
Opening grid , have access to wire
To construct these lattices , As a place to store copied or exchanged values .
The following is the code implementation , At present, it is quite lengthy , There will be ways to reduce it later :
import chisel3._
import chisel3.util._
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
row11 := io.in1
}.otherwise {
row10 := io.in1
row11 := io.in0
}
when(io.in2 < io.in3 ) {
row12 := io.in2
row13 := io.in3
}.otherwise {
row12 := io.in3
row13 := io.in2
}
val row20 = Wire(UInt(16.W))
val row21 = Wire(UInt(16.W))
val row22 = Wire(UInt(16.W))
val row23 = Wire(UInt(16.W))
when(row10 < row13) {
row20 := row10
row23 := row13
}.otherwise {
row20 := row13
row23 := row10
}
when(row11 < row12) {
row21 := row11
row22 := row12
}.otherwise {
row21 := row12
row22 := row11
}
when(row20 < row21) {
io.out0 := row20
io.out1 := row21
}.otherwise {
io.out0 := row21
io.out1 := row20
}
when(row22 < row23) {
io.out2 := row22
io.out3 := row23
}.otherwise {
io.out2 := row23
io.out3 := row22
}
}
object Sort4 extends App {
println(getVerilogString(new Sort4()))
}
Output Verilog The code is as follows :
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; // @[MyModule.scala 21:25 22:11 25:11]
wire [15:0] row11 = io_in0 < io_in1 ? io_in1 : io_in0; // @[MyModule.scala 21:25 23:11 26:11]
wire [15:0] row12 = io_in2 < io_in3 ? io_in2 : io_in3; // @[MyModule.scala 29:26 30:11 33:11]
wire [15:0] row13 = io_in2 < io_in3 ? io_in3 : io_in2; // @[MyModule.scala 29:26 31:11 34:11]
wire [15:0] row20 = row10 < row13 ? row10 : row13; // @[MyModule.scala 42:23 43:11 46:11]
wire [15:0] row23 = row10 < row13 ? row13 : row10; // @[MyModule.scala 42:23 44:11 47:11]
wire [15:0] row21 = row11 < row12 ? row11 : row12; // @[MyModule.scala 50:23 51:11 54:11]
wire [15:0] row22 = row11 < row12 ? row12 : row11; // @[MyModule.scala 50:23 52:11 55:11]
assign io_out0 = row20 < row21 ? row20 : row21; // @[MyModule.scala 58:23 59:13 62:13]
assign io_out1 = row20 < row21 ? row21 : row20; // @[MyModule.scala 58:23 60:13 63:13]
assign io_out2 = row22 < row23 ? row22 : row23; // @[MyModule.scala 66:23 67:13 70:13]
assign io_out3 = row22 < row23 ? row23 : row22; // @[MyModule.scala 66:23 68:13 71:13]
endmodule
Test code :
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class MyModuleTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "MyOperators"
it should "get right results" in {
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!!")
}
}
The test passed .
The tester can also be used Scala in List
Characteristics of (Scala Standard Library 2.12.13 - scala.collection.immutable.List (scala-lang.org)), Construct permutations and combinations of various inputs :
test(new Sort4) {
c =>
List(1, 2, 3, 4).permutations.foreach {
case i0 :: i1 :: i2 :: i3 => {
println(s"Sorting $i0 $i1 $i2 $i3")
c.io.in0.poke(i0.U)
c.io.in1.poke(i1.U)
c.io.in2.poke(i2.U)
c.io.in3.poke(i3.U)
c.io.out0.expect(1.U)
c.io.out1.expect(2.U)
c.io.out2.expect(3.U)
c.io.out3.expect(4.U)
}
case _ => println(s"Matching Error!")
}
}
println("SUCCESS!!")
there case
yes Scala The classic use of . front List(1, 2, 3, 4).permutations.foreach
The result is a variety of permutations List
The iterator , Every List
Input as a variable into the later partial function . hinder case
Used to match patterns ,i0 :: i1 :: i2 :: i3
Is to construct a pattern , Used to match four values in the list , The function is equivalent to unpacking the values in the list to four variables .
practice
Polynomial circuit
x 2 − 2 x + 1 2 x 2 + 6 x + 3 4 x 2 − 10 x − 5 x^2 - 2x + 1\\ 2x^2 + 6x + 3\\ 4x^2 - 10x -5 x2−2x+12x2+6x+34x2−10x−5
Construct a circuit to calculate the above polynomial , A selector input is used to decide which polynomial to calculate .
Tips : Use Wire
To hold the x 2 x^2 x2, So you only need to calculate once .
First, use the idea of test driven development Scala Write a model :
def poly0(x: Int): Int = {
x * x - 2 * x + 1}
def poly1(x: Int): Int = {
2 * x * x + 6 * x + 3}
def poly2(x: Int): Int = {
4 * x * x - 10 * x - 5}
def main(args: Array[String]): Unit = {
assert(poly0(0) == 1)
assert(poly1(0) == 3)
assert(poly2(0) == -5)
assert(poly0(1) == 0)
assert(poly1(1) == 11)
assert(poly2(1) == -11)
}
The test passed , Now wrap these three as a parameterized function , Like hardware . Use Scala Medium if
sentence , Based on input select
Select the corresponding polynomial :
def poly0(x: Int): Int = {
x * x - 2 * x + 1}
def poly1(x: Int): Int = {
2 * x * x + 6 * x + 3}
def poly2(x: Int): Int = {
4 * x * x - 10 * x - 5}
def poly(select: Int, x: Int): Int = {
if(select == 0) {
poly0(x)
} else if(select == 1) {
poly1(x)
} else {
poly2(x)
}
}
def main(args: Array[String]): Unit = {
assert(poly(1, 0) == 3)
assert(poly(1, 1) == 11)
assert(poly(2, 1) == -11)
}
The test passed .
Now let's follow the original idea :
import chisel3._
import chisel3.util._
class Polynomial extends Module {
val io = IO(new Bundle {
val select = Input(UInt(2.W))
val x = Input(SInt(32.W))
val out = Output(SInt(32.W))
})
val result = Wire(SInt(32.W))
val square = Wire(SInt(32.W))
square := io.x * io.x
when(io.select === 0.U) {
result := square - 2.S * io.x + 1.S
}.elsewhen(io.select === 1.U) {
result := 2.S * square + 6.S * io.x + 3.S
}.otherwise {
result := 4.S * square - 10.S * io.x -5.S
}
io.out := result
}
object Polynomial extends App {
println(getVerilogString(new Polynomial))
}
Output is as follows :
module Polynomial(
input clock,
input reset,
input [1:0] io_select,
input [31:0] io_x,
output [31:0] io_out
);
wire [63:0] _square_T = $signed(io_x) * $signed(io_x); // @[MyModule.scala 14:18]
wire [34:0] _result_T = 3'sh2 * $signed(io_x); // @[MyModule.scala 16:28]
wire [31:0] square = _square_T[31:0]; // @[MyModule.scala 12:20 14:10]
wire [34:0] _GEN_3 = {
{3{square[31]}},square}; // @[MyModule.scala 16:22]
wire [34:0] _result_T_3 = $signed(_GEN_3) - $signed(_result_T); // @[MyModule.scala 16:22]
wire [34:0] _result_T_6 = $signed(_result_T_3) + 35'sh1; // @[MyModule.scala 16:35]
wire [34:0] _result_T_7 = 3'sh2 * $signed(square); // @[MyModule.scala 18:19]
wire [35:0] _result_T_8 = 4'sh6 * $signed(io_x); // @[MyModule.scala 18:34]
wire [35:0] _GEN_4 = {
{1{_result_T_7[34]}},_result_T_7}; // @[MyModule.scala 18:28]
wire [35:0] _result_T_11 = $signed(_GEN_4) + $signed(_result_T_8); // @[MyModule.scala 18:28]
wire [35:0] _result_T_14 = $signed(_result_T_11) + 36'sh3; // @[MyModule.scala 18:41]
wire [35:0] _result_T_15 = 4'sh4 * $signed(square); // @[MyModule.scala 20:19]
wire [36:0] _result_T_16 = 5'sha * $signed(io_x); // @[MyModule.scala 20:35]
wire [36:0] _GEN_5 = {
{1{_result_T_15[35]}},_result_T_15}; // @[MyModule.scala 20:28]
wire [36:0] _result_T_19 = $signed(_GEN_5) - $signed(_result_T_16); // @[MyModule.scala 20:28]
wire [36:0] _result_T_22 = $signed(_result_T_19) - 37'sh5; // @[MyModule.scala 20:42]
wire [36:0] _GEN_0 = io_select == 2'h1 ? $signed({
{1{_result_T_14[35]}},_result_T_14}) : $signed(_result_T_22); // @[MyModule.scala 17:33 18:12 20:12]
wire [36:0] _GEN_1 = io_select == 2'h0 ? $signed({
{2{_result_T_6[34]}},_result_T_6}) : $signed(_GEN_0); // @[MyModule.scala 15:27 16:12]
assign io_out = _GEN_1[31:0]; // @[MyModule.scala 11:20]
endmodule
The test code is as follows :
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class MyModuleTest extends AnyFlatSpec with ChiselScalatestTester {
def poly0(x: Int): Int = {
x * x - 2 * x + 1}
def poly1(x: Int): Int = {
2 * x * x + 6 * x + 3}
def poly2(x: Int): Int = {
4 * x * x - 10 * x - 5}
def poly(select: Int, x: Int): Int = {
if(select == 0) {
poly0(x)
} else if(select == 1) {
poly1(x)
} else {
poly2(x)
}
}
behavior of "MyOperators"
it should "get right results" in {
test(new Polynomial) {
c =>
for(x <- 0 to 20) {
for(select <- 0 to 2) {
c.io.select.poke(select.U)
c.io.x.poke(x.S)
c.io.out.expect(poly(select, x).S)
}
}
}
println("SUCCESS!!")
}
}
The test passed .
Finite state machine (FSM)
It's too painful to optimize the logic of state machine through Karnaugh map , Comprehensive tools can be used to optimize , It will also produce non intuitive 、 Unreadable code . While using Chisel It is a wise choice to write with control flow and final connection semantics .
A master will experience four states in his study career : Free (idel), Write code (coding), Write a paper (writing), graduation (graduate). The transition of these States is based on three conditions : coffee , Suddenly enlightened idea, From the tutor pressure. once graduation 了 , Will return Free state .
Below FSM Figure shows these States and the transitions between them :
All transformations without labels in the figure ( That is, no input ) Will return to Free State rather than stay in the current state . The priority entered is coffee > idea > pressure, So when you are idle, if you receive coffee and pressure at the same time , It will be transferred to the state of writing code .
Here is Scala Test code :
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class MyModuleTest extends AnyFlatSpec with ChiselScalatestTester {
def states = Map("idle" -> 0, "coding" -> 1, "writing" -> 2, "grad" -> 3)
def gradLife (state: Int, coffee: Boolean, idea: Boolean, pressure: Boolean): Int = {
var nextState = states("idle")
if (state == states("idle")) {
if (coffee) {
nextState = states("coding")}
else if (idea) {
nextState = states("idle")}
else if (pressure) {
nextState = states("writing")}
} else if (state == states("coding")) {
if (coffee) {
nextState = states("coding")}
else if (idea || pressure) {
nextState = states("writing")}
} else if (state == states("writing")) {
if (coffee || idea) {
nextState = states("writing")}
else if (pressure) {
nextState = states("grad")}
}
nextState
}
(0 until states.size).foreach{
state => assert(gradLife(state, false, false, false) == states("idle")) }
assert(gradLife(states("writing"), true, false, true) == states("writing"))
assert(gradLife(states("idle"), true, true, true) == states("coding"))
assert(gradLife(states("idle"), false, true, true) == states("idle"))
assert(gradLife(states("grad"), false, false, false) == states("idle"))
}
I haven't learned temporal logic yet , So the current state here is represented by an input to the module , The next state is represented by an output , With the above gradLife
Consistent function . Now use Chisel To achieve .
Chisel Provides a convenient state machine mapping function , be called Enum
, In order to use these States , Think of them as UInt
Use literal value .
Be careful , In hardware, three equal signs are used to judge equality ===
!
The implementation is as follows :
import chisel3._
import chisel3.util._
class GradLife extends Module {
val io = IO(new Bundle {
val state = Input(UInt(2.W))
val coffee = Input(Bool())
val idea = Input(Bool())
val pressure = Input(Bool())
val nextState = Output(UInt(2.W))
})
val idle :: coding :: writing :: grad :: Nil = Enum(4)
io.nextState := idle
when(io.state === idle) {
when(io.coffee) {
io.nextState := coding
}.elsewhen(io.idea) {
io.nextState := idle
}.elsewhen(io.pressure) {
io.nextState := writing
}
}.elsewhen(io.state === coding) {
when(io.coffee) {
io.nextState := coding
}.elsewhen(io.idea || io.pressure) {
io.nextState := writing
}
}.elsewhen(io.state === writing) {
when(io.coffee || io.idea) {
io.nextState := writing
}.elsewhen(io.pressure) {
io.nextState := grad
}
}
}
object GradLife extends App {
println(getVerilogString(new GradLife))
}
Output Verilog The code is as follows :
module GradLife(
input clock,
input reset,
input [1:0] io_state,
input io_coffee,
input io_idea,
input io_pressure,
output [1:0] io_nextState
);
wire [1:0] _GEN_0 = io_pressure ? 2'h2 : 2'h0; // @[MyModule.scala 15:16 21:29 22:20]
wire [1:0] _GEN_1 = io_idea ? 2'h0 : _GEN_0; // @[MyModule.scala 19:25 20:20]
wire [1:0] _GEN_2 = io_coffee ? 2'h1 : _GEN_1; // @[MyModule.scala 17:21 18:20]
wire [1:0] _GEN_3 = io_idea | io_pressure ? 2'h2 : 2'h0; // @[MyModule.scala 15:16 27:40 28:20]
wire [1:0] _GEN_4 = io_coffee ? 2'h1 : _GEN_3; // @[MyModule.scala 25:21 26:20]
wire [1:0] _GEN_5 = io_pressure ? 2'h3 : 2'h0; // @[MyModule.scala 15:16 33:29 34:20]
wire [1:0] _GEN_6 = io_coffee | io_idea ? 2'h2 : _GEN_5; // @[MyModule.scala 31:32 32:20]
wire [1:0] _GEN_7 = io_state == 2'h2 ? _GEN_6 : 2'h0; // @[MyModule.scala 15:16 30:36]
wire [1:0] _GEN_8 = io_state == 2'h1 ? _GEN_4 : _GEN_7; // @[MyModule.scala 24:35]
assign io_nextState = io_state == 2'h0 ? _GEN_2 : _GEN_8; // @[MyModule.scala 16:27]
endmodule
The test code is as follows :
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class MyModuleTest extends AnyFlatSpec with ChiselScalatestTester {
def states = Map("idle" -> 0, "coding" -> 1, "writing" -> 2, "grad" -> 3)
def gradLife (state: Int, coffee: Boolean, idea: Boolean, pressure: Boolean): Int = {
var nextState = states("idle")
if (state == states("idle")) {
if (coffee) {
nextState = states("coding")}
else if (idea) {
nextState = states("idle")}
else if (pressure) {
nextState = states("writing")}
} else if (state == states("coding")) {
if (coffee) {
nextState = states("coding")}
else if (idea || pressure) {
nextState = states("writing")}
} else if (state == states("writing")) {
if (coffee || idea) {
nextState = states("writing")}
else if (pressure) {
nextState = states("grad")}
}
nextState
}
behavior of "MyOperators"
it should "get right results" in {
test(new GradLife) {
c =>
for (state <- 0 to 3) {
for (coffee <- List(true, false)) {
for (idea <- List(true, false)) {
for (pressure <- List(true, false)) {
c.io.state.poke(state.U)
c.io.coffee.poke(coffee.B)
c.io.idea.poke(idea.B)
c.io.pressure.poke(pressure.B)
c.io.nextState.expect(gradLife(state, coffee, idea, pressure).U)
}
}
}
}
}
println("SUCCESS!!")
}
}
The test passed .
边栏推荐
- C method question 1
- Oracle statistics by time
- 2022注册测绘师备考开始 还在不知所措?手把手教你怎么考?
- Rock-paper-scissors
- StringUtils工具类
- SAP memory parameter tuning process
- P1308 [noip2011 popularity group] count the number of words
- Take you hand in hand to build Eureka client with idea
- Anxin can internally test offline voice module vb-01 to communicate with esp-c3-12f
- Class C design questions
猜你喜欢
Open source hardware small project: anxinco esp-c3f control ws2812
Markdown
Live-Server使用
Live server usage
Progress broadcast | all 29 shield machines of Guangzhou Metro Line 7 have been launched
95.(cesium篇)cesium动态单体化-3D建筑物(楼栋)
Understand TCP's three handshakes and four waves with love
How to change the formula picture in the paper directly into the formula in word
二叉排序树【BST】——创建、查找、删除、输出
C simple question one
随机推荐
激光slam学习(2D/3D、偏实践)
8.31 Tencent interview
C method question 2
C - Fibonacci sequence again
Understand TCP's three handshakes and four waves with love
0-1 knapsack problem
Markdown
Home appliance industry channel business collaboration system solution: help home appliance enterprises quickly realize the Internet of channels
Rock-paper-scissors
MySQL架构
C language greedy snake
How to change the formula picture in the paper directly into the formula in word
Anti climbing means cracking the second
Summary of SQL single table query 2020.7.27
Get started with mongodb
ping报错:未知的名称或服务
The file format and extension of XLS do not match
ASP. Net open web page
[stm32+esp8266 connect Tencent cloud IOT development platform 2] stm32+esp8266-01s connect Tencent cloud
Boost regex library source code compilation