Merge branch 'master' into use-companion
This commit is contained in:
		
							
								
								
									
										48
									
								
								src/main/scala/uncore/tilelink2/Buffer.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/main/scala/uncore/tilelink2/Buffer.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  |  | ||||||
|  | class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule | ||||||
|  | { | ||||||
|  |   val node = TLIdentityNode() | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val in  = io.in(0) | ||||||
|  |     val out = io.out(0) | ||||||
|  |  | ||||||
|  |     out.a <> Queue(in .a, entries, pipe) | ||||||
|  |     in .d <> Queue(out.d, entries, pipe) | ||||||
|  |      | ||||||
|  |     val edge = node.edgesOut(0) // same as edgeIn(0) | ||||||
|  |     if (edge.manager.anySupportAcquire && edge.client.anySupportProbe) { | ||||||
|  |       in .b <> Queue(out.b, entries, pipe) | ||||||
|  |       out.c <> Queue(in .c, entries, pipe) | ||||||
|  |       out.e <> Queue(out.e, entries, pipe) | ||||||
|  |     } 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 TLBuffer | ||||||
|  | { | ||||||
|  |   // applied to the TL source node; connect (TLBuffer(x.node) -> y.node) | ||||||
|  |   def apply(x: TLBaseNode, entries: Int = 2, pipe: Boolean = false)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = { | ||||||
|  |     val buffer = LazyModule(new TLBuffer(entries, pipe)) | ||||||
|  |     lazyModule.connect(x -> buffer.node) | ||||||
|  |     buffer.node | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										183
									
								
								src/main/scala/uncore/tilelink2/Bundles.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/main/scala/uncore/tilelink2/Bundles.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle | ||||||
|  | { | ||||||
|  |   override def cloneType = { | ||||||
|  |     try { | ||||||
|  |       this.getClass.getConstructors.head.newInstance(params).asInstanceOf[this.type] | ||||||
|  |     } catch { | ||||||
|  |       case e: java.lang.IllegalArgumentException => | ||||||
|  |         throwException("Unable to use GenericParameterizedBundle.cloneType on " + | ||||||
|  |                        this.getClass + ", probably because " + this.getClass + | ||||||
|  |                        "() takes more than one argument.  Consider overriding " + | ||||||
|  |                        "cloneType() on " + this.getClass, e) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | abstract class TLBundleBase(params: TLBundleParameters) extends GenericParameterizedBundle(params) | ||||||
|  |  | ||||||
|  | // common combos in lazy policy: | ||||||
|  | //   Put + Acquire | ||||||
|  | //   Release + AccessAck | ||||||
|  |  | ||||||
|  | object TLMessages  | ||||||
|  | { | ||||||
|  |   //                                  A    B    C    D    E | ||||||
|  |   val PutFullData    = UInt(0) //     .    .                   => AccessAck | ||||||
|  |   val PutPartialData = UInt(1) //     .    .                   => AccessAck | ||||||
|  |   val ArithmeticData = UInt(2) //     .    .                   => AccessAckData | ||||||
|  |   val LogicalData    = UInt(3) //     .    .                   => AccessAckData | ||||||
|  |   val Get            = UInt(4) //     .    .                   => AccessAckData | ||||||
|  |   val Hint           = UInt(5) //     .    .                   => HintAck | ||||||
|  |   val Acquire        = UInt(6) //     .                        => Grant[Data] | ||||||
|  |   val Probe          = UInt(6) //          .                   => ProbeAck[Data] | ||||||
|  |   val AccessAck      = UInt(0) //               .    . | ||||||
|  |   val AccessAckData  = UInt(1) //               .    . | ||||||
|  |   val HintAck        = UInt(2) //               .    . | ||||||
|  | //val PutThroughData = UInt(3) //               .              // future extension ? | ||||||
|  |   val ProbeAck       = UInt(4) //               . | ||||||
|  |   val ProbeAckData   = UInt(5) //               . | ||||||
|  |   val Release        = UInt(6) //               .              => ReleaseAck | ||||||
|  |   val ReleaseData    = UInt(7) //               .              => ReleaseAck | ||||||
|  |   val Grant          = UInt(4) //                    .         => GrantAck | ||||||
|  |   val GrantData      = UInt(5) //                    .         => GrantAck | ||||||
|  |   val ReleaseAck     = UInt(6) //                    . | ||||||
|  |   val GrantAck       = UInt(0) //                         . | ||||||
|  |   | ||||||
|  |   def isA(x: UInt) = x <= Acquire | ||||||
|  |   def isB(x: UInt) = x <= Probe | ||||||
|  |   def isC(x: UInt) = x <= ReleaseData | ||||||
|  |   def isD(x: UInt) = x <= ReleaseAck | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLPermissions | ||||||
|  | { | ||||||
|  |   // Cap types (Grant = new permissions, Probe = permisions <= target) | ||||||
|  |   val toT = UInt(0) | ||||||
|  |   val toB = UInt(1) | ||||||
|  |   val toN = UInt(2) | ||||||
|  |   def isCap(x: UInt) = x <= toN | ||||||
|  |  | ||||||
|  |   // Grow types (Acquire = permissions >= target) | ||||||
|  |   val NtoB = UInt(0) | ||||||
|  |   val NtoT = UInt(1) | ||||||
|  |   val BtoT = UInt(2) | ||||||
|  |   def isGrow(x: UInt) = x <= BtoT | ||||||
|  |  | ||||||
|  |   // Shrink types (ProbeAck, Release) | ||||||
|  |   val TtoB = UInt(0) | ||||||
|  |   val TtoN = UInt(1) | ||||||
|  |   val BtoN = UInt(2) | ||||||
|  |   def isShrink(x: UInt) = x <= BtoN | ||||||
|  |  | ||||||
|  |   // Report types (ProbeAck) | ||||||
|  |   val TtoT = UInt(3) | ||||||
|  |   val BtoB = UInt(4) | ||||||
|  |   val NtoN = UInt(5) | ||||||
|  |   def isReport(x: UInt) = x <= NtoN | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLAtomics | ||||||
|  | { | ||||||
|  |   // Arithmetic types | ||||||
|  |   val MIN  = UInt(0) | ||||||
|  |   val MAX  = UInt(1) | ||||||
|  |   val MINU = UInt(2) | ||||||
|  |   val MAXU = UInt(3) | ||||||
|  |   val ADD  = UInt(4) | ||||||
|  |   def isArithmetic(x: UInt) = x <= ADD | ||||||
|  |  | ||||||
|  |   // Logical types | ||||||
|  |   val XOR  = UInt(0) | ||||||
|  |   val OR   = UInt(1) | ||||||
|  |   val AND  = UInt(2) | ||||||
|  |   val SWAP = UInt(3) | ||||||
|  |   def isLogical(x: UInt) = x <= SWAP | ||||||
|  | } | ||||||
|  |  | ||||||
|  | sealed trait TLChannel | ||||||
|  | sealed trait TLDataChannel extends TLChannel | ||||||
|  | sealed trait TLAddrChannel extends TLDataChannel | ||||||
|  |  | ||||||
|  | final class TLBundleA(params: TLBundleParameters) | ||||||
|  |   extends TLBundleBase(params) with TLAddrChannel | ||||||
|  | { | ||||||
|  |   // fixed fields during multibeat: | ||||||
|  |   val opcode  = UInt(width = 3) | ||||||
|  |   val param   = UInt(width = 3) // amo_opcode || perms || hint | ||||||
|  |   val size    = UInt(width = params.sizeBits) | ||||||
|  |   val source  = UInt(width = params.sourceBits) // from | ||||||
|  |   val addr_hi = UInt(width = params.addrHiBits) // to | ||||||
|  |   // variable fields during multibeat: | ||||||
|  |   val mask    = UInt(width = params.dataBits/8) | ||||||
|  |   val data    = UInt(width = params.dataBits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final class TLBundleB(params: TLBundleParameters) | ||||||
|  |   extends TLBundleBase(params) with TLAddrChannel | ||||||
|  | { | ||||||
|  |   // fixed fields during multibeat: | ||||||
|  |   val opcode  = UInt(width = 3) | ||||||
|  |   val param   = UInt(width = 3) | ||||||
|  |   val size    = UInt(width = params.sizeBits) | ||||||
|  |   val source  = UInt(width = params.sourceBits) // to | ||||||
|  |   val addr_hi = UInt(width = params.addrHiBits) // from | ||||||
|  |   // variable fields during multibeat: | ||||||
|  |   val mask    = UInt(width = params.dataBits/8) | ||||||
|  |   val data    = UInt(width = params.dataBits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final class TLBundleC(params: TLBundleParameters) | ||||||
|  |   extends TLBundleBase(params) with TLAddrChannel | ||||||
|  | { | ||||||
|  |   // fixed fields during multibeat: | ||||||
|  |   val opcode  = UInt(width = 3) | ||||||
|  |   val param   = UInt(width = 3) | ||||||
|  |   val size    = UInt(width = params.sizeBits) | ||||||
|  |   val source  = UInt(width = params.sourceBits) // from | ||||||
|  |   val addr_hi = UInt(width = params.addrHiBits) // to | ||||||
|  |   val addr_lo = UInt(width = params.addrLoBits) // instead of mask | ||||||
|  |   // variable fields during multibeat: | ||||||
|  |   val data    = UInt(width = params.dataBits) | ||||||
|  |   val error   = Bool() // AccessAck[Data] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final class TLBundleD(params: TLBundleParameters) | ||||||
|  |   extends TLBundleBase(params) with TLDataChannel | ||||||
|  | { | ||||||
|  |   // fixed fields during multibeat: | ||||||
|  |   val opcode  = UInt(width = 3) | ||||||
|  |   val param   = UInt(width = 2) | ||||||
|  |   val size    = UInt(width = params.sizeBits) | ||||||
|  |   val source  = UInt(width = params.sourceBits) // to | ||||||
|  |   val sink    = UInt(width = params.sinkBits)   // from | ||||||
|  |   val addr_lo = UInt(width = params.addrLoBits) // instead of mask | ||||||
|  |   // variable fields during multibeat: | ||||||
|  |   val data    = UInt(width = params.dataBits) | ||||||
|  |   val error   = Bool() // AccessAck[Data], Grant[Data] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final class TLBundleE(params: TLBundleParameters) | ||||||
|  |   extends TLBundleBase(params) with TLChannel | ||||||
|  | { | ||||||
|  |   val sink = UInt(width = params.sourceBits) // 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)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLBundle | ||||||
|  | { | ||||||
|  |   def apply(params: TLBundleParameters) = new TLBundle(params) | ||||||
|  | } | ||||||
							
								
								
									
										606
									
								
								src/main/scala/uncore/tilelink2/Edges.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										606
									
								
								src/main/scala/uncore/tilelink2/Edges.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,606 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  |  | ||||||
|  | class TLEdge( | ||||||
|  |   client:  TLClientPortParameters, | ||||||
|  |   manager: TLManagerPortParameters) | ||||||
|  |   extends TLEdgeParameters(client, manager) | ||||||
|  | { | ||||||
|  |   def isHiAligned(addr_hi: UInt, lgSize: UInt): Bool = { | ||||||
|  |     if (maxLgSize == 0) Bool(true) else { | ||||||
|  |       val mask = UIntToOH1(lgSize, maxLgSize) >> log2Ceil(manager.beatBytes) | ||||||
|  |       (addr_hi & mask) === UInt(0) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def isLoAligned(addr_lo: UInt, lgSize: UInt): Bool = { | ||||||
|  |     if (maxLgSize == 0) Bool(true) else { | ||||||
|  |       val mask = UIntToOH1(lgSize, maxLgSize) | ||||||
|  |       (addr_lo & mask) === UInt(0) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // This gets used everywhere, so make the smallest circuit possible ... | ||||||
|  |   def mask(addr_lo: UInt, lgSize: UInt): UInt = { | ||||||
|  |     val lgBytes = log2Ceil(manager.beatBytes) | ||||||
|  |     val sizeOH = UIntToOH(lgSize, lgBytes) | ||||||
|  |     def helper(i: Int): Seq[(Bool, Bool)] = { | ||||||
|  |       if (i == 0) { | ||||||
|  |         Seq((lgSize >= UInt(lgBytes), Bool(true))) | ||||||
|  |       } else { | ||||||
|  |         val sub = helper(i-1) | ||||||
|  |         val size = sizeOH(lgBytes - i) | ||||||
|  |         val bit = addr_lo(lgBytes - i) | ||||||
|  |         val nbit = !bit | ||||||
|  |         Seq.tabulate (1 << i) { j => | ||||||
|  |           val (sub_acc, sub_eq) = sub(j/2) | ||||||
|  |           val eq = sub_eq && (if (j % 2 == 1) bit else nbit) | ||||||
|  |           val acc = sub_acc || (size && eq) | ||||||
|  |           (acc, eq) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     Cat(helper(lgBytes).map(_._1).reverse) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def addr_lo(mask: UInt): UInt = { | ||||||
|  |     // Almost OHToUInt, but bits set => bits not set | ||||||
|  |     def helper(mask: UInt, width: Int): UInt = { | ||||||
|  |       if (width <= 1) { | ||||||
|  |         UInt(0) | ||||||
|  |       } else if (width == 2) { | ||||||
|  |         ~mask(0, 0) | ||||||
|  |       } else { | ||||||
|  |         val mid = 1 << (log2Up(width)-1) | ||||||
|  |         val hi = mask(width-1, mid) | ||||||
|  |         val lo = mask(mid-1, 0) | ||||||
|  |         Cat(!lo.orR, helper(hi | lo, mid)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     helper(mask, bundle.dataBits/8) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def staticHasData(bundle: TLChannel): Option[Boolean] = { | ||||||
|  |     bundle match { | ||||||
|  |       case _:TLBundleA => { | ||||||
|  |         // Do there exist A messages with Data? | ||||||
|  |         val aDataYes = manager.anySupportArithmetic || manager.anySupportLogical || manager.anySupportPutFull || manager.anySupportPutPartial | ||||||
|  |         // Do there exist A messages without Data? | ||||||
|  |         val aDataNo  = manager.anySupportAcquire || manager.anySupportGet || manager.anySupportHint | ||||||
|  |         // Statically optimize the case where hasData is a constant | ||||||
|  |         if (!aDataYes) Some(false) else if (!aDataNo) Some(true) else None | ||||||
|  |       } | ||||||
|  |       case _:TLBundleB => { | ||||||
|  |         // Do there exist B messages with Data? | ||||||
|  |         val bDataYes = client.anySupportArithmetic || client.anySupportLogical || client.anySupportPutFull || client.anySupportPutPartial | ||||||
|  |         // Do there exist B messages without Data? | ||||||
|  |         val bDataNo  = client.anySupportProbe || client.anySupportGet || client.anySupportHint | ||||||
|  |         // Statically optimize the case where hasData is a constant | ||||||
|  |         if (!bDataYes) Some(false) else if (!bDataNo) Some(true) else None | ||||||
|  |       } | ||||||
|  |       case _:TLBundleC => { | ||||||
|  |         // Do there eixst C messages with Data? | ||||||
|  |         val cDataYes = client.anySupportGet || client.anySupportArithmetic || client.anySupportLogical || client.anySupportProbe | ||||||
|  |         // Do there exist C messages without Data? | ||||||
|  |         val cDataNo  = client.anySupportPutFull || client.anySupportPutPartial || client.anySupportHint || client.anySupportProbe | ||||||
|  |         if (!cDataYes) Some(false) else if (!cDataNo) Some(true) else None | ||||||
|  |       } | ||||||
|  |       case _:TLBundleD => { | ||||||
|  |         // Do there eixst D messages with Data? | ||||||
|  |         val dDataYes = manager.anySupportGet || manager.anySupportArithmetic || manager.anySupportLogical || manager.anySupportAcquire | ||||||
|  |         // Do there exist D messages without Data? | ||||||
|  |         val dDataNo  = manager.anySupportPutFull || manager.anySupportPutPartial || manager.anySupportHint || manager.anySupportAcquire | ||||||
|  |         if (!dDataYes) Some(false) else if (!dDataNo) Some(true) else None | ||||||
|  |       } | ||||||
|  |       case _:TLBundleE => Some(false) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def hasFollowUp(x: TLChannel): Bool = { | ||||||
|  |     x match { | ||||||
|  |       case a: TLBundleA => Bool(true) | ||||||
|  |       case b: TLBundleB => Bool(true) | ||||||
|  |       case c: TLBundleC => c.opcode(2) && c.opcode(1) | ||||||
|  |         //    opcode === TLMessages.Release || | ||||||
|  |         //    opcode === TLMessages.ReleaseData | ||||||
|  |       case d: TLBundleD => d.opcode(2) && !d.opcode(1) | ||||||
|  |         //    opcode === TLMessages.Grant     || | ||||||
|  |         //    opcode === TLMessages.GrantData | ||||||
|  |       case e: TLBundleE => Bool(false) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def hasData(x: TLChannel): Bool = { | ||||||
|  |     val opdata = x match { | ||||||
|  |       case a: TLBundleA => !a.opcode(2) | ||||||
|  |         //    opcode === TLMessages.PutFullData    || | ||||||
|  |         //    opcode === TLMessages.PutPartialData || | ||||||
|  |         //    opcode === TLMessages.ArithmeticData || | ||||||
|  |         //    opcode === TLMessages.LogicalData | ||||||
|  |       case b: TLBundleB => !b.opcode(2) | ||||||
|  |         //    opcode === TLMessages.PutFullData    || | ||||||
|  |         //    opcode === TLMessages.PutPartialData || | ||||||
|  |         //    opcode === TLMessages.ArithmeticData || | ||||||
|  |         //    opcode === TLMessages.LogicalData | ||||||
|  |       case c: TLBundleC => c.opcode(0) | ||||||
|  |         //    opcode === TLMessages.AccessAckData || | ||||||
|  |         //    opcode === TLMessages.ProbeAckData  || | ||||||
|  |         //    opcode === TLMessages.ReleaseData | ||||||
|  |       case d: TLBundleD => d.opcode(0) | ||||||
|  |         //    opcode === TLMessages.AccessAckData || | ||||||
|  |         //    opcode === TLMessages.GrantData | ||||||
|  |       case e: TLBundleE => Bool(false) | ||||||
|  |     } | ||||||
|  |     staticHasData(x).map(Bool(_)).getOrElse(opdata) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def size(x: TLDataChannel): UInt = { | ||||||
|  |     x match { | ||||||
|  |       case a: TLBundleA => a.size | ||||||
|  |       case b: TLBundleB => b.size | ||||||
|  |       case c: TLBundleC => c.size | ||||||
|  |       case d: TLBundleD => d.size | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def data(x: TLDataChannel): UInt = { | ||||||
|  |     x match { | ||||||
|  |       case a: TLBundleA => a.data | ||||||
|  |       case b: TLBundleB => b.data | ||||||
|  |       case c: TLBundleC => c.data | ||||||
|  |       case d: TLBundleD => d.data | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def mask(x: TLDataChannel): UInt = { | ||||||
|  |     x match { | ||||||
|  |       case a: TLBundleA => a.mask | ||||||
|  |       case b: TLBundleB => b.mask | ||||||
|  |       case c: TLBundleC => mask(c.addr_lo, c.size) | ||||||
|  |       case d: TLBundleD => mask(d.addr_lo, d.size) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def addr_lo(x: TLDataChannel): UInt = { | ||||||
|  |     x match { | ||||||
|  |       case a: TLBundleA => addr_lo(a.mask) | ||||||
|  |       case b: TLBundleB => addr_lo(b.mask) | ||||||
|  |       case c: TLBundleC => c.addr_lo | ||||||
|  |       case d: TLBundleD => d.addr_lo | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def address(x: TLAddrChannel): UInt = { | ||||||
|  |     val hi = x match { | ||||||
|  |       case a: TLBundleA => a.addr_hi | ||||||
|  |       case b: TLBundleB => b.addr_hi | ||||||
|  |       case c: TLBundleC => c.addr_hi | ||||||
|  |     } | ||||||
|  |     if (manager.beatBytes == 1) hi else Cat(hi, addr_lo(x)) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def numBeats(x: TLChannel): UInt = { | ||||||
|  |     x match { | ||||||
|  |       case _: TLBundleE => UInt(1) | ||||||
|  |       case bundle: TLDataChannel => { | ||||||
|  |         val hasData = this.hasData(bundle) | ||||||
|  |         val size = this.size(bundle) | ||||||
|  |         val cutoff = log2Ceil(manager.beatBytes) | ||||||
|  |         val small = if (manager.maxTransfer <= manager.beatBytes) Bool(true) else size <= UInt(cutoff) | ||||||
|  |         val decode = UIntToOH(size, maxLgSize+1) >> cutoff | ||||||
|  |         Mux(!hasData || small, UInt(1), decode) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLEdgeOut( | ||||||
|  |   client:  TLClientPortParameters, | ||||||
|  |   manager: TLManagerPortParameters) | ||||||
|  |   extends TLEdge(client, manager) | ||||||
|  | { | ||||||
|  |   // Transfers | ||||||
|  |   def Acquire(fromSource: UInt, toAddress: UInt, lgSize: UInt, growPermissions: UInt) = { | ||||||
|  |     require (manager.anySupportAcquire) | ||||||
|  |     val legal = manager.supportsAcquire(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.Acquire | ||||||
|  |     a.param   := growPermissions | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := SInt(-1).asUInt | ||||||
|  |     a.data    := UInt(0) | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt) = { | ||||||
|  |     require (manager.anySupportAcquire) | ||||||
|  |     val legal = manager.supportsAcquire(toAddress, lgSize) | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.Release | ||||||
|  |     c.param   := shrinkPermissions | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := UInt(0) | ||||||
|  |     c.error   := Bool(false) | ||||||
|  |     (legal, c) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt, data: UInt) = { | ||||||
|  |     require (manager.anySupportAcquire) | ||||||
|  |     val legal = manager.supportsAcquire(toAddress, lgSize) | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.ReleaseData | ||||||
|  |     c.param   := shrinkPermissions | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := data | ||||||
|  |     c.error   := Bool(false) | ||||||
|  |     (legal, c) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def ProbeAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, reportPermissions: UInt) = { | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.ProbeAck | ||||||
|  |     c.param   := reportPermissions | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := UInt(0) | ||||||
|  |     c.error   := Bool(false) | ||||||
|  |     c | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def ProbeAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, reportPermissions: UInt, data: UInt) = { | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.ProbeAckData | ||||||
|  |     c.param   := reportPermissions | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := data | ||||||
|  |     c.error   := Bool(false) | ||||||
|  |     c | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def GrantAck(toSink: UInt) = { | ||||||
|  |     val e = Wire(new TLBundleE(bundle)) | ||||||
|  |     e.sink := toSink | ||||||
|  |     e | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Accesses | ||||||
|  |   def Get(fromSource: UInt, toAddress: UInt, lgSize: UInt) = { | ||||||
|  |     require (manager.anySupportGet) | ||||||
|  |     val legal = manager.supportsGet(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.Get | ||||||
|  |     a.param   := UInt(0) | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask(toAddress, lgSize) | ||||||
|  |     a.data    := UInt(0) | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt) = { | ||||||
|  |     require (manager.anySupportPutFull) | ||||||
|  |     val legal = manager.supportsPutFull(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.PutFullData | ||||||
|  |     a.param   := UInt(0) | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask(toAddress, lgSize) | ||||||
|  |     a.data    := data | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, mask : UInt) = { | ||||||
|  |     require (manager.anySupportPutPartial) | ||||||
|  |     val legal = manager.supportsPutPartial(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.PutPartialData | ||||||
|  |     a.param   := UInt(0) | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask | ||||||
|  |     a.data    := data | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Arithmetic(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { | ||||||
|  |     require (manager.anySupportArithmetic) | ||||||
|  |     val legal = manager.supportsArithmetic(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.ArithmeticData | ||||||
|  |     a.param   := atomic | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask(toAddress, lgSize) | ||||||
|  |     a.data    := data | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Logical(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { | ||||||
|  |     require (manager.anySupportLogical) | ||||||
|  |     val legal = manager.supportsLogical(toAddress, lgSize) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.LogicalData | ||||||
|  |     a.param   := atomic | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask(toAddress, lgSize) | ||||||
|  |     a.data    := data | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = { | ||||||
|  |     require (manager.anySupportHint) | ||||||
|  |     val legal = manager.supportsHint(toAddress) | ||||||
|  |     val a = Wire(new TLBundleA(bundle)) | ||||||
|  |     a.opcode  := TLMessages.Hint | ||||||
|  |     a.param   := param | ||||||
|  |     a.size    := lgSize | ||||||
|  |     a.source  := fromSource | ||||||
|  |     a.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     a.mask    := mask(toAddress, lgSize) | ||||||
|  |     a.data    := UInt(0) | ||||||
|  |     (legal, a) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def AccessAck(b: TLBundleB): TLBundleC = AccessAck(b.source, address(b), b.size) | ||||||
|  |   def AccessAck(b: TLBundleB, error: Bool): TLBundleC = AccessAck(b.source, address(b), b.size, error) | ||||||
|  |   def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt): TLBundleC = AccessAck(fromSource, toAddress, lgSize, Bool(false)) | ||||||
|  |   def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, error: Bool) = { | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.AccessAck | ||||||
|  |     c.param   := UInt(0) | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := UInt(0) | ||||||
|  |     c.error   := error | ||||||
|  |     c | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def AccessAck(b: TLBundleB, data: UInt): TLBundleC = AccessAck(b.source, address(b), b.size, data) | ||||||
|  |   def AccessAck(b: TLBundleB, data: UInt, error: Bool): TLBundleC = AccessAck(b.source, address(b), b.size, data, error) | ||||||
|  |   def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt): TLBundleC = AccessAck(fromSource, toAddress, lgSize, data, Bool(false)) | ||||||
|  |   def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, error: Bool) = { | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.AccessAckData | ||||||
|  |     c.param   := UInt(0) | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := data | ||||||
|  |     c.error   := error | ||||||
|  |     c | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def HintAck(b: TLBundleB): TLBundleC = HintAck(b.source, address(b), b.size) | ||||||
|  |   def HintAck(fromSource: UInt, toAddress: UInt, lgSize: UInt) = { | ||||||
|  |     val c = Wire(new TLBundleC(bundle)) | ||||||
|  |     c.opcode  := TLMessages.HintAck | ||||||
|  |     c.param   := UInt(0) | ||||||
|  |     c.size    := lgSize | ||||||
|  |     c.source  := fromSource | ||||||
|  |     c.addr_hi := toAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     c.addr_lo := toAddress | ||||||
|  |     c.data    := UInt(0) | ||||||
|  |     c.error   := Bool(false) | ||||||
|  |     c | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLEdgeIn( | ||||||
|  |   client:  TLClientPortParameters, | ||||||
|  |   manager: TLManagerPortParameters) | ||||||
|  |   extends TLEdge(client, manager) | ||||||
|  | { | ||||||
|  |   // Transfers | ||||||
|  |   def Probe(fromAddress: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt) = { | ||||||
|  |     require (client.anySupportProbe) | ||||||
|  |     val legal = client.supportsProbe(fromAddress, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.Probe | ||||||
|  |     b.param   := capPermissions | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := SInt(-1).asUInt | ||||||
|  |     b.data    := UInt(0) | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt): TLBundleD = Grant(fromAddress, fromSink, toSource, lgSize, capPermissions, Bool(false)) | ||||||
|  |   def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, error: Bool) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.Grant | ||||||
|  |     d.param   := capPermissions | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := UInt(0) | ||||||
|  |     d.error   := error | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, data: UInt): TLBundleD = Grant(fromAddress, fromSink, toSource, lgSize, capPermissions, data, Bool(false)) | ||||||
|  |   def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, data: UInt, error: Bool) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.GrantData | ||||||
|  |     d.param   := capPermissions | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := data | ||||||
|  |     d.error   := error | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def ReleaseAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.ReleaseAck | ||||||
|  |     d.param   := UInt(0) | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := UInt(0) | ||||||
|  |     d.error   := Bool(false) | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Accesses | ||||||
|  |   def Get(fromAddress: UInt, toSource: UInt, lgSize: UInt) = { | ||||||
|  |     require (client.anySupportGet) | ||||||
|  |     val legal = client.supportsGet(toSource, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.Get | ||||||
|  |     b.param   := UInt(0) | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask(fromAddress, lgSize) | ||||||
|  |     b.data    := UInt(0) | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Put(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt) = { | ||||||
|  |     require (client.anySupportPutFull) | ||||||
|  |     val legal = client.supportsPutFull(toSource, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.PutFullData | ||||||
|  |     b.param   := UInt(0) | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask(fromAddress, lgSize) | ||||||
|  |     b.data    := data | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Put(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, mask : UInt) = { | ||||||
|  |     require (client.anySupportPutPartial) | ||||||
|  |     val legal = client.supportsPutPartial(toSource, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.PutPartialData | ||||||
|  |     b.param   := UInt(0) | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask | ||||||
|  |     b.data    := data | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Arithmetic(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { | ||||||
|  |     require (client.anySupportArithmetic) | ||||||
|  |     val legal = client.supportsArithmetic(toSource, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.ArithmeticData | ||||||
|  |     b.param   := atomic | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask(fromAddress, lgSize) | ||||||
|  |     b.data    := data | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Logical(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { | ||||||
|  |     require (client.anySupportLogical) | ||||||
|  |     val legal = client.supportsLogical(toSource, lgSize) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.LogicalData | ||||||
|  |     b.param   := atomic | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask(fromAddress, lgSize) | ||||||
|  |     b.data    := data | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def Hint(fromAddress: UInt, toSource: UInt, lgSize: UInt, param: UInt) = { | ||||||
|  |     require (client.anySupportHint) | ||||||
|  |     val legal = client.supportsHint(toSource) | ||||||
|  |     val b = Wire(new TLBundleB(bundle)) | ||||||
|  |     b.opcode  := TLMessages.Hint | ||||||
|  |     b.param   := param | ||||||
|  |     b.size    := lgSize | ||||||
|  |     b.source  := toSource | ||||||
|  |     b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes) | ||||||
|  |     b.mask    := mask(fromAddress, lgSize) | ||||||
|  |     b.data    := UInt(0) | ||||||
|  |     (legal, b) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def AccessAck(a: TLBundleA, fromSink: UInt): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size) | ||||||
|  |   def AccessAck(a: TLBundleA, fromSink: UInt, error: Bool): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, error) | ||||||
|  |   def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt): TLBundleD = AccessAck(fromAddress, fromSink, toSource, lgSize, Bool(false)) | ||||||
|  |   def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, error: Bool) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.AccessAck | ||||||
|  |     d.param   := UInt(0) | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := UInt(0) | ||||||
|  |     d.error   := error | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def AccessAck(a: TLBundleA, fromSink: UInt, data: UInt): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, data) | ||||||
|  |   def AccessAck(a: TLBundleA, fromSink: UInt, data: UInt, error: Bool): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, data, error) | ||||||
|  |   def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, data: UInt): TLBundleD = AccessAck(fromAddress, fromSink, toSource, lgSize, data, Bool(false)) | ||||||
|  |   def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, data: UInt, error: Bool) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.AccessAckData | ||||||
|  |     d.param   := UInt(0) | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := data | ||||||
|  |     d.error   := error | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def HintAck(a: TLBundleA, sink: UInt = UInt(0)): TLBundleD = HintAck(address(a), sink, a.source, a.size) | ||||||
|  |   def HintAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = { | ||||||
|  |     val d = Wire(new TLBundleD(bundle)) | ||||||
|  |     d.opcode  := TLMessages.HintAck | ||||||
|  |     d.param   := UInt(0) | ||||||
|  |     d.size    := lgSize | ||||||
|  |     d.source  := toSource | ||||||
|  |     d.sink    := fromSink | ||||||
|  |     d.addr_lo := fromAddress | ||||||
|  |     d.data    := UInt(0) | ||||||
|  |     d.error   := Bool(false) | ||||||
|  |     d | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										249
									
								
								src/main/scala/uncore/tilelink2/Fragmenter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/main/scala/uncore/tilelink2/Fragmenter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  | import scala.math.{min,max} | ||||||
|  |  | ||||||
|  | // minSize: minimum size of transfers supported by all outward managers | ||||||
|  | // maxSize: maximum size of transfers supported after the Fragmenter is applied | ||||||
|  | // alwaysMin: fragment all requests down to minSize (else fragment to maximum supported by manager) | ||||||
|  | // Fragmenter modifies: PutFull, PutPartial, LogicalData, Get, Hint | ||||||
|  | // Fragmenter passes: ArithmeticData (truncated to minSize if alwaysMin) | ||||||
|  | // Fragmenter breaks: Acquire (and thus cuts BCE channels) | ||||||
|  | class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) extends LazyModule | ||||||
|  | { | ||||||
|  |   require (isPow2 (maxSize)) | ||||||
|  |   require (isPow2 (minSize)) | ||||||
|  |   require (minSize < maxSize) | ||||||
|  |  | ||||||
|  |   val fragmentBits = log2Ceil(maxSize / minSize) | ||||||
|  |  | ||||||
|  |   def expandTransfer(x: TransferSizes) = if (!x) x else { | ||||||
|  |     require (x.max >= minSize) // validate that we can apply the fragmenter correctly | ||||||
|  |     TransferSizes(x.min, maxSize) | ||||||
|  |   } | ||||||
|  |   def shrinkTransfer(x: TransferSizes) = | ||||||
|  |     if (!alwaysMin) x else | ||||||
|  |     if (x.min <= minSize) TransferSizes(x.min, min(minSize, x.max)) else | ||||||
|  |     TransferSizes.none | ||||||
|  |   def mapManager(m: TLManagerParameters) = m.copy( | ||||||
|  |     supportsAcquire    = TransferSizes.none, // this adapter breaks acquires | ||||||
|  |     supportsArithmetic = shrinkTransfer(m.supportsArithmetic), | ||||||
|  |     supportsLogical    = expandTransfer(m.supportsLogical), | ||||||
|  |     supportsGet        = expandTransfer(m.supportsGet), | ||||||
|  |     supportsPutFull    = expandTransfer(m.supportsPutFull), | ||||||
|  |     supportsPutPartial = expandTransfer(m.supportsPutPartial)) | ||||||
|  |   def mapClient(c: TLClientParameters) = c.copy( | ||||||
|  |     sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits), | ||||||
|  |     // since we break Acquires, none of these work either: | ||||||
|  |     supportsProbe      = TransferSizes.none, | ||||||
|  |     supportsArithmetic = TransferSizes.none, | ||||||
|  |     supportsLogical    = TransferSizes.none, | ||||||
|  |     supportsGet        = TransferSizes.none, | ||||||
|  |     supportsPutFull    = TransferSizes.none, | ||||||
|  |     supportsPutPartial = TransferSizes.none, | ||||||
|  |     supportsHint       = false) | ||||||
|  |  | ||||||
|  |   val node = TLAdapterNode( | ||||||
|  |     clientFn  = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) }, | ||||||
|  |     managerFn = { case Seq(m) => m.copy(managers = m.managers.map(mapManager)) }) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // All managers must share a common FIFO domain (responses might end up interleaved) | ||||||
|  |     val edgeOut   = node.edgesOut(0) | ||||||
|  |     val edgeIn    = node.edgesIn(0) | ||||||
|  |     val manager   = edgeOut.manager | ||||||
|  |     val managers  = manager.managers | ||||||
|  |     val beatBytes = manager.beatBytes | ||||||
|  |     val fifoId = managers(0).fifoId | ||||||
|  |     require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _)) | ||||||
|  |  | ||||||
|  |     // We don't support fragmenting to sub-beat accesses | ||||||
|  |     require (minSize >= beatBytes) | ||||||
|  |  | ||||||
|  |     /* The Fragmenter is a bit tricky, because there are 5 sizes in play: | ||||||
|  |      *   max  size -- the maximum transfer size possible | ||||||
|  |      *   orig size -- the original pre-fragmenter size | ||||||
|  |      *   frag size -- the modified post-fragmenter size | ||||||
|  |      *   min  size -- the threshold below which frag=orig | ||||||
|  |      *   beat size -- the amount transfered on any given beat | ||||||
|  |      * | ||||||
|  |      * The relationships are as follows: | ||||||
|  |      *   max >= orig >= frag | ||||||
|  |      *   max >  min  >= beat | ||||||
|  |      * It IS possible that orig <= min (then frag=orig; ie: no fragmentation) | ||||||
|  |      * | ||||||
|  |      * The fragment# (sent via TL.source) is measured in multiples of min size. | ||||||
|  |      * Meanwhile, to track the progress, counters measure in multiples of beat size. | ||||||
|  |      * | ||||||
|  |      * Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16. | ||||||
|  |      * | ||||||
|  |      * in.A    out.A (frag#)  out.D (frag#)  in.D     gen# ack# | ||||||
|  |      * get64   get16  6       ackD16  6      ackD64    12   15 | ||||||
|  |      *                        ackD16  6      ackD64         14 | ||||||
|  |      *                        ackD16  6      ackD64         13 | ||||||
|  |      *                        ackD16  6      ackD64         12 | ||||||
|  |      *         get16  4       ackD16  4      ackD64    8    11 | ||||||
|  |      *                        ackD16  4      ackD64         10 | ||||||
|  |      *                        ackD16  4      ackD64         9 | ||||||
|  |      *                        ackD16  4      ackD64         8 | ||||||
|  |      *         get16  2       ackD16  2      ackD64    4    7 | ||||||
|  |      *                        ackD16  2      ackD64         6 | ||||||
|  |      *                        ackD16  2      ackD64         5 | ||||||
|  |      *                        ackD16  2      ackD64         4 | ||||||
|  |      *         get16  0       ackD16  0      ackD64    0    3 | ||||||
|  |      *                        ackD16  0      ackD64         2 | ||||||
|  |      *                        ackD16  0      ackD64         1 | ||||||
|  |      *                        ackD16  0      ackD64         0 | ||||||
|  |      * | ||||||
|  |      * get8    get8   0       ackD8   0      ackD8     0    1 | ||||||
|  |      *                        ackD8   0      ackD8          0 | ||||||
|  |      * | ||||||
|  |      * get4    get4   0       ackD4   0      ackD4     0    0 | ||||||
|  |      * get1    get1   0       ackD1   0      ackD1     0    0 | ||||||
|  |      * | ||||||
|  |      * put64   put16  6                                15    | ||||||
|  |      * put64   put16  6                                14 | ||||||
|  |      * put64   put16  6                                13 | ||||||
|  |      * put64   put16  6       ack16   6                12    12 | ||||||
|  |      * put64   put16  4                                11 | ||||||
|  |      * put64   put16  4                                10 | ||||||
|  |      * put64   put16  4                                9 | ||||||
|  |      * put64   put16  4       ack16   4                8     8 | ||||||
|  |      * put64   put16  2                                7 | ||||||
|  |      * put64   put16  2                                6 | ||||||
|  |      * put64   put16  2                                5 | ||||||
|  |      * put64   put16  2       ack16   2                4     4 | ||||||
|  |      * put64   put16  0                                3 | ||||||
|  |      * put64   put16  0                                2 | ||||||
|  |      * put64   put16  0                                1 | ||||||
|  |      * put64   put16  0       ack16   0      ack64     0     0 | ||||||
|  |      * | ||||||
|  |      * put8    put8   0                                1 | ||||||
|  |      * put8    put8   0       ack8    0      ack8      0     0 | ||||||
|  |      * | ||||||
|  |      * put4    put4   0       ack4    0      ack4      0     0 | ||||||
|  |      * put1    put1   0       ack1    0      ack1      0     0 | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     val in = io.in(0) | ||||||
|  |     val out = io.out(0) | ||||||
|  |  | ||||||
|  |     val counterBits = log2Up(maxSize/beatBytes) | ||||||
|  |     val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer | ||||||
|  |  | ||||||
|  |     // First, handle the return path | ||||||
|  |     val acknum = RegInit(UInt(0, width = counterBits)) | ||||||
|  |     val dOrig = Reg(UInt()) | ||||||
|  |     val dFragnum = out.d.bits.source(fragmentBits-1, 0) | ||||||
|  |     val dFirst = acknum === UInt(0) | ||||||
|  |     val dsizeOH  = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1) | ||||||
|  |     val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Ceil(maxDownSize)) | ||||||
|  |     val dHasData = edgeOut.hasData(out.d.bits) | ||||||
|  |  | ||||||
|  |     // calculate new acknum | ||||||
|  |     val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes) | ||||||
|  |     val acknum_size = dsizeOH1 >> log2Ceil(beatBytes) | ||||||
|  |     assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0)) | ||||||
|  |     val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0)) | ||||||
|  |     val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes)) | ||||||
|  |     // calculate the original size | ||||||
|  |     val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1) | ||||||
|  |  | ||||||
|  |     when (out.d.fire()) { | ||||||
|  |       acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement) | ||||||
|  |       when (dFirst) { dOrig := dFirst_size } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Swallow up non-data ack fragments | ||||||
|  |     val drop = (out.d.bits.opcode === TLMessages.AccessAck) && (dFragnum =/= UInt(0)) | ||||||
|  |     out.d.ready := in.d.ready || drop | ||||||
|  |     in.d.valid  := out.d.valid && !drop | ||||||
|  |     in.d.bits   := out.d.bits // pass most stuff unchanged | ||||||
|  |     in.d.bits.source := out.d.bits.source >> fragmentBits | ||||||
|  |     in.d.bits.size   := Mux(dFirst, dFirst_size, dOrig) | ||||||
|  |  | ||||||
|  |     // What maximum transfer sizes do downstream devices support? | ||||||
|  |     val maxArithmetics = managers.map(_.supportsArithmetic.max) | ||||||
|  |     val maxLogicals    = managers.map(_.supportsLogical.max) | ||||||
|  |     val maxGets        = managers.map(_.supportsGet.max) | ||||||
|  |     val maxPutFulls    = managers.map(_.supportsPutFull.max) | ||||||
|  |     val maxPutPartials = managers.map(_.supportsPutPartial.max) | ||||||
|  |     val maxHints       = managers.map(m => if (m.supportsHint) maxDownSize else 0) | ||||||
|  |  | ||||||
|  |     // We assume that the request is valid => size 0 is impossible | ||||||
|  |     val lgMinSize = UInt(log2Ceil(minSize)) | ||||||
|  |     val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |     val maxLgLogicals    = maxLogicals   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |     val maxLgGets        = maxGets       .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |     val maxLgPutFulls    = maxPutFulls   .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |     val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |     val maxLgHints       = maxHints      .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) | ||||||
|  |  | ||||||
|  |     // If this is infront of a single manager, these become constants | ||||||
|  |     val find = manager.find(edgeIn.address(in.a.bits)) | ||||||
|  |     val maxLgArithmetic  = Mux1H(find, maxLgArithmetics) | ||||||
|  |     val maxLgLogical     = Mux1H(find, maxLgLogicals) | ||||||
|  |     val maxLgGet         = Mux1H(find, maxLgGets) | ||||||
|  |     val maxLgPutFull     = Mux1H(find, maxLgPutFulls) | ||||||
|  |     val maxLgPutPartial  = Mux1H(find, maxLgPutPartials) | ||||||
|  |     val maxLgHint        = Mux1H(find, maxLgHints) | ||||||
|  |  | ||||||
|  |     val limit = if (alwaysMin) lgMinSize else  | ||||||
|  |       MuxLookup(in.a.bits.opcode, lgMinSize, Array( | ||||||
|  |         TLMessages.PutFullData    -> maxLgPutFull, | ||||||
|  |         TLMessages.PutPartialData -> maxLgPutPartial, | ||||||
|  |         TLMessages.ArithmeticData -> maxLgArithmetic, | ||||||
|  |         TLMessages.LogicalData    -> maxLgLogical, | ||||||
|  |         TLMessages.Get            -> maxLgGet, | ||||||
|  |         TLMessages.Hint           -> maxLgHint)) | ||||||
|  |  | ||||||
|  |     val aOrig = in.a.bits.size | ||||||
|  |     val aFrag = Mux(aOrig > limit, limit, aOrig) | ||||||
|  |     val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize)) | ||||||
|  |     val aFragOH1 = UIntToOH1(aFrag, log2Ceil(maxDownSize)) | ||||||
|  |     val aHasData = node.edgesIn(0).hasData(in.a.bits) | ||||||
|  |     val aMask = Mux(aHasData, UInt(0), aFragOH1) | ||||||
|  |  | ||||||
|  |     val gennum = RegInit(UInt(0, width = counterBits)) | ||||||
|  |     val aFirst = gennum === UInt(0) | ||||||
|  |     val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1)) | ||||||
|  |     val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe | ||||||
|  |     val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize))) | ||||||
|  |  | ||||||
|  |     when (out.a.fire()) { gennum := new_gennum } | ||||||
|  |  | ||||||
|  |     val delay = !aHasData && aFragnum =/= UInt(0) | ||||||
|  |     out.a.valid := in.a.valid | ||||||
|  |     in.a.ready := out.a.ready && !delay | ||||||
|  |     out.a.bits := in.a.bits | ||||||
|  |     out.a.bits.addr_hi := in.a.bits.addr_hi | (~aFragnum << log2Ceil(minSize/beatBytes) & aOrigOH1 >> log2Ceil(beatBytes)) | ||||||
|  |     out.a.bits.source := Cat(in.a.bits.source, aFragnum) | ||||||
|  |     out.a.bits.size := aFrag | ||||||
|  |  | ||||||
|  |     // Tie off unused channels | ||||||
|  |     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 TLFragmenter | ||||||
|  | { | ||||||
|  |   // applied to the TL source node; connect (TLFragmenter(x.node, 256, 4) -> y.node) | ||||||
|  |   def apply(x: TLBaseNode, minSize: Int, maxSize: Int, alwaysMin: Boolean = false)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = { | ||||||
|  |     val fragmenter = LazyModule(new TLFragmenter(minSize, maxSize, alwaysMin)) | ||||||
|  |     lazyModule.connect(x -> fragmenter.node) | ||||||
|  |     fragmenter.node | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								src/main/scala/uncore/tilelink2/GPIO.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/main/scala/uncore/tilelink2/GPIO.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | case class GPIOParams(num: Int, address: BigInt) | ||||||
|  |  | ||||||
|  | trait GPIOBundle | ||||||
|  | { | ||||||
|  |   val params: GPIOParams | ||||||
|  |   val gpio = UInt(width = params.num) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | trait GPIOModule extends HasRegMap | ||||||
|  | { | ||||||
|  |   val params: GPIOParams | ||||||
|  |   val io: GPIOBundle | ||||||
|  |  | ||||||
|  |   val state = RegInit(UInt(0)) | ||||||
|  |   io.gpio := state | ||||||
|  |  | ||||||
|  |   regmap(0 -> Seq(RegField(params.num, state))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create a concrete TL2 version of the abstract GPIO slave | ||||||
|  | class TLGPIO(p: GPIOParams) extends TLRegisterRouter(p.address)( | ||||||
|  |   new TLRegBundle(p, _)    with GPIOBundle)( | ||||||
|  |   new TLRegModule(p, _, _) with GPIOModule) | ||||||
							
								
								
									
										99
									
								
								src/main/scala/uncore/tilelink2/HintHandler.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/main/scala/uncore/tilelink2/HintHandler.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  |  | ||||||
|  | // Acks Hints for managers that don't support them or Acks all Hints if !passthrough | ||||||
|  | class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true) extends LazyModule | ||||||
|  | { | ||||||
|  |   val node = TLAdapterNode( | ||||||
|  |     clientFn  = { case Seq(c) => if (supportClients)  c.copy(clients  = c.clients .map(_.copy(supportsHint = true))) else c }, | ||||||
|  |     managerFn = { case Seq(m) => if (supportManagers) m.copy(managers = m.managers.map(_.copy(supportsHint = true))) else m }) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val in  = io.in(0) | ||||||
|  |     val out = io.out(0) | ||||||
|  |     val edgeIn  = node.edgesIn(0) | ||||||
|  |     val edgeOut = node.edgesOut(0) | ||||||
|  |  | ||||||
|  |     // Don't add support for clients if there is no BCE channel | ||||||
|  |     val bce = edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe | ||||||
|  |     require (!supportClients || bce) | ||||||
|  |  | ||||||
|  |     if (supportManagers) { | ||||||
|  |       val handleA = if (passthrough) !edgeOut.manager.supportsHint(edgeIn.address(in.a.bits)) else Bool(true) | ||||||
|  |       val bypassD = handleA && in.a.bits.opcode === TLMessages.Hint | ||||||
|  |  | ||||||
|  |       // Prioritize existing D traffic over HintAck | ||||||
|  |       in.d.valid  := out.d.valid || (bypassD && in.a.valid) | ||||||
|  |       out.d.ready := in.d.ready | ||||||
|  |       in.d.bits   := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits)) | ||||||
|  |  | ||||||
|  |       in.a.ready  := Mux(bypassD, in.d.ready && !out.d.valid, out.a.ready) | ||||||
|  |       out.a.valid := in.a.valid && !bypassD | ||||||
|  |       out.a.bits  := in.a.bits | ||||||
|  |     } else { | ||||||
|  |       out.a.valid := in.a.valid | ||||||
|  |       in.a.ready := out.a.ready | ||||||
|  |       out.a.bits := in.a.bits | ||||||
|  |  | ||||||
|  |       in.d.valid := out.d.valid | ||||||
|  |       out.d.ready := in.d.ready | ||||||
|  |       in.d.bits := out.d.bits | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (supportClients) { | ||||||
|  |       val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source) else Bool(true) | ||||||
|  |       val bypassC = handleB && out.b.bits.opcode === TLMessages.Hint | ||||||
|  |  | ||||||
|  |       // Prioritize existing C traffic over HintAck | ||||||
|  |       out.c.valid := in.c.valid || (bypassC && in.b.valid) | ||||||
|  |       in.c.ready  := out.c.ready | ||||||
|  |       out.c.bits  := Mux(in.c.valid, in.c.bits, edgeOut.HintAck(out.b.bits)) | ||||||
|  |  | ||||||
|  |       out.b.ready := Mux(bypassC, out.c.ready && !in.c.valid, in.b.ready) | ||||||
|  |       in.b.valid  := out.b.valid && !bypassC | ||||||
|  |       in.b.bits   := out.b.bits | ||||||
|  |     } else if (bce) { | ||||||
|  |       in.b.valid := out.b.valid | ||||||
|  |       out.b.ready := in.b.ready | ||||||
|  |       in.b.bits := out.b.bits | ||||||
|  |  | ||||||
|  |       out.c.valid := in.c.valid | ||||||
|  |       in.c.ready := out.c.ready | ||||||
|  |       out.c.bits := in.c.bits | ||||||
|  |     } else { | ||||||
|  |       in.b.valid := Bool(false) | ||||||
|  |       in.c.ready := Bool(true) | ||||||
|  |       out.b.ready := Bool(true) | ||||||
|  |       out.c.valid := Bool(false) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (bce) { | ||||||
|  |       // Pass E through unchanged | ||||||
|  |       out.e.valid := in.e.valid | ||||||
|  |       in.e.ready := out.e.ready | ||||||
|  |       out.e.bits := in.e.bits | ||||||
|  |     } else { | ||||||
|  |       in.e.ready := Bool(true) | ||||||
|  |       out.e.valid := Bool(false) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLHintHandler | ||||||
|  | { | ||||||
|  |   // applied to the TL source node; connect (TLHintHandler(x.node) -> y.node) | ||||||
|  |   def apply(x: TLBaseNode, supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = { | ||||||
|  |     val hints = LazyModule(new TLHintHandler(supportManagers, supportClients, passthrough)) | ||||||
|  |     lazyModule.connect(x -> hints.node) | ||||||
|  |     hints.node | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								src/main/scala/uncore/tilelink2/LazyModule.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/main/scala/uncore/tilelink2/LazyModule.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo._ | ||||||
|  |  | ||||||
|  | abstract class LazyModule | ||||||
|  | { | ||||||
|  |   protected[tilelink2] var bindings = List[() => Unit]() | ||||||
|  |   protected[tilelink2] var children = List[LazyModule]() | ||||||
|  |   protected[tilelink2] var info: SourceInfo = UnlocatableSourceInfo | ||||||
|  |   protected[tilelink2] val parent = LazyModule.stack.headOption | ||||||
|  |  | ||||||
|  |   LazyModule.stack = this :: LazyModule.stack | ||||||
|  |   parent.foreach(p => p.children = this :: p.children) | ||||||
|  |  | ||||||
|  |   // Use as: connect(source -> sink, source2 -> sink2, ...) | ||||||
|  |   def connect[PO, PI, EO, EI, B <: Bundle](edges: (BaseNode[PO, PI, EO, EI, B], BaseNode[PO, PI, EO, EI, B])*)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     edges.foreach { case (source, sink) => | ||||||
|  |       bindings = (source edge sink) :: bindings | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def module: LazyModuleImp | ||||||
|  |   implicit val lazyModule = this | ||||||
|  |  | ||||||
|  |   protected[tilelink2] 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 () } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object LazyModule | ||||||
|  | { | ||||||
|  |   protected[tilelink2] var stack = List[LazyModule]() | ||||||
|  |  | ||||||
|  |   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 && (stack.head eq bc)) | ||||||
|  |     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) | ||||||
|  |  | ||||||
|  |   override def desiredName = outer.getClass.getName.split('.').last | ||||||
|  |   outer.instantiate() | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								src/main/scala/uncore/tilelink2/Legacy.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/main/scala/uncore/tilelink2/Legacy.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import cde.Parameters | ||||||
|  | import uncore.tilelink._ | ||||||
|  | import uncore.constants._ | ||||||
|  |  | ||||||
|  | // Instantiate 'val p' before HasTileLinkParameters tries to use it | ||||||
|  | abstract class LegacyLazyModuleImp(module: LazyModule)(implicit val p: Parameters) | ||||||
|  |   extends LazyModuleImp(module) with HasTileLinkParameters | ||||||
|  |  | ||||||
|  | class TLLegacy(implicit val p: Parameters) extends LazyModule with HasTileLinkParameters | ||||||
|  | { | ||||||
|  |   // TL legacy clients don't support anything fancy | ||||||
|  |   val node = TLClientNode(TLClientParameters( | ||||||
|  |     sourceId = IdRange(0, 1 << tlClientXactIdBits))) | ||||||
|  |  | ||||||
|  |   lazy val module = new LegacyLazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val legacy = new ClientUncachedTileLinkIO()(p).flip | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TL legacy is dumb. All managers must support it's accesses. | ||||||
|  |     val edge = node.edgesOut(0) | ||||||
|  |     require (edge.manager.beatBytes == tlDataBytes) | ||||||
|  |     edge.manager.managers.foreach { m => | ||||||
|  |       // If a slave supports read at all, it must support all TL Legacy requires | ||||||
|  |       if (m.supportsGet) { | ||||||
|  |         require (m.supportsGet.contains(TransferSizes(tlDataBytes))) | ||||||
|  |         require (m.supportsGet.contains(TransferSizes(tlDataBeats * tlDataBytes))) | ||||||
|  |       } | ||||||
|  |       // Likewise, any put support must mean full put support | ||||||
|  |       if (m.supportsPutPartial) { | ||||||
|  |         require (m.supportsPutPartial.contains(TransferSizes(tlDataBytes))) | ||||||
|  |         require (m.supportsPutPartial.contains(TransferSizes(tlDataBeats * tlDataBytes))) | ||||||
|  |       } | ||||||
|  |       // Any atomic support => must support 32-bit up to beat size of all types | ||||||
|  |       if (m.supportsArithmetic || m.supportsLogical) { | ||||||
|  |         require (m.supportsArithmetic.contains(TransferSizes(4, tlDataBytes))) | ||||||
|  |         require (m.supportsLogical   .contains(TransferSizes(4, tlDataBytes))) | ||||||
|  |       } | ||||||
|  |       // We straight-up require hints | ||||||
|  |       require (edge.manager.allSupportHint) | ||||||
|  |     } | ||||||
|  |     // TL legacy will not generate PutFull | ||||||
|  |     // During conversion from TL Legacy, we won't support Acquire | ||||||
|  |  | ||||||
|  |     // Must be able to fit TL2 sink_id into TL legacy | ||||||
|  |     require ((1 << tlManagerXactIdBits) >= edge.manager.endSinkId) | ||||||
|  |  | ||||||
|  |     val out = io.out(0) | ||||||
|  |     out.a.valid := io.legacy.acquire.valid | ||||||
|  |     out.d.ready := io.legacy.grant  .ready | ||||||
|  |     io.legacy.acquire.ready := out.a.ready | ||||||
|  |     io.legacy.grant  .valid := out.d.valid | ||||||
|  |  | ||||||
|  |     val source  = io.legacy.acquire.bits.client_xact_id | ||||||
|  |     val data    = io.legacy.acquire.bits.data | ||||||
|  |     val wmask   = io.legacy.acquire.bits.wmask() | ||||||
|  |     val address = io.legacy.acquire.bits.full_addr() | ||||||
|  |  | ||||||
|  |     val beat  = UInt(log2Ceil(tlDataBytes)) | ||||||
|  |     val block = UInt(log2Ceil(tlDataBytes*tlDataBeats)) | ||||||
|  |  | ||||||
|  |     // Only create atomic messages if TL2 managers support them | ||||||
|  |     val atomics = if (edge.manager.anySupportLogical) { | ||||||
|  |       MuxLookup(io.legacy.acquire.bits.op_code(), Wire(new TLBundleA(edge.bundle)), Array( | ||||||
|  |         MemoryOpConstants.M_XA_SWAP -> edge.Logical(source, address, beat, data, TLAtomics.SWAP)._2, | ||||||
|  |         MemoryOpConstants.M_XA_XOR  -> edge.Logical(source, address, beat, data, TLAtomics.XOR) ._2, | ||||||
|  |         MemoryOpConstants.M_XA_OR   -> edge.Logical(source, address, beat, data, TLAtomics.OR)  ._2, | ||||||
|  |         MemoryOpConstants.M_XA_AND  -> edge.Logical(source, address, beat, data, TLAtomics.AND) ._2, | ||||||
|  |         MemoryOpConstants.M_XA_ADD  -> edge.Arithmetic(source, address, beat, data, TLAtomics.ADD)._2, | ||||||
|  |         MemoryOpConstants.M_XA_MIN  -> edge.Arithmetic(source, address, beat, data, TLAtomics.MIN)._2, | ||||||
|  |         MemoryOpConstants.M_XA_MAX  -> edge.Arithmetic(source, address, beat, data, TLAtomics.MAX)._2, | ||||||
|  |         MemoryOpConstants.M_XA_MINU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MINU)._2, | ||||||
|  |         MemoryOpConstants.M_XA_MAXU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MAXU)._2)) | ||||||
|  |     } else { | ||||||
|  |       Wire(new TLBundleA(edge.bundle)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     out.a.bits := MuxLookup(io.legacy.acquire.bits.a_type, Wire(new TLBundleA(edge.bundle)), Array( | ||||||
|  |       Acquire.getType         -> edge.Get (source, address, beat) ._2, | ||||||
|  |       Acquire.getBlockType    -> edge.Get (source, address, block)._2, | ||||||
|  |       Acquire.putType         -> edge.Put (source, address, beat,  data, wmask)._2, | ||||||
|  |       Acquire.putBlockType    -> edge.Put (source, address, block, data, wmask)._2, | ||||||
|  |       Acquire.getPrefetchType -> edge.Hint(source, address, block, UInt(0))._2, | ||||||
|  |       Acquire.putPrefetchType -> edge.Hint(source, address, block, UInt(1))._2, | ||||||
|  |       Acquire.putAtomicType   -> atomics)) | ||||||
|  |  | ||||||
|  |     val beatMask  = UInt(tlDataBytes-1) | ||||||
|  |     val blockMask = UInt(tlDataBytes*tlDataBeats-1) | ||||||
|  |     val addressMask = MuxLookup(io.legacy.acquire.bits.a_type, beatMask, Array( | ||||||
|  |       Acquire.getType         -> beatMask, | ||||||
|  |       Acquire.getBlockType    -> blockMask, | ||||||
|  |       Acquire.putType         -> beatMask, | ||||||
|  |       Acquire.putBlockType    -> blockMask, | ||||||
|  |       Acquire.getPrefetchType -> blockMask, | ||||||
|  |       Acquire.putPrefetchType -> blockMask, | ||||||
|  |       Acquire.putAtomicType   -> beatMask)) | ||||||
|  |  | ||||||
|  |     // Get rid of some unneeded muxes | ||||||
|  |     out.a.bits.source  := source | ||||||
|  |     out.a.bits.data    := data | ||||||
|  |     out.a.bits.addr_hi := ~(~address | addressMask) >> log2Ceil(tlDataBytes) | ||||||
|  |  | ||||||
|  |     // TL legacy does not support bus errors | ||||||
|  |     assert (!out.d.bits.error) | ||||||
|  |  | ||||||
|  |     // Recreate the beat address counter | ||||||
|  |     val beatCounter = RegInit(UInt(0, width = tlBeatAddrBits)) | ||||||
|  |     when (out.d.fire() && edge.hasData(out.d.bits) && out.d.bits.size === block) { | ||||||
|  |       beatCounter := beatCounter + UInt(1) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val grant = io.legacy.grant.bits | ||||||
|  |     grant.g_type := MuxLookup(out.d.bits.opcode, Grant.prefetchAckType, Array( | ||||||
|  |       TLMessages.AccessAck     -> Grant.putAckType, | ||||||
|  |       TLMessages.AccessAckData -> Mux(out.d.bits.size === beat, Grant.getDataBeatType, Grant.getDataBlockType), | ||||||
|  |       TLMessages.HintAck       -> Grant.prefetchAckType)) | ||||||
|  |     grant.is_builtin_type := Bool(true) | ||||||
|  |     grant.client_xact_id  := out.d.bits.source | ||||||
|  |     grant.manager_xact_id := out.d.bits.sink | ||||||
|  |     grant.data            := out.d.bits.data | ||||||
|  |     grant.addr_beat       := beatCounter | ||||||
|  |  | ||||||
|  |     // Tie off unused channels | ||||||
|  |     out.b.ready := Bool(true) | ||||||
|  |     out.c.valid := Bool(false) | ||||||
|  |     out.e.valid := Bool(false) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										411
									
								
								src/main/scala/uncore/tilelink2/Monitor.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								src/main/scala/uncore/tilelink2/Monitor.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,411 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.{SourceInfo, SourceLine} | ||||||
|  |  | ||||||
|  | object TLMonitor | ||||||
|  | { | ||||||
|  |   def extra(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     sourceInfo match { | ||||||
|  |       case SourceLine(filename, line, col) => s" (connected at $filename:$line:$col)" | ||||||
|  |       case _ => "" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormatA(bundle: TLBundleA, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     assert (TLMessages.isA(bundle.opcode), "'A' channel has invalid opcode" + extra) | ||||||
|  |  | ||||||
|  |     // Reuse these subexpressions to save some firrtl lines | ||||||
|  |     val source_ok = edge.client.contains(bundle.source) | ||||||
|  |     val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) | ||||||
|  |     val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size) | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Acquire) { | ||||||
|  |       assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel Acquire carries invalid source ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'A' channel Acquire smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel Acquire address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isGrow(bundle.param), "'A' channel Acquire carries invalid grow param" + extra) | ||||||
|  |       assert (~bundle.mask === UInt(0), "'A' channel Acquire contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Get) { | ||||||
|  |       assert (edge.manager.supportsGet(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel Get carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel Get address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'A' channel Get carries invalid param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel Get contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.PutFullData) { | ||||||
|  |       assert (edge.manager.supportsPutFull(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel PutFull carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel PutFull address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'A' channel PutFull carries invalid param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel PutFull contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.PutPartialData) { | ||||||
|  |       assert (edge.manager.supportsPutPartial(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel PutPartial carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel PutPartial address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'A' channel PutPartial carries invalid param" + extra) | ||||||
|  |       assert ((bundle.mask & ~mask) === UInt(0), "'A' channel PutPartial contains invalid mask" + extra) | ||||||
|  |       assert (bundle.mask =/= UInt(0), "'A' channel PutPartial has a zero mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ArithmeticData) { | ||||||
|  |       assert (edge.manager.supportsArithmetic(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel Arithmetic carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel Arithmetic address not aligned to size" + extra) | ||||||
|  |       assert (TLAtomics.isArithmetic(bundle.param), "'A' channel Arithmetic carries invalid opcode param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel Arithmetic contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.LogicalData) { | ||||||
|  |       assert (edge.manager.supportsLogical(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel Logical carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel Logical address not aligned to size" + extra) | ||||||
|  |       assert (TLAtomics.isLogical(bundle.param), "'A' channel Logical carries invalid opcode param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel Logical contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Hint) { | ||||||
|  |       assert (edge.manager.supportsHint(edge.address(bundle)), "'A' channel carries Hint type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'A' channel Hint carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'A' channel Hint address not aligned to size" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel Hint contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormatB(bundle: TLBundleB, edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     assert (TLMessages.isB(bundle.opcode), "'B' channel has invalid opcode" + extra) | ||||||
|  |  | ||||||
|  |     // Reuse these subexpressions to save some firrtl lines | ||||||
|  |     val address_ok = edge.manager.contains(bundle.source) | ||||||
|  |     val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) | ||||||
|  |     val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size) | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Probe) { | ||||||
|  |       assert (edge.client.supportsProbe(bundle.source, bundle.size), "'B' channel carries Probe type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel Probe carries unmanaged address" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'B' channel Probe smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel Probe address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isCap(bundle.param), "'B' channel Probe carries invalid cap param" + extra) | ||||||
|  |       assert (~bundle.mask === UInt(0).asUInt, "'B' channel Probe contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Get) { | ||||||
|  |       assert (edge.client.supportsGet(bundle.source, bundle.size), "'B' channel carries Get type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel Get carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel Get address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'B' channel Get carries invalid param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'A' channel Get contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.PutFullData) { | ||||||
|  |       assert (edge.client.supportsPutFull(bundle.source, bundle.size), "'B' channel carries PutFull type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel PutFull carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel PutFull address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'B' channel PutFull carries invalid param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'B' channel PutFull contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.PutPartialData) { | ||||||
|  |       assert (edge.client.supportsPutPartial(bundle.source, bundle.size), "'B' channel carries PutPartial type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel PutPartial carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel PutPartial address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'B' channel PutPartial carries invalid param" + extra) | ||||||
|  |       assert ((bundle.mask & ~mask) === UInt(0), "'B' channel PutPartial contains invalid mask" + extra) | ||||||
|  |       assert (bundle.mask =/= UInt(0), "'B' channel PutPartial has a zero mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ArithmeticData) { | ||||||
|  |       assert (edge.client.supportsArithmetic(bundle.source, bundle.size), "'B' channel carries Arithmetic type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel Arithmetic carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel Arithmetic address not aligned to size" + extra) | ||||||
|  |       assert (TLAtomics.isArithmetic(bundle.param), "'B' channel Arithmetic carries invalid opcode param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'B' channel Arithmetic contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.LogicalData) { | ||||||
|  |       assert (edge.client.supportsLogical(bundle.source, bundle.size), "'B' channel carries Logical type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel Logical carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel Logical address not aligned to size" + extra) | ||||||
|  |       assert (TLAtomics.isLogical(bundle.param), "'B' channel Logical carries invalid opcode param" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'B' channel Logical contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Hint) { | ||||||
|  |       assert (edge.client.supportsHint(bundle.source), "'B' channel carries Hint type unsupported by client" + extra) | ||||||
|  |       assert (address_ok, "'B' channel Hint carries unmanaged address" + extra) | ||||||
|  |       assert (is_aligned, "'B' channel Hint address not aligned to size" + extra) | ||||||
|  |       assert (bundle.mask === mask, "'B' channel Hint contains invalid mask" + extra) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormatC(bundle: TLBundleC, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     assert (TLMessages.isC(bundle.opcode), "'C' channel has invalid opcode" + extra) | ||||||
|  |  | ||||||
|  |     val source_ok = edge.client.contains(bundle.source) | ||||||
|  |     val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) && edge.isLoAligned(bundle.addr_lo, bundle.size) | ||||||
|  |     val address_ok = edge.manager.contains(bundle.source) | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ProbeAck) { | ||||||
|  |       assert (address_ok, "'C' channel ProbeAck carries unmanaged address" + extra) | ||||||
|  |       assert (source_ok, "'C' channel ProbeAck carries invalid source ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ProbeAck smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel ProbeAck address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isReport(bundle.param), "'C' channel ProbeAck carries invalid report param" + extra) | ||||||
|  |       assert (!bundle.error, "'C' channel Probe carries an error" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ProbeAckData) { | ||||||
|  |       assert (address_ok, "'C' channel ProbeAckData carries unmanaged address" + extra) | ||||||
|  |       assert (source_ok, "'C' channel ProbeAckData carries invalid source ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ProbeAckData smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel ProbeAckData address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isReport(bundle.param), "'C' channel ProbeAckData carries invalid report param" + extra) | ||||||
|  |       assert (!bundle.error, "'C' channel ProbeData carries an error" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Release) { | ||||||
|  |       assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'C' channel Release carries invalid source ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel Release smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel Release address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isShrink(bundle.param), "'C' channel Release carries invalid shrink param" + extra) | ||||||
|  |       assert (!bundle.error, "'C' channel Release carries an error" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ReleaseData) { | ||||||
|  |       assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra) | ||||||
|  |       assert (source_ok, "'C' channel ReleaseData carries invalid source ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ReleaseData smaller than a beat" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel ReleaseData address not aligned to size" + extra) | ||||||
|  |       assert (TLPermissions.isShrink(bundle.param), "'C' channel ReleaseData carries invalid shrink param" + extra) | ||||||
|  |       assert (!bundle.error, "'C' channel ReleaseData carries an error" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.AccessAck) { | ||||||
|  |       assert (address_ok, "'C' channel AccessAck carries unmanaged address" + extra) | ||||||
|  |       assert (source_ok, "'C' channel AccessAck carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel AccessAck address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'C' channel AccessAck carries invalid param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.AccessAckData) { | ||||||
|  |       assert (address_ok, "'C' channel AccessAckData carries unmanaged address" + extra) | ||||||
|  |       assert (source_ok, "'C' channel AccessAckData carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel AccessAckData address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'C' channel AccessAckData carries invalid param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.HintAck) { | ||||||
|  |       assert (address_ok, "'C' channel HintAck carries unmanaged address" + extra) | ||||||
|  |       assert (source_ok, "'C' channel HintAck carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'C' channel HintAck address not aligned to size" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'C' channel HintAck carries invalid param" + extra) | ||||||
|  |       assert (!bundle.error, "'C' channel HintAck carries an error" + extra) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormatD(bundle: TLBundleD, edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     assert (TLMessages.isD(bundle.opcode), "'D' channel has invalid opcode" + extra) | ||||||
|  |  | ||||||
|  |     val source_ok = edge.client.contains(bundle.source) | ||||||
|  |     val is_aligned = edge.isLoAligned(bundle.addr_lo, bundle.size) | ||||||
|  |     val sink_ok = edge.manager.containsById(bundle.sink) | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.ReleaseAck) { | ||||||
|  |       assert (source_ok, "'D' channel ReleaseAck carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel ReleaseAck address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel ReleaseAck carries invalid sink ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel ReleaseAck smaller than a beat" + extra) | ||||||
|  |       assert (bundle.param === UInt(0), "'D' channel ReleaseeAck carries invalid param" + extra) | ||||||
|  |       assert (!bundle.error, "'D' channel ReleaseAck carries an error" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.Grant) { | ||||||
|  |       assert (source_ok, "'D' channel Grant carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel Grant address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel Grant carries invalid sink ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel Grant smaller than a beat" + extra) | ||||||
|  |       assert (TLPermissions.isCap(bundle.param), "'D' channel Grant carries invalid cap param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.GrantData) { | ||||||
|  |       assert (source_ok, "'D' channel GrantData carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel GrantData address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel GrantData carries invalid sink ID" + extra) | ||||||
|  |       assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel GrantData smaller than a beat" + extra) | ||||||
|  |       assert (TLPermissions.isCap(bundle.param), "'D' channel GrantData carries invalid cap param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.AccessAck) { | ||||||
|  |       assert (source_ok, "'D' channel AccessAck carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel AccessAck address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel AccessAck carries invalid sink ID" + extra) | ||||||
|  |       // size is ignored | ||||||
|  |       assert (bundle.param === UInt(0), "'D' channel AccessAck carries invalid param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.AccessAckData) { | ||||||
|  |       assert (source_ok, "'D' channel AccessAckData carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel AccessAckData address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel AccessAckData carries invalid sink ID" + extra) | ||||||
|  |       // size is ignored | ||||||
|  |       assert (bundle.param === UInt(0), "'D' channel AccessAckData carries invalid param" + extra) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     when (bundle.opcode === TLMessages.HintAck) { | ||||||
|  |       assert (source_ok, "'D' channel HintAck carries invalid source ID" + extra) | ||||||
|  |       assert (is_aligned, "'D' channel HintAck address not aligned to size" + extra) | ||||||
|  |       assert (sink_ok, "'D' channel HintAck carries invalid sink ID" + extra) | ||||||
|  |       // size is ignored | ||||||
|  |       assert (bundle.param === UInt(0), "'D' channel HintAck carries invalid param" + extra) | ||||||
|  |       assert (!bundle.error, "'D' channel HintAck carries an error" + extra) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormatE(bundle: TLBundleE, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     assert (edge.manager.containsById(bundle.sink), "'E' channels carries invalid sink ID" + extra) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeFormat(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     when (bundleOut.a.valid) { legalizeFormatA(bundleOut.a.bits, edgeOut) } | ||||||
|  |     when (bundleIn .b.valid) { legalizeFormatB(bundleIn .b.bits, edgeIn)  } | ||||||
|  |     when (bundleOut.c.valid) { legalizeFormatC(bundleOut.c.bits, edgeOut) } | ||||||
|  |     when (bundleIn .d.valid) { legalizeFormatD(bundleIn .d.bits, edgeIn)  } | ||||||
|  |     when (bundleOut.e.valid) { legalizeFormatE(bundleOut.e.bits, edgeOut) } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeMultibeatA(a: DecoupledIO[TLBundleA], edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) | ||||||
|  |     val opcode  = Reg(UInt()) | ||||||
|  |     val param   = Reg(UInt()) | ||||||
|  |     val size    = Reg(UInt()) | ||||||
|  |     val source  = Reg(UInt()) | ||||||
|  |     val addr_hi = Reg(UInt()) | ||||||
|  |     when (a.valid && counter =/= UInt(0)) { | ||||||
|  |       assert (a.bits.opcode === opcode, "'A' channel opcode changed within multibeat operation" + extra) | ||||||
|  |       assert (a.bits.param  === param,  "'A' channel param changed within multibeat operation" + extra) | ||||||
|  |       assert (a.bits.size   === size,   "'A' channel size changed within multibeat operation" + extra) | ||||||
|  |       assert (a.bits.source === source, "'A' channel source changed within multibeat operation" + extra) | ||||||
|  |       assert (a.bits.addr_hi=== addr_hi,"'A' channel addr_hi changed with multibeat operation" + extra) | ||||||
|  |     } | ||||||
|  |     when (a.fire()) { | ||||||
|  |       counter := counter - UInt(1) | ||||||
|  |       when (counter === UInt(0)) { | ||||||
|  |         counter := edge.numBeats(a.bits) - UInt(1) | ||||||
|  |         opcode  := a.bits.opcode | ||||||
|  |         param   := a.bits.param | ||||||
|  |         size    := a.bits.size | ||||||
|  |         source  := a.bits.source | ||||||
|  |         addr_hi := a.bits.addr_hi | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeMultibeatB(b: DecoupledIO[TLBundleB], edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) | ||||||
|  |     val opcode  = Reg(UInt()) | ||||||
|  |     val param   = Reg(UInt()) | ||||||
|  |     val size    = Reg(UInt()) | ||||||
|  |     val source  = Reg(UInt()) | ||||||
|  |     val addr_hi = Reg(UInt()) | ||||||
|  |     when (b.valid && counter =/= UInt(0)) { | ||||||
|  |       assert (b.bits.opcode === opcode, "'B' channel opcode changed within multibeat operation" + extra) | ||||||
|  |       assert (b.bits.param  === param,  "'B' channel param changed within multibeat operation" + extra) | ||||||
|  |       assert (b.bits.size   === size,   "'B' channel size changed within multibeat operation" + extra) | ||||||
|  |       assert (b.bits.source === source, "'B' channel source changed within multibeat operation" + extra) | ||||||
|  |       assert (b.bits.addr_hi=== addr_hi,"'B' channel addr_hi changed with multibeat operation" + extra) | ||||||
|  |     } | ||||||
|  |     when (b.fire()) { | ||||||
|  |       counter := counter - UInt(1) | ||||||
|  |       when (counter === UInt(0)) { | ||||||
|  |         counter := edge.numBeats(b.bits) - UInt(1) | ||||||
|  |         opcode  := b.bits.opcode | ||||||
|  |         param   := b.bits.param | ||||||
|  |         size    := b.bits.size | ||||||
|  |         source  := b.bits.source | ||||||
|  |         addr_hi := b.bits.addr_hi | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeMultibeatC(c: DecoupledIO[TLBundleC], edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) | ||||||
|  |     val opcode  = Reg(UInt()) | ||||||
|  |     val param   = Reg(UInt()) | ||||||
|  |     val size    = Reg(UInt()) | ||||||
|  |     val source  = Reg(UInt()) | ||||||
|  |     val addr_hi = Reg(UInt()) | ||||||
|  |     val addr_lo = Reg(UInt()) | ||||||
|  |     when (c.valid && counter =/= UInt(0)) { | ||||||
|  |       assert (c.bits.opcode === opcode, "'C' channel opcode changed within multibeat operation" + extra) | ||||||
|  |       assert (c.bits.param  === param,  "'C' channel param changed within multibeat operation" + extra) | ||||||
|  |       assert (c.bits.size   === size,   "'C' channel size changed within multibeat operation" + extra) | ||||||
|  |       assert (c.bits.source === source, "'C' channel source changed within multibeat operation" + extra) | ||||||
|  |       assert (c.bits.addr_hi=== addr_hi,"'C' channel addr_hi changed with multibeat operation" + extra) | ||||||
|  |       assert (c.bits.addr_lo=== addr_lo,"'C' channel addr_lo changed with multibeat operation" + extra) | ||||||
|  |     } | ||||||
|  |     when (c.fire()) { | ||||||
|  |       counter := counter - UInt(1) | ||||||
|  |       when (counter === UInt(0)) { | ||||||
|  |         counter := edge.numBeats(c.bits) - UInt(1) | ||||||
|  |         opcode  := c.bits.opcode | ||||||
|  |         param   := c.bits.param | ||||||
|  |         size    := c.bits.size | ||||||
|  |         source  := c.bits.source | ||||||
|  |         addr_hi := c.bits.addr_hi | ||||||
|  |         addr_lo := c.bits.addr_lo | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeMultibeatD(d: DecoupledIO[TLBundleD], edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer))) | ||||||
|  |     val opcode  = Reg(UInt()) | ||||||
|  |     val param   = Reg(UInt()) | ||||||
|  |     val size    = Reg(UInt()) | ||||||
|  |     val source  = Reg(UInt()) | ||||||
|  |     val sink    = Reg(UInt()) | ||||||
|  |     val addr_lo = Reg(UInt()) | ||||||
|  |     when (d.valid && counter =/= UInt(0)) { | ||||||
|  |       assert (d.bits.opcode === opcode, "'D' channel opcode changed within multibeat operation" + extra) | ||||||
|  |       assert (d.bits.param  === param,  "'D' channel param changed within multibeat operation" + extra) | ||||||
|  |       assert (d.bits.size   === size,   "'D' channel size changed within multibeat operation" + extra) | ||||||
|  |       assert (d.bits.source === source, "'D' channel source changed within multibeat operation" + extra) | ||||||
|  |       assert (d.bits.sink   === sink,   "'D' channel sink changed with multibeat operation" + extra) | ||||||
|  |       assert (d.bits.addr_lo=== addr_lo,"'C' channel addr_lo changed with multibeat operation" + extra) | ||||||
|  |     } | ||||||
|  |     when (d.fire()) { | ||||||
|  |       counter := counter - UInt(1) | ||||||
|  |       when (counter === UInt(0)) { | ||||||
|  |         counter := edge.numBeats(d.bits) - UInt(1) | ||||||
|  |         opcode  := d.bits.opcode | ||||||
|  |         param   := d.bits.param | ||||||
|  |         size    := d.bits.size | ||||||
|  |         source  := d.bits.source | ||||||
|  |         sink    := d.bits.sink | ||||||
|  |         addr_lo := d.bits.addr_lo | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalizeMultibeat(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     legalizeMultibeatA(bundleOut.a, edgeOut) | ||||||
|  |     legalizeMultibeatB(bundleOut.b, edgeIn) | ||||||
|  |     legalizeMultibeatC(bundleOut.c, edgeOut) | ||||||
|  |     legalizeMultibeatD(bundleOut.d, edgeIn) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def legalize(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     legalizeFormat   (bundleOut, edgeOut, bundleIn, edgeIn) | ||||||
|  |     legalizeMultibeat(bundleOut, edgeOut, bundleIn, edgeIn) | ||||||
|  |     // !!! validate source uniqueness | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										138
									
								
								src/main/scala/uncore/tilelink2/Narrower.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/main/scala/uncore/tilelink2/Narrower.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  | import scala.math.{min,max} | ||||||
|  |  | ||||||
|  | // innBeatBytes => the bus width after the adapter | ||||||
|  | class TLNarrower(innerBeatBytes: Int) extends LazyModule | ||||||
|  | { | ||||||
|  |   val node = TLAdapterNode( | ||||||
|  |     clientFn  = { case Seq(c) => c }, | ||||||
|  |     managerFn = { case Seq(m) => m.copy(beatBytes = innerBeatBytes) }) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val edgeOut = node.edgesOut(0) | ||||||
|  |     val edgeIn  = node.edgesIn(0) | ||||||
|  |     val outerBeatBytes = edgeOut.manager.beatBytes | ||||||
|  |     require (outerBeatBytes < innerBeatBytes) | ||||||
|  |  | ||||||
|  |     val ratio = innerBeatBytes / outerBeatBytes | ||||||
|  |     val bce = edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe | ||||||
|  |  | ||||||
|  |     def trailingZeros(x: Int) = if (x > 0) Some(log2Ceil(x & -x)) else None | ||||||
|  |  | ||||||
|  |     def split(edge: TLEdge, in: TLDataChannel, fire: Bool): (Bool, UInt, UInt) = { | ||||||
|  |       val dataSlices = Vec.tabulate (ratio) { i => edge.data(in)((i+1)*outerBeatBytes*8-1, i*outerBeatBytes*8) } | ||||||
|  |       val maskSlices = Vec.tabulate (ratio) { i => edge.mask(in)((i+1)*outerBeatBytes  -1, i*outerBeatBytes)   } | ||||||
|  |       val filter = Reg(UInt(width = ratio), init = SInt(-1, width = ratio).asUInt) | ||||||
|  |       val mask = maskSlices.map(_.orR) | ||||||
|  |       val hasData = edge.hasData(in) | ||||||
|  |  | ||||||
|  |       // decoded_size = 1111 (for smallest), 0101, 0001 (for largest) | ||||||
|  |       val sizeOH1 = UIntToOH1(edge.size(in), log2Ceil(innerBeatBytes)) >> log2Ceil(outerBeatBytes) | ||||||
|  |       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 (fire) { | ||||||
|  |         filter := new_filter  | ||||||
|  |         when (!hasData) { filter := SInt(-1, width = ratio).asUInt } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (edge.staticHasData(in) == Some(false)) { | ||||||
|  |         (Bool(true), UInt(0), UInt(0)) | ||||||
|  |       } else { | ||||||
|  |         val select = Cat(mask.reverse) & new_filter | ||||||
|  |         (last, Mux1H(select, dataSlices), Mux1H(select, maskSlices)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def merge(edge: TLEdge, in: TLDataChannel, fire: Bool): (Bool, UInt) = { | ||||||
|  |       val count = RegInit(UInt(0, width = log2Ceil(ratio))) | ||||||
|  |       val rdata = Reg(UInt(width = (ratio-1)*outerBeatBytes*8)) | ||||||
|  |       val data = Cat(edge.data(in), rdata) | ||||||
|  |       val first = count === UInt(0) | ||||||
|  |       val limit = UIntToOH1(edge.size(in), log2Ceil(innerBeatBytes)) >> log2Ceil(outerBeatBytes) | ||||||
|  |       val last = count === limit || !edge.hasData(in) | ||||||
|  |  | ||||||
|  |       when (fire) { | ||||||
|  |         rdata := data >> outerBeatBytes*8 | ||||||
|  |         count := count + UInt(1) | ||||||
|  |         when (last) { count := UInt(0) } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       val cases = Seq.tabulate(log2Ceil(ratio)+1) { i => | ||||||
|  |         val high = innerBeatBytes*8 | ||||||
|  |         val take = (1 << i)*outerBeatBytes*8 | ||||||
|  |         Fill(1 << (log2Ceil(ratio)-i), data(high-1, high-take)) | ||||||
|  |       } | ||||||
|  |       val mux = Vec.tabulate(log2Ceil(edge.maxTransfer)+1) { lgSize => | ||||||
|  |         cases(min(max(lgSize - log2Ceil(outerBeatBytes), 0), log2Ceil(ratio))) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (edge.staticHasData(in) == Some(false)) { | ||||||
|  |         (Bool(true), UInt(0)) | ||||||
|  |       } else { | ||||||
|  |         (last, mux(edge.size(in))) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val in = io.in(0) | ||||||
|  |     val out = io.out(0) | ||||||
|  |  | ||||||
|  |     val (alast, adata, amask) = split(edgeIn, in.a.bits, out.a.fire()) | ||||||
|  |     in.a.ready := out.a.ready && alast | ||||||
|  |     out.a.valid := in.a.valid | ||||||
|  |     out.a.bits := in.a.bits | ||||||
|  |     out.a.bits.addr_hi := Cat(in.a.bits.addr_hi, edgeIn.addr_lo(in.a.bits) >> log2Ceil(outerBeatBytes)) | ||||||
|  |     out.a.bits.data := adata | ||||||
|  |     out.a.bits.mask := amask | ||||||
|  |  | ||||||
|  |     val (dlast, ddata) = merge(edgeOut, out.d.bits, out.d.fire()) | ||||||
|  |     out.d.ready := in.d.ready || !dlast | ||||||
|  |     in.d.valid := out.d.valid && dlast | ||||||
|  |     in.d.bits := out.d.bits | ||||||
|  |     in.d.bits.data := ddata | ||||||
|  |  | ||||||
|  |     if (bce) { | ||||||
|  |       require (false) | ||||||
|  |       // C has no wmask !!! | ||||||
|  | //      val (clast, cdata, cmask) = split(in.c.bits, out.c.fire()) | ||||||
|  | //      in.c.ready := out.c.ready && clast | ||||||
|  | //      out.c.valid := in.c.valid | ||||||
|  | //      out.c.bits := in.c.bits | ||||||
|  | //      out.c.bits.data := cdata | ||||||
|  | //      out.c.bits.mask := cmask | ||||||
|  |  | ||||||
|  |       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 TLNarrower | ||||||
|  | { | ||||||
|  |   // applied to the TL source node; connect (Narrower(x.node, 16) -> y.node) | ||||||
|  |   def apply(x: TLBaseNode, innerBeatBytes: Int)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = { | ||||||
|  |     val narrower = LazyModule(new TLNarrower(innerBeatBytes)) | ||||||
|  |     lazyModule.connect(x -> narrower.node) | ||||||
|  |     narrower.node | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								src/main/scala/uncore/tilelink2/Nodes.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/main/scala/uncore/tilelink2/Nodes.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import scala.collection.mutable.ListBuffer | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  |  | ||||||
|  | // PI = PortInputParameters | ||||||
|  | // PO = PortOutputParameters | ||||||
|  | // EI = EdgeInput | ||||||
|  | // EO = EdgeOutput | ||||||
|  | abstract class NodeImp[PO, PI, EO, EI, B <: Bundle] | ||||||
|  | { | ||||||
|  |   def edgeO(po: PO, pi: PI): EO | ||||||
|  |   def edgeI(po: PO, pi: PI): EI | ||||||
|  |   def bundleO(eo: Seq[EO]): Vec[B] | ||||||
|  |   def bundleI(ei: Seq[EI]): Vec[B] | ||||||
|  |   def connect(bo: B, eo: EO, bi: B, ei: EI)(implicit sourceInfo: SourceInfo): Unit | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class BaseNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])( | ||||||
|  |   private val oFn: Option[Seq[PO] => PO], | ||||||
|  |   private val iFn: Option[Seq[PI] => PI], | ||||||
|  |   private val numPO: Range.Inclusive, | ||||||
|  |   private val numPI: Range.Inclusive) | ||||||
|  | { | ||||||
|  |   // At least 0 ports must be supported | ||||||
|  |   require (!numPO.isEmpty) | ||||||
|  |   require (!numPI.isEmpty) | ||||||
|  |   require (numPO.start >= 0) | ||||||
|  |   require (numPI.start >= 0) | ||||||
|  |  | ||||||
|  |   val noOs = numPO.size == 1 && numPO.contains(0) | ||||||
|  |   val noIs = numPI.size == 1 && numPI.contains(0) | ||||||
|  |  | ||||||
|  |   require (noOs || oFn.isDefined) | ||||||
|  |   require (noIs || iFn.isDefined) | ||||||
|  |  | ||||||
|  |   private val accPO = ListBuffer[BaseNode[PO, PI, EO, EI, B]]() | ||||||
|  |   private val accPI = ListBuffer[BaseNode[PO, PI, EO, EI, B]]() | ||||||
|  |   private var oRealized  = false | ||||||
|  |   private var iRealized = false | ||||||
|  |  | ||||||
|  |   private lazy val oPorts = { oRealized = true; require (numPO.contains(accPO.size)); accPO.result() } | ||||||
|  |   private lazy val iPorts = { iRealized = true; require (numPI.contains(accPI.size)); accPI.result() } | ||||||
|  |   private lazy val oParams : Option[PO] = oFn.map(_(iPorts.map(_.oParams.get))) | ||||||
|  |   private lazy val iParams : Option[PI] = iFn.map(_(oPorts.map(_.iParams.get))) | ||||||
|  |  | ||||||
|  |   lazy val edgesOut = oPorts.map { n => imp.edgeO(oParams.get, n.iParams.get) } | ||||||
|  |   lazy val edgesIn  = iPorts.map { n => imp.edgeI(n.oParams.get, iParams.get) } | ||||||
|  |  | ||||||
|  |   lazy val bundleOut = imp.bundleO(edgesOut) | ||||||
|  |   lazy val bundleIn  = imp.bundleI(edgesIn) | ||||||
|  |  | ||||||
|  |   def connectOut = bundleOut | ||||||
|  |   def connectIn = bundleIn | ||||||
|  |  | ||||||
|  |   // source.edge(sink) | ||||||
|  |   protected[tilelink2] def edge(x: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo) = { | ||||||
|  |     require (!noOs) | ||||||
|  |     require (!oRealized) | ||||||
|  |     require (!x.noIs) | ||||||
|  |     require (!x.iRealized) | ||||||
|  |     val i = x.accPI.size | ||||||
|  |     val o = accPO.size | ||||||
|  |     accPO += x | ||||||
|  |     x.accPI += this | ||||||
|  |     () => { | ||||||
|  |       imp.connect(connectOut(o), edgesOut(o), x.connectIn(i), x.edgesIn(i)) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class IdentityNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) | ||||||
|  |   extends BaseNode(imp)(Some{case Seq(x) => x}, Some{case Seq(x) => x}, 1 to 1, 1 to 1) | ||||||
|  |  | ||||||
|  | class OutputNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) | ||||||
|  | { | ||||||
|  |   override def connectOut = bundleOut | ||||||
|  |   override def connectIn  = bundleOut | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class InputNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) | ||||||
|  | { | ||||||
|  |   override def connectOut = bundleIn | ||||||
|  |   override def connectIn  = bundleIn | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SourceNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1) | ||||||
|  |   extends BaseNode(imp)(Some{case Seq() => po}, None, num, 0 to 0) | ||||||
|  | { | ||||||
|  |   require (num.end >= 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SinkNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1) | ||||||
|  |   extends BaseNode(imp)(None, Some{case Seq() => pi}, 0 to 0, num) | ||||||
|  | { | ||||||
|  |   require (num.end >= 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class InteriorNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) | ||||||
|  |   (oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive) | ||||||
|  |   extends BaseNode(imp)(Some(oFn), Some(iFn), numPO, numPI) | ||||||
|  | { | ||||||
|  |   require (numPO.end >= 1) | ||||||
|  |   require (numPI.end >= 1) | ||||||
|  | } | ||||||
							
								
								
									
										330
									
								
								src/main/scala/uncore/tilelink2/Parameters.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								src/main/scala/uncore/tilelink2/Parameters.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,330 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | 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 || min < x.max) 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) | ||||||
|  | { | ||||||
|  |   // Forbid misaligned base address (and empty sets) | ||||||
|  |   require ((base & mask) == 0) | ||||||
|  |  | ||||||
|  |   def contains(x: BigInt) = ~(~(x ^ base) | mask) == 0 | ||||||
|  |   def contains(x: UInt) = ~(~(x ^ UInt(base)) | UInt(mask)) === UInt(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 | ||||||
|  |   // 1 less than the number of bytes to which the manager should be aligned | ||||||
|  |   def alignment1 = ((mask + 1) & ~mask) - 1 | ||||||
|  |   def max = base | mask | ||||||
|  |  | ||||||
|  |   // A strided slave serves discontiguous ranges | ||||||
|  |   def strided = alignment1 != mask | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLManagerParameters( | ||||||
|  |   address:            Seq[AddressSet], | ||||||
|  |   sinkId:             IdRange       = IdRange(0, 1), | ||||||
|  |   regionType:         RegionType.T  = RegionType.GET_EFFECTS, | ||||||
|  |   // Supports both Acquire+Release+Finish of these sizes | ||||||
|  |   supportsAcquire:    TransferSizes = TransferSizes.none, | ||||||
|  |   supportsArithmetic: TransferSizes = TransferSizes.none, | ||||||
|  |   supportsLogical:    TransferSizes = TransferSizes.none, | ||||||
|  |   supportsGet:        TransferSizes = TransferSizes.none, | ||||||
|  |   supportsPutFull:    TransferSizes = TransferSizes.none, | ||||||
|  |   supportsPutPartial: TransferSizes = TransferSizes.none, | ||||||
|  |   supportsHint:       Boolean       = false, | ||||||
|  |   // If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order | ||||||
|  |   fifoId:             Option[Int]   = None) | ||||||
|  | { | ||||||
|  |   address.combinations(2).foreach({ case Seq(x,y) => | ||||||
|  |     require (!x.overlaps(y)) | ||||||
|  |   }) | ||||||
|  |   address.foreach({ case a => | ||||||
|  |     require (supportsAcquire.none || a.alignment1 >= supportsAcquire.max-1) | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   // Largest support transfer of all types | ||||||
|  |   val maxTransfer = List( | ||||||
|  |     supportsAcquire.max, | ||||||
|  |     supportsArithmetic.max, | ||||||
|  |     supportsLogical.max, | ||||||
|  |     supportsGet.max, | ||||||
|  |     supportsPutFull.max, | ||||||
|  |     supportsPutPartial.max).max | ||||||
|  |  | ||||||
|  |   // The device had better not support a transfer larger than it's alignment | ||||||
|  |   address.foreach({ case a => | ||||||
|  |     require (a.alignment1 >= maxTransfer-1) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes: Int) | ||||||
|  | { | ||||||
|  |   require (!managers.isEmpty) | ||||||
|  |   require (isPow2(beatBytes)) | ||||||
|  |  | ||||||
|  |   // Require disjoint ranges for Ids and addresses | ||||||
|  |   managers.combinations(2).foreach({ case Seq(x,y) => | ||||||
|  |     require (!x.sinkId.overlaps(y.sinkId)) | ||||||
|  |     x.address.foreach({ a => y.address.foreach({ b => | ||||||
|  |       require (!a.overlaps(b)) | ||||||
|  |     })}) | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   // Bounds on required sizes | ||||||
|  |   def endSinkId   = managers.map(_.sinkId.end).max | ||||||
|  |   def maxAddress  = managers.map(_.address.map(_.max).max).max | ||||||
|  |   def maxTransfer = managers.map(_.maxTransfer).max | ||||||
|  |    | ||||||
|  |   // Operation sizes supported by all outward Managers | ||||||
|  |   val allSupportAcquire    = managers.map(_.supportsAcquire)   .reduce(_ intersect _) | ||||||
|  |   val allSupportArithmetic = managers.map(_.supportsArithmetic).reduce(_ intersect _) | ||||||
|  |   val allSupportLogical    = managers.map(_.supportsLogical)   .reduce(_ intersect _) | ||||||
|  |   val allSupportGet        = managers.map(_.supportsGet)       .reduce(_ intersect _) | ||||||
|  |   val allSupportPutFull    = managers.map(_.supportsPutFull)   .reduce(_ intersect _) | ||||||
|  |   val allSupportPutPartial = managers.map(_.supportsPutPartial).reduce(_ intersect _) | ||||||
|  |   val allSupportHint       = managers.map(_.supportsHint)      .reduce(_    &&     _) | ||||||
|  |  | ||||||
|  |   // Operation supported by at least one outward Managers | ||||||
|  |   val anySupportAcquire    = managers.map(!_.supportsAcquire.none)   .reduce(_ || _) | ||||||
|  |   val anySupportArithmetic = managers.map(!_.supportsArithmetic.none).reduce(_ || _) | ||||||
|  |   val anySupportLogical    = managers.map(!_.supportsLogical.none)   .reduce(_ || _) | ||||||
|  |   val anySupportGet        = managers.map(!_.supportsGet.none)       .reduce(_ || _) | ||||||
|  |   val anySupportPutFull    = managers.map(!_.supportsPutFull.none)   .reduce(_ || _) | ||||||
|  |   val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _) | ||||||
|  |   val anySupportHint       = managers.map( _.supportsHint)           .reduce(_ || _) | ||||||
|  |  | ||||||
|  |   // These return Option[TLManagerParameters] for your convenience | ||||||
|  |   def find(address: BigInt) = managers.find(_.address.exists(_.contains(address))) | ||||||
|  |   def findById(id: Int) = managers.find(_.sinkId.contains(id)) | ||||||
|  |  | ||||||
|  |   // Synthesizable lookup methods | ||||||
|  |   def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) | ||||||
|  |   def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id))) | ||||||
|  |  | ||||||
|  |   // !!! need a cheaper version of find, where we assume a valid address match exists | ||||||
|  |    | ||||||
|  |   // Does this Port manage this ID/address? | ||||||
|  |   def contains(address: UInt) = find(address).reduce(_ || _) | ||||||
|  |   def containsById(id: UInt) = findById(id).reduce(_ || _) | ||||||
|  |    | ||||||
|  |   private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { | ||||||
|  |     val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) | ||||||
|  |     if (allSame) member(managers(0)).containsLg(lgSize) else { | ||||||
|  |       Mux1H(find(address), managers.map(member(_).containsLg(lgSize))) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Check for support of a given operation at a specific address | ||||||
|  |   val supportsAcquire    = safety_helper(_.supportsAcquire)    _ | ||||||
|  |   val supportsArithmetic = safety_helper(_.supportsArithmetic) _ | ||||||
|  |   val supportsLogical    = safety_helper(_.supportsLogical)    _ | ||||||
|  |   val supportsGet        = safety_helper(_.supportsGet)        _ | ||||||
|  |   val supportsPutFull    = safety_helper(_.supportsPutFull)    _ | ||||||
|  |   val supportsPutPartial = safety_helper(_.supportsPutPartial) _ | ||||||
|  |   def supportsHint(address: UInt) = { | ||||||
|  |     if (allSupportHint) Bool(true) else { | ||||||
|  |       Mux1H(find(address), managers.map(m => Bool(m.supportsHint))) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLClientParameters( | ||||||
|  |   sourceId:            IdRange       = IdRange(0,1), | ||||||
|  |   // Supports both Probe+Grant of these sizes | ||||||
|  |   supportsProbe:       TransferSizes = TransferSizes.none, | ||||||
|  |   supportsArithmetic:  TransferSizes = TransferSizes.none, | ||||||
|  |   supportsLogical:     TransferSizes = TransferSizes.none, | ||||||
|  |   supportsGet:         TransferSizes = TransferSizes.none, | ||||||
|  |   supportsPutFull:     TransferSizes = TransferSizes.none, | ||||||
|  |   supportsPutPartial:  TransferSizes = TransferSizes.none, | ||||||
|  |   supportsHint:        Boolean       = false) | ||||||
|  | { | ||||||
|  |   val maxTransfer = List( | ||||||
|  |     supportsProbe.max, | ||||||
|  |     supportsArithmetic.max, | ||||||
|  |     supportsLogical.max, | ||||||
|  |     supportsGet.max, | ||||||
|  |     supportsPutFull.max, | ||||||
|  |     supportsPutPartial.max).max | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLClientPortParameters(clients: Seq[TLClientParameters]) { | ||||||
|  |   require (!clients.isEmpty) | ||||||
|  |  | ||||||
|  |   // Require disjoint ranges for Ids | ||||||
|  |   clients.combinations(2).foreach({ case Seq(x,y) => | ||||||
|  |     require (!x.sourceId.overlaps(y.sourceId)) | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   // Bounds on required sizes | ||||||
|  |   def endSourceId = clients.map(_.sourceId.end).max | ||||||
|  |   def maxTransfer = clients.map(_.maxTransfer).max | ||||||
|  |  | ||||||
|  |   // Operation sizes supported by all inward Clients | ||||||
|  |   val allSupportProbe      = clients.map(_.supportsProbe)     .reduce(_ intersect _) | ||||||
|  |   val allSupportArithmetic = clients.map(_.supportsArithmetic).reduce(_ intersect _) | ||||||
|  |   val allSupportLogical    = clients.map(_.supportsLogical)   .reduce(_ intersect _) | ||||||
|  |   val allSupportGet        = clients.map(_.supportsGet)       .reduce(_ intersect _) | ||||||
|  |   val allSupportPutFull    = clients.map(_.supportsPutFull)   .reduce(_ intersect _) | ||||||
|  |   val allSupportPutPartial = clients.map(_.supportsPutPartial).reduce(_ intersect _) | ||||||
|  |   val allSupportHint       = clients.map(_.supportsHint)      .reduce(_    &&     _) | ||||||
|  |  | ||||||
|  |   // Operation is supported by at least one client | ||||||
|  |   val anySupportProbe      = clients.map(!_.supportsProbe.none)     .reduce(_ || _) | ||||||
|  |   val anySupportArithmetic = clients.map(!_.supportsArithmetic.none).reduce(_ || _) | ||||||
|  |   val anySupportLogical    = clients.map(!_.supportsLogical.none)   .reduce(_ || _) | ||||||
|  |   val anySupportGet        = clients.map(!_.supportsGet.none)       .reduce(_ || _) | ||||||
|  |   val anySupportPutFull    = clients.map(!_.supportsPutFull.none)   .reduce(_ || _) | ||||||
|  |   val anySupportPutPartial = clients.map(!_.supportsPutPartial.none).reduce(_ || _) | ||||||
|  |   val anySupportHint       = clients.map( _.supportsHint)           .reduce(_ || _) | ||||||
|  |  | ||||||
|  |   // These return Option[TLClientParameters] for your convenience | ||||||
|  |   def find(id: Int) = clients.find(_.sourceId.contains(id)) | ||||||
|  |    | ||||||
|  |   // Synthesizable lookup methods | ||||||
|  |   def find(id: UInt) = Vec(clients.map(_.sourceId.contains(id))) | ||||||
|  |   def contains(id: UInt) = find(id).reduce(_ || _) | ||||||
|  |    | ||||||
|  |   private def safety_helper(member: TLClientParameters => TransferSizes)(id: UInt, lgSize: UInt) = { | ||||||
|  |     val allSame = clients.map(member(_) == member(clients(0))).reduce(_ && _) | ||||||
|  |     if (allSame) member(clients(0)).containsLg(lgSize) else { | ||||||
|  |       Mux1H(find(id), clients.map(member(_).containsLg(lgSize))) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Check for support of a given operation at a specific id | ||||||
|  |   val supportsProbe      = safety_helper(_.supportsProbe)      _ | ||||||
|  |   val supportsArithmetic = safety_helper(_.supportsArithmetic) _ | ||||||
|  |   val supportsLogical    = safety_helper(_.supportsLogical)    _ | ||||||
|  |   val supportsGet        = safety_helper(_.supportsGet)        _ | ||||||
|  |   val supportsPutFull    = safety_helper(_.supportsPutFull)    _ | ||||||
|  |   val supportsPutPartial = safety_helper(_.supportsPutPartial) _ | ||||||
|  |   def supportsHint(id: UInt) = { | ||||||
|  |     if (allSupportHint) Bool(true) else { | ||||||
|  |       Mux1H(find(id), clients.map(c => Bool(c.supportsHint))) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLBundleParameters( | ||||||
|  |   addrHiBits: Int, | ||||||
|  |   dataBits:   Int, | ||||||
|  |   sourceBits: Int, | ||||||
|  |   sinkBits:   Int, | ||||||
|  |   sizeBits:   Int) | ||||||
|  | { | ||||||
|  |   // Chisel has issues with 0-width wires | ||||||
|  |   require (addrHiBits  >= 1) | ||||||
|  |   require (dataBits    >= 8) | ||||||
|  |   require (sourceBits  >= 1) | ||||||
|  |   require (sinkBits    >= 1) | ||||||
|  |   require (sizeBits    >= 1) | ||||||
|  |   require (isPow2(dataBits)) | ||||||
|  |  | ||||||
|  |   val addrLoBits = log2Up(dataBits/8) | ||||||
|  |  | ||||||
|  |   def union(x: TLBundleParameters) = | ||||||
|  |     TLBundleParameters( | ||||||
|  |       max(addrHiBits,  x.addrHiBits), | ||||||
|  |       max(dataBits,    x.dataBits), | ||||||
|  |       max(sourceBits,  x.sourceBits), | ||||||
|  |       max(sinkBits,    x.sinkBits), | ||||||
|  |       max(sizeBits,    x.sizeBits)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLEdgeParameters( | ||||||
|  |   client:  TLClientPortParameters, | ||||||
|  |   manager: TLManagerPortParameters) | ||||||
|  | { | ||||||
|  |   val maxTransfer = max(client.maxTransfer, manager.maxTransfer) | ||||||
|  |   val maxLgSize = log2Up(maxTransfer) | ||||||
|  |  | ||||||
|  |   // Sanity check the link... | ||||||
|  |   require (maxTransfer >= manager.beatBytes) | ||||||
|  |  | ||||||
|  |   val bundle = TLBundleParameters( | ||||||
|  |     addrHiBits  = log2Up(manager.maxAddress + 1) - log2Up(manager.beatBytes), | ||||||
|  |     dataBits    = manager.beatBytes * 8, | ||||||
|  |     sourceBits  = log2Up(client.endSourceId), | ||||||
|  |     sinkBits    = log2Up(manager.endSinkId), | ||||||
|  |     sizeBits    = log2Up(maxLgSize+1)) | ||||||
|  | } | ||||||
							
								
								
									
										85
									
								
								src/main/scala/uncore/tilelink2/RegField.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/main/scala/uncore/tilelink2/RegField.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | case class RegReadFn private(combinational: Boolean, fn: (Bool, Bool) => (Bool, Bool, UInt)) | ||||||
|  | object RegReadFn | ||||||
|  | { | ||||||
|  |   // (ivalid: Bool, oready: Bool) => (iready: Bool, ovalid: Bool, data: UInt) | ||||||
|  |   // iready may combinationally depend on oready | ||||||
|  |   // all other combinational dependencies forbidden (e.g. ovalid <= ivalid) | ||||||
|  |   // iready must eventually go high without requiring ivalid to go high | ||||||
|  |   // ovalid must eventually go high without requiring oready to go high | ||||||
|  |   // effects must become visible on the cycle after ovalid && oready | ||||||
|  |   implicit def apply(x: (Bool, Bool) => (Bool, Bool, UInt)) = | ||||||
|  |     new RegReadFn(false, x) | ||||||
|  |   // (ready: Bool) => (valid: Bool, data: UInt) | ||||||
|  |   // valid must not combinationally depend on ready | ||||||
|  |   // valid must eventually go high without requiring ready to go high | ||||||
|  |   // => this means that reading cannot trigger creation of the output data | ||||||
|  |   //    if you need this, use the more general i&o ready-valid method above | ||||||
|  |   // effects must become visible on the cycle after valid && ready | ||||||
|  |   implicit def apply(x: Bool => (Bool, UInt)) = | ||||||
|  |     new RegReadFn(true, { case (_, oready) => | ||||||
|  |       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 register | ||||||
|  |   implicit def apply(x: UInt):RegReadFn = RegReadFn(ready => (Bool(true), x)) | ||||||
|  |   // noop | ||||||
|  |   implicit def apply(x: Unit):RegReadFn = RegReadFn(UInt(0)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class RegWriteFn private(combinational: Boolean, fn: (Bool, Bool, UInt) => (Bool, Bool)) | ||||||
|  | object RegWriteFn | ||||||
|  | { | ||||||
|  |   // (ivalid: Bool, oready: Bool, data: UInt) => (iready: Bool, ovalid: Bool) | ||||||
|  |   // iready may combinationally depend on both oready and data | ||||||
|  |   // all other combinational dependencies forbidden (e.g. ovalid <= ivalid) | ||||||
|  |   // iready must eventually go high without requiring ivalid to go high | ||||||
|  |   // ovalid must eventually go high without requiring oready to go high | ||||||
|  |   // effects must become visible on the cycle after ovalid && oready | ||||||
|  |   implicit def apply(x: (Bool, Bool, UInt) => (Bool, Bool)) = | ||||||
|  |     new RegWriteFn(false, x) | ||||||
|  |   // (valid: Bool, data: UInt) => (ready: Bool) | ||||||
|  |   // ready may combinationally depend on data (but not valid) | ||||||
|  |   // ready must eventually go high without requiring valid to go high | ||||||
|  |   // effects must become visible on the cycle after valid && ready | ||||||
|  |   implicit def apply(x: (Bool, UInt) => Bool) = | ||||||
|  |     // combinational => data valid on oready | ||||||
|  |     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 }) | ||||||
|  |   // updates a register | ||||||
|  |   implicit def apply(x: UInt): RegWriteFn = RegWriteFn((valid, data) => { when (valid) { x := data }; Bool(true) }) | ||||||
|  |   // noop | ||||||
|  |   implicit def apply(x: Unit): RegWriteFn = RegWriteFn((valid, data) => { Bool(true) }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class RegField(width: Int, read: RegReadFn, write: RegWriteFn) | ||||||
|  | { | ||||||
|  |   require (width > 0) | ||||||
|  |   def pipelined = !read.combinational || !write.combinational | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object RegField | ||||||
|  | { | ||||||
|  |   type Map = (Int, Seq[RegField]) | ||||||
|  |   def apply(n: Int)            : RegField = apply(n, (), ()) | ||||||
|  |   def apply(n: Int, rw: UInt)  : RegField = apply(n, rw, rw) | ||||||
|  |   def R(n: Int, r: RegReadFn)  : RegField = apply(n, r, ()) | ||||||
|  |   def W(n: Int, w: RegWriteFn) : RegField = apply(n, (), w) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | trait HasRegMap | ||||||
|  | { | ||||||
|  |   def regmap(mapping: RegField.Map*): Unit | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // See GPIO.scala for an example of how to use regmap | ||||||
							
								
								
									
										148
									
								
								src/main/scala/uncore/tilelink2/RegMapper.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/main/scala/uncore/tilelink2/RegMapper.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | // A bus agnostic register interface to a register-based device | ||||||
|  |  | ||||||
|  | case class RegMapperParams(indexBits: Int, maskBits: Int, extraBits: Int) | ||||||
|  |  | ||||||
|  | class RegMapperInput(params: RegMapperParams) extends GenericParameterizedBundle(params) | ||||||
|  | { | ||||||
|  |   val read  = Bool() | ||||||
|  |   val index = UInt(width = params.indexBits) | ||||||
|  |   val data  = UInt(width = params.maskBits*8) | ||||||
|  |   val mask  = UInt(width = params.maskBits) | ||||||
|  |   val extra = UInt(width = params.extraBits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class RegMapperOutput(params: RegMapperParams) extends GenericParameterizedBundle(params) | ||||||
|  | { | ||||||
|  |   val read  = Bool() | ||||||
|  |   val data  = UInt(width = params.maskBits*8) | ||||||
|  |   val extra = UInt(width = params.extraBits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object RegMapper | ||||||
|  | { | ||||||
|  |   // Create a generic register-based device | ||||||
|  |   def apply(bytes: Int, concurrency: Option[Int], in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { | ||||||
|  |     val regmap = mapping.toList | ||||||
|  |     require (!regmap.isEmpty) | ||||||
|  |  | ||||||
|  |     // Ensure no register appears twice | ||||||
|  |     regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) => | ||||||
|  |       require (reg1 != reg2) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Flatten the regmap into (Reg:Int, Offset:Int, field:RegField) | ||||||
|  |     val flat = regmap.map { case (reg, fields) => | ||||||
|  |       val offsets = fields.scanLeft(0)(_ + _.width).init | ||||||
|  |       (offsets zip fields) map { case (o, f) => (reg, o, f) } | ||||||
|  |     }.flatten | ||||||
|  |     require (!flat.isEmpty) | ||||||
|  |  | ||||||
|  |     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))) | ||||||
|  |     front.bits := in.bits | ||||||
|  |  | ||||||
|  |     // Must this device pipeline the control channel? | ||||||
|  |     val pipelined = flat.map(_._3.pipelined).reduce(_ || _) | ||||||
|  |     val depth = concurrency.getOrElse(if (pipelined) 1 else 0) | ||||||
|  |     require (depth >= 0) | ||||||
|  |     require (!pipelined || depth > 0) | ||||||
|  |     val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front | ||||||
|  |  | ||||||
|  |     // Forward declaration of all flow control signals | ||||||
|  |     val rivalid = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val wivalid = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val riready = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val wiready = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val rovalid = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val wovalid = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val roready = Wire(Vec(flat.size, Bool())) | ||||||
|  |     val woready = Wire(Vec(flat.size, Bool())) | ||||||
|  |  | ||||||
|  |     // Per-register list of all control signals needed for data to flow | ||||||
|  |     val rifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } | ||||||
|  |     val wifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } | ||||||
|  |     val rofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } | ||||||
|  |     val wofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } | ||||||
|  |  | ||||||
|  |     // The output values for each register | ||||||
|  |     val dataOut = Array.tabulate(endIndex) { _ => UInt(0) } | ||||||
|  |  | ||||||
|  |     // Which bits are touched? | ||||||
|  |     val frontMask = FillInterleaved(8, front.bits.mask) | ||||||
|  |     val backMask  = FillInterleaved(8, back .bits.mask) | ||||||
|  |  | ||||||
|  |     // Connect the fields | ||||||
|  |     for (i <- 0 until flat.size) { | ||||||
|  |       val (reg, low, field) = flat(i) | ||||||
|  |       val high = low + field.width - 1 | ||||||
|  |       // Confirm that no register is too big | ||||||
|  |       require (high < 8*bytes) | ||||||
|  |       val rimask = frontMask(high, low).orR() | ||||||
|  |       val wimask = frontMask(high, low).andR() | ||||||
|  |       val romask = backMask(high, low).orR() | ||||||
|  |       val womask = backMask(high, low).andR() | ||||||
|  |       val data = if (field.write.combinational) back.bits.data else front.bits.data | ||||||
|  |       val (f_riready, f_rovalid, f_data) = field.read.fn(rivalid(i) && rimask, roready(i) && romask) | ||||||
|  |       val (f_wiready, f_wovalid) = field.write.fn(wivalid(i) && wimask, woready(i) && womask, data(high, low)) | ||||||
|  |       riready(i) := f_riready || !rimask | ||||||
|  |       wiready(i) := f_wiready || !wimask | ||||||
|  |       rovalid(i) := f_rovalid || !romask | ||||||
|  |       wovalid(i) := f_wovalid || !womask | ||||||
|  |       rifire(reg) = riready(i) +: rifire(reg) | ||||||
|  |       wifire(reg) = wiready(i) +: wifire(reg) | ||||||
|  |       rofire(reg) = rovalid(i) +: rofire(reg) | ||||||
|  |       wofire(reg) = wovalid(i) +: wofire(reg) | ||||||
|  |       dataOut(reg) = dataOut(reg) | ((f_data  << low) & (~UInt(0, width = high+1))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Is the selected register ready? | ||||||
|  |     val rifireMux = Vec(rifire.map(_.reduce(_ && _))) | ||||||
|  |     val wifireMux = Vec(wifire.map(_.reduce(_ && _))) | ||||||
|  |     val rofireMux = Vec(rofire.map(_.reduce(_ && _))) | ||||||
|  |     val wofireMux = Vec(wofire.map(_.reduce(_ && _))) | ||||||
|  |     val iready = Mux(front.bits.read, rifireMux(front.bits.index), wifireMux(front.bits.index)) | ||||||
|  |     val oready = Mux(back .bits.read, rofireMux(back .bits.index), wofireMux(back .bits.index)) | ||||||
|  |  | ||||||
|  |     // Connect the pipeline | ||||||
|  |     in.ready    := front.ready && iready | ||||||
|  |     front.valid := in.valid    && iready | ||||||
|  |     back.ready  := out.ready   && oready | ||||||
|  |     out.valid   := back.valid  && oready | ||||||
|  |  | ||||||
|  |     // Which register is touched? | ||||||
|  |     val frontSel = UIntToOH(front.bits.index) | ||||||
|  |     val backSel  = UIntToOH(back.bits.index) | ||||||
|  |  | ||||||
|  |     // Include the per-register one-hot selected criteria | ||||||
|  |     for (reg <- 0 until endIndex) { | ||||||
|  |       rifire(reg) = (in.valid && front.ready &&  front.bits.read && frontSel(reg)) +: rifire(reg) | ||||||
|  |       wifire(reg) = (in.valid && front.ready && !front.bits.read && frontSel(reg)) +: wifire(reg) | ||||||
|  |       rofire(reg) = (back.valid && out.ready &&  back .bits.read && backSel (reg)) +: rofire(reg) | ||||||
|  |       wofire(reg) = (back.valid && out.ready && !back .bits.read && backSel (reg)) +: wofire(reg) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Connect the field's ivalid and oready | ||||||
|  |     for (i <- 0 until flat.size) { | ||||||
|  |       val (reg, _, _ ) = flat(i) | ||||||
|  |       rivalid(i) := rifire(reg).filter(_ ne riready(i)).reduce(_ && _) | ||||||
|  |       wivalid(i) := wifire(reg).filter(_ ne wiready(i)).reduce(_ && _) | ||||||
|  |       roready(i) := rofire(reg).filter(_ ne rovalid(i)).reduce(_ && _) | ||||||
|  |       woready(i) := wofire(reg).filter(_ ne wovalid(i)).reduce(_ && _) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     out.bits.read  := back.bits.read | ||||||
|  |     out.bits.data  := Vec(dataOut)(back.bits.index) | ||||||
|  |     out.bits.extra := back.bits.extra | ||||||
|  |  | ||||||
|  |     (endIndex, out) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								src/main/scala/uncore/tilelink2/RegisterRouter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/main/scala/uncore/tilelink2/RegisterRouter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) | ||||||
|  |   extends TLManagerNode(beatBytes, TLManagerParameters( | ||||||
|  |     address            = Seq(address), | ||||||
|  |     supportsGet        = TransferSizes(1, beatBytes), | ||||||
|  |     supportsPutPartial = TransferSizes(1, beatBytes), | ||||||
|  |     supportsPutFull    = TransferSizes(1, beatBytes), | ||||||
|  |     fifoId             = Some(0))) // requests are handled in order | ||||||
|  | { | ||||||
|  |   require (!address.strided) | ||||||
|  |  | ||||||
|  |   // Calling this method causes the matching TL2 bundle to be | ||||||
|  |   // configured to route all requests to the listed RegFields. | ||||||
|  |   def regmap(mapping: RegField.Map*) = { | ||||||
|  |     val a = bundleIn(0).a | ||||||
|  |     val d = bundleIn(0).d | ||||||
|  |     val edge = edgesIn(0) | ||||||
|  |  | ||||||
|  |     // Please forgive me ... | ||||||
|  |     val baseEnd = 0 | ||||||
|  |     val (sizeEnd,   sizeOff)   = (edge.bundle.sizeBits   + baseEnd, baseEnd) | ||||||
|  |     val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd) | ||||||
|  |     val (addrLoEnd, addrLoOff) = (log2Ceil(beatBytes)    + sourceEnd, sourceEnd) | ||||||
|  |  | ||||||
|  |     val params = RegMapperParams(log2Up(address.mask+1), beatBytes, addrLoEnd) | ||||||
|  |     val in = Wire(Decoupled(new RegMapperInput(params))) | ||||||
|  |     in.bits.read  := a.bits.opcode === TLMessages.Get | ||||||
|  |     in.bits.index := a.bits.addr_hi | ||||||
|  |     in.bits.data  := a.bits.data | ||||||
|  |     in.bits.mask  := a.bits.mask | ||||||
|  |     in.bits.extra := Cat(edge.addr_lo(a.bits), a.bits.source, a.bits.size) | ||||||
|  |  | ||||||
|  |     // Invoke the register map builder | ||||||
|  |     val (endIndex, out) = RegMapper(beatBytes, concurrency, in, mapping:_*) | ||||||
|  |  | ||||||
|  |     // All registers must fit inside the device address space | ||||||
|  |     require (address.mask >= (endIndex-1)*beatBytes) | ||||||
|  |  | ||||||
|  |     // No flow control needed | ||||||
|  |     in.valid  := a.valid | ||||||
|  |     a.ready   := in.ready | ||||||
|  |     d.valid   := out.valid | ||||||
|  |     out.ready := d.ready | ||||||
|  |  | ||||||
|  |     // We must restore the size and addr_lo to enable width adapters to work | ||||||
|  |     d.bits := edge.AccessAck( | ||||||
|  |       fromAddress = out.bits.extra(addrLoEnd-1, addrLoOff), | ||||||
|  |       fromSink    = UInt(0), // our unique sink id | ||||||
|  |       toSource    = out.bits.extra(sourceEnd-1, sourceOff), | ||||||
|  |       lgSize      = out.bits.extra(sizeEnd-1, sizeOff)) | ||||||
|  |  | ||||||
|  |     // avoid a Mux on the data bus by manually overriding two fields | ||||||
|  |     d.bits.data := out.bits.data | ||||||
|  |     d.bits.opcode := Mux(out.bits.read, TLMessages.AccessAckData, TLMessages.AccessAck) | ||||||
|  |  | ||||||
|  |     // Tie off unused channels | ||||||
|  |     bundleIn(0).b.valid := Bool(false) | ||||||
|  |     bundleIn(0).c.ready := Bool(true) | ||||||
|  |     bundleIn(0).e.ready := Bool(true) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object TLRegisterNode | ||||||
|  | { | ||||||
|  |   def apply(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) = | ||||||
|  |     new TLRegisterNode(address, concurrency, beatBytes) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // These convenience methods below combine to make it possible to create a TL2  | ||||||
|  | // register mapped device from a totally abstract register mapped device. | ||||||
|  | // See GPIO.scala in this directory for an example | ||||||
|  |  | ||||||
|  | abstract class TLRegisterRouterBase(address: AddressSet, concurrency: Option[Int], beatBytes: Int) extends LazyModule | ||||||
|  | { | ||||||
|  |   val node = TLRegisterNode(address, concurrency, beatBytes) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLRegBundle[P](val params: P, val in: Vec[TLBundle]) extends Bundle | ||||||
|  |  | ||||||
|  | class TLRegModule[P, B <: Bundle](val params: P, bundleBuilder: => B, router: TLRegisterRouterBase) | ||||||
|  |   extends LazyModuleImp(router) with HasRegMap | ||||||
|  | { | ||||||
|  |   val io = bundleBuilder | ||||||
|  |   def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLRegisterRouter[B <: Bundle, M <: LazyModuleImp] | ||||||
|  |    (base: BigInt, size: BigInt = 4096, concurrency: Option[Int] = None, beatBytes: Int = 4) | ||||||
|  |    (bundleBuilder: Vec[TLBundle] => B) | ||||||
|  |    (moduleBuilder: (=> B, TLRegisterRouterBase) => M) | ||||||
|  |   extends TLRegisterRouterBase(AddressSet(base, size-1), concurrency, beatBytes) | ||||||
|  | { | ||||||
|  |   require (size % 4096 == 0) // devices should be 4K aligned | ||||||
|  |   require (isPow2(size)) | ||||||
|  |   require (size >= 4096) | ||||||
|  |  | ||||||
|  |   lazy val module = moduleBuilder(bundleBuilder(node.bundleIn), this) | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								src/main/scala/uncore/tilelink2/SRAM.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/main/scala/uncore/tilelink2/SRAM.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | class TLRAM(address: AddressSet, beatBytes: Int = 4) extends LazyModule | ||||||
|  | { | ||||||
|  |   val node = TLManagerNode(beatBytes, TLManagerParameters( | ||||||
|  |     address            = List(address), | ||||||
|  |     regionType         = RegionType.UNCACHED, | ||||||
|  |     supportsGet        = TransferSizes(1, beatBytes), | ||||||
|  |     supportsPutPartial = TransferSizes(1, beatBytes), | ||||||
|  |     supportsPutFull    = TransferSizes(1, beatBytes), | ||||||
|  |     fifoId             = Some(0))) // requests are handled in order | ||||||
|  |  | ||||||
|  |   // We require the address range to include an entire beat (for the write mask) | ||||||
|  |   require ((address.mask & (beatBytes-1)) == beatBytes-1) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in = node.bundleIn | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def bigBits(x: BigInt, tail: List[Boolean] = List.empty[Boolean]): List[Boolean] = | ||||||
|  |       if (x == 0) tail.reverse else bigBits(x >> 1, ((x & 1) == 1) :: tail) | ||||||
|  |     val mask = bigBits(address.mask >> log2Ceil(beatBytes)) | ||||||
|  |  | ||||||
|  |     val in = io.in(0) | ||||||
|  |     val addrBits = (mask zip in.a.bits.addr_hi.toBools).filter(_._1).map(_._2) | ||||||
|  |     val memAddress = Cat(addrBits.reverse) | ||||||
|  |     val mem = SeqMem(1 << addrBits.size, Vec(beatBytes, Bits(width = 8))) | ||||||
|  |  | ||||||
|  |     val d_full = RegInit(Bool(false)) | ||||||
|  |     val d_read = Reg(Bool()) | ||||||
|  |     val d_size = Reg(UInt()) | ||||||
|  |     val d_source = Reg(UInt()) | ||||||
|  |     val d_addr = Reg(UInt()) | ||||||
|  |     val d_data = Wire(UInt()) | ||||||
|  |  | ||||||
|  |     // Flow control | ||||||
|  |     when (in.d.fire()) { d_full := Bool(false) } | ||||||
|  |     when (in.a.fire()) { d_full := Bool(true)  } | ||||||
|  |     in.d.valid := d_full | ||||||
|  |     in.a.ready := in.d.ready || !d_full | ||||||
|  |  | ||||||
|  |     val edge = node.edgesIn(0) | ||||||
|  |     in.d.bits := edge.AccessAck(d_addr, UInt(0), d_source, d_size) | ||||||
|  |     // avoid data-bus Mux | ||||||
|  |     in.d.bits.data := d_data | ||||||
|  |     in.d.bits.opcode := Mux(d_read, TLMessages.AccessAckData, TLMessages.AccessAck) | ||||||
|  |  | ||||||
|  |     val read = in.a.bits.opcode === TLMessages.Get | ||||||
|  |     val rdata = Wire(Vec(beatBytes, Bits(width = 8))) | ||||||
|  |     val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) } | ||||||
|  |     d_data := Cat(rdata.reverse) | ||||||
|  |     when (in.a.fire()) { | ||||||
|  |       d_read   := read | ||||||
|  |       d_size   := in.a.bits.size | ||||||
|  |       d_source := in.a.bits.source | ||||||
|  |       d_addr   := edge.addr_lo(in.a.bits) | ||||||
|  |       when (read) { | ||||||
|  |         rdata := mem.read(memAddress) | ||||||
|  |       } .otherwise { | ||||||
|  |         mem.write(memAddress, wdata, in.a.bits.mask.toBools) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Tie off unused channels | ||||||
|  |     in.b.valid := Bool(false) | ||||||
|  |     in.c.ready := Bool(true) | ||||||
|  |     in.e.ready := Bool(true) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								src/main/scala/uncore/tilelink2/TLNodes.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/main/scala/uncore/tilelink2/TLNodes.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  | import scala.collection.mutable.ListBuffer | ||||||
|  | import chisel3.internal.sourceinfo.SourceInfo | ||||||
|  |  | ||||||
|  | object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] | ||||||
|  | { | ||||||
|  |   def edgeO(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(po, pi) | ||||||
|  |   def edgeI(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeIn  = new TLEdgeIn(po, pi) | ||||||
|  |   def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = { | ||||||
|  |     require (!eo.isEmpty) | ||||||
|  |     Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_)))) | ||||||
|  |   } | ||||||
|  |   def bundleI(ei: Seq[TLEdgeIn]): Vec[TLBundle] = { | ||||||
|  |     require (!ei.isEmpty) | ||||||
|  |     Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def connect(bo: TLBundle, eo: TLEdgeOut, bi: TLBundle, ei: TLEdgeIn)(implicit sourceInfo: SourceInfo): Unit = { | ||||||
|  |     require (eo.asInstanceOf[TLEdgeParameters] == ei.asInstanceOf[TLEdgeParameters]) | ||||||
|  |     TLMonitor.legalize(bo, eo, bi, ei) | ||||||
|  |     bi <> bo | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | case class TLIdentityNode() extends IdentityNode(TLImp) | ||||||
|  | case class TLOutputNode() extends OutputNode(TLImp) | ||||||
|  | case class TLInputNode() extends InputNode(TLImp) | ||||||
|  |  | ||||||
|  | case class TLClientNode(params: TLClientParameters, numPorts: Range.Inclusive = 1 to 1) | ||||||
|  |   extends SourceNode(TLImp)(TLClientPortParameters(Seq(params)), numPorts) | ||||||
|  |  | ||||||
|  | case class TLManagerNode(beatBytes: Int, params: TLManagerParameters, numPorts: Range.Inclusive = 1 to 1) | ||||||
|  |   extends SinkNode(TLImp)(TLManagerPortParameters(Seq(params), beatBytes), numPorts) | ||||||
|  |  | ||||||
|  | case class TLAdapterNode( | ||||||
|  |   clientFn:        Seq[TLClientPortParameters]  => TLClientPortParameters, | ||||||
|  |   managerFn:       Seq[TLManagerPortParameters] => TLManagerPortParameters, | ||||||
|  |   numClientPorts:  Range.Inclusive = 1 to 1, | ||||||
|  |   numManagerPorts: Range.Inclusive = 1 to 1) | ||||||
|  |   extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) | ||||||
							
								
								
									
										218
									
								
								src/main/scala/uncore/tilelink2/Xbar.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/main/scala/uncore/tilelink2/Xbar.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | |||||||
|  | // See LICENSE for license details. | ||||||
|  |  | ||||||
|  | package uncore.tilelink2 | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | object TLXbar | ||||||
|  | { | ||||||
|  |   def lowestIndex(requests: Vec[Bool], execute: Bool) = { | ||||||
|  |     // lowest-index first is stateless; ignore execute | ||||||
|  |     val ors = Vec(requests.scanLeft(Bool(false))(_ || _).init) // prefix-OR | ||||||
|  |     Vec((ors zip requests) map { case (o, r) => !o && r }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extends LazyModule | ||||||
|  | { | ||||||
|  |   def mapInputIds (ports: Seq[TLClientPortParameters ]) = assignRanges(ports.map(_.endSourceId)) | ||||||
|  |   def mapOutputIds(ports: Seq[TLManagerPortParameters]) = assignRanges(ports.map(_.endSinkId)) | ||||||
|  |  | ||||||
|  |   def assignRanges(sizes: Seq[Int]) = { | ||||||
|  |     val pow2Sizes = sizes.map(1 << log2Ceil(_)) | ||||||
|  |     val tuples = pow2Sizes.zipWithIndex.sortBy(_._1) // record old index, then sort by increasing size | ||||||
|  |     val starts = tuples.scanRight(0)(_._1 + _).tail // suffix-sum of the sizes = the start positions | ||||||
|  |     val ranges = (tuples zip starts) map { case ((sz, i), st) => (IdRange(st, st+sz), i) } | ||||||
|  |     ranges.sortBy(_._2).map(_._1) // Restore orignal order | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def relabeler() = { | ||||||
|  |     var idFactory = 0 | ||||||
|  |     () => { | ||||||
|  |       val fifoMap = scala.collection.mutable.HashMap.empty[Int, Int] | ||||||
|  |       (x: Int) => { | ||||||
|  |         if (fifoMap.contains(x)) fifoMap(x) else { | ||||||
|  |           val out = idFactory | ||||||
|  |           idFactory = idFactory + 1 | ||||||
|  |           fifoMap += (x -> out) | ||||||
|  |           out | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   val node = TLAdapterNode( | ||||||
|  |     numClientPorts  = 1 to 32, | ||||||
|  |     numManagerPorts = 1 to 32, | ||||||
|  |     clientFn  = { seq => | ||||||
|  |       val clients = (mapInputIds(seq) zip seq) flatMap { case (range, port) => | ||||||
|  |         port.clients map { client => client.copy( | ||||||
|  |           sourceId = client.sourceId.shift(range.start) | ||||||
|  |         )} | ||||||
|  |       } | ||||||
|  |       TLClientPortParameters(clients) | ||||||
|  |     }, | ||||||
|  |     managerFn = { seq => | ||||||
|  |       val fifoIdFactory = relabeler() | ||||||
|  |       val managers = (mapOutputIds(seq) zip seq) flatMap { case (range, port) => | ||||||
|  |         require (port.beatBytes == seq(0).beatBytes) | ||||||
|  |         val fifoIdMapper = fifoIdFactory() | ||||||
|  |         port.managers map { manager => manager.copy( | ||||||
|  |           sinkId = manager.sinkId.shift(range.start), | ||||||
|  |           fifoId = manager.fifoId.map(fifoIdMapper(_)) | ||||||
|  |         )} | ||||||
|  |       } | ||||||
|  |       TLManagerPortParameters(managers, seq(0).beatBytes) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |   lazy val module = new LazyModuleImp(this) { | ||||||
|  |     val io = new Bundle { | ||||||
|  |       val in  = node.bundleIn | ||||||
|  |       val out = node.bundleOut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Grab the port ID mapping | ||||||
|  |     val inputIdRanges = mapInputIds(node.edgesIn.map(_.client)) | ||||||
|  |     val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager)) | ||||||
|  |  | ||||||
|  |     // We need an intermediate size of bundle with the widest possible identifiers | ||||||
|  |     val wide_bundle = io.in(0).params.union(io.out(0).params) | ||||||
|  |  | ||||||
|  |     // Handle size = 1 gracefully (Chisel3 empty range is broken) | ||||||
|  |     def trim(id: UInt, size: Int) = if (size <= 1) UInt(0) else id(log2Ceil(size)-1, 0) | ||||||
|  |     def transpose(x: Seq[Seq[Bool]]) = Vec.tabulate(x(0).size) { i => Vec.tabulate(x.size) { j => x(j)(i) } } | ||||||
|  |  | ||||||
|  |     // Transform input bundle sources (sinks use global namespace on both sides) | ||||||
|  |     val in = Wire(Vec(io.in.size, TLBundle(wide_bundle))) | ||||||
|  |     for (i <- 0 until in.size) { | ||||||
|  |       val r = inputIdRanges(i) | ||||||
|  |       in(i) <> io.in(i) | ||||||
|  |       // prefix sources | ||||||
|  |       in(i).a.bits.source := io.in(i).a.bits.source | UInt(r.start) | ||||||
|  |       in(i).c.bits.source := io.in(i).c.bits.source | UInt(r.start) | ||||||
|  |       // defix sources | ||||||
|  |       io.in(i).b.bits.source := trim(in(i).b.bits.source, r.size) | ||||||
|  |       io.in(i).d.bits.source := trim(in(i).d.bits.source, r.size) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Transform output bundle sinks (sources use global namespace on both sides) | ||||||
|  |     val out = Wire(Vec(io.out.size, TLBundle(wide_bundle))) | ||||||
|  |     for (i <- 0 until out.size) { | ||||||
|  |       val r = outputIdRanges(i) | ||||||
|  |       io.out(i) <> out(i) | ||||||
|  |       // prefix sinks | ||||||
|  |       out(i).d.bits.sink := io.out(i).d.bits.sink | UInt(r.start) | ||||||
|  |       // defix sinks | ||||||
|  |       io.out(i).e.bits.sink := trim(out(i).e.bits.sink, r.size) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // The crossbar cross-connection state; defined later | ||||||
|  |     val grantedAIO = Wire(Vec(in .size, Vec(out.size, Bool()))) | ||||||
|  |     val grantedBOI = Wire(Vec(out.size, Vec(in .size, Bool()))) | ||||||
|  |     val grantedCIO = Wire(Vec(in .size, Vec(out.size, Bool()))) | ||||||
|  |     val grantedDOI = Wire(Vec(out.size, Vec(in .size, Bool()))) | ||||||
|  |     val grantedEIO = Wire(Vec(in .size, Vec(out.size, Bool()))) | ||||||
|  |  | ||||||
|  |     val grantedAOI = transpose(grantedAIO) | ||||||
|  |     val grantedBIO = transpose(grantedBOI) | ||||||
|  |     val grantedCOI = transpose(grantedCIO) | ||||||
|  |     val grantedDIO = transpose(grantedDOI) | ||||||
|  |     val grantedEOI = transpose(grantedEIO) | ||||||
|  |  | ||||||
|  |     // Mux1H passes a single-source through unmasked. That's bad for control. | ||||||
|  |     def Mux1C(sel: Seq[Bool], ctl: Seq[Bool]) = (sel zip ctl).map{ case (a,b) => a && b }.reduce(_ || _) | ||||||
|  |  | ||||||
|  |     // Mux clients to managers | ||||||
|  |     for (o <- 0 until out.size) { | ||||||
|  |       out(o).a.valid := Mux1C(grantedAOI(o), in.map(_.a.valid)) | ||||||
|  |       out(o).a.bits  := Mux1H(grantedAOI(o), in.map(_.a.bits)) | ||||||
|  |       out(o).b.ready := Mux1C(grantedBOI(o), in.map(_.b.ready)) | ||||||
|  |       out(o).c.valid := Mux1C(grantedCOI(o), in.map(_.c.valid)) | ||||||
|  |       out(o).c.bits  := Mux1H(grantedCOI(o), in.map(_.c.bits)) | ||||||
|  |       out(o).d.ready := Mux1C(grantedDOI(o), in.map(_.d.ready)) | ||||||
|  |       out(o).e.valid := Mux1C(grantedEOI(o), in.map(_.e.valid)) | ||||||
|  |       out(o).e.bits  := Mux1H(grantedEOI(o), in.map(_.e.bits)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Mux managers to clients | ||||||
|  |     for (i <- 0 until in.size) { | ||||||
|  |       in(i).a.ready := Mux1C(grantedAIO(i), out.map(_.a.ready)) | ||||||
|  |       in(i).b.valid := Mux1C(grantedBIO(i), out.map(_.b.valid)) | ||||||
|  |       in(i).b.bits  := Mux1H(grantedBIO(i), out.map(_.b.bits)) | ||||||
|  |       in(i).c.ready := Mux1C(grantedCIO(i), out.map(_.c.ready)) | ||||||
|  |       in(i).d.valid := Mux1C(grantedDIO(i), out.map(_.d.valid)) | ||||||
|  |       in(i).d.bits  := Mux1H(grantedDIO(i), out.map(_.d.bits)) | ||||||
|  |       in(i).e.ready := Mux1C(grantedEIO(i), out.map(_.e.ready)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val requestAIO = Vec(in.map  { i => Vec(node.edgesOut.map  { o => i.a.valid && o.manager.contains(o.address(i.a.bits)) }) }) | ||||||
|  |     val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map  { i => o.b.valid && i        .contains(o.b.bits.source)     }) }) | ||||||
|  |     val requestCIO = Vec(in.map  { i => Vec(node.edgesOut.map  { o => i.c.valid && o.manager.contains(o.address(i.c.bits)) }) }) | ||||||
|  |     val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map  { i => o.d.valid && i        .contains(o.d.bits.source)     }) }) | ||||||
|  |     val requestEIO = Vec(in.map  { i => Vec(outputIdRanges.map { o => i.e.valid && o        .contains(i.e.bits.sink)       }) }) | ||||||
|  |  | ||||||
|  |     val beatsA = Vec((in  zip node.edgesIn)  map { case (i, e) => e.numBeats(i.a.bits) }) | ||||||
|  |     val beatsB = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.b.bits) }) | ||||||
|  |     val beatsC = Vec((in  zip node.edgesIn)  map { case (i, e) => e.numBeats(i.c.bits) }) | ||||||
|  |     val beatsD = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.d.bits) }) | ||||||
|  |     val beatsE = Vec((in  zip node.edgesIn)  map { case (i, e) => e.numBeats(i.e.bits) }) | ||||||
|  |  | ||||||
|  |     // Which pairs support support transfers | ||||||
|  |     val maskIO = Vec.tabulate(in.size) { i => Vec.tabulate(out.size) { o =>  | ||||||
|  |       Bool(node.edgesIn(i).client.anySupportProbe && node.edgesOut(o).manager.anySupportAcquire) | ||||||
|  |     } } | ||||||
|  |     val maskOI = transpose(maskIO) | ||||||
|  |  | ||||||
|  |     // Mask out BCE channel connections (to be optimized away) for transfer-incapable pairings | ||||||
|  |     def mask(a: Seq[Seq[Bool]], b: Seq[Seq[Bool]]) =  | ||||||
|  |       Vec((a zip b) map { case (x, y) => Vec((x zip y) map { case (a, b) => a && b }) }) | ||||||
|  |  | ||||||
|  |     grantedAIO :=      arbitrate(     requestAIO,          beatsA, out.map(_.a.fire())) | ||||||
|  |     grantedBOI := mask(arbitrate(mask(requestBOI, maskOI), beatsB, in .map(_.b.fire())), maskOI) | ||||||
|  |     grantedCIO := mask(arbitrate(mask(requestCIO, maskIO), beatsC, out.map(_.c.fire())), maskIO) | ||||||
|  |     grantedDOI :=      arbitrate(     requestDOI,          beatsD, in .map(_.d.fire())) | ||||||
|  |     grantedEIO := mask(arbitrate(mask(requestEIO, maskIO), beatsE, out.map(_.e.fire())), maskIO) | ||||||
|  |  | ||||||
|  |     def arbitrate(request: Seq[Seq[Bool]], beats: Seq[UInt], progress: Seq[Bool]) = { | ||||||
|  |       request foreach { row => require (row.size == progress.size) } // consistent # of resources | ||||||
|  |       request foreach { resources => // only one resource is requested | ||||||
|  |         val prefixOR = resources.scanLeft(Bool(false))(_ || _).init | ||||||
|  |         assert (!(prefixOR zip resources).map{case (a, b) => a && b}.reduce(_ || _)) | ||||||
|  |       } | ||||||
|  |       transpose((transpose(request) zip progress).map { case (r,p) => arbitrate1(r, beats, p) }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     def arbitrate1(requests: Vec[Bool], beats: Seq[UInt], progress: Bool) = { | ||||||
|  |       require (requests.size == beats.size) // consistent # of requesters | ||||||
|  |  | ||||||
|  |       val beatsLeft = RegInit(UInt(0)) | ||||||
|  |       val idle = beatsLeft === UInt(0) | ||||||
|  |  | ||||||
|  |       // Apply policy to select which requester wins | ||||||
|  |       val winners = Vec(policy(requests, idle)) | ||||||
|  |  | ||||||
|  |       // Winners must be a subset of requests | ||||||
|  |       assert ((winners zip requests).map { case (w,r) => !w || r } .reduce(_ && _)) | ||||||
|  |       // There must be only one winner | ||||||
|  |       val prefixOR = winners.scanLeft(Bool(false))(_ || _).init | ||||||
|  |       assert ((prefixOR zip winners).map { case (p,w) => !p || !w }.reduce(_ && _)) | ||||||
|  |  | ||||||
|  |       // Supposing we take the winner as input, how many beats must be sent? | ||||||
|  |       val maskedBeats = (winners zip beats).map { case (w,b) => Mux(w, b, UInt(0)) } | ||||||
|  |       val initBeats = maskedBeats.reduceLeft(_ | _) // no winner => 0 beats | ||||||
|  |       // What is the counter state before progress? | ||||||
|  |       val todoBeats = Mux(idle, initBeats, beatsLeft) | ||||||
|  |       // Apply progress and register the result | ||||||
|  |       beatsLeft := todoBeats - progress.asUInt | ||||||
|  |       assert (!progress || todoBeats =/= UInt(0)) // underflow should be impossible | ||||||
|  |  | ||||||
|  |       // The previous arbitration state of the resource | ||||||
|  |       val state = RegInit(Vec.fill(requests.size)(Bool(false))) | ||||||
|  |       // Only take a new value while idle | ||||||
|  |       val muxState = Mux(idle, winners, state) | ||||||
|  |       state := muxState | ||||||
|  |  | ||||||
|  |       muxState | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/main/scala/uncore/tilelink2/package.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/main/scala/uncore/tilelink2/package.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package uncore | ||||||
|  |  | ||||||
|  | import Chisel._ | ||||||
|  |  | ||||||
|  | package object tilelink2 | ||||||
|  | { | ||||||
|  |   type TLBaseNode = BaseNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] | ||||||
|  |   def OH1ToUInt(x: UInt) = OHToUInt((x << 1 | UInt(1)) ^ x) | ||||||
|  |   def UIntToOH1(x: UInt, width: Int) = ~(SInt(-1, width=width).asUInt << x)(width-1, 0) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user