From 16820210322ca52e112a5e9e943b8159fa31b027 Mon Sep 17 00:00:00 2001 From: unlsycn Date: Thu, 31 Oct 2024 06:51:48 +0000 Subject: [PATCH] [rtl] complete stdlib Queue and add "almost" ports Signed-off-by: unlsycn --- stdlib/src/Queue.scala | 139 +++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/stdlib/src/Queue.scala b/stdlib/src/Queue.scala index 329388b1a..7b17a93f1 100644 --- a/stdlib/src/Queue.scala +++ b/stdlib/src/Queue.scala @@ -7,12 +7,14 @@ import org.chipsalliance.dwbb.interface.DW_fifo_s1_sf.{Interface => DwbbFifoInte import chisel3.ltl.AssertProperty import chisel3.util.{Decoupled, DecoupledIO, DeqIO, EnqIO, ReadyValidIO} -class QueueIO[T <: Data](private val gen: T) extends Bundle { +class QueueIO[T <: Data](private val gen: T, entries: Int) extends Bundle { val enq = Flipped(EnqIO(gen)) val deq = Flipped(DeqIO(gen)) - val empty = Output(Bool()) - val full = Output(Bool()) + val empty = Output(Bool()) + val full = Output(Bool()) + val almostEmpty = if (entries >= 2) Some(Output(Bool())) else None + val almostFull = if (entries >= 2) Some(Output(Bool())) else None } object Queue { @@ -23,7 +25,6 @@ object Queue { flow: Boolean = false, resetMem: Boolean = false ): DecoupledIO[T] = { - val io = this.io(chiselTypeOf(enq.bits), entries, pipe, flow, resetMem) io.enq <> enq @@ -31,71 +32,101 @@ object Queue { } def io[T <: Data]( - gen: T, - entries: Int = 2, - pipe: Boolean = false, - flow: Boolean = false, - resetMem: Boolean = false + gen: T, + entries: Int = 2, + pipe: Boolean = false, + flow: Boolean = false, + resetMem: Boolean = false, + almostEmptyLevel: Int = 1, + almostFullLevel: Int = 1 ): QueueIO[T] = { - require(gen.getWidth <= 2048, "Data width must be less than 2048") - require(entries <= 1024, "Entries must be less than 1024") + require( + Range.inclusive(1, 2048).contains(gen.getWidth), + "Data width must be between 1 and 2048" + ) + require( + Range.inclusive(1, 1024).contains(entries), + "Entries must be between 1 and 1024" + ) - val clock = Module.clock - val reset = Module.reset + val io = Wire(new QueueIO(gen, entries)) - val io = Wire(new QueueIO(gen)) + if (entries == 1) { + val data = Reg(gen) + val empty = RegInit(true.B) + val full = !empty - if (entries < 2) { - val queue = Module(new chisel3.util.Queue(gen, entries, pipe, flow)) - queue.io.enq <> io.enq - queue.io.deq <> io.deq - io.empty := queue.io.count === 0.U - io.full := queue.io.count === entries.U + val push = io.enq.fire && (if (flow) !(empty && io.deq.ready) else true.B) + io.enq.ready := empty || (if (pipe) io.deq.ready else false.B) + data := Mux(push, io.enq.bits, data) - return io - } + val pop = io.deq.ready && full + io.deq.valid := full || (if (flow) io.enq.valid else false.B) + io.deq.bits := (if (flow) Mux(empty, io.enq.bits, data) else data) - // TODO: use sync reset for now and wait for t1 to migrate to FixedIOModule - // require(reset.typeName == "Bool" || reset.typeName == "AsyncReset") - val useAsyncReset = reset.typeName == "AsyncReset" - - val fifo = Instantiate( - new DwbbFifo( - new DwbbFifoParameter( - width = gen.getWidth, - depth = entries, - errMode = "unlatched", - rstMode = (useAsyncReset, resetMem) match { - case (false, false) => "sync_wo_mem" - case (false, true) => "sync_with_mem" - case (true, false) => "async_wo_mem" - case (true, true) => "async_with_mem" - } + empty := Mux(push =/= pop, pop, empty) + + io.empty := empty + io.full := full + } else { + require( + Range.inclusive(1, entries - 1).contains(almostEmptyLevel), + "almost empty level must be between 1 and entries-1" + ) + require( + Range.inclusive(1, entries - 1).contains(almostFullLevel), + "almost full level must be between 1 and entries-1" + ) + + val clock = Module.clock + val reset = Module.reset + + // TODO: use sync reset for now and wait for t1 to migrate to FixedIOModule + // require(reset.typeName == "Bool" || reset.typeName == "AsyncReset") + val useAsyncReset = reset.typeName == "AsyncReset" + + val fifo = Instantiate( + new DwbbFifo( + new DwbbFifoParameter( + width = gen.getWidth, + depth = entries, + aeLevel = almostEmptyLevel, + afLevel = almostFullLevel, + errMode = "unlatched", + rstMode = (useAsyncReset, resetMem) match { + case (false, false) => "sync_wo_mem" + case (false, true) => "sync_with_mem" + case (true, false) => "async_wo_mem" + case (true, true) => "async_with_mem" + } + ) ) ) - ) - val dataIn = io.enq.bits.asUInt - val dataOut = fifo.io.data_out.asTypeOf(io.deq.bits) + val dataIn = io.enq.bits.asUInt + val dataOut = fifo.io.data_out.asTypeOf(io.deq.bits) - fifo.io.clk := clock - fifo.io.rst_n := ~(reset.asBool) + fifo.io.clk := clock + fifo.io.rst_n := ~(reset.asBool) - fifo.io.diag_n := ~(false.B) + fifo.io.diag_n := ~(false.B) - io.enq.ready := !fifo.io.full || (if (pipe) io.deq.ready else false.B) - fifo.io.push_req_n := ~(io.enq.fire && (if (flow) !(fifo.io.empty && io.deq.ready) else true.B)) - fifo.io.data_in := dataIn + io.enq.ready := !fifo.io.full || (if (pipe) io.deq.ready else false.B) + fifo.io.push_req_n := ~(io.enq.fire && (if (flow) !(fifo.io.empty && io.deq.ready) else true.B)) + fifo.io.data_in := dataIn - io.deq.valid := !fifo.io.empty || (if (flow) io.enq.valid else false.B) - fifo.io.pop_req_n := ~(io.deq.ready && !fifo.io.empty) - io.deq.bits := (if (flow) Mux(fifo.io.empty, io.enq.bits, dataOut) else dataOut) + io.deq.valid := !fifo.io.empty || (if (flow) io.enq.valid else false.B) + fifo.io.pop_req_n := ~(io.deq.ready && !fifo.io.empty) + io.deq.bits := (if (flow) Mux(fifo.io.empty, io.enq.bits, dataOut) else dataOut) - io.empty := fifo.io.empty - io.full := fifo.io.full + io.empty := fifo.io.empty + io.full := fifo.io.full + io.almostEmpty.get := fifo.io.almost_empty + io.almostFull.get := fifo.io.almost_full - // There should be no error since we guarantee to push/pop items only when the fifo is neither empty nor full. - AssertProperty(!fifo.io.error) + // There should be no error since we guarantee to push/pop items only when the fifo is neither empty nor full. + AssertProperty(!fifo.io.error) + } io }