Scalafmt & rename & update doc
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
//package freechips.rocketchip.rocket
|
|
||||||
package freechips.rocketchip.tilelink
|
package freechips.rocketchip.tilelink
|
||||||
|
|
||||||
import chisel3._
|
import chisel3._
|
||||||
@@ -9,16 +8,12 @@ import freechips.rocketchip.diplomacy._
|
|||||||
import freechips.rocketchip.tilelink._
|
import freechips.rocketchip.tilelink._
|
||||||
import org.chipsalliance.cde.config.{Parameters, Field}
|
import org.chipsalliance.cde.config.{Parameters, Field}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Param and Key are used during SoC Generation
|
|
||||||
|
|
||||||
case class L1SystemParam(wordSize: Int = 16, busWidthInBytes: Int = 8)
|
case class L1SystemParam(wordSize: Int = 16, busWidthInBytes: Int = 8)
|
||||||
case object L1SystemKey extends Field[Option[L1SystemConfig]](None /*default*/)
|
case object L1SystemKey extends Field[Option[L1SystemConfig]](None /*default*/ )
|
||||||
|
|
||||||
case class L1SystemConfig(
|
case class L1SystemConfig(
|
||||||
numBanks: Int,
|
numBanks: Int,
|
||||||
wordSize: Int, //This is the read/write granularity of the L1 cache
|
wordSize: Int, // This is the read/write granularity of the L1 cache
|
||||||
cacheLineSize: Int,
|
cacheLineSize: Int,
|
||||||
coreTagWidth: Int,
|
coreTagWidth: Int,
|
||||||
writeInfoReqQSize: Int,
|
writeInfoReqQSize: Int,
|
||||||
@@ -32,7 +27,8 @@ case class L1SystemConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object defaultL1SystemConfig extends L1SystemConfig(
|
object defaultL1SystemConfig
|
||||||
|
extends L1SystemConfig(
|
||||||
numBanks = 4,
|
numBanks = 4,
|
||||||
wordSize = 16,
|
wordSize = 16,
|
||||||
cacheLineSize = 16,
|
cacheLineSize = 16,
|
||||||
@@ -40,58 +36,46 @@ object defaultL1SystemConfig extends L1SystemConfig(
|
|||||||
writeInfoReqQSize = 16,
|
writeInfoReqQSize = 16,
|
||||||
mshrSize = 8,
|
mshrSize = 8,
|
||||||
l2ReqSourceGenSize = 8,
|
l2ReqSourceGenSize = 8,
|
||||||
uncachedAddrSets = Seq(AddressSet(0x2000000L, 0xFFL)),
|
uncachedAddrSets = Seq(AddressSet(0x2000000L, 0xffL)),
|
||||||
icacheInstAddrSets = Seq(AddressSet(0x80000000L, 0xFFFFFFFL))
|
icacheInstAddrSets = Seq(AddressSet(0x80000000L, 0xfffffffL))
|
||||||
)
|
) {
|
||||||
|
require(mshrSize != l2ReqSourceGenSize)
|
||||||
|
}
|
||||||
|
|
||||||
class L1System (config:L1SystemConfig) (implicit p: Parameters) extends LazyModule {
|
class L1System(config: L1SystemConfig)(implicit p: Parameters)
|
||||||
|
extends LazyModule {
|
||||||
|
// icache bank
|
||||||
|
val icache_bank = LazyModule(new VortexFatBank(config, 0, isICache = true))
|
||||||
|
|
||||||
//icache bank
|
// dcache banks
|
||||||
val icache_bank = LazyModule(new VortexFatBank(config, 0, isICache=true))
|
|
||||||
|
|
||||||
//dcache banks
|
|
||||||
val dcache_banks = Seq.tabulate(config.numBanks) { bankId =>
|
val dcache_banks = Seq.tabulate(config.numBanks) { bankId =>
|
||||||
val bank = LazyModule(new VortexFatBank(config, bankId))
|
val bank = LazyModule(new VortexFatBank(config, bankId))
|
||||||
bank
|
bank
|
||||||
}
|
}
|
||||||
//passthrough
|
// passthrough
|
||||||
val passThrough = LazyModule(new FatBankPassThrough(config))
|
val passThrough = LazyModule(new FatBankPassThrough(config))
|
||||||
|
|
||||||
//L1System exposes to upstream as a dmemXbar
|
// L1System exposes to upstream as a dmemXbar
|
||||||
val dmemXbar = LazyModule(new TLXbar)
|
val dmemXbar = LazyModule(new TLXbar)
|
||||||
dcache_banks.foreach { _.coalToVxCacheNode :=* dmemXbar.node}
|
dcache_banks.foreach { _.coalToVxCacheNode :=* dmemXbar.node }
|
||||||
passThrough.coalToVxCacheNode :=* dmemXbar.node
|
passThrough.coalToVxCacheNode :=* dmemXbar.node
|
||||||
icache_bank.coalToVxCacheNode :=* dmemXbar.node
|
icache_bank.coalToVxCacheNode :=* dmemXbar.node
|
||||||
|
|
||||||
//L1System exposes to downstream as one tileLink Identity Node
|
// L1System exposes to downstream as one tileLink Identity Node
|
||||||
val L1SystemToL2Node = TLIdentityNode()
|
val L1SystemToL2Node = TLIdentityNode()
|
||||||
dcache_banks.foreach{ L1SystemToL2Node := _.vxCacheToL2Node}
|
dcache_banks.foreach { L1SystemToL2Node := _.vxCacheToL2Node }
|
||||||
L1SystemToL2Node := passThrough.vxCacheToL2Node
|
L1SystemToL2Node := passThrough.vxCacheToL2Node
|
||||||
L1SystemToL2Node := icache_bank.vxCacheToL2Node
|
L1SystemToL2Node := icache_bank.vxCacheToL2Node
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this)
|
lazy val module = new LazyModuleImp(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Make the FatBank Pass Through a Blocking Module
|
||||||
//To-Do
|
class FatBankPassThrough(config: L1SystemConfig)(implicit p: Parameters)
|
||||||
//Make the FatBank Pass Through a Blocking Module
|
extends LazyModule {
|
||||||
class FatBankPassThrough(config:L1SystemConfig) (implicit p: Parameters) extends LazyModule {
|
// Slave node to upstream
|
||||||
|
val managerParam = Seq(
|
||||||
val clientParam = Seq(TLMasterPortParameters.v1(
|
TLSlavePortParameters.v1(
|
||||||
clients = Seq(
|
|
||||||
TLMasterParameters.v1(
|
|
||||||
name = "VortexFatBank",
|
|
||||||
sourceId = IdRange(0, 1 << (log2Ceil(config.l2ReqSourceGenSize)+5) ),
|
|
||||||
supportsProbe = TransferSizes(1, config.wordSize),
|
|
||||||
supportsGet = TransferSizes(1, config.wordSize),
|
|
||||||
supportsPutFull = TransferSizes(1, config.wordSize),
|
|
||||||
supportsPutPartial = TransferSizes(1, config.wordSize)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
val managerParam = Seq(TLSlavePortParameters.v1(
|
|
||||||
beatBytes = config.wordSize,
|
beatBytes = config.wordSize,
|
||||||
managers = Seq(
|
managers = Seq(
|
||||||
TLSlaveParameters.v1(
|
TLSlaveParameters.v1(
|
||||||
@@ -104,14 +88,31 @@ class FatBankPassThrough(config:L1SystemConfig) (implicit p: Parameters) extends
|
|||||||
fifoId = Some(0)
|
fifoId = Some(0)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Master node to downstream
|
||||||
|
val clientParam = Seq(
|
||||||
|
TLMasterPortParameters.v1(
|
||||||
|
clients = Seq(
|
||||||
|
TLMasterParameters.v1(
|
||||||
|
name = "VortexFatBank",
|
||||||
|
sourceId = IdRange(0, 1 << (log2Ceil(config.l2ReqSourceGenSize) + 5)),
|
||||||
|
supportsProbe = TransferSizes(1, config.wordSize),
|
||||||
|
supportsGet = TransferSizes(1, config.wordSize),
|
||||||
|
supportsPutFull = TransferSizes(1, config.wordSize),
|
||||||
|
supportsPutPartial = TransferSizes(1, config.wordSize)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val coalToVxCacheNode = TLManagerNode(managerParam)
|
val coalToVxCacheNode = TLManagerNode(managerParam)
|
||||||
val vxCacheFetchNode = TLClientNode(clientParam)
|
val vxCacheFetchNode = TLClientNode(clientParam)
|
||||||
val vxCacheToL2Node = TLIdentityNode()
|
val vxCacheToL2Node = TLIdentityNode()
|
||||||
vxCacheToL2Node := TLWidthWidget(config.cacheLineSize) := vxCacheFetchNode
|
vxCacheToL2Node := TLWidthWidget(config.cacheLineSize) := vxCacheFetchNode
|
||||||
|
|
||||||
//the implementation to make everything a pass through
|
// the implementation to make everything a pass through
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val (upstream, _) = coalToVxCacheNode.in(0)
|
val (upstream, _) = coalToVxCacheNode.in(0)
|
||||||
val (downstream, _) = vxCacheFetchNode.out(0)
|
val (downstream, _) = vxCacheFetchNode.out(0)
|
||||||
@@ -119,52 +120,38 @@ class FatBankPassThrough(config:L1SystemConfig) (implicit p: Parameters) extends
|
|||||||
downstream.a <> upstream.a
|
downstream.a <> upstream.a
|
||||||
upstream.d <> downstream.d
|
upstream.d <> downstream.d
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VortexFatBank(
|
||||||
|
config: L1SystemConfig,
|
||||||
|
bankId: Int,
|
||||||
class VortexFatBank (config: L1SystemConfig, bankId: Int, isICache: Boolean = false)
|
isICache: Boolean = false
|
||||||
(implicit p: Parameters) extends LazyModule {
|
)(implicit p: Parameters)
|
||||||
|
extends LazyModule {
|
||||||
|
// Generate AddressSet by excluding Addr we don't want
|
||||||
//Generate AddressSet by excluding Addr we don't want
|
|
||||||
def generateAddressSets(): Seq[AddressSet] = {
|
def generateAddressSets(): Seq[AddressSet] = {
|
||||||
|
if (isICache) {
|
||||||
if (isICache){
|
|
||||||
config.icacheInstAddrSets
|
config.icacheInstAddrSets
|
||||||
//Seq(AddressSet(0x00000000L, 0xFFFFFFFFL))
|
// Seq(AddressSet(0x00000000L, 0xFFFFFFFFL))
|
||||||
} else {
|
} else {
|
||||||
//suppose have 4 bank
|
// suppose have 4 bank
|
||||||
//base for bank 1: ...000000|01|0000
|
// base for bank 1: ...000000|01|0000
|
||||||
//mask for bank 1; 111111|00|1111
|
// mask for bank 1; 111111|00|1111
|
||||||
val mask = 0xFFFFFFFFL ^ ((config.numBanks-1)*config.wordSize)
|
val mask = 0xffffffffL ^ ((config.numBanks - 1) * config.wordSize)
|
||||||
val base = 0x00000000L | (bankId * config.wordSize)
|
val base = 0x00000000L | (bankId * config.wordSize)
|
||||||
|
|
||||||
val excludeSets = (config.uncachedAddrSets ++ config.icacheInstAddrSets)
|
val excludeSets = (config.uncachedAddrSets ++ config.icacheInstAddrSets)
|
||||||
var remainingSets: Seq[AddressSet] = Seq(AddressSet(base, mask))
|
var remainingSets: Seq[AddressSet] = Seq(AddressSet(base, mask))
|
||||||
for(excludeSet <- excludeSets) {
|
for (excludeSet <- excludeSets) {
|
||||||
remainingSets = remainingSets.flatMap(_.subtract(excludeSet))
|
remainingSets = remainingSets.flatMap(_.subtract(excludeSet))
|
||||||
}
|
}
|
||||||
remainingSets
|
remainingSets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val clientParam = Seq(TLMasterPortParameters.v1(
|
// Slave node to upstream
|
||||||
clients = Seq(
|
val managerParam = Seq(
|
||||||
TLMasterParameters.v1(
|
TLSlavePortParameters.v1(
|
||||||
name = "VortexFatBank",
|
|
||||||
sourceId = IdRange(0, config.l2ReqSourceGenSize),
|
|
||||||
supportsProbe = TransferSizes(1, config.wordSize),
|
|
||||||
supportsGet = TransferSizes(1, config.wordSize),
|
|
||||||
supportsPutFull = TransferSizes(1, config.wordSize),
|
|
||||||
supportsPutPartial = TransferSizes(1, config.wordSize)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
val managerParam = Seq(TLSlavePortParameters.v1(
|
|
||||||
beatBytes = config.wordSize,
|
beatBytes = config.wordSize,
|
||||||
managers = Seq(
|
managers = Seq(
|
||||||
TLSlaveParameters.v1(
|
TLSlaveParameters.v1(
|
||||||
@@ -177,44 +164,59 @@ class VortexFatBank (config: L1SystemConfig, bankId: Int, isICache: Boolean = fa
|
|||||||
fifoId = Some(0)
|
fifoId = Some(0)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Master node to downstream
|
||||||
|
val clientParam = Seq(
|
||||||
|
TLMasterPortParameters.v1(
|
||||||
|
clients = Seq(
|
||||||
|
TLMasterParameters.v1(
|
||||||
|
name = "VortexFatBank",
|
||||||
|
sourceId = IdRange(0, config.l2ReqSourceGenSize),
|
||||||
|
supportsProbe = TransferSizes(1, config.wordSize),
|
||||||
|
supportsGet = TransferSizes(1, config.wordSize),
|
||||||
|
supportsPutFull = TransferSizes(1, config.wordSize),
|
||||||
|
supportsPutPartial = TransferSizes(1, config.wordSize)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val coalToVxCacheNode = TLManagerNode(managerParam)
|
val coalToVxCacheNode = TLManagerNode(managerParam)
|
||||||
val vxCacheToL2Node = TLIdentityNode()
|
val vxCacheToL2Node = TLIdentityNode()
|
||||||
val vxCacheFetchNode = TLClientNode(clientParam)
|
val vxCacheFetchNode = TLClientNode(clientParam)
|
||||||
|
|
||||||
//We need this widthWidget here, because whenever the fatBank is performing
|
// We need this widthWidget here, because whenever the fatBank is performing
|
||||||
//read and write to Mem, it must have the illusion that dataWidth is as big as
|
// read and write to Mem, it must have the illusion that dataWidth is as big as
|
||||||
//as its cacheline size
|
// as its cacheline size
|
||||||
vxCacheToL2Node := TLWidthWidget(config.cacheLineSize) := vxCacheFetchNode
|
vxCacheToL2Node := TLWidthWidget(config.cacheLineSize) := vxCacheFetchNode
|
||||||
lazy val module = new VortexFatBankImp(this, config);
|
lazy val module = new VortexFatBankImp(this, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
class VortexFatBankImp (
|
class VortexFatBankImp(
|
||||||
outer: VortexFatBank,
|
outer: VortexFatBank,
|
||||||
config: L1SystemConfig
|
config: L1SystemConfig
|
||||||
) extends LazyModuleImp(outer) {
|
) extends LazyModuleImp(outer) {
|
||||||
|
val vxCache = Module(
|
||||||
val vxCache = Module(new VX_cache(
|
new VX_cache(
|
||||||
WORD_SIZE=config.wordSize,
|
WORD_SIZE = config.wordSize,
|
||||||
CACHE_LINE_SIZE=config.cacheLineSize,
|
CACHE_LINE_SIZE = config.cacheLineSize,
|
||||||
CORE_TAG_WIDTH= config.coreTagPlusSizeWidth,
|
CORE_TAG_WIDTH = config.coreTagPlusSizeWidth,
|
||||||
MSHR_SIZE= config.mshrSize
|
MSHR_SIZE = config.mshrSize
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
vxCache.io.clk := clock
|
vxCache.io.clk := clock
|
||||||
vxCache.io.reset := reset
|
vxCache.io.reset := reset
|
||||||
|
|
||||||
|
|
||||||
val writeReqCount = RegInit(UInt(32.W), 0.U)
|
val writeReqCount = RegInit(UInt(32.W), 0.U)
|
||||||
val writeInputFire = Wire(Bool())
|
val writeInputFire = Wire(Bool())
|
||||||
val writeOutputFire = Wire(Bool())
|
val writeOutputFire = Wire(Bool())
|
||||||
|
|
||||||
|
when(writeInputFire && ~writeOutputFire) {
|
||||||
when(writeInputFire && ~writeOutputFire){
|
|
||||||
writeReqCount := writeReqCount + 1.U
|
writeReqCount := writeReqCount + 1.U
|
||||||
}.elsewhen(~writeInputFire && writeOutputFire){
|
}.elsewhen(~writeInputFire && writeOutputFire) {
|
||||||
writeReqCount := writeReqCount - 1.U
|
writeReqCount := writeReqCount - 1.U
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,8 +224,6 @@ class VortexFatBankImp (
|
|||||||
dontTouch(writeOutputFire)
|
dontTouch(writeOutputFire)
|
||||||
dontTouch(writeReqCount)
|
dontTouch(writeReqCount)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WriteReqInfo extends Bundle {
|
class WriteReqInfo extends Bundle {
|
||||||
val id = UInt(32.W)
|
val id = UInt(32.W)
|
||||||
val size = UInt(32.W)
|
val size = UInt(32.W)
|
||||||
@@ -234,7 +234,14 @@ class VortexFatBankImp (
|
|||||||
val id = UInt(config.coreTagWidth.W)
|
val id = UInt(config.coreTagWidth.W)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rcvWriteReqInfo = Module(new Queue((new WriteReqInfo).cloneType, config.writeInfoReqQSize, true, false))
|
val rcvWriteReqInfo = Module(
|
||||||
|
new Queue(
|
||||||
|
(new WriteReqInfo).cloneType,
|
||||||
|
config.writeInfoReqQSize,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
val readReqInfo = Wire(new ReadReqInfo(config))
|
val readReqInfo = Wire(new ReadReqInfo(config))
|
||||||
|
|
||||||
// Translate TL request from Coalescer to requests for VX_cache
|
// Translate TL request from Coalescer to requests for VX_cache
|
||||||
@@ -243,46 +250,50 @@ class VortexFatBankImp (
|
|||||||
// coal -> vxCache request on channel A
|
// coal -> vxCache request on channel A
|
||||||
val coalToBankA = coalToBankBundle.a;
|
val coalToBankA = coalToBankBundle.a;
|
||||||
|
|
||||||
coalToBankA.ready := vxCache.io.core_req_ready && rcvWriteReqInfo.io.enq.ready //not optimal
|
coalToBankA.ready := vxCache.io.core_req_ready && rcvWriteReqInfo.io.enq.ready // not optimal
|
||||||
vxCache.io.core_req_valid := coalToBankA.valid
|
vxCache.io.core_req_valid := coalToBankA.valid
|
||||||
|
|
||||||
// read = 0, write = 1
|
// read = 0, write = 1
|
||||||
vxCache.io.core_req_rw := !(coalToBankA.bits.opcode === TLMessages.Get)
|
vxCache.io.core_req_rw := !(coalToBankA.bits.opcode === TLMessages.Get)
|
||||||
//4 is also hardcoded, it should be log2WordSize
|
// 4 is also hardcoded, it should be log2WordSize
|
||||||
vxCache.io.core_req_addr := coalToBankA.bits.address(31, log2Ceil(config.wordSize))
|
vxCache.io.core_req_addr := coalToBankA.bits.address(
|
||||||
|
31,
|
||||||
|
log2Ceil(config.wordSize)
|
||||||
|
)
|
||||||
vxCache.io.core_req_byteen := coalToBankA.bits.mask
|
vxCache.io.core_req_byteen := coalToBankA.bits.mask
|
||||||
vxCache.io.core_req_data := coalToBankA.bits.data
|
vxCache.io.core_req_data := coalToBankA.bits.data
|
||||||
|
|
||||||
//combine size and tag field into one big wire, to put into vxCache.io.core_req_tag
|
// combine size and tag field into one big wire, to put into vxCache.io.core_req_tag
|
||||||
readReqInfo.id := coalToBankA.bits.source
|
readReqInfo.id := coalToBankA.bits.source
|
||||||
readReqInfo.size := coalToBankA.bits.size
|
readReqInfo.size := coalToBankA.bits.size
|
||||||
vxCache.io.core_req_tag := readReqInfo.asTypeOf(vxCache.io.core_req_tag)
|
vxCache.io.core_req_tag := readReqInfo.asTypeOf(vxCache.io.core_req_tag)
|
||||||
|
|
||||||
writeInputFire := vxCache.io.core_req_rw && coalToBankA.fire
|
writeInputFire := vxCache.io.core_req_rw && coalToBankA.fire
|
||||||
|
|
||||||
// we ignore param, size, corrupt fields
|
// ignore param, size, corrupt fields
|
||||||
|
|
||||||
// vxCache -> coal response on channel D
|
// vxCache -> coal response on channel D
|
||||||
// ok ... this part is a little tricky, the downstream coalescer requires the L1 cache
|
// ok ... this part is a little tricky, the downstream coalescer requires
|
||||||
// to send ack and dataAck, this is how coalescer knows when an inflight ID has retired
|
// the L1 cache to send ack and dataAck, this is how coalescer knows when
|
||||||
// if we don't send ack, the coalescer will run out of IDs, and can't generate new request
|
// an inflight ID has retired if we don't send ack, the coalescer will run
|
||||||
|
// out of IDs, and can't generate new request
|
||||||
|
|
||||||
// for read request, we send AckData when the FatBank has a valid output
|
// for read request, we send AckData when the FatBank has a valid output
|
||||||
// for write request, we can ack whenever we have a valid entry in rcvWriteReqInfo Queue
|
//
|
||||||
|
// for write request, we can ack whenever we have a valid entry in
|
||||||
//I think this just shows the flaws of Tilelink. CPU never waits for an Ack upon regular write request
|
// rcvWriteReqInfo Queue
|
||||||
//the Core should unconditionally move forward after every regular write request
|
|
||||||
|
|
||||||
val coalToBankD = coalToBankBundle.d;
|
val coalToBankD = coalToBankBundle.d;
|
||||||
|
|
||||||
|
// FIXME: currently assuming below buffer is never full
|
||||||
//<FIXME> currently assuming below buffer is never full
|
|
||||||
rcvWriteReqInfo.io.enq.valid := !(coalToBankA.bits.opcode === TLMessages.Get) && coalToBankA.valid && coalToBankA.ready
|
rcvWriteReqInfo.io.enq.valid := !(coalToBankA.bits.opcode === TLMessages.Get) && coalToBankA.valid && coalToBankA.ready
|
||||||
rcvWriteReqInfo.io.enq.bits.id := coalToBankA.bits.source
|
rcvWriteReqInfo.io.enq.bits.id := coalToBankA.bits.source
|
||||||
rcvWriteReqInfo.io.enq.bits.size := coalToBankA.bits.size
|
rcvWriteReqInfo.io.enq.bits.size := coalToBankA.bits.size
|
||||||
|
|
||||||
//prioritize Ack for Read, so we only deque from writeReqInfo, if we don't have a readReq we need to ack
|
// prioritize Ack for Read, so we only deque from writeReqInfo, if we don't
|
||||||
//vxCache.io.core_rsp_valid means readDataAck
|
// have a readReq we need to ack
|
||||||
|
//
|
||||||
|
// vxCache.io.core_rsp_valid means readDataAck
|
||||||
rcvWriteReqInfo.io.deq.ready := coalToBankD.ready && ~vxCache.io.core_rsp_valid
|
rcvWriteReqInfo.io.deq.ready := coalToBankD.ready && ~vxCache.io.core_rsp_valid
|
||||||
|
|
||||||
vxCache.io.core_rsp_ready := coalToBankD.ready
|
vxCache.io.core_rsp_ready := coalToBankD.ready
|
||||||
@@ -310,113 +321,98 @@ class VortexFatBankImp (
|
|||||||
coalToBankD.bits.sink := 0.U
|
coalToBankD.bits.sink := 0.U
|
||||||
coalToBankD.bits.denied := false.B
|
coalToBankD.bits.denied := false.B
|
||||||
coalToBankD.bits.corrupt := false.B
|
coalToBankD.bits.corrupt := false.B
|
||||||
|
|
||||||
coalToBankD.bits.data := vxCache.io.core_rsp_data
|
coalToBankD.bits.data := vxCache.io.core_rsp_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since Vortex L1 is a write-through cache, it doesn't bookkeep writes and
|
||||||
//Using Hansung's Source Generator
|
// therefore doesn't allocate a new UUID for write requests. We use a
|
||||||
//Why do we need to do this, what is the issue ?
|
// separate source ID allocator to solve this.
|
||||||
//Tilelink requires all inflight Read and Write Request to have a unique source_ID
|
val sourceGen = Module(
|
||||||
//vx_cache can indeed guarantee that all active read operation has unique ID
|
new NewSourceGenerator(
|
||||||
//However, since the cache is write_through, so it can't ensure unique ID for write operation
|
|
||||||
//Therefore, we need our own internal source_ID generator for all write operation
|
|
||||||
|
|
||||||
|
|
||||||
val sourceGen = Module( new NewSourceGenerator(
|
|
||||||
log2Ceil(config.l2ReqSourceGenSize),
|
log2Ceil(config.l2ReqSourceGenSize),
|
||||||
metadata = Some(UInt(32.W)),
|
metadata = Some(UInt(32.W)),
|
||||||
ignoreInUse = false)
|
ignoreInUse = false
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Translate VX_cache mem request to a TL request to be sent to L2
|
// Translate VX_cache mem request to a TL request to be sent to L2
|
||||||
def VXReq2TLReq = {
|
def VXReq2TLReq = {
|
||||||
val (vxCacheToL2Bundle, _) = outer.vxCacheFetchNode.out.head
|
val (tlOutToL2, _) = outer.vxCacheFetchNode.out.head
|
||||||
// vxCache -> L2 request on channel A
|
|
||||||
val vxCacheToL2A = vxCacheToL2Bundle.a;
|
|
||||||
|
|
||||||
|
// vxCache -> downstream L2 request
|
||||||
|
vxCache.io.mem_req_ready := tlOutToL2.a.ready && sourceGen.io.id.valid
|
||||||
|
tlOutToL2.a.valid := vxCache.io.mem_req_valid && sourceGen.io.id.valid
|
||||||
|
|
||||||
//Read Operation is ready as long as downstream L2 is ready
|
sourceGen.io.gen := tlOutToL2.a.fire
|
||||||
|
sourceGen.io.meta := vxCache.io.mem_req_tag // save the old read id
|
||||||
|
|
||||||
vxCache.io.mem_req_ready := vxCacheToL2A.ready && sourceGen.io.id.valid
|
writeOutputFire := tlOutToL2.a.fire && vxCache.io.mem_req_rw
|
||||||
|
|
||||||
vxCacheToL2A.valid := vxCache.io.mem_req_valid && sourceGen.io.id.valid
|
tlOutToL2.a.bits.opcode := Mux(
|
||||||
|
|
||||||
sourceGen.io.gen := vxCacheToL2A.fire
|
|
||||||
|
|
||||||
|
|
||||||
writeOutputFire := vxCacheToL2A.fire && vxCache.io.mem_req_rw
|
|
||||||
|
|
||||||
vxCacheToL2A.bits.opcode := Mux(
|
|
||||||
vxCache.io.mem_req_rw,
|
vxCache.io.mem_req_rw,
|
||||||
Mux(vxCache.io.mem_req_byteen.andR, TLMessages.PutFullData, TLMessages.PutPartialData),
|
Mux(
|
||||||
|
vxCache.io.mem_req_byteen.andR,
|
||||||
|
TLMessages.PutFullData,
|
||||||
|
TLMessages.PutPartialData
|
||||||
|
),
|
||||||
TLMessages.Get
|
TLMessages.Get
|
||||||
)
|
)
|
||||||
|
|
||||||
vxCacheToL2A.bits.address := Cat(vxCache.io.mem_req_addr, 0.U(4.W))
|
tlOutToL2.a.bits.address := Cat(vxCache.io.mem_req_addr, 0.U(4.W))
|
||||||
|
tlOutToL2.a.bits.mask := Mux(
|
||||||
vxCacheToL2A.bits.mask := Mux(
|
|
||||||
vxCache.io.mem_req_rw,
|
vxCache.io.mem_req_rw,
|
||||||
vxCache.io.mem_req_byteen,
|
vxCache.io.mem_req_byteen,
|
||||||
0xFFFF.U
|
0xffff.U
|
||||||
)
|
)
|
||||||
|
tlOutToL2.a.bits.data := vxCache.io.mem_req_data
|
||||||
|
tlOutToL2.a.bits.source := sourceGen.io.id.bits
|
||||||
|
// ignore param, size, corrupt fields
|
||||||
|
tlOutToL2.a.bits.param := 0.U
|
||||||
|
tlOutToL2.a.bits.size := 4.U // FIXME: hardcoded
|
||||||
|
tlOutToL2.a.bits.corrupt := false.B
|
||||||
|
// downstream L2 -> vxCache response
|
||||||
|
tlOutToL2.d.ready := vxCache.io.mem_rsp_ready
|
||||||
|
|
||||||
vxCacheToL2A.bits.data := vxCache.io.mem_req_data
|
vxCache.io.mem_rsp_valid :=
|
||||||
|
tlOutToL2.d.valid && (tlOutToL2.d.bits.opcode === TLMessages.AccessAckData)
|
||||||
vxCacheToL2A.bits.source := sourceGen.io.id.bits
|
|
||||||
|
|
||||||
sourceGen.io.meta := vxCache.io.mem_req_tag //save the old read id
|
|
||||||
|
|
||||||
vxCacheToL2A.bits.param := 0.U
|
|
||||||
vxCacheToL2A.bits.size := 4.U
|
|
||||||
vxCacheToL2A.bits.corrupt := false.B
|
|
||||||
|
|
||||||
// we ignore param, size, corrupt fields
|
|
||||||
|
|
||||||
// L2 -> vxCache response on channel D
|
|
||||||
val vxCacheToL2D = vxCacheToL2Bundle.d;
|
|
||||||
vxCacheToL2D.ready := vxCache.io.mem_rsp_ready
|
|
||||||
|
|
||||||
vxCache.io.mem_rsp_valid := vxCacheToL2D.valid && vxCacheToL2D.bits.opcode === TLMessages.AccessAckData
|
|
||||||
vxCache.io.mem_rsp_tag := sourceGen.io.peek
|
vxCache.io.mem_rsp_tag := sourceGen.io.peek
|
||||||
vxCache.io.mem_rsp_data := vxCacheToL2D.bits.data
|
vxCache.io.mem_rsp_data := tlOutToL2.d.bits.data
|
||||||
|
|
||||||
// all ids needs to be reclaimed
|
|
||||||
sourceGen.io.reclaim.valid := vxCacheToL2D.fire
|
|
||||||
sourceGen.io.reclaim.bits := vxCacheToL2D.bits.source
|
|
||||||
|
|
||||||
|
sourceGen.io.reclaim.valid := tlOutToL2.d.fire
|
||||||
|
sourceGen.io.reclaim.bits := tlOutToL2.d.bits.source
|
||||||
}
|
}
|
||||||
|
|
||||||
TLReq2VXReq
|
TLReq2VXReq
|
||||||
VXReq2TLReq
|
VXReq2TLReq
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class VX_cache (
|
class VX_cache(
|
||||||
CACHE_ID: Int = 0,
|
CACHE_ID: Int = 0,
|
||||||
CACHE_SIZE: Int = 16384/4, //<FIXME, divided by 4 for faster simulation
|
CACHE_SIZE: Int = 16384 / 4, // <FIXME, divided by 4 for faster simulation
|
||||||
CACHE_LINE_SIZE: Int = 16,
|
CACHE_LINE_SIZE: Int = 16,
|
||||||
NUM_PORTS: Int = 1,
|
NUM_PORTS: Int = 1,
|
||||||
WORD_SIZE: Int = 16, // hack - one "word" is enough to satisfy all 4 warps after decoalescing.
|
WORD_SIZE: Int =
|
||||||
|
16, // hack - one "word" is enough to satisfy all 4 warps after decoalescing.
|
||||||
CREQ_SIZE: Int = 0,
|
CREQ_SIZE: Int = 0,
|
||||||
CRSQ_SIZE: Int = 2,
|
CRSQ_SIZE: Int = 2,
|
||||||
MSHR_SIZE: Int = 8,
|
MSHR_SIZE: Int = 8,
|
||||||
MRSQ_SIZE: Int = 0,
|
MRSQ_SIZE: Int = 0,
|
||||||
MREQ_SIZE: Int = 4,
|
MREQ_SIZE: Int = 4,
|
||||||
WRITE_ENABLE: Int = 1,
|
WRITE_ENABLE: Int = 1,
|
||||||
CORE_TAG_WIDTH: Int = 10, // source ID ranges from 0 to 1 << 10, we need to allocate upper bits to save size
|
CORE_TAG_WIDTH: Int =
|
||||||
CORE_TAG_ID_BITS: Int = 5, // no idea what this is, just match it with default L1 dcache
|
10, // source ID ranges from 0 to 1 << 10, we need to allocate upper bits to save size
|
||||||
|
CORE_TAG_ID_BITS: Int =
|
||||||
|
5, // no idea what this is, just match it with default L1 dcache
|
||||||
BANK_ADDR_OFFSET: Int = 0,
|
BANK_ADDR_OFFSET: Int = 0,
|
||||||
NC_ENABLE: Int = 0, //NC_ENABLE=1 means the cache becomes a passthrough
|
NC_ENABLE: Int = 0, // NC_ENABLE=1 means the cache becomes a passthrough
|
||||||
WORD_ADDR_WIDTH: Int = 28, // 16 byte "word" = 4 bits
|
WORD_ADDR_WIDTH: Int = 28, // 16 byte "word" = 4 bits
|
||||||
MEM_TAG_WIDTH: Int = 14, // Elaborated value is also completely different from (32 - log2Ceil(CACHE_LINE_SIZE)). This should match with sourceIds on client node associated with this cache
|
MEM_TAG_WIDTH: Int =
|
||||||
|
14, // Elaborated value is also completely different from (32 - log2Ceil(CACHE_LINE_SIZE)). This should match with sourceIds on client node associated with this cache
|
||||||
MEM_ADDR_WIDTH: Int = 28 // 16 byte cache line = 4 bits
|
MEM_ADDR_WIDTH: Int = 28 // 16 byte cache line = 4 bits
|
||||||
) extends BlackBox (
|
) extends BlackBox(
|
||||||
Map(
|
Map(
|
||||||
"CACHE_ID" -> CACHE_ID,
|
"CACHE_ID" -> CACHE_ID,
|
||||||
"NUM_REQS" -> 1, //Force NUM_REQS to be 1, we use their Cache as our individual Bank
|
"NUM_REQS" -> 1, // Force NUM_REQS to be 1, we use their Cache as our individual Bank
|
||||||
"CACHE_SIZE" -> CACHE_SIZE,
|
"CACHE_SIZE" -> CACHE_SIZE,
|
||||||
"CACHE_LINE_SIZE" -> CACHE_LINE_SIZE,
|
"CACHE_LINE_SIZE" -> CACHE_LINE_SIZE,
|
||||||
"NUM_PORTS" -> NUM_PORTS,
|
"NUM_PORTS" -> NUM_PORTS,
|
||||||
@@ -431,9 +427,10 @@ class VX_cache (
|
|||||||
"CORE_TAG_ID_BITS" -> CORE_TAG_ID_BITS,
|
"CORE_TAG_ID_BITS" -> CORE_TAG_ID_BITS,
|
||||||
"MEM_TAG_WIDTH" -> MEM_TAG_WIDTH,
|
"MEM_TAG_WIDTH" -> MEM_TAG_WIDTH,
|
||||||
"BANK_ADDR_OFFSET" -> BANK_ADDR_OFFSET,
|
"BANK_ADDR_OFFSET" -> BANK_ADDR_OFFSET,
|
||||||
"NC_ENABLE" -> NC_ENABLE,
|
"NC_ENABLE" -> NC_ENABLE
|
||||||
)
|
)
|
||||||
) with HasBlackBoxResource {
|
)
|
||||||
|
with HasBlackBoxResource {
|
||||||
|
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val clk = Input(Clock())
|
val clk = Input(Clock())
|
||||||
@@ -451,7 +448,8 @@ class VX_cache (
|
|||||||
val core_req_ready = Output(Bool())
|
val core_req_ready = Output(Bool())
|
||||||
|
|
||||||
val core_rsp_valid = Output(Bool()) // 1 bit wide
|
val core_rsp_valid = Output(Bool()) // 1 bit wide
|
||||||
val core_rsp_tmask = Output(Bool()) // 1 bit wide, probably can ignore (check waveform)
|
val core_rsp_tmask =
|
||||||
|
Output(Bool()) // 1 bit wide, probably can ignore (check waveform)
|
||||||
val core_rsp_data = Output(UInt((WORD_SIZE * 8).W))
|
val core_rsp_data = Output(UInt((WORD_SIZE * 8).W))
|
||||||
val core_rsp_tag = Output(UInt(CORE_TAG_WIDTH.W))
|
val core_rsp_tag = Output(UInt(CORE_TAG_WIDTH.W))
|
||||||
val core_rsp_ready = Input(Bool())
|
val core_rsp_ready = Input(Bool())
|
||||||
@@ -471,7 +469,6 @@ class VX_cache (
|
|||||||
val mem_rsp_ready = Output(Bool())
|
val mem_rsp_ready = Output(Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
addResource("/vsrc/vortex/hw/rtl/VX_dispatch.sv")
|
addResource("/vsrc/vortex/hw/rtl/VX_dispatch.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/VX_issue.sv")
|
addResource("/vsrc/vortex/hw/rtl/VX_issue.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/cache/VX_cache_define.vh")
|
addResource("/vsrc/vortex/hw/rtl/cache/VX_cache_define.vh")
|
||||||
@@ -588,7 +585,7 @@ class VX_cache (
|
|||||||
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_perf_tex_if.sv")
|
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_perf_tex_if.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_mem_req_if.sv")
|
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_mem_req_if.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_fpu_req_if.sv")
|
addResource("/vsrc/vortex/hw/rtl/interfaces/VX_fpu_req_if.sv")
|
||||||
//addResource("/vsrc/vortex/hw/rtl/cache/VX_shared_mem.sv")
|
// addResource("/vsrc/vortex/hw/rtl/cache/VX_shared_mem.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/cache/VX_core_rsp_merge.sv")
|
addResource("/vsrc/vortex/hw/rtl/cache/VX_core_rsp_merge.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/cache/VX_tag_access.sv")
|
addResource("/vsrc/vortex/hw/rtl/cache/VX_tag_access.sv")
|
||||||
addResource("/vsrc/vortex/hw/rtl/cache/VX_core_req_bank_sel.sv")
|
addResource("/vsrc/vortex/hw/rtl/cache/VX_core_req_bank_sel.sv")
|
||||||
@@ -601,11 +598,8 @@ class VX_cache (
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <FIXME> Delete the following NewSourceGenerator when merging with origin/graphics
|
||||||
|
// we should just use the one in coalescing.scala written by hansung
|
||||||
|
|
||||||
//<FIXME> Delete the following NewSourceGenerator when merging with origin/graphics
|
|
||||||
//we should just use the one in coalescing.scala written by hansung
|
|
||||||
|
|
||||||
class NewSourceGenerator[T <: Data](
|
class NewSourceGenerator[T <: Data](
|
||||||
sourceWidth: Int,
|
sourceWidth: Int,
|
||||||
@@ -663,32 +657,40 @@ class NewSourceGenerator[T <: Data](
|
|||||||
io.id.bits := lowestFree
|
io.id.bits := lowestFree
|
||||||
when(io.gen && io.id.valid /* fire */ ) {
|
when(io.gen && io.id.valid /* fire */ ) {
|
||||||
occupancyTable(io.id.bits).id.valid := true.B // mark in use
|
occupancyTable(io.id.bits).id.valid := true.B // mark in use
|
||||||
occupancyTable(io.id.bits).age := 0.U // reset age upon issuing, double safety
|
occupancyTable(
|
||||||
|
io.id.bits
|
||||||
|
).age := 0.U // reset age upon issuing, double safety
|
||||||
if (metadata.isDefined) {
|
if (metadata.isDefined) {
|
||||||
occupancyTable(io.id.bits).meta := io.meta
|
occupancyTable(io.id.bits).meta := io.meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increase age of all inflight IDs by 1, except for the one being reclaimed
|
// Increase age of all inflight IDs by 1, except for the one being reclaimed
|
||||||
for(i <- 0 until numSourceId) {
|
for (i <- 0 until numSourceId) {
|
||||||
when(occupancyTable(i).id.valid && (i.U =/= io.reclaim.bits || !io.reclaim.valid)) {
|
when(
|
||||||
|
occupancyTable(
|
||||||
|
i
|
||||||
|
).id.valid && (i.U =/= io.reclaim.bits || !io.reclaim.valid)
|
||||||
|
) {
|
||||||
occupancyTable(i).age := occupancyTable(i).age + 1.U
|
occupancyTable(i).age := occupancyTable(i).age + 1.U
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when(io.reclaim.valid) {
|
when(io.reclaim.valid) {
|
||||||
assert(occupancyTable(io.reclaim.bits).id.valid === true.B, "tried to reclaim a non-used id")
|
assert(
|
||||||
|
occupancyTable(io.reclaim.bits).id.valid === true.B,
|
||||||
|
"tried to reclaim a non-used id"
|
||||||
|
)
|
||||||
occupancyTable(io.reclaim.bits).id.valid := false.B // mark freed
|
occupancyTable(io.reclaim.bits).id.valid := false.B // mark freed
|
||||||
occupancyTable(io.reclaim.bits).age := 0.U
|
occupancyTable(io.reclaim.bits).age := 0.U
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
io.peek := {
|
io.peek := {
|
||||||
if (metadata.isDefined) occupancyTable(io.reclaim.bits).meta else 0.U
|
if (metadata.isDefined) occupancyTable(io.reclaim.bits).meta else 0.U
|
||||||
}
|
}
|
||||||
|
|
||||||
when(io.gen && io.id.valid) {
|
when(io.gen && io.id.valid) {
|
||||||
when (!io.reclaim.valid) {
|
when(!io.reclaim.valid) {
|
||||||
assert(outstanding < (1 << sourceWidth).U)
|
assert(outstanding < (1 << sourceWidth).U)
|
||||||
outstanding := outstanding + 1.U
|
outstanding := outstanding + 1.U
|
||||||
}
|
}
|
||||||
@@ -699,7 +701,9 @@ class NewSourceGenerator[T <: Data](
|
|||||||
|
|
||||||
// Debugging wires
|
// Debugging wires
|
||||||
val ages = VecInit((0 until numSourceId).map(i => occupancyTable(i).age))
|
val ages = VecInit((0 until numSourceId).map(i => occupancyTable(i).age))
|
||||||
val oldestIndex = PriorityEncoder(ages.map(a => a === ages.reduce((x, y) => Mux(x > y, x, y))))
|
val oldestIndex = PriorityEncoder(
|
||||||
|
ages.map(a => a === ages.reduce((x, y) => Mux(x > y, x, y)))
|
||||||
|
)
|
||||||
val oldestIdInflight = Wire(UInt(sourceWidth.W))
|
val oldestIdInflight = Wire(UInt(sourceWidth.W))
|
||||||
val oldestMetadata = Wire(getMetadataType)
|
val oldestMetadata = Wire(getMetadataType)
|
||||||
val oldestAge = Wire(UInt(32.W))
|
val oldestAge = Wire(UInt(32.W))
|
||||||
@@ -707,7 +711,10 @@ class NewSourceGenerator[T <: Data](
|
|||||||
oldestIdInflight := oldestIndex
|
oldestIdInflight := oldestIndex
|
||||||
oldestMetadata := occupancyTable(oldestIndex).meta
|
oldestMetadata := occupancyTable(oldestIndex).meta
|
||||||
oldestAge := occupancyTable(oldestIndex).age
|
oldestAge := occupancyTable(oldestIndex).age
|
||||||
assert(oldestAge <= 2000.U, "One id in the SourceGen is not released for long time, potential bug !")
|
assert(
|
||||||
|
oldestAge <= 2000.U,
|
||||||
|
"One id in the SourceGen is not released for long time, potential bug !"
|
||||||
|
)
|
||||||
|
|
||||||
dontTouch(oldestIdInflight)
|
dontTouch(oldestIdInflight)
|
||||||
dontTouch(oldestMetadata)
|
dontTouch(oldestMetadata)
|
||||||
|
|||||||
Reference in New Issue
Block a user