blob: bc8f0ecf7f9cbb5e5ba5d95ce15f5c20d6064bc2 [file] [log] [blame]
// Copyright 2024 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._
import common.CircularBufferMulti
/** An interface which encapsulates up-to n DecoupledIO interfaces. The
* convention is that the first nValid interfaces are considered valid.
*/
class DecoupledVectorIO[T <: Data](gen: T, n: Int) extends Bundle {
val nReady = Input(UInt(log2Up(n+1).W))
val nValid = Output(UInt(log2Up(n+1).W))
val bits = Output(Vec(n, gen))
}
object DecoupledVectorIO {
def apply[T <: Data](gen: T, n: Int): DecoupledVectorIO[T] = new DecoupledVectorIO(gen, n)
}
//TODO: Consider removing this here and in UnchachedFetch?
object PrioritySelect {
def apply(in: Seq[Bool]): Vec[Bool] = {
val seenValid = in.scan(false.B)(_ || _).take(in.length)
VecInit((in zip seenValid).map({case (x, seen) => x && !seen}))
}
}
// Note any instruction will be available to dequeu one full cycle follong enqueuing.
// This must be accounted for when using the instruction buffer as there is no backpressure.
class InstructionBuffer[T <: Data](val gen: T,
val n: Int,
val window: Int) extends Module {
assert(window % n == 0)
val io = IO(new Bundle {
val feedIn = Flipped(DecoupledVectorIO(gen, n))
val out = Vec(n, Decoupled(gen))
val flush = Input(Bool())
val nEnqueued = Output(UInt(log2Ceil(window + 1).W))
val nSpace = Output(UInt(log2Ceil(window + 1).W))
})
dontTouch(io)
val circularBuffer = Module(new CircularBufferMulti(t = gen, n = n, capacity = window))
// Enqueue Logic
val feedInReady = Mux(circularBuffer.io.nSpace < n.U, circularBuffer.io.nSpace, n.U)
io.feedIn.nReady := feedInReady
circularBuffer.io.enqValid := io.feedIn.nValid
circularBuffer.io.enqData := io.feedIn.bits
circularBuffer.io.flush := io.flush
// Dequeue Logic: Always make n elements visible, but only set valid the lesser n nEnqueud and n
// Don't show data is valid if flushing
for (nIndex <- 0 until n) {
io.out(nIndex).valid := (nIndex.U < circularBuffer.io.nEnqueued) && !io.flush
io.out(nIndex).bits := circularBuffer.io.dataOut(nIndex)
}
// Confirm ready signals are contiguous with assert (ex only ready(0) and ready(2) set should fail)
assert(OneHotInOrder(io.out.map(_.fire)), p"OneHotInOrder - Instructions not dispatched in order.")
val nReady = PopCount(io.out.map(_.fire))
circularBuffer.io.deqReady := nReady
io.nEnqueued := circularBuffer.io.nEnqueued
io.nSpace := circularBuffer.io.nSpace
}