1
0

tilelink2: move general-purpose code out of tilelink2 package

This commit is contained in:
Wesley W. Terpstra
2016-10-03 15:17:36 -07:00
parent c85e42a303
commit f05298d9bc
42 changed files with 527 additions and 476 deletions

View File

@ -0,0 +1,131 @@
// See LICENSE for license details.
package diplomacy
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))
}
}
}

View File

@ -0,0 +1,96 @@
// See LICENSE for license details.
package diplomacy
import Chisel._
import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo}
abstract class LazyModule
{
protected[diplomacy] var bindings = List[() => Unit]()
protected[diplomacy] var children = List[LazyModule]()
protected[diplomacy] var nodes = List[BaseNode]()
protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo
protected[diplomacy] val parent = LazyModule.stack.headOption
LazyModule.stack = this :: LazyModule.stack
parent.foreach(p => p.children = this :: p.children)
def name = getClass.getName.split('.').last
def line = sourceLine(info)
def module: LazyModuleImp
protected[diplomacy] def instantiate() = {
children.reverse.foreach { c =>
// !!! fix chisel3 so we can pass the desired sourceInfo
// implicit val sourceInfo = c.module.outer.info
Module(c.module)
}
bindings.reverse.foreach { f => f () }
}
def omitGraphML = nodes.isEmpty && children.isEmpty
lazy val graphML: String = parent.map(_.graphML).getOrElse {
val buf = new StringBuilder
buf ++= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
buf ++= "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:y=\"http://www.yworks.com/xml/graphml\">\n"
buf ++= " <key for=\"node\" id=\"n\" yfiles.type=\"nodegraphics\"/>\n"
buf ++= " <key for=\"edge\" id=\"e\" yfiles.type=\"edgegraphics\"/>\n"
buf ++= " <graph id=\"G\" edgedefault=\"directed\">\n"
nodesGraphML(buf, " ")
edgesGraphML(buf, " ")
buf ++= " </graph>\n"
buf ++= "</graphml>\n"
buf.toString
}
private val index = { LazyModule.index = LazyModule.index + 1; LazyModule.index }
private def nodesGraphML(buf: StringBuilder, pad: String) {
buf ++= s"""${pad}<node id=\"${index}\">\n"""
buf ++= s"""${pad} <data key=\"n\"><y:ShapeNode><y:NodeLabel modelName=\"sides\" modelPosition=\"w\" fontSize=\"10\" borderDistance=\"1.0\" rotationAngle=\"270.0\">${module.name}</y:NodeLabel></y:ShapeNode></data>\n"""
buf ++= s"""${pad} <graph id=\"${index}::\" edgedefault=\"directed\">\n"""
nodes.filter(!_.omitGraphML).foreach { n =>
buf ++= s"""${pad} <node id=\"${index}::${n.index}\"/>\n"""
}
children.filter(!_.omitGraphML).foreach { _.nodesGraphML(buf, pad + " ") }
buf ++= s"""${pad} </graph>\n"""
buf ++= s"""${pad}</node>\n"""
}
private def edgesGraphML(buf: StringBuilder, pad: String) {
nodes.filter(!_.omitGraphML) foreach { n => n.outputs.filter(!_.omitGraphML).foreach { o =>
buf ++= pad
buf ++= "<edge"
buf ++= s""" source=\"${index}::${n.index}\""""
buf ++= s""" target=\"${o.lazyModule.index}::${o.index}\"><data key=\"e\"><y:PolyLineEdge><y:Arrows source=\"none\" target=\"standard\"/><y:LineStyle color=\"${o.colour}\" type=\"line\" width=\"1.0\"/></y:PolyLineEdge></data></edge>\n"""
} }
children.filter(!_.omitGraphML).foreach { c => c.edgesGraphML(buf, pad) }
}
}
object LazyModule
{
protected[diplomacy] var stack = List[LazyModule]()
private var index = 0
def apply[T <: LazyModule](bc: T)(implicit sourceInfo: SourceInfo): T = {
// Make sure the user put LazyModule around modules in the correct order
// If this require fails, probably some grandchild was missing a LazyModule
// ... or you applied LazyModule twice
require (!stack.isEmpty, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}")
require (stack.head eq bc, s"LazyModule() applied to ${bc.name} before ${stack.head.name} ${sourceLine(sourceInfo)}")
stack = stack.tail
bc.info = sourceInfo
bc
}
}
abstract class LazyModuleImp(outer: LazyModule) extends Module
{
// .module had better not be accessed while LazyModules are still being built!
require (LazyModule.stack.isEmpty, s"${outer.name}.module was constructed before LazyModule() was run on ${LazyModule.stack.head.name}")
override def desiredName = outer.name
outer.instantiate()
}

View File

