blob: 5dbbef20956813eec99c9cf1806edbaee3d2de82 [file] [log] [blame]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import chisel3._
import chisel3.util._
class CircularBufferMulti[T <: Data](t: T, n: Int, capacity: Int) extends Module {
// For the time being, restrict to powers of 2
assert(isPow2(n))
assert(isPow2(capacity))
val io = IO(new Bundle {
val enqValid = Input(UInt(log2Ceil(n + 1).W))
val enqData = Input(Vec(n, t))
val nEnqueued = Output(UInt(log2Ceil(capacity + 1).W))
val nSpace = Output(UInt(log2Ceil(capacity + 1).W))
val dataOut = Output(Vec(n, t))
val deqReady = Input(UInt(log2Ceil(n + 1).W))
val flush = Input(Bool())
})
dontTouch(io)
// Note first assert below should be sufficient it allows enqueueing items when buffer
// is full or close to full provided deqReady >= enqValid.
// The second assert is more conservative, and will never allow enqueueing more items
// than there is space in the buffer, under any circumstances. May be removed if
// desire for more is greater than the need to be more conservative.
assert(io.nEnqueued +& io.enqValid -& io.deqReady <= capacity.U)
assert(io.enqValid <= (capacity.U -& io.nEnqueued))
assert(io.deqReady <= io.nEnqueued)
val buffer = RegInit(VecInit.fill(capacity)(0.U.asTypeOf(t)))
val enqPtr = RegInit(0.U(log2Ceil(capacity).W))
val deqPtr = RegInit(0.U(log2Ceil(capacity).W))
val expandedInput = Wire(Vec(capacity, Valid(t)))
for (i <- 0 until capacity) {
if (i < n) {
expandedInput(i) := MakeValid(i.U < io.enqValid, io.enqData(i))
} else {
expandedInput(i) := MakeInvalid(t)
}
}
val rotatedInput = RotateVectorLeft(expandedInput, enqPtr)
for (i <- 0 until capacity) {
buffer(i) := Mux(rotatedInput(i).valid, rotatedInput(i).bits, buffer(i))
}
var nEnqueued = RegInit(0.U(io.nEnqueued.getWidth.W))
when(io.flush) {
enqPtr := 0.U
deqPtr := 0.U
nEnqueued := 0.U
} .otherwise {
enqPtr := enqPtr + io.enqValid
deqPtr := deqPtr + io.deqReady
nEnqueued := nEnqueued + io.enqValid - io.deqReady
}
io.nEnqueued := nEnqueued
io.nSpace := capacity.U - nEnqueued
val outputBufferView = RotateVectorRight(buffer, deqPtr)
for (i <- 0 until n) {
io.dataOut(i) := outputBufferView(i)
}
}