当前位置:网站首页>Chisel tutorial - 03 Combinatorial logic in chisel (chisel3 cheat sheet is attached at the end)
Chisel tutorial - 03 Combinatorial logic in chisel (chisel3 cheat sheet is attached at the end)
2022-07-07 23:41:00 【github-3rr0r】
Chisel Combinatorial logic
This section will introduce how to use Chisel Component to implement combinatorial logic .
This section will demonstrate three basic Chisel type (UInt, Unsigned integer ;SInt, Signed integers ;Bool, Boolean value ) How to connect and operate .
It's important to note that all Chisel Variables are declared as Scala Of val
, Never use Scala Medium var
To realize hardware construction , Because the structure itself will not change after definition , Only its value can be changed when running on hardware . Wires can be used for parameterized types .
Common operators
Add implementation
First construct a Module
, I won't introduce this in detail :
import chisel3._
class MyModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
}
Based on this , We can use various operators on data :
import chisel3._
class MyModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
io.out := io.in
val two = 1 + 1
println(two)
val utwo = 1.U + 1.U
println(utwo)
}
object MyModule extends App {
println(getVerilogString(new MyModule()))
}
Output is as follows :
You can see the first addition val two = 1 + 1
The result of printing is 2, And the second addition val utwo = 1.U + 1.U
The result of printing is MyModule.utwo: OpResult[UInt<1>]
. The reason is that the first is to combine two Scala Add whole numbers , The latter is to combine two Chisel Of UInt
Add up , So it is regarded as a hardware node when printing , Output pointer and type name . Be careful ,1.U
Yes, it will Scala Of Int
1 convert to Chisel Of UInt
Face value .
And although the content of the test has nothing to do with input and output , But also connect the output to , Otherwise, it will report a mistake :
If the data types on both sides of the operator do not match, an error will also be reported , such as :
class MyModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val two = 1.U + 1
println(two)
io.out := io.in
}
Will cause type mismatch errors :
So clear the differences between different types when performing operations ,Scala It's a strongly typed language , Therefore, all transformations must be explicit .
reduce 、 ride 、 Division implementation
Look at other operators :
import chisel3._
class MyOperators extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out_add = Output(UInt(4.W))
val out_sub = Output(UInt(4.W))
val out_mul = Output(UInt(4.W))
})
io.out_add := 1.U + 4.U
io.out_sub := 2.U - 1.U
io.out_mul := 4.U * 2.U
}
object MyOperators extends App {
println(getVerilogString(new MyOperators()))
}
The output is :
module MyOperators(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out_add,
output [3:0] io_out_sub,
output [3:0] io_out_mul
);
wire [1:0] _io_out_sub_T_1 = 2'h2 - 2'h1; // @[MyModule.scala 12:21]
wire [4:0] _io_out_mul_T = 3'h4 * 2'h2; // @[MyModule.scala 13:21]
assign io_out_add = 4'h5; // @[MyModule.scala 11:14]
assign io_out_sub = {
{2'd0}, _io_out_sub_T_1}; // @[MyModule.scala 12:14]
assign io_out_mul = _io_out_mul_T[3:0]; // @[MyModule.scala 13:14]
endmodule
MyModuleTest.scala
The contents are as follows :
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 MyOperators) {
c =>
c.io.out_add.expect(5.U)
c.io.out_sub.expect(1.U)
c.io.out_mul.expect(8.U)
}
println("SUCCESS!!")
}
}
Test result passed .
Multiplexer (Mux) And splicing (Concatenation)
Chisel Built in multiple selection operator Mux
And splicing operators Cat
.
among ,Mux
Similar to the traditional ternary operator , The parameters are ( Conditions , The value when it is true , The value when it is false )
, Suggest using true.B
and false.B
To create Chisel Boolean values in .
Cat
The two parameters of are high order (MSB) And low order (LSB), But only two parameters can be accepted , If you want to splice multiple values, you need to nest multiple Cat
Or use more advanced features .
Usage examples are as follows :
import chisel3._
import chisel3.util._
class MyOperators extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out_mux = Output(UInt(4.W))
val out_cat = Output(UInt(4.W))
})
val s = true.B
io.out_mux := Mux(s, 3.U, 0.U)
io.out_cat := Cat(2.U, 1.U)
}
object MyOperators extends App {
println(getVerilogString(new MyOperators()))
}
Output is as follows :
module MyOperators(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out_mux,
output [3:0] io_out_cat
);
assign io_out_mux = 4'h3; // @[MyModule.scala 12:14]
assign io_out_cat = 4'h5; // @[MyModule.scala 13:14]
endmodule
Notice the generated Verilog There is no code at all mux perhaps concat Combinatorial logic implementation of , Instead, two constants are assigned .
This is because FIRRTL The circuit is simplified in the conversion process , Eliminated some obvious logic .
test :
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 MyOperators) {
c =>
c.io.out_mux.expect(3.U)
c.io.out_cat.expect(5.U)
}
println("SUCCESS!!")
}
}
The test passed .
About Chisel The list of operators can refer to Chisel cheatsheet, The complete list and implementation details can be referred to Chisel API.
practice
Realize multiplication and addition (MAC)
Realize multiplication and addition , Input is 4bit Unsigned integer A,B and C, Output is 8bit Unsigned integer (A * B) + C
, And pass the test :
test(new MAC) {
c =>
val cycles = 100
import scala.util.Random
for (i <- 0 until cycles) {
val in_a = Random.nextInt(16)
val in_b = Random.nextInt(16)
val in_c = Random.nextInt(16)
c.io.in_a.poke(in_a.U)
c.io.in_b.poke(in_b.U)
c.io.in_c.poke(in_c.U)
c.io.out.expect((in_a * in_b + in_c).U)
}
}
println("SUCCESS!!")
It's done directly , The answer is as follows :
import chisel3._
import chisel3.util._
class MAC extends Module {
val io = IO(new Bundle {
val in_a = Input(UInt(4.W))
val in_b = Input(UInt(4.W))
val in_c = Input(UInt(4.W))
val out = Output(UInt(8.W))
})
io.out := (io.in_a * io.in_b) + io.in_c
}
object MyOperators extends App {
println(getVerilogString(new MAC()))
}
Output is as follows :
module MAC(
input clock,
input reset,
input [3:0] io_in_a,
input [3:0] io_in_b,
input [3:0] io_in_c,
output [7:0] io_out
);
wire [7:0] _io_out_T = io_in_a * io_in_b; // @[MyModule.scala 12:22]
wire [7:0] _GEN_0 = {
{4'd0}, io_in_c}; // @[MyModule.scala 12:33]
assign io_out = _io_out_T + _GEN_0; // @[MyModule.scala 12:33]
endmodule
The test passed .
Arbiter (Arbiter)
The arbiter above is used to put FIFO The data in is arbitrated into two parallel processing units , The following rules :
- If both processing units are empty , Then send it to PE0;
- If at least one is available , The arbiter should tell FIFO Ready to accept data ;
- Before asserting that the data is valid , wait for PE Assert that it is ready ;
- Tips : You may need binary operators to implement ;
Templates and tests are as follows :
class Arbiter extends Module {
val io = IO(new Bundle {
// FIFO
val fifo_valid = Input(Bool())
val fifo_ready = Output(Bool())
val fifo_data = Input(UInt(16.W))
// PE0
val pe0_valid = Output(Bool())
val pe0_ready = Input(Bool())
val pe0_data = Output(UInt(16.W))
// PE1
val pe1_valid = Output(Bool())
val pe1_ready = Input(Bool())
val pe1_data = Output(UInt(16.W))
})
/* Fill in the corresponding code here */
}
test(new Arbiter) {
c =>
import scala.util.Random
val data = Random.nextInt(65536)
c.io.fifo_data.poke(data.U)
for (i <- 0 until 8) {
c.io.fifo_valid.poke((((i >> 0) % 2) != 0).B)
c.io.pe0_ready.poke((((i >> 1) % 2) != 0).B)
c.io.pe1_ready.poke((((i >> 2) % 2) != 0).B)
c.io.fifo_ready.expect((i > 1).B)
c.io.pe0_valid.expect((i == 3 || i == 7).B)
c.io.pe1_valid.expect((i == 5).B)
if (i == 3 || i ==7) {
c.io.pe0_data.expect((data).U)
} else if (i == 5) {
c.io.pe1_data.expect((data).U)
}
}
}
println("SUCCESS!!")
Observe :
- From FIFO Two inputs of , Namely FIFO Effective and FIFO The data of , One to FIFO The output of informs that it is ready ;
- To PE There are two outputs , It's data and information PE Is the data valid , One PE The input of tells whether it is ready ;
Then our ideas are as follows :
- Passing signal
pe0_ready
andpe1_ready
determinefifo_ready
( Are there any free PE); - If FIFO The data is valid ,PE0 If you are ready, let PE0 On ( Set up pe0_valid);
- If FIFO The data is valid , And PE0 Not ready ,PE1 Be on it , Then let's PE1 On ( Set up pe1_valid);
- At the same time, provide data , But only set pe_valid Will be useful ;
The answer is as follows :
import chisel3._
import chisel3.util._
class Arbiter extends Module {
val io = IO(new Bundle {
// FIFO
val fifo_valid = Input(Bool())
val fifo_ready = Output(Bool())
val fifo_data = Input(UInt(16.W))
// PE0
val pe0_valid = Output(Bool())
val pe0_ready = Input(Bool())
val pe0_data = Output(UInt(16.W))
// PE1
val pe1_valid = Output(Bool())
val pe1_ready = Input(Bool())
val pe1_data = Output(UInt(16.W))
})
io.fifo_ready := io.pe0_ready || io.pe1_ready
io.pe0_valid := io.fifo_valid & io.pe0_ready
io.pe1_valid := io.fifo_valid & io.pe1_ready & !io.pe0_ready
io.pe0_data := io.fifo_data
io.pe1_data := io.fifo_data
}
object Arbiter extends App {
println(getVerilogString(new Arbiter()))
}
Output is as follows :
module Arbiter(
input clock,
input reset,
input io_fifo_valid,
output io_fifo_ready,
input [15:0] io_fifo_data,
output io_pe0_valid,
input io_pe0_ready,
output [15:0] io_pe0_data,
output io_pe1_valid,
input io_pe1_ready,
output [15:0] io_pe1_data
);
assign io_fifo_ready = io_pe0_ready | io_pe1_ready; // @[MyModule.scala 22:33]
assign io_pe0_valid = io_fifo_valid & io_pe0_ready; // @[MyModule.scala 23:33]
assign io_pe0_data = io_fifo_data; // @[MyModule.scala 25:15]
assign io_pe1_valid = io_fifo_valid & io_pe1_ready & ~io_pe0_ready; // @[MyModule.scala 24:48]
assign io_pe1_data = io_fifo_data; // @[MyModule.scala 26:15]
endmodule
The test passed .
Parametric adder
This part of the exercise will reflect Chisel Powerful features —— Parameterization capability .
Here, it is required to construct a parameterized adder , It can be saturated when overflow occurs , You can also get stage results . For example, for 4bit The integer addition of ,15+15 You can get 15, You can also get 14, It depends on the parameters given .
The template is as follows :
class ParameterizedAdder(saturate: Boolean) extends Module {
val io = IO(new Bundle {
val in_a = Input(UInt(4.W))
val in_b = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
/* Fill in the corresponding code here */
}
for (saturate <- Seq(true, false)) {
test(new ParameterizedAdder(saturate)) {
c =>
// 100 random tests
val cycles = 100
import scala.util.Random
import scala.math.min
for (i <- 0 until cycles) {
val in_a = Random.nextInt(16)
val in_b = Random.nextInt(16)
c.io.in_a.poke(in_a.U)
c.io.in_b.poke(in_b.U)
if (saturate) {
c.io.out.expect(min(in_a + in_b, 15).U)
} else {
c.io.out.expect(((in_a + in_b) % 16).U)
}
}
// ensure we test saturation vs. truncation
c.io.in_a.poke(15.U)
c.io.in_b.poke(15.U)
if (saturate) {
c.io.out.expect(15.U)
} else {
c.io.out.expect(14.U)
}
}
}
println("SUCCESS!!")
Observe the template above , The parameter passed in is called saturate
, The type is Scala Boolean in , instead of Chisel Boolean in . So what we want to create here is not a saturated and truncated hardware , It's a generator , Or generate a saturation adder , Or generate a truncated adder , This is determined at the time of compilation .
Then it should be noted that the input and output are 4bit Of UInt
,Chisel There is built-in width reasoning , according to cheatsheet It says , The bit width of the conventional addition result is equal to the widest of the two inputs , In other words, the calculation of the project can only get one 4bit Connection of :
val sum = io.in_a + io.in_b
In order to check whether the results need to be saturated , You need to put the addition result into a 5bit In the connection of .
according to cheatsheet Description of , have access to +&
The operator :
val sum = io.in_a +& io.in_b
Last , If you put one 4bit Of UInt
Connect to a 5bit Of UInt
, That will automatically truncate the most significant bit , Using this feature, we can easily get the truncation result for the unsaturated adder .
The answer is as follows :
import chisel3._
import chisel3.util._
class ParameterizedAdder(saturate: Boolean) extends Module {
val io = IO(new Bundle {
val in_a = Input(UInt(4.W))
val in_b = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val sum = io.in_a +& io.in_b
if (saturate) {
io.out := Mux(sum > 15.U, 15.U, sum)
} else {
io.out := sum
}
}
object ParameterizedAdder extends App {
println(getVerilogString(new ParameterizedAdder(true)))
println(getVerilogString(new ParameterizedAdder(false)))
}
Generated Verilog as follows :
// saturation
module ParameterizedAdder(
input clock,
input reset,
input [3:0] io_in_a,
input [3:0] io_in_b,
output [3:0] io_out
);
assign io_out = 4'hf; // @[MyModule.scala 13:12]
endmodule
// truncation
module ParameterizedAdder(
input clock,
input reset,
input [3:0] io_in_a,
input [3:0] io_in_b,
output [3:0] io_out
);
wire [4:0] sum = io_in_a + io_in_b; // @[MyModule.scala 11:21]
assign io_out = sum[3:0]; // @[MyModule.scala 15:12]
endmodule
The test passed .
Chisel3 Cheat Sheet
The last attached Chisel3 Of Cheat Sheet:
边栏推荐
- SAP HR family member information
- Wechat applet development beginner 1
- SAP HR奖罚信息导出
- ASP. Net query implementation
- Windows set redis to start automatically
- How to login and enable synchronization function in Google browser
- Anxinco esp32-a1s development board is adapted to Baidu dueros routine to realize online voice function
- Chisel tutorial - 02 Chisel environment configuration and implementation and testing of the first chisel module
- 【LeetCode】20、有效的括号
- Anxinco EC series modules are connected to the multi protocol access products of onenet Internet of things open platform
猜你喜欢
Lm12 rolling heikin Ashi double K-line filter
[experiment sharing] log in to Cisco devices through the console port
As a new force, chenglian premium products was initially injected, and the shares of relevant listed companies rose 150% in response
Pycharm basic settings latest version 2022
【路径规划】使用垂距限值法与贝塞尔优化A星路径
ping报错:未知的名称或服务
C cat and dog
Solution of intelligent supply chain collaboration platform in electronic equipment industry: solve inefficiency and enable digital upgrading of industry
Balanced binary tree [AVL tree] - insert, delete
The efficient s2b2c e-commerce system helps electronic material enterprises improve their adaptability in this way
随机推荐
As a new force, chenglian premium products was initially injected, and the shares of relevant listed companies rose 150% in response
Pycharm basic settings latest version 2022
ASP. Net query implementation
Navicat connects Oracle
【LeetCode】20、有效的括号
Summary of SQL single table query 2020.7.27
MongoDB快速入门
May day d-light
StringUtils工具类
C method question 2
一份假Offer如何盗走了「Axie infinity」5.4亿美元?
SAP HR labor contract information 0016
[untitled]
Class C design questions
Benchmarking Detection Transfer Learning with Vision Transformers(2021-11)
redis缓存工具类,值得拥有~
P1055 [noip2008 popularization group] ISBN number
New potential energy of industrial integration, Xiamen station of city chain technology digital summit successfully held
C - Fibonacci sequence again
C language greedy snake