diff --git a/chisel3 b/chisel3 index 2ff229da..dda64c1d 160000 --- a/chisel3 +++ b/chisel3 @@ -1 +1 @@ -Subproject commit 2ff229dac5f915e7f583cbf9cc8118674a4e52a5 +Subproject commit dda64c1dee16b5da15ac690bd3cd6759c3d5c032 diff --git a/firrtl b/firrtl index 7c38199c..726c8083 160000 --- a/firrtl +++ b/firrtl @@ -1 +1 @@ -Subproject commit 7c38199ce7a5d9dd7e27ffbb9b2b2770b972ed94 +Subproject commit 726c808375fe513c70376bf05e76dd938e578bf9 diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala index 14604529..36254d47 100644 --- a/src/main/scala/coreplex/Configs.scala +++ b/src/main/scala/coreplex/Configs.scala @@ -139,7 +139,7 @@ class BaseCoreplexConfig extends Config ( else new MESICoherence(site(L2DirectoryRepresentation))), nManagers = site(NBanksPerMemoryChannel)*site(NMemoryChannels) + 1 /* MMIO */, nCachingClients = site(NCachedTileLinkPorts), - nCachelessClients = site(NCoreplexExtClients).get + site(NUncachedTileLinkPorts), + nCachelessClients = site(NCoreplexExtClients) + site(NUncachedTileLinkPorts), maxClientXacts = max_int( // L1 cache site(DCacheKey).nMSHRs + 1 /* IOMSHR */, @@ -170,7 +170,7 @@ class BaseCoreplexConfig extends Config ( TileLinkParameters( coherencePolicy = new MICoherence( new NullRepresentation(site(NBanksPerMemoryChannel))), - nManagers = site(GlobalAddrMap).get.subMap("io").numSlaves, + nManagers = 1, nCachingClients = 0, nCachelessClients = 1, maxClientXacts = 4, diff --git a/src/main/scala/coreplex/Coreplex.scala b/src/main/scala/coreplex/Coreplex.scala index 1c6ef1b9..3523293d 100644 --- a/src/main/scala/coreplex/Coreplex.scala +++ b/src/main/scala/coreplex/Coreplex.scala @@ -32,7 +32,7 @@ trait HasCoreplexParameters { lazy val innerParams = p.alterPartial({ case TLId => "L1toL2" }) lazy val outermostParams = p.alterPartial({ case TLId => "Outermost" }) lazy val outermostMMIOParams = p.alterPartial({ case TLId => "MMIO_Outermost" }) - lazy val globalAddrMap = p(rocketchip.GlobalAddrMap).get + lazy val globalAddrMap = p(rocketchip.GlobalAddrMap) } case class CoreplexConfig( @@ -56,7 +56,7 @@ abstract class Coreplex(implicit val p: Parameters, implicit val c: CoreplexConf val slave = Vec(c.nSlaves, new ClientUncachedTileLinkIO()(innerParams)).flip val interrupts = Vec(c.nExtInterrupts, Bool()).asInput val debug = new DebugBusIO()(p).flip - val prci = Vec(c.nTiles, new PRCITileIO).flip + val clint = Vec(c.nTiles, new CoreplexLocalInterrupts).asInput val success = Bool(OUTPUT) } @@ -149,8 +149,8 @@ class DefaultCoreplex(tp: Parameters, tc: CoreplexConfig) extends Coreplex()(tp, // connect coreplex-internal interrupts to tiles for (((tile, tileReset), i) <- (tileList zip tileResets) zipWithIndex) { - tileReset := io.prci(i).reset - tile.io.interrupts := io.prci(i).interrupts + tileReset := reset // TODO should tiles be reset separately from coreplex? + tile.io.interrupts := io.clint(i) tile.io.interrupts.meip := plic.io.harts(plic.cfg.context(i, 'M')) tile.io.interrupts.seip.foreach(_ := plic.io.harts(plic.cfg.context(i, 'S'))) tile.io.interrupts.debug := debugModule.io.debugInterrupts(i) diff --git a/src/main/scala/groundtest/Comparator.scala b/src/main/scala/groundtest/Comparator.scala index 67cffb9e..8c5c8213 100644 --- a/src/main/scala/groundtest/Comparator.scala +++ b/src/main/scala/groundtest/Comparator.scala @@ -338,7 +338,7 @@ class ComparatorSink(implicit val p: Parameters) extends Module assert (g.is_builtin_type, "grant not builtin") assert (base.g_type === g.g_type, "g_type mismatch") - assert (base.addr_beat === g.addr_beat || !g.hasData(), "addr_beat mismatch") + assert (base.addr_beat === g.addr_beat || !g.hasMultibeatData(), "addr_beat mismatch") assert (base.data === g.data || !g.hasData(), "data mismatch") assert_conds.zipWithIndex.foreach { case (cond, i) => diff --git a/src/main/scala/groundtest/Configs.scala b/src/main/scala/groundtest/Configs.scala index d7db98f2..2a450de7 100644 --- a/src/main/scala/groundtest/Configs.scala +++ b/src/main/scala/groundtest/Configs.scala @@ -92,7 +92,7 @@ class WithGroundTest extends Config( else new MESICoherence(site(L2DirectoryRepresentation))), nManagers = site(NBanksPerMemoryChannel)*site(NMemoryChannels) + 1, nCachingClients = site(NCachedTileLinkPorts), - nCachelessClients = site(NCoreplexExtClients).get + site(NUncachedTileLinkPorts), + nCachelessClients = site(NCoreplexExtClients) + site(NUncachedTileLinkPorts), maxClientXacts = ((site(DCacheKey).nMSHRs + 1) +: site(GroundTestKey).map(_.maxXacts)) .reduce(max(_, _)), @@ -137,11 +137,11 @@ class WithComparator extends Config( case BuildGroundTest => (p: Parameters) => Module(new ComparatorCore()(p)) case ComparatorKey => ComparatorParameters( - targets = Seq("mem", "io:ext:testram").map(name => - site(GlobalAddrMap).get(name).start.longValue), + targets = Seq("mem", "io:ext:TL2:testram").map(name => + site(GlobalAddrMap)(name).start.longValue), width = 8, operations = 1000, - atomics = site(UseAtomics), + atomics = false, // !!! re-enable soon: site(UseAtomics), prefetches = site("COMPARATOR_PREFETCHES")) case FPUConfig => None case UseAtomics => false @@ -168,7 +168,7 @@ class WithMemtest extends Config( } case GeneratorKey => GeneratorParameters( maxRequests = 128, - startAddress = site(GlobalAddrMap).get("mem").start) + startAddress = site(GlobalAddrMap)("mem").start) case BuildGroundTest => (p: Parameters) => Module(new GeneratorTest()(p)) case _ => throw new CDEMatchError @@ -228,7 +228,7 @@ class WithNastiConverterTest extends Config( } case GeneratorKey => GeneratorParameters( maxRequests = 128, - startAddress = site(GlobalAddrMap).get("mem").start) + startAddress = site(GlobalAddrMap)("mem").start) case BuildGroundTest => (p: Parameters) => Module(new NastiConverterTest()(p)) case _ => throw new CDEMatchError @@ -248,7 +248,7 @@ class WithTraceGen extends Config( val nSets = 32 // L2 NSets val nWays = 1 val blockOffset = site(CacheBlockOffsetBits) - val baseAddr = site(GlobalAddrMap).get("mem").start + val baseAddr = site(GlobalAddrMap)("mem").start val nBeats = site(MIFDataBeats) List.tabulate(4 * nWays) { i => Seq.tabulate(nBeats) { j => (j * 8) + ((i * nSets) << blockOffset) } @@ -260,6 +260,7 @@ class WithTraceGen extends Config( knobValues = { case "L1D_SETS" => 16 case "L1D_WAYS" => 1 + case _ => throw new CDEMatchError }) class WithPCIeMockupTest extends Config( @@ -270,7 +271,7 @@ class WithPCIeMockupTest extends Config( GroundTestTileSettings(1)) case GeneratorKey => GeneratorParameters( maxRequests = 128, - startAddress = site(GlobalAddrMap).get("mem").start) + startAddress = site(GlobalAddrMap)("mem").start) case BuildGroundTest => (p: Parameters) => p(TileId) match { case 0 => Module(new GeneratorTest()(p)) @@ -304,7 +305,7 @@ class WithDirectComparator extends Config( targets = Seq(0L, 0x100L), width = 8, operations = 1000, - atomics = site(UseAtomics), + atomics = false, // !!! re-enable soon: site(UseAtomics), prefetches = site("COMPARATOR_PREFETCHES")) case FPUConfig => None case UseAtomics => false diff --git a/src/main/scala/groundtest/Regression.scala b/src/main/scala/groundtest/Regression.scala index ef9eb576..3eb37eed 100644 --- a/src/main/scala/groundtest/Regression.scala +++ b/src/main/scala/groundtest/Regression.scala @@ -72,7 +72,7 @@ class IOGetAfterPutBlockRegression(implicit p: Parameters) extends Regression()( io.mem.grant.ready := Bool(true) io.cache.req.valid := !get_sent && started - io.cache.req.bits.addr := UInt(addrMap("io:ext:bootrom").start) + io.cache.req.bits.addr := UInt(addrMap("io:ext:TL2:bootrom").start) io.cache.req.bits.typ := UInt(log2Ceil(32 / 8)) io.cache.req.bits.cmd := M_XRD io.cache.req.bits.tag := UInt(0) diff --git a/src/main/scala/groundtest/TestHarness.scala b/src/main/scala/groundtest/TestHarness.scala index 8853c513..dbabf0da 100644 --- a/src/main/scala/groundtest/TestHarness.scala +++ b/src/main/scala/groundtest/TestHarness.scala @@ -4,4 +4,4 @@ import Chisel._ import cde.Parameters // !!! TODO: Replace with a groundtest-specific test harness -class TestHarness(implicit p: Parameters) extends rocketchip.TestHarness()(p) +class TestHarness(implicit p: Parameters) extends rocketchip.TestHarness(p) diff --git a/src/main/scala/junctions/addrmap.scala b/src/main/scala/junctions/addrmap.scala index b3f1722a..19613ae7 100644 --- a/src/main/scala/junctions/addrmap.scala +++ b/src/main/scala/junctions/addrmap.scala @@ -12,7 +12,7 @@ trait HasAddrMapParameters { implicit val p: Parameters val paddrBits = p(PAddrBits) - def addrMap = p(rocketchip.GlobalAddrMap).get + def addrMap = p(rocketchip.GlobalAddrMap) } case class MemAttr(prot: Int, cacheable: Boolean = false) diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala index 37ad9047..7003df30 100644 --- a/src/main/scala/rocketchip/Configs.scala +++ b/src/main/scala/rocketchip/Configs.scala @@ -20,9 +20,6 @@ import cde.{Parameters, Config, Dump, Knob, CDEMatchError} class BasePlatformConfig extends Config( topDefinitions = { - val configString = new GlobalVariable[String] - val globalAddrMap = new GlobalVariable[AddrMap] - val nCoreplexExtClients = new GlobalVariable[Int] (pname,site,here) => { type PF = PartialFunction[Any,Any] def findBy(sname:Any):Any = here[PF](site[Any](sname))(pname) @@ -55,7 +52,6 @@ class BasePlatformConfig extends Config( case NExtMMIOTLChannels => 0 case AsyncBusChannels => false case NExtBusAXIChannels => 0 - case NCoreplexExtClients => nCoreplexExtClients case HastiId => "Ext" case HastiKey("TL") => HastiParameters( @@ -69,8 +65,6 @@ class BasePlatformConfig extends Config( case NMemoryChannels => Dump("N_MEM_CHANNELS", 1) case TMemoryChannels => BusType.AXI case ExtMemSize => Dump("MEM_SIZE", 0x10000000L) - case ConfigString => configString - case GlobalAddrMap => globalAddrMap case RTCPeriod => 100 // gives 10 MHz RTC assuming 1 GHz uncore clock case BuildExampleTop => (p: Parameters) => uncore.tilelink2.LazyModule(new ExampleTop(p)) @@ -152,6 +146,7 @@ class RoccExampleConfig extends Config(new WithRoccExample ++ new BaseConfig) class WithMIFDataBits(n: Int) extends Config( (pname, site, here) => pname match { case MIFDataBits => Dump("MIF_DATA_BITS", n) + case _ => throw new CDEMatchError }) class MIF128BitConfig extends Config( @@ -182,12 +177,14 @@ class TinyConfig extends Config( class WithAsyncDebug extends Config ( (pname, site, here) => pname match { - case AsyncDebugBus => true + case AsyncDebugBus => true + case _ => throw new CDEMatchError } ) class WithJtagDTM extends Config ( (pname, site, here) => pname match { - case IncludeJtagDTM => true + case IncludeJtagDTM => true + case _ => throw new CDEMatchError } ) diff --git a/src/main/scala/rocketchip/Generator.scala b/src/main/scala/rocketchip/Generator.scala index ec0d6a39..f4f24247 100644 --- a/src/main/scala/rocketchip/Generator.scala +++ b/src/main/scala/rocketchip/Generator.scala @@ -56,6 +56,10 @@ trait HasGeneratorUtilities { } } +object ConfigStringOutput { + var contents: Option[String] = None +} + trait Generator extends App with HasGeneratorUtilities { lazy val names = { require(args.size == 5, "Usage: sbt> " + @@ -67,20 +71,25 @@ trait Generator extends App with HasGeneratorUtilities { configProject = args(3), configs = args(4)) } + + lazy val td = names.targetDir lazy val config = getConfig(names) lazy val world = config.toInstance lazy val params = Parameters.root(world) lazy val circuit = elaborate(names, params) + lazy val longName = names.topModuleClass + "." + names.configs + + def writeOutputFiles() { + TestGeneration.addSuite(new RegressionTestSuite(params(RegressionTestNames))) + writeOutputFile(td, s"$longName.d", TestGeneration.generateMakefrag) // Coreplex-specific test suites + writeOutputFile(td, s"$longName.prm", ParameterDump.getDump) // Parameters flagged with Dump() + writeOutputFile(td, s"${names.configs}.knb", world.getKnobs) // Knobs for DSE + writeOutputFile(td, s"${names.configs}.cst", world.getConstraints) // Constraints for DSE + ConfigStringOutput.contents.foreach(c => writeOutputFile(td, s"${names.configs}.cfg", c)) // String for software + } } object RocketChipGenerator extends Generator { - val longName = names.topModuleClass + "." + names.configs - val td = names.targetDir Driver.dumpFirrtl(circuit, Some(new File(td, s"$longName.fir"))) // FIRRTL - TestGeneration.addSuite(new RegressionTestSuite(params(RegressionTestNames))) - writeOutputFile(td, s"$longName.d", TestGeneration.generateMakefrag) // Coreplex-specific test suites - writeOutputFile(td, s"$longName.prm", ParameterDump.getDump) // Parameters flagged with Dump() - writeOutputFile(td, s"${names.configs}.knb", world.getKnobs) // Knobs for DSE - writeOutputFile(td, s"${names.configs}.cst", world.getConstraints) // Constraints for DSE - writeOutputFile(td, s"${names.configs}.cfg", params(ConfigString).get) // String for software + writeOutputFiles() } diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index 5fd15e97..f2d867cc 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -7,11 +7,13 @@ import cde.{Parameters, Field} import junctions._ import junctions.NastiConstants._ import uncore.tilelink._ -import uncore.tilelink2.{LazyModule, LazyModuleImp} +import uncore.tilelink2._ import uncore.converters._ import uncore.devices._ import uncore.util._ import rocket.Util._ +import rocket.XLen +import scala.math.max import coreplex._ /** Options for memory bus interface */ @@ -202,10 +204,10 @@ trait PeripheryMasterMMIOModule extends HasPeripheryParameters { implicit val p: Parameters val outer: PeripheryMasterMMIO val io: PeripheryMasterMMIOBundle - val mmioNetwork: Option[TileLinkRecursiveInterconnect] + val mmioNetwork: TileLinkRecursiveInterconnect val mmio_ports = p(ExtMMIOPorts) map { port => - TileLinkWidthAdapter(mmioNetwork.get.port(port.name), "MMIO_Outermost") + TileLinkWidthAdapter(mmioNetwork.port(port.name), "MMIO_Outermost") } val mmio_axi_start = 0 @@ -277,37 +279,47 @@ trait PeripherySlaveModule extends HasPeripheryParameters { ///// -/** Always-ON block */ -trait PeripheryAON extends LazyModule { +trait PeripheryCoreplexLocalInterrupter extends LazyModule with HasPeripheryParameters { implicit val p: Parameters + val peripheryBus: TLXbar + + // CoreplexLocalInterrupter must be at least 64b if XLen >= 64 + val beatBytes = (innerMMIOParams(XLen) min 64) / 8 + val clintConfig = CoreplexLocalInterrupterConfig(beatBytes) + val clint = LazyModule(new CoreplexLocalInterrupter(clintConfig)(innerMMIOParams)) + // The periphery bus is 32-bit, so we may need to adapt its width to XLen + clint.node := TLFragmenter(TLWidthWidget(peripheryBus.node, 4), beatBytes, 256) + + // TL1 legacy val pDevices: ResourceManager[AddrMapEntry] - - pDevices.add(AddrMapEntry("prci", MemSize(0x4000000, MemAttr(AddrMapProt.RW)))) + pDevices.add(AddrMapEntry("clint", MemRange(clintConfig.address, clintConfig.size, MemAttr(AddrMapProt.RW)))) } -trait PeripheryAONBundle { +trait PeripheryCoreplexLocalInterrupterBundle { implicit val p: Parameters } -trait PeripheryAONModule extends HasPeripheryParameters { +trait PeripheryCoreplexLocalInterrupterModule extends HasPeripheryParameters { implicit val p: Parameters - val outer: PeripheryAON - val io: PeripheryAONBundle - val mmioNetwork: Option[TileLinkRecursiveInterconnect] + val outer: PeripheryCoreplexLocalInterrupter + val io: PeripheryCoreplexLocalInterrupterBundle val coreplex: Coreplex - val prci = Module(new PRCI()(innerMMIOParams)) - prci.io.rtcTick := Counter(p(RTCPeriod)).inc() - prci.io.tl <> mmioNetwork.get.port("prci") - coreplex.io.prci <> prci.io.tiles + outer.clint.module.io.rtcTick := Counter(p(RTCPeriod)).inc() + coreplex.io.clint <> outer.clint.module.io.tiles } ///// trait PeripheryBootROM extends LazyModule { implicit val p: Parameters - val pDevices: ResourceManager[AddrMapEntry] + val peripheryBus: TLXbar + val rom = LazyModule(new TLROM(0x1000, 0x1000, GenerateBootROM(p))) + rom.node := TLFragmenter(peripheryBus.node, 4, 256) + + // TL1 legacy address map + val pDevices: ResourceManager[AddrMapEntry] pDevices.add(AddrMapEntry("bootrom", MemRange(0x1000, 4096, MemAttr(AddrMapProt.RX)))) } @@ -319,20 +331,23 @@ trait PeripheryBootROMModule extends HasPeripheryParameters { implicit val p: Parameters val outer: PeripheryBootROM val io: PeripheryBootROMBundle - val mmioNetwork: Option[TileLinkRecursiveInterconnect] - - val bootROM = Module(new ROMSlave(GenerateBootROM(p))(innerMMIOParams)) - bootROM.io <> mmioNetwork.get.port("bootrom") } ///// trait PeripheryTestRAM extends LazyModule { implicit val p: Parameters - val pDevices: ResourceManager[AddrMapEntry] + val peripheryBus: TLXbar + val ramBase = 0x52000000 val ramSize = 0x1000 - pDevices.add(AddrMapEntry("testram", MemSize(ramSize, MemAttr(AddrMapProt.RW)))) + + val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1))) + sram.node := TLFragmenter(peripheryBus.node, 4, 256) + + // TL1 legacy address map + val pDevices: ResourceManager[AddrMapEntry] + pDevices.add(AddrMapEntry("testram", MemRange(ramBase, ramSize, MemAttr(AddrMapProt.RW)))) } trait PeripheryTestRAMBundle { @@ -342,22 +357,16 @@ trait PeripheryTestRAMBundle { trait PeripheryTestRAMModule extends HasPeripheryParameters { implicit val p: Parameters val outer: PeripheryTestRAM - val io: PeripheryTestRAMBundle - val mmioNetwork: Option[TileLinkRecursiveInterconnect] - - val testram = Module(new TileLinkTestRAM(outer.ramSize)(innerMMIOParams)) - testram.io <> mmioNetwork.get.port("testram") } ///// trait PeripheryTestBusMaster extends LazyModule { implicit val p: Parameters - val pBusMasters: RangeManager - val pDevices: ResourceManager[AddrMapEntry] + val peripheryBus: TLXbar - pBusMasters.add("busmaster", 1) - pDevices.add(AddrMapEntry("busmaster", MemSize(4096, MemAttr(AddrMapProt.RW)))) + val fuzzer = LazyModule(new TLFuzzer(5000)) + peripheryBus.node := fuzzer.node } trait PeripheryTestBusMasterBundle { @@ -367,16 +376,4 @@ trait PeripheryTestBusMasterBundle { trait PeripheryTestBusMasterModule { implicit val p: Parameters val outer: PeripheryTestBusMaster - val io: PeripheryTestBusMasterBundle - val mmioNetwork: Option[TileLinkRecursiveInterconnect] - val coreplex: Coreplex - - val busmaster = Module(new groundtest.ExampleBusMaster()(p)) - busmaster.io.mmio <> mmioNetwork.get.port("busmaster") - - { - val r = outer.pBusMasters.range("busmaster") - require(r._2 - r._1 == 1, "RangeManager should return 1 slot") - coreplex.io.slave(r._1) <> busmaster.io.mem - } } diff --git a/src/main/scala/rocketchip/TestHarness.scala b/src/main/scala/rocketchip/TestHarness.scala index 33a26444..1f17d355 100644 --- a/src/main/scala/rocketchip/TestHarness.scala +++ b/src/main/scala/rocketchip/TestHarness.scala @@ -12,11 +12,12 @@ import junctions.NastiConstants._ case object BuildExampleTop extends Field[Parameters => ExampleTop] case object SimMemLatency extends Field[Int] -class TestHarness(implicit val p: Parameters) extends Module with HasAddrMapParameters { +class TestHarness(q: Parameters) extends Module { val io = new Bundle { val success = Bool(OUTPUT) } - val dut = p(BuildExampleTop)(p).module + val dut = q(BuildExampleTop)(q).module + implicit val p = dut.p // This test harness isn't especially flexible yet require(dut.io.mem_clk.isEmpty) @@ -34,7 +35,7 @@ class TestHarness(implicit val p: Parameters) extends Module with HasAddrMapPara int := false if (dut.io.mem_axi.nonEmpty) { - val memSize = addrMap("mem").size + val memSize = p(GlobalAddrMap)("mem").size require(memSize % dut.io.mem_axi.size == 0) for (axi <- dut.io.mem_axi) { val mem = Module(new SimAXIMem(memSize / dut.io.mem_axi.size)) diff --git a/src/main/scala/rocketchip/Top.scala b/src/main/scala/rocketchip/Top.scala index 46610458..f467eee2 100644 --- a/src/main/scala/rocketchip/Top.scala +++ b/src/main/scala/rocketchip/Top.scala @@ -6,7 +6,7 @@ import Chisel._ import cde.{Parameters, Field} import junctions._ import uncore.tilelink._ -import uncore.tilelink2.{LazyModule, LazyModuleImp} +import uncore.tilelink2._ import uncore.devices._ import util.ParameterizedBundle import rocket._ @@ -14,18 +14,41 @@ import rocket.Util._ import coreplex._ // the following parameters will be refactored properly with TL2 -case object GlobalAddrMap extends Field[GlobalVariable[AddrMap]] -case object ConfigString extends Field[GlobalVariable[String]] -case object NCoreplexExtClients extends Field[GlobalVariable[Int]] +case object GlobalAddrMap extends Field[AddrMap] +case object ConfigString extends Field[String] +case object NCoreplexExtClients extends Field[Int] /** Function for building Coreplex */ case object BuildCoreplex extends Field[(Parameters, CoreplexConfig) => Coreplex] /** Base Top with no Periphery */ -abstract class BaseTop(val p: Parameters) extends LazyModule { +abstract class BaseTop(q: Parameters) extends LazyModule { // the following variables will be refactored properly with TL2 val pInterrupts = new RangeManager val pBusMasters = new RangeManager val pDevices = new ResourceManager[AddrMapEntry] + + lazy val c = CoreplexConfig( + nTiles = q(NTiles), + nExtInterrupts = pInterrupts.sum, + nSlaves = pBusMasters.sum, + nMemChannels = q(NMemoryChannels), + hasSupervisor = q(UseVM), + hasExtMMIOPort = true + ) + + lazy val genGlobalAddrMap = GenerateGlobalAddrMap(q, pDevices.get) + private val qWithMap = q.alterPartial({case GlobalAddrMap => genGlobalAddrMap}) + + lazy val genConfigString = GenerateConfigString(qWithMap, c, pDevices.get) + implicit val p = qWithMap.alterPartial({ + case ConfigString => genConfigString + case NCoreplexExtClients => pBusMasters.sum}) + + // Add a peripheral bus + val peripheryBus = LazyModule(new TLXbar) + val legacy = LazyModule(new TLLegacy()(p.alterPartial({ case TLId => "L2toMMIO" }))) + + peripheryBus.node := TLBuffer(TLWidthWidget(TLHintHandler(legacy.node), legacy.tlDataBytes)) } class BaseTopBundle(val p: Parameters, val c: Coreplex) extends ParameterizedBundle()(p) { @@ -35,24 +58,19 @@ class BaseTopBundle(val p: Parameters, val c: Coreplex) extends ParameterizedBun class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, b: Coreplex => B) extends LazyModuleImp(l) { val outer: L = l - val c = CoreplexConfig( - nTiles = p(NTiles), - nExtInterrupts = outer.pInterrupts.sum, - nSlaves = outer.pBusMasters.sum, - nMemChannels = p(NMemoryChannels), - hasSupervisor = p(UseVM), - hasExtMMIOPort = !(outer.pDevices.get.isEmpty && p(ExtMMIOPorts).isEmpty) - ) + val coreplex = p(BuildCoreplex)(p, outer.c) + val io: B = b(coreplex) - def genGlobalAddrMap = GenerateGlobalAddrMap(p, outer.pDevices.get) - def genConfigString = GenerateConfigString(p, c, outer.pDevices.get) + io.success := coreplex.io.success - p(NCoreplexExtClients).assign(outer.pBusMasters.sum) - p(GlobalAddrMap).assign(genGlobalAddrMap) - p(ConfigString).assign(genConfigString) + val mmioNetwork = + Module(new TileLinkRecursiveInterconnect(1, p(GlobalAddrMap).subMap("io:ext"))( + p.alterPartial({ case TLId => "L2toMMIO" }))) + mmioNetwork.io.in.head <> coreplex.io.master.mmio.get + outer.legacy.module.io.legacy <> mmioNetwork.port("TL2") println("Generated Address Map") - for (entry <- p(GlobalAddrMap).get.flatten) { + for (entry <- p(GlobalAddrMap).flatten) { val name = entry.name val start = entry.region.start val end = entry.region.start + entry.region.size - 1 @@ -60,36 +78,27 @@ class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, } println("Generated Configuration String") - println(p(ConfigString).get) - - val coreplex = p(BuildCoreplex)(p, c) - val io: B = b(coreplex) - - io.success := coreplex.io.success - - val mmioNetwork = c.hasExtMMIOPort.option( - Module(new TileLinkRecursiveInterconnect(1, p(GlobalAddrMap).get.subMap("io:ext"))( - p.alterPartial({ case TLId => "L2toMMIO" })))) - mmioNetwork.foreach { _.io.in.head <> coreplex.io.master.mmio.get } + println(p(ConfigString)) + ConfigStringOutput.contents = Some(p(ConfigString)) } /** Example Top with Periphery */ -class ExampleTop(p: Parameters) extends BaseTop(p) - with PeripheryBootROM with PeripheryDebug with PeripheryExtInterrupts with PeripheryAON +class ExampleTop(q: Parameters) extends BaseTop(q) + with PeripheryBootROM with PeripheryDebug with PeripheryExtInterrupts with PeripheryCoreplexLocalInterrupter with PeripheryMasterMem with PeripheryMasterMMIO with PeripherySlave { override lazy val module = Module(new ExampleTopModule(p, this, new ExampleTopBundle(p, _))) } class ExampleTopBundle(p: Parameters, c: Coreplex) extends BaseTopBundle(p, c) - with PeripheryBootROMBundle with PeripheryDebugBundle with PeripheryExtInterruptsBundle with PeripheryAONBundle + with PeripheryBootROMBundle with PeripheryDebugBundle with PeripheryExtInterruptsBundle with PeripheryCoreplexLocalInterrupterBundle with PeripheryMasterMemBundle with PeripheryMasterMMIOBundle with PeripherySlaveBundle class ExampleTopModule[+L <: ExampleTop, +B <: ExampleTopBundle](p: Parameters, l: L, b: Coreplex => B) extends BaseTopModule(p, l, b) - with PeripheryBootROMModule with PeripheryDebugModule with PeripheryExtInterruptsModule with PeripheryAONModule + with PeripheryBootROMModule with PeripheryDebugModule with PeripheryExtInterruptsModule with PeripheryCoreplexLocalInterrupterModule with PeripheryMasterMemModule with PeripheryMasterMMIOModule with PeripherySlaveModule /** Example Top with TestRAM */ -class ExampleTopWithTestRAM(p: Parameters) extends ExampleTop(p) +class ExampleTopWithTestRAM(q: Parameters) extends ExampleTop(q) with PeripheryTestRAM { override lazy val module = Module(new ExampleTopWithTestRAMModule(p, this, new ExampleTopWithTestRAMBundle(p, _))) } diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index fb0d8676..bb32006b 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -64,10 +64,8 @@ object GenerateGlobalAddrMap { new AddrMap(entries) } - lazy val extIOAddrMap = new AddrMap( - pDevicesEntries ++ p(ExtMMIOPorts), - start = BigInt("50000000", 16), - collapse = true) + lazy val tl2AddrMap = new AddrMap(pDevicesEntries, collapse = true) + lazy val extIOAddrMap = new AddrMap(AddrMapEntry("TL2", tl2AddrMap) +: p(ExtMMIOPorts), collapse = true) val memBase = 0x80000000L val memSize = p(ExtMemSize) @@ -83,9 +81,9 @@ object GenerateGlobalAddrMap { object GenerateConfigString { def apply(p: Parameters, c: CoreplexConfig, pDevicesEntries: Seq[AddrMapEntry]) = { - val addrMap = p(GlobalAddrMap).get + val addrMap = p(GlobalAddrMap) val plicAddr = addrMap("io:int:plic").start - val prciAddr = addrMap("io:ext:prci").start + val clint = CoreplexLocalInterrupterConfig(0, addrMap("io:ext:TL2:clint").start) val xLen = p(XLen) val res = new StringBuilder res append "plic {\n" @@ -94,7 +92,7 @@ object GenerateConfigString { res append s" ndevs ${c.plicKey.nDevices};\n" res append "};\n" res append "rtc {\n" - res append s" addr 0x${(prciAddr + PRCI.time).toString(16)};\n" + res append s" addr 0x${clint.timeAddress.toString(16)};\n" res append "};\n" if (addrMap contains "mem") { res append "ram {\n" @@ -117,8 +115,8 @@ object GenerateConfigString { res append s" $i {\n" res append " 0 {\n" res append s" isa $isa;\n" - res append s" timecmp 0x${(prciAddr + PRCI.timecmp(i)).toString(16)};\n" - res append s" ipi 0x${(prciAddr + PRCI.msip(i)).toString(16)};\n" + res append s" timecmp 0x${clint.timecmpAddress(i).toString(16)};\n" + res append s" ipi 0x${clint.msipAddress(i).toString(16)};\n" res append s" plic {\n" res append s" m {\n" res append s" ie 0x${(plicAddr + c.plicKey.enableAddr(i, 'M')).toString(16)};\n" @@ -138,7 +136,7 @@ object GenerateConfigString { } res append "};\n" pDevicesEntries foreach { entry => - val region = addrMap("io:ext:" + entry.name) + val region = addrMap("io:ext:TL2:" + entry.name) res append s"${entry.name} {\n" res append s" addr 0x${region.start.toString(16)};\n" res append s" size 0x${region.size.toString(16)}; \n" @@ -158,8 +156,8 @@ object GenerateBootROM { // for now, have the reset vector jump straight to memory val memBase = ( - if (p(GlobalAddrMap).get contains "mem") p(GlobalAddrMap).get("mem") - else p(GlobalAddrMap).get("io:int:dmem0") + if (p(GlobalAddrMap) contains "mem") p(GlobalAddrMap)("mem") + else p(GlobalAddrMap)("io:int:dmem0") ).start val resetToMemDist = memBase - p(ResetVector) require(resetToMemDist == (resetToMemDist.toInt >> 12 << 12)) @@ -168,6 +166,6 @@ object GenerateBootROM { require(rom.getInt(12) == 0, "Config string address position should not be occupied by code") rom.putInt(12, configStringAddr) - rom.array() ++ (p(ConfigString).get.getBytes.toSeq) + rom.array() ++ (p(ConfigString).getBytes.toSeq) } } diff --git a/src/main/scala/uncore/Builder.scala b/src/main/scala/uncore/Builder.scala index b5605082..a75f5688 100644 --- a/src/main/scala/uncore/Builder.scala +++ b/src/main/scala/uncore/Builder.scala @@ -1,7 +1,7 @@ package uncore import Chisel._ -import cde.{Config, Parameters, ParameterDump, Knob, Dump} +import cde.{Config, Parameters, ParameterDump, Knob, Dump, CDEMatchError} import junctions.PAddrBits import uncore.tilelink._ import uncore.agents._ @@ -85,6 +85,7 @@ class DefaultL2Config extends Config ( case ECCCode => None case AmoAluOperandBits => 64 case SplitMetadata => false + case _ => throw new CDEMatchError // case XLen => 128 }}, knobValues = { @@ -93,22 +94,24 @@ class DefaultL2Config extends Config ( case "NTILES" => 2 case "N_CACHED_TILES" => 2 case "L2_CAPACITY_IN_KB" => 256 + case _ => throw new CDEMatchError } ) class WithPLRU extends Config( (pname, site, here) => pname match { case L2Replacer => () => new SeqPLRU(site(NSets), site(NWays)) + case _ => throw new CDEMatchError }) class PLRUL2Config extends Config(new WithPLRU ++ new DefaultL2Config) -class With1L2Ways extends Config(knobValues = { case "L2_WAYS" => 1 }) -class With2L2Ways extends Config(knobValues = { case "L2_WAYS" => 2 }) -class With4L2Ways extends Config(knobValues = { case "L2_WAYS" => 4 }) +class With1L2Ways extends Config(knobValues = { case "L2_WAYS" => 1; case _ => throw new CDEMatchError }) +class With2L2Ways extends Config(knobValues = { case "L2_WAYS" => 2; case _ => throw new CDEMatchError }) +class With4L2Ways extends Config(knobValues = { case "L2_WAYS" => 4; case _ => throw new CDEMatchError }) -class With1Cached extends Config(knobValues = { case "N_CACHED_TILES" => 1 }) -class With2Cached extends Config(knobValues = { case "N_CACHED_TILES" => 2 }) +class With1Cached extends Config(knobValues = { case "N_CACHED_TILES" => 1; case _ => throw new CDEMatchError }) +class With2Cached extends Config(knobValues = { case "N_CACHED_TILES" => 2; case _ => throw new CDEMatchError }) class W1Cached1WaysConfig extends Config(new With1L2Ways ++ new With1Cached ++ new DefaultL2Config) diff --git a/src/main/scala/uncore/devices/Prci.scala b/src/main/scala/uncore/devices/Prci.scala index d607be91..ff9800dc 100644 --- a/src/main/scala/uncore/devices/Prci.scala +++ b/src/main/scala/uncore/devices/Prci.scala @@ -6,114 +6,84 @@ import Chisel._ import rocket.Util._ import junctions._ import junctions.NastiConstants._ -import uncore.tilelink._ +import uncore.tilelink2._ import uncore.util._ +import scala.math.{min,max} import cde.{Parameters, Field} /** Number of tiles */ case object NTiles extends Field[Int] -class PRCITileIO(implicit p: Parameters) extends Bundle { - val reset = Bool(OUTPUT) - val interrupts = new Bundle { - val mtip = Bool() - val msip = Bool() - } - - override def cloneType: this.type = new PRCITileIO().asInstanceOf[this.type] +class CoreplexLocalInterrupts extends Bundle { + val mtip = Bool() + val msip = Bool() } -object PRCI { - def msip(hart: Int) = hart * msipBytes - def timecmp(hart: Int) = 0x4000 + hart * timecmpBytes - def time = 0xbff8 +case class CoreplexLocalInterrupterConfig(beatBytes: Int, address: BigInt = 0x44000000) { + def msipOffset(hart: Int) = hart * msipBytes + def msipAddress(hart: Int) = address + msipOffset(hart) + def timecmpOffset(hart: Int) = 0x4000 + hart * timecmpBytes + def timecmpAddress(hart: Int) = address + timecmpOffset(hart) + def timeOffset = 0xbff8 + def timeAddress = address + timeOffset def msipBytes = 4 def timecmpBytes = 8 - def size = 0xc000 + def size = 0x10000 +} + +trait MixCoreplexLocalInterrupterParameters { + val params: (CoreplexLocalInterrupterConfig, Parameters) + val c = params._1 + implicit val p = params._2 +} + +trait CoreplexLocalInterrupterBundle extends Bundle with MixCoreplexLocalInterrupterParameters { + val tiles = Vec(p(NTiles), new CoreplexLocalInterrupts).asOutput + val rtcTick = Bool(INPUT) +} + +trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCoreplexLocalInterrupterParameters { + val io: CoreplexLocalInterrupterBundle + + val timeWidth = 64 + val time = Reg(init=UInt(0, width = timeWidth)) + when (io.rtcTick) { time := time + UInt(1) } + + val timecmp = Seq.fill(p(NTiles)) { Reg(UInt(width = timeWidth)) } + val ipi = Seq.fill(p(NTiles)) { RegInit(UInt(0, width = 1)) } + + for ((tile, i) <- io.tiles zipWithIndex) { + tile.msip := ipi(i)(0) + tile.mtip := time >= timecmp(i) + } + + def pad = RegField(8) // each use is a new field + val ipi_fields = ipi.map(r => Seq(RegField(1, r), RegField(7), pad, pad, pad)).flatten + val timecmp_fields = timecmp.map(RegField.bytes(_)).flatten + val time_fields = Seq.fill(c.timeOffset % c.beatBytes)(pad) ++ RegField.bytes(time) + + /* 0000 msip hart 0 + * 0004 msip hart 1 + * 4000 mtimecmp hart 0 lo + * 4004 mtimecmp hart 0 hi + * 4008 mtimecmp hart 1 lo + * 400c mtimecmp hart 1 hi + * bff8 mtime lo + * bffc mtime hi + */ + val ipi_base = 0 + val timecmp_base = c.timecmpOffset(0) / c.beatBytes + val time_base = c.timeOffset / c.beatBytes + + regmap(( + RegField.split(ipi_fields, ipi_base, c.beatBytes) ++ + RegField.split(timecmp_fields, timecmp_base, c.beatBytes) ++ + RegField.split(time_fields, time_base, c.beatBytes)):_*) } /** Power, Reset, Clock, Interrupt */ -class PRCI(implicit val p: Parameters) extends Module - with HasTileLinkParameters - with HasAddrMapParameters { - val io = new Bundle { - val tl = new ClientUncachedTileLinkIO().flip - val tiles = Vec(p(NTiles), new PRCITileIO) - val rtcTick = Bool(INPUT) - } - - val timeWidth = 64 - val timecmp = Reg(Vec(p(NTiles), UInt(width = timeWidth))) - val time = Reg(init=UInt(0, timeWidth)) - when (io.rtcTick) { time := time + UInt(1) } - - val ipi = Reg(init=Vec.fill(p(NTiles))(UInt(0, 32))) - - val acq = Queue(io.tl.acquire, 1) - val addr = acq.bits.full_addr()(log2Ceil(PRCI.size)-1,0) - val read = acq.bits.isBuiltInType(Acquire.getType) - val rdata = Wire(init=UInt(0)) - io.tl.grant.valid := acq.valid - acq.ready := io.tl.grant.ready - io.tl.grant.bits := Grant( - is_builtin_type = Bool(true), - g_type = acq.bits.getBuiltInGrantType(), - client_xact_id = acq.bits.client_xact_id, - manager_xact_id = UInt(0), - addr_beat = UInt(0), - data = rdata) - - when (addr(log2Floor(PRCI.time))) { - require(log2Floor(PRCI.timecmp(p(NTiles)-1)) < log2Floor(PRCI.time)) - rdata := store(Seq(time), acq.bits, io.tl.grant.fire()) - }.elsewhen (addr >= PRCI.timecmp(0)) { - rdata := store(timecmp, acq.bits, io.tl.grant.fire()) - }.otherwise { - rdata := store(ipi, acq.bits, io.tl.grant.fire()) & Fill(tlDataBits/32, UInt(1, 32)) - } - - for ((tile, i) <- io.tiles zipWithIndex) { - tile.interrupts.msip := ipi(i)(0) - tile.interrupts.mtip := time >= timecmp(i) - tile.reset := reset - } - - // TODO generalize these to help other TL slaves - def load(v: Seq[UInt], acq: Acquire): UInt = { - val w = v.head.getWidth - val a = acq.full_addr() - require(isPow2(w) && w >= 8) - if (w > tlDataBits) { - (v(a.extract(log2Ceil(w/8*v.size)-1,log2Ceil(w/8))) >> a.extract(log2Ceil(w/8)-1,log2Ceil(tlDataBytes)))(tlDataBits-1,0) - } else { - val row: Seq[UInt] = for (i <- 0 until v.size by tlDataBits/w) - yield Cat(v.slice(i, i + tlDataBits/w).reverse) - if (row.size == 1) row.head - else row(a(log2Ceil(w/8*v.size)-1,log2Ceil(tlDataBytes))) - } - } - - def store(v: Seq[UInt], acq: Acquire, en: Bool): UInt = { - val w = v.head.getWidth - require(isPow2(w) && w >= 8) - val a = acq.full_addr() - val rdata = load(v, acq) - val wdata = (acq.data & acq.full_wmask()) | (rdata & ~acq.full_wmask()) - when (en && acq.isBuiltInType(Acquire.putType)) { - if (w <= tlDataBits) { - val word = - if (tlDataBits/w >= v.size) UInt(0) - else a(log2Up(w/8*v.size)-1,log2Up(tlDataBytes)) - for (i <- 0 until v.size) when (word === i/(tlDataBits/w)) { - val base = i % (tlDataBits/w) - v(i) := wdata >> (w * (i % (tlDataBits/w))) - } - } else { - val i = a.extract(log2Ceil(w/8*v.size)-1,log2Ceil(w/8)) - val mask = FillInterleaved(tlDataBits, UIntToOH(a.extract(log2Ceil(w/8)-1,log2Ceil(tlDataBytes)))) - v(i) := (wdata & mask) | (v(i) & ~mask) - } - } - rdata - } -} +// Magic TL2 Incantation to create a TL2 Slave +class CoreplexLocalInterrupter(c: CoreplexLocalInterrupterConfig)(implicit val p: Parameters) + extends TLRegisterRouter(c.address, 0, c.size, None, c.beatBytes, false)( + new TLRegBundle((c, p), _) with CoreplexLocalInterrupterBundle)( + new TLRegModule((c, p), _, _) with CoreplexLocalInterrupterModule) diff --git a/src/main/scala/uncore/devices/Rom.scala b/src/main/scala/uncore/devices/Rom.scala index d30812f4..8e31cbe5 100644 --- a/src/main/scala/uncore/devices/Rom.scala +++ b/src/main/scala/uncore/devices/Rom.scala @@ -4,9 +4,46 @@ import Chisel._ import unittest.UnitTest import junctions._ import uncore.tilelink._ +import uncore.tilelink2._ import uncore.util._ import cde.{Parameters, Field} +class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], beatBytes: Int = 4) extends LazyModule +{ + val node = TLManagerNode(beatBytes, TLManagerParameters( + address = List(AddressSet(base, size-1)), + regionType = RegionType.UNCACHED, + supportsGet = TransferSizes(1, beatBytes), + fifoId = Some(0))) + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val in = node.bundleIn + } + + val contents = contentsDelayed + require (contents.size <= size) + + val in = io.in(0) + val edge = node.edgesIn(0) + + val words = (contents ++ Seq.fill(size-contents.size)(0.toByte)).grouped(beatBytes).toSeq + val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8}) + val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes))) + + in.d.valid := in.a.valid + in.a.ready := in.d.ready + + val index = in.a.bits.addr_hi(log2Ceil(size/beatBytes)-1,0) + in.d.bits := edge.AccessAck(in.a.bits, UInt(0), rom(index)) + + // Tie off unused channels + in.b.valid := Bool(false) + in.c.ready := Bool(true) + in.e.ready := Bool(true) + } +} + class ROMSlave(contents: Seq[Byte])(implicit val p: Parameters) extends Module with HasTileLinkParameters with HasAddrMapParameters { diff --git a/src/main/scala/uncore/tilelink2/AddressDecoder.scala b/src/main/scala/uncore/tilelink2/AddressDecoder.scala new file mode 100644 index 00000000..e937d60d --- /dev/null +++ b/src/main/scala/uncore/tilelink2/AddressDecoder.scala @@ -0,0 +1,118 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ +import scala.math.{max,min} + +object AddressDecoder +{ + type Port = Seq[AddressSet] + type Ports = Seq[Port] + type Partition = Ports + type Partitions = Seq[Partition] + + val addressOrder = Ordering.ordered[AddressSet] + val portOrder = Ordering.Iterable(addressOrder) + val partitionOrder = Ordering.Iterable(portOrder) + + // Find the minimum subset of bits needed to disambiguate port addresses. + // ie: inspecting only the bits in the output, you can look at an address + // and decide to which port (outer Seq) the address belongs. + def apply(ports: Ports): BigInt = if (ports.size <= 1) 0 else { + // Every port must have at least one address! + ports.foreach { p => require (!p.isEmpty) } + // Verify the user did not give us an impossible problem + ports.combinations(2).foreach { case Seq(x, y) => + x.foreach { a => y.foreach { b => + require (!a.overlaps(b)) // it must be possible to disambiguate addresses! + } } + } + val maxBits = log2Ceil(ports.map(_.map(_.max).max).max + 1) + val bits = (0 until maxBits).map(BigInt(1) << _).toSeq + val selected = recurse(Seq(ports.map(_.sorted).sorted(portOrder)), bits) + selected.reduceLeft(_ | _) + } + + // A simpler version that works for a Seq[Int] + def apply(keys: Seq[Int]): Int = { + val ports = keys.map(b => Seq(AddressSet(b, 0))) + apply(ports).toInt + } + + // The algorithm has a set of partitions, discriminated by the selected bits. + // Each partion has a set of ports, listing all addresses that lead to that port. + // Seq[Seq[Seq[AddressSet]]] + // ^^^^^^^^^^^^^^^ set of addresses that are routed out this port + // ^^^ the list of ports + // ^^^ cases already distinguished by the selected bits thus far + // + // Solving this problem is NP-hard, so we use a simple greedy heuristic: + // pick the bit which minimizes the number of ports in each partition + // as a secondary goal, reduce the number of AddressSets within a partition + + val bigValue = 100000 + def bitScore(partitions: Partitions): Int = { + val maxPortsPerPartition = partitions.map(_.size).max + val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max + maxPortsPerPartition * bigValue + maxSetsPerPartition + } + + def partitionPort(port: Port, bit: BigInt): (Port, Port) = { + val addr_a = AddressSet(0, ~bit) + val addr_b = AddressSet(bit, ~bit) + // The addresses were sorted, so the filtered addresses are still sorted + val subset_a = port.filter(_.overlaps(addr_a)) + val subset_b = port.filter(_.overlaps(addr_b)) + (subset_a, subset_b) + } + + def partitionPorts(ports: Ports, bit: BigInt): (Ports, Ports) = { + val partitioned_ports = ports.map(p => partitionPort(p, bit)) + // because partitionPort dropped AddresSets, the ports might no longer be sorted + val case_a_ports = partitioned_ports.map(_._1).filter(!_.isEmpty).sorted(portOrder) + val case_b_ports = partitioned_ports.map(_._2).filter(!_.isEmpty).sorted(portOrder) + (case_a_ports, case_b_ports) + } + + def partitionPartitions(partitions: Partitions, bit: BigInt): Partitions = { + val partitioned_partitions = partitions.map(p => partitionPorts(p, bit)) + val case_a_partitions = partitioned_partitions.map(_._1) + val case_b_partitions = partitioned_partitions.map(_._2) + val new_partitions = (case_a_partitions ++ case_b_partitions).sorted(partitionOrder) + // Prevent combinational memory explosion; if two partitions are equal, keep only one + // Note: AddressSets in a port are sorted, and ports in a partition are sorted. + // This makes it easy to structurally compare two partitions for equality + val keep = (new_partitions.init zip new_partitions.tail) filter { case (a,b) => partitionOrder.compare(a,b) != 0 } map { _._2 } + new_partitions.head +: keep + } + + // requirement: ports have sorted addresses and are sorted lexicographically + val debug = false + def recurse(partitions: Partitions, bits: Seq[BigInt]): Seq[BigInt] = { + if (debug) { + println("Partitioning:") + partitions.foreach { partition => + println(" Partition:") + partition.foreach { port => + print(" ") + port.foreach { a => print(s" ${a}") } + println("") + } + } + } + val candidates = bits.map { bit => + val result = partitionPartitions(partitions, bit) + val score = bitScore(result) + (score, bit, result) + } + val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Int, BigInt, Partitions), Int](_._1)) + if (debug) println("=> Selected bit 0x%x".format(bestBit)) + if (bestScore < 2*bigValue) { + if (debug) println("---") + Seq(bestBit) + } else { + bestBit +: recurse(bestPartitions, bits.filter(_ != bestBit)) + } + } +} diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index 32fd539b..30775583 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -146,7 +146,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten val dFragnum = out.d.bits.source(fragmentBits-1, 0) val dFirst = acknum === UInt(0) val dsizeOH = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1) - val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Ceil(maxDownSize)) + val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Up(maxDownSize)) val dHasData = edgeOut.hasData(out.d.bits) // calculate new acknum @@ -209,7 +209,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten val aOrig = in.a.bits.size val aFrag = Mux(aOrig > limit, limit, aOrig) val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize)) - val aFragOH1 = UIntToOH1(aFrag, log2Ceil(maxDownSize)) + val aFragOH1 = UIntToOH1(aFrag, log2Up(maxDownSize)) val aHasData = node.edgesIn(0).hasData(in.a.bits) val aMask = Mux(aHasData, UInt(0), aFragOH1) diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index 3d57f03c..4e1bd12d 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -32,22 +32,36 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_) if (supportManagers && !smartManagers) { + // State of the Hint bypass + val counter = RegInit(UInt(0, width = log2Up(edgeOut.manager.maxTransfer/edgeOut.manager.beatBytes))) + val hintHoldsD = RegInit(Bool(false)) + val outerHoldsD = counter =/= UInt(0) + // Only one of them can hold it + assert (!hintHoldsD || !outerHoldsD) + + // Count outer D beats + val beats1 = edgeOut.numBeats1(out.d.bits) + when (out.d.fire()) { counter := Mux(outerHoldsD, counter - UInt(1), beats1) } + + // Who wants what? val address = edgeIn.address(in.a.bits) val handleA = if (passthrough) !edgeOut.manager.supportsHint(address, edgeIn.size(in.a.bits)) else Bool(true) - val bypassD = handleA && in.a.bits.opcode === TLMessages.Hint + val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint + val hintWantsD = in.a.valid && hintBitsAtA + val outerWantsD = out.d.valid // Prioritize existing D traffic over HintAck (and finish multibeat xfers) - val beats1 = edgeOut.numBeats1(out.d.bits) - val counter = RegInit(UInt(0, width = log2Up(edgeOut.manager.maxTransfer/edgeOut.manager.beatBytes))) - val first = counter === UInt(0) - when (out.d.fire()) { counter := Mux(first, beats1, counter - UInt(1)) } + val hintWinsD = hintHoldsD || (!outerHoldsD && !outerWantsD) + hintHoldsD := hintWantsD && hintWinsD && !in.d.ready + // Hint can only hold D b/c it still wants it from last cycle + assert (!hintHoldsD || hintWantsD) - in.d.valid := out.d.valid || (bypassD && in.a.valid && first) - out.d.ready := in.d.ready - in.d.bits := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address))) + in.d.valid := Mux(hintWinsD, hintWantsD, outerWantsD) + in.d.bits := Mux(hintWinsD, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address)), out.d.bits) + out.d.ready := in.d.ready && !hintHoldsD - in.a.ready := Mux(bypassD, in.d.ready && first && !out.d.valid, out.a.ready) - out.a.valid := in.a.valid && !bypassD + in.a.ready := Mux(hintBitsAtA, hintWinsD && in.d.ready, out.a.ready) + out.a.valid := in.a.valid && !hintBitsAtA out.a.bits := in.a.bits } else { out.a.valid := in.a.valid @@ -60,21 +74,35 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f } if (supportClients && !smartClients) { + // State of the Hint bypass + val counter = RegInit(UInt(0, width = log2Up(edgeIn.client.maxTransfer/edgeIn.manager.beatBytes))) + val hintHoldsC = RegInit(Bool(false)) + val innerHoldsC = counter =/= UInt(0) + // Only one of them can hold it + assert (!hintHoldsC || !innerHoldsC) + + // Count inner C beats + val beats1 = edgeIn.numBeats1(in.c.bits) + when (in.c.fire()) { counter := Mux(innerHoldsC, counter - UInt(1), beats1) } + + // Who wants what? val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true) - val bypassC = handleB && out.b.bits.opcode === TLMessages.Hint + val hintBitsAtB = handleB && out.b.bits.opcode === TLMessages.Hint + val hintWantsC = out.b.valid && hintBitsAtB + val innerWantsC = in.c.valid // Prioritize existing C traffic over HintAck (and finish multibeat xfers) - val beats1 = edgeIn.numBeats1(in.c.bits) - val counter = RegInit(UInt(0, width = log2Up(edgeIn.client.maxTransfer/edgeIn.manager.beatBytes))) - val first = counter === UInt(0) - when (in.c.fire()) { counter := Mux(first, beats1, counter - UInt(1)) } + val hintWinsC = hintHoldsC || (!innerHoldsC && !innerWantsC) + hintHoldsC := hintWantsC && hintWinsC && !out.c.ready + // Hint can only hold C b/c it still wants it from last cycle + assert (!hintHoldsC || hintWantsC) - out.c.valid := in.c.valid || (bypassC && in.b.valid && first) - in.c.ready := out.c.ready - out.c.bits := Mux(in.c.valid, in.c.bits, edgeOut.HintAck(out.b.bits)) + out.c.valid := Mux(hintWinsC, hintWantsC, innerWantsC) + out.c.bits := Mux(hintWinsC, edgeOut.HintAck(out.b.bits), in.c.bits) + in.c.ready := out.c.ready && !hintHoldsC - out.b.ready := Mux(bypassC, out.c.ready && first && !in.c.valid, in.b.ready) - in.b.valid := out.b.valid && !bypassC + out.b.ready := Mux(hintBitsAtB, hintWinsC && out.c.ready, in.b.ready) + in.b.valid := out.b.valid && !hintBitsAtB in.b.bits := out.b.bits } else if (bce) { in.b.valid := out.b.valid diff --git a/src/main/scala/uncore/tilelink2/Legacy.scala b/src/main/scala/uncore/tilelink2/Legacy.scala index 99ccc2fe..e55f8c05 100644 --- a/src/main/scala/uncore/tilelink2/Legacy.scala +++ b/src/main/scala/uncore/tilelink2/Legacy.scala @@ -49,7 +49,7 @@ class TLLegacy(implicit val p: Parameters) extends LazyModule with HasTileLinkPa // During conversion from TL Legacy, we won't support Acquire // Must be able to fit TL2 sink_id into TL legacy - require ((1 << tlManagerXactIdBits) >= edge.manager.endSinkId) + require ((1 << tlManagerXactIdBits) >= edge.manager.endSinkId || !edge.manager.anySupportAcquire) val out = io.out(0) out.a.valid := io.legacy.acquire.valid @@ -78,6 +78,8 @@ class TLLegacy(implicit val p: Parameters) extends LazyModule with HasTileLinkPa MemoryOpConstants.M_XA_MINU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MINU)._2, MemoryOpConstants.M_XA_MAXU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MAXU)._2)) } else { + // If no managers support atomics, assert fail if TL1 asks for them + assert (!io.legacy.acquire.valid || io.legacy.acquire.bits.a_type =/= Acquire.putAtomicType) Wire(new TLBundleA(edge.bundle)) } diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index ffd4c5f6..9ab63c9c 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -406,7 +406,7 @@ object TLMonitor val last_v = RegNext(irr.valid, Bool(false)) val last_r = RegNext(irr.ready, Bool(false)) val last_b = RegNext(irr.bits) - val bits_changed = irr.bits.toBits === last_b.toBits + val bits_changed = irr.bits.asUInt === last_b.asUInt when (last_v && !last_r) { assert(irr.valid, s"${irr.bits.channelName} had contents that were revoked by the supplier (valid lowered)" + extra) diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index 025d137b..a0de1a84 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -76,7 +76,7 @@ class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( def connectOut = bundleOut def connectIn = bundleIn - protected[tilelink2] def := (y: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo) = { + def := (y: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo) = { val x = this // x := y val info = sourceLine(sourceInfo, " at ", "") require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index a6891af5..6423db3c 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -63,7 +63,7 @@ case class TransferSizes(min: Int, max: Int) def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max) def intersect(x: TransferSizes) = - if (x.max < min || min < x.max) TransferSizes.none + if (x.max < min || max < x.min) TransferSizes.none else TransferSizes(scala.math.max(min, x.min), scala.math.min(max, x.max)) } @@ -78,7 +78,7 @@ object TransferSizes { // Base is the base address, and mask are the bits consumed by the manager // e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff // e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ... -case class AddressSet(base: BigInt, mask: BigInt) +case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] { // Forbid misaligned base address (and empty sets) require ((base & mask) == 0) @@ -97,12 +97,24 @@ case class AddressSet(base: BigInt, mask: BigInt) // A strided slave serves discontiguous ranges def strided = alignment1 != mask + + // AddressSets have one natural Ordering (the containment order) + def compare(x: AddressSet) = { + val primary = (this.base - x.base).signum // smallest address first + val secondary = (x.mask - this.mask).signum // largest mask first + if (primary != 0) primary else secondary + } + + // We always want to see things in hex + override def toString() = "AddressSet(0x%x, 0x%x)".format(base, mask) } case class TLManagerParameters( address: Seq[AddressSet], - sinkId: IdRange = IdRange(0, 1), - regionType: RegionType.T = RegionType.GET_EFFECTS, + sinkId: IdRange = IdRange(0, 1), + regionType: RegionType.T = RegionType.GET_EFFECTS, + executable: Boolean = false, // processor can execute from this memory + nodePath: Seq[TLBaseNode] = Seq(), // Supports both Acquire+Release+Finish of these sizes supportsAcquire: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none, @@ -183,6 +195,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes def findFifoId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) def hasFifoId(address: UInt) = Mux1H(find(address), managers.map(m => Bool(m.fifoId.isDefined))) + lazy val addressMask = AddressDecoder(managers.map(_.address)) // !!! need a cheaper version of find, where we assume a valid address match exists // Does this Port manage this ID/address? @@ -207,7 +220,8 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes } case class TLClientParameters( - sourceId: IdRange = IdRange(0,1), + sourceId: IdRange = IdRange(0,1), + nodePath: Seq[TLBaseNode] = Seq(), // Supports both Probe+Grant of these sizes supportsProbe: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none, diff --git a/src/main/scala/uncore/tilelink2/RegField.scala b/src/main/scala/uncore/tilelink2/RegField.scala index fa258b5c..2b372208 100644 --- a/src/main/scala/uncore/tilelink2/RegField.scala +++ b/src/main/scala/uncore/tilelink2/RegField.scala @@ -105,6 +105,28 @@ object RegField bb.d := data Bool(true) })) + + // Split a large register into a sequence of byte fields + // The bytes can be individually written, as they are one byte per field + def bytes(x: UInt): Seq[RegField] = { + require (x.getWidth % 8 == 0) + val bytes = Seq.tabulate(x.getWidth/8) { i => x(8*(i+1)-1, 8*i) } + val wires = bytes.map { b => Wire(init = b) } + x := Cat(wires.reverse) + Seq.tabulate(x.getWidth/8) { i => + RegField(8, bytes(i), RegWriteFn { (valid, data) => + when (valid) { wires(i) := data } + Bool(true) + }) + } + } + + // Divide a long sequence of RegFields into a maximum sized registers + // Your input RegFields may not cross a beatBytes boundary! + def split(fields: Seq[RegField], base: Int, beatBytes: Int = 4): Seq[RegField.Map] = { + val offsets = fields.map(_.width).scanLeft(0)(_ + _).init + (offsets zip fields).groupBy(_._1 / (beatBytes*8)).toList.map(r => (r._1 + base, r._2.map(_._2))) + } } trait HasRegMap diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index 88882737..b20937f8 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -28,8 +28,8 @@ class RegMapperOutput(params: RegMapperParams) extends GenericParameterizedBundl object RegMapper { // Create a generic register-based device - def apply(bytes: Int, concurrency: Option[Int], in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { - val regmap = mapping.toList + def apply(bytes: Int, concurrency: Option[Int], undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { + val regmap = mapping.toList.filter(!_._2.isEmpty) require (!regmap.isEmpty) // Ensure no register appears twice @@ -37,15 +37,31 @@ object RegMapper require (reg1 != reg2) } - // Flatten the regmap into (Reg:Int, Offset:Int, field:RegField) - val flat = regmap.map { case (reg, fields) => - val offsets = fields.scanLeft(0)(_ + _.width).init - (offsets zip fields) map { case (o, f) => (reg, o, f) } - }.flatten - require (!flat.isEmpty) + // Convert to and from Bits + def toBits(x: Int, tail: List[Boolean] = List.empty): List[Boolean] = + if (x == 0) tail.reverse else toBits(x >> 1, ((x & 1) == 1) :: tail) + def ofBits(bits: List[Boolean]) = bits.foldRight(0){ case (x,y) => (if (x) 1 else 0) | y << 1 } + // Find the minimal mask that can decide the register map + val mask = AddressDecoder(regmap.map(_._1)) + val maskFilter = toBits(mask) + val maskBits = maskFilter.filter(x => x).size + + // Calculate size and indexes into the register map val endIndex = 1 << log2Ceil(regmap.map(_._1).max+1) val params = RegMapperParams(log2Up(endIndex), bytes, in.bits.params.extraBits) + val regSize = 1 << maskBits + def regIndexI(x: Int) = ofBits((maskFilter zip toBits(x)).filter(_._1).map(_._2)) + def regIndexU(x: UInt) = if (maskBits == 0) UInt(0) else + Cat((maskFilter zip x.toBools).filter(_._1).map(_._2).reverse) + + // Flatten the regmap into (RegIndex:Int, Offset:Int, field:RegField) + val flat = regmap.map { case (reg, fields) => + val offsets = fields.scanLeft(0)(_ + _.width).init + val index = regIndexI(reg) + // println("mapping 0x%x -> 0x%x for 0x%x/%d".format(reg, index, mask, maskBits)) + (offsets zip fields) map { case (o, f) => (index, o, f) } + }.flatten val out = Wire(Irrevocable(new RegMapperOutput(params))) val front = Wire(Irrevocable(new RegMapperInput(params))) @@ -69,13 +85,13 @@ object RegMapper val woready = Wire(Vec(flat.size, Bool())) // Per-register list of all control signals needed for data to flow - val rifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val wifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val rofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val wofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } + val rifire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val wifire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val rofire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val wofire = Array.tabulate(regSize) { i => Seq(Bool(true)) } // The output values for each register - val dataOut = Array.tabulate(endIndex) { _ => UInt(0) } + val dataOut = Array.tabulate(regSize) { _ => UInt(0) } // Which bits are touched? val frontMask = FillInterleaved(8, front.bits.mask) @@ -110,8 +126,10 @@ object RegMapper val wifireMux = Vec(wifire.map(_.reduce(_ && _))) val rofireMux = Vec(rofire.map(_.reduce(_ && _))) val wofireMux = Vec(wofire.map(_.reduce(_ && _))) - val iready = Mux(front.bits.read, rifireMux(front.bits.index), wifireMux(front.bits.index)) - val oready = Mux(back .bits.read, rofireMux(back .bits.index), wofireMux(back .bits.index)) + val iindex = regIndexU(front.bits.index) + val oindex = regIndexU(back .bits.index) + val iready = Mux(front.bits.read, rifireMux(iindex), wifireMux(iindex)) + val oready = Mux(back .bits.read, rofireMux(oindex), wofireMux(oindex)) // Connect the pipeline in.ready := front.ready && iready @@ -120,11 +138,11 @@ object RegMapper out.valid := back.valid && oready // Which register is touched? - val frontSel = UIntToOH(front.bits.index) - val backSel = UIntToOH(back.bits.index) + val frontSel = UIntToOH(iindex) + val backSel = UIntToOH(oindex) // Include the per-register one-hot selected criteria - for (reg <- 0 until endIndex) { + for (reg <- 0 until regSize) { rifire(reg) = (in.valid && front.ready && front.bits.read && frontSel(reg)) +: rifire(reg) wifire(reg) = (in.valid && front.ready && !front.bits.read && frontSel(reg)) +: wifire(reg) rofire(reg) = (back.valid && out.ready && back .bits.read && backSel (reg)) +: rofire(reg) @@ -141,7 +159,7 @@ object RegMapper } out.bits.read := back.bits.read - out.bits.data := Vec(dataOut)(back.bits.index) + out.bits.data := Vec(dataOut)(oindex) out.bits.extra := back.bits.extra (endIndex, out) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index ce46fc11..7cf6184d 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -4,7 +4,7 @@ package uncore.tilelink2 import Chisel._ -class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) +class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4, undefZero: Boolean = true) extends TLManagerNode(beatBytes, TLManagerParameters( address = Seq(address), supportsGet = TransferSizes(1, beatBytes), @@ -25,7 +25,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB val baseEnd = 0 val (sizeEnd, sizeOff) = (edge.bundle.sizeBits + baseEnd, baseEnd) val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd) - val (addrLoEnd, addrLoOff) = (log2Ceil(beatBytes) + sourceEnd, sourceEnd) + val (addrLoEnd, addrLoOff) = (log2Up(beatBytes) + sourceEnd, sourceEnd) val params = RegMapperParams(log2Up(address.mask+1), beatBytes, addrLoEnd) val in = Wire(Decoupled(new RegMapperInput(params))) @@ -36,7 +36,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB in.bits.extra := Cat(edge.addr_lo(a.bits), a.bits.source, a.bits.size) // Invoke the register map builder - val (endIndex, out) = RegMapper(beatBytes, concurrency, in, mapping:_*) + val (endIndex, out) = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) // All registers must fit inside the device address space require (address.mask >= (endIndex-1)*beatBytes) @@ -67,17 +67,17 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB object TLRegisterNode { - def apply(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) = - new TLRegisterNode(address, concurrency, beatBytes) + def apply(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4, undefZero: Boolean = true) = + new TLRegisterNode(address, concurrency, beatBytes, undefZero) } // These convenience methods below combine to make it possible to create a TL2 // register mapped device from a totally abstract register mapped device. // See GPIO.scala in this directory for an example -abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int) extends LazyModule +abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int, undefZero: Boolean) extends LazyModule { - val node = TLRegisterNode(address, concurrency, beatBytes) + val node = TLRegisterNode(address, concurrency, beatBytes, undefZero) val intnode = IntSourceNode(name + s" @ ${address.base}", interrupts) } @@ -100,10 +100,10 @@ class TLRegModule[P, B <: TLRegBundleBase](val params: P, bundleBuilder: => B, r } class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp] - (base: BigInt, interrupts: Int = 0, size: BigInt = 4096, concurrency: Option[Int] = None, beatBytes: Int = 4) + (val base: BigInt, val interrupts: Int = 0, val size: BigInt = 4096, val concurrency: Option[Int] = None, val beatBytes: Int = 4, undefZero: Boolean = true) (bundleBuilder: TLRegBundleArg => B) (moduleBuilder: (=> B, TLRegisterRouterBase) => M) - extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes) + extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero) { require (isPow2(size)) // require (size >= 4096) ... not absolutely required, but highly recommended diff --git a/src/main/scala/uncore/tilelink2/SRAM.scala b/src/main/scala/uncore/tilelink2/SRAM.scala index 03e7385d..2970eb78 100644 --- a/src/main/scala/uncore/tilelink2/SRAM.scala +++ b/src/main/scala/uncore/tilelink2/SRAM.scala @@ -9,6 +9,7 @@ class TLRAM(address: AddressSet, beatBytes: Int = 4) extends LazyModule val node = TLManagerNode(beatBytes, TLManagerParameters( address = List(address), regionType = RegionType.UNCACHED, + executable = true, supportsGet = TransferSizes(1, beatBytes), supportsPutPartial = TransferSizes(1, beatBytes), supportsPutFull = TransferSizes(1, beatBytes), diff --git a/src/main/scala/uncore/util/BlackBoxRegs.scala b/src/main/scala/uncore/util/BlackBoxRegs.scala index 23271f9b..439adab0 100644 --- a/src/main/scala/uncore/util/BlackBoxRegs.scala +++ b/src/main/scala/uncore/util/BlackBoxRegs.scala @@ -5,13 +5,17 @@ import Chisel._ import cde.{Parameters} /** This black-boxes an Async Reset - * Reg. + * (or Set) + * Register. * * Because Chisel doesn't support * parameterized black boxes, * we unfortunately have to * instantiate a number of these. * + * We also have to hard-code the set/ + * reset behavior. + * * Do not confuse an asynchronous * reset signal with an asynchronously * reset reg. You should still @@ -22,29 +26,25 @@ import cde.{Parameters} * @param q Data Output * @param clk Clock Input * @param rst Reset Input + * @param en Write Enable Input * - * @param init Value to write at Reset. - * This is a constant, - * but this construction - * will likely make backend flows - * and lint tools unhappy. - * */ -class AsyncResetReg extends BlackBox { +abstract class AbstractBBReg extends BlackBox { val io = new Bundle { val d = Bool(INPUT) val q = Bool(OUTPUT) + val en = Bool(INPUT) val clk = Clock(INPUT) val rst = Bool(INPUT) - - val init = Bool(INPUT) } } +class AsyncResetReg extends AbstractBBReg +class AsyncSetReg extends AbstractBBReg class SimpleRegIO(val w: Int) extends Bundle{ @@ -55,29 +55,56 @@ class SimpleRegIO(val w: Int) extends Bundle{ } -class AsyncResetRegVec(val w: Int, val init: Int) extends Module { +class AsyncResetRegVec(val w: Int, val init: BigInt) extends Module { val io = new SimpleRegIO(w) - val bb_q = Wire(UInt(width = w)) - val bb_d = Wire(UInt(width = w)) + val bb_d = Mux(io.en, io.d, io.q) - val init_val = Wire(UInt(width = w)) - init_val := UInt(init, width = w) + val async_regs: List[AbstractBBReg] = List.tabulate(w)( + i => Module ( + if (((init >> i) % 2) > 0) + new AsyncSetReg + else + new AsyncResetReg) + ) - val async_regs = List.fill(w)(Module (new AsyncResetReg)) + io.q := async_regs.map(_.io.q).asUInt - bb_q := (async_regs.map(_.io.q)).asUInt() - bb_d := Mux(io.en , io.d , bb_q) - - io.q := bb_q - - for ((reg, idx) <- async_regs.zipWithIndex) { reg.io.clk := clock reg.io.rst := reset - reg.io.init := init_val(idx) - reg.io.d := bb_d(idx) + reg.io.d := bb_d(idx) + reg.io.en := io.en } } + +object AsyncResetReg { + def apply(d: Bool, clk: Clock, rst: Bool, init: Boolean): Bool = { + val reg: AbstractBBReg = + if (init) Module (new AsyncSetReg) + else Module(new AsyncResetReg) + reg.io.d := d + reg.io.clk := clk + reg.io.rst := rst + reg.io.en := Bool(true) + reg.io.q + } + + def apply(d: Bool, clk: Clock, rst: Bool): Bool = apply(d, clk, rst, false) + + def apply(updateData: UInt, resetData: BigInt, enable: Bool): UInt = { + val w = updateData.getWidth max resetData.bitLength + val reg = Module(new AsyncResetRegVec(w, resetData)) + reg.io.d := updateData + reg.io.en := enable + reg.io.q + } + + def apply(updateData: UInt, resetData: BigInt): UInt = apply(updateData, resetData, enable=Bool(true)) + + def apply(updateData: UInt, enable: Bool): UInt = apply(updateData, resetData=BigInt(0), enable) + + def apply(updateData: UInt): UInt = apply(updateData, resetData=BigInt(0), enable=Bool(true)) +} diff --git a/src/main/scala/unittest/TestHarness.scala b/src/main/scala/unittest/TestHarness.scala index a394e431..d689c876 100644 --- a/src/main/scala/unittest/TestHarness.scala +++ b/src/main/scala/unittest/TestHarness.scala @@ -11,10 +11,10 @@ class TestHarness(implicit val p: Parameters) extends Module { val success = Bool(OUTPUT) } - p(NCoreplexExtClients).assign(0) - p(ConfigString).assign("") - - val l1params = p.alterPartial({ case uncore.tilelink.TLId => "L1toL2" }) + val l1params = p.alterPartial({ + case NCoreplexExtClients => 0 + case ConfigString => "" + case uncore.tilelink.TLId => "L1toL2" }) val tests = Module(new UnitTestSuite()(l1params)) io.success := tests.io.finished diff --git a/vsim/Makefrag b/vsim/Makefrag index 31ee449b..f06fc032 100644 --- a/vsim/Makefrag +++ b/vsim/Makefrag @@ -8,10 +8,11 @@ bb_vsrcs = $(base_dir)/vsrc/DebugTransportModuleJtag.v \ $(base_dir)/vsrc/jtag_vpi.v \ $(base_dir)/vsrc/AsyncMailbox.v \ $(base_dir)/vsrc/AsyncResetReg.v \ + $(base_dir)/vsrc/AsyncSetReg.v \ $(base_dir)/vsrc/ClockDivider.v \ - $(base_dir)/vsrc/ClockToSignal.v \ - $(base_dir)/vsrc/SignalToClock.v \ - + $(base_dir)/vsrc/ClockToSignal.v \ + $(base_dir)/vsrc/SignalToClock.v \ + sim_vsrcs = \ $(generated_dir)/$(MODEL).$(CONFIG).v \ diff --git a/vsrc/AsyncResetReg.v b/vsrc/AsyncResetReg.v index 3adf16b1..4775f6c6 100644 --- a/vsrc/AsyncResetReg.v +++ b/vsrc/AsyncResetReg.v @@ -8,6 +8,8 @@ * we unfortunately have to * instantiate a number of these. * + * We also have to hard-code the set/reset. + * * Do not confuse an asynchronous * reset signal with an asynchronously * reset reg. You should still @@ -18,32 +20,25 @@ * @param q Data Output * @param clk Clock Input * @param rst Reset Input + * @param en Write Enable Input * - * @param init Value to write at Reset. - * This is a constant, - * but this construction - * will likely make backend flows - * and lint tools unhappy. - * */ module AsyncResetReg ( input d, output reg q, + input en, input clk, - input rst, - - input init); - + input rst); + always @(posedge clk or posedge rst) begin if (rst) begin - q <= init; - end else begin + q <= 1'b0; + end else if (en) begin q <= d; end - end diff --git a/vsrc/AsyncSetReg.v b/vsrc/AsyncSetReg.v new file mode 100644 index 00000000..ffafdab3 --- /dev/null +++ b/vsrc/AsyncSetReg.v @@ -0,0 +1,47 @@ + + +/** This black-boxes an Async Set + * Reg. + * + * Because Chisel doesn't support + * parameterized black boxes, + * we unfortunately have to + * instantiate a number of these. + * + * We also have to hard-code the set/reset. + * + * Do not confuse an asynchronous + * reset signal with an asynchronously + * reset reg. You should still + * properly synchronize your reset + * deassertion. + * + * @param d Data input + * @param q Data Output + * @param clk Clock Input + * @param rst Reset Input + * @param en Write Enable Input + * + */ + +module AsyncSetReg ( + input d, + output reg q, + input en, + + input clk, + input rst); + + always @(posedge clk or posedge rst) begin + + if (rst) begin + q <= 1'b1; + end else if (en) begin + q <= d; + end + + end + + +endmodule // AsyncSetReg +