#60 Got the new data cache design passing all tests and running linux

This commit is contained in:
Charles Papon 2019-04-02 23:44:53 +02:00
parent fd4da77084
commit 8be40e637b
7 changed files with 143 additions and 70 deletions

View File

@ -33,6 +33,7 @@ case class ExceptionCause() extends Bundle{
trait ExceptionService{
def newExceptionPort(stage : Stage, priority : Int = 0) : Flow[ExceptionCause]
def isExceptionPending() : Bool
}
trait PrivilegeService{

View File

@ -96,45 +96,45 @@ object LinuxGen {
new DummyFencePlugin(), //TODO should be removed for design with caches
//Uncomment the whole IBusSimplePlugin and comment IBusCachedPlugin if you want uncached iBus config
new IBusSimplePlugin(
resetVector = 0x80000000l,
cmdForkOnSecondStage = false,
cmdForkPersistence = false,
prediction = NONE,
historyRamSizeLog2 = 10,
catchAccessFault = true,
compressedGen = true,
busLatencyMin = 1,
injectorStage = true,
memoryTranslatorPortConfig = withMmu generate MmuPortConfig(
portTlbSize = 4
)
),
//Uncomment the whole IBusCachedPlugin and comment IBusSimplePlugin if you want cached iBus config
// new IBusCachedPlugin(
// new IBusSimplePlugin(
// resetVector = 0x80000000l,
// compressedGen = true,
// cmdForkOnSecondStage = false,
// cmdForkPersistence = false,
// prediction = NONE,
// historyRamSizeLog2 = 10,
// catchAccessFault = true,
// compressedGen = true,
// busLatencyMin = 1,
// injectorStage = true,
// config = InstructionCacheConfig(
// cacheSize = 4096,
// bytePerLine = 32,
// wayCount = 1,
// addressWidth = 32,
// cpuDataWidth = 32,
// memDataWidth = 32,
// catchIllegalAccess = true,
// catchAccessFault = true,
// asyncTagMemory = false,
// twoCycleRam = false,
// twoCycleCache = true
// )
// ),
// memoryTranslatorPortConfig = MmuPortConfig(
// memoryTranslatorPortConfig = withMmu generate MmuPortConfig(
// portTlbSize = 4
// )
// ),
//Uncomment the whole IBusCachedPlugin and comment IBusSimplePlugin if you want cached iBus config
new IBusCachedPlugin(
resetVector = 0x80000000l,
compressedGen = true,
prediction = NONE,
injectorStage = true,
config = InstructionCacheConfig(
cacheSize = 4096,
bytePerLine = 32,
wayCount = 1,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32,
catchIllegalAccess = true,
catchAccessFault = true,
asyncTagMemory = false,
twoCycleRam = false,
twoCycleCache = true
// )
),
memoryTranslatorPortConfig = MmuPortConfig(
portTlbSize = 4
)
),
// ).newTightlyCoupledPort(TightlyCoupledPortParameter("iBusTc", a => a(30 downto 28) === 0x0 && a(5))),
// new DBusSimplePlugin(
// catchAddressMisaligned = true,
@ -156,17 +156,16 @@ object LinuxGen {
catchAccessError = true,
catchIllegal = true,
catchUnaligned = true,
atomicEntriesCount = 2
)
// ),
// memoryTranslatorPortConfig = null
// memoryTranslatorPortConfig = MmuPortConfig(
// portTlbSize = 4
atomicEntriesCount = 1
// )
),
memoryTranslatorPortConfig = withMmu generate MmuPortConfig(
portTlbSize = 4
)
),
new StaticMemoryTranslatorPlugin(
ioRange = _(31 downto 28) === 0xF
),
// new StaticMemoryTranslatorPlugin(
// ioRange = _(31 downto 28) === 0xF
// ),
// new MemoryTranslatorPlugin(
// tlbSize = 32,
// virtualRange = _(31 downto 28) === 0xC,
@ -238,12 +237,12 @@ object LinuxGen {
new YamlPlugin("cpu0.yaml")
)
)
// if(withMmu) config.plugins += new MmuPlugin(
// virtualRange = a => True,
// // virtualRange = x => x(31 downto 24) =/= 0x81, //TODO It fix the DTB kernel access (workaround)
// ioRange = (x => if(litex) x(31 downto 28) === 0xB || x(31 downto 28) === 0xE || x(31 downto 28) === 0xF else x(31 downto 28) === 0xF),
// allowUserIo = true
// )
if(withMmu) config.plugins += new MmuPlugin(
virtualRange = a => True,
// virtualRange = x => x(31 downto 24) =/= 0x81, //TODO It fix the DTB kernel access (workaround)
ioRange = (x => if(litex) x(31 downto 28) === 0xB || x(31 downto 28) === 0xE || x(31 downto 28) === 0xF else x(31 downto 28) === 0xF),
allowUserIo = true
)
config
}

