New faster/smaller/multi way instruction cache design.
Single or dual stage
This commit is contained in:
parent
3853e0313b
commit
506e0e3f60
|
@ -119,6 +119,7 @@ trait Pipeline {
|
|||
for(stageIndex <- 0 until stages.length; stage = stages(stageIndex)){
|
||||
stage.arbitration.isStuckByOthers := stage.arbitration.haltByOther || stages.takeRight(stages.length - stageIndex - 1).map(s => s.arbitration.haltItself/* && !s.arbitration.removeIt*/).foldLeft(False)(_ || _)
|
||||
stage.arbitration.isStuck := stage.arbitration.haltItself || stage.arbitration.isStuckByOthers
|
||||
stage.arbitration.isMoving := !stage.arbitration.isStuck && !stage.arbitration.removeIt
|
||||
stage.arbitration.isFiring := stage.arbitration.isValid && !stage.arbitration.isStuck && !stage.arbitration.removeIt
|
||||
}
|
||||
|
||||
|
|
|
@ -48,11 +48,13 @@ class Stage() extends Area{
|
|||
val haltByOther = False //When settable, stuck the instruction, should only be set by something else than the stucked instruction
|
||||
val removeIt = False //When settable, unschedule the instruction as if it was never executed (no side effect)
|
||||
val flushAll = False //When settable, unschedule instructions in the current stage and all prior ones
|
||||
val redoIt = False //Allow to notify that a given instruction in a pipeline is rescheduled
|
||||
val isValid = RegInit(False) //Inform if a instruction is in the current stage
|
||||
val isStuck = Bool //Inform if the instruction is stuck (haltItself || haltByOther)
|
||||
val isStuckByOthers = Bool //Inform if the instruction is stuck by sombody else
|
||||
def isRemoved = removeIt //Inform if the instruction is going to be unschedule the current cycle
|
||||
val isFlushed = Bool //Inform if the instruction is flushed (flushAll set in the current or subsequents stages)
|
||||
val isMoving = Bool //Inform if the instruction is going somewere else (next stage or unscheduled)
|
||||
val isFiring = Bool //Inform if the current instruction will go to the next stage the next cycle (isValid && !isStuck && !removeIt)
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ object TestsWorkspace {
|
|||
// ),
|
||||
new IBusCachedPlugin(
|
||||
config = InstructionCacheConfig(
|
||||
cacheSize = 4096*4,
|
||||
bytePerLine =32,
|
||||
wayCount = 1,
|
||||
cacheSize = 4096,
|
||||
bytePerLine = 32,
|
||||
wayCount = 4,
|
||||
wrappedMemAccess = true,
|
||||
addressWidth = 32,
|
||||
cpuDataWidth = 32,
|
||||
|
@ -52,10 +52,11 @@ object TestsWorkspace {
|
|||
catchAccessFault = true,
|
||||
catchMemoryTranslationMiss = true,
|
||||
asyncTagMemory = false,
|
||||
twoStageLogic = true
|
||||
twoStageLogic = false,
|
||||
twoCycleRam = true
|
||||
),
|
||||
askMemoryTranslation = true,
|
||||
memoryTranslatorPortConfig = MemoryTranslatorPortConfig(
|
||||
memoryTranslatorPortConfig = MemoryTranslatorPortConfig(
|
||||
portTlbSize = 4
|
||||
)
|
||||
),
|
||||
|
@ -95,7 +96,7 @@ object TestsWorkspace {
|
|||
catchIllegalInstruction = true
|
||||
),
|
||||
new RegFilePlugin(
|
||||
regFileReadyKind = plugin.SYNC,
|
||||
regFileReadyKind = plugin.ASYNC,
|
||||
zeroBoot = false
|
||||
),
|
||||
new IntAluPlugin,
|
||||
|
@ -122,7 +123,7 @@ object TestsWorkspace {
|
|||
new BranchPlugin(
|
||||
earlyBranch = true,
|
||||
catchAddressMisaligned = true,
|
||||
prediction = DYNAMIC_TARGET,
|
||||
prediction = NONE,
|
||||
historyRamSizeLog2 = 8
|
||||
),
|
||||
new YamlPlugin("cpu0.yaml")
|
||||
|
|
|
@ -66,6 +66,7 @@ class VexRiscv(val config : VexRiscvConfig) extends Component with Pipeline{
|
|||
decode.input(config.INSTRUCTION).addAttribute(Verilator.public)
|
||||
decode.input(config.PC).addAttribute(Verilator.public)
|
||||
decode.arbitration.isValid.addAttribute(Verilator.public)
|
||||
decode.arbitration.flushAll.addAttribute(Verilator.public)
|
||||
decode.arbitration.haltItself.addAttribute(Verilator.public)
|
||||
writeBack.input(config.INSTRUCTION) keep() addAttribute(Verilator.public)
|
||||
writeBack.input(config.PC) keep() addAttribute(Verilator.public)
|
||||
|
|
|
@ -11,7 +11,7 @@ object FormalSimple extends App{
|
|||
def cpu() = new VexRiscv(
|
||||
config = VexRiscvConfig(
|
||||
plugins = List(
|
||||
new FomalPlugin,
|
||||
new FormalPlugin,
|
||||
new HaltOnExceptionPlugin,
|
||||
new PcManagerSimplePlugin(
|
||||
resetVector = 0x00000000l,
|
||||
|
|
|
@ -12,24 +12,24 @@ object VexRiscvSynthesisBench {
|
|||
def main(args: Array[String]) {
|
||||
|
||||
def wrap(that : => Component) : Component = that
|
||||
//Wrap with input/output registers
|
||||
// def wrap(that : => Component) : Component = {
|
||||
// //new WrapWithReg.Wrapper(that)
|
||||
// val c = that
|
||||
// c.rework {
|
||||
// for (e <- c.getOrdredNodeIo) {
|
||||
// if (e.isInput) {
|
||||
// e.asDirectionLess()
|
||||
// e := RegNext(RegNext(in(cloneOf(e))))
|
||||
//
|
||||
// } else {
|
||||
// e.asDirectionLess()
|
||||
// out(cloneOf(e)) := RegNext(RegNext(e))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// c
|
||||
// }
|
||||
// Wrap with input/output registers
|
||||
// def wrap(that : => Component) : Component = {
|
||||
// //new WrapWithReg.Wrapper(that)
|
||||
// val c = that
|
||||
// c.rework {
|
||||
// for (e <- c.getOrdredNodeIo) {
|
||||
// if (e.isInput) {
|
||||
// e.asDirectionLess()
|
||||
// e := RegNext(RegNext(in(cloneOf(e))))
|
||||
//
|
||||
// } else {
|
||||
// e.asDirectionLess()
|
||||
// out(cloneOf(e)) := RegNext(RegNext(e))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// c
|
||||
// }
|
||||
|
||||
val smallestNoCsr = new Rtl {
|
||||
override def getName(): String = "VexRiscv smallest no CSR"
|
||||
|
|
|
@ -18,7 +18,11 @@ case class InstructionCacheConfig( cacheSize : Int,
|
|||
catchAccessFault : Boolean,
|
||||
catchMemoryTranslationMiss : Boolean,
|
||||
asyncTagMemory : Boolean,
|
||||
twoStageLogic : Boolean){
|
||||
twoStageLogic : Boolean,
|
||||
twoCycleRam : Boolean = false,
|
||||
preResetFlush : Boolean = false){
|
||||
|
||||
def dataOnDecode = twoCycleRam && wayCount > 1
|
||||
def burstSize = bytePerLine*8/memDataWidth
|
||||
def catchSomething = catchAccessFault || catchMemoryTranslationMiss || catchIllegalAccess
|
||||
|
||||
|
@ -47,72 +51,65 @@ case class InstructionCacheConfig( cacheSize : Int,
|
|||
|
||||
case class InstructionCacheCpuPrefetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave{
|
||||
val isValid = Bool
|
||||
val isFiring = Bool
|
||||
val haltIt = Bool
|
||||
val address = UInt(p.addressWidth bit)
|
||||
val pc = UInt(p.addressWidth bit)
|
||||
|
||||
override def asMaster(): Unit = {
|
||||
out(isValid, isFiring, address)
|
||||
out(isValid, pc)
|
||||
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
|
||||
val pc = UInt(p.addressWidth bits)
|
||||
val data = Bits(p.cpuDataWidth bits)
|
||||
val mmuBus = MemoryTranslatorBus()
|
||||
|
||||
override def asMaster(): Unit = {
|
||||
out(isValid, isStuck, address)
|
||||
outWithNull(isStuckByOthers)
|
||||
inWithNull(error,data,haltIt)
|
||||
out(isValid, isStuck, pc)
|
||||
inWithNull(data)
|
||||
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 isStuck = Bool
|
||||
val pc = UInt(p.addressWidth bits)
|
||||
val redo = Bool
|
||||
val data = ifGen(p.dataOnDecode) (Bits(p.cpuDataWidth 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)
|
||||
out(isValid, isUser, isStuck, pc)
|
||||
in(redo)
|
||||
inWithNull(error,mmuMiss,illegalAccess,data)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
val decode = InstructionCacheCpuDecode(p)
|
||||
|
||||
override def asMaster(): Unit = {
|
||||
master(prefetch)
|
||||
master(fetch)
|
||||
if(p.twoStageLogic) master(decode)
|
||||
master(prefetch, fetch, decode)
|
||||
}
|
||||
}
|
||||
|
||||
case class InstructionCacheMemCmd(p : InstructionCacheConfig) extends Bundle{
|
||||
val address = UInt(p.addressWidth bit)
|
||||
val size = UInt(log2Up(log2Up(p.bytePerLine) + 1) bits)
|
||||
}
|
||||
|
||||
case class InstructionCacheMemRsp(p : InstructionCacheConfig) extends Bundle{
|
||||
val data = Bits(32 bit)
|
||||
val data = Bits(p.memDataWidth bit)
|
||||
val error = Bool
|
||||
}
|
||||
|
||||
|
@ -173,21 +170,21 @@ case class InstructionCacheFlushBus() extends Bundle with IMasterSlave{
|
|||
|
||||
class InstructionCache(p : InstructionCacheConfig) extends Component{
|
||||
import p._
|
||||
assert(wayCount == 1)
|
||||
assert(cpuDataWidth == memDataWidth)
|
||||
assert(cpuDataWidth == memDataWidth, "Need testing")
|
||||
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 memWordPerLine = lineWidth/memDataWidth
|
||||
val bytePerWord = wordWidth/8
|
||||
val bytePerMemWord = memDataWidth/8
|
||||
val wayLineCount = lineCount/wayCount
|
||||
val wayLineLog2 = log2Up(wayLineCount)
|
||||
val wayWordCount = wayLineCount * wordPerLine
|
||||
|
@ -195,44 +192,41 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{
|
|||
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 memWordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerMemWord)
|
||||
val memWordToCpuWordRange = log2Up(bytePerMemWord)-1 downto log2Up(bytePerWord)
|
||||
val tagLineRange = tagRange.high downto lineRange.low
|
||||
val lineWordRange = lineRange.high downto wordRange.low
|
||||
|
||||
class LineInfo extends Bundle{
|
||||
case class LineTag() extends Bundle{
|
||||
val valid = Bool
|
||||
val loading = Bool
|
||||
val error = if(catchAccessFault) Bool else null
|
||||
val error = Bool
|
||||
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 = Seq.fill(wayCount)(new Area{
|
||||
val tags = Mem(LineTag(),wayLineCount)
|
||||
val datas = Mem(Bits(memDataWidth bits),wayWordCount)
|
||||
|
||||
|
||||
val ways = Array.fill(wayCount)(new Area{
|
||||
val tags = Mem(new LineInfo(),wayLineCount)
|
||||
val datas = Mem(Bits(wordWidth bits),wayWordCount)
|
||||
if(preResetFlush){
|
||||
tags.initBigInt(List.fill(wayLineCount)(BigInt(0)))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
io.cpu.prefetch.haltIt := False
|
||||
|
||||
|
||||
|
||||
|
||||
val lineLoader = new Area{
|
||||
val requestIn = Stream(wrap(new Bundle{
|
||||
val addr = UInt(addressWidth bits)
|
||||
}))
|
||||
val fire = False
|
||||
val valid = RegInit(False) clearWhen(fire)
|
||||
val address = Reg(UInt(addressWidth bits))
|
||||
val hadError = RegInit(False) clearWhen(fire)
|
||||
|
||||
io.cpu.prefetch.haltIt setWhen(valid)
|
||||
|
||||
|
||||
val flushCounter = Reg(UInt(log2Up(wayLineCount) + 1 bit)) init(0)
|
||||
val flushCounter = Reg(UInt(log2Up(wayLineCount) + 1 bit)) init(if(preResetFlush) wayLineCount else 0)
|
||||
when(!flushCounter.msb){
|
||||
io.cpu.prefetch.haltIt := True
|
||||
flushCounter := flushCounter + 1
|
||||
|
@ -241,6 +235,7 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{
|
|||
io.cpu.prefetch.haltIt := True
|
||||
}
|
||||
val flushFromInterface = RegInit(False)
|
||||
io.flush.cmd.ready := !(valid || io.cpu.fetch.isValid) //io.cpu.fetch.isValid will avoid bug on first cycle miss
|
||||
when(io.flush.cmd.valid){
|
||||
io.cpu.prefetch.haltIt := True
|
||||
when(io.flush.cmd.ready){
|
||||
|
@ -251,204 +246,110 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{
|
|||
|
||||
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 cmdSent = RegInit(False) setWhen(io.mem.cmd.fire) clearWhen(fire)
|
||||
io.mem.cmd.valid := valid && !cmdSent
|
||||
io.mem.cmd.address := address(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit)
|
||||
io.mem.cmd.size := log2Up(p.bytePerLine)
|
||||
|
||||
val wayToAllocate = Counter(wayCount, fire)
|
||||
val wordIndex = Reg(UInt(log2Up(memWordPerLine) bits)) init(0)
|
||||
|
||||
|
||||
val write = new Area{
|
||||
val tag = ways.map(_.tags.writePort)
|
||||
val data = ways.map(_.datas.writePort)
|
||||
}
|
||||
|
||||
for(wayId <- 0 until wayCount){
|
||||
val wayHit = wayToAllocate === wayId
|
||||
val tag = write.tag(wayId)
|
||||
tag.valid := ((wayHit && fire) || !flushCounter.msb)
|
||||
tag.address := (flushCounter.msb ? address(lineRange) | flushCounter(flushCounter.high-1 downto 0))
|
||||
tag.data.valid := flushCounter.msb
|
||||
tag.data.error := hadError || io.mem.rsp.error
|
||||
tag.data.address := address(tagRange)
|
||||
|
||||
|
||||
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 data = write.data(wayId)
|
||||
data.valid := io.mem.rsp.valid && wayHit
|
||||
data.address := address(lineRange) @@ wordIndex
|
||||
data.data := io.mem.rsp.data
|
||||
}
|
||||
|
||||
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
|
||||
when(io.mem.rsp.valid) {
|
||||
wordIndex := (wordIndex + 1).resized
|
||||
hadError.setWhen(io.mem.rsp.error)
|
||||
when(wordIndex === wordIndex.maxValue) {
|
||||
fire := True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 fetchStage = new Area{
|
||||
val read = new Area{
|
||||
val waysValues = for(way <- ways) yield new Area{
|
||||
val tag = if(asyncTagMemory) {
|
||||
way.tags.readAsync(io.cpu.fetch.pc(lineRange))
|
||||
}else {
|
||||
way.tags.readSync(io.cpu.prefetch.pc(lineRange), !io.cpu.fetch.isStuck)
|
||||
}
|
||||
val data = way.datas.readSync(io.cpu.prefetch.pc(lineRange.high downto memWordRange.low), !io.cpu.fetch.isStuck)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// }
|
||||
val hit = if(!twoCycleRam) new Area{
|
||||
val hits = read.waysValues.map(way => way.tag.valid && way.tag.address === io.cpu.fetch.mmuBus.rsp.physicalAddress(tagRange))
|
||||
val valid = Cat(hits).orR
|
||||
val id = OHToUInt(hits)
|
||||
val error = read.waysValues.map(_.tag.error).read(id)
|
||||
val data = read.waysValues.map(_.data).read(id)
|
||||
val word = data.subdivideIn(cpuDataWidth bits).read(io.cpu.fetch.pc(memWordToCpuWordRange))
|
||||
io.cpu.fetch.data := word
|
||||
} else null
|
||||
|
||||
//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
|
||||
if(twoCycleRam && wayCount == 1){
|
||||
io.cpu.fetch.data := read.waysValues.head.data.subdivideIn(cpuDataWidth bits).read(io.cpu.fetch.pc(memWordToCpuWordRange))
|
||||
}
|
||||
|
||||
//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.virtualAddress := io.cpu.fetch.pc
|
||||
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)))
|
||||
|
||||
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 || (if(twoStageLogic) io.cpu.decode.isValid else False))
|
||||
|
||||
val decodeStage = new Area{
|
||||
def stage[T <: Data](that : T) = RegNextWhen(that,!io.cpu.decode.isStuck)
|
||||
val mmuRsp = stage(io.cpu.fetch.mmuBus.rsp)
|
||||
|
||||
val hit = if(!twoCycleRam) new Area{
|
||||
val valid = stage(fetchStage.hit.valid)
|
||||
val error = stage(fetchStage.hit.error)
|
||||
} else new Area{
|
||||
val tags = fetchStage.read.waysValues.map(way => stage(way.tag))
|
||||
val hits = tags.map(tag => tag.valid && tag.address === mmuRsp.physicalAddress(tagRange))
|
||||
val valid = Cat(hits).orR
|
||||
val id = OHToUInt(hits)
|
||||
val error = tags(id).error
|
||||
if(dataOnDecode) {
|
||||
val data = fetchStage.read.waysValues.map(way => stage(way.data)).read(id)
|
||||
val word = data.subdivideIn(cpuDataWidth bits).read(io.cpu.decode.pc(memWordToCpuWordRange))
|
||||
io.cpu.decode.data := word
|
||||
}
|
||||
}
|
||||
|
||||
io.cpu.decode.redo := io.cpu.decode.isValid && !hit.valid
|
||||
when(io.cpu.decode.redo){
|
||||
io.cpu.prefetch.haltIt := True
|
||||
lineLoader.valid := True
|
||||
lineLoader.address := mmuRsp.physicalAddress //Could be optimise if mmu not used
|
||||
}
|
||||
|
||||
if(catchAccessFault) io.cpu.decode.error := hit.error
|
||||
if(catchMemoryTranslationMiss) io.cpu.decode.mmuMiss := mmuRsp.miss
|
||||
if(catchIllegalAccess) io.cpu.decode.illegalAccess := !mmuRsp.allowExecute || (io.cpu.decode.isUser && !mmuRsp.allowUser)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package vexriscv.plugin
|
||||
|
||||
import spinal.lib.com.jtag.Jtag
|
||||
import spinal.lib.system.debugger.{SystemDebugger, JtagBridge, SystemDebuggerConfig}
|
||||
import vexriscv.plugin.IntAluPlugin.{AluCtrlEnum, ALU_CTRL}
|
||||
import spinal.lib.system.debugger.{JtagBridge, SystemDebugger, SystemDebuggerConfig}
|
||||
import vexriscv.plugin.IntAluPlugin.{ALU_CTRL, AluCtrlEnum}
|
||||
import vexriscv._
|
||||
import vexriscv.ip._
|
||||
import spinal.core._
|
||||
import spinal.lib._
|
||||
import spinal.lib.bus.amba3.apb.{Apb3Config, Apb3}
|
||||
import spinal.lib.bus.avalon.{AvalonMMConfig, AvalonMM}
|
||||
import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config}
|
||||
import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
|
||||
case class DebugExtensionCmd() extends Bundle{
|
||||
|
@ -92,10 +94,18 @@ case class DebugExtensionIo() extends Bundle with IMasterSlave{
|
|||
}
|
||||
}
|
||||
|
||||
class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] {
|
||||
|
||||
//Allow to avoid instruction cache plugin to be confused by new instruction poping in the pipeline
|
||||
trait InstructionInjector{
|
||||
def isInjecting(stage : Stage) : Bool
|
||||
}
|
||||
|
||||
class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] with InstructionInjector {
|
||||
|
||||
var io : DebugExtensionIo = null
|
||||
|
||||
val injectionAsks = ArrayBuffer[(Stage, Bool)]()
|
||||
var isInjectingOnDecode : Bool = null
|
||||
override def isInjecting(stage: Stage) : Bool = if(stage == pipeline.decode) isInjectingOnDecode else False
|
||||
|
||||
object IS_EBREAK extends Stageable(Bool)
|
||||
override def setup(pipeline: VexRiscv): Unit = {
|
||||
|
@ -114,13 +124,15 @@ class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] {
|
|||
SRC2_CTRL -> Src2CtrlEnum.PC,
|
||||
ALU_CTRL -> AluCtrlEnum.ADD_SUB //Used to get the PC value in busReadDataReg
|
||||
))
|
||||
|
||||
isInjectingOnDecode = Bool()
|
||||
}
|
||||
|
||||
override def build(pipeline: VexRiscv): Unit = {
|
||||
import pipeline._
|
||||
import pipeline.config._
|
||||
|
||||
debugClockDomain {pipeline plug new Area{
|
||||
val logic = debugClockDomain {pipeline plug new Area{
|
||||
val insertDecodeInstruction = False
|
||||
val firstCycle = RegNext(False) setWhen (io.bus.cmd.ready)
|
||||
val secondCycle = RegNext(firstCycle)
|
||||
|
@ -168,12 +180,21 @@ class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] {
|
|||
}
|
||||
}
|
||||
|
||||
//Assign the bus write data into the register who drive the decode instruction, even if it need to cross some hierarchy (caches)
|
||||
Component.current.addPrePopTask(() => {
|
||||
val reg = decode.input(INSTRUCTION).getDrivingReg
|
||||
reg.component.rework {
|
||||
when(insertDecodeInstruction.pull()) {
|
||||
reg := io.bus.cmd.data.pull()
|
||||
//Check if the decode instruction is driven by a register
|
||||
val instructionDriver = try {decode.input(INSTRUCTION).getDrivingReg} catch { case _ : Throwable => null}
|
||||
if(instructionDriver != null){ //If yes =>
|
||||
//Insert the instruction by writing the "fetch to decode instruction register",
|
||||
// Work even if it need to cross some hierarchy (caches)
|
||||
instructionDriver.component.rework {
|
||||
when(insertDecodeInstruction.pull()) {
|
||||
instructionDriver := io.bus.cmd.data.pull()
|
||||
}
|
||||
}
|
||||
} else{
|
||||
//Insert the instruction via a mux in the decode stage
|
||||
when(RegNext(insertDecodeInstruction)){
|
||||
decode.input(INSTRUCTION) := RegNext(io.bus.cmd.data)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -193,7 +214,9 @@ class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] {
|
|||
when(stepIt && prefetch.arbitration.isFiring) {
|
||||
haltIt := True
|
||||
}
|
||||
|
||||
when(stepIt && Cat(pipeline.stages.map(_.arbitration.redoIt)).asBits.orR) {
|
||||
haltIt := False
|
||||
}
|
||||
io.resetOut := RegNext(resetIt)
|
||||
|
||||
if(serviceExist(classOf[InterruptionInhibitor])) {
|
||||
|
@ -207,5 +230,8 @@ class DebugPlugin(val debugClockDomain : ClockDomain) extends Plugin[VexRiscv] {
|
|||
}
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
isInjectingOnDecode := RegNext(logic.insertDecodeInstruction) init(False)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ case class RvfiPort() extends Bundle with IMasterSlave {
|
|||
//2) JALR => clear PC(0)
|
||||
//3) input(INSTRUCTION)(5) REGFILE_WRITE_VALID memory read with exception would not fire properly
|
||||
|
||||
class FomalPlugin extends Plugin[VexRiscv]{
|
||||
class FormalPlugin extends Plugin[VexRiscv]{
|
||||
|
||||
var rvfi : RvfiPort = null
|
||||
|
|
@ -28,11 +28,15 @@ class HaltOnExceptionPlugin() extends Plugin[VexRiscv] with ExceptionService {
|
|||
stages.head.insert(FORMAL_HALT) := False
|
||||
stages.foreach(stage => {
|
||||
val stagePorts = exceptionPortsInfos.filter(_.stage == stage)
|
||||
if(stagePorts.nonEmpty)
|
||||
when(stagePorts.map(_.port.valid).orR){
|
||||
if(stagePorts.nonEmpty) {
|
||||
when(stagePorts.map(info => info.port.valid).orR) {
|
||||
stage.output(FORMAL_HALT) := True
|
||||
stage.arbitration.haltItself := True
|
||||
}
|
||||
for(stage <- stages){
|
||||
stage.output(FORMAL_HALT) clearWhen(stage.arbitration.isFlushed)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,17 @@ import spinal.lib._
|
|||
|
||||
class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] {
|
||||
import config._
|
||||
assert(twoStageLogic || !askMemoryTranslation)
|
||||
|
||||
var iBus : InstructionCacheMemBus = null
|
||||
var mmuBus : MemoryTranslatorBus = null
|
||||
var decodeExceptionPort : Flow[ExceptionCause] = null
|
||||
var privilegeService : PrivilegeService = null
|
||||
var redoBranch : Flow[UInt] = null
|
||||
|
||||
object FLUSH_ALL extends Stageable(Bool)
|
||||
object IBUS_ACCESS_ERROR extends Stageable(Bool)
|
||||
object IBUS_MMU_MISS extends Stageable(Bool)
|
||||
object IBUS_ILLEGAL_ACCESS extends Stageable(Bool)
|
||||
override def setup(pipeline: VexRiscv): Unit = {
|
||||
import Riscv._
|
||||
import pipeline.config._
|
||||
|
@ -29,6 +31,9 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
|||
FLUSH_ALL -> True
|
||||
))
|
||||
|
||||
//TODO manage priority with branch prediction
|
||||
redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(pipeline.decode)
|
||||
|
||||
if(catchSomething) {
|
||||
val exceptionService = pipeline.service(classOf[ExceptionService])
|
||||
decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode,1)
|
||||
|
@ -68,59 +73,55 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
|||
|
||||
//Connect prefetch cache side
|
||||
cache.io.cpu.prefetch.isValid := prefetch.arbitration.isValid
|
||||
cache.io.cpu.prefetch.isFiring := prefetch.arbitration.isFiring
|
||||
cache.io.cpu.prefetch.address := prefetch.output(PC)
|
||||
cache.io.cpu.prefetch.pc := prefetch.output(PC)
|
||||
prefetch.arbitration.haltItself setWhen(cache.io.cpu.prefetch.haltIt)
|
||||
|
||||
//Connect fetch cache side
|
||||
cache.io.cpu.fetch.isValid := fetch.arbitration.isValid
|
||||
cache.io.cpu.fetch.isStuck := fetch.arbitration.isStuck
|
||||
if(!twoStageLogic) cache.io.cpu.fetch.isStuckByOthers := fetch.arbitration.isStuckByOthers
|
||||
cache.io.cpu.fetch.address := fetch.output(PC)
|
||||
if(!twoStageLogic) {
|
||||
fetch.arbitration.haltItself setWhen (cache.io.cpu.fetch.haltIt)
|
||||
cache.io.cpu.fetch.pc := fetch.output(PC)
|
||||
|
||||
if (mmuBus != null) {
|
||||
cache.io.cpu.fetch.mmuBus <> mmuBus
|
||||
} else {
|
||||
cache.io.cpu.fetch.mmuBus.rsp.physicalAddress := cache.io.cpu.fetch.mmuBus.cmd.virtualAddress
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowExecute := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowRead := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowWrite := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowUser := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.isIoAccess := False
|
||||
cache.io.cpu.fetch.mmuBus.rsp.miss := False
|
||||
}
|
||||
|
||||
if(dataOnDecode){
|
||||
decode.insert(INSTRUCTION) := cache.io.cpu.decode.data
|
||||
}else{
|
||||
fetch.insert(INSTRUCTION) := cache.io.cpu.fetch.data
|
||||
decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck,decode.input(INSTRUCTION),fetch.output(INSTRUCTION))
|
||||
decode.insert(INSTRUCTION_READY) := True
|
||||
}else {
|
||||
if (mmuBus != null) {
|
||||
cache.io.cpu.fetch.mmuBus <> mmuBus
|
||||
} else {
|
||||
cache.io.cpu.fetch.mmuBus.rsp.physicalAddress := cache.io.cpu.fetch.mmuBus.cmd.virtualAddress
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowExecute := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowRead := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowWrite := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.allowUser := True
|
||||
cache.io.cpu.fetch.mmuBus.rsp.isIoAccess := False
|
||||
cache.io.cpu.fetch.mmuBus.rsp.miss := False
|
||||
}
|
||||
}
|
||||
decode.insert(INSTRUCTION_READY) := True
|
||||
|
||||
cache.io.cpu.decode.pc := decode.output(PC)
|
||||
|
||||
val ownDecode = pipeline.plugins.filter(_.isInstanceOf[InstructionInjector]).foldLeft(True)(_ && !_.asInstanceOf[InstructionInjector].isInjecting(decode))
|
||||
cache.io.cpu.decode.isValid := decode.arbitration.isValid && ownDecode
|
||||
cache.io.cpu.decode.isStuck := decode.arbitration.isStuck
|
||||
cache.io.cpu.decode.isUser := (if(privilegeService != null) privilegeService.isUser(decode) else False)
|
||||
// cache.io.cpu.decode.pc := decode.input(PC)
|
||||
|
||||
if(twoStageLogic){
|
||||
cache.io.cpu.decode.isValid := decode.arbitration.isValid && RegNextWhen(fetch.arbitration.isValid, !decode.arbitration.isStuck) //avoid inserted instruction from debug module
|
||||
decode.arbitration.haltItself.setWhen(cache.io.cpu.decode.haltIt)
|
||||
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)
|
||||
decode.insert(INSTRUCTION) := cache.io.cpu.decode.data
|
||||
decode.insert(INSTRUCTION_ANTICIPATED) := cache.io.cpu.decode.dataAnticipated
|
||||
decode.insert(INSTRUCTION_READY) := !cache.io.cpu.decode.haltIt
|
||||
redoBranch.valid := cache.io.cpu.decode.redo
|
||||
redoBranch.payload := decode.input(PC)
|
||||
when(redoBranch.valid){
|
||||
decode.arbitration.redoIt := True
|
||||
decode.arbitration.flushAll := True
|
||||
}
|
||||
|
||||
|
||||
if(catchSomething){
|
||||
if(catchAccessFault) {
|
||||
if (!twoStageLogic) fetch.insert(IBUS_ACCESS_ERROR) := cache.io.cpu.fetch.error
|
||||
if (twoStageLogic) decode.insert(IBUS_ACCESS_ERROR) := cache.io.cpu.decode.error
|
||||
}
|
||||
|
||||
val accessFault = if(catchAccessFault) decode.input(IBUS_ACCESS_ERROR) else False
|
||||
val accessFault = if(catchAccessFault) cache.io.cpu.decode.error else False
|
||||
val mmuMiss = if(catchMemoryTranslationMiss) cache.io.cpu.decode.mmuMiss else False
|
||||
val illegalAccess = if(catchIllegalAccess) cache.io.cpu.decode.illegalAccess else False
|
||||
|
||||
decodeExceptionPort.valid := decode.arbitration.isValid && (accessFault || mmuMiss || illegalAccess)
|
||||
decodeExceptionPort.valid := decode.arbitration.isValid && ownDecode && (accessFault || mmuMiss || illegalAccess)
|
||||
decodeExceptionPort.code := mmuMiss ? U(14) | 1
|
||||
decodeExceptionPort.badAddr := decode.input(PC)
|
||||
}
|
||||
|
@ -130,11 +131,10 @@ class IBusCachedPlugin(config : InstructionCacheConfig, askMemoryTranslation : B
|
|||
cache.io.flush.cmd.valid := False
|
||||
when(arbitration.isValid && input(FLUSH_ALL)){
|
||||
cache.io.flush.cmd.valid := True
|
||||
decode.arbitration.flushAll := True
|
||||
|
||||
when(!cache.io.flush.cmd.ready){
|
||||
arbitration.haltItself := True
|
||||
} otherwise {
|
||||
decode.arbitration.flushAll := True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,6 +358,10 @@ public:
|
|||
|
||||
top->reset = 1;
|
||||
top->eval();
|
||||
top->clk = 1;
|
||||
top->eval();
|
||||
top->clk = 0;
|
||||
top->eval();
|
||||
#ifdef CSR
|
||||
top->timerInterrupt = 0;
|
||||
top->externalInterrupt = 1;
|
||||
|
@ -435,7 +439,7 @@ public:
|
|||
for(SimElement* simElement : simElements) simElement->preCycle();
|
||||
|
||||
if(withInstructionReadCheck){
|
||||
if(top->VexRiscv->decode_arbitration_isValid && !top->VexRiscv->decode_arbitration_haltItself){
|
||||
if(top->VexRiscv->decode_arbitration_isValid && !top->VexRiscv->decode_arbitration_haltItself && !top->VexRiscv->decode_arbitration_flushAll){
|
||||
uint32_t expectedData;
|
||||
bool dummy;
|
||||
iBusAccess(top->VexRiscv->decode_PC, &expectedData, &dummy);
|
||||
|
@ -598,7 +602,7 @@ public:
|
|||
virtual void preCycle(){
|
||||
if (top->iBus_cmd_valid && top->iBus_cmd_ready && pendingCount == 0) {
|
||||
assertEq(top->iBus_cmd_payload_address & 3,0);
|
||||
pendingCount = 8;
|
||||
pendingCount = (1 << top->iBus_cmd_payload_size)/4;
|
||||
address = top->iBus_cmd_payload_address;
|
||||
}
|
||||
}
|
||||
|
@ -610,7 +614,7 @@ public:
|
|||
ws->iBusAccess(address,&top->iBus_rsp_payload_data,&error);
|
||||
top->iBus_rsp_payload_error = error;
|
||||
pendingCount--;
|
||||
address = (address & ~0x1F) + ((address + 4) & 0x1F);
|
||||
address = address + 4;
|
||||
top->iBus_rsp_valid = 1;
|
||||
}
|
||||
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(7) < 100 && pendingCount == 0;
|
||||
|
@ -1606,7 +1610,7 @@ string riscvTestDiv[] = {
|
|||
};
|
||||
|
||||
string freeRtosTests[] = {
|
||||
"AltBlock", "AltQTest", "AltBlckQ", "AltPollQ", "blocktim", "countsem", "dead", "EventGroupsDemo", "flop", "integer", "QPeek",
|
||||
"AltBlckQ", "AltBlock", "AltQTest", "AltPollQ", "blocktim", "countsem", "dead", "EventGroupsDemo", "flop", "integer", "QPeek",
|
||||
"QueueSet", "recmutex", "semtest", "TaskNotify", "BlockQ", "crhook", "dynamic",
|
||||
"GenQTest", "PollQ", "QueueOverwrite", "QueueSetPolling", "sp_flop", "test1"
|
||||
//"flop", "sp_flop" // <- Simple test
|
||||
|
@ -1714,7 +1718,7 @@ int main(int argc, char **argv, char **env) {
|
|||
#ifdef CSR
|
||||
uint32_t machineCsrRef[] = {1,11, 2,0x80000003u, 3,0x80000007u, 4,0x8000000bu, 5,6,7,0x80000007u ,
|
||||
8,6,9,6,10,4,11,4, 12,13,0, 14,2, 15,5,16,17,1 };
|
||||
redo(REDO,TestX28("machineCsr",machineCsrRef, sizeof(machineCsrRef)/4).noInstructionReadCheck()->run(4e4);)
|
||||
redo(REDO,TestX28("machineCsr",machineCsrRef, sizeof(machineCsrRef)/4).noInstructionReadCheck()->run(10e4);)
|
||||
#endif
|
||||
#ifdef MMU
|
||||
uint32_t mmuRef[] = {1,2,3, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x22222222, 4, 0x11111111, 0x33333333, 0x33333333, 5,
|
||||
|
|
|
@ -27,6 +27,7 @@ ADDCFLAGS += -CFLAGS -DREDO=${REDO}
|
|||
ADDCFLAGS += -CFLAGS -pthread
|
||||
|
||||
ADDCFLAGS += -CFLAGS -DTHREAD_COUNT=${THREAD_COUNT}
|
||||
|
||||
ifeq ($(DHRYSTONE),yes)
|
||||
ADDCFLAGS += -CFLAGS -DDHRYSTONE
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
package vexriscv
|
||||
|
||||
import spinal.core._
|
||||
import spinal.lib.master
|
||||
import vexriscv.ip.InstructionCacheConfig
|
||||
import vexriscv.plugin._
|
||||
|
||||
object PlayGen extends App{
|
||||
def cpu() = new VexRiscv(
|
||||
config = VexRiscvConfig(
|
||||
plugins = List(
|
||||
new IBusCachedPlugin(
|
||||
config = InstructionCacheConfig(
|
||||
cacheSize = 16,
|
||||
bytePerLine = 4,
|
||||
wayCount = 1,
|
||||
wrappedMemAccess = false,
|
||||
addressWidth = 32,
|
||||
cpuDataWidth = 32,
|
||||
memDataWidth = 32,
|
||||
catchIllegalAccess = false,
|
||||
catchAccessFault = false,
|
||||
catchMemoryTranslationMiss = false,
|
||||
asyncTagMemory = false,
|
||||
twoStageLogic = false,
|
||||
preResetFlush = false
|
||||
),
|
||||
askMemoryTranslation = false
|
||||
),
|
||||
new FormalPlugin,
|
||||
new HaltOnExceptionPlugin,
|
||||
new PcManagerSimplePlugin(
|
||||
resetVector = 0x00000000l,
|
||||
relaxedPcCalculation = false
|
||||
),
|
||||
// new IBusSimplePlugin(
|
||||
// interfaceKeepData = false,
|
||||
// catchAccessFault = false
|
||||
// ),
|
||||
new DBusSimplePlugin(
|
||||
catchAddressMisaligned = true,
|
||||
catchAccessFault = false
|
||||
),
|
||||
new DecoderSimplePlugin(
|
||||
catchIllegalInstruction = true,
|
||||
forceLegalInstructionComputation = true
|
||||
),
|
||||
new RegFilePlugin(
|
||||
regFileReadyKind = plugin.SYNC,
|
||||
zeroBoot = false
|
||||
),
|
||||
new IntAluPlugin,
|
||||
new SrcPlugin(
|
||||
separatedAddSub = false,
|
||||
executeInsertion = false
|
||||
),
|
||||
new FullBarrielShifterPlugin,
|
||||
new HazardSimplePlugin(
|
||||
bypassExecute = false,
|
||||
bypassMemory = false,
|
||||
bypassWriteBack = false,
|
||||
bypassWriteBackBuffer = false,
|
||||
pessimisticUseSrc = false,
|
||||
pessimisticWriteRegFile = false,
|
||||
pessimisticAddressMatch = false
|
||||
),
|
||||
new BranchPlugin(
|
||||
earlyBranch = false,
|
||||
catchAddressMisaligned = true,
|
||||
prediction = NONE
|
||||
),
|
||||
new YamlPlugin("cpu0.yaml")
|
||||
)
|
||||
)
|
||||
)
|
||||
// Wrap with input/output registers
|
||||
def wrap(that : => VexRiscv) : Component = {
|
||||
val c = that
|
||||
// c.rework {
|
||||
// for (e <- c.getOrdredNodeIo) {
|
||||
// if (e.isInput) {
|
||||
// e.asDirectionLess()
|
||||
// e := RegNext(RegNext(in(cloneOf(e))))
|
||||
//
|
||||
// } else {
|
||||
// e.asDirectionLess()
|
||||
// out(cloneOf(e)) := RegNext(RegNext(e))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
c.rework{
|
||||
c.config.plugins.foreach{
|
||||
case p : IBusCachedPlugin => {
|
||||
p.iBus.asDirectionLess().unsetName()
|
||||
val iBusNew = master(IBusSimpleBus(false)).setName("iBus")
|
||||
|
||||
iBusNew.cmd.valid := p.iBus.cmd.valid
|
||||
iBusNew.cmd.pc := p.iBus.cmd.address
|
||||
p.iBus.cmd.ready := iBusNew.cmd.ready
|
||||
|
||||
val pending = RegInit(False) clearWhen(iBusNew.rsp.ready) setWhen (iBusNew.cmd.fire)
|
||||
p.iBus.rsp.valid := iBusNew.rsp.ready & pending
|
||||
p.iBus.rsp.error := iBusNew.rsp.error
|
||||
p.iBus.rsp.data := iBusNew.rsp.inst
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
c
|
||||
}
|
||||
SpinalConfig(
|
||||
defaultConfigForClockDomains = ClockDomainConfig(
|
||||
resetKind = spinal.core.SYNC,
|
||||
resetActiveLevel = spinal.core.HIGH
|
||||
),
|
||||
inlineRom = true
|
||||
).generateVerilog(wrap(cpu()))
|
||||
}
|
Loading…
Reference in New Issue