New faster/smaller/multi way instruction cache design.

Single or dual stage
This commit is contained in:
Dolu1990 2018-02-16 02:21:08 +01:00
parent 3853e0313b
commit 506e0e3f60
14 changed files with 378 additions and 318 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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)

View File

@ -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,

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}
}
})
}
}

View File

@ -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
}
}
}

View File

@ -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,

View File

@ -27,6 +27,7 @@ ADDCFLAGS += -CFLAGS -DREDO=${REDO}
ADDCFLAGS += -CFLAGS -pthread
ADDCFLAGS += -CFLAGS -DTHREAD_COUNT=${THREAD_COUNT}
ifeq ($(DHRYSTONE),yes)
ADDCFLAGS += -CFLAGS -DDHRYSTONE
endif

View File

@ -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()))
}