一个简单的ALU代码如下:
package simple
import chisel3._
import chisel3.util._
/**
* This is a very basic ALU example.
*/
class Alu extends Module {
val io = IO(new Bundle {
val fn = Input(UInt(2.W))
val a = Input(UInt(4.W))
val b = Input(UInt(4.W))
val result = Output(UInt(4.W))
})
// Use shorter variable names
val fn = io.fn
val a = io.a
val b = io.b
val result = Wire(UInt(4.W)) //这样只是组合电路,用assign表示
// val result = RegInit(0.U(4.W)) //这样会生成了reg,并且会产生clock和reset引脚(reset)同步高电平复位。
// some default value is needed
result := 0.U
// The ALU selection
switch(fn) {
is(0.U) { result := a + b }
is(1.U) { result := a - b }
is(2.U) { result := a | b }
is(3.U) { result := a & b }
}
// Output on the LEDs
io.result := result
}
/**
* A top level to wire FPGA buttons and LEDs
* to the ALU input and output.
*/
class AluTop extends Module { //这是另外一个模块调用了这alu模块
val io = IO(new Bundle {
val sw = Input(UInt(10.W))
val led = Output(UInt(10.W))
})
val alu = Module(new Alu()) // 实例化一个Alu模块。
// Map switches to the ALU input ports
alu.io.fn := io.sw(1, 0)
alu.io.a := io.sw(5, 2)
alu.io.b := io.sw(9, 6)
// And the result to the LEDs (with 0 extension)
io.led := alu.io.result
}
// Generate the Verilog code by invoking the Driver
object AluMain extends App { //最后在makefile里面要产生的是AluMain这个顶层。
println("Generating the ALU hardware")
chisel3.Driver.execute(Array("--target-dir", "generated"), () => new AluTop())
}
在目录里运行命令行make AluMain,在generated目录产生生成的verilog代码如下:
最后生成的代码如下:
module Alu( // @[:@3.2]
input [1:0] io_fn, // @[:@6.4]
input [3:0] io_a, // @[:@6.4]
input [3:0] io_b, // @[:@6.4]
output [3:0] io_result // @[:@6.4]
);
wire _T_16; // @[Conditional.scala 37:30:@10.4]
wire [4:0] _T_17; // @[anyname.scala 38:27:@12.6]
wire [3:0] _T_18; // @[anyname.scala 38:27:@13.6]
wire _T_20; // @[Conditional.scala 37:30:@17.6]
wire [4:0] _T_21; // @[anyname.scala 39:27:@19.8]
wire [4:0] _T_22; // @[anyname.scala 39:27:@20.8]
wire [3:0] _T_23; // @[anyname.scala 39:27:@21.8]
wire _T_25; // @[Conditional.scala 37:30:@25.8]
wire [3:0] _T_26; // @[anyname.scala 40:27:@27.10]
wire _T_28; // @[Conditional.scala 37:30:@31.10]
wire [3:0] _T_29; // @[anyname.scala 41:27:@33.12]
wire [3:0] _GEN_0; // @[Conditional.scala 39:67:@32.10]
wire [3:0] _GEN_1; // @[Conditional.scala 39:67:@26.8]
wire [3:0] _GEN_2; // @[Conditional.scala 39:67:@18.6]
assign _T_16 = 2'h0 == io_fn; // @[Conditional.scala 37:30:@10.4]
assign _T_17 = io_a + io_b; // @[anyname.scala 38:27:@12.6]
assign _T_18 = _T_17[3:0]; // @[anyname.scala 38:27:@13.6]
assign _T_20 = 2'h1 == io_fn; // @[Conditional.scala 37:30:@17.6]
assign _T_21 = io_a - io_b; // @[anyname.scala 39:27:@19.8]
assign _T_22 = $unsigned(_T_21); // @[anyname.scala 39:27:@20.8]
assign _T_23 = _T_22[3:0]; // @[anyname.scala 39:27:@21.8]
assign _T_25 = 2'h2 == io_fn; // @[Conditional.scala 37:30:@25.8]
assign _T_26 = io_a | io_b; // @[anyname.scala 40:27:@27.10]
assign _T_28 = 2'h3 == io_fn; // @[Conditional.scala 37:30:@31.10]
assign _T_29 = io_a & io_b; // @[anyname.scala 41:27:@33.12]
assign _GEN_0 = _T_28 ? _T_29 : 4'h0; // @[Conditional.scala 39:67:@32.10]
assign _GEN_1 = _T_25 ? _T_26 : _GEN_0; // @[Conditional.scala 39:67:@26.8]
assign _GEN_2 = _T_20 ? _T_23 : _GEN_1; // @[Conditional.scala 39:67:@18.6]
assign io_result = _T_16 ? _T_18 : _GEN_2; // @[anyname.scala 45:13:@36.4]
endmodule
module AluTop( // @[:@38.2]
input clock, // @[:@39.4]
input reset, // @[:@40.4]
input [9:0] io_sw, // @[:@41.4]
output [9:0] io_led // @[:@41.4]
);
wire [1:0] alu_io_fn; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_a; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_b; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_result; // @[anyname.scala 58:19:@43.4]
Alu alu ( // @[anyname.scala 58:19:@43.4]
.io_fn(alu_io_fn),
.io_a(alu_io_a),
.io_b(alu_io_b),
.io_result(alu_io_result)
);
assign io_led = {{6'd0}, alu_io_result}; // @[anyname.scala 66:10:@52.4]
assign alu_io_fn = io_sw[1:0]; // @[anyname.scala 61:13:@47.4]
assign alu_io_a = io_sw[5:2]; // @[anyname.scala 62:12:@49.4]
assign alu_io_b = io_sw[9:6]; // @[anyname.scala 63:12:@51.4]
endmodule
如果我们定义第一个文件中result类型是寄存器类型的,如下
result = RegInit(0.U(4.W))
将会产生如下Verilog代码:
module Alu( // @[:@3.2]
input clock, // @[:@4.4]
input reset, // @[:@5.4]
input [1:0] io_fn, // @[:@6.4]
input [3:0] io_a, // @[:@6.4]
input [3:0] io_b, // @[:@6.4]
output [3:0] io_result // @[:@6.4]
);
reg [3:0] result; // @[anyname.scala 32:25:@8.4]
reg [31:0] _RAND_0;
wire _T_17; // @[Conditional.scala 37:30:@10.4]
wire [4:0] _T_18; // @[anyname.scala 38:27:@12.6]
wire [3:0] _T_19; // @[anyname.scala 38:27:@13.6]
wire _T_21; // @[Conditional.scala 37:30:@17.6]
wire [4:0] _T_22; // @[anyname.scala 39:27:@19.8]
wire [4:0] _T_23; // @[anyname.scala 39:27:@20.8]
wire [3:0] _T_24; // @[anyname.scala 39:27:@21.8]
wire _T_26; // @[Conditional.scala 37:30:@25.8]
wire [3:0] _T_27; // @[anyname.scala 40:27:@27.10]
wire _T_29; // @[Conditional.scala 37:30:@31.10]
wire [3:0] _T_30; // @[anyname.scala 41:27:@33.12]
wire [3:0] _GEN_0; // @[Conditional.scala 39:67:@32.10]
wire [3:0] _GEN_1; // @[Conditional.scala 39:67:@26.8]
wire [3:0] _GEN_2; // @[Conditional.scala 39:67:@18.6]
wire [3:0] _GEN_3; // @[Conditional.scala 40:58:@11.4]
assign _T_17 = 2'h0 == io_fn; // @[Conditional.scala 37:30:@10.4]
assign _T_18 = io_a + io_b; // @[anyname.scala 38:27:@12.6]
assign _T_19 = _T_18[3:0]; // @[anyname.scala 38:27:@13.6]
assign _T_21 = 2'h1 == io_fn; // @[Conditional.scala 37:30:@17.6]
assign _T_22 = io_a - io_b; // @[anyname.scala 39:27:@19.8]
assign _T_23 = $unsigned(_T_22); // @[anyname.scala 39:27:@20.8]
assign _T_24 = _T_23[3:0]; // @[anyname.scala 39:27:@21.8]
assign _T_26 = 2'h2 == io_fn; // @[Conditional.scala 37:30:@25.8]
assign _T_27 = io_a | io_b; // @[anyname.scala 40:27:@27.10]
assign _T_29 = 2'h3 == io_fn; // @[Conditional.scala 37:30:@31.10]
assign _T_30 = io_a & io_b; // @[anyname.scala 41:27:@33.12]
assign _GEN_0 = _T_29 ? _T_30 : 4'h0; // @[Conditional.scala 39:67:@32.10]
assign _GEN_1 = _T_26 ? _T_27 : _GEN_0; // @[Conditional.scala 39:67:@26.8]
assign _GEN_2 = _T_21 ? _T_24 : _GEN_1; // @[Conditional.scala 39:67:@18.6]
assign _GEN_3 = _T_17 ? _T_19 : _GEN_2; // @[Conditional.scala 40:58:@11.4]
assign io_result = result; // @[anyname.scala 45:13:@36.4]
`ifdef RANDOMIZE_GARBAGE_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_INVALID_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_REG_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE
integer initvar;
initial begin
`ifndef verilator
#0.002 begin end
`endif
`ifdef RANDOMIZE_REG_INIT
_RAND_0 = {1{$random}};
result = _RAND_0[3:0];
`endif // RANDOMIZE_REG_INIT
end
`endif // RANDOMIZE
always @(posedge clock) begin
if (reset) begin
result <= 4'h0;
end else begin
if (_T_17) begin
result <= _T_19;
end else begin
if (_T_21) begin
result <= _T_24;
end else begin
if (_T_26) begin
result <= _T_27;
end else begin
if (_T_29) begin
result <= _T_30;
end else begin
result <= 4'h0;
end
end
end
end
end
end
endmodule
module AluTop( // @[:@38.2]
input clock, // @[:@39.4]
input reset, // @[:@40.4]
input [9:0] io_sw, // @[:@41.4]
output [9:0] io_led // @[:@41.4]
);
wire alu_clock; // @[anyname.scala 58:19:@43.4]
wire alu_reset; // @[anyname.scala 58:19:@43.4]
wire [1:0] alu_io_fn; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_a; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_b; // @[anyname.scala 58:19:@43.4]
wire [3:0] alu_io_result; // @[anyname.scala 58:19:@43.4]
Alu alu ( // @[anyname.scala 58:19:@43.4]
.clock(alu_clock),
.reset(alu_reset),
.io_fn(alu_io_fn),
.io_a(alu_io_a),
.io_b(alu_io_b),
.io_result(alu_io_result)
);
assign io_led = {{6'd0}, alu_io_result}; // @[anyname.scala 66:10:@52.4]
assign alu_clock = clock; // @[:@44.4]
assign alu_reset = reset; // @[:@45.4]
assign alu_io_fn = io_sw[1:0]; // @[anyname.scala 61:13:@47.4]
assign alu_io_a = io_sw[5:2]; // @[anyname.scala 62:12:@49.4]
assign alu_io_b = io_sw[9:6]; // @[anyname.scala 63:12:@51.4]
endmodule
这里我们看result是寄存器类型的了,并且模块自动补充了clock和reset信号。这里我们生成的代码中if语句似乎有了优先级,但是实际上看都是对一个fn变量(信号)的各种情况的判断,所以综合工具不会产生实际的带有优先级的选择电路实现。
继续进行,make AluTester
看到运行正常,我们看看AluTester的代码实现:
package simple
import chisel3._
import chisel3.iotesters.PeekPokeTester
/**
* Test the Alu design
*/
class AluTester(dut: Alu) extends PeekPokeTester(dut) {
// This is exhaustive testing, which usually is not possible
for (a <- 0 to 15) {
for (b <- 0 to 15) {
for (op <- 0 to 3) {
val result =
op match {
case 0 => a + b
case 1 => a - b
case 2 => a | b
case 3 => a & b
}
val resMask = result & 0x0f
poke(dut.io.fn, op)
poke(dut.io.a, a)
poke(dut.io.b, b)
step(1)
expect(dut.io.result, resMask)
}
}
}
}
object AluTester extends App {
println("Testing the ALU")
iotesters.Driver.execute(Array[String](), () => new Alu()) {
c => new AluTester(c)
}
}
peek是偷窥,poke是探针的意思。这里就实际上测试平台,测试这那个Alu模块。其实相当于verilog里面的实例化instance.
这里给了两个操作数a,b以及操作类型op,并且测试平台算出一个结果,被测试模块测试出一个结果,两者用expect语句进行对比,在界面上显示出结果。
这里三层循环总共执行了16*16*4=1024次,所以界面报告执行了1024次。
这里有个step暂时不知道什么意思,估计是跟周期延迟相关吧,之后可能就遇到搞明白了。
从这个例子看这种test的意义不是很大,因为被检测模块和检测模块的执行代码都是一样的,同时对错,可信度不高。但是这里给出了我们test的编写套路,我们可以按照这个方式自己发挥。
在学习这些chisel代码时候,发现scala语言(chesel就是基于scala语言写的)几乎精简到无法精简:变量用到的时候当即声明就好,语句之间也不需要分号... 佩服大神们选择scala实现。
这代码里面还有scala种选择的实现,以及分支的实现,等一些语法,都值得好好学学。
另外我们看makefile文件:
SBT = sbt
# Generate Verilog code
AluMain:
$(SBT) "runMain simple.AluMain"
# Generate the C++ simulation and run the tests
AluTester:
$(SBT) "test:runMain simple.AluTester"
runMain simple.AluMain是生成verilog的命令,搜索路径是src\main\scala\simple,搜索这个目录下的所有.scala文件找到一个AluMain对象的实现。也就是说这个对象所在文件的文件名只要后缀是.scala就好,具体什么文件名无所谓。
同样 test:runMain simple.AluTester 是启动test运行的命令,搜索路径是src\test\scala\simple这个目录所有.scala文件中的AluTester这个对象并编译运行显示结果。
我所实验的文件可以从以下地址下载:
链接:https://pan.baidu.com/s/1apGyq0A8_UCgncJOTULWTw
提取码:j354
有兴趣可以加入QQ群进行讨论学习29304866
有问题或者想法可以在此留言