diff --git a/src/main/scala/diplomacy/AddressRange.scala b/src/main/scala/diplomacy/AddressRange.scala new file mode 100644 index 00000000..9df91417 --- /dev/null +++ b/src/main/scala/diplomacy/AddressRange.scala @@ -0,0 +1,75 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.diplomacy + +import Chisel._ + +// Use AddressSet instead -- this is just for pretty printing +case class AddressRange(base: BigInt, size: BigInt) extends Ordered[AddressRange] +{ + val end = base + size + + require (base >= 0, s"AddressRange base must be positive, got: $base") + require (size > 0, s"AddressRange size must be > 0, got: $size") + + def compare(x: AddressRange) = { + val primary = (this.base - x.base).signum + val secondary = (x.size - this.size).signum + if (primary != 0) primary else secondary + } + + def contains(x: AddressRange) = base <= x.base && x.end <= end + def union(x: AddressRange): Option[AddressRange] = { + if (base > x.end || x.base > end) { + None + } else { + val obase = if (base < x.base) base else x.base + val oend = if (end > x.end) end else x.end + Some(AddressRange(obase, oend-obase)) + } + } + + private def helper(base: BigInt, end: BigInt) = + if (base < end) Seq(AddressRange(base, end-base)) else Nil + def subtract(x: AddressRange) = + helper(base, end min x.base) ++ helper(base max x.end, end) + + // We always want to see things in hex + override def toString() = "AddressRange(0x%x, 0x%x)".format(base, size) +} + +object AddressRange +{ + def fromSets(seq: Seq[AddressSet]): Seq[AddressRange] = unify(seq.flatMap(_.toRanges)) + def unify(seq: Seq[AddressRange]): Seq[AddressRange] = { + if (seq.isEmpty) return Nil + val ranges = seq.sorted + ranges.tail.foldLeft(Seq(ranges.head)) { case (head :: tail, x) => + head.union(x) match { + case Some(z) => z :: tail + case None => x :: head :: tail + } + }.reverse + } + // Set subtraction... O(n*n) b/c I am lazy + def subtract(from: Seq[AddressRange], take: Seq[AddressRange]): Seq[AddressRange] = + take.foldLeft(from) { case (left, r) => left.flatMap { _.subtract(r) } } +} + +case class AddressMapEntry(range: AddressRange, permissions: ResourcePermissions, names: Seq[String]) { + val ResourcePermissions(r, w, x, c, a) = permissions + + def toString(aw: Int) = s"\t%${aw}x - %${aw}x %c%c%c%c%c %s".format( + range.base, + range.base+range.size, + if (a) 'A' else ' ', + if (r) 'R' else ' ', + if (w) 'W' else ' ', + if (x) 'X' else ' ', + if (c) 'C' else ' ', + names.mkString(", ")) + + def serialize = s"""{"base":[${range.base}],"size":[${range.size}],""" + + s""""r":[$r],"w":[$w],"x":[$x],"c":[$c],"a":[$a],""" + + s""""names":[${names.map('"'+_+'"').mkString(",")}]}""" +} diff --git a/src/main/scala/diplomacy/Parameters.scala b/src/main/scala/diplomacy/Parameters.scala index 4133b52f..95110582 100644 --- a/src/main/scala/diplomacy/Parameters.scala +++ b/src/main/scala/diplomacy/Parameters.scala @@ -104,40 +104,6 @@ object TransferSizes { implicit def asBool(x: TransferSizes) = !x.none } -// Use AddressSet instead -- this is just for pretty printing -case class AddressRange(base: BigInt, size: BigInt) extends Ordered[AddressRange] -{ - val end = base + size - - require (base >= 0, s"AddressRange base must be positive, got: $base") - require (size > 0, s"AddressRange size must be > 0, got: $size") - - def compare(x: AddressRange) = { - val primary = (this.base - x.base).signum - val secondary = (x.size - this.size).signum - if (primary != 0) primary else secondary - } - - def contains(x: AddressRange) = base <= x.base && x.end <= end - def union(x: AddressRange): Option[AddressRange] = { - if (base > x.end || x.base > end) { - None - } else { - val obase = if (base < x.base) base else x.base - val oend = if (end > x.end) end else x.end - Some(AddressRange(obase, oend-obase)) - } - } - - private def helper(base: BigInt, end: BigInt) = - if (base < end) Seq(AddressRange(base, end-base)) else Nil - def subtract(x: AddressRange) = - helper(base, end min x.base) ++ helper(base max x.end, end) - - // We always want to see things in hex - override def toString() = "AddressRange(0x%x, 0x%x)".format(base, size) -} - // 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 @@ -210,24 +176,6 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] } } -object AddressRange -{ - def fromSets(seq: Seq[AddressSet]): Seq[AddressRange] = unify(seq.flatMap(_.toRanges)) - def unify(seq: Seq[AddressRange]): Seq[AddressRange] = { - if (seq.isEmpty) return Nil - val ranges = seq.sorted - ranges.tail.foldLeft(Seq(ranges.head)) { case (head :: tail, x) => - head.union(x) match { - case Some(z) => z :: tail - case None => x :: head :: tail - } - }.reverse - } - // Set subtraction... O(n*n) b/c I am lazy - def subtract(from: Seq[AddressRange], take: Seq[AddressRange]): Seq[AddressRange] = - take.foldLeft(from) { case (left, r) => left.flatMap { _.subtract(r) } } -} - object AddressSet { val everything = AddressSet(0, -1) diff --git a/src/main/scala/diplomacy/Resources.scala b/src/main/scala/diplomacy/Resources.scala index 58ba6415..a8442d6f 100644 --- a/src/main/scala/diplomacy/Resources.scala +++ b/src/main/scala/diplomacy/Resources.scala @@ -251,6 +251,15 @@ trait BindingScope } } + private def collect(path: List[String], value: ResourceValue): List[(String, ResourceAddress)] = { + value match { + case r: ResourceAddress => List((path(1), r)) + case b: ResourceMapping => List((path(1), ResourceAddress(b.address, b.permissions))) + case ResourceMap(value, _) => value.toList.flatMap { case (key, seq) => seq.flatMap(r => collect(key :: path, r)) } + case _ => Nil + } + } + /** Generate the device tree. */ def bindingTree: ResourceMap = { eval @@ -263,6 +272,9 @@ trait BindingScope expand(tokens, Seq(ResourceMap(mapping, Seq(d.label)))) }) ResourceMap(SortedMap("/" -> tree)) } + + /** Collect resource addresses from tree. */ + def collectResourceAddresses = collect(Nil, bindingTree) } object BindingScope diff --git a/src/main/scala/rocket/DCache.scala b/src/main/scala/rocket/DCache.scala index 2fe5e830..6702437a 100644 --- a/src/main/scala/rocket/DCache.scala +++ b/src/main/scala/rocket/DCache.scala @@ -15,16 +15,15 @@ import TLMessages._ class DCacheErrors(implicit p: Parameters) extends L1HellaCacheBundle()(p) with CanHaveErrors { - val correctable = (cacheParams.tagECC.canCorrect || cacheParams.dataECC.canCorrect).option(Valid(UInt(width = paddrBits))) - val uncorrectable = (cacheParams.tagECC.canDetect || cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits))) + val correctable = (cacheParams.tagCode.canCorrect || cacheParams.dataCode.canCorrect).option(Valid(UInt(width = paddrBits))) + val uncorrectable = (cacheParams.tagCode.canDetect || cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits))) val bus = Valid(UInt(width = paddrBits)) } class DCacheDataReq(implicit p: Parameters) extends L1HellaCacheBundle()(p) { - val eccBytes = cacheParams.dataECCBytes val addr = Bits(width = untagBits) val write = Bool() - val wdata = UInt(width = cacheParams.dataECC.width(eccBytes*8) * rowBytes/eccBytes) + val wdata = UInt(width = encBits * rowBytes / eccBytes) val wordMask = UInt(width = rowBytes / wordBytes) val eccMask = UInt(width = wordBytes / eccBytes) val way_en = Bits(width = nWays) @@ -37,9 +36,6 @@ class DCacheDataArray(implicit p: Parameters) extends L1HellaCacheModule()(p) { } require(rowBytes % wordBytes == 0) - val eccBits = cacheParams.dataECCBytes * 8 - val encBits = cacheParams.dataECC.width(eccBits) - val encWordBits = encBits * (wordBits / eccBits) val eccMask = if (eccBits == wordBits) Seq(true.B) else io.req.bits.eccMask.toBools val wMask = if (nWays == 1) eccMask else (0 until nWays).flatMap(i => eccMask.map(_ && io.req.bits.way_en(i))) val wWords = io.req.bits.wdata.grouped(encBits * (wordBits / eccBits)) @@ -69,11 +65,8 @@ class DCache(hartid: Int, val scratch: () => Option[AddressSet] = () => None, va } class DCacheModule(outer: DCache) extends HellaCacheModule(outer) { - // no tag ECC support - val tECC = cacheParams.tagECC - val dECC = cacheParams.dataECC - val eccBytes = cacheParams.dataECCBytes - val eccBits = eccBytes * 8 + val tECC = cacheParams.tagCode + val dECC = cacheParams.dataCode require(isPow2(eccBytes) && eccBytes <= wordBytes) require(eccBytes == 1 || !dECC.isInstanceOf[IdentityCode]) val usingRMW = eccBytes > 1 || usingAtomicsInCache diff --git a/src/main/scala/rocket/HellaCache.scala b/src/main/scala/rocket/HellaCache.scala index dfd0a616..fd837632 100644 --- a/src/main/scala/rocket/HellaCache.scala +++ b/src/main/scala/rocket/HellaCache.scala @@ -19,8 +19,8 @@ case class DCacheParams( nWays: Int = 4, rowBits: Int = 64, nTLBEntries: Int = 32, - tagECC: Code = new IdentityCode, - dataECC: Code = new IdentityCode, + tagECC: Option[String] = None, + dataECC: Option[String] = None, dataECCBytes: Int = 1, nMSHRs: Int = 1, nSDQ: Int = 17, @@ -31,6 +31,9 @@ case class DCacheParams( pipelineWayMux: Boolean = false, scratch: Option[BigInt] = None) extends L1CacheParams { + def tagCode: Code = Code.fromString(tagECC) + def dataCode: Code = Code.fromString(dataECC) + def dataScratchpadBytes: Int = scratch.map(_ => nSets*blockBytes).getOrElse(0) def replacement = new RandomReplacement(nWays) @@ -58,7 +61,11 @@ trait HasL1HellaCacheParameters extends HasL1CacheParameters with HasCoreParamet def offsetlsb = wordOffBits def rowWords = rowBits/wordBits def doNarrowRead = coreDataBits * nWays % rowBits == 0 - def encDataBits = cacheParams.dataECC.width(coreDataBits) + def eccBytes = cacheParams.dataECCBytes + val eccBits = cacheParams.dataECCBytes * 8 + val encBits = cacheParams.dataCode.width(eccBits) + val encWordBits = encBits * (wordBits / eccBits) + def encDataBits = cacheParams.dataCode.width(coreDataBits) // NBDCache only def encRowBits = encDataBits*rowWords def lrscCycles = 32 // ISA requires 16-insn LRSC sequences to succeed def lrscBackoff = 3 // disallow LRSC reacquisition briefly diff --git a/src/main/scala/rocket/ICache.scala b/src/main/scala/rocket/ICache.scala index 2b5b1e65..fe1e3661 100644 --- a/src/main/scala/rocket/ICache.scala +++ b/src/main/scala/rocket/ICache.scala @@ -20,13 +20,15 @@ case class ICacheParams( rowBits: Int = 128, nTLBEntries: Int = 32, cacheIdBits: Int = 0, - tagECC: Code = new IdentityCode, - dataECC: Code = new IdentityCode, + tagECC: Option[String] = None, + dataECC: Option[String] = None, itimAddr: Option[BigInt] = None, prefetch: Boolean = false, blockBytes: Int = 64, latency: Int = 2, fetchBytes: Int = 4) extends L1CacheParams { + def tagCode: Code = Code.fromString(tagECC) + def dataCode: Code = Code.fromString(dataECC) def replacement = new RandomReplacement(nWays) } @@ -41,8 +43,8 @@ class ICacheReq(implicit p: Parameters) extends CoreBundle()(p) with HasL1ICache class ICacheErrors(implicit p: Parameters) extends CoreBundle()(p) with HasL1ICacheParameters with CanHaveErrors { - val correctable = (cacheParams.tagECC.canDetect || cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits))) - val uncorrectable = (cacheParams.itimAddr.nonEmpty && cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits))) + val correctable = (cacheParams.tagCode.canDetect || cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits))) + val uncorrectable = (cacheParams.itimAddr.nonEmpty && cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits))) } class ICache(val icacheParams: ICacheParams, val hartId: Int)(implicit p: Parameters) extends LazyModule { @@ -113,8 +115,8 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer) // Option.unzip does not exist :-( val (tl_in, edge_in) = outer.slaveNode.in.headOption.unzip - val tECC = cacheParams.tagECC - val dECC = cacheParams.dataECC + val tECC = cacheParams.tagCode + val dECC = cacheParams.dataCode require(isPow2(nSets) && isPow2(nWays)) require(!usingVM || pgIdxBits >= untagBits) diff --git a/src/main/scala/rocket/NBDcache.scala b/src/main/scala/rocket/NBDcache.scala index cf0fa9bb..7487daa3 100644 --- a/src/main/scala/rocket/NBDcache.scala +++ b/src/main/scala/rocket/NBDcache.scala @@ -668,8 +668,8 @@ class NonBlockingDCacheModule(outer: NonBlockingDCache) extends HellaCacheModule require(dataScratchpadSize == 0) // ECC is only supported on the data array - require(cacheParams.tagECC.isInstanceOf[IdentityCode]) - val dECC = cacheParams.dataECC + require(cacheParams.tagCode.isInstanceOf[IdentityCode]) + val dECC = cacheParams.dataCode val wb = Module(new WritebackUnit) val prober = Module(new ProbeUnit) diff --git a/src/main/scala/rocket/RocketCore.scala b/src/main/scala/rocket/RocketCore.scala index afde776f..5a4ac2e4 100644 --- a/src/main/scala/rocket/RocketCore.scala +++ b/src/main/scala/rocket/RocketCore.scala @@ -572,7 +572,7 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p) sboard.clear(ll_wen, ll_waddr) def id_sboard_clear_bypass(r: UInt) = { // ll_waddr arrives late when D$ has ECC, so reshuffle the hazard check - if (tileParams.dcache.get.dataECC.isInstanceOf[IdentityCode]) ll_wen && ll_waddr === r + if (!tileParams.dcache.get.dataECC.isDefined) ll_wen && ll_waddr === r else div.io.resp.fire() && div.io.resp.bits.tag === r || dmem_resp_replay && dmem_resp_xpu && dmem_resp_waddr === r } val id_sboard_hazard = checkHazards(hazard_targets, rd => sboard.read(rd) && !id_sboard_clear_bypass(rd)) diff --git a/src/main/scala/subsystem/BaseSubsystem.scala b/src/main/scala/subsystem/BaseSubsystem.scala index 8ff54586..2df929c7 100644 --- a/src/main/scala/subsystem/BaseSubsystem.scala +++ b/src/main/scala/subsystem/BaseSubsystem.scala @@ -97,39 +97,22 @@ abstract class BaseSubsystem(implicit p: Parameters) extends BareSubsystem { } } -abstract class BaseSubsystemModuleImp[+L <: BaseSubsystem](_outer: L) extends BareSubsystemModuleImp(_outer) { - println("Generated Address Map") - private val aw = (outer.sbus.busView.bundle.addressBits-1)/4 + 1 - private val fmt = s"\t%${aw}x - %${aw}x %c%c%c%c%c %s" - private def collect(path: List[String], value: ResourceValue): List[(String, ResourceAddress)] = { - value match { - case r: ResourceAddress => List((path(1), r)) - case b: ResourceMapping => List((path(1), ResourceAddress(b.address, b.permissions))) - case ResourceMap(value, _) => value.toList.flatMap { case (key, seq) => seq.flatMap(r => collect(key :: path, r)) } - case _ => Nil - } - } - private val ranges = collect(Nil, outer.bindingTree).groupBy(_._2).toList.flatMap { case (key, seq) => - AddressRange.fromSets(key.address).map { r => (r, key.permissions, seq.map(_._1)) } - }.sortBy(_._1) - private val json = ranges.map { case (range, ResourcePermissions(r, w, x, c, a), names) => - println(fmt.format( - range.base, - range.base+range.size, - if (a) 'A' else ' ', - if (r) 'R' else ' ', - if (w) 'W' else ' ', - if (x) 'X' else ' ', - if (c) 'C' else ' ', - names.mkString(", "))) - s"""{"base":[${range.base}],"size":[${range.size}],"r":[$r],"w":[$w],"x":[$x],"c":[$c],"a":[$a],"names":[${names.map('"'+_+'"').mkString(",")}]}""" +abstract class BaseSubsystemModuleImp[+L <: BaseSubsystem](_outer: L) extends BareSubsystemModuleImp(_outer) { + private val mapping: Seq[AddressMapEntry] = { + outer.collectResourceAddresses.groupBy(_._2).toList.flatMap { case (key, seq) => + AddressRange.fromSets(key.address).map { r => AddressMapEntry(r, key.permissions, seq.map(_._1)) } + }.sortBy(_.range) } + + println("Generated Address Map") + mapping.map(entry => println(entry.toString((outer.sbus.busView.bundle.addressBits-1)/4 + 1))) println("") - ElaborationArtefacts.add("memmap.json", s"""{"mapping":[${json.mkString(",")}]}""") + + ElaborationArtefacts.add("memmap.json", s"""{"mapping":[${mapping.map(_.serialize).mkString(",")}]}""") // Confirm that all of memory was described by DTS - private val dtsRanges = AddressRange.unify(ranges.map(_._1)) + private val dtsRanges = AddressRange.unify(mapping.map(_.range)) private val allRanges = AddressRange.unify(outer.topManagers.get.flatMap { m => AddressRange.fromSets(m.address) }) if (dtsRanges != allRanges) { diff --git a/src/main/scala/tilelink/ToAXI4.scala b/src/main/scala/tilelink/ToAXI4.scala index 2b733766..08fa0020 100644 --- a/src/main/scala/tilelink/ToAXI4.scala +++ b/src/main/scala/tilelink/ToAXI4.scala @@ -9,6 +9,30 @@ import freechips.rocketchip.util._ import freechips.rocketchip.amba.axi4._ import scala.math.{min, max} +class TLtoAXI4IdMap(tl: TLClientPortParameters, axi4: AXI4MasterPortParameters) { + private val axiDigits = String.valueOf(axi4.endId-1).length() + private val tlDigits = String.valueOf(tl.endSourceId-1).length() + private val fmt = s"\t[%${axiDigits}d, %${axiDigits}d) <= [%${tlDigits}d, %${tlDigits}d) %s%s%s" + private val sorted = tl.clients.sortWith(TLToAXI4.sortByType) + + val mapping: Seq[TLToAXI4IdMapEntry] = (sorted zip axi4.masters) map { case (c, m) => + TLToAXI4IdMapEntry(m.id, c.sourceId, c.name, c.supportsProbe, c.requestFifo) + } + + def pretty: String = mapping.map(_.pretty(fmt)).mkString(",\n") +} + +case class TLToAXI4IdMapEntry(axi4Id: IdRange, tlId: IdRange, name: String, isCache: Boolean, requestFifo: Boolean) { + def pretty(fmt: String) = fmt.format( + axi4Id.start, + axi4Id.end, + tlId.start, + tlId.end, + s""""$name"""", + if (isCache) " [CACHE]" else "", + if (requestFifo) " [FIFO]" else "") +} + case class TLToAXI4Node(stripBits: Int = 0)(implicit valName: ValName) extends MixedAdapterNode(TLImp, AXI4Imp)( dFn = { p => p.clients.foreach { c => @@ -59,32 +83,25 @@ class TLToAXI4(val combinational: Boolean = true, val adapterName: Option[String require (slaves(0).interleavedId.isDefined) slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) } - val axiDigits = String.valueOf(edgeOut.master.endId-1).length() - val tlDigits = String.valueOf(edgeIn.client.endSourceId-1).length() - // Construct the source=>ID mapping table - adapterName.foreach { n => println(s"$n AXI4-ID <= TL-Source mapping:") } + val map = new TLtoAXI4IdMap(edgeIn.client, edgeOut.master) val sourceStall = Wire(Vec(edgeIn.client.endSourceId, Bool())) val sourceTable = Wire(Vec(edgeIn.client.endSourceId, out.aw.bits.id)) val idStall = Wire(init = Vec.fill(edgeOut.master.endId) { Bool(false) }) var idCount = Array.fill(edgeOut.master.endId) { None:Option[Int] } - val maps = (edgeIn.client.clients.sortWith(TLToAXI4.sortByType) zip edgeOut.master.masters) flatMap { case (c, m) => - for (i <- 0 until c.sourceId.size) { - val id = m.id.start + (if (c.requestFifo) 0 else (i >> stripBits)) - sourceStall(c.sourceId.start + i) := idStall(id) - sourceTable(c.sourceId.start + i) := UInt(id) - } - if (c.requestFifo) { idCount(m.id.start) = Some(c.sourceId.size) } - adapterName.map { n => - val fmt = s"\t[%${axiDigits}d, %${axiDigits}d) <= [%${tlDigits}d, %${tlDigits}d) %s%s" - println(fmt.format(m.id.start, m.id.end, c.sourceId.start, c.sourceId.end, c.name, if (c.supportsProbe) " CACHE" else "")) - s"""{"axi4-id":[${m.id.start},${m.id.end}],"tilelink-id":[${c.sourceId.start},${c.sourceId.end}],"master":["${c.name}"],"cache":[${!(!c.supportsProbe)}]}""" + + map.mapping.foreach { case TLToAXI4IdMapEntry(axi4Id, tlId, _, _, fifo) => + for (i <- 0 until tlId.size) { + val id = axi4Id.start + (if (fifo) 0 else (i >> stripBits)) + sourceStall(tlId.start + i) := idStall(id) + sourceTable(tlId.start + i) := UInt(id) } + if (fifo) { idCount(axi4Id.start) = Some(tlId.size) } } adapterName.foreach { n => - println("") - ElaborationArtefacts.add(s"${n}.axi4.json", s"""{"mapping":[${maps.mkString(",")}]}""") + println(s"$n AXI4-ID <= TL-Source mapping:\n${map.pretty}\n") + ElaborationArtefacts.add(s"$n.axi4.json", s"""{"mapping":[${map.mapping.mkString(",")}]}""") } // We need to keep the following state from A => D: (size, source) diff --git a/src/main/scala/util/ECC.scala b/src/main/scala/util/ECC.scala index 6245513f..a3b39890 100644 --- a/src/main/scala/util/ECC.scala +++ b/src/main/scala/util/ECC.scala @@ -178,3 +178,15 @@ trait CanHaveErrors extends Bundle { val correctable: Option[ValidIO[UInt]] val uncorrectable: Option[ValidIO[UInt]] } + +object Code { + def fromString(s: Option[String]): Code = fromString(s.getOrElse("none")) + def fromString(s: String): Code = s.toLowerCase match { + case "none" => new IdentityCode + case "identity" => new IdentityCode + case "parity" => new ParityCode + case "sec" => new SECCode + case "secded" => new SECDEDCode + case _ => throw new IllegalArgumentException("Unknown ECC type") + } +}