blob: cc5f74ea286c9853cf8b62a0c49bcafd63329fac [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.soc
import chisel3._
/**
* A simple case class for defining memory regions.
*
* @param base The base address of the memory region.
* @param size The size of the memory region in bytes.
*/
case class AddressRange(base: BigInt, size: BigInt) {
/**
* Checks if a given dynamic address is within this range.
* @param addr The address to check.
* @return A Chisel Bool indicating if the address is contained.
*/
def contains(addr: UInt): Bool = {
(addr >= base.U) && (addr < (base + size).U)
}
}
/**
* Defines the parameters for a host (master) in the crossbar.
* @param name The unique name of the host.
* @param width The data width of the host interface.
*/
case class HostConfig(name: String, width: Int, clockDomain: String = "main")
/**
* Defines the parameters for a device (slave) in the crossbar.
* @param name The unique name of the device.
* @param addr A sequence of AddressRanges that this device occupies.
* @param clockDomain An identifier for the clock domain this device belongs to.
* @param width The data width of the device interface.
*/
case class DeviceConfig(
name: String,
addr: Seq[AddressRange],
clockDomain: String = "main",
width: Int = 32
)
/**
* This object contains the complete, concrete configuration for the Kelvin SoC crossbar,
* translated from the original tl_config.hjson file.
*/
object CrossbarConfig {
// List of all host (master) interfaces.
val hosts = Seq(
HostConfig("kelvin_core", width = 128),
HostConfig("ibex_core_i", width = 32, clockDomain = "ibex"),
HostConfig("ibex_core_d", width = 32, clockDomain = "ibex")
)
// List of all device (slave) interfaces with their address maps.
val devices = Seq(
DeviceConfig("kelvin_device", Seq(
AddressRange(0x00000000, 0x2000), // 8kB
AddressRange(0x00010000, 0x8000), // 32kB
AddressRange(0x00030000, 0x1000) // 4kB
), width = 128),
DeviceConfig("rom", Seq(AddressRange(0x10000000, 0x8000))), // 32kB
DeviceConfig("sram", Seq(AddressRange(0x20000000, 0x400000))), // 4MB
DeviceConfig("uart0", Seq(AddressRange(0x40000000, 0x1000))),
DeviceConfig("uart1", Seq(AddressRange(0x40010000, 0x1000))),
DeviceConfig(
name = "spi0",
addr = Seq(AddressRange(0x40020000, 0x1000)),
clockDomain = "spi" // This device is on a separate clock domain
)
)
// A map defining which hosts are allowed to connect to which devices.
val connections = Map(
"kelvin_core" -> Seq("sram", "uart1", "spi0", "kelvin_device"),
"ibex_core_i" -> Seq("rom", "sram"),
"ibex_core_d" -> Seq("rom", "sram", "uart0", "kelvin_device")
)
}
/**
* A standalone validator for the CrossbarConfig.
*
* This object can be run to check for configuration errors, such as overlapping
* address ranges between devices.
*/
object CrossbarConfigValidator extends App {
val devices = CrossbarConfig.devices
println("Running CrossbarConfig validation...")
// Check for address range collisions
for (i <- devices.indices) {
for (j <- i + 1 until devices.length) {
val dev1 = devices(i)
val dev2 = devices(j)
for (range1 <- dev1.addr) {
for (range2 <- dev2.addr) {
val start1 = range1.base
val end1 = range1.base + range1.size
val start2 = range2.base
val end2 = range2.base + range2.size
// Check for overlap: max(start1, start2) < min(end1, end2)
val overlap = (start1 < end2) && (start2 < end1)
if (overlap) {
val errorMsg =
s"""
|FATAL: Address range collision detected!
| Device 1: ${dev1.name} -> Range [0x${start1.toString(16)}, 0x${(end1 - 1).toString(16)}]
| Device 2: ${dev2.name} -> Range [0x${start2.toString(16)}, 0x${(end2 - 1).toString(16)}]
""".stripMargin
System.err.println(errorMsg)
throw new Exception("Crossbar configuration validation failed.")
}
}
}
}
}
println("Validation successful: No address range collisions found.")
// Pretty-print the configuration
println("\n--- Crossbar Configuration ---")
println("Hosts:")
CrossbarConfig.hosts.foreach(h => println(s" - ${h.name}"))
println("\nDevices:")
CrossbarConfig.devices.foreach {
d =>
println(s" - ${d.name} (${d.clockDomain} clock domain)")
d.addr.foreach {
a =>
println(f" - 0x${a.base}%08x - 0x${a.base + a.size - 1}%08x (Size: ${a.size / 1024}kB)")
}
}
println("\nConnections:")
CrossbarConfig.connections.foreach {
case (host, devices) =>
println(s" - ${host} -> [${devices.mkString(", ")}]")
}
println("\n--------------------------")
}