前言
在这个部分我们将学习到如何用Chisel语言实现组合逻辑。这一节会介绍基础的Chisel数据类型:
- UInt - 无符号数
- SInt - 有符号数
- Bool - 布尔数
注意: 所有的Chisel变量在Scala中都应声明为val
,因为硬件一旦定义生成就不会再改变了。(想象电路图都画好了,逻辑不能再改,能改变的只有输入输出。)
常见的操作符
class MyModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
}
声明一个类别 MyModule为Module类。这个声明表示这段代码会被编译为verilog代码。
MyModule模块内有一个4个bit无符号数的输入和输出。
示例代码
- Example 1
class MyModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(UInt(4.W))
})
val two = 1 + 1
println(two)
val utwo = 1.U + 1.U
println(utwo)
io.out := io.in
}
println(getVerilog(new MyModule))
编译后的verilog代码:
module MyModule(
input clock,
input reset,
input [3:0] io_in,
output [3:0] io_out
);
assign io_out = io_in; // @[cmd2.sc 12:10]
endmodule
输出:
2
UInt<1>(OpResult in MyModule)
two 是Scala语言的两个为Int类别的数直接相加。因此输出也是Int类。
而utwo是num.U
, .U代表Scala int 数字到 unit Chisel数字的映射,这样的相加会被直接识别为硬件
操作点(我理解的是寄存器),所以会直接输出函数类别和指针号。
- Example 2
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
}
println(getVerilog(new MyOperators))
编译后的verilog代码:
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] _T_3 = 2'h2 - 2'h1; // @[cmd3.sc 10:21]
wire [4:0] _T_4 = 3'h4 * 2'h2; // @[cmd3.sc 11:21]
assign io_out_add = 4'h5; // @[cmd3.sc 9:21]
assign io_out_sub = {{2'd0}, _T_3}; // @[cmd3.sc 10:21]
assign io_out_mul = _T_4[3:0]; // @[cmd3.sc 11:14]
endmodule
这里我觉得有个可以讨论的点,就是为什么Chisel翻译后的verilog操作这么复杂。
为什么不直接把计算后的结果assign给Verilog。
我的理解是:
这里可能需要理解到wire和assign在verilog中的区别:wire和operator有关,它会生成相应的逻辑阵列,也可以说是电路图。而assign是直接把值赋给寄存器。
因此当Chisel的源码中存在有运算符时,Scala的逻辑可能就对应着wire,我尝试着直接赋值,果然wire就消失了。
- Example 3
认识Mux
和 Cat
class MyOperatorsTwo 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) // should return 3.U, since s is true
io.out_cat := Cat(2.U, 1.U) // concatenates 2 (b10) with 1 (b1) to give 5 (101)
}
println(getVerilog(new MyOperatorsTwo))
test(new MyOperatorsTwo) { c =>
c.io.out_mux.expect(3.U)
c.io.out_cat.expect(5.U)
}
println("SUCCESS!!")
Mux 类似于C语言的 条件运算符 ? Mux(s, a, b), s.B 为Chisel布尔数, 当s为True时, 输出a, 否则输出b。
Cat 类似于verilog的合并 {a,b},比如上面的Cat(2.U, 1.U)
就是把无符号数 2 和 1 串起来 {2‘b10 , 1’b1} = {3’b101} = 5
更多的内置函数可以在Chisel提供的Chisel cheatsheet上面看到,单击直接下裁。