CHISEL点滴积累 1

一个简单的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

有问题或者想法可以在此留言

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值