View File

@ -8,6 +8,8 @@ import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig}
import spinal.lib.bus.wishbone.{Wishbone, WishboneConfig}
import spinal.lib.bus.simple._
//TODO flush
case class DataCacheConfig(cacheSize : Int,
bytePerLine : Int,
wayCount : Int,
@ -71,13 +73,12 @@ object DataCacheCpuExecute{
case class DataCacheCpuExecute(p : DataCacheConfig) extends Bundle with IMasterSlave{
val isValid = Bool
val isStuck = Bool
val address = UInt(p.addressWidth bit)
// val haltIt = Bool
val args = DataCacheCpuExecuteArgs(p)
override def asMaster(): Unit = {
out(isValid, isStuck, args, address)
out(isValid, args, address)
// in(haltIt)
}
}
@ -111,6 +112,7 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste
val isStuck = Bool
val isUser = Bool
val haltIt = Bool
val isWrite = Bool
val data = Bits(p.cpuDataWidth bit)
val address = UInt(p.addressWidth bit)
val mmuException, unalignedAccess , accessError = Bool
@ -120,7 +122,7 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste
override def asMaster(): Unit = {
out(isValid,isStuck,isUser, address)
in(haltIt, data, mmuException, unalignedAccess, accessError)
in(haltIt, data, mmuException, unalignedAccess, accessError, isWrite)
outWithNull(clearAtomicEntries)
}
}
@ -354,8 +356,8 @@ class DataCache(p : DataCacheConfig) extends Component{
val data = Mem(Bits(wordWidth bit), wayWordCount)
//Reads
val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.execute.isStuck)
val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.execute.isStuck)
val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.memory.isStuck)
val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.memory.isStuck)
//Writes
when(tagsWriteCmd.valid && tagsWriteCmd.way(i)){
@ -380,7 +382,7 @@ class DataCache(p : DataCacheConfig) extends Component{
dataWriteCmd.valid := False
dataWriteCmd.payload.assignDontCare()
when(io.cpu.execute.isValid && !io.cpu.execute.isStuck){
when(io.cpu.execute.isValid && !io.cpu.memory.isStuck){
tagsReadCmd.valid := True
dataReadCmd.valid := True
tagsReadCmd.payload := io.cpu.execute.address(lineRange)
@ -460,7 +462,6 @@ class DataCache(p : DataCacheConfig) extends Component{
val atomic = genAtomic generate new Area{
case class AtomicEntry() extends Bundle{
val valid = Bool()
val size = UInt(2 bits)
val address = UInt(addressWidth bits)
def init: this.type ={
@ -470,10 +471,9 @@ class DataCache(p : DataCacheConfig) extends Component{
}
val entries = Vec(Reg(AtomicEntry()).init, atomicEntriesCount)
val entriesAllocCounter = Counter(atomicEntriesCount)
val entriesHit = entries.map(e => e.valid && e.size === request.size && e.address === io.cpu.writeBack.address).orR
val entriesHit = entries.map(e => e.valid && e.address === io.cpu.writeBack.address).orR
when(io.cpu.writeBack.isValid && request.isAtomic && !request.wr){
entries(entriesAllocCounter).valid := True
entries(entriesAllocCounter).size := request.size //TODO remove size stuff
entries(entriesAllocCounter).address := io.cpu.writeBack.address
when(!io.cpu.writeBack.isStuck){
entriesAllocCounter.increment()
@ -486,11 +486,11 @@ class DataCache(p : DataCacheConfig) extends Component{
val memCmdSent = RegInit(False) setWhen (io.mem.cmd.ready) clearWhen (!io.cpu.writeBack.isStuck)
io.cpu.redo := mmuRsp.refilling
io.cpu.redo := False
io.cpu.writeBack.accessError := False
io.cpu.writeBack.mmuException := io.cpu.writeBack.isValid && (if(catchIllegal) mmuRsp.exception || (!mmuRsp.allowWrite && request.wr) || (!mmuRsp.allowRead && !request.wr) || (!mmuRsp.allowUser && io.cpu.writeBack.isUser) else False)
io.cpu.writeBack.unalignedAccess := io.cpu.writeBack.isValid && (if(catchUnaligned) ((request.size === 2 && mmuRsp.physicalAddress(1 downto 0) =/= 0) || (request.size === 1 && mmuRsp.physicalAddress(0 downto 0) =/= 0)) else False)
io.cpu.writeBack.isWrite := request.wr
when(io.cpu.writeBack.isValid) {
when(request.forceUncachedAccess || mmuRsp.isIoAccess) {
@ -503,6 +503,11 @@ class DataCache(p : DataCacheConfig) extends Component{
io.mem.cmd.data := request.data
io.mem.cmd.length := 0
io.mem.cmd.last := True
if(genAtomic) when(request.isAtomic && !atomic.entriesHit){
io.mem.cmd.valid := False
io.cpu.writeBack.haltIt := False
}
} otherwise {
when(waysHit || request.wr) { //Do not require a cache refill ?
//Data cache update
@ -524,6 +529,12 @@ class DataCache(p : DataCacheConfig) extends Component{
//On write to read colisions
io.cpu.redo := !request.wr && (colisions & waysHits) =/= 0
if(genAtomic) when(request.isAtomic && !atomic.entriesHit){
io.mem.cmd.valid := False
dataWriteCmd.valid := False
io.cpu.writeBack.haltIt := False
}
} otherwise { //Do refill
//Emit cmd
@ -554,6 +565,7 @@ class DataCache(p : DataCacheConfig) extends Component{
loaderValid := False
io.cpu.writeBack.haltIt := False
}
io.cpu.redo setWhen(io.cpu.writeBack.isValid && mmuRsp.refilling)
assert(!(io.cpu.writeBack.isValid && !io.cpu.writeBack.haltIt && io.cpu.writeBack.isStuck), "writeBack stuck by another plugin is not allowed")
@ -561,9 +573,6 @@ class DataCache(p : DataCacheConfig) extends Component{
when(request.isAtomic && request.wr){
io.cpu.writeBack.data := (!atomic.entriesHit).asBits.resized
}
when(request.isAtomic && !atomic.entriesHit){
io.mem.cmd.mask := 0
}
}
}

View File

@ -285,6 +285,9 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception
interface
}
var exceptionPending : Bool = null
override def isExceptionPending(): Bool = exceptionPending
var jumpInterface : Flow[UInt] = null
var timerInterrupt, externalInterrupt, softwareInterrupt : Bool = null
var externalInterruptS : Bool = null
@ -378,7 +381,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception
jumpInterface.valid := False
jumpInterface.payload.assignDontCare()
exceptionPending = False
timerInterrupt = in Bool() setName("timerInterrupt")
externalInterrupt = in Bool() setName("externalInterrupt")
softwareInterrupt = in Bool() setName("softwareInterrupt")
@ -677,7 +680,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception
else
exceptionValidsRegs(stageId) := False
}
if(stage != stages.last) when(stage.arbitration.isFlushed){
when(stage.arbitration.isFlushed){
exceptionValids(stageId) := False
}
}
@ -688,6 +691,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception
//Avoid the PC register of the last stage to change durring an exception handleing (Used to fill Xepc)
stages.last.dontSample.getOrElseUpdate(PC, ArrayBuffer[Bool]()) += exceptionValids.last
exceptionPending setWhen(exceptionValidsRegs.orR)
} else null

View File

@ -7,7 +7,7 @@ import spinal.lib._
import spinal.lib.bus.amba4.axi.Axi4
class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null) extends DBusCachedPlugin(config, memoryTranslatorPortConfig){
class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null) extends DBusCachedPlugin(config, memoryTranslatorPortConfig) {
var dAxi : Axi4 = null
override def build(pipeline: VexRiscv): Unit = {
@ -20,7 +20,7 @@ class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : An
class DBusCachedPlugin(config : DataCacheConfig,
memoryTranslatorPortConfig : Any = null,
csrInfo : Boolean = false) extends Plugin[VexRiscv]{
csrInfo : Boolean = false) extends Plugin[VexRiscv] with DBusAccessService {
import config._
var dBus : DataCacheMemBus = null
var mmuBus : MemoryTranslatorBus = null
@ -28,11 +28,19 @@ class DBusCachedPlugin(config : DataCacheConfig,
var privilegeService : PrivilegeService = null
var redoBranch : Flow[UInt] = null
@dontName var dBusAccess : DBusAccess = null
override def newDBusAccess(): DBusAccess = {
assert(dBusAccess == null)
dBusAccess = DBusAccess()
dBusAccess
}
object MEMORY_ENABLE extends Stageable(Bool)
object MEMORY_MANAGMENT extends Stageable(Bool)
object MEMORY_WR extends Stageable(Bool)
object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
object MEMORY_ATOMIC extends Stageable(Bool)
object IS_DBUS_SHARING extends Stageable(Bool())
override def setup(pipeline: VexRiscv): Unit = {
import Riscv._
@ -44,8 +52,9 @@ class DBusCachedPlugin(config : DataCacheConfig,
SRC1_CTRL -> Src1CtrlEnum.RS,
SRC_USE_SUB_LESS -> False,
MEMORY_ENABLE -> True,
RS1_USE -> True
) ++ (if (catchSomething) List(IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB) else Nil) //Used for access fault bad address in memory stage
RS1_USE -> True,
IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB
)
val loadActions = stdActions ++ List(
SRC2_CTRL -> Src2CtrlEnum.IMI,
@ -143,7 +152,6 @@ class DBusCachedPlugin(config : DataCacheConfig,
val size = input(INSTRUCTION)(13 downto 12).asUInt
cache.io.cpu.execute.isValid := arbitration.isValid && input(MEMORY_ENABLE)
cache.io.cpu.execute.isStuck := arbitration.isStuck
cache.io.cpu.execute.address := input(SRC_ADD).asUInt
cache.io.cpu.execute.args.wr := input(MEMORY_WR)
cache.io.cpu.execute.args.data := size.mux(
@ -194,7 +202,7 @@ class DBusCachedPlugin(config : DataCacheConfig,
redoBranch.payload := input(PC)
arbitration.flushAll setWhen(redoBranch.valid)
when(cache.io.cpu.writeBack.isValid) {
when(arbitration.isValid && input(MEMORY_ENABLE)) {
if (catchAccessError) when(cache.io.cpu.writeBack.accessError) {
exceptionBus.valid := True
exceptionBus.code := (input(MEMORY_WR) ? U(7) | U(5)).resized
@ -235,6 +243,45 @@ class DBusCachedPlugin(config : DataCacheConfig,
}
}
//Share access to the dBus (used by self refilled MMU)
val dBusSharing = (dBusAccess != null) generate pipeline plug new Area{
dBusAccess.cmd.ready := False
val forceDatapath = False
when(dBusAccess.cmd.valid){
decode.arbitration.haltByOther := True
when(!stagesFromExecute.map(_.arbitration.isValid).orR && !pipeline.service(classOf[ExceptionService]).isExceptionPending()){
when(!cache.io.cpu.redo) {
cache.io.cpu.execute.isValid := True
dBusAccess.cmd.ready := !execute.arbitration.isStuck
}
cache.io.cpu.execute.args.wr := dBusAccess.cmd.write
cache.io.cpu.execute.args.data := dBusAccess.cmd.data
cache.io.cpu.execute.args.size := dBusAccess.cmd.size
cache.io.cpu.execute.args.forceUncachedAccess := True //TODO Cached and redo management
if(genAtomic) cache.io.cpu.execute.args.isAtomic := False
cache.io.cpu.execute.address := dBusAccess.cmd.address //Will only be 12 muxes
forceDatapath := True
}
}
execute.insert(IS_DBUS_SHARING) := dBusAccess.cmd.fire
mmuBus.cmd.bypassTranslation setWhen(memory.input(IS_DBUS_SHARING))
cache.io.cpu.memory.isValid setWhen(memory.input(IS_DBUS_SHARING))
cache.io.cpu.writeBack.isValid setWhen(writeBack.input(IS_DBUS_SHARING))
dBusAccess.rsp.valid := writeBack.input(IS_DBUS_SHARING) && !cache.io.cpu.writeBack.isWrite && !cache.io.cpu.writeBack.haltIt
dBusAccess.rsp.data := cache.io.cpu.writeBack.data
dBusAccess.rsp.error := cache.io.cpu.writeBack.unalignedAccess || cache.io.cpu.writeBack.accessError
component.addPrePopTask{() =>
when(forceDatapath){
execute.output(REGFILE_WRITE_DATA) := dBusAccess.cmd.address.asBits
}
memory.input(IS_DBUS_SHARING) init(False)
writeBack.input(IS_DBUS_SHARING) init(False)
}
}
if(csrInfo){
val csr = service(classOf[CsrPlugin])
csr.r(0xCC0, 0 -> U(cacheSize/wayCount), 20 -> U(bytePerLine))

View File

@ -21,6 +21,8 @@ class HaltOnExceptionPlugin() extends Plugin[VexRiscv] with ExceptionService {
exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority)
interface
}
override def isExceptionPending(): Bool = False
override def build(pipeline: VexRiscv): Unit = {
import pipeline._

View File

@ -1316,6 +1316,16 @@ public:
if(isDBusCheckedRegion(addr)){
CpuRef::MemWrite w;
w.address = addr;
while((mask & 1) == 0){
mask >>= 1;
w.address++;
w.data >>= 8;
}
switch(mask){
case 1: size = 0; break;
case 3: size = min(1u, size); break;
case 15: size = min(2u, size); break;
}
w.size = 1 << size;
switch(size){
case 0: w.data = *data & 0xFF; break;
@ -3446,6 +3456,7 @@ int main(int argc, char **argv, char **env) {
// redo(REDO,WorkspaceRegression("deleg").withRiscvRef()->loadHex("../raw/deleg/build/deleg.hex")->bootAt(0x80000000u)->run(50e3););
// return 0;
redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3););
for(int idx = 0;idx < 1;idx++){