您当前的位置: 首页 >  ar

耐心的小黑

暂无认证

  • 2浏览

    0关注

    323博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

chisel 仲裁器Arbiter和队列Queue(ready-valid接口)

耐心的小黑 发布时间:2021-07-19 10:06:20 ,浏览量:2

一、ready-valid接口

ArbiterQueue都使用了ready-valid接口,该类型的端口在单一数据信号的基础上又添加了ready和valid信号以使用ready-valid握手协议。它包含3个信号:

  • ready:高有效时表示数据接收者consumer已经准备好接收信号,由consumer驱动。
  • valid:高有效时表示数据生产者producer已经准备好待发送的数据了,由producer驱动。
  • bits:是要在producer与consumer之间传输的数据。

需要注意的是,valid和ready信号之间不能存在组合逻辑关系,valid信号应该只依赖于此时的源数据是否有效,ready信号应该只依赖于此时的数据接收者是否准备好接收数据了。当在某个时钟周期,valid和ready同时有效时,数据被视为传输。

创建ready-valid接口很简单,使用单例对象Decoupled即可创建,有以下两种形式:

  • Decoupled(...):可以传入任意的数据类型,然后返回一个ready-valid接口,此时ready是input信号,valid和bits都是output信号。因此它是属于数据生产者producer的端口。

  • Flipped(Decoupled(...)):Flipped()会将ready-valid接口的信号方向进行取反,因此此时ready是output信号,valid和bits都是input信号。因此它是属于数据接收者consumer的端口。

数据接收者和发送者都是相对的,一定要根据具体的情况正确设置信号方向。 在这里插入图片描述

二、仲裁器Arbiter

Chisel内建了两种仲裁器,一种是优先仲裁器,另一种是循环仲裁器。

  • 优先仲裁器的输入通道的优先级是固定的,每次都是选择多个有效通道中优先级最高的。
  • 而循环仲裁器每次都从不同的起点开始仲裁,采用轮询方式查看各个通道是否有请求,优先选择先查到的有效通道。由于起点是依次变化的,所以每个通道总体来说具有相同的优先级。

第一种仲裁器优先仲裁器Arbiter在chisel3.util包下面,只定义了Arbiter类,没有单例对象,所以每次都需要通过new来创建Arbiter对象。

创建Arbiter对象的方式如下所示:

new Arbiter(gen: T, n: Int)

需要提供两个参数,gen是传输的数据的类型,n是待仲裁对象的个数,也即数据发送者producer的个数。数据接收者consumer的个数为默认为1。

Arbiter内部使用ArbiterIO定义端口,而ArbiterIO内部又使用Decoupled()创建最终所需的ready-valid接口,定义如下:

class ArbiterIO[T 
    c.io.in(0).valid.poke(false.B)
    c.io.in(1).valid.poke(false.B)
    c.io.out.ready.poke(false.B)
    println(s"Start:")
    println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}")
    println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}")
    c.io.in(1).valid.poke(true.B)  // Valid input 1
    c.io.in(1).bits.poke(42.U)
    c.io.out.ready.poke(true.B)
    // What do you think the output will be?
    println(s"valid input 1:")
    println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}")
    println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}")
    c.io.in(0).valid.poke(true.B)  // Valid inputs 0 and 1
    c.io.in(0).bits.poke(43.U)
    // What do you think the output will be? Which inputs will be ready?
    println(s"valid inputs 0 and 1:")
    println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}")
    println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}")
    c.io.in(1).valid.poke(false.B)  // Valid input 0
    // What do you think the output will be?
    println(s"valid input 0:")
    println(s"\tin(0).ready=${c.io.in(0).ready.peek().litValue}, in(1).ready=${c.io.in(1).ready.peek().litValue}")
    println(s"\tout.valid=${c.io.out.valid.peek().litValue}, out.bits=${c.io.out.bits.peek().litValue}")
}

Elaborating design… Done elaborating. Start: in(0).ready=0, in(1).ready=0 out.valid=0, out.bits=0 valid input 1: in(0).ready=1, in(1).ready=1 out.valid=1, out.bits=42 valid inputs 0 and 1: in(0).ready=1, in(1).ready=0 out.valid=1, out.bits=43 valid input 0: in(0).ready=1, in(1).ready=0 out.valid=1, out.bits=43 test Helper_Anon Success: 0 tests passed in 2 cycles in 0.077434 seconds 25.83 Hz

没有什么需要特别说明的,因为它就是一个组合逻辑的模块。

第二种仲裁器循环仲裁器RRArbiter也在chisel3.util包下面,并且只定义了RRArbiter类,没有单例对象,所以每次都需要通过new来创建RRArbiter对象。它的创建与调用方式和Arbiter是一样的,只是内部实现的仲裁逻辑不同。

三、队列Queue

Chisel内建了队列Queue,它会创建一个使用ready-valid接口 的FIFO,在chisel3.util包下面既定义了Queue类,也定义了其单例对象,所以有两种创建Queue对象的方式。

Queue内部使用QueueIO定义端口,QueueIO最终仍然是使用Decoupled()创建所需的ready-valid接口,定义如下:

class QueueIO[T             
关注
打赏
1640088279
查看更多评论
0.0403s