DataCache add invalidation feature

This commit is contained in:
Dolu1990 2020-04-07 19:18:20 +02:00
parent 1ef099e308
commit 0c8ea4a368
5 changed files with 108 additions and 26 deletions

View File

@ -27,7 +27,7 @@ import spinal.lib.bus.avalon.AvalonMM
import spinal.lib.eda.altera.{InterruptReceiverTag, ResetEmitterTag} import spinal.lib.eda.altera.{InterruptReceiverTag, ResetEmitterTag}
//make clean all SEED=42 MMU=no STOP_ON_ERROR=yes SMP=yes SUPERVISOR=yes REDO=10 DHRYSTONE=no LRSC=yes LINUX_REGRESSION=yes TRACE=yes TRACE_START=1000000000000l FLOW_INFO=no //make clean all SEED=42 MMU=no STOP_ON_ERROR=yes DBUS_EXCLUSIVE=yes SUPERVISOR=yes REDO=10 DHRYSTONE=no LRSC=yes LINUX_REGRESSION=yes TRACE=yes TRACE_START=1000000000000l FLOW_INFO=no
object TestsWorkspace { object TestsWorkspace {
def main(args: Array[String]) { def main(args: Array[String]) {
def configFull = { def configFull = {
@ -102,7 +102,8 @@ object TestsWorkspace {
catchUnaligned = true, catchUnaligned = true,
withLrSc = true, withLrSc = true,
withAmo = false, withAmo = false,
withSmp = true withExclusive = true,
withInvalidate = true
// ) // )
), ),
memoryTranslatorPortConfig = MmuPortConfig( memoryTranslatorPortConfig = MmuPortConfig(

View File

@ -25,7 +25,8 @@ case class DataCacheConfig(cacheSize : Int,
tagSizeShift : Int = 0, //Used to force infering ram tagSizeShift : Int = 0, //Used to force infering ram
withLrSc : Boolean = false, withLrSc : Boolean = false,
withAmo : Boolean = false, withAmo : Boolean = false,
withSmp : Boolean = false, withExclusive : Boolean = false,
withInvalidate : Boolean = false,
pendingMax : Int = 64, pendingMax : Int = 64,
mergeExecuteMemory : Boolean = false){ mergeExecuteMemory : Boolean = false){
assert(!(mergeExecuteMemory && (earlyDataMux || earlyWaysHits))) assert(!(mergeExecuteMemory && (earlyDataMux || earlyWaysHits)))
@ -33,9 +34,9 @@ case class DataCacheConfig(cacheSize : Int,
def burstSize = bytePerLine*8/memDataWidth def burstSize = bytePerLine*8/memDataWidth
val burstLength = bytePerLine/(memDataWidth/8) val burstLength = bytePerLine/(memDataWidth/8)
def catchSomething = catchUnaligned || catchIllegal || catchAccessError def catchSomething = catchUnaligned || catchIllegal || catchAccessError
def withInternalAmo = withAmo && !withSmp def withInternalAmo = withAmo && !withExclusive
def withInternalLrSc = withLrSc && !withSmp def withInternalLrSc = withLrSc && !withExclusive
def withExternalLrSc = withLrSc && withSmp def withExternalLrSc = withLrSc && withExclusive
def getAxi4SharedConfig() = Axi4Config( def getAxi4SharedConfig() = Axi4Config(
addressWidth = addressWidth, addressWidth = addressWidth,
dataWidth = memDataWidth, dataWidth = memDataWidth,
@ -171,14 +172,30 @@ case class DataCacheMemCmd(p : DataCacheConfig) extends Bundle{
val data = Bits(p.memDataWidth bits) val data = Bits(p.memDataWidth bits)
val mask = Bits(p.memDataWidth/8 bits) val mask = Bits(p.memDataWidth/8 bits)
val length = UInt(log2Up(p.burstLength) bits) val length = UInt(log2Up(p.burstLength) bits)
val exclusive = p.withSmp generate Bool() val exclusive = p.withExclusive generate Bool()
val last = Bool val last = Bool
} }
case class DataCacheMemRsp(p : DataCacheConfig) extends Bundle{ case class DataCacheMemRsp(p : DataCacheConfig) extends Bundle{
val last = Bool() val last = Bool()
val data = Bits(p.memDataWidth bit) val data = Bits(p.memDataWidth bit)
val error = Bool val error = Bool
val exclusive = p.withSmp generate Bool() val exclusive = p.withExclusive generate Bool()
}
case class DataCacheInvalidateCmd(p : DataCacheConfig) extends Bundle{
val address = UInt(p.addressWidth bit)
}
case class DataCacheInvalidateRsp(p : DataCacheConfig) extends Bundle{
val hit = Bool()
}
case class DataCacheInvalidateBus(p : DataCacheConfig) extends Bundle with IMasterSlave {
val cmd = Stream(DataCacheInvalidateCmd(p))
val rsp = Stream(DataCacheInvalidateRsp(p))
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
} }
case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave{ case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave{
@ -353,7 +370,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val io = new Bundle{ val io = new Bundle{
val cpu = slave(DataCacheCpuBus(p)) val cpu = slave(DataCacheCpuBus(p))
val mem = master(DataCacheMemBus(p)) val mem = master(DataCacheMemBus(p))
// val flushDone = out Bool //It pulse at the same time than the manager.request.fire val inv = withInvalidate generate slave(DataCacheInvalidateBus(p))
} }
val haltCpu = False val haltCpu = False
@ -371,6 +388,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine) val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine)
val lineRange = tagRange.low-1 downto log2Up(bytePerLine) val lineRange = tagRange.low-1 downto log2Up(bytePerLine)
val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord) val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord)
val hitRange = tagRange.high downto lineRange.low
class LineInfo() extends Bundle{ class LineInfo() extends Bundle{
@ -379,6 +397,7 @@ class DataCache(p : DataCacheConfig) extends Component{
} }
val tagsReadCmd = Flow(UInt(log2Up(wayLineCount) bits)) val tagsReadCmd = Flow(UInt(log2Up(wayLineCount) bits))
val tagsInvReadCmd = withInvalidate generate Flow(UInt(log2Up(wayLineCount) bits))
val tagsWriteCmd = Flow(new Bundle{ val tagsWriteCmd = Flow(new Bundle{
val way = Bits(wayCount bits) val way = Bits(wayCount bits)
val address = UInt(log2Up(wayLineCount) bits) val address = UInt(log2Up(wayLineCount) bits)
@ -403,6 +422,7 @@ class DataCache(p : DataCacheConfig) extends Component{
//Reads //Reads
val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.memory.isStuck) val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.memory.isStuck)
val tagsInvReadRsp = withInvalidate generate tags.readSync(tagsInvReadCmd.payload, tagsReadCmd.valid && !io.cpu.memory.isStuck)
val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.memory.isStuck) val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.memory.isStuck)
//Writes //Writes
@ -449,7 +469,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val rspSync = True val rspSync = True
val rspLast = True val rspLast = True
val memCmdSent = RegInit(False) setWhen (io.mem.cmd.ready) clearWhen (!io.cpu.writeBack.isStuck) val memCmdSent = RegInit(False) setWhen (io.mem.cmd.ready) clearWhen (!io.cpu.writeBack.isStuck)
val pending = withSmp generate new Area{ val pending = withExclusive generate new Area{
val counter = Reg(UInt(log2Up(pendingMax) + 1 bits)) init(0) val counter = Reg(UInt(log2Up(pendingMax) + 1 bits)) init(0)
counter := counter + U(io.mem.cmd.fire && io.mem.cmd.last) - U(io.mem.rsp.valid && io.mem.rsp.last) counter := counter + U(io.mem.cmd.fire && io.mem.cmd.last) - U(io.mem.rsp.valid && io.mem.rsp.last)
@ -468,7 +488,8 @@ class DataCache(p : DataCacheConfig) extends Component{
U(1) -> B"0011", U(1) -> B"0011",
default -> B"1111" default -> B"1111"
) |<< io.cpu.execute.address(1 downto 0) ) |<< io.cpu.execute.address(1 downto 0)
val colisions = collisionProcess(io.cpu.execute.address(lineRange.high downto wordRange.low), mask) val dataColisions = collisionProcess(io.cpu.execute.address(lineRange.high downto wordRange.low), mask)
val wayInvalidate = B(0, wayCount bits) //Used if invalidate enabled
} }
val stageA = new Area{ val stageA = new Area{
@ -483,11 +504,12 @@ class DataCache(p : DataCacheConfig) extends Component{
val wayHits = earlyWaysHits generate ways.map(way => (io.cpu.memory.mmuBus.rsp.physicalAddress(tagRange) === way.tagsReadRsp.address && way.tagsReadRsp.valid)) val wayHits = earlyWaysHits generate ways.map(way => (io.cpu.memory.mmuBus.rsp.physicalAddress(tagRange) === way.tagsReadRsp.address && way.tagsReadRsp.valid))
val dataMux = earlyDataMux generate MuxOH(wayHits, ways.map(_.dataReadRsp)) val dataMux = earlyDataMux generate MuxOH(wayHits, ways.map(_.dataReadRsp))
val colisions = if(mergeExecuteMemory){ val wayInvalidate = stagePipe(stage0. wayInvalidate)
stagePipe(stage0.colisions) val dataColisions = if(mergeExecuteMemory){
stagePipe(stage0.dataColisions)
} else { } else {
//Assume the writeback stage will never be unstall memory acces while memory stage is stalled //Assume the writeback stage will never be unstall memory acces while memory stage is stalled
stagePipe(stage0.colisions) | collisionProcess(io.cpu.memory.address(lineRange.high downto wordRange.low), mask) stagePipe(stage0.dataColisions) | collisionProcess(io.cpu.memory.address(lineRange.high downto wordRange.low), mask)
} }
} }
@ -499,11 +521,13 @@ class DataCache(p : DataCacheConfig) extends Component{
val mmuRsp = RegNextWhen(io.cpu.memory.mmuBus.rsp, !io.cpu.writeBack.isStuck && !mmuRspFreeze) val mmuRsp = RegNextWhen(io.cpu.memory.mmuBus.rsp, !io.cpu.writeBack.isStuck && !mmuRspFreeze)
val tagsReadRsp = ways.map(w => ramPipe(w.tagsReadRsp)) val tagsReadRsp = ways.map(w => ramPipe(w.tagsReadRsp))
val dataReadRsp = !earlyDataMux generate ways.map(w => ramPipe(w.dataReadRsp)) val dataReadRsp = !earlyDataMux generate ways.map(w => ramPipe(w.dataReadRsp))
val waysHits = if(earlyWaysHits) stagePipe(B(stageA.wayHits)) else B(tagsReadRsp.map(tag => mmuRsp.physicalAddress(tagRange) === tag.address && tag.valid).asBits()) val wayInvalidate = stagePipe(stageA. wayInvalidate)
val dataColisions = stagePipe(stageA.dataColisions)
val waysHitsBeforeInvalidate = if(earlyWaysHits) stagePipe(B(stageA.wayHits)) else B(tagsReadRsp.map(tag => mmuRsp.physicalAddress(tagRange) === tag.address && tag.valid).asBits())
val waysHits = waysHitsBeforeInvalidate & ~wayInvalidate
val waysHit = waysHits.orR val waysHit = waysHits.orR
val dataMux = if(earlyDataMux) stagePipe(stageA.dataMux) else MuxOH(waysHits, dataReadRsp) val dataMux = if(earlyDataMux) stagePipe(stageA.dataMux) else MuxOH(waysHits, dataReadRsp)
val mask = stagePipe(stageA.mask) val mask = stagePipe(stageA.mask)
val colisions = stagePipe(stageA.colisions)
//Loader interface //Loader interface
val loaderValid = False val loaderValid = False
@ -631,8 +655,8 @@ class DataCache(p : DataCacheConfig) extends Component{
} }
} }
//On write to read colisions //On write to read dataColisions
when((!request.wr || isAmo) && (colisions & waysHits) =/= 0){ when((!request.wr || isAmo) && (dataColisions & waysHits) =/= 0){
io.cpu.redo := True io.cpu.redo := True
if(withAmo) io.mem.cmd.valid := False if(withAmo) io.mem.cmd.valid := False
} }
@ -699,6 +723,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val counter = Counter(memTransactionPerLine) val counter = Counter(memTransactionPerLine)
val waysAllocator = Reg(Bits(wayCount bits)) init(1) val waysAllocator = Reg(Bits(wayCount bits)) init(1)
val error = RegInit(False) val error = RegInit(False)
val kill = False
when(valid && io.mem.rsp.valid && rspLast){ when(valid && io.mem.rsp.valid && rspLast){
dataWriteCmd.valid := True dataWriteCmd.valid := True
@ -731,5 +756,58 @@ class DataCache(p : DataCacheConfig) extends Component{
io.cpu.redo setWhen(valid) io.cpu.redo setWhen(valid)
stageB.mmuRspFreeze setWhen(stageB.loaderValid || valid) stageB.mmuRspFreeze setWhen(stageB.loaderValid || valid)
when(kill){
valid := False
error := False
tagsWriteCmd.valid := False
counter.clear()
}
}
val invalidate = withInvalidate generate new Area{
val loaderReadToWriteConflict = False
val s0 = new Area{
val input = io.inv.cmd.haltWhen(loaderReadToWriteConflict)
tagsInvReadCmd.valid := input.fire
tagsInvReadCmd.payload := input.address(lineRange)
val loaderHit = loader.valid && input.address(hitRange) === loader.baseAddress(hitRange)
when(loaderHit){
loader.kill := True
}
}
val s1 = new Area{
val input = s0.input.stage()
val loaderValid = RegNextWhen(loader.valid, s0.input.ready)
val loaderWay = RegNextWhen(loader.waysAllocator, s0.input.ready)
val loaderHit = RegNextWhen(s0.loaderHit, s0.input.ready)
var wayHits = B(ways.map(way => (input.address(tagRange) === way.tagsInvReadRsp.address && way.tagsInvReadRsp.valid)))
//Handle invalider read during loader write hazard
when(loaderValid && !loaderHit){
wayHits \= wayHits & ~loaderWay
}
}
val s2 = new Area{
val input = s1.input.stage()
val wayHits = RegNextWhen(s1.wayHits, s1.input.ready)
val wayHit = wayHits.orR
when(input.valid) {
stage0.wayInvalidate := wayHits
when(wayHit) {
tagsWriteCmd.valid := True
tagsWriteCmd.address := input.address(lineRange)
tagsWriteCmd.data.valid := False
tagsWriteCmd.way := wayHits
loaderReadToWriteConflict := input.address(lineRange) === s0.input.address(lineRange)
}
}
io.inv.rsp.arbitrationFrom(input)
io.inv.rsp.hit := wayHit
}
} }
} }

View File

@ -31,6 +31,7 @@ class DBusCachedPlugin(val config : DataCacheConfig,
assert(!(memoryTranslatorPortConfig != null && config.cacheSize/config.wayCount > 4096), "When the D$ is used with MMU, each way can't be bigger than a page (4096 bytes)") assert(!(memoryTranslatorPortConfig != null && config.cacheSize/config.wayCount > 4096), "When the D$ is used with MMU, each way can't be bigger than a page (4096 bytes)")
var dBus : DataCacheMemBus = null var dBus : DataCacheMemBus = null
var inv : DataCacheInvalidateBus = null
var mmuBus : MemoryTranslatorBus = null var mmuBus : MemoryTranslatorBus = null
var exceptionBus : Flow[ExceptionCause] = null var exceptionBus : Flow[ExceptionCause] = null
var privilegeService : PrivilegeService = null var privilegeService : PrivilegeService = null
@ -161,6 +162,7 @@ class DBusCachedPlugin(val config : DataCacheConfig,
import pipeline.config._ import pipeline.config._
dBus = master(DataCacheMemBus(this.config)).setName("dBus") dBus = master(DataCacheMemBus(this.config)).setName("dBus")
inv = withInvalidate generate slave(DataCacheInvalidateBus(this.config))
val cache = new DataCache(this.config.copy( val cache = new DataCache(this.config.copy(
mergeExecuteMemory = writeBack == null mergeExecuteMemory = writeBack == null
@ -171,6 +173,7 @@ class DBusCachedPlugin(val config : DataCacheConfig,
def cmdBuf = optionPipe(dBusCmdSlavePipe, cache.io.mem.cmd)(_.s2mPipe()) def cmdBuf = optionPipe(dBusCmdSlavePipe, cache.io.mem.cmd)(_.s2mPipe())
dBus.cmd << optionPipe(dBusCmdMasterPipe, cmdBuf)(_.m2sPipe()) dBus.cmd << optionPipe(dBusCmdMasterPipe, cmdBuf)(_.m2sPipe())
cache.io.mem.rsp << optionPipe(dBusRspSlavePipe,dBus.rsp)(_.m2sPipe()) cache.io.mem.rsp << optionPipe(dBusRspSlavePipe,dBus.rsp)(_.m2sPipe())
cache.io.inv <> inv
pipeline plug new Area{ pipeline plug new Area{
//Memory bandwidth counter //Memory bandwidth counter

View File

@ -904,7 +904,7 @@ public:
trap(0, 6, address); trap(0, 6, address);
} else { } else {
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; } if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
#ifdef SMP #ifdef DBUS_EXCLUSIVE
bool hit = lrscReserved && lrscReservedAddress == pAddr; bool hit = lrscReserved && lrscReservedAddress == pAddr;
#else #else
bool hit = lrscReserved; bool hit = lrscReserved;
@ -2353,7 +2353,7 @@ public:
virtual void preCycle(){ virtual void preCycle(){
if (top->dBus_cmd_valid && top->dBus_cmd_ready) { if (top->dBus_cmd_valid && top->dBus_cmd_ready) {
if(top->dBus_cmd_payload_wr){ if(top->dBus_cmd_payload_wr){
#ifndef SMP #ifndef DBUS_EXCLUSIVE
bool error; bool error;
ws->dBusAccess(top->dBus_cmd_payload_address,1,2,top->dBus_cmd_payload_mask,&top->dBus_cmd_payload_data,&error); ws->dBusAccess(top->dBus_cmd_payload_address,1,2,top->dBus_cmd_payload_mask,&top->dBus_cmd_payload_data,&error);
#else #else
@ -2373,7 +2373,7 @@ public:
for(int beat = 0;beat <= top->dBus_cmd_payload_length;beat++){ for(int beat = 0;beat <= top->dBus_cmd_payload_length;beat++){
ws->dBusAccess(top->dBus_cmd_payload_address + beat * 4,0,2,0,&rsp.data,&rsp.error); ws->dBusAccess(top->dBus_cmd_payload_address + beat * 4,0,2,0,&rsp.data,&rsp.error);
rsp.last = beat == top->dBus_cmd_payload_length; rsp.last = beat == top->dBus_cmd_payload_length;
#ifdef SMP #ifdef DBUS_EXCLUSIVE
if(top->dBus_cmd_payload_exclusive){ if(top->dBus_cmd_payload_exclusive){
rsp.exclusive = true; rsp.exclusive = true;
reservationValid = true; reservationValid = true;
@ -2395,7 +2395,7 @@ public:
top->dBus_rsp_payload_error = rsp.error; top->dBus_rsp_payload_error = rsp.error;
top->dBus_rsp_payload_data = rsp.data; top->dBus_rsp_payload_data = rsp.data;
top->dBus_rsp_payload_last = rsp.last; top->dBus_rsp_payload_last = rsp.last;
#ifdef SMP #ifdef DBUS_EXCLUSIVE
top->dBus_rsp_payload_exclusive = rsp.exclusive; top->dBus_rsp_payload_exclusive = rsp.exclusive;
#endif #endif
} else{ } else{
@ -2403,7 +2403,7 @@ public:
top->dBus_rsp_payload_data = VL_RANDOM_I(32); top->dBus_rsp_payload_data = VL_RANDOM_I(32);
top->dBus_rsp_payload_error = VL_RANDOM_I(1); top->dBus_rsp_payload_error = VL_RANDOM_I(1);
top->dBus_rsp_payload_last = VL_RANDOM_I(1); top->dBus_rsp_payload_last = VL_RANDOM_I(1);
#ifdef SMP #ifdef DBUS_EXCLUSIVE
top->dBus_rsp_payload_exclusive = VL_RANDOM_I(1); top->dBus_rsp_payload_exclusive = VL_RANDOM_I(1);
#endif #endif
} }

View File

@ -15,7 +15,7 @@ CSR_SKIP_TEST?=no
EBREAK?=no EBREAK?=no
FENCEI?=no FENCEI?=no
MMU?=yes MMU?=yes
SMP?=no DBUS_EXCLUSIVE?=no
SEED?=no SEED?=no
LRSC?=no LRSC?=no
AMO?=no AMO?=no
@ -218,8 +218,8 @@ ifeq ($(MMU),yes)
ADDCFLAGS += -CFLAGS -DMMU ADDCFLAGS += -CFLAGS -DMMU
endif endif
ifeq ($(SMP),yes) ifeq ($(DBUS_EXCLUSIVE),yes)
ADDCFLAGS += -CFLAGS -DSMP ADDCFLAGS += -CFLAGS -DDBUS_EXCLUSIVE
endif endif
ifeq ($(MUL),yes) ifeq ($(MUL),yes)