blob: 85d118af3596fec96f8a2bc1ca7d4d8bf779502a [file]
// 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.simulator.scalatest.ChiselSim
import org.scalatest.freespec.AnyFreeSpec
class FabricArbiterSpec extends AnyFreeSpec with ChiselSim {
var p = new Parameters
p.enableVector = false
"Kelvin Read - AXI Slave Any" in {
simulate(new FabricArbiter(p)) { dut =>
dut.io.source(0).readDataAddr.valid.poke(true.B)
dut.io.source(0).readDataAddr.bits.poke(0x8080.U)
dut.io.source(0).writeDataAddr.valid.poke(false.B)
dut.io.source(1).readDataAddr.bits.poke(0x4080.U)
dut.io.source(1).writeDataAddr.bits.poke(0x4080.U)
dut.io.source(1).writeDataBits.poke(0x5080.U)
dut.io.source(1).writeDataStrb.poke(0x1.U)
for (i <- 0 until 3) {
if (i == 0) { // None
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
} else if (i == 1) { // Read
dut.io.source(1).readDataAddr.valid.poke(true.B)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
} else { // Write
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(true.B)
}
// Check read command is accurate
dut.io.fabricBusy.expect(1)
dut.io.port.readDataAddr.valid.expect(1)
dut.io.port.writeDataAddr.valid.expect(0)
dut.io.port.readDataAddr.bits.expect(0x8080)
dut.clock.step()
// Check response is propagated back to source 0
dut.io.port.readData.valid.poke(true.B)
dut.io.port.readData.bits.poke(300 + i)
dut.io.source(0).readData.valid.expect(1)
dut.io.source(0).readData.bits.expect(300 + i)
}
}
}
"Kelvin Write - AXI Slave Any" in {
simulate(new FabricArbiter(p)) { dut =>
dut.io.source(0).readDataAddr.valid.poke(false.B)
dut.io.source(0).writeDataAddr.valid.poke(true.B)
dut.io.source(0).writeDataAddr.bits.poke(0x80B0.U)
dut.io.source(0).writeDataBits.poke(0x50B0.U)
dut.io.source(0).writeDataStrb.poke(0xF.U)
dut.io.source(1).readDataAddr.bits.poke(0x40B0.U)
dut.io.source(1).writeDataAddr.bits.poke(0x40B0.U)
dut.io.source(1).writeDataBits.poke(0x60B0.U)
dut.io.source(1).writeDataStrb.poke(0x12.U)
for (i <- 0 until 3) {
if (i == 0) { // None
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
} else if (i == 1) { // Read
dut.io.source(1).readDataAddr.valid.poke(true.B)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
} else { // Write
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(true.B)
}
// Check write command is accurate
dut.io.fabricBusy.expect(1)
dut.io.port.readDataAddr.valid.expect(0)
dut.io.port.writeDataAddr.valid.expect(1)
dut.io.port.writeDataAddr.bits.expect(0x80B0)
dut.io.port.writeDataBits.expect(0x50B0)
dut.io.port.writeDataStrb.expect(0xF)
}
}
}
"Kelvin None - AXI Slave Read" in {
simulate(new FabricArbiter(p)) { dut =>
dut.io.source(0).readDataAddr.valid.poke(false.B)
dut.io.source(0).writeDataAddr.valid.poke(false.B)
dut.io.source(1).readDataAddr.valid.poke(true.B)
dut.io.source(1).readDataAddr.bits.poke(0x40B0.U)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
dut.io.fabricBusy.expect(0)
dut.io.port.readDataAddr.valid.expect(1)
dut.io.port.readDataAddr.bits.expect(0x40B0)
dut.io.port.writeDataAddr.valid.expect(0)
dut.clock.step()
dut.io.port.readData.valid.poke(true.B)
dut.io.port.readData.bits.poke(777)
dut.io.source(1).readData.valid.expect(1)
dut.io.source(1).readData.bits.expect(777)
}
}
"Kelvin None - AXI Slave Write" in {
simulate(new FabricArbiter(p)) { dut =>
dut.io.source(0).readDataAddr.valid.poke(false.B)
dut.io.source(0).writeDataAddr.valid.poke(false.B)
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(true.B)
dut.io.source(1).writeDataAddr.bits.poke(0xB0B0.U)
dut.io.source(1).writeDataBits.poke(0xA0B0.U)
dut.io.source(1).writeDataStrb.poke(0xA.U)
dut.io.fabricBusy.expect(0)
dut.io.port.readDataAddr.valid.expect(0)
dut.io.port.writeDataAddr.valid.expect(1)
dut.io.port.writeDataAddr.bits.expect(0xB0B0)
}
}
"Both None" in {
simulate(new FabricArbiter(p)) { dut =>
dut.io.source(0).readDataAddr.valid.poke(false.B)
dut.io.source(0).writeDataAddr.valid.poke(false.B)
dut.io.source(1).readDataAddr.valid.poke(false.B)
dut.io.source(1).writeDataAddr.valid.poke(false.B)
dut.io.fabricBusy.expect(0)
dut.io.port.readDataAddr.valid.expect(0)
dut.io.port.writeDataAddr.valid.expect(0)
}
}
}
class FabricMuxSpec extends AnyFreeSpec with ChiselSim {
var p = new Parameters
p.enableVector = false
val memoryRegions = Seq(
new MemoryRegion(0x0000, 0x2000, MemoryRegionType.IMEM), // ITCM
new MemoryRegion(0x10000, 0x8000, MemoryRegionType.DMEM), // DTCM
new MemoryRegion(0x30000, 0x2000, MemoryRegionType.Peripheral), // CSR
)
"Writes" in {
simulate(new FabricMux(p, memoryRegions)) { dut =>
val inputAddrs = Seq(0x10, 0x10020, 0x30004)
val outputAddrs = Seq(0x10, 0x20, 0x4)
for (i <- 0 until memoryRegions.length) {
dut.io.source.readDataAddr.valid.poke(false.B)
dut.io.source.writeDataAddr.valid.poke(true.B)
dut.io.source.writeDataAddr.bits.poke(inputAddrs(i).U)
dut.io.source.writeDataBits.poke((123 + i).U)
dut.io.source.writeDataStrb.poke((21 + i).U)
for (j <- 0 until memoryRegions.length) {
dut.io.ports(j).readDataAddr.valid.expect(0)
if (i == j) {
dut.io.ports(j).writeDataAddr.valid.expect(1)
dut.io.ports(j).writeDataAddr.bits.expect(outputAddrs(i))
dut.io.ports(j).writeDataBits.expect(123 + i)
dut.io.ports(j).writeDataStrb.expect(21 + i)
} else {
dut.io.ports(j).writeDataAddr.valid.expect(0)
}
}
// Check periBusy gets forwarded correctly
dut.io.periBusy(i).poke(true.B)
dut.io.fabricBusy.expect(1)
dut.io.periBusy(i).poke(false.B)
dut.io.fabricBusy.expect(0)
}
// Invalid write
dut.io.source.writeDataAddr.bits.poke(0x90000.U)
dut.io.source.writeDataBits.poke(1123.U)
dut.io.source.writeDataStrb.poke(11.U)
for (j <- 0 until memoryRegions.length) {
dut.io.ports(j).readDataAddr.valid.expect(0)
dut.io.ports(j).writeDataAddr.valid.expect(0)
}
}
}
"Reads" in {
simulate(new FabricMux(p, memoryRegions)) { dut =>
val inputAddrs = Seq(0x10, 0x10020, 0x30004)
val outputAddrs = Seq(0x10, 0x20, 0x4)
for (i <- 0 until memoryRegions.length) {
dut.io.source.readDataAddr.valid.poke(true.B)
dut.io.source.readDataAddr.bits.poke(inputAddrs(i).U)
dut.io.source.writeDataAddr.valid.poke(false.B)
// Check command was forwarded correctly
for (j <- 0 until memoryRegions.length) {
dut.io.ports(j).writeDataAddr.valid.expect(0)
if (i == j) {
dut.io.ports(j).readDataAddr.valid.expect(1)
dut.io.ports(j).readDataAddr.bits.expect(outputAddrs(i))
} else {
dut.io.ports(j).readDataAddr.valid.expect(0)
}
}
// Check periBusy gets forwarded correctly
dut.io.periBusy(i).poke(true.B)
dut.io.fabricBusy.expect(1)
dut.io.periBusy(i).poke(false.B)
dut.io.fabricBusy.expect(0)
dut.clock.step()
// Check correct response is picked
for (j <- 0 until memoryRegions.length) {
dut.io.ports(j).readData.valid.poke(false.B)
dut.io.ports(j).readData.bits.poke((800 + j).U)
}
dut.io.ports(i).readData.valid.poke(true.B)
dut.io.source.readData.valid.expect(1)
dut.io.source.readData.bits.expect(800 + i)
}
// Invalid read
dut.io.source.readDataAddr.valid.poke(true.B)
dut.io.source.readDataAddr.bits.poke(0x90000.U)
dut.io.source.writeDataAddr.valid.poke(false.B)
for (i <- 0 until memoryRegions.length) {
dut.io.ports(i).readDataAddr.valid.expect(0)
}
}
}
}