blob: 9ca9374d1ee0c4c5e2afb3e2a457e9d05d32d29f [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 kelvin
import chisel3._
import chisel3.util._
import common._
// Picks one of two fabric commands to route to a port. Priority is given to the
// first port.
class FabricArbiter(p: Parameters) extends Module {
val io = IO(new Bundle {
val source = Vec(2, Flipped(new FabricIO(p)))
val fabricBusy = Output(Bool()) // Back pressure for the second port
val port = new FabricIO(p)
})
// Only read, or only write (or none) can be issued.
assert(!(io.source(0).readDataAddr.valid && io.source(0).writeDataAddr.valid))
assert(!(io.source(1).readDataAddr.valid && io.source(1).writeDataAddr.valid))
val source0Valid = io.source(0).readDataAddr.valid ||
io.source(0).writeDataAddr.valid
io.fabricBusy := source0Valid
io.port.readDataAddr := Mux(source0Valid, io.source(0).readDataAddr,
io.source(1).readDataAddr)
io.port.writeDataAddr := Mux(source0Valid, io.source(0).writeDataAddr,
io.source(1).writeDataAddr)
io.port.writeDataBits := Mux(source0Valid, io.source(0).writeDataBits,
io.source(1).writeDataBits)
io.port.writeDataStrb := Mux(source0Valid, io.source(0).writeDataStrb,
io.source(1).writeDataStrb)
// Broadcast SRAM outputs back
io.source(0).readData := io.port.readData
io.source(1).readData := io.port.readData
io.source(0).writeResp := io.port.writeResp
io.source(1).writeResp := io.port.writeResp
}
// Routes one fabric command from source to a given port.
class FabricMux(p: Parameters, regions: Seq[MemoryRegion]) extends Module {
val portCount = regions.length
val portIdxBits = log2Ceil(portCount)
val portIdxType = UInt(log2Ceil(portCount).W)
val io = IO(new Bundle {
val source = Flipped(new FabricIO(p))
val fabricBusy = Output(Bool())
val ports = Vec(portCount, new FabricIO(p))
val periBusy = Vec(portCount, Input(Bool()))
})
// Only read, or only write (or none) can be issued.
assert(!(io.source.readDataAddr.valid && io.source.writeDataAddr.valid))
// Determine which port to forward command to
val sourceValid = io.source.readDataAddr.valid ||
io.source.writeDataAddr.valid
val addr = MuxCase(0.U, Seq(
io.source.readDataAddr.valid -> io.source.readDataAddr.bits,
io.source.writeDataAddr.valid -> io.source.writeDataAddr.bits,
))
val selected = MuxCase(MakeInvalid(portIdxType), (0 until portCount).map(
x => (sourceValid && regions(x).contains(addr)) ->
MakeValid(true.B, x.U(portIdxBits.W))
))
val portSelected = (0 until portCount).map(
i => selected.valid && (selected.bits === i.U) && !io.periBusy(i))
assert(PopCount(VecInit(portSelected)) <= 1.U) // Should only select one port
io.fabricBusy := MuxCase(false.B, (0 until portCount).map(
i => (selected.valid && (selected.bits === i.U)) -> io.periBusy(i)
))
// Forward commands to ports
for (i <- 0 until portCount) {
val readAddr = io.source.readDataAddr.bits &
~regions(i).memStart.U(p.fetchAddrBits.W)
val writeAddr = io.source.writeDataAddr.bits &
~regions(i).memStart.U(p.fetchAddrBits.W)
io.ports(i).readDataAddr.valid :=
portSelected(i) && io.source.readDataAddr.valid
io.ports(i).readDataAddr.bits := Mux(portSelected(i), readAddr, 0.U)
io.ports(i).writeDataAddr.valid :=
portSelected(i) && io.source.writeDataAddr.valid
io.ports(i).writeDataAddr.bits := Mux(portSelected(i), writeAddr, 0.U)
io.ports(i).writeDataBits :=
Mux(portSelected(i), io.source.writeDataBits, 0.U)
io.ports(i).writeDataStrb :=
Mux(portSelected(i), io.source.writeDataStrb, 0.U)
}
// Pick writeResp from the correct port
io.source.writeResp := MuxCase(false.B, (0 until portCount).map(
i => portSelected(i) -> io.ports(i).writeResp,
))
// Pick readData from the correct port. Should be delayed by one cycle to
// match behaviours of peripherals.
val lastReadSelected = RegInit(MakeInvalid(portIdxType))
lastReadSelected := MuxCase(MakeInvalid(portIdxType), (0 until portCount).map(
i => (portSelected(i) && io.source.readDataAddr.valid) ->
MakeValid(true.B, i.U(portIdxBits.W))
))
io.source.readData := MuxCase(MakeInvalid(UInt(p.axi2DataBits.W)),
(0 until portCount).map(i =>
(lastReadSelected.valid && (lastReadSelected.bits === i.U)) ->
io.ports(i).readData
)
)
}