Add PrivilegeService (User) (not implemented)
Split caches from their plugins file
This commit is contained in:
parent
a51c27970b
commit
392f3a7d8c
|
@ -1,29 +1,11 @@
|
||||||
package SpinalRiscv.Plugin
|
package SpinalRiscv.Plugin
|
||||||
|
|
||||||
import SpinalRiscv.Riscv._
|
import SpinalRiscv.ip._
|
||||||
import SpinalRiscv._
|
import SpinalRiscv._
|
||||||
import spinal.core._
|
import spinal.core._
|
||||||
import spinal.lib._
|
import spinal.lib._
|
||||||
|
|
||||||
|
|
||||||
case class DataCacheConfig( cacheSize : Int,
|
|
||||||
bytePerLine : Int,
|
|
||||||
wayCount : Int,
|
|
||||||
addressWidth : Int,
|
|
||||||
cpuDataWidth : Int,
|
|
||||||
memDataWidth : Int,
|
|
||||||
catchAccessError : Boolean,
|
|
||||||
catchIllegal : Boolean,
|
|
||||||
catchUnaligned : Boolean,
|
|
||||||
catchMemoryTranslationMiss : Boolean,
|
|
||||||
clearTagsAfterReset : Boolean = true,
|
|
||||||
tagSizeShift : Int = 0){ //Used to force infering ram
|
|
||||||
def burstSize = bytePerLine*8/memDataWidth
|
|
||||||
val burstLength = bytePerLine/(memDataWidth/8)
|
|
||||||
def catchSomething = catchUnaligned || catchMemoryTranslationMiss || catchIllegal || catchAccessError
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv]{
|
class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv]{
|
||||||
|
@ -31,6 +13,8 @@ class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean
|
||||||
var dBus : DataCacheMemBus = null
|
var dBus : DataCacheMemBus = null
|
||||||
var mmuBus : MemoryTranslatorBus = null
|
var mmuBus : MemoryTranslatorBus = null
|
||||||
var exceptionBus : Flow[ExceptionCause] = null
|
var exceptionBus : Flow[ExceptionCause] = null
|
||||||
|
var privilegeService : PrivilegeService = null
|
||||||
|
|
||||||
object MEMORY_ENABLE extends Stageable(Bool)
|
object MEMORY_ENABLE extends Stageable(Bool)
|
||||||
object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
|
object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
|
||||||
|
|
||||||
|
@ -76,6 +60,9 @@ class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean
|
||||||
|
|
||||||
if(catchSomething)
|
if(catchSomething)
|
||||||
exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(pipeline.writeBack)
|
exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(pipeline.writeBack)
|
||||||
|
|
||||||
|
if(pipeline.serviceExist(classOf[PrivilegeService]))
|
||||||
|
privilegeService = pipeline.service(classOf[PrivilegeService])
|
||||||
}
|
}
|
||||||
|
|
||||||
override def build(pipeline: VexRiscv): Unit = {
|
override def build(pipeline: VexRiscv): Unit = {
|
||||||
|
@ -124,6 +111,7 @@ class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean
|
||||||
cache.io.cpu.memory.mmuBus.rsp.allowExecute := True
|
cache.io.cpu.memory.mmuBus.rsp.allowExecute := True
|
||||||
cache.io.cpu.memory.mmuBus.rsp.allowRead := True
|
cache.io.cpu.memory.mmuBus.rsp.allowRead := True
|
||||||
cache.io.cpu.memory.mmuBus.rsp.allowWrite := True
|
cache.io.cpu.memory.mmuBus.rsp.allowWrite := True
|
||||||
|
cache.io.cpu.memory.mmuBus.rsp.allowUser := True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,16 +119,21 @@ class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean
|
||||||
import writeBack._
|
import writeBack._
|
||||||
cache.io.cpu.writeBack.isValid := arbitration.isValid && input(MEMORY_ENABLE)
|
cache.io.cpu.writeBack.isValid := arbitration.isValid && input(MEMORY_ENABLE)
|
||||||
cache.io.cpu.writeBack.isStuck := arbitration.isStuck
|
cache.io.cpu.writeBack.isStuck := arbitration.isStuck
|
||||||
|
cache.io.cpu.writeBack.isUser := (if(privilegeService != null) privilegeService.isUser(writeBack) else False)
|
||||||
|
|
||||||
if(catchSomething) {
|
if(catchSomething) {
|
||||||
exceptionBus.valid := cache.io.cpu.writeBack.mmuMiss || cache.io.cpu.writeBack.accessError || cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.unalignedAccess
|
exceptionBus.valid := cache.io.cpu.writeBack.mmuMiss || cache.io.cpu.writeBack.accessError || cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.unalignedAccess
|
||||||
exceptionBus.badAddr := cache.io.cpu.writeBack.badAddr
|
exceptionBus.badAddr := cache.io.cpu.writeBack.badAddr
|
||||||
exceptionBus.code := 13
|
exceptionBus.code.assignDontCare()
|
||||||
when(cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.accessError){
|
when(cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.accessError){
|
||||||
exceptionBus.code := (input(INSTRUCTION)(5) ? U(7) | U(5)).resized
|
exceptionBus.code := (input(INSTRUCTION)(5) ? U(7) | U(5)).resized
|
||||||
}
|
}
|
||||||
when(cache.io.cpu.writeBack.unalignedAccess){
|
when(cache.io.cpu.writeBack.unalignedAccess){
|
||||||
exceptionBus.code := (input(INSTRUCTION)(5) ? U(6) | U(4)).resized
|
exceptionBus.code := (input(INSTRUCTION)(5) ? U(6) | U(4)).resized
|
||||||
}
|
}
|
||||||
|
when(cache.io.cpu.writeBack.mmuMiss){
|
||||||
|
exceptionBus.code := 13
|
||||||
|
}
|
||||||
}
|
}
|
||||||
arbitration.haltIt.setWhen(cache.io.cpu.writeBack.haltIt)
|
arbitration.haltIt.setWhen(cache.io.cpu.writeBack.haltIt)
|
||||||
|
|
||||||
|
@ -166,555 +159,3 @@ class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object Bypasser{
|
|
||||||
|
|
||||||
//shot readValid path
|
|
||||||
def writeFirstMemWrap[T <: Data](readValid : Bool, readLastAddress : UInt, readLastData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
|
||||||
val writeSample = readValid || (writeValid && writeAddress === readLastAddress)
|
|
||||||
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
|
||||||
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
|
||||||
val writeDataReg = RegNextWhen(writeData,writeSample)
|
|
||||||
(writeValidReg && writeAddressReg === readLastAddress) ? writeDataReg | readLastData
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//shot readValid path
|
|
||||||
def writeFirstMemWrap(readValid : Bool, readLastAddress : UInt, readLastData : Bits,writeValid : Bool, writeAddress : UInt, writeData : Bits,writeMask : Bits) : Bits = {
|
|
||||||
val writeHit = writeValid && writeAddress === readLastAddress
|
|
||||||
val writeSample = readValid || writeHit
|
|
||||||
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
|
||||||
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
|
||||||
val writeDataReg = Reg(writeData)
|
|
||||||
val writeMaskReg = Reg(Bits(widthOf(writeData)/8 bits))
|
|
||||||
val writeDataRegBytes = writeDataReg.subdivideIn(8 bits)
|
|
||||||
val writeDataBytes = writeData.subdivideIn(8 bits)
|
|
||||||
val ret = cloneOf(readLastData)
|
|
||||||
val retBytes = ret.subdivideIn(8 bits)
|
|
||||||
val readLastDataBytes = readLastData.subdivideIn(8 bits)
|
|
||||||
val writeRegHit = writeValidReg && writeAddressReg === readLastAddress
|
|
||||||
for(b <- writeMask.range){
|
|
||||||
when(writeHit && writeMask(b)){
|
|
||||||
writeMaskReg(b) := True
|
|
||||||
}
|
|
||||||
when(readValid) {
|
|
||||||
writeMaskReg(b) := writeMask(b)
|
|
||||||
}
|
|
||||||
when(readValid || (writeHit && writeMask(b))){
|
|
||||||
writeDataRegBytes(b) := writeDataBytes(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
retBytes(b) := (writeRegHit && writeMaskReg(b)) ? writeDataRegBytes(b) | readLastDataBytes(b)
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
//Long sample path
|
|
||||||
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,lastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) : (T,T) = {
|
|
||||||
// val hit = writeValid && (sample ? sampleAddress | lastAddress) === writeAddress
|
|
||||||
// val bypass = hit ? writeData | readData
|
|
||||||
// val reg = RegNextWhen(bypass,sample || hit)
|
|
||||||
// (reg,bypass)
|
|
||||||
// }
|
|
||||||
|
|
||||||
//Short sample path
|
|
||||||
def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, sampleData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
|
||||||
val bypass = (!sample || (writeValid && sampleAddress === writeAddress)) ? writeData | sampleData
|
|
||||||
val regEn = sample || (writeValid && sampleLastAddress === writeAddress)
|
|
||||||
val reg = RegNextWhen(bypass,regEn)
|
|
||||||
reg
|
|
||||||
}
|
|
||||||
|
|
||||||
def writeFirstRegWrap(sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, sampleData : Bits, writeValid : Bool, writeAddress : UInt, writeData : Bits,writeMask : Bits) = {
|
|
||||||
val byteCount = widthOf(writeMask)
|
|
||||||
val sampleWriteHit = writeValid && sampleAddress === writeAddress
|
|
||||||
val sampleLastHit = writeValid && sampleLastAddress === writeAddress
|
|
||||||
val regBytes = Vec(Bits(8 bits),byteCount)
|
|
||||||
for(b <- writeMask.range){
|
|
||||||
val bypass = Mux(!sample || (sampleWriteHit && writeMask(b)), writeData(b*8, 8 bits), sampleData(b*8, 8 bits))
|
|
||||||
val regEn = sample || (sampleLastHit && writeMask(b))
|
|
||||||
regBytes(b) := RegNextWhen(bypass,regEn)
|
|
||||||
}
|
|
||||||
regBytes.asBits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DataCacheCpuCmdKind extends SpinalEnum{
|
|
||||||
val MEMORY,MANAGMENT = newElement()
|
|
||||||
}
|
|
||||||
|
|
||||||
object DataCacheCpuExecute{
|
|
||||||
implicit def implArgs(that : DataCacheCpuExecute) = that.args
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DataCacheCpuExecute(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val isValid = Bool
|
|
||||||
val isStuck = Bool
|
|
||||||
// val haltIt = Bool
|
|
||||||
val args = DataCacheCpuExecuteArgs(p)
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid, isStuck, args)
|
|
||||||
// in(haltIt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DataCacheCpuExecuteArgs(p : DataCacheConfig) extends Bundle{
|
|
||||||
val kind = DataCacheCpuCmdKind()
|
|
||||||
val wr = Bool
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
val data = Bits(p.cpuDataWidth bit)
|
|
||||||
val size = UInt(2 bits)
|
|
||||||
val forceUncachedAccess = Bool
|
|
||||||
val clean, invalidate, way = Bool
|
|
||||||
// val all = Bool //Address should be zero when "all" is used
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DataCacheCpuMemory(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val isValid = Bool
|
|
||||||
val isStuck = Bool
|
|
||||||
val isRemoved = Bool
|
|
||||||
val mmuBus = MemoryTranslatorBus()
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid, isStuck, isRemoved)
|
|
||||||
slave(mmuBus)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val isValid = Bool
|
|
||||||
val isStuck = Bool
|
|
||||||
val haltIt = Bool
|
|
||||||
val data = Bits(p.cpuDataWidth bit)
|
|
||||||
val mmuMiss, illegalAccess, unalignedAccess , accessError = Bool
|
|
||||||
val badAddr = UInt(32 bits)
|
|
||||||
// val exceptionBus = if(p.catchSomething) Flow(ExceptionCause()) else null
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid,isStuck)
|
|
||||||
in(haltIt, data, mmuMiss,illegalAccess , unalignedAccess, accessError, badAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DataCacheCpuBus(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val execute = DataCacheCpuExecute(p)
|
|
||||||
val memory = DataCacheCpuMemory(p)
|
|
||||||
val writeBack = DataCacheCpuWriteBack(p)
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(execute)
|
|
||||||
master(memory)
|
|
||||||
master(writeBack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
case class DataCacheMemCmd(p : DataCacheConfig) extends Bundle{
|
|
||||||
val wr = Bool
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
val data = Bits(p.memDataWidth bits)
|
|
||||||
val mask = Bits(p.memDataWidth/8 bits)
|
|
||||||
val length = UInt(log2Up(p.burstLength+1) bit)
|
|
||||||
}
|
|
||||||
case class DataCacheMemRsp(p : DataCacheConfig) extends Bundle{
|
|
||||||
val data = Bits(p.memDataWidth bit)
|
|
||||||
val error = Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val cmd = Stream (DataCacheMemCmd(p))
|
|
||||||
val rsp = Flow (DataCacheMemRsp(p))
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(cmd)
|
|
||||||
slave(rsp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DataCache(p : DataCacheConfig) extends Component{
|
|
||||||
import p._
|
|
||||||
import DataCacheCpuCmdKind._
|
|
||||||
assert(wayCount == 1)
|
|
||||||
assert(cpuDataWidth == memDataWidth)
|
|
||||||
|
|
||||||
val io = new Bundle{
|
|
||||||
val cpu = slave(DataCacheCpuBus(p))
|
|
||||||
val mem = master(DataCacheMemBus(p))
|
|
||||||
// val flushDone = out Bool //It pulse at the same time than the manager.request.fire
|
|
||||||
}
|
|
||||||
val haltCpu = False
|
|
||||||
val lineWidth = bytePerLine*8
|
|
||||||
val lineCount = cacheSize/bytePerLine
|
|
||||||
val wordWidth = Math.max(memDataWidth,cpuDataWidth)
|
|
||||||
val wordWidthLog2 = log2Up(wordWidth)
|
|
||||||
val wordPerLine = lineWidth/wordWidth
|
|
||||||
val bytePerWord = wordWidth/8
|
|
||||||
val wayLineCount = lineCount/wayCount
|
|
||||||
val wayLineLog2 = log2Up(wayLineCount)
|
|
||||||
val wayWordCount = wayLineCount * wordPerLine
|
|
||||||
val memTransactionPerLine = p.bytePerLine / (p.memDataWidth/8)
|
|
||||||
|
|
||||||
val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine)
|
|
||||||
val lineRange = tagRange.low-1 downto log2Up(bytePerLine)
|
|
||||||
val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord)
|
|
||||||
|
|
||||||
|
|
||||||
class LineInfo() extends Bundle{
|
|
||||||
val used = Bool
|
|
||||||
val dirty = Bool
|
|
||||||
val address = UInt(tagRange.length bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
val tagsReadCmd = Flow(UInt(log2Up(wayLineCount) bits))
|
|
||||||
val tagsWriteCmd = Flow(new Bundle{
|
|
||||||
// val way = UInt(log2Up(wayCount) bits)
|
|
||||||
val address = UInt(log2Up(wayLineCount) bits)
|
|
||||||
val data = new LineInfo()
|
|
||||||
})
|
|
||||||
|
|
||||||
val tagsWriteLastCmd = RegNext(tagsWriteCmd)
|
|
||||||
|
|
||||||
val dataReadCmd = Flow(UInt(log2Up(wayWordCount) bits))
|
|
||||||
val dataWriteCmd = Flow(new Bundle{
|
|
||||||
// val way = UInt(log2Up(wayCount) bits)
|
|
||||||
val address = UInt(log2Up(wayWordCount) bits)
|
|
||||||
val data = Bits(wordWidth bits)
|
|
||||||
val mask = Bits(wordWidth/8 bits)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
tagsReadCmd.valid := False
|
|
||||||
tagsReadCmd.payload.assignDontCare()
|
|
||||||
dataReadCmd.valid := False
|
|
||||||
dataReadCmd.payload.assignDontCare()
|
|
||||||
tagsWriteCmd.valid := False
|
|
||||||
tagsWriteCmd.payload.assignDontCare()
|
|
||||||
dataWriteCmd.valid := False
|
|
||||||
dataWriteCmd.payload.assignDontCare()
|
|
||||||
io.mem.cmd.valid := False
|
|
||||||
io.mem.cmd.payload.assignDontCare()
|
|
||||||
|
|
||||||
|
|
||||||
val way = new Area{
|
|
||||||
val tags = Mem(new LineInfo(),wayLineCount)
|
|
||||||
val data = Mem(Bits(wordWidth bit),wayWordCount)
|
|
||||||
|
|
||||||
when(tagsWriteCmd.valid){
|
|
||||||
tags(tagsWriteCmd.address) := tagsWriteCmd.data
|
|
||||||
}
|
|
||||||
when(dataWriteCmd.valid){
|
|
||||||
data.write(
|
|
||||||
address = dataWriteCmd.address,
|
|
||||||
data = dataWriteCmd.data,
|
|
||||||
mask = dataWriteCmd.mask
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val tagReadRspOneAddress = RegNextWhen(tagsReadCmd.payload, tagsReadCmd.valid)
|
|
||||||
val tagReadRspOne = Bypasser.writeFirstMemWrap(
|
|
||||||
readValid = tagsReadCmd.valid,
|
|
||||||
readLastAddress = tagReadRspOneAddress,
|
|
||||||
readLastData = tags.readSync(tagsReadCmd.payload,enable = tagsReadCmd.valid),
|
|
||||||
writeValid = tagsWriteCmd.valid,
|
|
||||||
writeAddress = tagsWriteCmd.address,
|
|
||||||
writeData = tagsWriteCmd.data
|
|
||||||
)
|
|
||||||
|
|
||||||
val dataReadRspOneKeepAddress = False
|
|
||||||
val dataReadRspOneAddress = RegNextWhen(dataReadCmd.payload, dataReadCmd.valid && !dataReadRspOneKeepAddress)
|
|
||||||
val dataReadRspOneWithoutBypass = data.readSync(dataReadCmd.payload,enable = dataReadCmd.valid)
|
|
||||||
val dataReadRspOne = Bypasser.writeFirstMemWrap(
|
|
||||||
readValid = dataReadCmd.valid,
|
|
||||||
readLastAddress = dataReadRspOneAddress,
|
|
||||||
readLastData = dataReadRspOneWithoutBypass,
|
|
||||||
writeValid = dataWriteCmd.valid,
|
|
||||||
writeAddress = dataWriteCmd.address,
|
|
||||||
writeData = dataWriteCmd.data,
|
|
||||||
writeMask = dataWriteCmd.mask
|
|
||||||
)
|
|
||||||
|
|
||||||
val tagReadRspTwoEnable = !io.cpu.writeBack.isStuck
|
|
||||||
val tagReadRspTwoRegIn = (tagsWriteCmd.valid && tagsWriteCmd.address === tagReadRspOneAddress) ? tagsWriteCmd.data | tagReadRspOne
|
|
||||||
val tagReadRspTwo = RegNextWhen(tagReadRspTwoRegIn ,tagReadRspTwoEnable)
|
|
||||||
|
|
||||||
|
|
||||||
val dataReadRspTwoEnable = !io.cpu.writeBack.isStuck
|
|
||||||
val dataReadRspTwo = Bypasser.writeFirstRegWrap(
|
|
||||||
sample = dataReadRspTwoEnable,
|
|
||||||
sampleAddress = dataReadRspOneAddress,
|
|
||||||
sampleLastAddress = RegNextWhen(dataReadRspOneAddress, dataReadRspTwoEnable),
|
|
||||||
sampleData = dataReadRspOne,
|
|
||||||
writeValid = dataWriteCmd.valid,
|
|
||||||
writeAddress = dataWriteCmd.address,
|
|
||||||
writeData = dataWriteCmd.data,
|
|
||||||
writeMask = dataWriteCmd.mask
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
when(io.cpu.execute.isValid && !io.cpu.execute.isStuck){
|
|
||||||
tagsReadCmd.valid := True
|
|
||||||
tagsReadCmd.payload := io.cpu.execute.address(lineRange)
|
|
||||||
|
|
||||||
dataReadCmd.valid := True
|
|
||||||
dataReadCmd.payload := io.cpu.execute.address(lineRange.high downto wordRange.low) //TODO FMAX maybe critical path could be default
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val victim = new Area{
|
|
||||||
val requestIn = Stream(cloneable(new Bundle{
|
|
||||||
// val way = UInt(log2Up(wayCount) bits)
|
|
||||||
val address = UInt(p.addressWidth bits)
|
|
||||||
}))
|
|
||||||
requestIn.valid := False
|
|
||||||
requestIn.payload.assignDontCare()
|
|
||||||
|
|
||||||
val request = requestIn.stage() //TODO FMAX half pipe ?
|
|
||||||
request.ready := False
|
|
||||||
|
|
||||||
val buffer = Mem(Bits(p.memDataWidth bits),memTransactionPerLine << tagSizeShift) // WARNING << tagSizeShift could resolve cyclone II issue, //.add(new AttributeString("ramstyle","M4K"))
|
|
||||||
|
|
||||||
//Send line read commands to fill the buffer
|
|
||||||
val readLineCmdCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
|
||||||
val dataReadCmdOccure = False
|
|
||||||
val dataReadRestored = RegInit(False)
|
|
||||||
when(request.valid){
|
|
||||||
when(!readLineCmdCounter.msb) {
|
|
||||||
readLineCmdCounter := readLineCmdCounter + 1
|
|
||||||
//dataReadCmd := request.address(lineRange.high downto wordRange.low) Done in the manager
|
|
||||||
dataReadCmdOccure := True
|
|
||||||
dataReadCmd.valid := True
|
|
||||||
dataReadCmd.payload := request.address(lineRange) @@ readLineCmdCounter(readLineCmdCounter.high - 1 downto 0)
|
|
||||||
way.dataReadRspOneKeepAddress := True
|
|
||||||
} otherwise {
|
|
||||||
when(!dataReadRestored) {
|
|
||||||
dataReadCmd.valid := True
|
|
||||||
dataReadCmd.payload := way.dataReadRspOneAddress //Restore stage one readed value
|
|
||||||
assert(io.cpu.memory.isStuck,"Should not issue instructions when a victim line is not entirly in the victim cache",FAILURE)
|
|
||||||
}
|
|
||||||
dataReadRestored := True
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataReadRestored clearWhen(request.ready)
|
|
||||||
|
|
||||||
//Fill the buffer with line read responses
|
|
||||||
val readLineRspCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
|
||||||
when(Delay(dataReadCmdOccure,1, init=False)){
|
|
||||||
buffer(readLineRspCounter.resized) := way.dataReadRspOneWithoutBypass
|
|
||||||
readLineRspCounter := readLineRspCounter + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send buffer read commands
|
|
||||||
val bufferReadCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
|
||||||
val bufferReadStream = Stream(buffer.addressType)
|
|
||||||
bufferReadStream.valid := readLineRspCounter > bufferReadCounter
|
|
||||||
bufferReadStream.payload := bufferReadCounter.resized
|
|
||||||
when(bufferReadStream.fire){
|
|
||||||
bufferReadCounter := bufferReadCounter + 1
|
|
||||||
}
|
|
||||||
val bufferReaded = buffer.streamReadSync(bufferReadStream).stage
|
|
||||||
bufferReaded.ready := False
|
|
||||||
|
|
||||||
//Send memory writes from bufffer read responses
|
|
||||||
val bufferReadedCounter = Reg(UInt(log2Up(memTransactionPerLine) bits)) init(0)
|
|
||||||
val memCmdAlreadyUsed = False
|
|
||||||
when(bufferReaded.valid) {
|
|
||||||
io.mem.cmd.valid := True
|
|
||||||
io.mem.cmd.wr := True
|
|
||||||
io.mem.cmd.address := request.address(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
|
||||||
io.mem.cmd.length := p.burstLength
|
|
||||||
io.mem.cmd.data := bufferReaded.payload
|
|
||||||
io.mem.cmd.mask := (1<<(wordWidth/8))-1
|
|
||||||
|
|
||||||
when(!memCmdAlreadyUsed && io.mem.cmd.ready){
|
|
||||||
bufferReaded.ready := True
|
|
||||||
bufferReadedCounter := bufferReadedCounter + 1
|
|
||||||
when(bufferReadedCounter === bufferReadedCounter.maxValue){
|
|
||||||
request.ready := True
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val counter = Counter(memTransactionPerLine)
|
|
||||||
when(request.ready){
|
|
||||||
readLineCmdCounter.msb := False
|
|
||||||
readLineRspCounter.msb := False
|
|
||||||
bufferReadCounter.msb := False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val stageA = new Area{
|
|
||||||
val request = RegNextWhen(io.cpu.execute.args, !io.cpu.memory.isStuck)
|
|
||||||
io.cpu.memory.mmuBus.cmd.isValid := io.cpu.memory.isValid && request.kind === MEMORY //TODO filter request kind
|
|
||||||
io.cpu.memory.mmuBus.cmd.virtualAddress := request.address
|
|
||||||
io.cpu.memory.mmuBus.cmd.bypassTranslation := request.way
|
|
||||||
}
|
|
||||||
|
|
||||||
val stageB = new Area {
|
|
||||||
val request = RegNextWhen(stageA.request, !io.cpu.writeBack.isStuck)
|
|
||||||
val mmuRsp = RegNextWhen(io.cpu.memory.mmuBus.rsp, !io.cpu.writeBack.isStuck)
|
|
||||||
// val waysHit = RegNextWhen(way.tagReadRspTwoRegIn.used && stageA.mmuRsp.physicalAddress(tagRange) === way.tagReadRspTwoRegIn.address,!io.cpu.writeBack.isStuck) //Manual retiming
|
|
||||||
val waysHit = way.tagReadRspTwo.used && mmuRsp.physicalAddress(tagRange) === way.tagReadRspTwo.address
|
|
||||||
|
|
||||||
|
|
||||||
//Loader interface
|
|
||||||
val loaderValid = False
|
|
||||||
val loaderReady = False
|
|
||||||
val loadingDone = RegNext(loaderValid && loaderReady) init(False) //one cycle pulse
|
|
||||||
|
|
||||||
//delayedXX are used to relax logic timings in flush and evict modes
|
|
||||||
val delayedIsStuck = RegNext(io.cpu.writeBack.isStuck)
|
|
||||||
val delayedWaysHitValid = RegNext(waysHit)
|
|
||||||
|
|
||||||
val victimNotSent = RegInit(False) clearWhen(victim.requestIn.ready) setWhen(!io.cpu.memory.isStuck)
|
|
||||||
val loadingNotDone = RegInit(False) clearWhen(loaderReady) setWhen(!io.cpu.memory.isStuck)
|
|
||||||
|
|
||||||
val writeMask = request.size.mux (
|
|
||||||
U(0) -> B"0001",
|
|
||||||
U(1) -> B"0011",
|
|
||||||
default -> B"1111"
|
|
||||||
) |<< mmuRsp.physicalAddress(1 downto 0)
|
|
||||||
|
|
||||||
io.cpu.writeBack.haltIt := io.cpu.writeBack.isValid
|
|
||||||
io.cpu.writeBack.mmuMiss := False
|
|
||||||
io.cpu.writeBack.illegalAccess := False
|
|
||||||
io.cpu.writeBack.unalignedAccess := False
|
|
||||||
io.cpu.writeBack.accessError := (if(catchAccessError) io.mem.rsp.valid && io.mem.rsp.error else False)
|
|
||||||
io.cpu.writeBack.badAddr := request.address
|
|
||||||
|
|
||||||
//Evict the cache after reset logics
|
|
||||||
val bootEvicts = if(clearTagsAfterReset) new Area {
|
|
||||||
val valid = RegInit(True)
|
|
||||||
mmuRsp.physicalAddress init (0)
|
|
||||||
when(valid) {
|
|
||||||
tagsWriteCmd.valid := valid
|
|
||||||
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
|
||||||
tagsWriteCmd.data.used := False
|
|
||||||
when(mmuRsp.physicalAddress(lineRange) =/= lineCount - 1) {
|
|
||||||
mmuRsp.physicalAddress.getDrivingReg(lineRange) := mmuRsp.physicalAddress(lineRange) + 1
|
|
||||||
io.cpu.writeBack.haltIt := True
|
|
||||||
} otherwise {
|
|
||||||
valid := False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when(io.cpu.writeBack.isValid) {
|
|
||||||
if (catchMemoryTranslationMiss) {
|
|
||||||
io.cpu.writeBack.mmuMiss := mmuRsp.miss
|
|
||||||
}
|
|
||||||
switch(request.kind) {
|
|
||||||
is(MANAGMENT) {
|
|
||||||
when(delayedIsStuck && !mmuRsp.miss) {
|
|
||||||
when(delayedWaysHitValid || (request.way && way.tagReadRspTwo.used)) {
|
|
||||||
io.cpu.writeBack.haltIt.clearWhen(!(victim.requestIn.valid && !victim.requestIn.ready))
|
|
||||||
victim.requestIn.valid := request.clean && way.tagReadRspTwo.dirty
|
|
||||||
tagsWriteCmd.valid := victim.requestIn.ready
|
|
||||||
} otherwise{
|
|
||||||
io.cpu.writeBack.haltIt := False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
|
|
||||||
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
|
||||||
tagsWriteCmd.data.used := !request.invalidate
|
|
||||||
tagsWriteCmd.data.dirty := !request.clean
|
|
||||||
}
|
|
||||||
is(MEMORY) {
|
|
||||||
val illegal = if(catchIllegal) (request.wr && !mmuRsp.allowWrite) || (!request.wr && !mmuRsp.allowRead) else False
|
|
||||||
val unaligned = if(catchUnaligned) ((request.size === 2 && mmuRsp.physicalAddress(1 downto 0) =/= 0) || (request.size === 1 && mmuRsp.physicalAddress(0 downto 0) =/= 0)) else False
|
|
||||||
io.cpu.writeBack.illegalAccess := illegal
|
|
||||||
io.cpu.writeBack.unalignedAccess := unaligned
|
|
||||||
when((Bool(!catchMemoryTranslationMiss) || !mmuRsp.miss) && !illegal && !unaligned) {
|
|
||||||
when(request.forceUncachedAccess || mmuRsp.isIoAccess) {
|
|
||||||
val memCmdSent = RegInit(False)
|
|
||||||
when(!victim.request.valid) {
|
|
||||||
//Avoid mixing memory request while victim is pending
|
|
||||||
io.mem.cmd.wr := request.wr
|
|
||||||
io.mem.cmd.address := mmuRsp.physicalAddress(tagRange.high downto wordRange.low) @@ U(0, wordRange.low bit)
|
|
||||||
io.mem.cmd.mask := writeMask
|
|
||||||
io.mem.cmd.data := request.data
|
|
||||||
io.mem.cmd.length := 1
|
|
||||||
|
|
||||||
when(!memCmdSent) {
|
|
||||||
io.mem.cmd.valid := True
|
|
||||||
memCmdSent setWhen (io.mem.cmd.ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
io.cpu.writeBack.haltIt.clearWhen(memCmdSent && (io.mem.rsp.fire || request.wr)) //Cut mem.cmd.ready path but insert one cycle stall when write
|
|
||||||
}
|
|
||||||
memCmdSent clearWhen (!io.cpu.writeBack.isStuck)
|
|
||||||
} otherwise {
|
|
||||||
when(waysHit || !loadingNotDone) {
|
|
||||||
io.cpu.writeBack.haltIt := False
|
|
||||||
dataWriteCmd.valid := request.wr
|
|
||||||
dataWriteCmd.address := mmuRsp.physicalAddress(lineRange.high downto wordRange.low)
|
|
||||||
dataWriteCmd.data := request.data
|
|
||||||
dataWriteCmd.mask := writeMask
|
|
||||||
|
|
||||||
tagsWriteCmd.valid := (!loadingNotDone) || request.wr
|
|
||||||
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
|
||||||
tagsWriteCmd.data.used := True
|
|
||||||
tagsWriteCmd.data.dirty := request.wr
|
|
||||||
tagsWriteCmd.data.address := mmuRsp.physicalAddress(tagRange)
|
|
||||||
} otherwise {
|
|
||||||
val victimRequired = way.tagReadRspTwo.used && way.tagReadRspTwo.dirty
|
|
||||||
loaderValid := loadingNotDone && !(victimNotSent && victim.request.isStall) //Additional condition used to be sure of that all previous victim are written into the RAM
|
|
||||||
victim.requestIn.valid := victimRequired && victimNotSent
|
|
||||||
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
assert(!(io.cpu.writeBack.isValid && !io.cpu.writeBack.haltIt && io.cpu.writeBack.isStuck), "writeBack stuck by another plugin is not allowed")
|
|
||||||
io.cpu.writeBack.data := (request.forceUncachedAccess || mmuRsp.isIoAccess) ? io.mem.rsp.data | way.dataReadRspTwo //not multi ways
|
|
||||||
}
|
|
||||||
|
|
||||||
//The whole life of a loading task, the corresponding manager request is present
|
|
||||||
val loader = new Area{
|
|
||||||
val valid = RegNext(stageB.loaderValid) init(False)
|
|
||||||
val baseAddress = stageB.mmuRsp.physicalAddress
|
|
||||||
|
|
||||||
val memCmdSent = RegInit(False)
|
|
||||||
when(valid && !memCmdSent) {
|
|
||||||
io.mem.cmd.valid := True
|
|
||||||
io.mem.cmd.wr := False
|
|
||||||
io.mem.cmd.address := baseAddress(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
|
||||||
io.mem.cmd.length := p.burstLength
|
|
||||||
}
|
|
||||||
|
|
||||||
when(valid && io.mem.cmd.ready){
|
|
||||||
memCmdSent := True
|
|
||||||
}
|
|
||||||
|
|
||||||
when(valid && !memCmdSent) {
|
|
||||||
victim.memCmdAlreadyUsed := True
|
|
||||||
}
|
|
||||||
|
|
||||||
val counter = Counter(memTransactionPerLine)
|
|
||||||
when(valid && io.mem.rsp.valid){
|
|
||||||
dataWriteCmd.valid := True
|
|
||||||
dataWriteCmd.address := baseAddress(lineRange) @@ counter
|
|
||||||
dataWriteCmd.data := io.mem.rsp.data
|
|
||||||
dataWriteCmd.mask := (1<<(wordWidth/8))-1
|
|
||||||
counter.increment()
|
|
||||||
}
|
|
||||||
|
|
||||||
when(counter.willOverflow){
|
|
||||||
memCmdSent := False
|
|
||||||
valid := False
|
|
||||||
stageB.loaderReady := True
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +1,11 @@
|
||||||
package SpinalRiscv.Plugin
|
package SpinalRiscv.Plugin
|
||||||
|
|
||||||
import SpinalRiscv._
|
import SpinalRiscv._
|
||||||
|
import SpinalRiscv.ip._
|
||||||
import spinal.core._
|
import spinal.core._
|
||||||
import spinal.lib._
|
import spinal.lib._
|
||||||
|
|
||||||
|
|
||||||
case class InstructionCacheConfig( cacheSize : Int,
|
|
||||||
bytePerLine : Int,
|
|
||||||
wayCount : Int,
|
|
||||||
wrappedMemAccess : Boolean,
|
|
||||||
addressWidth : Int,
|
|
||||||
cpuDataWidth : Int,
|
|
||||||
memDataWidth : Int,
|
|
||||||
catchIllegalAccess : Boolean,
|
|
||||||
catchAccessFault : Boolean,
|
|
||||||
catchMemoryTranslationMiss : Boolean,
|
|
||||||
asyncTagMemory : Boolean,
|
|
||||||
twoStageLogic : Boolean){
|
|
||||||
def burstSize = bytePerLine*8/memDataWidth
|
|
||||||
def catchSomething = catchAccessFault || catchMemoryTranslationMiss || catchIllegalAccess
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] {
|
class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] {
|
||||||
import config._
|
import config._
|
||||||
assert(twoStageLogic || !askMemoryTranslation)
|
assert(twoStageLogic || !askMemoryTranslation)
|
||||||
|
@ -31,6 +13,7 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
||||||
var iBus : InstructionCacheMemBus = null
|
var iBus : InstructionCacheMemBus = null
|
||||||
var mmuBus : MemoryTranslatorBus = null
|
var mmuBus : MemoryTranslatorBus = null
|
||||||
var decodeExceptionPort : Flow[ExceptionCause] = null
|
var decodeExceptionPort : Flow[ExceptionCause] = null
|
||||||
|
var privilegeService : PrivilegeService = null
|
||||||
|
|
||||||
|
|
||||||
object IBUS_ACCESS_ERROR extends Stageable(Bool)
|
object IBUS_ACCESS_ERROR extends Stageable(Bool)
|
||||||
|
@ -44,6 +27,9 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
||||||
|
|
||||||
if(askMemoryTranslation)
|
if(askMemoryTranslation)
|
||||||
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(pipeline.fetch, memoryTranslatorPortConfig)
|
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(pipeline.fetch, memoryTranslatorPortConfig)
|
||||||
|
|
||||||
|
if(pipeline.serviceExist(classOf[PrivilegeService]))
|
||||||
|
privilegeService = pipeline.service(classOf[PrivilegeService])
|
||||||
}
|
}
|
||||||
|
|
||||||
override def build(pipeline: VexRiscv): Unit = {
|
override def build(pipeline: VexRiscv): Unit = {
|
||||||
|
@ -88,6 +74,7 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
||||||
cache.io.cpu.decode.isValid := decode.arbitration.isValid
|
cache.io.cpu.decode.isValid := decode.arbitration.isValid
|
||||||
decode.arbitration.haltIt.setWhen(cache.io.cpu.decode.haltIt)
|
decode.arbitration.haltIt.setWhen(cache.io.cpu.decode.haltIt)
|
||||||
cache.io.cpu.decode.isStuck := decode.arbitration.isStuck
|
cache.io.cpu.decode.isStuck := decode.arbitration.isStuck
|
||||||
|
cache.io.cpu.decode.isUser := (if(privilegeService != null) privilegeService.isUser(writeBack) else False)
|
||||||
cache.io.cpu.decode.address := decode.input(PC)
|
cache.io.cpu.decode.address := decode.input(PC)
|
||||||
decode.insert(INSTRUCTION) := cache.io.cpu.decode.data
|
decode.insert(INSTRUCTION) := cache.io.cpu.decode.data
|
||||||
decode.insert(INSTRUCTION_ANTICIPATED) := cache.io.cpu.decode.dataAnticipated
|
decode.insert(INSTRUCTION_ANTICIPATED) := cache.io.cpu.decode.dataAnticipated
|
||||||
|
@ -111,403 +98,3 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
case class InstructionCacheCpuPrefetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val isValid = Bool
|
|
||||||
val isFiring = Bool
|
|
||||||
val haltIt = Bool
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid, isFiring, address)
|
|
||||||
in(haltIt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheCpuFetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave {
|
|
||||||
val isValid = Bool
|
|
||||||
val haltIt = if(!p.twoStageLogic) Bool else null
|
|
||||||
val isStuck = Bool
|
|
||||||
val isStuckByOthers = if(!p.twoStageLogic) Bool else null
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
val data = if(!p.twoStageLogic) Bits(32 bit) else null
|
|
||||||
val error = if(!p.twoStageLogic && p.catchAccessFault) Bool else null
|
|
||||||
val mmuBus = if(p.twoStageLogic) MemoryTranslatorBus() else null
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid, isStuck, address)
|
|
||||||
outWithNull(isStuckByOthers)
|
|
||||||
inWithNull(error,data,haltIt)
|
|
||||||
slaveWithNull(mmuBus)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheCpuDecode(p : InstructionCacheConfig) extends Bundle with IMasterSlave {
|
|
||||||
require(p.twoStageLogic)
|
|
||||||
val isValid = Bool
|
|
||||||
val haltIt = Bool
|
|
||||||
val isStuck = Bool
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
val data = Bits(32 bit)
|
|
||||||
val dataAnticipated = Bits(32 bits)
|
|
||||||
val error = if(p.catchAccessFault) Bool else null
|
|
||||||
val mmuMiss = if(p.catchMemoryTranslationMiss) Bool else null
|
|
||||||
val illegalAccess = if(p.catchIllegalAccess) Bool else null
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
out(isValid, isStuck, address)
|
|
||||||
in(haltIt, data, dataAnticipated)
|
|
||||||
inWithNull(error,mmuMiss,illegalAccess)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheCpuBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val prefetch = InstructionCacheCpuPrefetch(p)
|
|
||||||
val fetch = InstructionCacheCpuFetch(p)
|
|
||||||
val decode = if(p.twoStageLogic) InstructionCacheCpuDecode(p) else null
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(prefetch)
|
|
||||||
master(fetch)
|
|
||||||
if(p.twoStageLogic) master(decode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheMemCmd(p : InstructionCacheConfig) extends Bundle{
|
|
||||||
val address = UInt(p.addressWidth bit)
|
|
||||||
}
|
|
||||||
case class InstructionCacheMemRsp(p : InstructionCacheConfig) extends Bundle{
|
|
||||||
val data = Bits(32 bit)
|
|
||||||
val error = Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheMemBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
|
||||||
val cmd = Stream (InstructionCacheMemCmd(p))
|
|
||||||
val rsp = Flow (InstructionCacheMemRsp(p))
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(cmd)
|
|
||||||
slave(rsp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class InstructionCacheFlushBus() extends Bundle with IMasterSlave{
|
|
||||||
val cmd = Event
|
|
||||||
val rsp = Bool
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(cmd)
|
|
||||||
in(rsp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InstructionCache(p : InstructionCacheConfig) extends Component{
|
|
||||||
import p._
|
|
||||||
assert(wayCount == 1)
|
|
||||||
assert(cpuDataWidth == memDataWidth)
|
|
||||||
val io = new Bundle{
|
|
||||||
val flush = slave(InstructionCacheFlushBus())
|
|
||||||
// val translator = master(InstructionCacheTranslationBus(p))
|
|
||||||
val cpu = slave(InstructionCacheCpuBus(p))
|
|
||||||
val mem = master(InstructionCacheMemBus(p))
|
|
||||||
}
|
|
||||||
// val haltCpu = False
|
|
||||||
val lineWidth = bytePerLine*8
|
|
||||||
val lineCount = cacheSize/bytePerLine
|
|
||||||
val wordWidth = Math.max(memDataWidth,32)
|
|
||||||
val wordWidthLog2 = log2Up(wordWidth)
|
|
||||||
val wordPerLine = lineWidth/wordWidth
|
|
||||||
val bytePerWord = wordWidth/8
|
|
||||||
val wayLineCount = lineCount/wayCount
|
|
||||||
val wayLineLog2 = log2Up(wayLineCount)
|
|
||||||
val wayWordCount = wayLineCount * wordPerLine
|
|
||||||
|
|
||||||
val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine)
|
|
||||||
val lineRange = tagRange.low-1 downto log2Up(bytePerLine)
|
|
||||||
val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord)
|
|
||||||
val tagLineRange = tagRange.high downto lineRange.low
|
|
||||||
val lineWordRange = lineRange.high downto wordRange.low
|
|
||||||
|
|
||||||
class LineInfo extends Bundle{
|
|
||||||
val valid = Bool
|
|
||||||
val loading = Bool
|
|
||||||
val error = if(catchAccessFault) Bool else null
|
|
||||||
val address = UInt(tagRange.length bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LineInfoWithHit extends LineInfo{
|
|
||||||
val hit = Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
def LineInfoWithHit(lineInfo : LineInfo, testTag : UInt) = {
|
|
||||||
val ret = new LineInfoWithHit()
|
|
||||||
ret.assignSomeByName(lineInfo)
|
|
||||||
ret.hit := lineInfo.valid && lineInfo.address === testTag
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val ways = Array.fill(wayCount)(new Area{
|
|
||||||
val tags = Mem(new LineInfo(),wayLineCount)
|
|
||||||
val datas = Mem(Bits(wordWidth bits),wayWordCount)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
io.cpu.prefetch.haltIt := False
|
|
||||||
|
|
||||||
val lineLoader = new Area{
|
|
||||||
val requestIn = Stream(wrap(new Bundle{
|
|
||||||
val addr = UInt(addressWidth bits)
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val flushCounter = Reg(UInt(log2Up(wayLineCount) + 1 bit)) init(0)
|
|
||||||
when(!flushCounter.msb){
|
|
||||||
io.cpu.prefetch.haltIt := True
|
|
||||||
flushCounter := flushCounter + 1
|
|
||||||
}
|
|
||||||
when(!RegNext(flushCounter.msb)){
|
|
||||||
io.cpu.prefetch.haltIt := True
|
|
||||||
}
|
|
||||||
val flushFromInterface = RegInit(False)
|
|
||||||
when(io.flush.cmd.valid){
|
|
||||||
io.cpu.prefetch.haltIt := True
|
|
||||||
when(io.flush.cmd.ready){
|
|
||||||
flushCounter := 0
|
|
||||||
flushFromInterface := True
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
io.flush.rsp := flushCounter.msb.rise && flushFromInterface
|
|
||||||
|
|
||||||
val loadingWithErrorReg = if(catchAccessFault) RegInit(False) else null
|
|
||||||
val loadingWithError = if(catchAccessFault) Bool else null
|
|
||||||
if(catchAccessFault) {
|
|
||||||
loadingWithError := loadingWithErrorReg
|
|
||||||
loadingWithErrorReg := loadingWithError
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val request = requestIn.stage()
|
|
||||||
|
|
||||||
|
|
||||||
//Send memory requests
|
|
||||||
val memCmdSended = RegInit(False) setWhen(io.mem.cmd.fire)
|
|
||||||
io.mem.cmd.valid := request.valid && !memCmdSended
|
|
||||||
if(wrappedMemAccess)
|
|
||||||
io.mem.cmd.address := request.addr(tagRange.high downto wordRange.low) @@ U(0,wordRange.low bit)
|
|
||||||
else
|
|
||||||
io.mem.cmd.address := request.addr(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
|
||||||
|
|
||||||
val wordIndex = Reg(UInt(log2Up(wordPerLine) bit))
|
|
||||||
val loadedWordsNext = Bits(wordPerLine bit)
|
|
||||||
val loadedWords = RegNext(loadedWordsNext)
|
|
||||||
val loadedWordsReadable = RegNext(loadedWords)
|
|
||||||
loadedWordsNext := loadedWords
|
|
||||||
|
|
||||||
val waysDatasWritePort = ways(0).datas.writePort //Not multi ways
|
|
||||||
waysDatasWritePort.valid := io.mem.rsp.valid
|
|
||||||
waysDatasWritePort.address := request.addr(lineRange) @@ wordIndex
|
|
||||||
waysDatasWritePort.data := io.mem.rsp.data
|
|
||||||
when(io.mem.rsp.valid){
|
|
||||||
wordIndex := wordIndex + 1
|
|
||||||
loadedWordsNext(wordIndex) := True
|
|
||||||
if(catchAccessFault) loadingWithError setWhen io.mem.rsp.error
|
|
||||||
}
|
|
||||||
|
|
||||||
val memRspLast = loadedWordsNext === B(loadedWordsNext.range -> true)
|
|
||||||
|
|
||||||
val readyDelay = Reg(UInt(1 bit))
|
|
||||||
when(memRspLast){
|
|
||||||
readyDelay := readyDelay + 1
|
|
||||||
}
|
|
||||||
request.ready := readyDelay === 1
|
|
||||||
|
|
||||||
val waysTagsWritePort = ways(0).tags.writePort //not multi way
|
|
||||||
waysTagsWritePort.valid := io.mem.rsp.valid || !flushCounter.msb
|
|
||||||
waysTagsWritePort.address := Mux(flushCounter.msb,request.addr(lineRange),flushCounter(flushCounter.high-1 downto 0))
|
|
||||||
waysTagsWritePort.data.valid := flushCounter.msb
|
|
||||||
waysTagsWritePort.data.address := request.addr(tagRange)
|
|
||||||
waysTagsWritePort.data.loading := !memRspLast
|
|
||||||
if(catchAccessFault) waysTagsWritePort.data.error := loadingWithError
|
|
||||||
|
|
||||||
|
|
||||||
when(requestIn.ready){
|
|
||||||
memCmdSended := False
|
|
||||||
wordIndex := requestIn.addr(wordRange)
|
|
||||||
loadedWords := 0
|
|
||||||
loadedWordsReadable := 0
|
|
||||||
readyDelay := 0
|
|
||||||
if(catchAccessFault) loadingWithErrorReg := False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val task = if(!twoStageLogic) new Area{
|
|
||||||
val waysHitValid = False
|
|
||||||
val waysHitError = Bool.assignDontCare()
|
|
||||||
val waysHitWord = Bits(wordWidth bit)
|
|
||||||
|
|
||||||
val waysRead = for(way <- ways) yield new Area{
|
|
||||||
val readAddress = Mux(io.cpu.fetch.isStuck,io.cpu.fetch.address,io.cpu.prefetch.address) //TODO FMAX
|
|
||||||
val tag = if(asyncTagMemory)
|
|
||||||
way.tags.readAsync(io.cpu.fetch.address(lineRange),writeFirst)
|
|
||||||
else
|
|
||||||
way.tags.readSync(readAddress(lineRange),readUnderWrite = readFirst)
|
|
||||||
|
|
||||||
val data = way.datas.readSync(readAddress(lineRange.high downto wordRange.low))
|
|
||||||
waysHitWord := data //Not applicable to multi way
|
|
||||||
when(tag.valid && tag.address === io.cpu.fetch.address(tagRange)) {
|
|
||||||
waysHitValid := True
|
|
||||||
if(catchAccessFault) waysHitError := tag.error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val hit = waysHitValid && !(waysRead(0).tag.loading && !(if(asyncTagMemory) lineLoader.loadedWords else RegNext(lineLoader.loadedWords))(io.cpu.fetch.address(wordRange)))
|
|
||||||
io.cpu.fetch.haltIt := io.cpu.fetch.isValid && !hit
|
|
||||||
io.cpu.fetch.data := waysHitWord
|
|
||||||
if(catchAccessFault) io.cpu.fetch.error := waysRead(0).tag.error
|
|
||||||
lineLoader.requestIn.valid := io.cpu.fetch.isValid && !hit //TODO avoid duplicated request
|
|
||||||
lineLoader.requestIn.addr := io.cpu.fetch.address
|
|
||||||
} else new Area{
|
|
||||||
//Long readValidPath
|
|
||||||
// def writeFirstMemWrap[T <: Data](readValid : Bool, readAddress : UInt, lastAddress : UInt, readData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
|
||||||
// val hit = writeValid && (readValid ? readAddress | lastAddress) === writeAddress
|
|
||||||
// val overrideValid = RegInit(False) clearWhen(readValid) setWhen(hit)
|
|
||||||
// val overrideValue = RegNextWhen(writeData,hit)
|
|
||||||
// overrideValid ? overrideValue | readData
|
|
||||||
// }
|
|
||||||
|
|
||||||
//shot readValid path
|
|
||||||
def writeFirstMemWrap[T <: Data](readValid : Bool, readLastAddress : UInt, readData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
|
||||||
val writeSample = readValid || (writeValid && writeAddress === readLastAddress)
|
|
||||||
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
|
||||||
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
|
||||||
val writeDataReg = RegNextWhen(writeData,writeSample)
|
|
||||||
(writeValidReg && writeAddressReg === readLastAddress) ? writeDataReg | readData
|
|
||||||
}
|
|
||||||
|
|
||||||
//Long sample path
|
|
||||||
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,lastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) : (T,T) = {
|
|
||||||
// val hit = writeValid && (sample ? sampleAddress | lastAddress) === writeAddress
|
|
||||||
// val bypass = hit ? writeData | readData
|
|
||||||
// val reg = RegNextWhen(bypass,sample || hit)
|
|
||||||
// (reg,bypass)
|
|
||||||
// }
|
|
||||||
|
|
||||||
//Short sample path
|
|
||||||
def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
|
||||||
val preWrite = (writeValid && sampleAddress === writeAddress)
|
|
||||||
val postWrite = (writeValid && sampleLastAddress === writeAddress)
|
|
||||||
val bypass = (!sample || preWrite) ? writeData | readData
|
|
||||||
val regEn = sample || postWrite
|
|
||||||
val reg = RegNextWhen(bypass,regEn)
|
|
||||||
(reg,bypass,regEn,preWrite,postWrite)
|
|
||||||
}
|
|
||||||
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
|
||||||
// val bypass = (!sample || (writeValid && sampleAddress === writeAddress)) ? writeData | readData
|
|
||||||
// val regEn = sample || (writeValid && sampleLastAddress === writeAddress)
|
|
||||||
// val reg = RegNextWhen(bypass,regEn)
|
|
||||||
// (reg,bypass,regEn,False,False)
|
|
||||||
// }
|
|
||||||
require(wayCount == 1)
|
|
||||||
val memRead = new Area{
|
|
||||||
val way = ways(0)
|
|
||||||
val tag = if(asyncTagMemory)
|
|
||||||
way.tags.readAsync(io.cpu.fetch.address(lineRange),writeFirst)
|
|
||||||
else
|
|
||||||
writeFirstMemWrap(
|
|
||||||
readValid = !io.cpu.fetch.isStuck,
|
|
||||||
// readAddress = io.cpu.prefetch.address(lineRange),
|
|
||||||
readLastAddress = io.cpu.fetch.address(lineRange),
|
|
||||||
readData = way.tags.readSync(io.cpu.prefetch.address(lineRange),enable = !io.cpu.fetch.isStuck),
|
|
||||||
writeValid = lineLoader.waysTagsWritePort.valid,
|
|
||||||
writeAddress = lineLoader.waysTagsWritePort.address,
|
|
||||||
writeData = lineLoader.waysTagsWritePort.data
|
|
||||||
)
|
|
||||||
|
|
||||||
val data = writeFirstMemWrap(
|
|
||||||
readValid = !io.cpu.fetch.isStuck,
|
|
||||||
// readAddress = io.cpu.prefetch.address(lineWordRange),
|
|
||||||
readLastAddress = io.cpu.fetch.address(lineWordRange),
|
|
||||||
readData = way.datas.readSync(io.cpu.prefetch.address(lineWordRange),enable = !io.cpu.fetch.isStuck),
|
|
||||||
writeValid = lineLoader.waysDatasWritePort.valid,
|
|
||||||
writeAddress = lineLoader.waysDatasWritePort.address,
|
|
||||||
writeData = lineLoader.waysDatasWritePort.data
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val tag = writeFirstRegWrap(
|
|
||||||
sample = !io.cpu.decode.isStuck,
|
|
||||||
sampleAddress = io.cpu.fetch.address(lineRange),
|
|
||||||
sampleLastAddress = io.cpu.decode.address(lineRange),
|
|
||||||
readData = LineInfoWithHit(memRead.tag,io.cpu.fetch.address(tagRange)),
|
|
||||||
writeValid = lineLoader.waysTagsWritePort.valid,
|
|
||||||
writeAddress = lineLoader.waysTagsWritePort.address,
|
|
||||||
writeData = LineInfoWithHit(lineLoader.waysTagsWritePort.data,io.cpu.fetch.address(tagRange)) //TODO wrong address src
|
|
||||||
)._1
|
|
||||||
|
|
||||||
val (data,dataRegIn,dataRegEn,dataPreWrite,dataPostWrite) = writeFirstRegWrap(
|
|
||||||
sample = !io.cpu.decode.isStuck,
|
|
||||||
sampleAddress = io.cpu.fetch.address(lineWordRange),
|
|
||||||
sampleLastAddress = io.cpu.decode.address(lineWordRange),
|
|
||||||
readData = memRead.data,
|
|
||||||
writeValid = lineLoader.waysDatasWritePort.valid,
|
|
||||||
writeAddress = lineLoader.waysDatasWritePort.address,
|
|
||||||
writeData = lineLoader.waysDatasWritePort.data
|
|
||||||
)
|
|
||||||
|
|
||||||
io.cpu.fetch.mmuBus.cmd.isValid := io.cpu.fetch.isValid
|
|
||||||
io.cpu.fetch.mmuBus.cmd.virtualAddress := io.cpu.fetch.address
|
|
||||||
io.cpu.fetch.mmuBus.cmd.bypassTranslation := False
|
|
||||||
val mmuRsp = RegNextWhen(io.cpu.fetch.mmuBus.rsp,!io.cpu.decode.isStuck)
|
|
||||||
|
|
||||||
val hit = tag.valid && tag.address === mmuRsp.physicalAddress(tagRange) && !(tag.loading && !lineLoader.loadedWords(mmuRsp.physicalAddress(wordRange)))
|
|
||||||
// val hit = tag.hit && !(tag.loading && !lineLoader.loadedWords(mmuRsp.physicalAddress(wordRange)))
|
|
||||||
|
|
||||||
io.cpu.decode.haltIt := io.cpu.decode.isValid && !hit //TODO PERF not halit it when removed, Should probably be applyed in many other places
|
|
||||||
io.cpu.decode.data := data
|
|
||||||
// io.cpu.decode.dataAnticipated := dataRegEn ? dataRegIn | data
|
|
||||||
io.cpu.decode.dataAnticipated := io.cpu.decode.isStuck ? Mux(dataPostWrite,lineLoader.waysDatasWritePort.data,data) | Mux(dataPreWrite,lineLoader.waysDatasWritePort.data,memRead.data)
|
|
||||||
if(catchAccessFault) io.cpu.decode.error := tag.error
|
|
||||||
if(catchMemoryTranslationMiss) io.cpu.decode.mmuMiss := mmuRsp.miss
|
|
||||||
if(catchIllegalAccess) io.cpu.decode.illegalAccess := !mmuRsp.miss && !mmuRsp.allowExecute
|
|
||||||
|
|
||||||
lineLoader.requestIn.valid := io.cpu.decode.isValid && !hit && !mmuRsp.miss//TODO avoid duplicated request
|
|
||||||
lineLoader.requestIn.addr := mmuRsp.physicalAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
io.flush.cmd.ready := !(lineLoader.request.valid || io.cpu.fetch.isValid)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//object InstructionCacheMain{
|
|
||||||
//
|
|
||||||
// def main(args: Array[String]) {
|
|
||||||
// implicit val p = InstructionCacheConfig(
|
|
||||||
// cacheSize =4096,
|
|
||||||
// bytePerLine =32,
|
|
||||||
// wayCount = 1,
|
|
||||||
// wrappedMemAccess = true,
|
|
||||||
// addressWidth = 32,
|
|
||||||
// cpuDataWidth = 32,
|
|
||||||
// memDataWidth = 32,
|
|
||||||
// catchAccessFault = true)
|
|
||||||
// // val io = new Bundle{
|
|
||||||
// // val cpu = slave(InstructionCacheCpuBus())
|
|
||||||
// // val mem = master(InstructionCacheMemBus())
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// SpinalVhdl(new InstructionCache(p))
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ case class CsrMapping(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with ExceptionService {
|
class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with ExceptionService with PrivilegeService{
|
||||||
import config._
|
import config._
|
||||||
import CsrAccess._
|
import CsrAccess._
|
||||||
|
|
||||||
|
@ -144,6 +144,8 @@ class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with Except
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def isUser(stage : Stage) : Bool = False
|
||||||
|
|
||||||
override def build(pipeline: VexRiscv): Unit = {
|
override def build(pipeline: VexRiscv): Unit = {
|
||||||
import pipeline._
|
import pipeline._
|
||||||
import pipeline.config._
|
import pipeline.config._
|
||||||
|
|
|
@ -47,7 +47,7 @@ class MemoryTranslatorPlugin(tlbSize : Int,
|
||||||
val valid = Bool
|
val valid = Bool
|
||||||
val virtualAddress = UInt(20 bits)
|
val virtualAddress = UInt(20 bits)
|
||||||
val physicalAddress = UInt(20 bits)
|
val physicalAddress = UInt(20 bits)
|
||||||
val allowRead, allowWrite, allowExecute = Bool
|
val allowRead, allowWrite, allowExecute, allowUser = Bool
|
||||||
|
|
||||||
def init = {
|
def init = {
|
||||||
valid init (False)
|
valid init (False)
|
||||||
|
@ -107,12 +107,14 @@ class MemoryTranslatorPlugin(tlbSize : Int,
|
||||||
port.bus.rsp.allowRead := cacheLine.allowRead
|
port.bus.rsp.allowRead := cacheLine.allowRead
|
||||||
port.bus.rsp.allowWrite := cacheLine.allowWrite
|
port.bus.rsp.allowWrite := cacheLine.allowWrite
|
||||||
port.bus.rsp.allowExecute := cacheLine.allowExecute
|
port.bus.rsp.allowExecute := cacheLine.allowExecute
|
||||||
|
port.bus.rsp.allowUser := cacheLine.allowUser
|
||||||
port.stage.arbitration.haltIt setWhen (port.bus.cmd.isValid && !cacheHit && !sharedMiss)
|
port.stage.arbitration.haltIt setWhen (port.bus.cmd.isValid && !cacheHit && !sharedMiss)
|
||||||
} otherwise {
|
} otherwise {
|
||||||
port.bus.rsp.physicalAddress := port.bus.cmd.virtualAddress
|
port.bus.rsp.physicalAddress := port.bus.cmd.virtualAddress
|
||||||
port.bus.rsp.allowRead := True
|
port.bus.rsp.allowRead := True
|
||||||
port.bus.rsp.allowWrite := True
|
port.bus.rsp.allowWrite := True
|
||||||
port.bus.rsp.allowExecute := True
|
port.bus.rsp.allowExecute := True
|
||||||
|
port.bus.rsp.allowUser := True
|
||||||
}
|
}
|
||||||
port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
|
port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
|
||||||
port.bus.rsp.miss := sharedMiss
|
port.bus.rsp.miss := sharedMiss
|
||||||
|
@ -132,10 +134,11 @@ class MemoryTranslatorPlugin(tlbSize : Int,
|
||||||
val line = CacheLine()
|
val line = CacheLine()
|
||||||
line.virtualAddress := tlbWriteBuffer
|
line.virtualAddress := tlbWriteBuffer
|
||||||
line.physicalAddress := input(REG2)(19 downto 0).asUInt
|
line.physicalAddress := input(REG2)(19 downto 0).asUInt
|
||||||
line.valid := input(REG2)(31)
|
line.allowUser := input(REG2)(27)
|
||||||
line.allowRead := input(REG2)(28)
|
line.allowRead := input(REG2)(28)
|
||||||
line.allowWrite := input(REG2)(29)
|
line.allowWrite := input(REG2)(29)
|
||||||
line.allowExecute := input(REG2)(30)
|
line.allowExecute := input(REG2)(30)
|
||||||
|
line.valid := input(REG2)(31)
|
||||||
core.shared.cache(input(SRC1)(log2Up(tlbSize)-1 downto 0).asUInt) := line
|
core.shared.cache(input(SRC1)(log2Up(tlbSize)-1 downto 0).asUInt) := line
|
||||||
|
|
||||||
core.ports.foreach(_.cache.foreach(_.valid := False)) //Invalidate all ports caches
|
core.ports.foreach(_.cache.foreach(_.valid := False)) //Invalidate all ports caches
|
||||||
|
|
|
@ -22,6 +22,10 @@ trait ExceptionService{
|
||||||
def newExceptionPort(stage : Stage, priority : Int = 0) : Flow[ExceptionCause]
|
def newExceptionPort(stage : Stage, priority : Int = 0) : Flow[ExceptionCause]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait PrivilegeService{
|
||||||
|
def isUser(stage : Stage) : Bool
|
||||||
|
}
|
||||||
|
|
||||||
case class MemoryTranslatorCmd() extends Bundle{
|
case class MemoryTranslatorCmd() extends Bundle{
|
||||||
val isValid = Bool
|
val isValid = Bool
|
||||||
val virtualAddress = UInt(32 bits)
|
val virtualAddress = UInt(32 bits)
|
||||||
|
@ -30,7 +34,7 @@ case class MemoryTranslatorCmd() extends Bundle{
|
||||||
case class MemoryTranslatorRsp() extends Bundle{
|
case class MemoryTranslatorRsp() extends Bundle{
|
||||||
val physicalAddress = UInt(32 bits)
|
val physicalAddress = UInt(32 bits)
|
||||||
val isIoAccess = Bool
|
val isIoAccess = Bool
|
||||||
val allowRead, allowWrite, allowExecute = Bool
|
val allowRead, allowWrite, allowExecute, allowUser = Bool
|
||||||
val miss = Bool
|
val miss = Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ package SpinalRiscv
|
||||||
import SpinalRiscv.Plugin._
|
import SpinalRiscv.Plugin._
|
||||||
import spinal.core._
|
import spinal.core._
|
||||||
import spinal.lib._
|
import spinal.lib._
|
||||||
|
import SpinalRiscv.ip._
|
||||||
|
|
||||||
object TopLevel {
|
object TopLevel {
|
||||||
def main(args: Array[String]) {
|
def main(args: Array[String]) {
|
||||||
|
|
|
@ -0,0 +1,577 @@
|
||||||
|
package SpinalRiscv.ip
|
||||||
|
|
||||||
|
import SpinalRiscv._
|
||||||
|
import spinal.core._
|
||||||
|
import spinal.lib._
|
||||||
|
|
||||||
|
|
||||||
|
case class DataCacheConfig( cacheSize : Int,
|
||||||
|
bytePerLine : Int,
|
||||||
|
wayCount : Int,
|
||||||
|
addressWidth : Int,
|
||||||
|
cpuDataWidth : Int,
|
||||||
|
memDataWidth : Int,
|
||||||
|
catchAccessError : Boolean,
|
||||||
|
catchIllegal : Boolean,
|
||||||
|
catchUnaligned : Boolean,
|
||||||
|
catchMemoryTranslationMiss : Boolean,
|
||||||
|
clearTagsAfterReset : Boolean = true,
|
||||||
|
tagSizeShift : Int = 0){ //Used to force infering ram
|
||||||
|
def burstSize = bytePerLine*8/memDataWidth
|
||||||
|
val burstLength = bytePerLine/(memDataWidth/8)
|
||||||
|
def catchSomething = catchUnaligned || catchMemoryTranslationMiss || catchIllegal || catchAccessError
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object Bypasser{
|
||||||
|
|
||||||
|
//shot readValid path
|
||||||
|
def writeFirstMemWrap[T <: Data](readValid : Bool, readLastAddress : UInt, readLastData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
||||||
|
val writeSample = readValid || (writeValid && writeAddress === readLastAddress)
|
||||||
|
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
||||||
|
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
||||||
|
val writeDataReg = RegNextWhen(writeData,writeSample)
|
||||||
|
(writeValidReg && writeAddressReg === readLastAddress) ? writeDataReg | readLastData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//shot readValid path
|
||||||
|
def writeFirstMemWrap(readValid : Bool, readLastAddress : UInt, readLastData : Bits,writeValid : Bool, writeAddress : UInt, writeData : Bits,writeMask : Bits) : Bits = {
|
||||||
|
val writeHit = writeValid && writeAddress === readLastAddress
|
||||||
|
val writeSample = readValid || writeHit
|
||||||
|
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
||||||
|
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
||||||
|
val writeDataReg = Reg(writeData)
|
||||||
|
val writeMaskReg = Reg(Bits(widthOf(writeData)/8 bits))
|
||||||
|
val writeDataRegBytes = writeDataReg.subdivideIn(8 bits)
|
||||||
|
val writeDataBytes = writeData.subdivideIn(8 bits)
|
||||||
|
val ret = cloneOf(readLastData)
|
||||||
|
val retBytes = ret.subdivideIn(8 bits)
|
||||||
|
val readLastDataBytes = readLastData.subdivideIn(8 bits)
|
||||||
|
val writeRegHit = writeValidReg && writeAddressReg === readLastAddress
|
||||||
|
for(b <- writeMask.range){
|
||||||
|
when(writeHit && writeMask(b)){
|
||||||
|
writeMaskReg(b) := True
|
||||||
|
}
|
||||||
|
when(readValid) {
|
||||||
|
writeMaskReg(b) := writeMask(b)
|
||||||
|
}
|
||||||
|
when(readValid || (writeHit && writeMask(b))){
|
||||||
|
writeDataRegBytes(b) := writeDataBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
retBytes(b) := (writeRegHit && writeMaskReg(b)) ? writeDataRegBytes(b) | readLastDataBytes(b)
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
//Long sample path
|
||||||
|
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,lastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) : (T,T) = {
|
||||||
|
// val hit = writeValid && (sample ? sampleAddress | lastAddress) === writeAddress
|
||||||
|
// val bypass = hit ? writeData | readData
|
||||||
|
// val reg = RegNextWhen(bypass,sample || hit)
|
||||||
|
// (reg,bypass)
|
||||||
|
// }
|
||||||
|
|
||||||
|
//Short sample path
|
||||||
|
def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, sampleData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
||||||
|
val bypass = (!sample || (writeValid && sampleAddress === writeAddress)) ? writeData | sampleData
|
||||||
|
val regEn = sample || (writeValid && sampleLastAddress === writeAddress)
|
||||||
|
val reg = RegNextWhen(bypass,regEn)
|
||||||
|
reg
|
||||||
|
}
|
||||||
|
|
||||||
|
def writeFirstRegWrap(sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, sampleData : Bits, writeValid : Bool, writeAddress : UInt, writeData : Bits,writeMask : Bits) = {
|
||||||
|
val byteCount = widthOf(writeMask)
|
||||||
|
val sampleWriteHit = writeValid && sampleAddress === writeAddress
|
||||||
|
val sampleLastHit = writeValid && sampleLastAddress === writeAddress
|
||||||
|
val regBytes = Vec(Bits(8 bits),byteCount)
|
||||||
|
for(b <- writeMask.range){
|
||||||
|
val bypass = Mux(!sample || (sampleWriteHit && writeMask(b)), writeData(b*8, 8 bits), sampleData(b*8, 8 bits))
|
||||||
|
val regEn = sample || (sampleLastHit && writeMask(b))
|
||||||
|
regBytes(b) := RegNextWhen(bypass,regEn)
|
||||||
|
}
|
||||||
|
regBytes.asBits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DataCacheCpuCmdKind extends SpinalEnum{
|
||||||
|
val MEMORY,MANAGMENT = newElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
object DataCacheCpuExecute{
|
||||||
|
implicit def implArgs(that : DataCacheCpuExecute) = that.args
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataCacheCpuExecute(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val isValid = Bool
|
||||||
|
val isStuck = Bool
|
||||||
|
// val haltIt = Bool
|
||||||
|
val args = DataCacheCpuExecuteArgs(p)
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid, isStuck, args)
|
||||||
|
// in(haltIt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataCacheCpuExecuteArgs(p : DataCacheConfig) extends Bundle{
|
||||||
|
val kind = DataCacheCpuCmdKind()
|
||||||
|
val wr = Bool
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
val data = Bits(p.cpuDataWidth bit)
|
||||||
|
val size = UInt(2 bits)
|
||||||
|
val forceUncachedAccess = Bool
|
||||||
|
val clean, invalidate, way = Bool
|
||||||
|
// val all = Bool //Address should be zero when "all" is used
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataCacheCpuMemory(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val isValid = Bool
|
||||||
|
val isStuck = Bool
|
||||||
|
val isRemoved = Bool
|
||||||
|
val mmuBus = MemoryTranslatorBus()
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid, isStuck, isRemoved)
|
||||||
|
slave(mmuBus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val isValid = Bool
|
||||||
|
val isStuck = Bool
|
||||||
|
val isUser = Bool
|
||||||
|
val haltIt = Bool
|
||||||
|
val data = Bits(p.cpuDataWidth bit)
|
||||||
|
val mmuMiss, illegalAccess, unalignedAccess , accessError = Bool
|
||||||
|
val badAddr = UInt(32 bits)
|
||||||
|
// val exceptionBus = if(p.catchSomething) Flow(ExceptionCause()) else null
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid,isStuck,isUser)
|
||||||
|
in(haltIt, data, mmuMiss,illegalAccess , unalignedAccess, accessError, badAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataCacheCpuBus(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val execute = DataCacheCpuExecute(p)
|
||||||
|
val memory = DataCacheCpuMemory(p)
|
||||||
|
val writeBack = DataCacheCpuWriteBack(p)
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(execute)
|
||||||
|
master(memory)
|
||||||
|
master(writeBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case class DataCacheMemCmd(p : DataCacheConfig) extends Bundle{
|
||||||
|
val wr = Bool
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
val data = Bits(p.memDataWidth bits)
|
||||||
|
val mask = Bits(p.memDataWidth/8 bits)
|
||||||
|
val length = UInt(log2Up(p.burstLength+1) bit)
|
||||||
|
}
|
||||||
|
case class DataCacheMemRsp(p : DataCacheConfig) extends Bundle{
|
||||||
|
val data = Bits(p.memDataWidth bit)
|
||||||
|
val error = Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val cmd = Stream (DataCacheMemCmd(p))
|
||||||
|
val rsp = Flow (DataCacheMemRsp(p))
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(cmd)
|
||||||
|
slave(rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DataCache(p : DataCacheConfig) extends Component{
|
||||||
|
import p._
|
||||||
|
import DataCacheCpuCmdKind._
|
||||||
|
assert(wayCount == 1)
|
||||||
|
assert(cpuDataWidth == memDataWidth)
|
||||||
|
|
||||||
|
val io = new Bundle{
|
||||||
|
val cpu = slave(DataCacheCpuBus(p))
|
||||||
|
val mem = master(DataCacheMemBus(p))
|
||||||
|
// val flushDone = out Bool //It pulse at the same time than the manager.request.fire
|
||||||
|
}
|
||||||
|
val haltCpu = False
|
||||||
|
val lineWidth = bytePerLine*8
|
||||||
|
val lineCount = cacheSize/bytePerLine
|
||||||
|
val wordWidth = Math.max(memDataWidth,cpuDataWidth)
|
||||||
|
val wordWidthLog2 = log2Up(wordWidth)
|
||||||
|
val wordPerLine = lineWidth/wordWidth
|
||||||
|
val bytePerWord = wordWidth/8
|
||||||
|
val wayLineCount = lineCount/wayCount
|
||||||
|
val wayLineLog2 = log2Up(wayLineCount)
|
||||||
|
val wayWordCount = wayLineCount * wordPerLine
|
||||||
|
val memTransactionPerLine = p.bytePerLine / (p.memDataWidth/8)
|
||||||
|
|
||||||
|
val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine)
|
||||||
|
val lineRange = tagRange.low-1 downto log2Up(bytePerLine)
|
||||||
|
val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord)
|
||||||
|
|
||||||
|
|
||||||
|
class LineInfo() extends Bundle{
|
||||||
|
val used = Bool
|
||||||
|
val dirty = Bool
|
||||||
|
val address = UInt(tagRange.length bit)
|
||||||
|
}
|
||||||
|
|
||||||
|
val tagsReadCmd = Flow(UInt(log2Up(wayLineCount) bits))
|
||||||
|
val tagsWriteCmd = Flow(new Bundle{
|
||||||
|
// val way = UInt(log2Up(wayCount) bits)
|
||||||
|
val address = UInt(log2Up(wayLineCount) bits)
|
||||||
|
val data = new LineInfo()
|
||||||
|
})
|
||||||
|
|
||||||
|
val tagsWriteLastCmd = RegNext(tagsWriteCmd)
|
||||||
|
|
||||||
|
val dataReadCmd = Flow(UInt(log2Up(wayWordCount) bits))
|
||||||
|
val dataWriteCmd = Flow(new Bundle{
|
||||||
|
// val way = UInt(log2Up(wayCount) bits)
|
||||||
|
val address = UInt(log2Up(wayWordCount) bits)
|
||||||
|
val data = Bits(wordWidth bits)
|
||||||
|
val mask = Bits(wordWidth/8 bits)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
tagsReadCmd.valid := False
|
||||||
|
tagsReadCmd.payload.assignDontCare()
|
||||||
|
dataReadCmd.valid := False
|
||||||
|
dataReadCmd.payload.assignDontCare()
|
||||||
|
tagsWriteCmd.valid := False
|
||||||
|
tagsWriteCmd.payload.assignDontCare()
|
||||||
|
dataWriteCmd.valid := False
|
||||||
|
dataWriteCmd.payload.assignDontCare()
|
||||||
|
io.mem.cmd.valid := False
|
||||||
|
io.mem.cmd.payload.assignDontCare()
|
||||||
|
|
||||||
|
|
||||||
|
val way = new Area{
|
||||||
|
val tags = Mem(new LineInfo(),wayLineCount)
|
||||||
|
val data = Mem(Bits(wordWidth bit),wayWordCount)
|
||||||
|
|
||||||
|
when(tagsWriteCmd.valid){
|
||||||
|
tags(tagsWriteCmd.address) := tagsWriteCmd.data
|
||||||
|
}
|
||||||
|
when(dataWriteCmd.valid){
|
||||||
|
data.write(
|
||||||
|
address = dataWriteCmd.address,
|
||||||
|
data = dataWriteCmd.data,
|
||||||
|
mask = dataWriteCmd.mask
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val tagReadRspOneAddress = RegNextWhen(tagsReadCmd.payload, tagsReadCmd.valid)
|
||||||
|
val tagReadRspOne = Bypasser.writeFirstMemWrap(
|
||||||
|
readValid = tagsReadCmd.valid,
|
||||||
|
readLastAddress = tagReadRspOneAddress,
|
||||||
|
readLastData = tags.readSync(tagsReadCmd.payload,enable = tagsReadCmd.valid),
|
||||||
|
writeValid = tagsWriteCmd.valid,
|
||||||
|
writeAddress = tagsWriteCmd.address,
|
||||||
|
writeData = tagsWriteCmd.data
|
||||||
|
)
|
||||||
|
|
||||||
|
val dataReadRspOneKeepAddress = False
|
||||||
|
val dataReadRspOneAddress = RegNextWhen(dataReadCmd.payload, dataReadCmd.valid && !dataReadRspOneKeepAddress)
|
||||||
|
val dataReadRspOneWithoutBypass = data.readSync(dataReadCmd.payload,enable = dataReadCmd.valid)
|
||||||
|
val dataReadRspOne = Bypasser.writeFirstMemWrap(
|
||||||
|
readValid = dataReadCmd.valid,
|
||||||
|
readLastAddress = dataReadRspOneAddress,
|
||||||
|
readLastData = dataReadRspOneWithoutBypass,
|
||||||
|
writeValid = dataWriteCmd.valid,
|
||||||
|
writeAddress = dataWriteCmd.address,
|
||||||
|
writeData = dataWriteCmd.data,
|
||||||
|
writeMask = dataWriteCmd.mask
|
||||||
|
)
|
||||||
|
|
||||||
|
val tagReadRspTwoEnable = !io.cpu.writeBack.isStuck
|
||||||
|
val tagReadRspTwoRegIn = (tagsWriteCmd.valid && tagsWriteCmd.address === tagReadRspOneAddress) ? tagsWriteCmd.data | tagReadRspOne
|
||||||
|
val tagReadRspTwo = RegNextWhen(tagReadRspTwoRegIn ,tagReadRspTwoEnable)
|
||||||
|
|
||||||
|
|
||||||
|
val dataReadRspTwoEnable = !io.cpu.writeBack.isStuck
|
||||||
|
val dataReadRspTwo = Bypasser.writeFirstRegWrap(
|
||||||
|
sample = dataReadRspTwoEnable,
|
||||||
|
sampleAddress = dataReadRspOneAddress,
|
||||||
|
sampleLastAddress = RegNextWhen(dataReadRspOneAddress, dataReadRspTwoEnable),
|
||||||
|
sampleData = dataReadRspOne,
|
||||||
|
writeValid = dataWriteCmd.valid,
|
||||||
|
writeAddress = dataWriteCmd.address,
|
||||||
|
writeData = dataWriteCmd.data,
|
||||||
|
writeMask = dataWriteCmd.mask
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
when(io.cpu.execute.isValid && !io.cpu.execute.isStuck){
|
||||||
|
tagsReadCmd.valid := True
|
||||||
|
tagsReadCmd.payload := io.cpu.execute.address(lineRange)
|
||||||
|
|
||||||
|
dataReadCmd.valid := True
|
||||||
|
dataReadCmd.payload := io.cpu.execute.address(lineRange.high downto wordRange.low) //TODO FMAX maybe critical path could be default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val victim = new Area{
|
||||||
|
val requestIn = Stream(cloneable(new Bundle{
|
||||||
|
// val way = UInt(log2Up(wayCount) bits)
|
||||||
|
val address = UInt(p.addressWidth bits)
|
||||||
|
}))
|
||||||
|
requestIn.valid := False
|
||||||
|
requestIn.payload.assignDontCare()
|
||||||
|
|
||||||
|
val request = requestIn.stage() //TODO FMAX half pipe ?
|
||||||
|
request.ready := False
|
||||||
|
|
||||||
|
val buffer = Mem(Bits(p.memDataWidth bits),memTransactionPerLine << tagSizeShift) // WARNING << tagSizeShift could resolve cyclone II issue, //.add(new AttributeString("ramstyle","M4K"))
|
||||||
|
|
||||||
|
//Send line read commands to fill the buffer
|
||||||
|
val readLineCmdCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
||||||
|
val dataReadCmdOccure = False
|
||||||
|
val dataReadRestored = RegInit(False)
|
||||||
|
when(request.valid){
|
||||||
|
when(!readLineCmdCounter.msb) {
|
||||||
|
readLineCmdCounter := readLineCmdCounter + 1
|
||||||
|
//dataReadCmd := request.address(lineRange.high downto wordRange.low) Done in the manager
|
||||||
|
dataReadCmdOccure := True
|
||||||
|
dataReadCmd.valid := True
|
||||||
|
dataReadCmd.payload := request.address(lineRange) @@ readLineCmdCounter(readLineCmdCounter.high - 1 downto 0)
|
||||||
|
way.dataReadRspOneKeepAddress := True
|
||||||
|
} otherwise {
|
||||||
|
when(!dataReadRestored) {
|
||||||
|
dataReadCmd.valid := True
|
||||||
|
dataReadCmd.payload := way.dataReadRspOneAddress //Restore stage one readed value
|
||||||
|
assert(io.cpu.memory.isStuck,"Should not issue instructions when a victim line is not entirly in the victim cache",FAILURE)
|
||||||
|
}
|
||||||
|
dataReadRestored := True
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataReadRestored clearWhen(request.ready)
|
||||||
|
|
||||||
|
//Fill the buffer with line read responses
|
||||||
|
val readLineRspCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
||||||
|
when(Delay(dataReadCmdOccure,1, init=False)){
|
||||||
|
buffer(readLineRspCounter.resized) := way.dataReadRspOneWithoutBypass
|
||||||
|
readLineRspCounter := readLineRspCounter + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send buffer read commands
|
||||||
|
val bufferReadCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
|
||||||
|
val bufferReadStream = Stream(buffer.addressType)
|
||||||
|
bufferReadStream.valid := readLineRspCounter > bufferReadCounter
|
||||||
|
bufferReadStream.payload := bufferReadCounter.resized
|
||||||
|
when(bufferReadStream.fire){
|
||||||
|
bufferReadCounter := bufferReadCounter + 1
|
||||||
|
}
|
||||||
|
val bufferReaded = buffer.streamReadSync(bufferReadStream).stage
|
||||||
|
bufferReaded.ready := False
|
||||||
|
|
||||||
|
//Send memory writes from bufffer read responses
|
||||||
|
val bufferReadedCounter = Reg(UInt(log2Up(memTransactionPerLine) bits)) init(0)
|
||||||
|
val memCmdAlreadyUsed = False
|
||||||
|
when(bufferReaded.valid) {
|
||||||
|
io.mem.cmd.valid := True
|
||||||
|
io.mem.cmd.wr := True
|
||||||
|
io.mem.cmd.address := request.address(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
||||||
|
io.mem.cmd.length := p.burstLength
|
||||||
|
io.mem.cmd.data := bufferReaded.payload
|
||||||
|
io.mem.cmd.mask := (1<<(wordWidth/8))-1
|
||||||
|
|
||||||
|
when(!memCmdAlreadyUsed && io.mem.cmd.ready){
|
||||||
|
bufferReaded.ready := True
|
||||||
|
bufferReadedCounter := bufferReadedCounter + 1
|
||||||
|
when(bufferReadedCounter === bufferReadedCounter.maxValue){
|
||||||
|
request.ready := True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val counter = Counter(memTransactionPerLine)
|
||||||
|
when(request.ready){
|
||||||
|
readLineCmdCounter.msb := False
|
||||||
|
readLineRspCounter.msb := False
|
||||||
|
bufferReadCounter.msb := False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val stageA = new Area{
|
||||||
|
val request = RegNextWhen(io.cpu.execute.args, !io.cpu.memory.isStuck)
|
||||||
|
io.cpu.memory.mmuBus.cmd.isValid := io.cpu.memory.isValid && request.kind === MEMORY //TODO filter request kind
|
||||||
|
io.cpu.memory.mmuBus.cmd.virtualAddress := request.address
|
||||||
|
io.cpu.memory.mmuBus.cmd.bypassTranslation := request.way
|
||||||
|
}
|
||||||
|
|
||||||
|
val stageB = new Area {
|
||||||
|
val request = RegNextWhen(stageA.request, !io.cpu.writeBack.isStuck)
|
||||||
|
val mmuRsp = RegNextWhen(io.cpu.memory.mmuBus.rsp, !io.cpu.writeBack.isStuck)
|
||||||
|
// val waysHit = RegNextWhen(way.tagReadRspTwoRegIn.used && stageA.mmuRsp.physicalAddress(tagRange) === way.tagReadRspTwoRegIn.address,!io.cpu.writeBack.isStuck) //Manual retiming
|
||||||
|
val waysHit = way.tagReadRspTwo.used && mmuRsp.physicalAddress(tagRange) === way.tagReadRspTwo.address
|
||||||
|
|
||||||
|
|
||||||
|
//Loader interface
|
||||||
|
val loaderValid = False
|
||||||
|
val loaderReady = False
|
||||||
|
val loadingDone = RegNext(loaderValid && loaderReady) init(False) //one cycle pulse
|
||||||
|
|
||||||
|
//delayedXX are used to relax logic timings in flush and evict modes
|
||||||
|
val delayedIsStuck = RegNext(io.cpu.writeBack.isStuck)
|
||||||
|
val delayedWaysHitValid = RegNext(waysHit)
|
||||||
|
|
||||||
|
val victimNotSent = RegInit(False) clearWhen(victim.requestIn.ready) setWhen(!io.cpu.memory.isStuck)
|
||||||
|
val loadingNotDone = RegInit(False) clearWhen(loaderReady) setWhen(!io.cpu.memory.isStuck)
|
||||||
|
|
||||||
|
val writeMask = request.size.mux (
|
||||||
|
U(0) -> B"0001",
|
||||||
|
U(1) -> B"0011",
|
||||||
|
default -> B"1111"
|
||||||
|
) |<< mmuRsp.physicalAddress(1 downto 0)
|
||||||
|
|
||||||
|
io.cpu.writeBack.haltIt := io.cpu.writeBack.isValid
|
||||||
|
io.cpu.writeBack.mmuMiss := False
|
||||||
|
io.cpu.writeBack.illegalAccess := False
|
||||||
|
io.cpu.writeBack.unalignedAccess := False
|
||||||
|
io.cpu.writeBack.accessError := (if(catchAccessError) io.mem.rsp.valid && io.mem.rsp.error else False)
|
||||||
|
io.cpu.writeBack.badAddr := request.address
|
||||||
|
|
||||||
|
//Evict the cache after reset logics
|
||||||
|
val bootEvicts = if(clearTagsAfterReset) new Area {
|
||||||
|
val valid = RegInit(True)
|
||||||
|
mmuRsp.physicalAddress init (0)
|
||||||
|
when(valid) {
|
||||||
|
tagsWriteCmd.valid := valid
|
||||||
|
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
||||||
|
tagsWriteCmd.data.used := False
|
||||||
|
when(mmuRsp.physicalAddress(lineRange) =/= lineCount - 1) {
|
||||||
|
mmuRsp.physicalAddress.getDrivingReg(lineRange) := mmuRsp.physicalAddress(lineRange) + 1
|
||||||
|
io.cpu.writeBack.haltIt := True
|
||||||
|
} otherwise {
|
||||||
|
valid := False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(io.cpu.writeBack.isValid) {
|
||||||
|
if (catchMemoryTranslationMiss) {
|
||||||
|
io.cpu.writeBack.mmuMiss := mmuRsp.miss
|
||||||
|
}
|
||||||
|
switch(request.kind) {
|
||||||
|
is(MANAGMENT) {
|
||||||
|
when(delayedIsStuck && !mmuRsp.miss) {
|
||||||
|
when(delayedWaysHitValid || (request.way && way.tagReadRspTwo.used)) {
|
||||||
|
io.cpu.writeBack.haltIt.clearWhen(!(victim.requestIn.valid && !victim.requestIn.ready))
|
||||||
|
victim.requestIn.valid := request.clean && way.tagReadRspTwo.dirty
|
||||||
|
tagsWriteCmd.valid := victim.requestIn.ready
|
||||||
|
} otherwise{
|
||||||
|
io.cpu.writeBack.haltIt := False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
|
||||||
|
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
||||||
|
tagsWriteCmd.data.used := !request.invalidate
|
||||||
|
tagsWriteCmd.data.dirty := !request.clean
|
||||||
|
}
|
||||||
|
is(MEMORY) {
|
||||||
|
val illegal = if(catchIllegal) (request.wr && !mmuRsp.allowWrite) || (!request.wr && !mmuRsp.allowRead) || (io.cpu.writeBack.isUser && !mmuRsp.allowUser) else False
|
||||||
|
val unaligned = if(catchUnaligned) ((request.size === 2 && mmuRsp.physicalAddress(1 downto 0) =/= 0) || (request.size === 1 && mmuRsp.physicalAddress(0 downto 0) =/= 0)) else False
|
||||||
|
io.cpu.writeBack.illegalAccess := illegal
|
||||||
|
io.cpu.writeBack.unalignedAccess := unaligned
|
||||||
|
when((Bool(!catchMemoryTranslationMiss) || !mmuRsp.miss) && !illegal && !unaligned) {
|
||||||
|
when(request.forceUncachedAccess || mmuRsp.isIoAccess) {
|
||||||
|
val memCmdSent = RegInit(False)
|
||||||
|
when(!victim.request.valid) {
|
||||||
|
//Avoid mixing memory request while victim is pending
|
||||||
|
io.mem.cmd.wr := request.wr
|
||||||
|
io.mem.cmd.address := mmuRsp.physicalAddress(tagRange.high downto wordRange.low) @@ U(0, wordRange.low bit)
|
||||||
|
io.mem.cmd.mask := writeMask
|
||||||
|
io.mem.cmd.data := request.data
|
||||||
|
io.mem.cmd.length := 1
|
||||||
|
|
||||||
|
when(!memCmdSent) {
|
||||||
|
io.mem.cmd.valid := True
|
||||||
|
memCmdSent setWhen (io.mem.cmd.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.cpu.writeBack.haltIt.clearWhen(memCmdSent && (io.mem.rsp.fire || request.wr)) //Cut mem.cmd.ready path but insert one cycle stall when write
|
||||||
|
}
|
||||||
|
memCmdSent clearWhen (!io.cpu.writeBack.isStuck)
|
||||||
|
} otherwise {
|
||||||
|
when(waysHit || !loadingNotDone) {
|
||||||
|
io.cpu.writeBack.haltIt := False
|
||||||
|
dataWriteCmd.valid := request.wr
|
||||||
|
dataWriteCmd.address := mmuRsp.physicalAddress(lineRange.high downto wordRange.low)
|
||||||
|
dataWriteCmd.data := request.data
|
||||||
|
dataWriteCmd.mask := writeMask
|
||||||
|
|
||||||
|
tagsWriteCmd.valid := (!loadingNotDone) || request.wr
|
||||||
|
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
|
||||||
|
tagsWriteCmd.data.used := True
|
||||||
|
tagsWriteCmd.data.dirty := request.wr
|
||||||
|
tagsWriteCmd.data.address := mmuRsp.physicalAddress(tagRange)
|
||||||
|
} otherwise {
|
||||||
|
val victimRequired = way.tagReadRspTwo.used && way.tagReadRspTwo.dirty
|
||||||
|
loaderValid := loadingNotDone && !(victimNotSent && victim.request.isStall) //Additional condition used to be sure of that all previous victim are written into the RAM
|
||||||
|
victim.requestIn.valid := victimRequired && victimNotSent
|
||||||
|
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
assert(!(io.cpu.writeBack.isValid && !io.cpu.writeBack.haltIt && io.cpu.writeBack.isStuck), "writeBack stuck by another plugin is not allowed")
|
||||||
|
io.cpu.writeBack.data := (request.forceUncachedAccess || mmuRsp.isIoAccess) ? io.mem.rsp.data | way.dataReadRspTwo //not multi ways
|
||||||
|
}
|
||||||
|
|
||||||
|
//The whole life of a loading task, the corresponding manager request is present
|
||||||
|
val loader = new Area{
|
||||||
|
val valid = RegNext(stageB.loaderValid) init(False)
|
||||||
|
val baseAddress = stageB.mmuRsp.physicalAddress
|
||||||
|
|
||||||
|
val memCmdSent = RegInit(False)
|
||||||
|
when(valid && !memCmdSent) {
|
||||||
|
io.mem.cmd.valid := True
|
||||||
|
io.mem.cmd.wr := False
|
||||||
|
io.mem.cmd.address := baseAddress(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
||||||
|
io.mem.cmd.length := p.burstLength
|
||||||
|
}
|
||||||
|
|
||||||
|
when(valid && io.mem.cmd.ready){
|
||||||
|
memCmdSent := True
|
||||||
|
}
|
||||||
|
|
||||||
|
when(valid && !memCmdSent) {
|
||||||
|
victim.memCmdAlreadyUsed := True
|
||||||
|
}
|
||||||
|
|
||||||
|
val counter = Counter(memTransactionPerLine)
|
||||||
|
when(valid && io.mem.rsp.valid){
|
||||||
|
dataWriteCmd.valid := True
|
||||||
|
dataWriteCmd.address := baseAddress(lineRange) @@ counter
|
||||||
|
dataWriteCmd.data := io.mem.rsp.data
|
||||||
|
dataWriteCmd.mask := (1<<(wordWidth/8))-1
|
||||||
|
counter.increment()
|
||||||
|
}
|
||||||
|
|
||||||
|
when(counter.willOverflow){
|
||||||
|
memCmdSent := False
|
||||||
|
valid := False
|
||||||
|
stageB.loaderReady := True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
package SpinalRiscv.ip
|
||||||
|
|
||||||
|
import SpinalRiscv._
|
||||||
|
import spinal.core._
|
||||||
|
import spinal.lib._
|
||||||
|
|
||||||
|
|
||||||
|
case class InstructionCacheConfig( cacheSize : Int,
|
||||||
|
bytePerLine : Int,
|
||||||
|
wayCount : Int,
|
||||||
|
wrappedMemAccess : Boolean,
|
||||||
|
addressWidth : Int,
|
||||||
|
cpuDataWidth : Int,
|
||||||
|
memDataWidth : Int,
|
||||||
|
catchIllegalAccess : Boolean,
|
||||||
|
catchAccessFault : Boolean,
|
||||||
|
catchMemoryTranslationMiss : Boolean,
|
||||||
|
asyncTagMemory : Boolean,
|
||||||
|
twoStageLogic : Boolean){
|
||||||
|
def burstSize = bytePerLine*8/memDataWidth
|
||||||
|
def catchSomething = catchAccessFault || catchMemoryTranslationMiss || catchIllegalAccess
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case class InstructionCacheCpuPrefetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val isValid = Bool
|
||||||
|
val isFiring = Bool
|
||||||
|
val haltIt = Bool
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid, isFiring, address)
|
||||||
|
in(haltIt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheCpuFetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave {
|
||||||
|
val isValid = Bool
|
||||||
|
val haltIt = if(!p.twoStageLogic) Bool else null
|
||||||
|
val isStuck = Bool
|
||||||
|
val isStuckByOthers = if(!p.twoStageLogic) Bool else null
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
val data = if(!p.twoStageLogic) Bits(32 bit) else null
|
||||||
|
val error = if(!p.twoStageLogic && p.catchAccessFault) Bool else null
|
||||||
|
val mmuBus = if(p.twoStageLogic) MemoryTranslatorBus() else null
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid, isStuck, address)
|
||||||
|
outWithNull(isStuckByOthers)
|
||||||
|
inWithNull(error,data,haltIt)
|
||||||
|
slaveWithNull(mmuBus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheCpuDecode(p : InstructionCacheConfig) extends Bundle with IMasterSlave {
|
||||||
|
require(p.twoStageLogic)
|
||||||
|
val isValid = Bool
|
||||||
|
val haltIt = Bool
|
||||||
|
val isStuck = Bool
|
||||||
|
val isUser = Bool
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
val data = Bits(32 bit)
|
||||||
|
val dataAnticipated = Bits(32 bits)
|
||||||
|
val error = if(p.catchAccessFault) Bool else null
|
||||||
|
val mmuMiss = if(p.catchMemoryTranslationMiss) Bool else null
|
||||||
|
val illegalAccess = if(p.catchIllegalAccess) Bool else null
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
out(isValid, isStuck, address, isUser)
|
||||||
|
in(haltIt, data, dataAnticipated)
|
||||||
|
inWithNull(error,mmuMiss,illegalAccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheCpuBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val prefetch = InstructionCacheCpuPrefetch(p)
|
||||||
|
val fetch = InstructionCacheCpuFetch(p)
|
||||||
|
val decode = if(p.twoStageLogic) InstructionCacheCpuDecode(p) else null
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(prefetch)
|
||||||
|
master(fetch)
|
||||||
|
if(p.twoStageLogic) master(decode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheMemCmd(p : InstructionCacheConfig) extends Bundle{
|
||||||
|
val address = UInt(p.addressWidth bit)
|
||||||
|
}
|
||||||
|
case class InstructionCacheMemRsp(p : InstructionCacheConfig) extends Bundle{
|
||||||
|
val data = Bits(32 bit)
|
||||||
|
val error = Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheMemBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
||||||
|
val cmd = Stream (InstructionCacheMemCmd(p))
|
||||||
|
val rsp = Flow (InstructionCacheMemRsp(p))
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(cmd)
|
||||||
|
slave(rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class InstructionCacheFlushBus() extends Bundle with IMasterSlave{
|
||||||
|
val cmd = Event
|
||||||
|
val rsp = Bool
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(cmd)
|
||||||
|
in(rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstructionCache(p : InstructionCacheConfig) extends Component{
|
||||||
|
import p._
|
||||||
|
assert(wayCount == 1)
|
||||||
|
assert(cpuDataWidth == memDataWidth)
|
||||||
|
val io = new Bundle{
|
||||||
|
val flush = slave(InstructionCacheFlushBus())
|
||||||
|
// val translator = master(InstructionCacheTranslationBus(p))
|
||||||
|
val cpu = slave(InstructionCacheCpuBus(p))
|
||||||
|
val mem = master(InstructionCacheMemBus(p))
|
||||||
|
}
|
||||||
|
// val haltCpu = False
|
||||||
|
val lineWidth = bytePerLine*8
|
||||||
|
val lineCount = cacheSize/bytePerLine
|
||||||
|
val wordWidth = Math.max(memDataWidth,32)
|
||||||
|
val wordWidthLog2 = log2Up(wordWidth)
|
||||||
|
val wordPerLine = lineWidth/wordWidth
|
||||||
|
val bytePerWord = wordWidth/8
|
||||||
|
val wayLineCount = lineCount/wayCount
|
||||||
|
val wayLineLog2 = log2Up(wayLineCount)
|
||||||
|
val wayWordCount = wayLineCount * wordPerLine
|
||||||
|
|
||||||
|
val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine)
|
||||||
|
val lineRange = tagRange.low-1 downto log2Up(bytePerLine)
|
||||||
|
val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord)
|
||||||
|
val tagLineRange = tagRange.high downto lineRange.low
|
||||||
|
val lineWordRange = lineRange.high downto wordRange.low
|
||||||
|
|
||||||
|
class LineInfo extends Bundle{
|
||||||
|
val valid = Bool
|
||||||
|
val loading = Bool
|
||||||
|
val error = if(catchAccessFault) Bool else null
|
||||||
|
val address = UInt(tagRange.length bit)
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineInfoWithHit extends LineInfo{
|
||||||
|
val hit = Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
def LineInfoWithHit(lineInfo : LineInfo, testTag : UInt) = {
|
||||||
|
val ret = new LineInfoWithHit()
|
||||||
|
ret.assignSomeByName(lineInfo)
|
||||||
|
ret.hit := lineInfo.valid && lineInfo.address === testTag
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val ways = Array.fill(wayCount)(new Area{
|
||||||
|
val tags = Mem(new LineInfo(),wayLineCount)
|
||||||
|
val datas = Mem(Bits(wordWidth bits),wayWordCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
io.cpu.prefetch.haltIt := False
|
||||||
|
|
||||||
|
val lineLoader = new Area{
|
||||||
|
val requestIn = Stream(wrap(new Bundle{
|
||||||
|
val addr = UInt(addressWidth bits)
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val flushCounter = Reg(UInt(log2Up(wayLineCount) + 1 bit)) init(0)
|
||||||
|
when(!flushCounter.msb){
|
||||||
|
io.cpu.prefetch.haltIt := True
|
||||||
|
flushCounter := flushCounter + 1
|
||||||
|
}
|
||||||
|
when(!RegNext(flushCounter.msb)){
|
||||||
|
io.cpu.prefetch.haltIt := True
|
||||||
|
}
|
||||||
|
val flushFromInterface = RegInit(False)
|
||||||
|
when(io.flush.cmd.valid){
|
||||||
|
io.cpu.prefetch.haltIt := True
|
||||||
|
when(io.flush.cmd.ready){
|
||||||
|
flushCounter := 0
|
||||||
|
flushFromInterface := True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
io.flush.rsp := flushCounter.msb.rise && flushFromInterface
|
||||||
|
|
||||||
|
val loadingWithErrorReg = if(catchAccessFault) RegInit(False) else null
|
||||||
|
val loadingWithError = if(catchAccessFault) Bool else null
|
||||||
|
if(catchAccessFault) {
|
||||||
|
loadingWithError := loadingWithErrorReg
|
||||||
|
loadingWithErrorReg := loadingWithError
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val request = requestIn.stage()
|
||||||
|
|
||||||
|
|
||||||
|
//Send memory requests
|
||||||
|
val memCmdSended = RegInit(False) setWhen(io.mem.cmd.fire)
|
||||||
|
io.mem.cmd.valid := request.valid && !memCmdSended
|
||||||
|
if(wrappedMemAccess)
|
||||||
|
io.mem.cmd.address := request.addr(tagRange.high downto wordRange.low) @@ U(0,wordRange.low bit)
|
||||||
|
else
|
||||||
|
io.mem.cmd.address := request.addr(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
||||||
|
|
||||||
|
val wordIndex = Reg(UInt(log2Up(wordPerLine) bit))
|
||||||
|
val loadedWordsNext = Bits(wordPerLine bit)
|
||||||
|
val loadedWords = RegNext(loadedWordsNext)
|
||||||
|
val loadedWordsReadable = RegNext(loadedWords)
|
||||||
|
loadedWordsNext := loadedWords
|
||||||
|
|
||||||
|
val waysDatasWritePort = ways(0).datas.writePort //Not multi ways
|
||||||
|
waysDatasWritePort.valid := io.mem.rsp.valid
|
||||||
|
waysDatasWritePort.address := request.addr(lineRange) @@ wordIndex
|
||||||
|
waysDatasWritePort.data := io.mem.rsp.data
|
||||||
|
when(io.mem.rsp.valid){
|
||||||
|
wordIndex := wordIndex + 1
|
||||||
|
loadedWordsNext(wordIndex) := True
|
||||||
|
if(catchAccessFault) loadingWithError setWhen io.mem.rsp.error
|
||||||
|
}
|
||||||
|
|
||||||
|
val memRspLast = loadedWordsNext === B(loadedWordsNext.range -> true)
|
||||||
|
|
||||||
|
val readyDelay = Reg(UInt(1 bit))
|
||||||
|
when(memRspLast){
|
||||||
|
readyDelay := readyDelay + 1
|
||||||
|
}
|
||||||
|
request.ready := readyDelay === 1
|
||||||
|
|
||||||
|
val waysTagsWritePort = ways(0).tags.writePort //not multi way
|
||||||
|
waysTagsWritePort.valid := io.mem.rsp.valid || !flushCounter.msb
|
||||||
|
waysTagsWritePort.address := Mux(flushCounter.msb,request.addr(lineRange),flushCounter(flushCounter.high-1 downto 0))
|
||||||
|
waysTagsWritePort.data.valid := flushCounter.msb
|
||||||
|
waysTagsWritePort.data.address := request.addr(tagRange)
|
||||||
|
waysTagsWritePort.data.loading := !memRspLast
|
||||||
|
if(catchAccessFault) waysTagsWritePort.data.error := loadingWithError
|
||||||
|
|
||||||
|
|
||||||
|
when(requestIn.ready){
|
||||||
|
memCmdSended := False
|
||||||
|
wordIndex := requestIn.addr(wordRange)
|
||||||
|
loadedWords := 0
|
||||||
|
loadedWordsReadable := 0
|
||||||
|
readyDelay := 0
|
||||||
|
if(catchAccessFault) loadingWithErrorReg := False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val task = if(!twoStageLogic) new Area{
|
||||||
|
val waysHitValid = False
|
||||||
|
val waysHitError = Bool.assignDontCare()
|
||||||
|
val waysHitWord = Bits(wordWidth bit)
|
||||||
|
|
||||||
|
val waysRead = for(way <- ways) yield new Area{
|
||||||
|
val readAddress = Mux(io.cpu.fetch.isStuck,io.cpu.fetch.address,io.cpu.prefetch.address) //TODO FMAX
|
||||||
|
val tag = if(asyncTagMemory)
|
||||||
|
way.tags.readAsync(io.cpu.fetch.address(lineRange),writeFirst)
|
||||||
|
else
|
||||||
|
way.tags.readSync(readAddress(lineRange),readUnderWrite = readFirst)
|
||||||
|
|
||||||
|
val data = way.datas.readSync(readAddress(lineRange.high downto wordRange.low))
|
||||||
|
waysHitWord := data //Not applicable to multi way
|
||||||
|
when(tag.valid && tag.address === io.cpu.fetch.address(tagRange)) {
|
||||||
|
waysHitValid := True
|
||||||
|
if(catchAccessFault) waysHitError := tag.error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val hit = waysHitValid && !(waysRead(0).tag.loading && !(if(asyncTagMemory) lineLoader.loadedWords else RegNext(lineLoader.loadedWords))(io.cpu.fetch.address(wordRange)))
|
||||||
|
io.cpu.fetch.haltIt := io.cpu.fetch.isValid && !hit
|
||||||
|
io.cpu.fetch.data := waysHitWord
|
||||||
|
if(catchAccessFault) io.cpu.fetch.error := waysRead(0).tag.error
|
||||||
|
lineLoader.requestIn.valid := io.cpu.fetch.isValid && !hit //TODO avoid duplicated request
|
||||||
|
lineLoader.requestIn.addr := io.cpu.fetch.address
|
||||||
|
} else new Area{
|
||||||
|
//Long readValidPath
|
||||||
|
// def writeFirstMemWrap[T <: Data](readValid : Bool, readAddress : UInt, lastAddress : UInt, readData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
||||||
|
// val hit = writeValid && (readValid ? readAddress | lastAddress) === writeAddress
|
||||||
|
// val overrideValid = RegInit(False) clearWhen(readValid) setWhen(hit)
|
||||||
|
// val overrideValue = RegNextWhen(writeData,hit)
|
||||||
|
// overrideValid ? overrideValue | readData
|
||||||
|
// }
|
||||||
|
|
||||||
|
//shot readValid path
|
||||||
|
def writeFirstMemWrap[T <: Data](readValid : Bool, readLastAddress : UInt, readData : T,writeValid : Bool, writeAddress : UInt, writeData : T) : T = {
|
||||||
|
val writeSample = readValid || (writeValid && writeAddress === readLastAddress)
|
||||||
|
val writeValidReg = RegNextWhen(writeValid,writeSample)
|
||||||
|
val writeAddressReg = RegNextWhen(writeAddress,writeSample)
|
||||||
|
val writeDataReg = RegNextWhen(writeData,writeSample)
|
||||||
|
(writeValidReg && writeAddressReg === readLastAddress) ? writeDataReg | readData
|
||||||
|
}
|
||||||
|
|
||||||
|
//Long sample path
|
||||||
|
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,lastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) : (T,T) = {
|
||||||
|
// val hit = writeValid && (sample ? sampleAddress | lastAddress) === writeAddress
|
||||||
|
// val bypass = hit ? writeData | readData
|
||||||
|
// val reg = RegNextWhen(bypass,sample || hit)
|
||||||
|
// (reg,bypass)
|
||||||
|
// }
|
||||||
|
|
||||||
|
//Short sample path
|
||||||
|
def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
||||||
|
val preWrite = (writeValid && sampleAddress === writeAddress)
|
||||||
|
val postWrite = (writeValid && sampleLastAddress === writeAddress)
|
||||||
|
val bypass = (!sample || preWrite) ? writeData | readData
|
||||||
|
val regEn = sample || postWrite
|
||||||
|
val reg = RegNextWhen(bypass,regEn)
|
||||||
|
(reg,bypass,regEn,preWrite,postWrite)
|
||||||
|
}
|
||||||
|
// def writeFirstRegWrap[T <: Data](sample : Bool, sampleAddress : UInt,sampleLastAddress : UInt, readData : T, writeValid : Bool, writeAddress : UInt, writeData : T) = {
|
||||||
|
// val bypass = (!sample || (writeValid && sampleAddress === writeAddress)) ? writeData | readData
|
||||||
|
// val regEn = sample || (writeValid && sampleLastAddress === writeAddress)
|
||||||
|
// val reg = RegNextWhen(bypass,regEn)
|
||||||
|
// (reg,bypass,regEn,False,False)
|
||||||
|
// }
|
||||||
|
require(wayCount == 1)
|
||||||
|
val memRead = new Area{
|
||||||
|
val way = ways(0)
|
||||||
|
val tag = if(asyncTagMemory)
|
||||||
|
way.tags.readAsync(io.cpu.fetch.address(lineRange),writeFirst)
|
||||||
|
else
|
||||||
|
writeFirstMemWrap(
|
||||||
|
readValid = !io.cpu.fetch.isStuck,
|
||||||
|
// readAddress = io.cpu.prefetch.address(lineRange),
|
||||||
|
readLastAddress = io.cpu.fetch.address(lineRange),
|
||||||
|
readData = way.tags.readSync(io.cpu.prefetch.address(lineRange),enable = !io.cpu.fetch.isStuck),
|
||||||
|
writeValid = lineLoader.waysTagsWritePort.valid,
|
||||||
|
writeAddress = lineLoader.waysTagsWritePort.address,
|
||||||
|
writeData = lineLoader.waysTagsWritePort.data
|
||||||
|
)
|
||||||
|
|
||||||
|
val data = writeFirstMemWrap(
|
||||||
|
readValid = !io.cpu.fetch.isStuck,
|
||||||
|
// readAddress = io.cpu.prefetch.address(lineWordRange),
|
||||||
|
readLastAddress = io.cpu.fetch.address(lineWordRange),
|
||||||
|
readData = way.datas.readSync(io.cpu.prefetch.address(lineWordRange),enable = !io.cpu.fetch.isStuck),
|
||||||
|
writeValid = lineLoader.waysDatasWritePort.valid,
|
||||||
|
writeAddress = lineLoader.waysDatasWritePort.address,
|
||||||
|
writeData = lineLoader.waysDatasWritePort.data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val tag = writeFirstRegWrap(
|
||||||
|
sample = !io.cpu.decode.isStuck,
|
||||||
|
sampleAddress = io.cpu.fetch.address(lineRange),
|
||||||
|
sampleLastAddress = io.cpu.decode.address(lineRange),
|
||||||
|
readData = LineInfoWithHit(memRead.tag,io.cpu.fetch.address(tagRange)),
|
||||||
|
writeValid = lineLoader.waysTagsWritePort.valid,
|
||||||
|
writeAddress = lineLoader.waysTagsWritePort.address,
|
||||||
|
writeData = LineInfoWithHit(lineLoader.waysTagsWritePort.data,io.cpu.fetch.address(tagRange)) //TODO wrong address src
|
||||||
|
)._1
|
||||||
|
|
||||||
|
val (data,dataRegIn,dataRegEn,dataPreWrite,dataPostWrite) = writeFirstRegWrap(
|
||||||
|
sample = !io.cpu.decode.isStuck,
|
||||||
|
sampleAddress = io.cpu.fetch.address(lineWordRange),
|
||||||
|
sampleLastAddress = io.cpu.decode.address(lineWordRange),
|
||||||
|
readData = memRead.data,
|
||||||
|
writeValid = lineLoader.waysDatasWritePort.valid,
|
||||||
|
writeAddress = lineLoader.waysDatasWritePort.address,
|
||||||
|
writeData = lineLoader.waysDatasWritePort.data
|
||||||
|
)
|
||||||
|
|
||||||
|
io.cpu.fetch.mmuBus.cmd.isValid := io.cpu.fetch.isValid
|
||||||
|
io.cpu.fetch.mmuBus.cmd.virtualAddress := io.cpu.fetch.address
|
||||||
|
io.cpu.fetch.mmuBus.cmd.bypassTranslation := False
|
||||||
|
val mmuRsp = RegNextWhen(io.cpu.fetch.mmuBus.rsp,!io.cpu.decode.isStuck)
|
||||||
|
|
||||||
|
val hit = tag.valid && tag.address === mmuRsp.physicalAddress(tagRange) && !(tag.loading && !lineLoader.loadedWords(mmuRsp.physicalAddress(wordRange)))
|
||||||
|
// val hit = tag.hit && !(tag.loading && !lineLoader.loadedWords(mmuRsp.physicalAddress(wordRange)))
|
||||||
|
|
||||||
|
io.cpu.decode.haltIt := io.cpu.decode.isValid && !hit //TODO PERF not halit it when removed, Should probably be applyed in many other places
|
||||||
|
io.cpu.decode.data := data
|
||||||
|
// io.cpu.decode.dataAnticipated := dataRegEn ? dataRegIn | data
|
||||||
|
io.cpu.decode.dataAnticipated := io.cpu.decode.isStuck ? Mux(dataPostWrite,lineLoader.waysDatasWritePort.data,data) | Mux(dataPreWrite,lineLoader.waysDatasWritePort.data,memRead.data)
|
||||||
|
if(catchAccessFault) io.cpu.decode.error := tag.error
|
||||||
|
if(catchMemoryTranslationMiss) io.cpu.decode.mmuMiss := mmuRsp.miss
|
||||||
|
if(catchIllegalAccess) io.cpu.decode.illegalAccess := !mmuRsp.allowExecute || (io.cpu.decode.isUser && !mmuRsp.allowUser)
|
||||||
|
|
||||||
|
lineLoader.requestIn.valid := io.cpu.decode.isValid && !hit && !mmuRsp.miss//TODO avoid duplicated request
|
||||||
|
lineLoader.requestIn.addr := mmuRsp.physicalAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
io.flush.cmd.ready := !(lineLoader.request.valid || io.cpu.fetch.isValid)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue