Refactor package hierarchy and remove legacy bus protocol implementations (#845)
* Refactors package hierarchy. Additionally: - Removes legacy ground tests and configs - Removes legacy bus protocol implementations - Removes NTiles - Adds devices package - Adds more functions to util package
This commit is contained in:
		
							
								
								
									
										68
									
								
								src/main/scala/amba/axi4/Buffer.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/main/scala/amba/axi4/Buffer.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import chisel3.util.IrrevocableIO | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import scala.math.{min,max} | ||||
|  | ||||
| // pipe is only used if a queue has depth = 1 | ||||
| class AXI4Buffer( | ||||
|   aw: BufferParams, | ||||
|   w:  BufferParams, | ||||
|   b:  BufferParams, | ||||
|   ar: BufferParams, | ||||
|   r:  BufferParams)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   def this(aw: BufferParams, br: BufferParams)(implicit p: Parameters) = this(aw, aw, br, aw, br) | ||||
|   def this(x: BufferParams)(implicit p: Parameters) = this(x, x) | ||||
|   def this()(implicit p: Parameters) = this(BufferParams.default) | ||||
|  | ||||
|   val node = AXI4AdapterNode( | ||||
|     masterFn = { p => p }, | ||||
|     slaveFn  = { p => p.copy(minLatency = p.minLatency + min(aw.latency,ar.latency) + min(r.latency,b.latency)) }) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     def buffer[T <: Data](config: BufferParams, data: IrrevocableIO[T]): IrrevocableIO[T] = { | ||||
|       if (config.isDefined) { | ||||
|         Queue.irrevocable(data, config.depth, pipe=config.pipe, flow=config.flow) | ||||
|       } else { | ||||
|         data | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|       out.aw <> buffer(aw, in .aw) | ||||
|       out.w  <> buffer(w,  in .w) | ||||
|       in .b  <> buffer(b,  out.b) | ||||
|       out.ar <> buffer(ar, in .ar) | ||||
|       in .r  <> buffer(r,  out.r) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4Buffer | ||||
| { | ||||
|   // applied to the AXI4 source node; y.node := AXI4Buffer(x.node) | ||||
|   def apply()                                  (x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = apply(BufferParams.default)(x) | ||||
|   def apply(z: BufferParams)                   (x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = apply(z, z)(x) | ||||
|   def apply(aw: BufferParams, br: BufferParams)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = apply(aw, aw, br, aw, br)(x) | ||||
|   def apply( | ||||
|     aw: BufferParams, | ||||
|     w:  BufferParams, | ||||
|     b:  BufferParams, | ||||
|     ar: BufferParams, | ||||
|     r:  BufferParams)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { | ||||
|     val buffer = LazyModule(new AXI4Buffer(aw, w, b, ar, r)) | ||||
|     buffer.node := x | ||||
|     buffer.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										78
									
								
								src/main/scala/amba/axi4/Bundles.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/main/scala/amba/axi4/Bundles.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.util.Irrevocable | ||||
| import freechips.rocketchip.util.GenericParameterizedBundle | ||||
|  | ||||
| abstract class AXI4BundleBase(params: AXI4BundleParameters) extends GenericParameterizedBundle(params) | ||||
|  | ||||
| abstract class AXI4BundleA(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   val id     = UInt(width = params.idBits) | ||||
|   val addr   = UInt(width = params.addrBits) | ||||
|   val len    = UInt(width = params.lenBits)  // number of beats - 1 | ||||
|   val size   = UInt(width = params.sizeBits) // bytes in beat = 2^size | ||||
|   val burst  = UInt(width = params.burstBits) | ||||
|   val lock   = UInt(width = params.lockBits) | ||||
|   val cache  = UInt(width = params.cacheBits) | ||||
|   val prot   = UInt(width = params.protBits) | ||||
|   val qos    = UInt(width = params.qosBits)  // 0=no QoS, bigger = higher priority | ||||
|   val user = if (params.userBits > 0) Some(UInt(width = params.userBits)) else None | ||||
|   // val region = UInt(width = 4) // optional | ||||
|  | ||||
|   // Number of bytes-1 in this operation | ||||
|   def bytes1(x:Int=0) = { | ||||
|     val maxShift = 1 << params.sizeBits | ||||
|     val tail = UInt((BigInt(1) << maxShift) - 1) | ||||
|     (Cat(len, tail) << size) >> maxShift | ||||
|   } | ||||
| } | ||||
|  | ||||
| // A non-standard bundle that can be both AR and AW | ||||
| class AXI4BundleARW(params: AXI4BundleParameters) extends AXI4BundleA(params) | ||||
| { | ||||
|   val wen = Bool() | ||||
| } | ||||
|  | ||||
| class AXI4BundleAW(params: AXI4BundleParameters) extends AXI4BundleA(params) | ||||
| class AXI4BundleAR(params: AXI4BundleParameters) extends AXI4BundleA(params) | ||||
|  | ||||
| class AXI4BundleW(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   // id ... removed in AXI4 | ||||
|   val data = UInt(width = params.dataBits) | ||||
|   val strb = UInt(width = params.dataBits/8) | ||||
|   val last = Bool() | ||||
| } | ||||
|  | ||||
| class AXI4BundleR(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   val id   = UInt(width = params.idBits) | ||||
|   val data = UInt(width = params.dataBits) | ||||
|   val resp = UInt(width = params.respBits) | ||||
|   val user = if (params.userBits > 0) Some(UInt(width = params.userBits)) else None | ||||
|   val last = Bool() | ||||
| } | ||||
|  | ||||
| class AXI4BundleB(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   val id   = UInt(width = params.idBits) | ||||
|   val resp = UInt(width = params.respBits) | ||||
|   val user = if (params.userBits > 0) Some(UInt(width = params.userBits)) else None | ||||
| } | ||||
|  | ||||
| class AXI4Bundle(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   val aw = Irrevocable(new AXI4BundleAW(params)) | ||||
|   val w  = Irrevocable(new AXI4BundleW (params)) | ||||
|   val b  = Irrevocable(new AXI4BundleB (params)).flip | ||||
|   val ar = Irrevocable(new AXI4BundleAR(params)) | ||||
|   val r  = Irrevocable(new AXI4BundleR (params)).flip | ||||
| } | ||||
|  | ||||
| object AXI4Bundle | ||||
| { | ||||
|   def apply(params: AXI4BundleParameters) = new AXI4Bundle(params) | ||||
| } | ||||
							
								
								
									
										114
									
								
								src/main/scala/amba/axi4/Deinterleaver.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/main/scala/amba/axi4/Deinterleaver.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import chisel3.util.IrrevocableIO | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.util.{leftOR, rightOR, UIntToOH1, OH1ToOH} | ||||
| import scala.math.{min,max} | ||||
|  | ||||
| class AXI4Deinterleaver(maxReadBytes: Int)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   require (maxReadBytes >= 1 && isPow2(maxReadBytes)) | ||||
|  | ||||
|   val node = AXI4AdapterNode( | ||||
|     masterFn = { mp => mp }, | ||||
|     slaveFn  = { sp => sp.copy(slaves = sp.slaves.map(s => s.copy( | ||||
|       supportsRead = s.supportsRead.intersect(TransferSizes(1, maxReadBytes)), | ||||
|       interleavedId = Some(0)))) | ||||
|   }) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|       val endId = edgeOut.master.endId | ||||
|       val beatBytes = edgeOut.slave.beatBytes | ||||
|       val beats = (maxReadBytes+beatBytes-1) / beatBytes | ||||
|  | ||||
|       // This adapter leaves the control + write paths completely untouched | ||||
|       out.ar <> in.ar | ||||
|       out.aw <> in.aw | ||||
|       out.w <> in.w | ||||
|       in.b <> out.b | ||||
|  | ||||
|       if (beats <= 1) { | ||||
|         // Nothing to do if only single-beat R | ||||
|         in.r <> out.r | ||||
|       } else { | ||||
|         // Queues to buffer R responses | ||||
|         val qs = Seq.tabulate(endId) { i => | ||||
|           val depth = edgeOut.master.masters.find(_.id.contains(i)).flatMap(_.maxFlight).getOrElse(0) | ||||
|           if (depth > 0) { | ||||
|             Module(new Queue(out.r.bits, beats)).io | ||||
|           } else { | ||||
|             Wire(new QueueIO(out.r.bits, beats)) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         // Which ID is being enqueued and dequeued? | ||||
|         val locked = RegInit(Bool(false)) | ||||
|         val deq_id = Reg(UInt(width=log2Up(endId))) | ||||
|         val enq_id = out.r.bits.id | ||||
|         val deq_OH = UIntToOH(deq_id, endId) | ||||
|         val enq_OH = UIntToOH(enq_id, endId) | ||||
|  | ||||
|         // Track the number of completely received bursts per FIFO id | ||||
|         val pending = Cat(Seq.tabulate(endId) { i => | ||||
|           val depth = edgeOut.master.masters.find(_.id.contains(i)).flatMap(_.maxFlight).getOrElse(0) | ||||
|           if (depth == 0) { | ||||
|             Bool(false) | ||||
|           } else { | ||||
|             val count = RegInit(UInt(0, width=log2Ceil(beats+1))) | ||||
|             val next = Wire(count) | ||||
|             val inc = enq_OH(i) && out.r.fire() && out.r.bits.last | ||||
|             val dec = deq_OH(i) && in.r.fire() && in.r.bits.last | ||||
|             next := count + inc.asUInt - dec.asUInt | ||||
|             count := next | ||||
|             // Bounds checking | ||||
|             assert (!dec || count =/= UInt(0)) | ||||
|             assert (!inc || count =/= UInt(beats)) | ||||
|             next =/= UInt(0) | ||||
|           } | ||||
|         }.reverse) | ||||
|  | ||||
|         // Select which Q will we start sending next cycle | ||||
|         val winner  = pending & ~(leftOR(pending) << 1) | ||||
|         when (!locked || (in.r.fire() && in.r.bits.last)) { | ||||
|           locked := pending.orR | ||||
|           deq_id := OHToUInt(winner) | ||||
|         } | ||||
|  | ||||
|         // Transmit the selected burst to inner | ||||
|         in.r.valid := locked | ||||
|         in.r.bits  := Vec(qs.map(_.deq.bits))(deq_id) | ||||
|         (deq_OH.toBools zip qs) foreach { case (s, q) => | ||||
|           q.deq.ready := s && in.r.fire() | ||||
|         } | ||||
|  | ||||
|         // Feed response into matching Q | ||||
|         out.r.ready := Vec(qs.map(_.enq.ready))(enq_id) | ||||
|         (enq_OH.toBools zip qs) foreach { case (s, q) => | ||||
|           q.enq.valid := s && out.r.valid | ||||
|           q.enq.bits := out.r.bits | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4Deinterleaver | ||||
| { | ||||
|   // applied to the AXI4 source node; y.node := AXI4Deinterleaver()(x.node) | ||||
|   def apply(maxReadBytes: Int)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { | ||||
|     val deinterleaver = LazyModule(new AXI4Deinterleaver(maxReadBytes)) | ||||
|     deinterleaver.node := x | ||||
|     deinterleaver.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										214
									
								
								src/main/scala/amba/axi4/Fragmenter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/main/scala/amba/axi4/Fragmenter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import chisel3.util.IrrevocableIO | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.util.{leftOR, rightOR, UIntToOH1, OH1ToOH} | ||||
| import scala.math.{min,max} | ||||
|  | ||||
| class AXI4Fragmenter()(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val maxBeats = 1 << AXI4Parameters.lenBits | ||||
|   def expandTransfer(x: TransferSizes, beatBytes: Int, alignment: BigInt) = | ||||
|     if (!x) x else TransferSizes(x.min, alignment.min(maxBeats*beatBytes).intValue) | ||||
|   def mapSlave(s: AXI4SlaveParameters, beatBytes: Int) = s.copy( | ||||
|     supportsWrite = expandTransfer(s.supportsWrite, beatBytes, s.minAlignment), | ||||
|     supportsRead  = expandTransfer(s.supportsRead,  beatBytes, s.minAlignment), | ||||
|     interleavedId = None) // this breaks interleaving guarantees | ||||
|   def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true) | ||||
|  | ||||
|   val node = AXI4AdapterNode( | ||||
|     masterFn = { mp => mp.copy(masters = mp.masters.map(m => mapMaster(m)), userBits = mp.userBits + 1) }, | ||||
|     slaveFn  = { sp => sp.copy(slaves  = sp.slaves .map(s => mapSlave(s, sp.beatBytes))) }) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|       val slave     = edgeOut.slave | ||||
|       val slaves    = slave.slaves | ||||
|       val beatBytes = slave.beatBytes | ||||
|       val lgBytes   = log2Ceil(beatBytes) | ||||
|       val master    = edgeIn.master | ||||
|       val masters   = master.masters | ||||
|  | ||||
|       // We don't support fragmenting to sub-beat accesses | ||||
|       slaves.foreach { s => | ||||
|         require (!s.supportsRead  || s.supportsRead.contains(beatBytes)) | ||||
|         require (!s.supportsWrite || s.supportsWrite.contains(beatBytes)) | ||||
|       } | ||||
|  | ||||
|       /* We need to decompose a request into  | ||||
|        *   FIXED => each beat is a new request | ||||
|        *   WRAP/INCR => take xfr up to next power of two, capped by max size of target | ||||
|        * | ||||
|        * On AR and AW, we fragment one request into many | ||||
|        * On W we set 'last' on beats which are fragment boundaries | ||||
|        * On R we clear 'last' on the fragments being reassembled | ||||
|        * On B we clear 'valid' on the responses for the injected fragments | ||||
|        * | ||||
|        * AR=>R and AW+W=>B are completely independent state machines. | ||||
|        */ | ||||
|  | ||||
|       /* Returns the number of beats to execute and the new address */ | ||||
|       def fragment(a: IrrevocableIO[AXI4BundleA], supportedSizes1: Seq[Int]): (IrrevocableIO[AXI4BundleA], Bool, UInt) = { | ||||
|         val out = Wire(a) | ||||
|  | ||||
|         val busy   = RegInit(Bool(false)) | ||||
|         val r_addr = Reg(UInt(width = a.bits.params.addrBits)) | ||||
|         val r_len  = Reg(UInt(width = AXI4Parameters.lenBits)) | ||||
|  | ||||
|         val len  = Mux(busy, r_len,  a.bits.len) | ||||
|         val addr = Mux(busy, r_addr, a.bits.addr) | ||||
|  | ||||
|         val lo = if (lgBytes == 0) UInt(0) else addr(lgBytes-1, 0) | ||||
|         val hi = addr >> lgBytes | ||||
|         val alignment = hi(AXI4Parameters.lenBits-1,0) | ||||
|  | ||||
|         // We don't care about illegal addresses; bursts or no bursts... whatever circuit is simpler (AXI4ToTL will fix it) | ||||
|         val sizes1 = (supportedSizes1 zip slave.slaves.map(_.address)).filter(_._1 >= 0).groupBy(_._1).mapValues(_.flatMap(_._2)) | ||||
|         val reductionMask = AddressDecoder(sizes1.values.toList) | ||||
|         val support1 = Mux1H(sizes1.toList.map { case (v, a) => // maximum supported size-1 based on target address | ||||
|           (AddressSet.unify(a.map(_.widen(~reductionMask)).distinct).map(_.contains(addr)).reduce(_||_), UInt(v)) | ||||
|         }) | ||||
|  | ||||
|         /* We need to compute the largest transfer allowed by the AXI len. | ||||
|          * len+1 is the number of beats to execute. | ||||
|          * We want the MSB(len+1)-1; one less than the largest power of two we could execute. | ||||
|          * There are two cases; either len is 2^n-1 in which case we leave it unchanged, ELSE | ||||
|          *   fill the bits from highest to lowest, and shift right by one bit. | ||||
|          */ | ||||
|         val fillLow  = rightOR(len) >> 1   // set   all bits in positions <  a set     bit | ||||
|         val wipeHigh = ~leftOR(~len)       // clear all bits in position  >= a cleared bit | ||||
|         val remain1  = fillLow | wipeHigh  // MSB(a.len+1)-1 | ||||
|         val align1   = ~leftOR(alignment)  // transfer size limited by address alignment | ||||
|         val maxSupported1 = remain1 & align1 & support1 // Take the minimum of all the limits | ||||
|  | ||||
|         // Things that cause us to degenerate to a single beat | ||||
|         val fixed = a.bits.burst === AXI4Parameters.BURST_FIXED | ||||
|         val narrow = a.bits.size =/= UInt(lgBytes) | ||||
|         val bad = fixed || narrow | ||||
|  | ||||
|         // The number of beats-1 to execute | ||||
|         val beats1 = Mux(bad, UInt(0), maxSupported1) | ||||
|         val beats = OH1ToOH(beats1) // beats1 + 1 | ||||
|  | ||||
|         val inc_addr = addr + (beats << a.bits.size) // address after adding transfer | ||||
|         val wrapMask = a.bits.bytes1() // only these bits may change, if wrapping | ||||
|         val mux_addr = Wire(init = inc_addr) | ||||
|         when (a.bits.burst === AXI4Parameters.BURST_WRAP) { | ||||
|           mux_addr := (inc_addr & wrapMask) | ~(~a.bits.addr | wrapMask) | ||||
|         } | ||||
|         when (a.bits.burst === AXI4Parameters.BURST_FIXED) { | ||||
|           mux_addr := a.bits.addr | ||||
|         } | ||||
|  | ||||
|         val last = beats1 === len | ||||
|         a.ready := out.ready && last | ||||
|         out.valid := a.valid | ||||
|  | ||||
|         out.bits := a.bits | ||||
|         out.bits.len := beats1 | ||||
|  | ||||
|         // We forcibly align every access. If the first beat was misaligned, the strb bits | ||||
|         // for the lower addresses must not have been set. Therefore, rounding the address | ||||
|         // down is harmless. We can do this after the address update algorithm, because the | ||||
|         // incremented values will be rounded down the same way. Furthermore, a subword | ||||
|         // offset cannot cause a premature wrap-around. | ||||
|         out.bits.addr := ~(~addr | UIntToOH1(a.bits.size, lgBytes)) | ||||
|  | ||||
|         when (out.fire()) { | ||||
|           busy := !last | ||||
|           r_addr := mux_addr | ||||
|           r_len  := len - beats | ||||
|         } | ||||
|  | ||||
|         (out, last, beats) | ||||
|       } | ||||
|  | ||||
|       // The size to which we will fragment the access | ||||
|       val readSizes1  = slaves.map(s => s.supportsRead .max/beatBytes-1) | ||||
|       val writeSizes1 = slaves.map(s => s.supportsWrite.max/beatBytes-1) | ||||
|  | ||||
|       // Irrevocable queues in front because we want to accept the request before responses come back | ||||
|       val (in_ar, ar_last, _)       = fragment(Queue.irrevocable(in.ar, 1, flow=true), readSizes1) | ||||
|       val (in_aw, aw_last, w_beats) = fragment(Queue.irrevocable(in.aw, 1, flow=true), writeSizes1) | ||||
|  | ||||
|       // AXI ready may not depend on valid of other channels | ||||
|       // We cut wready here along with awready and arready before AXI4ToTL | ||||
|       val in_w = Queue.irrevocable(in.w, 1, flow=true) | ||||
|  | ||||
|       // AR flow control; super easy | ||||
|       out.ar <> in_ar | ||||
|       out.ar.bits.user.get := Cat(in_ar.bits.user.toList ++ Seq(ar_last)) | ||||
|  | ||||
|       // When does W channel start counting a new transfer | ||||
|       val wbeats_latched = RegInit(Bool(false)) | ||||
|       val wbeats_ready = Wire(Bool()) | ||||
|       val wbeats_valid = Wire(Bool()) | ||||
|       when (wbeats_valid && wbeats_ready) { wbeats_latched := Bool(true) } | ||||
|       when (out.aw.fire()) { wbeats_latched := Bool(false) } | ||||
|  | ||||
|       // AW flow control | ||||
|       out.aw.valid := in_aw.valid && (wbeats_ready || wbeats_latched) | ||||
|       in_aw.ready := out.aw.ready && (wbeats_ready || wbeats_latched) | ||||
|       wbeats_valid := in_aw.valid && !wbeats_latched | ||||
|       out.aw.bits := in_aw.bits | ||||
|       out.aw.bits.user.get := Cat(in_aw.bits.user.toList ++ Seq(aw_last)) | ||||
|  | ||||
|       // We need to inject 'last' into the W channel fragments, count! | ||||
|       val w_counter = RegInit(UInt(0, width = AXI4Parameters.lenBits+1)) | ||||
|       val w_idle = w_counter === UInt(0) | ||||
|       val w_todo = Mux(w_idle, Mux(wbeats_valid, w_beats, UInt(0)), w_counter) | ||||
|       val w_last = w_todo === UInt(1) | ||||
|       w_counter := w_todo - out.w.fire() | ||||
|       assert (!out.w.fire() || w_todo =/= UInt(0)) // underflow impossible | ||||
|  | ||||
|       // W flow control | ||||
|       wbeats_ready := w_idle | ||||
|       out.w.valid := in_w.valid && (!wbeats_ready || wbeats_valid) | ||||
|       in_w.ready := out.w.ready && (!wbeats_ready || wbeats_valid) | ||||
|       out.w.bits := in_w.bits | ||||
|       out.w.bits.last := w_last | ||||
|       // We should also recreate the last last | ||||
|       assert (!out.w.valid || !in_w.bits.last || w_last) | ||||
|  | ||||
|       // R flow control | ||||
|       val r_last = out.r.bits.user.get(0) | ||||
|       in.r <> out.r | ||||
|       in.r.bits.last := out.r.bits.last && r_last | ||||
|       in.r.bits.user.foreach { _ := out.r.bits.user.get >> 1 } | ||||
|  | ||||
|       // B flow control | ||||
|       val b_last = out.b.bits.user.get(0) | ||||
|       in.b <> out.b | ||||
|       in.b.valid := out.b.valid && b_last | ||||
|       out.b.ready := in.b.ready || !b_last | ||||
|       in.b.bits.user.foreach { _ := out.b.bits.user.get >> 1 } | ||||
|  | ||||
|       // Merge errors from dropped B responses | ||||
|       val error = RegInit(Vec.fill(edgeIn.master.endId) { UInt(0, width = AXI4Parameters.respBits)}) | ||||
|       in.b.bits.resp := out.b.bits.resp | error(out.b.bits.id) | ||||
|       (error zip UIntToOH(out.b.bits.id, edgeIn.master.endId).toBools) foreach { case (reg, sel) => | ||||
|         when (sel && out.b.fire()) { reg := Mux(b_last, UInt(0), reg | out.b.bits.resp) } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4Fragmenter | ||||
| { | ||||
|   // applied to the AXI4 source node; y.node := AXI4Fragmenter()(x.node) | ||||
|   def apply()(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { | ||||
|     val fragmenter = LazyModule(new AXI4Fragmenter) | ||||
|     fragmenter.node := x | ||||
|     fragmenter.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										90
									
								
								src/main/scala/amba/axi4/IdIndexer.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/main/scala/amba/axi4/IdIndexer.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import scala.math.{min,max} | ||||
|  | ||||
| class AXI4IdIndexer(idBits: Int)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   require (idBits >= 0) | ||||
|  | ||||
|   val node = AXI4AdapterNode( | ||||
|     masterFn = { mp => | ||||
|       // Create one new "master" per ID | ||||
|       val masters = Array.tabulate(1 << idBits) { i => AXI4MasterParameters( | ||||
|          name      = "", | ||||
|          id        = IdRange(i, i+1), | ||||
|          aligned   = true, | ||||
|          maxFlight = Some(0)) | ||||
|       } | ||||
|       // Accumluate the names of masters we squish | ||||
|       val names = Array.fill(1 << idBits) { new scala.collection.mutable.HashSet[String]() } | ||||
|       // Squash the information from original masters into new ID masters | ||||
|       mp.masters.foreach { m => | ||||
|         for (i <- m.id.start until m.id.end) { | ||||
|           val j = i % (1 << idBits) | ||||
|           val old = masters(j) | ||||
|           names(j) += m.name | ||||
|           masters(j) = old.copy( | ||||
|             aligned   = old.aligned && m.aligned, | ||||
|             maxFlight = old.maxFlight.flatMap { o => m.maxFlight.map { n => o+n } }) | ||||
|         } | ||||
|       } | ||||
|       mp.copy( | ||||
|         userBits = mp.userBits + max(0, log2Ceil(mp.endId) - idBits), | ||||
|         masters  = masters.zipWithIndex.map { case (m,i) => m.copy(name = names(i).toList.mkString(", "))}) | ||||
|     }, | ||||
|     slaveFn = { sp => sp | ||||
|     }) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|  | ||||
|       // Leave everything mostly untouched | ||||
|       out.ar <> in.ar | ||||
|       out.aw <> in.aw | ||||
|       out.w <> in.w | ||||
|       in.b <> out.b | ||||
|       in.r <> out.r | ||||
|  | ||||
|       val bits = log2Ceil(edgeIn.master.endId) - idBits | ||||
|       if (bits > 0) { | ||||
|         // (in.aX.bits.id >> idBits).width = bits > 0 | ||||
|         out.ar.bits.user.get := Cat(in.ar.bits.user.toList ++ Seq(in.ar.bits.id >> idBits)) | ||||
|         out.aw.bits.user.get := Cat(in.aw.bits.user.toList ++ Seq(in.aw.bits.id >> idBits)) | ||||
|         // user.isDefined => width > 0 | ||||
|         in.r.bits.user.foreach { _ := out.r.bits.user.get >> bits } | ||||
|         in.b.bits.user.foreach { _ := out.b.bits.user.get >> bits } | ||||
|         // Special care is needed in case of 0 idBits, b/c .id has width 1 still | ||||
|         if (idBits == 0) { | ||||
|           out.ar.bits.id := UInt(0) | ||||
|           out.aw.bits.id := UInt(0) | ||||
|           in.r.bits.id := out.r.bits.user.get | ||||
|           in.b.bits.id := out.b.bits.user.get | ||||
|         } else { | ||||
|           in.r.bits.id := Cat(out.r.bits.user.get, out.r.bits.id) | ||||
|           in.b.bits.id := Cat(out.b.bits.user.get, out.b.bits.id) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4IdIndexer | ||||
| { | ||||
|   // applied to the AXI4 source node; y.node := AXI4IdIndexer(idBits)(x.node) | ||||
|   def apply(idBits: Int)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { | ||||
|     val indexer = LazyModule(new AXI4IdIndexer(idBits)) | ||||
|     indexer.node := x | ||||
|     indexer.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/main/scala/amba/axi4/Nodes.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/main/scala/amba/axi4/Nodes.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
|  | ||||
| object AXI4Imp extends NodeImp[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4EdgeParameters, AXI4EdgeParameters, AXI4Bundle] | ||||
| { | ||||
|   def edgeO(pd: AXI4MasterPortParameters, pu: AXI4SlavePortParameters): AXI4EdgeParameters = AXI4EdgeParameters(pd, pu) | ||||
|   def edgeI(pd: AXI4MasterPortParameters, pu: AXI4SlavePortParameters): AXI4EdgeParameters = AXI4EdgeParameters(pd, pu) | ||||
|  | ||||
|   def bundleO(eo: AXI4EdgeParameters): AXI4Bundle = AXI4Bundle(eo.bundle) | ||||
|   def bundleI(ei: AXI4EdgeParameters): AXI4Bundle = AXI4Bundle(ei.bundle) | ||||
|  | ||||
|   def colour = "#00ccff" // bluish | ||||
|   override def labelI(ei: AXI4EdgeParameters) = (ei.slave.beatBytes * 8).toString | ||||
|   override def labelO(eo: AXI4EdgeParameters) = (eo.slave.beatBytes * 8).toString | ||||
|  | ||||
|   override def mixO(pd: AXI4MasterPortParameters, node: OutwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4MasterPortParameters  = | ||||
|    pd.copy(masters = pd.masters.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||
|   override def mixI(pu: AXI4SlavePortParameters, node: InwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4SlavePortParameters = | ||||
|    pu.copy(slaves  = pu.slaves.map { m => m.copy (nodePath = node +: m.nodePath) }) | ||||
| } | ||||
|  | ||||
| // Nodes implemented inside modules | ||||
| case class AXI4IdentityNode() extends IdentityNode(AXI4Imp) | ||||
| case class AXI4MasterNode(portParams: Seq[AXI4MasterPortParameters]) extends SourceNode(AXI4Imp)(portParams) | ||||
| case class AXI4SlaveNode(portParams: Seq[AXI4SlavePortParameters]) extends SinkNode(AXI4Imp)(portParams) | ||||
| case class AXI4AdapterNode( | ||||
|   masterFn:  AXI4MasterPortParameters => AXI4MasterPortParameters, | ||||
|   slaveFn:   AXI4SlavePortParameters  => AXI4SlavePortParameters, | ||||
|   numPorts:  Range.Inclusive = 0 to 999) | ||||
|   extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts) | ||||
|  | ||||
| // Nodes passed from an inner module | ||||
| case class AXI4OutputNode() extends OutputNode(AXI4Imp) | ||||
| case class AXI4InputNode() extends InputNode(AXI4Imp) | ||||
|  | ||||
| // Nodes used for external ports | ||||
| case class AXI4BlindOutputNode(portParams: Seq[AXI4SlavePortParameters]) extends BlindOutputNode(AXI4Imp)(portParams) | ||||
| case class AXI4BlindInputNode(portParams: Seq[AXI4MasterPortParameters]) extends BlindInputNode(AXI4Imp)(portParams) | ||||
|  | ||||
| case class AXI4InternalOutputNode(portParams: Seq[AXI4SlavePortParameters]) extends InternalOutputNode(AXI4Imp)(portParams) | ||||
| case class AXI4InternalInputNode(portParams: Seq[AXI4MasterPortParameters]) extends InternalInputNode(AXI4Imp)(portParams) | ||||
							
								
								
									
										130
									
								
								src/main/scala/amba/axi4/Parameters.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/main/scala/amba/axi4/Parameters.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import scala.math.max | ||||
|  | ||||
| case class AXI4SlaveParameters( | ||||
|   address:       Seq[AddressSet], | ||||
|   resources:     Seq[Resource] = Nil, | ||||
|   regionType:    RegionType.T  = RegionType.GET_EFFECTS, | ||||
|   executable:    Boolean       = false, // processor can execute from this memory | ||||
|   nodePath:      Seq[BaseNode] = Seq(), | ||||
|   supportsWrite: TransferSizes = TransferSizes.none, | ||||
|   supportsRead:  TransferSizes = TransferSizes.none, | ||||
|   interleavedId: Option[Int]   = None) // The device will not interleave responses (R+B) | ||||
| { | ||||
|   address.foreach { a => require (a.finite) } | ||||
|   address.combinations(2).foreach { case Seq(x,y) => require (!x.overlaps(y), s"$x and $y overlap") } | ||||
|  | ||||
|   val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") | ||||
|   val maxTransfer = max(supportsWrite.max, supportsRead.max) | ||||
|   val maxAddress = address.map(_.max).max | ||||
|   val minAlignment = address.map(_.alignment).min | ||||
|  | ||||
|   // The device had better not support a transfer larger than its alignment | ||||
|   require (minAlignment >= maxTransfer, | ||||
|     s"minAlignment ($minAlignment) must be >= maxTransfer ($maxTransfer)") | ||||
| } | ||||
|  | ||||
| case class AXI4SlavePortParameters( | ||||
|   slaves:     Seq[AXI4SlaveParameters], | ||||
|   beatBytes:  Int, | ||||
|   minLatency: Int = 1) | ||||
| { | ||||
|   require (!slaves.isEmpty) | ||||
|   require (isPow2(beatBytes)) | ||||
|  | ||||
|   val maxTransfer = slaves.map(_.maxTransfer).max | ||||
|   val maxAddress = slaves.map(_.maxAddress).max | ||||
|  | ||||
|   // Check the link is not pointlessly wide | ||||
|   require (maxTransfer >= beatBytes, | ||||
|     s"maxTransfer ($maxTransfer) should not be smaller than bus width ($beatBytes)") | ||||
|   // Check that the link can be implemented in AXI4 | ||||
|   val limit = beatBytes * (1 << AXI4Parameters.lenBits) | ||||
|   require (maxTransfer <= limit, | ||||
|     s"maxTransfer ($maxTransfer) cannot be larger than $limit on a $beatBytes*8 width bus") | ||||
|  | ||||
|   // Require disjoint ranges for addresses | ||||
|   slaves.combinations(2).foreach { case Seq(x,y) => | ||||
|     x.address.foreach { a => y.address.foreach { b => | ||||
|       require (!a.overlaps(b), s"$a and $b overlap") | ||||
|     } } | ||||
|   } | ||||
| } | ||||
|  | ||||
| case class AXI4MasterParameters( | ||||
|   name:      String, | ||||
|   id:        IdRange       = IdRange(0, 1), | ||||
|   aligned:   Boolean       = false, | ||||
|   maxFlight: Option[Int]   = None, // None = infinite, else is a per-ID cap | ||||
|   nodePath:  Seq[BaseNode] = Seq()) | ||||
| { | ||||
|   maxFlight.foreach { m => require (m >= 0) } | ||||
| } | ||||
|  | ||||
| case class AXI4MasterPortParameters( | ||||
|   masters:   Seq[AXI4MasterParameters], | ||||
|   userBits:  Int = 0) | ||||
| { | ||||
|   val endId = masters.map(_.id.end).max | ||||
|   require (userBits >= 0) | ||||
|  | ||||
|   // Require disjoint ranges for ids | ||||
|   IdRange.overlaps(masters.map(_.id)).foreach { case (x, y) => | ||||
|     require (!x.overlaps(y), s"AXI4MasterParameters.id $x and $y overlap") | ||||
|   } | ||||
| } | ||||
|  | ||||
| case class AXI4BundleParameters( | ||||
|   addrBits: Int, | ||||
|   dataBits: Int, | ||||
|   idBits:   Int, | ||||
|   userBits: Int) | ||||
| { | ||||
|   require (dataBits >= 8, s"AXI4 data bits must be >= 8 (got $dataBits)") | ||||
|   require (addrBits >= 1, s"AXI4 addr bits must be >= 1 (got $addrBits)") | ||||
|   require (idBits >= 1, s"AXI4 id bits must be >= 1 (got $idBits)") | ||||
|   require (isPow2(dataBits), s"AXI4 data bits must be pow2 (got $dataBits)") | ||||
|  | ||||
|   // Bring the globals into scope | ||||
|   val lenBits   = AXI4Parameters.lenBits | ||||
|   val sizeBits  = AXI4Parameters.sizeBits | ||||
|   val burstBits = AXI4Parameters.burstBits | ||||
|   val lockBits  = AXI4Parameters.lockBits | ||||
|   val cacheBits = AXI4Parameters.cacheBits | ||||
|   val protBits  = AXI4Parameters.protBits | ||||
|   val qosBits   = AXI4Parameters.qosBits | ||||
|   val respBits  = AXI4Parameters.respBits | ||||
|  | ||||
|   def union(x: AXI4BundleParameters) = | ||||
|     AXI4BundleParameters( | ||||
|       max(addrBits, x.addrBits), | ||||
|       max(dataBits, x.dataBits), | ||||
|       max(idBits,   x.idBits), | ||||
|       max(userBits, x.userBits)) | ||||
| } | ||||
|  | ||||
| object AXI4BundleParameters | ||||
| { | ||||
|   val emptyBundleParams = AXI4BundleParameters(addrBits=1, dataBits=8, idBits=1, userBits=0) | ||||
|   def union(x: Seq[AXI4BundleParameters]) = x.foldLeft(emptyBundleParams)((x,y) => x.union(y)) | ||||
|  | ||||
|   def apply(master: AXI4MasterPortParameters, slave: AXI4SlavePortParameters) = | ||||
|     new AXI4BundleParameters( | ||||
|       addrBits = log2Up(slave.maxAddress+1), | ||||
|       dataBits = slave.beatBytes * 8, | ||||
|       idBits   = log2Up(master.endId), | ||||
|       userBits = master.userBits) | ||||
| } | ||||
|  | ||||
| case class AXI4EdgeParameters( | ||||
|   master: AXI4MasterPortParameters, | ||||
|   slave:  AXI4SlavePortParameters) | ||||
| { | ||||
|   val bundle = AXI4BundleParameters(master, slave) | ||||
| } | ||||
							
								
								
									
										37
									
								
								src/main/scala/amba/axi4/Protocol.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/main/scala/amba/axi4/Protocol.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.util.{Irrevocable, IrrevocableIO} | ||||
|  | ||||
| object AXI4Parameters | ||||
| { | ||||
|   // These are all fixed by the AXI4 standard: | ||||
|   val lenBits   = 8 | ||||
|   val sizeBits  = 3 | ||||
|   val burstBits = 2 | ||||
|   val lockBits  = 1 | ||||
|   val cacheBits = 4 | ||||
|   val protBits  = 3 | ||||
|   val qosBits   = 4 | ||||
|   val respBits  = 2 | ||||
|  | ||||
|   def CACHE_RALLOCATE  = UInt(8, width = cacheBits) | ||||
|   def CACHE_WALLOCATE  = UInt(4, width = cacheBits) | ||||
|   def CACHE_MODIFIABLE = UInt(2, width = cacheBits) | ||||
|   def CACHE_BUFFERABLE = UInt(1, width = cacheBits) | ||||
|  | ||||
|   def PROT_PRIVILEDGED = UInt(1, width = protBits) | ||||
|   def PROT_INSECURE    = UInt(2, width = protBits) | ||||
|   def PROT_INSTRUCTION = UInt(4, width = protBits) | ||||
|  | ||||
|   def BURST_FIXED = UInt(0, width = burstBits) | ||||
|   def BURST_INCR  = UInt(1, width = burstBits) | ||||
|   def BURST_WRAP  = UInt(2, width = burstBits) | ||||
|  | ||||
|   def RESP_OKAY   = UInt(0, width = respBits) | ||||
|   def RESP_EXOKAY = UInt(1, width = respBits) | ||||
|   def RESP_SLVERR = UInt(2, width = respBits) | ||||
|   def RESP_DECERR = UInt(3, width = respBits) | ||||
| } | ||||
							
								
								
									
										124
									
								
								src/main/scala/amba/axi4/RegisterRouter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/main/scala/amba/axi4/RegisterRouter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.regmapper._ | ||||
| import freechips.rocketchip.tilelink.{IntSourceNode, IntSourcePortSimple} | ||||
| import freechips.rocketchip.util.{HeterogeneousBag, MaskGen} | ||||
| import scala.math.{min,max} | ||||
|  | ||||
| class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||
|   extends AXI4SlaveNode(Seq(AXI4SlavePortParameters( | ||||
|     Seq(AXI4SlaveParameters( | ||||
|       address       = Seq(address), | ||||
|       executable    = executable, | ||||
|       supportsWrite = TransferSizes(1, beatBytes), | ||||
|       supportsRead  = TransferSizes(1, beatBytes), | ||||
|       interleavedId = Some(0))), | ||||
|     beatBytes  = beatBytes, | ||||
|     minLatency = 1))) | ||||
| { | ||||
|   require (address.contiguous) | ||||
|  | ||||
|   // Calling this method causes the matching AXI4 bundle to be | ||||
|   // configured to route all requests to the listed RegFields. | ||||
|   def regmap(mapping: RegField.Map*) = { | ||||
|     val ar = bundleIn(0).ar | ||||
|     val aw = bundleIn(0).aw | ||||
|     val w  = bundleIn(0).w | ||||
|     val r  = bundleIn(0).r | ||||
|     val b  = bundleIn(0).b | ||||
|  | ||||
|     val params = RegMapperParams(log2Up((address.mask+1)/beatBytes), beatBytes, ar.bits.params.idBits + ar.bits.params.userBits) | ||||
|     val in = Wire(Decoupled(new RegMapperInput(params))) | ||||
|  | ||||
|     // Prefer to execute reads first | ||||
|     in.valid := ar.valid || (aw.valid && w.valid) | ||||
|     ar.ready := in.ready | ||||
|     aw.ready := in.ready && !ar.valid && w .valid | ||||
|     w .ready := in.ready && !ar.valid && aw.valid | ||||
|  | ||||
|     val ar_extra = Cat(Seq(ar.bits.id) ++ ar.bits.user.toList) | ||||
|     val aw_extra = Cat(Seq(aw.bits.id) ++ aw.bits.user.toList) | ||||
|     val in_extra = Mux(ar.valid, ar_extra, aw_extra) | ||||
|     val addr = Mux(ar.valid, ar.bits.addr, aw.bits.addr) | ||||
|     val mask = MaskGen(ar.bits.addr, ar.bits.size, beatBytes) | ||||
|  | ||||
|     in.bits.read  := ar.valid | ||||
|     in.bits.index := addr >> log2Ceil(beatBytes) | ||||
|     in.bits.data  := w.bits.data | ||||
|     in.bits.mask  := Mux(ar.valid, mask, w.bits.strb) | ||||
|     in.bits.extra := in_extra | ||||
|  | ||||
|     // Invoke the register map builder and make it Irrevocable | ||||
|     val out = Queue.irrevocable( | ||||
|       RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*), | ||||
|       entries = 2) | ||||
|  | ||||
|     // No flow control needed | ||||
|     out.ready := Mux(out.bits.read, r.ready, b.ready) | ||||
|     r.valid := out.valid &&  out.bits.read | ||||
|     b.valid := out.valid && !out.bits.read | ||||
|  | ||||
|     val out_id = if (r.bits.params.idBits == 0) UInt(0) else (out.bits.extra >> ar.bits.params.userBits) | ||||
|  | ||||
|     r.bits.id   := out_id | ||||
|     r.bits.data := out.bits.data | ||||
|     r.bits.last := Bool(true) | ||||
|     r.bits.resp := AXI4Parameters.RESP_OKAY | ||||
|     r.bits.user.foreach { _ := out.bits.extra } | ||||
|  | ||||
|     b.bits.id   := out_id | ||||
|     b.bits.resp := AXI4Parameters.RESP_OKAY | ||||
|     b.bits.user.foreach { _ := out.bits.extra } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4RegisterNode | ||||
| { | ||||
|   def apply(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) = | ||||
|     new AXI4RegisterNode(address, concurrency, beatBytes, undefZero, executable) | ||||
| } | ||||
|  | ||||
| // These convenience methods below combine to make it possible to create a AXI4 | ||||
| // register mapped device from a totally abstract register mapped device. | ||||
|  | ||||
| abstract class AXI4RegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val node = AXI4RegisterNode(address, concurrency, beatBytes, undefZero, executable) | ||||
|   val intnode = IntSourceNode(IntSourcePortSimple(num = interrupts)) | ||||
| } | ||||
|  | ||||
| case class AXI4RegBundleArg(interrupts: HeterogeneousBag[Vec[Bool]], in: HeterogeneousBag[AXI4Bundle])(implicit val p: Parameters) | ||||
|  | ||||
| class AXI4RegBundleBase(arg: AXI4RegBundleArg) extends Bundle | ||||
| { | ||||
|   implicit val p = arg.p | ||||
|   val interrupts = arg.interrupts | ||||
|   val in = arg.in | ||||
| } | ||||
|  | ||||
| class AXI4RegBundle[P](val params: P, arg: AXI4RegBundleArg) extends AXI4RegBundleBase(arg) | ||||
|  | ||||
| class AXI4RegModule[P, B <: AXI4RegBundleBase](val params: P, bundleBuilder: => B, router: AXI4RegisterRouterBase) | ||||
|   extends LazyModuleImp(router) with HasRegMap | ||||
| { | ||||
|   val io = bundleBuilder | ||||
|   val interrupts = if (io.interrupts.isEmpty) Vec(0, Bool()) else io.interrupts(0) | ||||
|   def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*) | ||||
| } | ||||
|  | ||||
| class AXI4RegisterRouter[B <: AXI4RegBundleBase, M <: LazyModuleImp] | ||||
|    (val base: BigInt, val interrupts: Int = 0, val size: BigInt = 4096, val concurrency: Int = 0, val beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) | ||||
|    (bundleBuilder: AXI4RegBundleArg => B) | ||||
|    (moduleBuilder: (=> B, AXI4RegisterRouterBase) => M)(implicit p: Parameters) | ||||
|   extends AXI4RegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero, executable) | ||||
| { | ||||
|   require (isPow2(size)) | ||||
|   // require (size >= 4096) ... not absolutely required, but highly recommended | ||||
|  | ||||
|   lazy val module = moduleBuilder(bundleBuilder(AXI4RegBundleArg(intnode.bundleOut, node.bundleIn)), this) | ||||
| } | ||||
							
								
								
									
										90
									
								
								src/main/scala/amba/axi4/SRAM.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/main/scala/amba/axi4/SRAM.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.util._ | ||||
|  | ||||
| class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val node = AXI4SlaveNode(Seq(AXI4SlavePortParameters( | ||||
|     Seq(AXI4SlaveParameters( | ||||
|       address       = List(address), | ||||
|       regionType    = RegionType.UNCACHED, | ||||
|       executable    = executable, | ||||
|       supportsRead  = TransferSizes(1, beatBytes), | ||||
|       supportsWrite = TransferSizes(1, beatBytes), | ||||
|       interleavedId = Some(0))), | ||||
|     beatBytes  = beatBytes, | ||||
|     minLatency = 1))) | ||||
|  | ||||
|   // 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 mem = SeqMem(1 << mask.filter(b=>b).size, Vec(beatBytes, Bits(width = 8))) | ||||
|  | ||||
|     val r_addr = Cat((mask zip (in.ar.bits.addr >> log2Ceil(beatBytes)).toBools).filter(_._1).map(_._2).reverse) | ||||
|     val w_addr = Cat((mask zip (in.aw.bits.addr >> log2Ceil(beatBytes)).toBools).filter(_._1).map(_._2).reverse) | ||||
|  | ||||
|     val w_full = RegInit(Bool(false)) | ||||
|     val w_id   = Reg(UInt()) | ||||
|     val w_user = Reg(UInt(width = 1 max in.params.userBits)) | ||||
|  | ||||
|     when (in. b.fire()) { w_full := Bool(false) } | ||||
|     when (in.aw.fire()) { w_full := Bool(true) } | ||||
|  | ||||
|     when (in.aw.fire()) { | ||||
|       w_id := in.aw.bits.id | ||||
|       in.aw.bits.user.foreach { w_user := _ } | ||||
|     } | ||||
|  | ||||
|     val wdata = Vec.tabulate(beatBytes) { i => in.w.bits.data(8*(i+1)-1, 8*i) } | ||||
|     when (in.aw.fire()) { | ||||
|       mem.write(w_addr, wdata, in.w.bits.strb.toBools) | ||||
|     } | ||||
|  | ||||
|     in. b.valid := w_full | ||||
|     in.aw.ready := in. w.valid && (in.b.ready || !w_full) | ||||
|     in. w.ready := in.aw.valid && (in.b.ready || !w_full) | ||||
|  | ||||
|     in.b.bits.id   := w_id | ||||
|     in.b.bits.resp := AXI4Parameters.RESP_OKAY | ||||
|     in.b.bits.user.foreach { _ := w_user } | ||||
|  | ||||
|     val r_full = RegInit(Bool(false)) | ||||
|     val r_id   = Reg(UInt()) | ||||
|     val r_user = Reg(UInt(width = 1 max in.params.userBits)) | ||||
|  | ||||
|     when (in. r.fire()) { r_full := Bool(false) } | ||||
|     when (in.ar.fire()) { r_full := Bool(true) } | ||||
|  | ||||
|     when (in.ar.fire()) { | ||||
|       r_id := in.ar.bits.id | ||||
|       in.ar.bits.user.foreach { r_user := _ } | ||||
|     } | ||||
|  | ||||
|     val ren = in.ar.fire() | ||||
|     val rdata = mem.readAndHold(r_addr, ren) | ||||
|  | ||||
|     in. r.valid := r_full | ||||
|     in.ar.ready := in.r.ready || !r_full | ||||
|  | ||||
|     in.r.bits.id   := r_id | ||||
|     in.r.bits.resp := AXI4Parameters.RESP_OKAY | ||||
|     in.r.bits.data := Cat(rdata.reverse) | ||||
|     in.r.bits.user.foreach { _ := r_user } | ||||
|     in.r.bits.last := Bool(true) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										139
									
								
								src/main/scala/amba/axi4/Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/main/scala/amba/axi4/Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.devices.tilelink.TLError | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.tilelink._ | ||||
| import freechips.rocketchip.unittest._ | ||||
|  | ||||
| class RRTest0(address: BigInt)(implicit p: Parameters) extends AXI4RegisterRouter(address, 0, 32, 0, 4)( | ||||
|   new AXI4RegBundle((), _)    with RRTest0Bundle)( | ||||
|   new AXI4RegModule((), _, _) with RRTest0Module) | ||||
|  | ||||
| class RRTest1(address: BigInt)(implicit p: Parameters) extends AXI4RegisterRouter(address, 0, 32, 6, 4, false)( | ||||
|   new AXI4RegBundle((), _)    with RRTest1Bundle)( | ||||
|   new AXI4RegModule((), _, _) with RRTest1Module) | ||||
|  | ||||
| class AXI4LiteFuzzRAM(txns: Int)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val fuzz  = LazyModule(new TLFuzzer(txns)) | ||||
|   val model = LazyModule(new TLRAMModel("AXI4LiteFuzzRAM")) | ||||
|   val xbar  = LazyModule(new TLXbar) | ||||
|   val gpio  = LazyModule(new RRTest1(0x400)) | ||||
|   val ram   = LazyModule(new AXI4RAM(AddressSet(0x0, 0x3ff))) | ||||
|  | ||||
|   model.node := fuzz.node | ||||
|   xbar.node  := TLDelayer(0.1)(TLBuffer(BufferParams.flow)(TLDelayer(0.2)(model.node))) | ||||
|   ram.node   := AXI4UserYanker()(AXI4IdIndexer(0)(TLToAXI4(4, true )(TLFragmenter(4, 16)(xbar.node)))) | ||||
|   gpio.node  := AXI4UserYanker()(AXI4IdIndexer(0)(TLToAXI4(4, false)(TLFragmenter(4, 16)(xbar.node)))) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) with HasUnitTestIO { | ||||
|     io.finished := fuzz.module.io.finished | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4LiteFuzzRAMTest(txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) { | ||||
|   val dut = Module(LazyModule(new AXI4LiteFuzzRAM(txns)).module) | ||||
|   io.finished := dut.io.finished | ||||
| } | ||||
|  | ||||
| class AXI4FullFuzzRAM(txns: Int)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val fuzz  = LazyModule(new TLFuzzer(txns)) | ||||
|   val model = LazyModule(new TLRAMModel("AXI4FullFuzzRAM")) | ||||
|   val xbar  = LazyModule(new TLXbar) | ||||
|   val gpio  = LazyModule(new RRTest0(0x400)) | ||||
|   val ram   = LazyModule(new AXI4RAM(AddressSet(0x0, 0x3ff))) | ||||
|  | ||||
|   model.node := fuzz.node | ||||
|   xbar.node  := TLDelayer(0.1)(TLBuffer(BufferParams.flow)(TLDelayer(0.2)(model.node))) | ||||
|   ram.node   := AXI4Fragmenter()(AXI4Deinterleaver(16)(TLToAXI4(4,false)(xbar.node))) | ||||
|   gpio.node  := AXI4Fragmenter()(AXI4Deinterleaver(16)(TLToAXI4(4,true )(xbar.node))) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) with HasUnitTestIO { | ||||
|     io.finished := fuzz.module.io.finished | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4FullFuzzRAMTest(txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) { | ||||
|   val dut = Module(LazyModule(new AXI4FullFuzzRAM(txns)).module) | ||||
|   io.finished := dut.io.finished | ||||
| } | ||||
|  | ||||
| trait HasFuzzTarget { | ||||
|   val fuzzAddr = AddressSet(0x0, 0xfff) | ||||
| } | ||||
|  | ||||
| class AXI4FuzzMaster(txns: Int)(implicit p: Parameters) extends LazyModule with HasFuzzTarget | ||||
| { | ||||
|   val node  = AXI4OutputNode() | ||||
|   val fuzz  = LazyModule(new TLFuzzer(txns, overrideAddress = Some(fuzzAddr))) | ||||
|   val model = LazyModule(new TLRAMModel("AXI4FuzzMaster")) | ||||
|  | ||||
|   model.node := fuzz.node | ||||
|   node := | ||||
|     AXI4UserYanker()( | ||||
|     AXI4Deinterleaver(64)( | ||||
|     TLToAXI4(4)( | ||||
|     TLDelayer(0.1)( | ||||
|     TLBuffer(BufferParams.flow)( | ||||
|     TLDelayer(0.1)( | ||||
|     model.node)))))) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val out = node.bundleOut | ||||
|       val finished = Bool(OUTPUT) | ||||
|     } | ||||
|  | ||||
|     io.finished := fuzz.module.io.finished | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4FuzzSlave()(implicit p: Parameters) extends LazyModule with HasFuzzTarget | ||||
| { | ||||
|   val node = AXI4InputNode() | ||||
|   val xbar = LazyModule(new TLXbar) | ||||
|   val ram  = LazyModule(new TLRAM(fuzzAddr)) | ||||
|   val error= LazyModule(new TLError(Seq(AddressSet(0x1800, 0xff)))) | ||||
|  | ||||
|   ram.node   := TLFragmenter(4, 16)(xbar.node) | ||||
|   error.node := TLFragmenter(4, 16)(xbar.node) | ||||
|  | ||||
|   xbar.node := | ||||
|     TLFIFOFixer()( | ||||
|     TLDelayer(0.1)( | ||||
|     TLBuffer(BufferParams.flow)( | ||||
|     TLDelayer(0.1)( | ||||
|     AXI4ToTL()( | ||||
|     AXI4UserYanker(Some(4))( | ||||
|     AXI4Fragmenter()( | ||||
|     AXI4IdIndexer(2)( | ||||
|     node)))))))) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in = node.bundleIn | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4FuzzBridge(txns: Int)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val master = LazyModule(new AXI4FuzzMaster(txns)) | ||||
|   val slave  = LazyModule(new AXI4FuzzSlave) | ||||
|  | ||||
|   slave.node := master.node | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) with HasUnitTestIO { | ||||
|     io.finished := master.module.io.finished | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4BridgeTest(txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) { | ||||
|   val dut = Module(LazyModule(new AXI4FuzzBridge(txns)).module) | ||||
|   io.finished := dut.io.finished | ||||
| } | ||||
							
								
								
									
										174
									
								
								src/main/scala/amba/axi4/ToTL.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/main/scala/amba/axi4/ToTL.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.tilelink._ | ||||
| import freechips.rocketchip.util._ | ||||
|  | ||||
| case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)( | ||||
|   dFn = { case AXI4MasterPortParameters(masters, userBits) => | ||||
|     masters.foreach { m => require (m.maxFlight.isDefined, "AXI4 must include a transaction maximum per ID to convert to TL") } | ||||
|     val maxFlight = masters.map(_.maxFlight.get).max | ||||
|     TLClientPortParameters( | ||||
|       clients = masters.filter(_.maxFlight != Some(0)).flatMap { m => | ||||
|         for (id <- m.id.start until m.id.end) | ||||
|           yield TLClientParameters( | ||||
|             name        = s"${m.name} ID#${id}", | ||||
|             sourceId    = IdRange(id * maxFlight*2, (id+1) * maxFlight*2), // R+W ids are distinct | ||||
|             nodePath    = m.nodePath, | ||||
|             requestFifo = true) | ||||
|       }) | ||||
|   }, | ||||
|   uFn = { mp => AXI4SlavePortParameters( | ||||
|     slaves = mp.managers.map { m => | ||||
|       val maxXfer = TransferSizes(1, mp.beatBytes * (1 << AXI4Parameters.lenBits)) | ||||
|       AXI4SlaveParameters( | ||||
|         address       = m.address, | ||||
|         resources     = m.resources, | ||||
|         regionType    = m.regionType, | ||||
|         executable    = m.executable, | ||||
|         nodePath      = m.nodePath, | ||||
|         supportsWrite = m.supportsPutPartial.intersect(maxXfer), | ||||
|         supportsRead  = m.supportsGet.intersect(maxXfer), | ||||
|         interleavedId = Some(0))}, // TL2 never interleaves D beats | ||||
|     beatBytes = mp.beatBytes, | ||||
|     minLatency = mp.minLatency) | ||||
|   }) | ||||
|  | ||||
| class AXI4ToTL()(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val node = AXI4ToTLNode() | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|       val numIds = edgeIn.master.endId | ||||
|       val beatBytes = edgeOut.manager.beatBytes | ||||
|       val beatCountBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1 | ||||
|       val maxFlight = edgeIn.master.masters.map(_.maxFlight.get).max | ||||
|       val logFlight = log2Ceil(maxFlight) | ||||
|       val txnCountBits = log2Ceil(maxFlight+1) // wrap-around must not block b_allow | ||||
|       val addedBits = logFlight + 1 // +1 for read vs. write source ID | ||||
|  | ||||
|       require (edgeIn.master.userBits == 0, "AXI4 user bits cannot be transported by TL") | ||||
|       require (edgeIn.master.masters(0).aligned) | ||||
|       edgeOut.manager.requireFifo() | ||||
|  | ||||
|       // Look for an Error device to redirect bad requests | ||||
|       val errorDevs = edgeOut.manager.managers.filter(_.nodePath.last.lazyModule.className == "TLError") | ||||
|       require (!errorDevs.isEmpty, "There is no TLError reachable from AXI4ToTL. One must be instantiated.") | ||||
|       val error = errorDevs.head.address.head.base | ||||
|       require (errorDevs.head.supportsPutPartial.contains(edgeOut.manager.maxTransfer), | ||||
|         s"Error device supports ${errorDevs.head.supportsPutPartial} PutPartial but must support ${edgeOut.manager.maxTransfer}") | ||||
|       require (errorDevs.head.supportsGet.contains(edgeOut.manager.maxTransfer), | ||||
|         s"Error device supports ${errorDevs.head.supportsGet} Get but must support ${edgeOut.manager.maxTransfer}") | ||||
|  | ||||
|       val r_out = Wire(out.a) | ||||
|       val r_size1 = in.ar.bits.bytes1() | ||||
|       val r_size = OH1ToUInt(r_size1) | ||||
|       val r_ok = edgeOut.manager.supportsGetSafe(in.ar.bits.addr, r_size) | ||||
|       val r_addr = Mux(r_ok, in.ar.bits.addr, UInt(error) | in.ar.bits.addr(log2Up(beatBytes)-1, 0)) | ||||
|       val r_count = RegInit(Vec.fill(numIds) { UInt(0, width = txnCountBits) }) | ||||
|       val r_id = Cat(in.ar.bits.id, r_count(in.ar.bits.id)(logFlight-1,0), UInt(0, width=1)) | ||||
|  | ||||
|       assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, beatCountBits)) // because aligned | ||||
|       in.ar.ready := r_out.ready | ||||
|       r_out.valid := in.ar.valid | ||||
|       r_out.bits := edgeOut.Get(r_id, r_addr, r_size)._2 | ||||
|  | ||||
|       val r_sel = UIntToOH(in.ar.bits.id, numIds) | ||||
|       (r_sel.toBools zip r_count) foreach { case (s, r) => | ||||
|         when (in.ar.fire() && s) { r := r + UInt(1) } | ||||
|       } | ||||
|  | ||||
|       val w_out = Wire(out.a) | ||||
|       val w_size1 = in.aw.bits.bytes1() | ||||
|       val w_size = OH1ToUInt(w_size1) | ||||
|       val w_ok = edgeOut.manager.supportsPutPartialSafe(in.aw.bits.addr, w_size) | ||||
|       val w_addr = Mux(w_ok, in.aw.bits.addr, UInt(error) | in.aw.bits.addr(log2Up(beatBytes)-1, 0)) | ||||
|       val w_count = RegInit(Vec.fill(numIds) { UInt(0, width = txnCountBits) }) | ||||
|       val w_id = Cat(in.aw.bits.id, w_count(in.aw.bits.id)(logFlight-1,0), UInt(1, width=1)) | ||||
|  | ||||
|       assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, beatCountBits)) // because aligned | ||||
|       assert (!in.aw.valid || in.aw.bits.len === UInt(0) || in.aw.bits.size === UInt(log2Ceil(beatBytes))) // because aligned | ||||
|       in.aw.ready := w_out.ready && in.w.valid && in.w.bits.last | ||||
|       in.w.ready  := w_out.ready && in.aw.valid | ||||
|       w_out.valid := in.aw.valid && in.w.valid | ||||
|       w_out.bits := edgeOut.Put(w_id, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2 | ||||
|  | ||||
|       val w_sel = UIntToOH(in.aw.bits.id, numIds) | ||||
|       (w_sel.toBools zip w_count) foreach { case (s, r) => | ||||
|         when (in.aw.fire() && s) { r := r + UInt(1) } | ||||
|       } | ||||
|  | ||||
|       TLArbiter(TLArbiter.roundRobin)(out.a, (UInt(0), r_out), (in.aw.bits.len, w_out)) | ||||
|  | ||||
|       val ok_b  = Wire(in.b) | ||||
|       val ok_r  = Wire(in.r) | ||||
|  | ||||
|       val d_resp = Mux(out.d.bits.error, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY) | ||||
|       val d_hasData = edgeOut.hasData(out.d.bits) | ||||
|       val d_last = edgeOut.last(out.d) | ||||
|  | ||||
|       out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready) | ||||
|       ok_r.valid := out.d.valid && d_hasData | ||||
|       ok_b.valid := out.d.valid && !d_hasData | ||||
|  | ||||
|       ok_r.bits.id   := out.d.bits.source >> addedBits | ||||
|       ok_r.bits.data := out.d.bits.data | ||||
|       ok_r.bits.resp := d_resp | ||||
|       ok_r.bits.last := d_last | ||||
|  | ||||
|       // AXI4 needs irrevocable behaviour | ||||
|       in.r <> Queue.irrevocable(ok_r, 1, flow=true) | ||||
|  | ||||
|       ok_b.bits.id   := out.d.bits.source >> addedBits | ||||
|       ok_b.bits.resp := d_resp | ||||
|  | ||||
|       // AXI4 needs irrevocable behaviour | ||||
|       val q_b = Queue.irrevocable(ok_b, 1, flow=true) | ||||
|  | ||||
|       // We need to prevent sending B valid before the last W beat is accepted | ||||
|       // TileLink allows early acknowledgement of a write burst, but AXI does not. | ||||
|       val b_count = RegInit(Vec.fill(numIds) { UInt(0, width = txnCountBits) }) | ||||
|       val b_allow = b_count(in.b.bits.id) =/= w_count(in.b.bits.id) | ||||
|       val b_sel = UIntToOH(in.b.bits.id, numIds) | ||||
|  | ||||
|       (b_sel.toBools zip b_count) foreach { case (s, r) => | ||||
|         when (in.b.fire() && s) { r := r + UInt(1) } | ||||
|       } | ||||
|  | ||||
|       in.b.bits := q_b.bits | ||||
|       in.b.valid := q_b.valid && b_allow | ||||
|       q_b.ready := in.b.ready && b_allow | ||||
|  | ||||
|       // Unused channels | ||||
|       out.b.ready := Bool(true) | ||||
|       out.c.valid := Bool(false) | ||||
|       out.e.valid := Bool(false) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AXI4BundleRError(params: AXI4BundleParameters) extends AXI4BundleBase(params) | ||||
| { | ||||
|   val id   = UInt(width = params.idBits) | ||||
|   val last = Bool() | ||||
| } | ||||
|  | ||||
| object AXI4ToTL | ||||
| { | ||||
|   def apply()(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = { | ||||
|     val tl = LazyModule(new AXI4ToTL) | ||||
|     tl.node := x | ||||
|     tl.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										106
									
								
								src/main/scala/amba/axi4/UserYanker.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/main/scala/amba/axi4/UserYanker.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba.axi4 | ||||
|  | ||||
| import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| import freechips.rocketchip.config.Parameters | ||||
| import freechips.rocketchip.diplomacy._ | ||||
| import freechips.rocketchip.util.UIntToOH1 | ||||
|  | ||||
| class AXI4UserYanker(capMaxFlight: Option[Int] = None)(implicit p: Parameters) extends LazyModule | ||||
| { | ||||
|   val node = AXI4AdapterNode( | ||||
|     masterFn = { mp => mp.copy( | ||||
|       userBits = 0, | ||||
|       masters = mp.masters.map { m => m.copy( | ||||
|         maxFlight = (m.maxFlight, capMaxFlight) match { | ||||
|           case (Some(x), Some(y)) => Some(x min y) | ||||
|           case (Some(x), None)    => Some(x) | ||||
|           case (None,    Some(y)) => Some(y) | ||||
|           case (None,    None)    => None })})}, | ||||
|     slaveFn = { sp => sp }) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val out = node.bundleOut | ||||
|     } | ||||
|  | ||||
|     ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => | ||||
|       val bits = edgeIn.bundle.userBits | ||||
|       val need_bypass = edgeOut.slave.minLatency < 1 | ||||
|       require (bits > 0) // useless UserYanker! | ||||
|  | ||||
|       edgeOut.master.masters.foreach { m => | ||||
|         require (m.maxFlight.isDefined, "UserYanker needs a flight cap on each ID") | ||||
|       } | ||||
|  | ||||
|       def queue(id: Int) = { | ||||
|         val depth = edgeOut.master.masters.find(_.id.contains(id)).flatMap(_.maxFlight).getOrElse(0) | ||||
|         if (depth == 0) { | ||||
|           Wire(new QueueIO(UInt(width = bits), 1)) // unused ID => undefined value | ||||
|         } else { | ||||
|           Module(new Queue(UInt(width = bits), depth, flow=need_bypass)).io | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       val rqueues = Seq.tabulate(edgeIn.master.endId) { i => queue(i) } | ||||
|       val wqueues = Seq.tabulate(edgeIn.master.endId) { i => queue(i) } | ||||
|  | ||||
|       val arid = in.ar.bits.id | ||||
|       val ar_ready = Vec(rqueues.map(_.enq.ready))(arid) | ||||
|       in .ar.ready := out.ar.ready && ar_ready | ||||
|       out.ar.valid := in .ar.valid && ar_ready | ||||
|       out.ar.bits  := in .ar.bits | ||||
|  | ||||
|       val rid = out.r.bits.id | ||||
|       val r_valid = Vec(rqueues.map(_.deq.valid))(rid) | ||||
|       val r_bits = Vec(rqueues.map(_.deq.bits))(rid) | ||||
|       assert (!out.r.valid || r_valid) // Q must be ready faster than the response | ||||
|       in.r <> out.r | ||||
|       in.r.bits.user.get := r_bits | ||||
|  | ||||
|       val arsel = UIntToOH(arid, edgeIn.master.endId).toBools | ||||
|       val rsel  = UIntToOH(rid,  edgeIn.master.endId).toBools | ||||
|       (rqueues zip (arsel zip rsel)) foreach { case (q, (ar, r)) => | ||||
|         q.deq.ready := out.r .valid && in .r .ready && r && out.r.bits.last | ||||
|         q.enq.valid := in .ar.valid && out.ar.ready && ar | ||||
|         q.enq.bits  := in.ar.bits.user.get | ||||
|       } | ||||
|  | ||||
|       val awid = in.aw.bits.id | ||||
|       val aw_ready = Vec(wqueues.map(_.enq.ready))(awid) | ||||
|       in .aw.ready := out.aw.ready && aw_ready | ||||
|       out.aw.valid := in .aw.valid && aw_ready | ||||
|       out.aw.bits  := in .aw.bits | ||||
|  | ||||
|       val bid = out.b.bits.id | ||||
|       val b_valid = Vec(wqueues.map(_.deq.valid))(bid) | ||||
|       val b_bits = Vec(wqueues.map(_.deq.bits))(bid) | ||||
|       assert (!out.b.valid || b_valid) // Q must be ready faster than the response | ||||
|       in.b <> out.b | ||||
|       in.b.bits.user.get := b_bits | ||||
|  | ||||
|       val awsel = UIntToOH(awid, edgeIn.master.endId).toBools | ||||
|       val bsel  = UIntToOH(bid,  edgeIn.master.endId).toBools | ||||
|       (wqueues zip (awsel zip bsel)) foreach { case (q, (aw, b)) => | ||||
|         q.deq.ready := out.b .valid && in .b .ready && b | ||||
|         q.enq.valid := in .aw.valid && out.aw.ready && aw | ||||
|         q.enq.bits  := in.aw.bits.user.get | ||||
|       } | ||||
|  | ||||
|       out.w <> in.w | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| object AXI4UserYanker | ||||
| { | ||||
|   // applied to the AXI4 source node; y.node := AXI4UserYanker(idBits, maxFlight)(x.node) | ||||
|   def apply(capMaxFlight: Option[Int] = None)(x: AXI4OutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AXI4OutwardNode = { | ||||
|     val yanker = LazyModule(new AXI4UserYanker(capMaxFlight)) | ||||
|     yanker.node := x | ||||
|     yanker.node | ||||
|   } | ||||
| } | ||||
							
								
								
									
										11
									
								
								src/main/scala/amba/axi4/package.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/main/scala/amba/axi4/package.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // See LICENSE.SiFive for license details. | ||||
|  | ||||
| package freechips.rocketchip.amba | ||||
|  | ||||
| import Chisel._ | ||||
| import freechips.rocketchip.diplomacy.OutwardNodeHandle | ||||
|  | ||||
| package object axi4 | ||||
| { | ||||
|   type AXI4OutwardNode = OutwardNodeHandle[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle] | ||||
| } | ||||
		Reference in New Issue
	
	Block a user