Data cache WIP

refractoring
This commit is contained in:
Charles Papon 2017-03-31 15:20:51 +02:00
parent 26597f78cd
commit 2f384364d8
6 changed files with 736 additions and 145 deletions

View File

@ -0,0 +1,533 @@
package SpinalRiscv.Plugin
import spinal.core._
import spinal.lib._
class DBusCachedPlugin {
}
case class DataCacheConfig( cacheSize : Int,
bytePerLine : Int,
wayCount : Int,
addressWidth : Int,
cpuDataWidth : Int,
memDataWidth : Int){
def burstSize = bytePerLine*8/memDataWidth
val burstLength = bytePerLine/(memDataWidth/8)
}
object DataCacheCpuCmdKind extends SpinalEnum{
val MEMORY,FLUSH,EVICT = 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 mask = Bits(p.cpuDataWidth/8 bit)
val bypass = 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 isStall = Bool
val haltIt = Bool
override def asMaster(): Unit = {
out(isValid, isStall)
in(haltIt)
}
}
case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMasterSlave{
val isValid = Bool
val haltIt = Bool
val data = Bits(p.cpuDataWidth bit)
override def asMaster(): Unit = {
out(isValid)
in(haltIt, data)
}
}
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)
}
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._
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 = 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 = 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)
})
tagsWriteCmd.valid := False
tagsWriteCmd.payload.assignDontCare()
dataWriteCmd.valid := False
dataWriteCmd.payload.assignDontCare()
io.mem.cmd.valid := False
io.mem.cmd.payload.assignDontCare()
val ways = Array.tabulate(wayCount)(id => new Area{
val tags = Mem(new LineInfo(),wayLineCount)
val data = Mem(Bits(wordWidth bit),wayWordCount)
when(tagsWriteCmd.valid && tagsWriteCmd.way === id){
tags(tagsWriteCmd.address) := tagsWriteCmd.data
}
when(dataWriteCmd.valid && dataWriteCmd.way === id){
data.write(
address = dataWriteCmd.address,
data = dataWriteCmd.data,
mask = dataWriteCmd.mask
)
}
val dataReadRsp = data.readSync(dataReadCmd)
})
val dataReadedValue = Vec(id => RegNext(ways(id).dataReadRsp),ways.length)
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()
request.ready := False
val buffer = Mem(Bits(p.memDataWidth bits),memTransactionPerLine << 1) // << 1 because of cyclone II issue, should be removed //.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 dataReadCmdOccureLast = RegNext(dataReadCmdOccure)
when(request.valid && !readLineCmdCounter.msb){
readLineCmdCounter := readLineCmdCounter + 1
//dataReadCmd := request.address(lineRange.high downto wordRange.low) Done in the manager
dataReadCmdOccure := True
}
//Fill the buffer with line read responses
val readLineRspCounter = Reg(UInt(log2Up(memTransactionPerLine + 1) bits)) init(0)
when(readLineCmdCounter >= 2 && !readLineRspCounter.msb && Delay(dataReadCmdOccure,2)){
buffer(readLineRspCounter.resized) := dataReadedValue(request.way)
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 manager = new Area {
io.flushDone := False
io.cpu.execute.haltIt := False
val request = RegNextWhen(io.cpu.execute.args, !io.cpu.execute.isStuck)
//Evict the cache after reset
val requestValid = io.cpu.memory.isValid || RegNextWhen(False, !io.cpu.memory.isStall, True)
request.kind.getDrivingReg.init(DataCacheCpuCmdKind.EVICT)
request.all.getDrivingReg.init(True)
request.address.getDrivingReg.init(0)
io.cpu.memory.haltIt := requestValid
val waysHitValid = False
val waysHitOneHot = Bits(wayCount bits)
val waysHitId = OHToUInt(waysHitOneHot)
val waysHitInfo = new LineInfo().assignDontCare()
//delayedXX are used to relax logic timings in flush and evict modes
val delayedValid = RegNext(io.cpu.memory.isStall) init(False)
val delayedWaysHitValid = RegNext(waysHitValid)
val delayedWaysHitId = RegNext(waysHitId)
val waysReadAddress = Mux(io.cpu.memory.isStall, request.address, io.cpu.execute.address)
tagsReadCmd := waysReadAddress(lineRange)
dataReadCmd := waysReadAddress(lineRange.high downto wordRange.low)
when(victim.dataReadCmdOccure){
dataReadCmd := victim.request.address(lineRange) @@ victim.readLineCmdCounter(victim.readLineCmdCounter.high-1 downto 0)
}
val waysRead = for ((way,id) <- ways.zipWithIndex) yield new Area {
val tag = way.tags.readSync(tagsReadCmd)
//Write first
when(tagsWriteLastCmd.valid && tagsWriteLastCmd.way === id && tagsWriteLastCmd.address === RegNext(waysReadAddress(lineRange))){
tag := tagsWriteLastCmd.data
}
waysHitOneHot(id) := tag.used && tag.address === request.address(tagRange)
when(waysHitOneHot(id)) {
waysHitValid := True
waysHitInfo := tag
}
}
val writebackWayId = U(0) //Only one way compatible
val writebackWayInfo = Vec(waysRead.map(_.tag))(writebackWayId)
val cpuRspIn = Stream(wrap(new Bundle{
val fromBypass = Bool
val wayId = UInt(log2Up(wayCount) bits)
}))
cpuRspIn.valid := False
cpuRspIn.fromBypass := False
cpuRspIn.wayId := waysHitId
//Loader interface
val loaderValid = False
val loaderReady = False
val loadingDone = RegNext(loaderValid && loaderReady) init(False) //one cycle pulse
val victimSent = RegInit(False)
victimSent := (victimSent || victim.requestIn.fire) && io.cpu.memory.isStall
val flushAllState = RegInit(False) //Used to keep logic timings fast
val flushAllDone = RegNext(False) init(False)
when(requestValid) {
switch(request.kind) {
import DataCacheCpuCmdKind._
is(EVICT){
when(request.all){
tagsWriteCmd.valid := True
tagsWriteCmd.way := 0
tagsWriteCmd.address := request.address(lineRange)
tagsWriteCmd.data.used := False
when(request.address(lineRange) =/= lineCount-1){
request.address.getDrivingReg(lineRange) := request.address(lineRange) + 1
}otherwise{
io.cpu.memory.haltIt := False
}
}otherwise{
when(delayedValid) {
when(delayedWaysHitValid) {
tagsWriteCmd.valid := True
tagsWriteCmd.way := delayedWaysHitId
tagsWriteCmd.address := request.address(lineRange)
tagsWriteCmd.data.used := False
}
io.cpu.memory.haltIt := False
}
}
}
is(FLUSH) {
when(request.all) {
when(!flushAllState){
victim.requestIn.valid := waysRead(0).tag.used && waysRead(0).tag.dirty
victim.requestIn.way := writebackWayId
victim.requestIn.address := writebackWayInfo.address @@ request.address(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
tagsWriteCmd.way := writebackWayId
tagsWriteCmd.address := request.address(lineRange)
tagsWriteCmd.data.used := False
when(!victim.requestIn.isStall) {
request.address.getDrivingReg(lineRange) := request.address(lineRange) + 1
flushAllDone := request.address(lineRange) === lineCount-1
flushAllState := True
tagsWriteCmd.valid := True
}
} otherwise{
//Wait tag read
flushAllState := False
io.cpu.memory.haltIt.clearWhen(flushAllDone)
io.flushDone := flushAllDone
}
} otherwise {
when(delayedValid) {
when(delayedWaysHitValid) {
io.cpu.memory.haltIt.clearWhen(victim.requestIn.ready)
victim.requestIn.valid := True
victim.requestIn.way := writebackWayId
victim.requestIn.address := writebackWayInfo.address @@ request.address(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
tagsWriteCmd.valid := victim.requestIn.ready
tagsWriteCmd.way := writebackWayId
tagsWriteCmd.address := request.address(lineRange)
tagsWriteCmd.data.used := False
} otherwise{
io.cpu.memory.haltIt := False
io.flushDone := True
}
}
}
}
is(MEMORY) {
when(request.bypass) {
when(!victim.request.valid) {
//Can't insert mem cmd into a victim write burst
io.mem.cmd.valid := !(!request.wr && !cpuRspIn.ready)
io.mem.cmd.wr := request.wr
io.mem.cmd.address := request.address(tagRange.high downto wordRange.low) @@ U(0,wordRange.low bit)
io.mem.cmd.mask := request.mask
io.mem.cmd.data := request.data
io.mem.cmd.length := 1
cpuRspIn.valid := !request.wr && io.mem.cmd.fire
cpuRspIn.fromBypass := True
io.cpu.memory.haltIt.clearWhen(io.mem.cmd.fire)
} otherwise {
io.cpu.memory.haltIt := True //TODO redondent ?
}
} otherwise {
when(waysHitValid && !loadingDone) { // !loadingDone => don't solve the request directly after loader (data write to read latency)
when(request.wr) {
dataWriteCmd.valid := True
dataWriteCmd.way := waysHitId
dataWriteCmd.address := request.address(lineRange.high downto wordRange.low)
dataWriteCmd.data := request.data
dataWriteCmd.mask := request.mask
tagsWriteCmd.valid := True
tagsWriteCmd.way := waysHitId
tagsWriteCmd.address := request.address(lineRange)
tagsWriteCmd.data.used := True
tagsWriteCmd.data.dirty := True
tagsWriteCmd.data.address := request.address(tagRange)
io.cpu.memory.haltIt := False
} otherwise {
cpuRspIn.valid := !victim.dataReadCmdOccureLast
io.cpu.memory.haltIt.clearWhen(cpuRspIn.ready && !victim.dataReadCmdOccureLast) //dataReadCmdOccure to avoid the case where flush,then read will victim use data read
}
} otherwise {
io.cpu.memory.haltIt := True //Exit this state automaticly (tags read port write first logic) TODO redondent ?
loaderValid := !loadingDone && !(!victimSent && victim.request.isStall) //Wait previous victim request to be completed
when(writebackWayInfo.used && writebackWayInfo.dirty) {
victim.requestIn.valid := !victimSent
victim.requestIn.way := writebackWayId
victim.requestIn.address := writebackWayInfo.address @@ request.address(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
}
}
}
}
}
}
val cpuRsp = cpuRspIn.m2sPipe()
val cpuRspIsWaitingMemRsp = cpuRsp.valid && io.mem.rsp.valid
io.cpu.writeBack.haltIt := io.cpu.memory.isValid && cpuRsp.isStall
io.cpu.writeBack.data := Mux(cpuRsp.fromBypass,io.mem.rsp.data,dataReadedValue(cpuRsp.wayId))
cpuRsp.ready := !(cpuRsp.fromBypass && !io.mem.rsp.valid)
}
//The whole life of a loading task, the corresponding manager request is present
val loader = new Area{
val valid = RegNext(manager.loaderValid)
val wayId = RegNext(manager.writebackWayId)
val baseAddress = manager.request.address
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(io.mem.rsp.valid && !manager.cpuRspIsWaitingMemRsp){
dataWriteCmd.valid := True
dataWriteCmd.way := wayId
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
tagsWriteCmd.valid := True
tagsWriteCmd.way := wayId
tagsWriteCmd.address := baseAddress(lineRange)
tagsWriteCmd.data.used := True
tagsWriteCmd.data.dirty := False
tagsWriteCmd.data.address := baseAddress(tagRange)
manager.loaderReady := True
}
}
//Avoid read after write data hazard
//TODO FIX it to not stall write after read ? , requestValid is pessimistic ?
io.cpu.execute.haltIt := io.cpu.execute.address === manager.request.address && manager.requestValid && io.cpu.execute.isValid
}
object DataCacheMain{
def main(args: Array[String]) {
SpinalVhdl({
implicit val p = DataCacheConfig(
cacheSize =4096,
bytePerLine =32,
wayCount = 1,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32)
new WrapWithReg.Wrapper(new DataCache(p)).setDefinitionName("TopLevel")
})
// SpinalVhdl({
// implicit val p = DataCacheConfig(
// cacheSize =512,
// bytePerLine =16,
// wayCount = 1,
// addressWidth = 12,
// cpuDataWidth = 16,
// memDataWidth = 16)
// new DataCache(p)
// })
}
}

View File

@ -12,16 +12,29 @@ case class DBusSimpleCmd() extends Bundle{
val size = UInt(2 bit)
}
case class DBusSimpleRsp() extends Bundle{
case class DBusSimpleRsp() extends Bundle with IMasterSlave{
val ready = Bool
val error = Bool
val data = Bits(32 bit)
override def asMaster(): Unit = {
out(ready,error,data)
}
}
case class DBusSimpleBus() extends Bundle with IMasterSlave{
val cmd = Stream(DBusSimpleCmd())
val rsp = DBusSimpleRsp()
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
}
class DBusSimplePlugin(catchAddressMisaligned : Boolean, catchAccessFault : Boolean) extends Plugin[VexRiscv]{
var dCmd : Stream[DBusSimpleCmd] = null
var dRsp : DBusSimpleRsp = null
var dBus : DBusSimpleBus = null
object MemoryCtrlEnum extends SpinalEnum{
val WR, RD = newElement()
@ -88,49 +101,50 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean, catchAccessFault : Bool
import pipeline._
import pipeline.config._
//Emit dCmd request
dBus = master(DBusSimpleBus()).setName("dBus")
//Emit dBus.cmd request
execute plug new Area{
import execute._
dCmd = master(Stream(DBusSimpleCmd())).setName("dCmd")
dCmd.valid := arbitration.isValid && input(MEMORY_ENABLE) && !arbitration.isStuckByOthers && !arbitration.removeIt
dCmd.wr := input(INSTRUCTION)(5)
dCmd.address := input(SRC_ADD_SUB).asUInt
dCmd.size := input(INSTRUCTION)(13 downto 12).asUInt
dCmd.payload.data := dCmd.size.mux (
dBus.cmd.valid := arbitration.isValid && input(MEMORY_ENABLE) && !arbitration.isStuckByOthers && !arbitration.removeIt
dBus.cmd.wr := input(INSTRUCTION)(5)
dBus.cmd.address := input(SRC_ADD_SUB).asUInt
dBus.cmd.size := input(INSTRUCTION)(13 downto 12).asUInt
dBus.cmd.payload.data := dBus.cmd.size.mux (
U(0) -> input(REG2)(7 downto 0) ## input(REG2)(7 downto 0) ## input(REG2)(7 downto 0) ## input(REG2)(7 downto 0),
U(1) -> input(REG2)(15 downto 0) ## input(REG2)(15 downto 0),
default -> input(REG2)(31 downto 0)
)
when(arbitration.isValid && input(MEMORY_ENABLE) && !dCmd.ready){
when(arbitration.isValid && input(MEMORY_ENABLE) && !dBus.cmd.ready){
arbitration.haltIt := True
}
insert(MEMORY_ADDRESS_LOW) := dCmd.address(1 downto 0)
insert(MEMORY_ADDRESS_LOW) := dBus.cmd.address(1 downto 0)
if(catchAddressMisaligned){
executeExceptionPort.code := (dCmd.wr ? U(6) | U(4)).resized
executeExceptionPort.badAddr := dCmd.address
executeExceptionPort.code := (dBus.cmd.wr ? U(6) | U(4)).resized
executeExceptionPort.badAddr := dBus.cmd.address
executeExceptionPort.valid := (arbitration.isValid && input(MEMORY_ENABLE)
&& ((dCmd.size === 2 && dCmd.address(1 downto 0) =/= 0) || (dCmd.size === 1 && dCmd.address(0 downto 0) =/= 0)))
&& ((dBus.cmd.size === 2 && dBus.cmd.address(1 downto 0) =/= 0) || (dBus.cmd.size === 1 && dBus.cmd.address(0 downto 0) =/= 0)))
}
}
//Collect dRsp read responses
//Collect dBus.rsp read responses
memory plug new Area {
import memory._
dRsp = in(DBusSimpleRsp()).setName("dRsp")
insert(MEMORY_READ_DATA) := dRsp.data
arbitration.haltIt setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !dRsp.ready)
insert(MEMORY_READ_DATA) := dBus.rsp.data
arbitration.haltIt setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !dBus.rsp.ready)
if(catchAccessFault){
memoryExceptionPort.valid := arbitration.isValid && input(MEMORY_ENABLE) && dRsp.ready && dRsp.error
memoryExceptionPort.valid := arbitration.isValid && input(MEMORY_ENABLE) && dBus.rsp.ready && dBus.rsp.error
memoryExceptionPort.code := 5
memoryExceptionPort.badAddr := input(REGFILE_WRITE_DATA).asUInt //Drived by IntAluPlugin
}
assert(!(dRsp.ready && input(MEMORY_ENABLE) && arbitration.isValid && arbitration.isStuck),"DBusSimplePlugin doesn't allow memory stage stall when read happend")
assert(!(dBus.rsp.ready && input(MEMORY_ENABLE) && arbitration.isValid && arbitration.isStuck),"DBusSimplePlugin doesn't allow memory stage stall when read happend")
}
//Reformat read responses, REGFILE_WRITE_DATA overriding

View File

@ -9,15 +9,29 @@ case class IBusSimpleCmd() extends Bundle{
val pc = UInt(32 bits)
}
case class IBusSimpleRsp() extends Bundle{
case class IBusSimpleRsp() extends Bundle with IMasterSlave{
val ready = Bool
val error = Bool
val inst = Bits(32 bits)
override def asMaster(): Unit = {
out(ready,error,inst)
}
}
case class IBusSimpleBus() extends Bundle with IMasterSlave{
var cmd = Stream(IBusSimpleCmd())
var rsp = IBusSimpleRsp()
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
}
class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean) extends Plugin[VexRiscv]{
var iCmd : Stream[IBusSimpleCmd] = null
var iRsp : IBusSimpleRsp = null
var iBus : IBusSimpleBus = null
object IBUS_ACCESS_ERROR extends Stageable(Bool)
var decodeExceptionPort : Flow[ExceptionCause] = null
@ -34,18 +48,18 @@ class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean)
import pipeline._
import pipeline.config._
//Emit iCmd request
require(interfaceKeepData)
iCmd = master(Stream(IBusSimpleCmd())).setName("iCmd")
iCmd.valid := prefetch.arbitration.isFiring //prefetch.arbitration.isValid && !prefetch.arbitration.isStuckByOthers
iCmd.pc := prefetch.output(PC)
prefetch.arbitration.haltIt setWhen(!iCmd.ready)
iBus = master(IBusSimpleBus()).setName("iBus")
//Emit iBus.cmd request
iBus.cmd.valid := prefetch.arbitration.isFiring //prefetch.arbitration.isValid && !prefetch.arbitration.isStuckByOthers
iBus.cmd.pc := prefetch.output(PC)
prefetch.arbitration.haltIt setWhen(!iBus.cmd.ready)
//Insert iRsp into INSTRUCTION
iRsp = in(IBusSimpleRsp()).setName("iRsp")
fetch.insert(INSTRUCTION) := iRsp.inst
fetch.insert(IBUS_ACCESS_ERROR) := iRsp.error
fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iRsp.ready)
//Insert iBus.rsp into INSTRUCTION
fetch.insert(INSTRUCTION) := iBus.rsp.inst
fetch.insert(IBUS_ACCESS_ERROR) := iBus.rsp.error
fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iBus.rsp.ready)
if(catchAccessFault){
decodeExceptionPort.valid := decode.arbitration.isValid && decode.input(IBUS_ACCESS_ERROR)

View File

@ -79,22 +79,22 @@ object TopLevel {
config.plugins ++= List(
new PcManagerSimplePlugin(0x00000000l, false),
// new IBusSimplePlugin(
// interfaceKeepData = true,
// catchAccessFault = true
// ),
new IBusCachedPlugin(
config = InstructionCacheConfig(
cacheSize =4096,
bytePerLine =32,
wayCount = 1,
wrappedMemAccess = true,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32,
catchAccessFault = true
)
new IBusSimplePlugin(
interfaceKeepData = true,
catchAccessFault = true
),
// new IBusCachedPlugin(
// config = InstructionCacheConfig(
// cacheSize =4096,
// bytePerLine =32,
// wayCount = 1,
// wrappedMemAccess = true,
// addressWidth = 32,
// cpuDataWidth = 32,
// memDataWidth = 32,
// catchAccessFault = true
// )
// ),
new DecoderSimplePlugin(
catchIllegalInstruction = true
),

View File

@ -141,8 +141,7 @@ public:
};
class Workspace;
void fillSimELements(Workspace *ws);
class Workspace{
public:
@ -150,6 +149,8 @@ public:
vector<SimElement*> simElements;
Memory mem;
string name;
uint64_t mTimeCmp = 0;
uint64_t mTime = 0;
VVexRiscv* top;
int i;
uint32_t bootPc = -1;
@ -173,7 +174,7 @@ public:
regTraces.open (name + ".regTrace");
memTraces.open (name + ".memTrace");
logTraces.open (name + ".logTrace");
fillSimELements(this);
fillSimELements();
}
virtual ~Workspace(){
@ -190,7 +191,7 @@ public:
Workspace* bootAt(uint32_t pc) { bootPc = pc;}
virtual bool isAccessError(uint32_t addr) { return addr == 0xF00FFF60u; }
virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error) {
assert(addr % 4 == 0);
*data = ( (mem[addr + 0] << 0)
@ -199,10 +200,66 @@ public:
| (mem[addr + 3] << 24));
*error = addr == 0xF00FFF60u;
}
virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size, uint32_t *data, bool *error) {
*error = addr == 0xF00FFF60u;
if(wr){
memTraces <<
#ifdef TRACE_WITH_TIME
(currentTime
#ifdef REF
-2
#endif
) <<
#endif
" : WRITE mem" << (1 << size) << "[" << addr << "] = " << *data << endl;
for(uint32_t b = 0;b < (1 << size);b++){
uint32_t offset = (addr+b)&0x3;
*mem.get(addr + b) = *data >> (offset*8);
}
switch(addr){
case 0xF00FFF00u: {
cout << mem[0xF00FFF00u];
logTraces << (char)mem[0xF00FFF00u];
break;
}
case 0xF00FFF20u: pass(); break;
case 0xF00FFF48u: mTimeCmp = (mTimeCmp & 0xFFFFFFFF00000000) | *data;break;
case 0xF00FFF4Cu: mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)*data) << 32); /*cout << "mTimeCmp <= " << mTimeCmp << endl; */break;
}
}else{
*data = VL_RANDOM_I(32);
for(uint32_t b = 0;b < (1 << size);b++){
uint32_t offset = (addr+b)&0x3;
*data &= ~(0xFF << (offset*8));
*data |= mem[addr + b] << (offset*8);
}
switch(addr){
case 0xF00FFF10u:
*data = i/2;
break;
case 0xF00FFF40u: *data = mTime; break;
case 0xF00FFF44u: *data = mTime >> 32; break;
case 0xF00FFF48u: *data = mTimeCmp; break;
case 0xF00FFF4Cu: *data = mTimeCmp >> 32; break;
}
memTraces <<
#ifdef TRACE_WITH_TIME
(currentTime
#ifdef REF
-2
#endif
) <<
#endif
" : READ mem" << (1 << size) << "[" << addr << "] = " << *data << endl;
}
}
virtual void postReset() {}
virtual void checks(){}
virtual void pass(){ throw success();}
virtual void fail(){ throw std::exception();}
virtual void fillSimELements();
void dump(int i){
#ifdef TRACE
if(i/2 >= TRACE_START) tfp->dump(i);
@ -210,8 +267,7 @@ public:
}
Workspace* run(uint32_t timeout = 5000){
// cout << "Start " << name << endl;
uint64_t mTimeCmp = 0;
uint64_t mTime = 0;
currentTime = 4;
// init trace dump
Verilated::traceEverOn(true);
@ -224,8 +280,6 @@ public:
// Reset
top->clk = 0;
top->reset = 0;
top->dCmd_ready = 1;
top->dRsp_ready = 1;
for(SimElement* simElement : simElements) simElement->onReset();
@ -251,9 +305,6 @@ public:
try {
// run simulation for 100 clock periods
uint32_t dRsp_inst_next = VL_RANDOM_I(32);
bool dRsp_error_next = false;
bool dRsp_ready_pending = false;
for (i = 16; i < timeout*2; i+=2) {
mTime = i/2;
#ifdef CSR
@ -275,63 +326,6 @@ public:
dump(i + 1);
if (top->dCmd_valid && top->dCmd_ready && ! dRsp_ready_pending) {
dRsp_ready_pending = true;
dRsp_inst_next = VL_RANDOM_I(32);
uint32_t addr = top->dCmd_payload_address;
dRsp_error_next = isAccessError(addr);
if(top->dCmd_payload_wr){
memTraces <<
#ifdef TRACE_WITH_TIME
(currentTime
#ifdef REF
-2
#endif
) <<
#endif
" : WRITE mem" << (1 << top->dCmd_payload_size) << "[" << addr << "] = " << top->dCmd_payload_data << endl;
for(uint32_t b = 0;b < (1 << top->dCmd_payload_size);b++){
uint32_t offset = (addr+b)&0x3;
*mem.get(addr + b) = top->dCmd_payload_data >> (offset*8);
}
switch(addr){
case 0xF00FFF00u: {
cout << mem[0xF00FFF00u];
logTraces << (char)mem[0xF00FFF00u];
break;
}
case 0xF00FFF20u: pass(); break;
case 0xF00FFF48u: mTimeCmp = (mTimeCmp & 0xFFFFFFFF00000000) | top->dCmd_payload_data;break;
case 0xF00FFF4Cu: mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)top->dCmd_payload_data) << 32); /*cout << "mTimeCmp <= " << mTimeCmp << endl; */break;
}
}else{
for(uint32_t b = 0;b < (1 << top->dCmd_payload_size);b++){
uint32_t offset = (addr+b)&0x3;
dRsp_inst_next &= ~(0xFF << (offset*8));
dRsp_inst_next |= mem[addr + b] << (offset*8);
}
switch(addr){
case 0xF00FFF10u:
dRsp_inst_next = i/2;
break;
case 0xF00FFF40u: dRsp_inst_next = mTime; break;
case 0xF00FFF44u: dRsp_inst_next = mTime >> 32; break;
case 0xF00FFF48u: dRsp_inst_next = mTimeCmp; break;
case 0xF00FFF4Cu: dRsp_inst_next = mTimeCmp >> 32; break;
}
memTraces <<
#ifdef TRACE_WITH_TIME
(currentTime
#ifdef REF
-2
#endif
) <<
#endif
" : READ mem" << (1 << top->dCmd_payload_size) << "[" << addr << "] = " << dRsp_inst_next << endl;
}
}
if(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_valid == 1 && top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address != 0){
regTraces <<
@ -358,17 +352,6 @@ public:
for(SimElement* simElement : simElements) simElement->postCycle();
top->dRsp_ready = 0;
if(dRsp_ready_pending && (!dStall || VL_RANDOM_I(8) < 100)){
top->dRsp_data = dRsp_inst_next;
dRsp_ready_pending = false;
top->dRsp_ready = 1;
top->dRsp_error = dRsp_error_next;
} else{
top->dRsp_data = VL_RANDOM_I(32);
}
if(dStall) top->dCmd_ready = VL_RANDOM_I(8) < 100 && !dRsp_ready_pending;
if (Verilated::gotFinish())
@ -411,27 +394,27 @@ public:
}
virtual void onReset(){
top->iCmd_ready = 1;
top->iRsp_ready = 1;
top->iBus_cmd_ready = 1;
top->iBus_rsp_ready = 1;
}
virtual void preCycle(){
if (top->iCmd_valid && top->iCmd_ready && !pending) {
assertEq(top->iCmd_payload_pc & 3,0);
if (top->iBus_cmd_valid && top->iBus_cmd_ready && !pending) {
assertEq(top->iBus_cmd_payload_pc & 3,0);
pending = true;
ws->iBusAccess(top->iCmd_payload_pc,&inst_next,&error_next);
ws->iBusAccess(top->iBus_cmd_payload_pc,&inst_next,&error_next);
}
}
virtual void postCycle(){
top->iRsp_ready = !pending;
if(pending && (!ws->iStall || VL_RANDOM_I(8) < 100)){
top->iRsp_inst = inst_next;
top->iBus_rsp_ready = !pending;
if(pending && (!ws->iStall || VL_RANDOM_I(7) < 100)){
top->iBus_rsp_inst = inst_next;
pending = false;
top->iRsp_ready = 1;
top->iRsp_error = error_next;
top->iBus_rsp_ready = 1;
top->iBus_rsp_error = error_next;
}
if(ws->iStall) top->iCmd_ready = VL_RANDOM_I(8) < 100 && !pending;
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(7) < 100 && !pending;
}
};
#endif
@ -469,25 +452,72 @@ public:
virtual void postCycle(){
bool error;
top->iBus_rsp_valid = 0;
if(pendingCount != 0 && (!ws->iStall || VL_RANDOM_I(8) < 100)){
if(pendingCount != 0 && (!ws->iStall || VL_RANDOM_I(7) < 100)){
ws->iBusAccess(address,&top->iBus_rsp_payload_data,&error);
top->iBus_rsp_payload_error = error;
pendingCount--;
address = (address & ~0x1F) + ((address + 4) & 0x1F);
top->iBus_rsp_valid = 1;
}
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(8) < 100 && pendingCount == 0;
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(7) < 100 && pendingCount == 0;
}
};
#endif
void fillSimELements(Workspace *ws){
#ifdef DBUS_SIMPLE
class DBusSimple : public SimElement{
public:
uint32_t data_next = VL_RANDOM_I(32);
bool error_next = false;
bool pending = false;
Workspace *ws;
VVexRiscv* top;
DBusSimple(Workspace* ws){
this->ws = ws;
this->top = ws->top;
}
virtual void onReset(){
top->dBus_cmd_ready = 1;
top->dBus_rsp_ready = 1;
}
virtual void preCycle(){
if (top->dBus_cmd_valid && top->dBus_cmd_ready && !pending) {
pending = true;
data_next = top->dBus_cmd_payload_data;
ws->dBusAccess(top->dBus_cmd_payload_address,top->dBus_cmd_payload_wr,top->dBus_cmd_payload_size,&data_next,&error_next);
}
}
virtual void postCycle(){
top->dBus_rsp_ready = 0;
if(pending && (!ws->dStall || VL_RANDOM_I(7) < 100)){
pending = false;
top->dBus_rsp_ready = 1;
top->dBus_rsp_data = data_next;
top->dBus_rsp_error = error_next;
} else{
top->dBus_rsp_data = VL_RANDOM_I(32);
}
if(ws->dStall) top->dBus_cmd_ready = VL_RANDOM_I(7) < 100 && !pending;
}
};
#endif
void Workspace::fillSimELements(){
#ifdef IBUS_SIMPLE
ws->simElements.push_back(new IBusSimple(ws));
simElements.push_back(new IBusSimple(this));
#endif
#ifdef IBUS_CACHED
ws->simElements.push_back(new IBusCached(ws));
simElements.push_back(new IBusCached(this));
#endif
#ifdef DBUS_SIMPLE
simElements.push_back(new DBusSimple(this));
#endif
}

View File

@ -1,4 +1,4 @@
IBUS=IBUS_CACHED
IBUS=IBUS_SIMPLE
DBUS=DBUS_SIMPLE
TRACE=no
TRACE_START=0