tilelink2: compute minimal decisive mask
This commit is contained in:
parent
fb24e847fd
commit
6b1c57aedc
118
src/main/scala/uncore/tilelink2/AddressDecoder.scala
Normal file
118
src/main/scala/uncore/tilelink2/AddressDecoder.scala
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.math.{max,min}
|
||||||
|
|
||||||
|
object AddressDecoder
|
||||||
|
{
|
||||||
|
type Port = Seq[AddressSet]
|
||||||
|
type Ports = Seq[Port]
|
||||||
|
type Partition = Ports
|
||||||
|
type Partitions = Seq[Partition]
|
||||||
|
|
||||||
|
val addressOrder = Ordering.ordered[AddressSet]
|
||||||
|
val portOrder = Ordering.Iterable(addressOrder)
|
||||||
|
val partitionOrder = Ordering.Iterable(portOrder)
|
||||||
|
|
||||||
|
// Find the minimum subset of bits needed to disambiguate port addresses.
|
||||||
|
// ie: inspecting only the bits in the output, you can look at an address
|
||||||
|
// and decide to which port (outer Seq) the address belongs.
|
||||||
|
def apply(ports: Ports): BigInt = if (ports.size <= 1) 0 else {
|
||||||
|
// Every port must have at least one address!
|
||||||
|
ports.foreach { p => require (!p.isEmpty) }
|
||||||
|
// Verify the user did not give us an impossible problem
|
||||||
|
ports.combinations(2).foreach { case Seq(x, y) =>
|
||||||
|
x.foreach { a => y.foreach { b =>
|
||||||
|
require (!a.overlaps(b)) // it must be possible to disambiguate addresses!
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
val maxBits = log2Ceil(ports.map(_.map(_.max).max).max + 1)
|
||||||
|
val bits = (0 until maxBits).map(BigInt(1) << _).toSeq
|
||||||
|
val selected = recurse(Seq(ports.map(_.sorted).sorted(portOrder)), bits)
|
||||||
|
selected.reduceLeft(_ | _)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simpler version that works for a Seq[Int]
|
||||||
|
def apply(keys: Seq[Int]): Int = {
|
||||||
|
val ports = keys.map(b => Seq(AddressSet(b, 0)))
|
||||||
|
apply(ports).toInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// The algorithm has a set of partitions, discriminated by the selected bits.
|
||||||
|
// Each partion has a set of ports, listing all addresses that lead to that port.
|
||||||
|
// Seq[Seq[Seq[AddressSet]]]
|
||||||
|
// ^^^^^^^^^^^^^^^ set of addresses that are routed out this port
|
||||||
|
// ^^^ the list of ports
|
||||||
|
// ^^^ cases already distinguished by the selected bits thus far
|
||||||
|
//
|
||||||
|
// Solving this problem is NP-hard, so we use a simple greedy heuristic:
|
||||||
|
// pick the bit which minimizes the number of ports in each partition
|
||||||
|
// as a secondary goal, reduce the number of AddressSets within a partition
|
||||||
|
|
||||||
|
val bigValue = 100000
|
||||||
|
def bitScore(partitions: Partitions): Int = {
|
||||||
|
val maxPortsPerPartition = partitions.map(_.size).max
|
||||||
|
val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max
|
||||||
|
maxPortsPerPartition * bigValue + maxSetsPerPartition
|
||||||
|
}
|
||||||
|
|
||||||
|
def partitionPort(port: Port, bit: BigInt): (Port, Port) = {
|
||||||
|
val addr_a = AddressSet(0, ~bit)
|
||||||
|
val addr_b = AddressSet(bit, ~bit)
|
||||||
|
// The addresses were sorted, so the filtered addresses are still sorted
|
||||||
|
val subset_a = port.filter(_.overlaps(addr_a))
|
||||||
|
val subset_b = port.filter(_.overlaps(addr_b))
|
||||||
|
(subset_a, subset_b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def partitionPorts(ports: Ports, bit: BigInt): (Ports, Ports) = {
|
||||||
|
val partitioned_ports = ports.map(p => partitionPort(p, bit))
|
||||||
|
// because partitionPort dropped AddresSets, the ports might no longer be sorted
|
||||||
|
val case_a_ports = partitioned_ports.map(_._1).filter(!_.isEmpty).sorted(portOrder)
|
||||||
|
val case_b_ports = partitioned_ports.map(_._2).filter(!_.isEmpty).sorted(portOrder)
|
||||||
|
(case_a_ports, case_b_ports)
|
||||||
|
}
|
||||||
|
|
||||||
|
def partitionPartitions(partitions: Partitions, bit: BigInt): Partitions = {
|
||||||
|
val partitioned_partitions = partitions.map(p => partitionPorts(p, bit))
|
||||||
|
val case_a_partitions = partitioned_partitions.map(_._1)
|
||||||
|
val case_b_partitions = partitioned_partitions.map(_._2)
|
||||||
|
val new_partitions = (case_a_partitions ++ case_b_partitions).sorted(partitionOrder)
|
||||||
|
// Prevent combinational memory explosion; if two partitions are equal, keep only one
|
||||||
|
// Note: AddressSets in a port are sorted, and ports in a partition are sorted.
|
||||||
|
// This makes it easy to structurally compare two partitions for equality
|
||||||
|
val keep = (new_partitions.init zip new_partitions.tail) filter { case (a,b) => partitionOrder.compare(a,b) != 0 } map { _._2 }
|
||||||
|
new_partitions.head +: keep
|
||||||
|
}
|
||||||
|
|
||||||
|
// requirement: ports have sorted addresses and are sorted lexicographically
|
||||||
|
val debug = false
|
||||||
|
def recurse(partitions: Partitions, bits: Seq[BigInt]): Seq[BigInt] = {
|
||||||
|
if (debug) {
|
||||||
|
println("Partitioning:")
|
||||||
|
partitions.foreach { partition =>
|
||||||
|
println(" Partition:")
|
||||||
|
partition.foreach { port =>
|
||||||
|
print(" ")
|
||||||
|
port.foreach { a => print(s" ${a}") }
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val candidates = bits.map { bit =>
|
||||||
|
val result = partitionPartitions(partitions, bit)
|
||||||
|
val score = bitScore(result)
|
||||||
|
(score, bit, result)
|
||||||
|
}
|
||||||
|
val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Int, BigInt, Partitions), Int](_._1))
|
||||||
|
if (debug) println("=> Selected bit 0x%x".format(bestBit))
|
||||||
|
if (bestScore < 2*bigValue) {
|
||||||
|
if (debug) println("---")
|
||||||
|
Seq(bestBit)
|
||||||
|
} else {
|
||||||
|
bestBit +: recurse(bestPartitions, bits.filter(_ != bestBit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,7 @@ object TransferSizes {
|
|||||||
// Base is the base address, and mask are the bits consumed by the manager
|
// Base is the base address, and mask are the bits consumed by the manager
|
||||||
// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff
|
// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff
|
||||||
// e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ...
|
// e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ...
|
||||||
case class AddressSet(base: BigInt, mask: BigInt)
|
case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet]
|
||||||
{
|
{
|
||||||
// Forbid misaligned base address (and empty sets)
|
// Forbid misaligned base address (and empty sets)
|
||||||
require ((base & mask) == 0)
|
require ((base & mask) == 0)
|
||||||
@ -97,6 +97,16 @@ case class AddressSet(base: BigInt, mask: BigInt)
|
|||||||
|
|
||||||
// A strided slave serves discontiguous ranges
|
// A strided slave serves discontiguous ranges
|
||||||
def strided = alignment1 != mask
|
def strided = alignment1 != mask
|
||||||
|
|
||||||
|
// AddressSets have one natural Ordering (the containment order)
|
||||||
|
def compare(x: AddressSet) = {
|
||||||
|
val primary = (this.base - x.base).signum // smallest address first
|
||||||
|
val secondary = (x.mask - this.mask).signum // largest mask first
|
||||||
|
if (primary != 0) primary else secondary
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always want to see things in hex
|
||||||
|
override def toString() = "AddressSet(0x%x, 0x%x)".format(base, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class TLManagerParameters(
|
case class TLManagerParameters(
|
||||||
@ -185,6 +195,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
|||||||
def findFifoId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0))))
|
def findFifoId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0))))
|
||||||
def hasFifoId(address: UInt) = Mux1H(find(address), managers.map(m => Bool(m.fifoId.isDefined)))
|
def hasFifoId(address: UInt) = Mux1H(find(address), managers.map(m => Bool(m.fifoId.isDefined)))
|
||||||
|
|
||||||
|
lazy val addressMask = AddressDecoder(managers.map(_.address))
|
||||||
// !!! need a cheaper version of find, where we assume a valid address match exists
|
// !!! need a cheaper version of find, where we assume a valid address match exists
|
||||||
|
|
||||||
// Does this Port manage this ID/address?
|
// Does this Port manage this ID/address?
|
||||||
|
Loading…
Reference in New Issue
Block a user