Kika's
Blog
摄于 图片简介 | CC BY 4.0 | 换一张

Chisel Queue详解

2026-02-12 16 views

总述

Chisel封装了一个非常强大的Queue,同时还建立了一个非常好的抽象模型,了解使用这个玩意可以有助于我们理解很多种类的缓冲。

Queue(io.in, entries = 4, pipe = false, flow = false)

参数介绍如下:

  • entries (Int):队列的深度(容量)。
  • pipe (Boolean):默认为false,是否允许 valid 信号的组合逻辑穿透,可以理解为“当队列为空时,可直接跳过整个队列”
    • pipe = true
      • 如果队列是空的,输入端的 valid 会直接组合连接到输出端的 valid。
      • 效果:数据可以实现 0 周期延迟(即当拍进入,当拍流出)。
      • 代价:会增加 valid 路径上的组合逻辑深度。没有切断时序路径。
    • pipe = false:
      • 数据必须先存入寄存器,下一拍才能流出。
      • 效果:最小 1 周期延迟。
      • 优势:更好的时序(打断了 valid 路径),此时输出永远来自寄存器,切断了时序路径。
  • flow (Boolean):默认值:false,是否允许 ready 和数据的组合逻辑穿透。
    • flow = true:
      • 即使队列满了,如果当拍下游读走了一个数据(deq.ready 为高),上游可以在当拍写入一个数据(enq.ready 保持高)。
      • 效果:允许流式传输(Flow-through),满载时吞吐量更高。
      • 代价:形成 ready 信号的长组合逻辑链。

下面介绍使用Queue实现的常见电路。

Skid Buffer

或叫Double BufferPing-Pong Buffer之前介绍Double Buffer的博客详细分析过。

核心特性是:深度为 2,并且在输入 ready 和输出 valid/bits 上都切断了组合逻辑路径(全寄存器输出),同时维持 100% 的吞吐量。

val skid_buffer = Queue(io.in, entries = 2, pipe = false, flow = false)

这里pipe=false,flow = false都很好理解,都是为了切断组合逻辑路径。entries=2是为了保持原来的100%吞吐量,否则深度为1的buffer只能“读一拍、写一拍”(状态“满-空-满-空”),导致吞吐量只有 50%。

流水线反压寄存器

首先明确什么是“反压”:在数字逻辑流水线(Pipeline)中,数据像水一样从上游流向下游。当“排水口”(消费者)堵塞或者处理不过来时,压力会沿着管道逆向传导回“进水口”(生产者),迫使上游停止发送数据,这个压力就是所谓的反压。

如果我们使用下面的Queue创建流水线寄存器,由于pipe=false,flow = false隔离了组合逻辑,这个流水线的时序会相当好。但是其吞吐量变成原来的50%,原理即上一篇Double Buffer博客中讲述的“读一拍、写一拍”(状态“满-空-满-空”)。

Queue(io.in, entries = 1, pipe = false, flow = false)

而如果我们使用下面的Queue创建流水线寄存器,我们使用flow = true牺牲了ready信号链路的组合逻辑时序(每一级流水线的ready的信号都连着后续流水线所有ready信号,形成一条较长的组合逻辑链),但换取了100%的吞吐量。这个做法是CPU流水线寄存器常用的设计。

Queue(io.in, entries = 1, pipe = false, flow = true)