吃透Chisel语言.14.Chisel模块详解(一)——初识Chisel模块(Module)并实现一个计数器

Chisel模块详解(一)——初识Chisel模块(Module)并实现一个计数器

比较大的数字电路设计都会构造为一组组件,而且这些组件是有层级地组织起来的。每个组件都有自己输入输出线网的接口,通常叫作端口(port),跟集成电路(IC)中的输入输出引脚是差不多东西。组件之间可以通过线网连接输入输出而连接到一起,组件内部也可以包含子组件来构造有层级的数字电路。对于有层级的组件,最外层的组件是直接和芯片上的物理针脚连接的,被称作为顶层组件(Top-Level Component)。

Chisel模块详解这一部分会讲解组件在Chisel中是怎么描述的、又是如何整合到一起的,还提供了一些简单的组件的例子。不过这一部分涉及到的模块还都比较小,比如一个简单的加法器,只是用来展示定义、实例化和连接组件的基本原理。实际编写Chisel代码的时候,要写的模块肯定代码量大管饱,不像加法器一样就那么一两行。这一篇文章首先介绍Chisel的基本组件,也就是模块(Module)。

利用Chisel中的Module实现一个计数器

Chisel中将硬件组件叫作模块(Module),每个模块都是从Module类拓展来的,包含一个用于定义接口的io字段。这个接口通过Bundle定义,然后通过调用IO()封装起来。Bundle前面讲过,它可以包含多个字段,这里就用于表示模块所有的输入输出接口。接口的方向通过调用Input()Output()封装一个字段为输入或输出来确定,而这个输入输出是相对于这个模块自身来说的。

我们先举一个例子,一个八位无符号数的加法器模块,下面是加法器模块的示意图:

在这里插入图片描述

可以看到,这个加法器模块有两个输入(ab),还有一个输出y。下面的代码给出了Chisel中这个加法器的定义:

class Adder extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt(8.W))
        val b = Input(UInt(8.W))
        val y = Output(UInt(8.W))
    })
    
    io.y := io.a + io.b
}

模块中对输入输出接口的访问通过.运算符完成,比如io.a,因为这些输入输出是io这个Bundle的成员。

再来一个简单的例子,是个八位的无符号整数寄存器,示意图如下:

在这里插入图片描述

输入只有一个d,输出是qd为下个时钟周期上升沿存到寄存器的值,q是当前寄存器存放的值。在Chisel中的实现如下:

class Register extends Module {
    val io = IO(new Bundle {
        val d = Input(UInt(8.W))
        val q = Output(UInt(8.W))
    })
    
    val reg = RegInit(0.U)
    io.q := reg
    reg := io.d
}

有了一个加法器,又有了一个寄存器,现在我们可以用它们构造一个计数器,计数从0到9,然后再从0开始计数,示意图如下:

在这里插入图片描述

看起来稍微有点复杂了,但稍微观察就可以发现,这个计数器就是由一个加法器、一个Mux和一个寄存器组成的。加法器用于累加1,Mux用于选择是否清0重置计数器,即选择输出加法器的计算结果还是输出0,寄存器用于保存当前计数器的值。那么在Chisel上实现如下:

class Count10 extends Module {
    val io = IO(new Bundle {
        val dout = Output(UInt(8.W))
    })
    
    // 利用写好的两个模块
    val add = Module(new Adder())
    val reg = Module(new Register())
    
    // 寄存器存放的为当前的计数
    val count = reg.io.q
    
    // 当前计数和1.U作为加法器的输入
    add.io.a := 1.U
    add.io.b := count
    val result = add.io.y
    
    // 如果累加达到了10.U,则计数器清零
    val next = Mux(result === 10.U, 0.U, result)
    reg.io.d := next
    
    io.dout := count
}

可以看到代码中,我们使用new关键字实例化了两个模块,然后封装为一个Module并给了它命名,即addreg。加法器的输入是1.Ucount,输出命名为result,如果result达到了10.u,就代表应该清零了,输出也应该是0,所以我们用Mux根据result的值是否为10.U来选择输出0.U还是result,也可以根据count的值是否为9.U来判断。最后我们把Mux的输出作为寄存器的输入,寄存器的输出作为Count10模块的输出io.dout。如此,一个Chisel计数器就实现了。

结语

这一篇文章以加法器、寄存器和计数器的例子说明了Chisel中模块的用法,不过只是给了个初步的印象。这一部分的后面几篇文章会讲解更进阶的模块的用法,比如嵌套的模块、Bulk连接等,并用他们实现一些稍微丰富的模块。最后还会专门用一篇文章来讲Chisel模块如何连接外部的模块,比如一个Verilog模块,这在使用IP的时候很有用,敬请期待。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值