1
0
rocket-chip/src/main/scala/uncore/tilelink2/AddressDecoder.scala
2016-09-16 16:09:00 -07:00

132 lines
5.3 KiB
Scala

// 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 ports!
} }
}
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)
val output = selected.reduceLeft(_ | _)
// Modify the AddressSets to allow the new wider match functions
val widePorts = ports.map { _.map { _.widen(~output) } }
// Verify that it remains possible to disambiguate all ports
widePorts.combinations(2).foreach { case Seq(x, y) =>
x.foreach { a => y.foreach { b =>
require (!a.overlaps(b))
} }
}
output
}
// 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
def bitScore(partitions: Partitions): Seq[Int] = {
val maxPortsPerPartition = partitions.map(_.size).max
val sumPortsPerPartition = partitions.map(_.size).sum
val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max
val sumSetsPerPartition = partitions.map(_.map(_.size).sum).sum
Seq(maxPortsPerPartition, sumPortsPerPartition, maxSetsPerPartition, sumSetsPerPartition)
}
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).filter(!_.isEmpty)
val case_b_partitions = partitioned_partitions.map(_._2).filter(!_.isEmpty)
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[(Seq[Int], BigInt, Partitions), Iterable[Int]](_._1.toIterable))
if (debug) println("=> Selected bit 0x%x".format(bestBit))
if (bestScore(0) <= 1) {
if (debug) println("---")
Seq(bestBit)
} else {
bestBit +: recurse(bestPartitions, bits.filter(_ != bestBit))
}
}
}