From e02d149cbec417e950ae74590bfbb5455bdf451c Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Wed, 14 Sep 2016 17:43:07 -0700 Subject: [PATCH] [tilelink2] Convert TileLink2 to use IrrevocableIO. Add checks to the Monitor to enforce Irrevocable semantics on TLEdges. Update the RegisterRouterTests to pass the new Monitor assertions. --- src/main/scala/uncore/tilelink2/Bundles.scala | 20 +++++--- .../scala/uncore/tilelink2/Crossing.scala | 10 ++-- src/main/scala/uncore/tilelink2/Monitor.scala | 48 ++++++++++++++----- .../scala/uncore/tilelink2/RegField.scala | 10 ++-- .../scala/uncore/tilelink2/RegMapper.scala | 5 +- .../uncore/tilelink2/RegisterCrossing.scala | 9 ++-- .../uncore/tilelink2/RegisterRouterTest.scala | 27 +++++++---- .../scala/uncore/tilelink2/WidthWidget.scala | 7 +-- 8 files changed, 90 insertions(+), 46 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Bundles.scala b/src/main/scala/uncore/tilelink2/Bundles.scala index 5698b4f8..128c5070 100644 --- a/src/main/scala/uncore/tilelink2/Bundles.scala +++ b/src/main/scala/uncore/tilelink2/Bundles.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import chisel3.util.Irrevocable abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle { @@ -100,13 +101,16 @@ object TLAtomics def isLogical(x: UInt) = x <= SWAP } -sealed trait TLChannel extends TLBundleBase +sealed trait TLChannel extends TLBundleBase { + val channelName: String +} sealed trait TLDataChannel extends TLChannel sealed trait TLAddrChannel extends TLDataChannel final class TLBundleA(params: TLBundleParameters) extends TLBundleBase(params) with TLAddrChannel { + val channelName = "'A' channel" // fixed fields during multibeat: val opcode = UInt(width = 3) val param = UInt(width = 3) // amo_opcode || perms || hint @@ -121,6 +125,7 @@ final class TLBundleA(params: TLBundleParameters) final class TLBundleB(params: TLBundleParameters) extends TLBundleBase(params) with TLAddrChannel { + val channelName = "'B' channel" // fixed fields during multibeat: val opcode = UInt(width = 3) val param = UInt(width = 3) @@ -135,6 +140,7 @@ final class TLBundleB(params: TLBundleParameters) final class TLBundleC(params: TLBundleParameters) extends TLBundleBase(params) with TLAddrChannel { + val channelName = "'C' channel" // fixed fields during multibeat: val opcode = UInt(width = 3) val param = UInt(width = 3) @@ -150,6 +156,7 @@ final class TLBundleC(params: TLBundleParameters) final class TLBundleD(params: TLBundleParameters) extends TLBundleBase(params) with TLDataChannel { + val channelName = "'D' channel" // fixed fields during multibeat: val opcode = UInt(width = 3) val param = UInt(width = 2) @@ -165,16 +172,17 @@ final class TLBundleD(params: TLBundleParameters) final class TLBundleE(params: TLBundleParameters) extends TLBundleBase(params) with TLChannel { + val channelName = "'E' channel" val sink = UInt(width = params.sinkBits) // to } class TLBundle(params: TLBundleParameters) extends TLBundleBase(params) { - val a = Decoupled(new TLBundleA(params)) - val b = Decoupled(new TLBundleB(params)).flip - val c = Decoupled(new TLBundleC(params)) - val d = Decoupled(new TLBundleD(params)).flip - val e = Decoupled(new TLBundleE(params)) + val a = Irrevocable(new TLBundleA(params)) + val b = Irrevocable(new TLBundleB(params)).flip + val c = Irrevocable(new TLBundleC(params)) + val d = Irrevocable(new TLBundleD(params)).flip + val e = Irrevocable(new TLBundleE(params)) } object TLBundle diff --git a/src/main/scala/uncore/tilelink2/Crossing.scala b/src/main/scala/uncore/tilelink2/Crossing.scala index 1f903c9a..4ef5b834 100644 --- a/src/main/scala/uncore/tilelink2/Crossing.scala +++ b/src/main/scala/uncore/tilelink2/Crossing.scala @@ -22,13 +22,13 @@ class TLAsyncCrossing(depth: Int = 8, sync: Int = 3) extends LazyModule // Transfer all TL2 bundles from/to the same domains ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => - out.a <> AsyncDecoupledCrossing(io.in_clock, io.in_reset, in.a, io.out_clock, io.out_reset, depth, sync) - in.d <> AsyncDecoupledCrossing(io.out_clock, io.out_reset, out.d, io.in_clock, io.in_reset, depth, sync) + out.a <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.a, io.out_clock, io.out_reset, depth, sync) + in.d <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.d, io.in_clock, io.in_reset, depth, sync) if (edgeOut.manager.anySupportAcquire && edgeOut.client.anySupportProbe) { - in.b <> AsyncDecoupledCrossing(io.out_clock, io.out_reset, out.b, io.in_clock, io.in_reset, depth, sync) - out.c <> AsyncDecoupledCrossing(io.in_clock, io.in_reset, in.c, io.out_clock, io.out_reset, depth, sync) - out.e <> AsyncDecoupledCrossing(io.in_clock, io.in_reset, in.e, io.out_clock, io.out_reset, depth, sync) + in.b <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.b, io.in_clock, io.in_reset, depth, sync) + out.c <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.c, io.out_clock, io.out_reset, depth, sync) + out.e <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.e, io.out_clock, io.out_reset, depth, sync) } else { in.b.valid := Bool(false) in.c.ready := Bool(true) diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index 6691a320..ffd4c5f6 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine} +import chisel3.util.{Irrevocable, IrrevocableIO} object TLMonitor { @@ -14,7 +15,7 @@ object TLMonitor } } - def legalizeFormatA(bundle: TLBundleA, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeFormatA(bundle: TLBundleA, edge: TLEdge)(implicit sourceInfo: SourceInfo) { assert (TLMessages.isA(bundle.opcode), "'A' channel has invalid opcode" + extra) // Reuse these subexpressions to save some firrtl lines @@ -79,7 +80,7 @@ object TLMonitor } } - def legalizeFormatB(bundle: TLBundleB, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeFormatB(bundle: TLBundleB, edge: TLEdge)(implicit sourceInfo: SourceInfo) { assert (TLMessages.isB(bundle.opcode), "'B' channel has invalid opcode" + extra) // Reuse these subexpressions to save some firrtl lines @@ -144,7 +145,7 @@ object TLMonitor } } - def legalizeFormatC(bundle: TLBundleC, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeFormatC(bundle: TLBundleC, edge: TLEdge)(implicit sourceInfo: SourceInfo) { assert (TLMessages.isC(bundle.opcode), "'C' channel has invalid opcode" + extra) val source_ok = edge.client.contains(bundle.source) @@ -210,7 +211,7 @@ object TLMonitor } } - def legalizeFormatD(bundle: TLBundleD, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeFormatD(bundle: TLBundleD, edge: TLEdge)(implicit sourceInfo: SourceInfo) { assert (TLMessages.isD(bundle.opcode), "'D' channel has invalid opcode" + extra) val source_ok = edge.client.contains(bundle.source) @@ -268,7 +269,7 @@ object TLMonitor } } - def legalizeFormatE(bundle: TLBundleE, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeFormatE(bundle: TLBundleE, edge: TLEdge)(implicit sourceInfo: SourceInfo) { assert (edge.manager.containsById(bundle.sink), "'E' channels carries invalid sink ID" + extra) } @@ -280,7 +281,7 @@ object TLMonitor when (bundle.e.valid) { legalizeFormatE(bundle.e.bits, edge) } } - def legalizeMultibeatA(a: DecoupledIO[TLBundleA], edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeMultibeatA(a: IrrevocableIO[TLBundleA], edge: TLEdge)(implicit sourceInfo: SourceInfo) { val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) val opcode = Reg(UInt()) val param = Reg(UInt()) @@ -307,7 +308,7 @@ object TLMonitor } } - def legalizeMultibeatB(b: DecoupledIO[TLBundleB], edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeMultibeatB(b: IrrevocableIO[TLBundleB], edge: TLEdge)(implicit sourceInfo: SourceInfo) { val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) val opcode = Reg(UInt()) val param = Reg(UInt()) @@ -334,7 +335,7 @@ object TLMonitor } } - def legalizeMultibeatC(c: DecoupledIO[TLBundleC], edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeMultibeatC(c: IrrevocableIO[TLBundleC], edge: TLEdge)(implicit sourceInfo: SourceInfo) { val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) val opcode = Reg(UInt()) val param = Reg(UInt()) @@ -364,7 +365,7 @@ object TLMonitor } } - def legalizeMultibeatD(d: DecoupledIO[TLBundleD], edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeMultibeatD(d: IrrevocableIO[TLBundleD], edge: TLEdge)(implicit sourceInfo: SourceInfo) { val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) val opcode = Reg(UInt()) val param = Reg(UInt()) @@ -394,16 +395,37 @@ object TLMonitor } } - def legalizeMultibeat(bundle: TLBundle, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { + def legalizeMultibeat(bundle: TLBundle, edge: TLEdge)(implicit sourceInfo: SourceInfo) { legalizeMultibeatA(bundle.a, edge) legalizeMultibeatB(bundle.b, edge) legalizeMultibeatC(bundle.c, edge) legalizeMultibeatD(bundle.d, edge) } - def legalize(bundle: TLBundle, edge: TLEdge)(implicit sourceInfo: SourceInfo) = { - legalizeFormat (bundle, edge) - legalizeMultibeat(bundle, edge) + def legalizeIrrevocable(irr: IrrevocableIO[TLChannel], edge: TLEdge)(implicit sourceInfo: SourceInfo) { + val last_v = RegNext(irr.valid, Bool(false)) + val last_r = RegNext(irr.ready, Bool(false)) + val last_b = RegNext(irr.bits) + val bits_changed = irr.bits.toBits === last_b.toBits + + when (last_v && !last_r) { + assert(irr.valid, s"${irr.bits.channelName} had contents that were revoked by the supplier (valid lowered)" + extra) + assert(bits_changed, s"${irr.bits.channelName} had contents that were revoked by the supplier (contents changed)" + extra) + } + } + + def legalizeIrrevocable(bundle: TLBundle, edge: TLEdge)(implicit sourceInfo: SourceInfo) { + legalizeIrrevocable(bundle.a, edge) + legalizeIrrevocable(bundle.b, edge) + legalizeIrrevocable(bundle.c, edge) + legalizeIrrevocable(bundle.d, edge) + legalizeIrrevocable(bundle.e, edge) + } + + def legalize(bundle: TLBundle, edge: TLEdge)(implicit sourceInfo: SourceInfo) { + legalizeFormat (bundle, edge) + legalizeMultibeat (bundle, edge) + legalizeIrrevocable(bundle, edge) // !!! validate source uniqueness } } diff --git a/src/main/scala/uncore/tilelink2/RegField.scala b/src/main/scala/uncore/tilelink2/RegField.scala index c3f758fc..fa258b5c 100644 --- a/src/main/scala/uncore/tilelink2/RegField.scala +++ b/src/main/scala/uncore/tilelink2/RegField.scala @@ -3,7 +3,7 @@ package uncore.tilelink2 import Chisel._ - +import chisel3.util.{Irrevocable, IrrevocableIO} import uncore.util.{SimpleRegIO} case class RegReadFn private(combinational: Boolean, fn: (Bool, Bool) => (Bool, Bool, UInt)) @@ -34,8 +34,8 @@ object RegReadFn val (ovalid, data) = x(oready) (Bool(true), ovalid, data) }) - // read from a DecoupledIO (only safe if there is a consistent source of data) - implicit def apply(x: DecoupledIO[UInt]):RegReadFn = RegReadFn(ready => { x.ready := ready; (x.valid, x.bits) }) + // read from a IrrevocableIO (only safe if there is a consistent source of data) + implicit def apply(x: IrrevocableIO[UInt]):RegReadFn = RegReadFn(ready => { x.ready := ready; (x.valid, x.bits) }) // read from a register implicit def apply(x: UInt):RegReadFn = RegReadFn(ready => (Bool(true), x)) // noop @@ -69,8 +69,8 @@ object RegWriteFn new RegWriteFn(true, { case (_, oready, data) => (Bool(true), x(oready, data)) }) - // write to a DecoupledIO (only safe if there is a consistent sink draining data) - implicit def apply(x: DecoupledIO[UInt]): RegWriteFn = RegWriteFn((valid, data) => { x.valid := valid; x.bits := data; x.ready }) + // write to a IrrevocableIO (only safe if there is a consistent sink draining data) + implicit def apply(x: IrrevocableIO[UInt]): RegWriteFn = RegWriteFn((valid, data) => { x.valid := valid; x.bits := data; x.ready }) // updates a register (or adds a mux to a wire) implicit def apply(x: UInt): RegWriteFn = RegWriteFn((valid, data) => { when (valid) { x := data }; Bool(true) }) // noop diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index f7e34e5d..88882737 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import chisel3.util.{Irrevocable, IrrevocableIO} // A bus agnostic register interface to a register-based device @@ -46,8 +47,8 @@ object RegMapper val endIndex = 1 << log2Ceil(regmap.map(_._1).max+1) val params = RegMapperParams(log2Up(endIndex), bytes, in.bits.params.extraBits) - val out = Wire(Decoupled(new RegMapperOutput(params))) - val front = Wire(Decoupled(new RegMapperInput(params))) + val out = Wire(Irrevocable(new RegMapperOutput(params))) + val front = Wire(Irrevocable(new RegMapperInput(params))) front.bits := in.bits // Must this device pipeline the control channel? diff --git a/src/main/scala/uncore/tilelink2/RegisterCrossing.scala b/src/main/scala/uncore/tilelink2/RegisterCrossing.scala index cb66aca5..843c559e 100644 --- a/src/main/scala/uncore/tilelink2/RegisterCrossing.scala +++ b/src/main/scala/uncore/tilelink2/RegisterCrossing.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import chisel3.util.{Irrevocable, IrrevocableIO} import junctions._ // A very simple flow control state machine, run in the specified clock domain @@ -24,8 +25,8 @@ class BusyRegisterCrossing(clock: Clock, reset: Bool) // RegField should support connecting to one of these class RegisterWriteIO[T <: Data](gen: T) extends Bundle { - val request = Decoupled(gen).flip() - val response = Decoupled(Bool()) // ignore .bits + val request = Irrevocable(gen).flip() + val response = Irrevocable(Bool()) // ignore .bits } // To turn on/off a domain: @@ -84,8 +85,8 @@ class RegisterWriteCrossing[T <: Data](gen: T, sync: Int = 3) extends Module { // RegField should support connecting to one of these class RegisterReadIO[T <: Data](gen: T) extends Bundle { - val request = Decoupled(Bool()).flip() // ignore .bits - val response = Decoupled(gen) + val request = Irrevocable(Bool()).flip() // ignore .bits + val response = Irrevocable(gen) } class RegisterReadCrossingIO[T <: Data](gen: T) extends Bundle { diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index be81b4c8..cdc9814e 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -33,18 +33,24 @@ class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => B io.rvalid := rvalid(rfire) io.wready := wready(wfire) - io.rdata := Mux(rfire, reg, UInt(0)) + io.rdata := reg when (wfire) { reg := io.wdata } } object RRTestCombinational { private var seed = 42 + def always: Bool => Bool = _ => Bool(true) - def random: Bool => Bool = { + + def random: Bool => Bool = { fire => seed = seed + 1 - _ => LFSR16Seed(seed)(0) + val lfsr = LFSR16Seed(seed) + val reg = RegInit(Bool(true)) + reg := Mux(reg, !fire, lfsr(0) && lfsr(1)) + reg } + def delay(x: Int): Bool => Bool = { fire => val reg = RegInit(UInt(0, width = log2Ceil(x+1))) val ready = reg === UInt(0) @@ -89,7 +95,7 @@ class RRTestRequest(val bits: Int, val rofire = io.roready && rovalid val wofire = io.woready && wovalid - io.rdata := Mux(rofire, reg, UInt(0)) + io.rdata := reg when (wofire) { reg := wdata } } @@ -115,22 +121,27 @@ object RRTestRequest } (ready(0), full(x-1), data(x-1)) } + def busy: (Bool, Bool, UInt) => (Bool, Bool, UInt) = { seed = seed + 1 (ivalid, oready, idata) => { val lfsr = LFSR16Seed(seed) val busy = RegInit(Bool(false)) val data = Reg(UInt(width = idata.getWidth)) - val progress = lfsr(0) - when (progress) { - busy := Mux(busy, !oready, ivalid) - } + val progress = RegInit(Bool(false)) val iready = progress && !busy val ovalid = progress && busy + when (progress) { + busy := Mux(busy, !oready, ivalid) + progress := !oready + } .otherwise { + progress := lfsr(0) + } when (ivalid && iready) { data := idata } (iready, ovalid, data) } } + def request(bits: Int, rflow: (Bool, Bool, UInt) => (Bool, Bool, UInt), wflow: (Bool, Bool, UInt) => (Bool, Bool, UInt)): RegField = { diff --git a/src/main/scala/uncore/tilelink2/WidthWidget.scala b/src/main/scala/uncore/tilelink2/WidthWidget.scala index 7acf1b05..cd2f141a 100644 --- a/src/main/scala/uncore/tilelink2/WidthWidget.scala +++ b/src/main/scala/uncore/tilelink2/WidthWidget.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import chisel3.util.{Irrevocable, IrrevocableIO} import scala.math.{min,max} // innBeatBytes => the new client-facing bus width @@ -19,7 +20,7 @@ class TLWidthWidget(innerBeatBytes: Int) extends LazyModule val out = node.bundleOut } - def merge[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = { + def merge[T <: TLDataChannel](edgeIn: TLEdge, in: IrrevocableIO[T], edgeOut: TLEdge, out: IrrevocableIO[T]) = { val inBytes = edgeIn.manager.beatBytes val outBytes = edgeOut.manager.beatBytes val ratio = outBytes / inBytes @@ -81,7 +82,7 @@ class TLWidthWidget(innerBeatBytes: Int) extends LazyModule } } - def split[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = { + def split[T <: TLDataChannel](edgeIn: TLEdge, in: IrrevocableIO[T], edgeOut: TLEdge, out: IrrevocableIO[T]) = { val inBytes = edgeIn.manager.beatBytes val outBytes = edgeOut.manager.beatBytes val ratio = inBytes / outBytes @@ -131,7 +132,7 @@ class TLWidthWidget(innerBeatBytes: Int) extends LazyModule // addr_lo gets truncated automagically } - def splice[T <: TLDataChannel](edgeIn: TLEdge, in: DecoupledIO[T], edgeOut: TLEdge, out: DecoupledIO[T]) = { + def splice[T <: TLDataChannel](edgeIn: TLEdge, in: IrrevocableIO[T], edgeOut: TLEdge, out: IrrevocableIO[T]) = { if (edgeIn.manager.beatBytes == edgeOut.manager.beatBytes) { // nothing to do; pass it through out <> in