通用 FIFO 可以定义如下:
- 抽象数据类型
- 抽象FIFO深度
- 有握手信号的FIFO
import chisel3.util.log2Up
import chisel3._
// 数据类型
class DataBundle extends Bundle{
val a = UInt(32.W)
val b = UInt(32.W)
}
class Fifo[T <: Data](gen: T, n: Int) extends Module {
val io = IO(new Bundle{
val enqVal = Input(Bool())
val enqRdy = Output(Bool())
val deqVal = Output(Bool())
val deqRdy = Input(Bool())
val enqDat = Input(gen)
val deqDat = Output(gen)
})
val enqPtr = RegInit(0.U(log2Up(n).W)) // 栈尾
val deqPtr = RegInit(0.U(log2Up(n).W)) // 栈首
val isFull = RegInit(false.B) // 是否满了
val doEnq = io.enqRdy && io.enqVal // 需要执行入栈,入栈操作开启且入栈的元素有效
val doDeq = io.deqRdy && io.deqVal // 执行出栈
val isEmpty = !isFull && (enqPtr === deqPtr) // 栈空
val deqPtrInc = deqPtr + 1.U
val enqPtrInc = enqPtr + 1.U
// 判断接下来是否会满
val isFullNext = Mux(doEnq && !doDeq && (enqPtrInc === deqPtr), // 入栈,且不出栈,且栈接下会满
true.B , Mux(doDeq && isFull, // 要出栈,且满了
false.B, isFull))
enqPtr := Mux(doEnq, enqPtrInc, enqPtr) // 入栈,改变尾,向后加一个元素
deqPtr := Mux(doDeq, deqPtrInc, deqPtr) // 出栈,改变首,头向后移一个
isFull := isFullNext
val ram = Mem(n, gen)
when(doEnq){
ram(enqPtr) := io.enqDat
}
io.enqRdy := !isFull
io.deqVal := !isEmpty
ram(deqPtr) <> io.deqDat
}
// 具有 8 个 DataBundle 类型元素的 Fifo 实例化为:
println(getVerilog(new Fifo(new DataBundle, 8)))
然后可以将具有 8 个 DataBundle 类型元素的 Fifo 实例化为:
val fifo = Module(new Fifo(new DataBundle, 8))
也可以定义一个通用的解耦(就绪/有效)接口:
class DecoupledIO[T <: Data](data: T) extends Bundle {
val ready = Input(Bool())
val valid = Output(Bool())
val bits = Output(data)
}
然后可以使用此模板将握手协议添加到任何信号集:
class DecoupledDemo extends DecoupledIO(new DataBundle)
FIFO接口现在可以简化如下:
class Fifo2[T <: Data](gen: T, n: Int) extends Module {
val io = IO(new Bundle{
val enq = Flipped(new DecoupledIO(gen)) // Flipped是反转接口
val deq = new DecoupledIO(gen)
})
val enqPtr = RegInit(0.U(log2Up(n).W)) // 栈尾
val deqPtr = RegInit(0.U(log2Up(n).W)) // 栈首
val isFull = RegInit(false.B) // 是否满了
val doEnq = io.enq.ready && io.enq.valid // 需要执行入栈,入栈操作开启且入栈的元素有效
val doDeq = io.deq.ready && io.deq.valid // 执行出栈
val isEmpty = !isFull && (enqPtr === deqPtr) // 栈空
val deqPtrInc = deqPtr + 1.U
val enqPtrInc = enqPtr + 1.U
// 判断接下来是否会满
val isFullNext = Mux(doEnq && !doDeq && (enqPtrInc === deqPtr), // 入栈,且不出栈,且栈接下会满
true.B , Mux(doDeq && isFull, // 要出栈,且满了
false.B, isFull))
enqPtr := Mux(doEnq, enqPtrInc, enqPtr) // 入栈,改变尾,向后加一个元素
deqPtr := Mux(doDeq, deqPtrInc, deqPtr) // 出栈,改变首,头向后移一个
isFull := isFullNext
val ram = Mem(n, gen)
when(doEnq){
ram(enqPtr) := io.enq.bits
}
io.enq.ready := !isFull
io.deq.valid := !isEmpty
ram(deqPtr) <> io.deq.bits
}