2016-09-08 19:38:38 +02:00
|
|
|
// See LICENSE for license details.
|
|
|
|
|
|
|
|
package uncore.tilelink2
|
|
|
|
|
|
|
|
import Chisel._
|
|
|
|
import chisel3.internal.sourceinfo.SourceInfo
|
2016-10-04 00:17:36 +02:00
|
|
|
import diplomacy._
|
2016-09-08 19:38:38 +02:00
|
|
|
import scala.math.{min,max}
|
|
|
|
|
|
|
|
// innBeatBytes => the new client-facing bus width
|
|
|
|
class TLWidthWidget(innerBeatBytes: Int) extends LazyModule
|
|
|
|
{
|
|
|
|
val node = TLAdapterNode(
|
2016-10-13 06:11:32 +02:00
|
|
|
clientFn = { case Seq(c) => c },
|
|
|
|
managerFn = { case Seq(m) => m.copy(beatBytes = innerBeatBytes) })
|
2016-09-08 19:38:38 +02:00
|
|
|
|
|
|
|
lazy val module = new LazyModuleImp(this) {
|
|
|
|
val io = new Bundle {
|
|
|
|
val in = node.bundleIn
|
|
|
|
val out = node.bundleOut
|
|
|
|
}
|
|
|
|
|
2016-10-13 03:09:01 +02:00
|
|
|
def merge[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = {
|
2016-09-08 19:38:38 +02:00
|
|
|
val inBytes = edgeIn.manager.beatBytes
|
|
|
|
val outBytes = edgeOut.manager.beatBytes
|
|
|
|
val ratio = outBytes / inBytes
|
|
|
|
|
|
|
|
val rdata = Reg(UInt(width = (ratio-1)*inBytes*8))
|
|
|
|
val rmask = Reg(UInt(width = (ratio-1)*inBytes))
|
|
|
|
val data = Cat(edgeIn.data(in.bits), rdata)
|
|
|
|
val mask = Cat(edgeIn.mask(in.bits), rmask)
|
|
|
|
val size = edgeIn.size(in.bits)
|
|
|
|
val hasData = edgeIn.hasData(in.bits)
|
2016-09-27 02:00:03 +02:00
|
|
|
val addr_all = in.bits match {
|
2016-09-08 19:38:38 +02:00
|
|
|
case x: TLAddrChannel => edgeIn.address(x)
|
|
|
|
case _ => UInt(0)
|
|
|
|
}
|
2016-09-27 02:00:03 +02:00
|
|
|
val addr_hi = edgeOut.addr_hi(addr_all)
|
|
|
|
val addr_lo = edgeOut.addr_lo(addr_all)
|
2016-09-08 19:38:38 +02:00
|
|
|
|
|
|
|
val count = RegInit(UInt(0, width = log2Ceil(ratio)))
|
|
|
|
val first = count === UInt(0)
|
|
|
|
val limit = UIntToOH1(size, log2Ceil(outBytes)) >> log2Ceil(inBytes)
|
|
|
|
val last = count === limit || !hasData
|
|
|
|
|
|
|
|
when (in.fire()) {
|
|
|
|
rdata := data >> inBytes*8
|
|
|
|
rmask := mask >> inBytes
|
|
|
|
count := count + UInt(1)
|
|
|
|
when (last) { count := UInt(0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
val cases = Seq.tabulate(log2Ceil(ratio)+1) { i =>
|
|
|
|
val high = outBytes
|
|
|
|
val take = (1 << i)*inBytes
|
|
|
|
(Fill(1 << (log2Ceil(ratio)-i), data(high*8-1, (high-take)*8)),
|
|
|
|
Fill(1 << (log2Ceil(ratio)-i), mask(high -1, (high-take))))
|
|
|
|
}
|
|
|
|
val dataMux = Vec.tabulate(log2Ceil(edgeIn.maxTransfer)+1) { lgSize =>
|
|
|
|
cases(min(max(lgSize - log2Ceil(inBytes), 0), log2Ceil(ratio)))._1
|
|
|
|
}
|
|
|
|
val maskMux = Vec.tabulate(log2Ceil(edgeIn.maxTransfer)+1) { lgSize =>
|
|
|
|
cases(min(max(lgSize - log2Ceil(inBytes), 0), log2Ceil(ratio)))._2
|
|
|
|
}
|
|
|
|
|
|
|
|
val dataOut = if (edgeIn.staticHasData(in.bits) == Some(false)) UInt(0) else dataMux(size)
|
2016-09-27 23:06:02 +02:00
|
|
|
val maskFull = edgeOut.mask(addr_lo, size)
|
|
|
|
val maskOut = Mux(hasData, maskMux(size) & maskFull, maskFull)
|
2016-09-08 19:38:38 +02:00
|
|
|
|
|
|
|
in.ready := out.ready || !last
|
|
|
|
out.valid := in.valid && last
|
|
|
|
out.bits := in.bits
|
|
|
|
edgeOut.data(out.bits) := dataOut
|
|
|
|
|
|
|
|
out.bits match {
|
2016-09-27 02:00:03 +02:00
|
|
|
case a: TLBundleA => a.addr_hi := addr_hi; a.mask := maskOut
|
|
|
|
case b: TLBundleB => b.addr_hi := addr_hi; b.mask := maskOut
|
|
|
|
case c: TLBundleC => c.addr_hi := addr_hi; c.addr_lo := addr_lo
|
2016-09-08 19:38:38 +02:00
|
|
|
case d: TLBundleD => ()
|
|
|
|
// addr_lo gets padded with 0s on D channel, the only lossy transform in this core
|
|
|
|
// this should be safe, because we only care about addr_log on D to determine which
|
|
|
|
// piece of data to extract when the D data bus is narrowed. Since we duplicated the
|
|
|
|
// data to all locations, addr_lo still points at a valid copy.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-13 03:09:01 +02:00
|
|
|
def split[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = {
|
2016-09-08 19:38:38 +02:00
|
|
|
val inBytes = edgeIn.manager.beatBytes
|
|
|
|
val outBytes = edgeOut.manager.beatBytes
|
|
|
|
val ratio = inBytes / outBytes
|
|
|
|
|
|
|
|
val hasData = edgeIn.hasData(in.bits)
|
|
|
|
val size = edgeIn.size(in.bits)
|
|
|
|
val data = edgeIn.data(in.bits)
|
|
|
|
val mask = edgeIn.mask(in.bits)
|
|
|
|
val addr = in.bits match {
|
|
|
|
case x: TLAddrChannel => edgeIn.address(x) >> log2Ceil(outBytes)
|
|
|
|
case _ => UInt(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
val dataSlices = Vec.tabulate(ratio) { i => data((i+1)*outBytes*8-1, i*outBytes*8) }
|
|
|
|
val maskSlices = Vec.tabulate(ratio) { i => mask((i+1)*outBytes -1, i*outBytes) }
|
|
|
|
val filter = Reg(UInt(width = ratio), init = SInt(-1, width = ratio).asUInt)
|
|
|
|
val maskR = maskSlices.map(_.orR)
|
|
|
|
|
|
|
|
// decoded_size = 1111 (for smallest), 0101, 0001 (for largest)
|
|
|
|
val sizeOH1 = UIntToOH1(size, log2Ceil(inBytes)) >> log2Ceil(outBytes)
|
|
|
|
val decoded_size = Seq.tabulate(ratio) { i => trailingZeros(i).map(!sizeOH1(_)).getOrElse(Bool(true)) }
|
|
|
|
|
|
|
|
val first = filter(ratio-1)
|
|
|
|
val new_filter = Mux(first, Cat(decoded_size.reverse), filter << 1)
|
|
|
|
val last = new_filter(ratio-1) || !hasData
|
|
|
|
when (out.fire()) {
|
|
|
|
filter := new_filter
|
|
|
|
when (!hasData) { filter := SInt(-1, width = ratio).asUInt }
|
|
|
|
}
|
|
|
|
|
|
|
|
val select = Cat(maskR.reverse) & new_filter
|
|
|
|
val dataOut = if (edgeIn.staticHasData(in.bits) == Some(false)) UInt(0) else Mux1H(select, dataSlices)
|
|
|
|
val maskOut = Mux1H(select, maskSlices)
|
|
|
|
|
2016-10-13 05:11:05 +02:00
|
|
|
out <> in
|
2016-09-08 19:38:38 +02:00
|
|
|
edgeOut.data(out.bits) := dataOut
|
|
|
|
|
|
|
|
out.bits match {
|
|
|
|
case a: TLBundleA => a.addr_hi := addr; a.mask := maskOut
|
|
|
|
case b: TLBundleB => b.addr_hi := addr; b.mask := maskOut
|
|
|
|
case c: TLBundleC => c.addr_hi := addr
|
|
|
|
case d: TLBundleD => ()
|
|
|
|
}
|
|
|
|
|
|
|
|
// addr_lo gets truncated automagically
|
2016-10-13 05:11:05 +02:00
|
|
|
|
|
|
|
// Repeat the input if we're not last
|
|
|
|
!last
|
2016-09-08 19:38:38 +02:00
|
|
|
}
|
|
|
|
|
2016-10-13 03:09:01 +02:00
|
|
|
def splice[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = {
|
2016-09-08 19:38:38 +02:00
|
|
|
if (edgeIn.manager.beatBytes == edgeOut.manager.beatBytes) {
|
|
|
|
// nothing to do; pass it through
|
|
|
|
out <> in
|
|
|
|
} else if (edgeIn.manager.beatBytes > edgeOut.manager.beatBytes) {
|
|
|
|
// split input to output
|
2016-10-13 05:11:05 +02:00
|
|
|
val repeat = Wire(Bool())
|
|
|
|
repeat := split(edgeIn, Repeater(in, repeat), edgeOut, out)
|
2016-09-08 19:38:38 +02:00
|
|
|
} else {
|
|
|
|
// merge input to output
|
|
|
|
merge(edgeIn, in, edgeOut, out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val edgeOut = node.edgesOut(0)
|
|
|
|
val edgeIn = node.edgesIn(0)
|
|
|
|
val in = io.in(0)
|
|
|
|
val out = io.out(0)
|
|
|
|
|
|
|
|
splice(edgeIn, in.a, edgeOut, out.a)
|
|
|
|
splice(edgeOut, out.d, edgeIn, in.d)
|
|
|
|
|
|
|
|
if (edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe) {
|
|
|
|
splice(edgeOut, out.b, edgeIn, in.b)
|
|
|
|
splice(edgeIn, in.c, edgeOut, out.c)
|
|
|
|
in.e.ready := out.e.ready
|
|
|
|
out.e.valid := in.e.valid
|
|
|
|
out.e.bits := in.e.bits
|
|
|
|
} else {
|
|
|
|
in.b.valid := Bool(false)
|
|
|
|
in.c.ready := Bool(true)
|
|
|
|
in.e.ready := Bool(true)
|
|
|
|
out.b.ready := Bool(true)
|
|
|
|
out.c.valid := Bool(false)
|
|
|
|
out.e.valid := Bool(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object TLWidthWidget
|
|
|
|
{
|
2016-09-09 08:06:59 +02:00
|
|
|
// applied to the TL source node; y.node := WidthWidget(x.node, 16)
|
2016-09-30 10:39:35 +02:00
|
|
|
def apply(innerBeatBytes: Int)(x: TLOutwardNode)(implicit sourceInfo: SourceInfo): TLOutwardNode = {
|
2016-09-08 19:38:38 +02:00
|
|
|
val widget = LazyModule(new TLWidthWidget(innerBeatBytes))
|
2016-09-09 08:06:59 +02:00
|
|
|
widget.node := x
|
2016-09-08 19:38:38 +02:00
|
|
|
widget.node
|
|
|
|
}
|
|
|
|
}
|
2016-09-29 00:11:05 +02:00
|
|
|
|
|
|
|
/** Synthesizeable unit tests */
|
|
|
|
import unittest._
|
|
|
|
|
|
|
|
class TLRAMWidthWidget(first: Int, second: Int) extends LazyModule {
|
|
|
|
val fuzz = LazyModule(new TLFuzzer(5000))
|
|
|
|
val model = LazyModule(new TLRAMModel)
|
|
|
|
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)))
|
|
|
|
|
|
|
|
model.node := fuzz.node
|
|
|
|
ram.node := TLFragmenter(4, 256)(
|
|
|
|
if (first == second ) { TLWidthWidget(first)(model.node) }
|
|
|
|
else {
|
|
|
|
TLWidthWidget(second)(
|
|
|
|
TLWidthWidget(first)(model.node))})
|
|
|
|
|
|
|
|
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
|
|
|
|
io.finished := fuzz.module.io.finished
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TLRAMWidthWidgetTest(little: Int, big: Int) extends UnitTest(timeout = 500000) {
|
|
|
|
io.finished := Module(LazyModule(new TLRAMWidthWidget(little,big)).module).io.finished
|
|
|
|
}
|