吃透Chisel语言.17.Chisel模块详解(四)——用函数实现轻量级模块

本文介绍了如何使用Chisel中的函数来实现轻量级模块,以简化组合电路和有状态电路的定义,提高代码效率。通过示例展示了如何用函数创建加法器和延迟模块,并对比了使用传统模块方式的复杂性。强调了合理利用Chisel内置函数以避免重复造轮子的重要性。
摘要由CSDN通过智能技术生成

Chisel模块详解(四)——用函数实现轻量级模块

前面三篇文章我们学习了模块的实现语法、模块嵌套以及模块之间的连接方法,一般来说模块是Chisel中构造硬件描述的基本方法。但是Chisel中的模块和Verilog这类语言一样,定义模块会需要很多样板代码,又是从Module拓展,又是定义IO接口的,实例化和连接的时候又很费劲。不过Chisel中还是有简单的方法来定义轻量级模块的,那就是使用函数来实现。这一篇文章就讲讲如何用函数实现轻量级模块。

函数实现轻量级组合电路

Scala函数可以接受Chisel类型的参数并返回生成的硬件,举一个简单的例子,还是加法器,用Chisel的模块来实现是这样的:

class adder extends Module {
    val io = IO(new Bundle {
        val x = Input(UInt(32.W))
        val y = Input(UInt(32.W))
        val z = Output(UInt(32.W))
    })
    z := x + y
}

然后使用adder模块的使用就用new实例化,然后用Module封装,再把它的输入输出和要使用adder的信号连接起来,比如:

class testModule extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt(32.W))
        val b = Input(UInt(32.W))
        val out = Output(UInt(32.W))
    })
    val adder = Module(new adder())
    adder.io.x := io.a
    adder.io.y := io.b
    io.out := adder.io.z
}

生成的Verilog代码如下:

module adder(
  input  [31:0] io_x,
  input  [31:0] io_y,
  output [31:0] io_z
);
  assign io_z = io_x + io_y; // @[hello.scala 13:18]
endmodule
module test(
  input         clock,
  input         reset,
  input  [31:0] io_a,
  input  [31:0] io_b,
  output [31:0] io_out
);
  wire [31:0] adder_io_x; // @[hello.scala 22:23]
  wire [31:0] adder_io_y; // @[hello.scala 22:23]
  wire [31:0] adder_io_z; // @[hello.scala 22:23]
  adder adder ( // @[hello.scala 22:23]
    .io_x(adder_io_x),
    .io_y(adder_io_y),
    .io_z(adder_io_z)
  );
  assign io_out = adder_io_z; // @[hello.scala 25:12]
  assign adder_io_x = io_a; // @[hello.scala 23:16]
  assign adder_io_y = io_b; // @[hello.scala 24:16]
endmodule

其实是有点麻烦的,而且就这还整了个模块嵌套,其实我们可以用函数来生成一个adder

def adder (x: UInt, y: UInt) = {
    x + y
}

这个函数的参数是两个Chisel的UInt,返回值是x + y,现在可以这么生成并使用加法器:

class testModule extends Module {
    def adder (x: UInt, y: UInt) = {
        x + y
    }

    val io = IO(new Bundle {
        val a = Input(UInt(32.W))
        val b = Input(UInt(32.W))
        val out = Output(UInt(32.W))
    })

    val out = adder(io.a, io.b)
    io.out := out
}

生成的Verilog代码如下:

module testModule(
  input         clock,
  input         reset,
  input  [31:0] io_a,
  input  [31:0] io_b,
  output [31:0] io_out
);
  assign io_out = io_a + io_b; // @[hello.scala 18:11]
endmodule

非常简洁,没有模块的嵌套,毕竟加法器这种组件还用个模块有点太奢侈的。

这就是轻量级模块,而adder函数就是个硬件生成器。在展开的时候这个函数不会执行加法操作,而是创建了一个加法器的硬件实例。当然了,这里加法器的例子是刻意选取的简单例子,Chisel已经定义了加法器函数,也就是+(that:UInt)运算符。

函数实现轻量级有状态电路

函数作为轻量级的硬件生成器,还可以包含状态,比如寄存器。下面的例子会返回一个单周期延迟模块(也就是寄存器):

def delay(x : UInt) = RegNext(x)

如果函数体只有一行的话,我们可以省略大括号,然后和函数写到一行里面。

我们现在可以通过以这个函数为参数调用这个函数构造一个二周期延迟模块:

class testModule extends Module {
    def delay(x : UInt) = RegNext(x)

    val io = IO(new Bundle {
        val delIn = Input(UInt(32.W))
        val delOut = Output(UInt(32.W))
    })
	// 调用两次delay
    io.delOut := delay(delay(io.delIn))
}

生成的Verilog代码如下:

module testModule(
  input         clock,
  input         reset,
  input  [31:0] io_delIn,
  output [31:0] io_delOut
);
  reg [31:0] io_delOut_REG; // @[hello.scala 17:34]
  reg [31:0] io_delOut_REG_1; // @[hello.scala 17:34]
  assign io_delOut = io_delOut_REG_1; // @[hello.scala 24:15]
  always @(posedge clock) begin
    io_delOut_REG <= io_delIn; // @[hello.scala 17:34]
    io_delOut_REG_1 <= io_delOut_REG; // @[hello.scala 17:34]
  end
endmodule

可以看到,构造的模块中有两个寄存器,输入会在另两个周期后变成输出。不过这个例子跟加法器的例子一样的,RegNext()也是Chisel中已经有了的函数,这里直接调用RegNext()替代delay是一样的。

上面的两个例子中,函数都是在Module内部定义的,但是通常这种模块都会在不同的模块中用到,所以更好的办法是把这类实用函数都放到一个Scala对象里面,不管在哪个模块都可以调用。

结语

这一篇文章讲述的函数实现轻量级模块内容很简单,用好可以帮我们节约很多时间,在Scala对象中实现所有实用函数可以方便所有模块调用,大幅度提升编码效率。不过首先还是用好Chisel提供了的函数,避免重复造轮子,也方便Chisel编译器优化生成的硬件电路。除了Chisel中我们写的模块,我们可能还需要在我们的工程中使用现有的IP,而这些IP通常是用Verilog来写的,我们该如何使用这些IP呢?下一篇文章就详细说说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值