@ -0,0 +1,193 @@
// See LICENSE for license details.
package diplomacy
import Chisel._
import scala.collection.mutable.ListBuffer
import chisel3.internal.sourceinfo.SourceInfo
// DI = Downwards flowing Parameters received on the inner side of the node
// UI = Upwards flowing Parameters generated by the inner side of the node
// EI = Edge Parameters describing a connection on the inner side of the node
// BI = Bundle type used when connecting to the inner side of the node
trait InwardNodeImp[DI, UI, EI, BI <: Data]
{
def edgeI(pd: DI, pu: UI): EI
def bundleI(ei: Seq[EI]): Vec[BI]
def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu
def colour: String
def connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit)
}
// DO = Downwards flowing Parameters generated by the outner side of the node
// UO = Upwards flowing Parameters received on the outner side of the node
// EO = Edge Parameters describing a connection on the outer side of the node
// BO = Bundle type used when connecting to the outer side of the node
trait OutwardNodeImp[DO, UO, EO, BO <: Data]
{
def edgeO(pd: DO, pu: UO): EO
def bundleO(eo: Seq[EO]): Vec[BO]
def mixO(pd: DO, node: OutwardNode[DO, UO, BO]): DO = pd
}
abstract class NodeImp[D, U, EO, EI, B <: Data]
extends Object with InwardNodeImp[D, U, EI, B] with OutwardNodeImp[D, U, EO, B]
abstract class BaseNode
{
// You cannot create a Node outside a LazyModule!
require (!LazyModule.stack.isEmpty)
val lazyModule = LazyModule.stack.head
val index = lazyModule.nodes.size
lazyModule.nodes = this :: lazyModule.nodes
def name = lazyModule.name + "." + getClass.getName.split('.').last
def omitGraphML = outputs.isEmpty && inputs.isEmpty
protected[diplomacy] def outputs: Seq[BaseNode]
protected[diplomacy] def inputs: Seq[BaseNode]
protected[diplomacy] def colour: String
}
trait InwardNode[DI, UI, BI <: Data] extends BaseNode
{
protected[diplomacy] val numPI: Range.Inclusive
require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}")
require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}")
private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]()
private var iRealized = false
protected[diplomacy] def iPushed = accPI.size
protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) {
val info = sourceLine(sourceInfo, " at ", "")
val noIs = numPI.size == 1 && numPI.contains(0)
require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info)
require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info)
accPI += ((index, node))
}
private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}")
protected[diplomacy] lazy val iPorts = { iRealized = true; reqI(); accPI.result() }
protected[diplomacy] val iParams: Seq[UI]
protected[diplomacy] def iConnect: Vec[BI]
}
trait OutwardNode[DO, UO, BO <: Data] extends BaseNode
{
protected[diplomacy] val numPO: Range.Inclusive
require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}")
require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}")
private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]()
private var oRealized = false
protected[diplomacy] def oPushed = accPO.size
protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) {
val info = sourceLine(sourceInfo, " at ", "")
val noOs = numPO.size == 1 && numPO.contains(0)
require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info)
require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info)
accPO += ((index, node))
}
private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}")
protected[diplomacy] lazy val oPorts = { oRealized = true; reqO(); accPO.result() }
protected[diplomacy] val oParams: Seq[DO]
protected[diplomacy] def oConnect: Vec[BO]
}
class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
inner: InwardNodeImp [DI, UI, EI, BI],
outer: OutwardNodeImp[DO, UO, EO, BO])(
private val dFn: (Int, Seq[DI]) => Seq[DO],
private val uFn: (Int, Seq[UO]) => Seq[UI],
protected[diplomacy] val numPO: Range.Inclusive,
protected[diplomacy] val numPI: Range.Inclusive)
extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO]
{
// meta-data for printing the node graph
protected[diplomacy] def colour = inner.colour
protected[diplomacy] def outputs = oPorts.map(_._2)
protected[diplomacy] def inputs = iPorts.map(_._2)
private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}")
protected[diplomacy] lazy val oParams: Seq[DO] = {
val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) })
reqE(oPorts.size, o.size)
o.map(outer.mixO(_, this))
}
protected[diplomacy] lazy val iParams: Seq[UI] = {
val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) })
reqE(i.size, iPorts.size)
i.map(inner.mixI(_, this))
}
lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => outer.edgeO(o, n.iParams(i)) }
lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => inner.edgeI(n.oParams(o), i) }
lazy val bundleOut = outer.bundleO(edgesOut)
lazy val bundleIn = inner.bundleI(edgesIn)
def oConnect = bundleOut
def iConnect = bundleIn
// connects the outward part of a node with the inward part of this node
def := (y: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo): Option[LazyModule] = {
val x = this // x := y
val info = sourceLine(sourceInfo, " at ", "")
require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info)
val i = x.iPushed
val o = y.oPushed
y.oPush(i, x)
x.iPush(o, y)
val (out, binding) = inner.connect(y.oConnect(o), x.iConnect(i), x.edgesIn(i))
LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings
out
}
}
class SimpleNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(
oFn: (Int, Seq[D]) => Seq[D],
iFn: (Int, Seq[U]) => Seq[U],
numPO: Range.Inclusive,
numPI: Range.Inclusive)
extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI)
class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])
extends SimpleNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999)
class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
{
override def oConnect = bundleOut
override def iConnect = bundleOut
}
class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
{
override def oConnect = bundleIn
override def iConnect = bundleIn
}
class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1)
extends SimpleNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0)
{
require (num.end >= 1, s"${name} is a source which does not accept outputs${lazyModule.line}")
}
class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1)
extends SimpleNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num)
{
require (num.end >= 1, s"${name} is a sink which does not accept inputs${lazyModule.line}")
}
class InteriorNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])
(oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive)
extends SimpleNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI)
{
require (numPO.end >= 1, s"${name} is an adapter which does not accept outputs${lazyModule.line}")
require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}")
}

