Merge pull request #310 from ucb-bar/rxia-testharness-refactor
Resolve merge conflicts in testharness-refactor
This commit is contained in:
commit
7ff7076dab
2
chisel3
2
chisel3
@ -1 +1 @@
|
||||
Subproject commit 2ff229dac5f915e7f583cbf9cc8118674a4e52a5
|
||||
Subproject commit dda64c1dee16b5da15ac690bd3cd6759c3d5c032
|
2
firrtl
2
firrtl
@ -1 +1 @@
|
||||
Subproject commit 7c38199ce7a5d9dd7e27ffbb9b2b2770b972ed94
|
||||
Subproject commit 726c808375fe513c70376bf05e76dd938e578bf9
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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) =>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
@ -183,11 +178,13 @@ class TinyConfig extends Config(
|
||||
class WithAsyncDebug extends Config (
|
||||
(pname, site, here) => pname match {
|
||||
case AsyncDebugBus => true
|
||||
case _ => throw new CDEMatchError
|
||||
}
|
||||
)
|
||||
|
||||
class WithJtagDTM extends Config (
|
||||
(pname, site, here) => pname match {
|
||||
case IncludeJtagDTM => true
|
||||
case _ => throw new CDEMatchError
|
||||
}
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
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
|
||||
writeOutputFile(td, s"${names.configs}.cfg", params(ConfigString).get) // String for software
|
||||
ConfigStringOutput.contents.foreach(c => writeOutputFile(td, s"${names.configs}.cfg", c)) // String for software
|
||||
}
|
||||
}
|
||||
|
||||
object RocketChipGenerator extends Generator {
|
||||
Driver.dumpFirrtl(circuit, Some(new File(td, s"$longName.fir"))) // FIRRTL
|
||||
writeOutputFiles()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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, _)))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
class CoreplexLocalInterrupts extends Bundle {
|
||||
val mtip = Bool()
|
||||
val msip = Bool()
|
||||
}
|
||||
|
||||
override def cloneType: this.type = new PRCITileIO().asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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 {
|
||||
|
118
src/main/scala/uncore/tilelink2/AddressDecoder.scala
Normal file
118
src/main/scala/uncore/tilelink2/AddressDecoder.scala
Normal file
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
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?
|
||||
@ -208,6 +221,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
||||
|
||||
case class TLClientParameters(
|
||||
sourceId: IdRange = IdRange(0,1),
|
||||
nodePath: Seq[TLBaseNode] = Seq(),
|
||||
// Supports both Probe+Grant of these sizes
|
||||
supportsProbe: TransferSizes = TransferSizes.none,
|
||||
supportsArithmetic: TransferSizes = TransferSizes.none,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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 init Value to write at Reset.
|
||||
* This is a constant,
|
||||
* but this construction
|
||||
* will likely make backend flows
|
||||
* and lint tools unhappy.
|
||||
* @param en Write Enable Input
|
||||
*
|
||||
*/
|
||||
|
||||
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.fill(w)(Module (new AsyncResetReg))
|
||||
|
||||
bb_q := (async_regs.map(_.io.q)).asUInt()
|
||||
bb_d := Mux(io.en , io.d , bb_q)
|
||||
|
||||
io.q := bb_q
|
||||
val async_regs: List[AbstractBBReg] = List.tabulate(w)(
|
||||
i => Module (
|
||||
if (((init >> i) % 2) > 0)
|
||||
new AsyncSetReg
|
||||
else
|
||||
new AsyncResetReg)
|
||||
)
|
||||
|
||||
io.q := async_regs.map(_.io.q).asUInt
|
||||
|
||||
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.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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -8,6 +8,7 @@ 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 \
|
||||
|
@ -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 init Value to write at Reset.
|
||||
* This is a constant,
|
||||
* but this construction
|
||||
* will likely make backend flows
|
||||
* and lint tools unhappy.
|
||||
* @param en Write Enable Input
|
||||
*
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
47
vsrc/AsyncSetReg.v
Normal file
47
vsrc/AsyncSetReg.v
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user