View File

@ -0,0 +1,146 @@
// See LICENSE for license details.
package diplomacy
import Chisel._
import scala.math.max
/** Options for memory regions */
object RegionType {
sealed trait T
case object CACHED extends T
case object TRACKED extends T
case object UNCACHED extends T
case object PUT_EFFECTS extends T
case object GET_EFFECTS extends T // GET_EFFECTS => PUT_EFFECTS
val cases = Seq(CACHED, TRACKED, UNCACHED, PUT_EFFECTS, GET_EFFECTS)
}
// A non-empty half-open range; [start, end)
case class IdRange(start: Int, end: Int)
{
require (start >= 0)
require (start < end) // not empty
// This is a strict partial ordering
def <(x: IdRange) = end <= x.start
def >(x: IdRange) = x < this
def overlaps(x: IdRange) = start < x.end && x.start < end
def contains(x: IdRange) = start <= x.start && x.end <= end
// contains => overlaps (because empty is forbidden)
def contains(x: Int) = start <= x && x < end
def contains(x: UInt) =
if (start+1 == end) { UInt(start) === x }
else if (isPow2(end-start) && ((end | start) & (end-start-1)) == 0)
{ ~(~(UInt(start) ^ x) | UInt(end-start-1)) === UInt(0) }
else { UInt(start) <= x && x < UInt(end) }
def shift(x: Int) = IdRange(start+x, end+x)
def size = end - start
}
// An potentially empty inclusive range of 2-powers [min, max] (in bytes)
case class TransferSizes(min: Int, max: Int)
{
def this(x: Int) = this(x, x)
require (min <= max)
require (min >= 0 && max >= 0)
require (max == 0 || isPow2(max))
require (min == 0 || isPow2(min))
require (max == 0 || min != 0) // 0 is forbidden unless (0,0)
def none = min == 0
def contains(x: Int) = isPow2(x) && min <= x && x <= max
def containsLg(x: Int) = contains(1 << x)
def containsLg(x: UInt) =
if (none) Bool(false)
else if (min == max) { UInt(log2Ceil(min)) === x }
else { UInt(log2Ceil(min)) <= x && x <= UInt(log2Ceil(max)) }
def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max)
def intersect(x: TransferSizes) =
if (x.max < min || max < x.min) TransferSizes.none
else TransferSizes(scala.math.max(min, x.min), scala.math.min(max, x.max))
}
object TransferSizes {
def apply(x: Int) = new TransferSizes(x)
val none = new TransferSizes(0)
implicit def asBool(x: TransferSizes) = !x.none
}
// AddressSets specify the address space managed 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=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ...
case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet]
{
// Forbid misaligned base address (and empty sets)
require ((base & mask) == 0)
require (base >= 0) // TL2 address widths are not fixed => negative is ambiguous
// We do allow negative mask (=> ignore all high bits)
def contains(x: BigInt) = ((x ^ base) & ~mask) == 0
def contains(x: UInt) = ((x ^ UInt(base)).zext() & SInt(~mask)) === SInt(0)
// overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1)
def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0
// contains iff bitwise: x.mask => mask && contains(x.base)
def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0
// The number of bytes to which the manager must be aligned
def alignment = ((mask + 1) & ~mask)
// Is this a contiguous memory range
def contiguous = alignment == mask+1
def finite = mask >= 0
def max = { require (finite); base | mask }
// Widen the match function to ignore all bits in imask
def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask)
// AddressSets have one natural Ordering (the containment order, if contiguous)
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() = {
if (mask >= 0) {
"AddressSet(0x%x, 0x%x)".format(base, mask)
} else {
"AddressSet(0x%x, ~0x%x)".format(base, ~mask)
}
}
}
object AddressSet
{
def misaligned(base: BigInt, size: BigInt): Seq[AddressSet] = {
val largestPow2 = BigInt(1) << log2Floor(size)
val mostZeros = (base + size - 1) & ~(largestPow2 - 1)
def splitLo(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = {
if (low == high) tail else {
val toggleBits = low ^ high
val misalignment = toggleBits & (-toggleBits)
splitLo(low+misalignment, high, AddressSet(low, misalignment-1) +: tail)
}
}
def splitHi(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = {
if (low == high) tail else {
val toggleBits = low ^ high
val misalignment = toggleBits & (-toggleBits)
splitHi(low, high-misalignment, AddressSet(high-misalignment, misalignment-1) +: tail)
}
}
splitLo(base, mostZeros, splitHi(mostZeros, base+size, Seq())).sorted
}
}

View File

@ -0,0 +1,10 @@
import Chisel._
import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo}
package object diplomacy
{
def sourceLine(sourceInfo: SourceInfo, prefix: String = " (", suffix: String = ")") = sourceInfo match {
case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix"
case _ => ""
}
}