diff options
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala')
-rw-r--r-- | VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala new file mode 100644 index 0000000..372cfcc --- /dev/null +++ b/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -0,0 +1,614 @@ +package vexriscv.plugin + +import vexriscv._ +import spinal.core._ +import spinal.lib._ +import spinal.lib.bus.amba3.ahblite.{AhbLite3Config, AhbLite3Master} +import spinal.lib.bus.amba4.axi._ +import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig} +import spinal.lib.bus.bmb.{Bmb, BmbParameter} +import spinal.lib.bus.wishbone.{Wishbone, WishboneConfig} +import spinal.lib.bus.simple._ +import vexriscv.ip.DataCacheMemCmd + +import scala.collection.mutable.ArrayBuffer + + +case class DBusSimpleCmd() extends Bundle{ + val wr = Bool + val address = UInt(32 bits) + val data = Bits(32 bit) + val size = UInt(2 bit) +} + +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) + } +} + + +object DBusSimpleBus{ + def getAxi4Config() = Axi4Config( + addressWidth = 32, + dataWidth = 32, + useId = false, + useRegion = false, + useBurst = false, + useLock = false, + useQos = false, + useLen = false, + useResp = true + ) + + def getAvalonConfig() = AvalonMMConfig.pipelined( + addressWidth = 32, + dataWidth = 32).copy( + useByteEnable = true, + useResponse = true, + maximumPendingReadTransactions = 1 + ) + + def getWishboneConfig() = WishboneConfig( + addressWidth = 30, + dataWidth = 32, + selWidth = 4, + useSTALL = false, + useLOCK = false, + useERR = true, + useRTY = false, + tgaWidth = 0, + tgcWidth = 0, + tgdWidth = 0, + useBTE = true, + useCTI = true + ) + + def getPipelinedMemoryBusConfig() = PipelinedMemoryBusConfig( + addressWidth = 32, + dataWidth = 32 + ) + + def getAhbLite3Config() = AhbLite3Config( + addressWidth = 32, + dataWidth = 32 + ) + def getBmbParameter() = BmbParameter( + addressWidth = 32, + dataWidth = 32, + lengthWidth = 2, + sourceWidth = 0, + contextWidth = 1, + alignment = BmbParameter.BurstAlignement.LENGTH + ) +} + +case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMasterSlave{ + val cmd = Stream(DBusSimpleCmd()) + val rsp = DBusSimpleRsp() + + override def asMaster(): Unit = { + master(cmd) + slave(rsp) + } + + def cmdS2mPipe() : DBusSimpleBus = { + val s = DBusSimpleBus(bigEndian) + s.cmd << this.cmd.s2mPipe() + this.rsp := s.rsp + s + } + + def genMask(cmd : DBusSimpleCmd) = { + if(bigEndian) + cmd.size.mux( + U(0) -> B"1000", + U(1) -> B"1100", + default -> B"1111" + ) |>> cmd.address(1 downto 0) + else + cmd.size.mux( + U(0) -> B"0001", + U(1) -> B"0011", + default -> B"1111" + ) |<< cmd.address(1 downto 0) + } + + def toAxi4Shared(stageCmd : Boolean = false, pendingWritesMax : Int = 7): Axi4Shared = { + val axi = Axi4Shared(DBusSimpleBus.getAxi4Config()) + + val cmdPreFork = if (stageCmd) cmd.stage.stage().s2mPipe() else cmd + + val pendingWrites = CounterUpDown( + stateCount = pendingWritesMax + 1, + incWhen = cmdPreFork.fire && cmdPreFork.wr, + decWhen = axi.writeRsp.fire + ) + + val hazard = (pendingWrites =/= 0 && cmdPreFork.valid && !cmdPreFork.wr) || pendingWrites === pendingWritesMax + val (cmdFork, dataFork) = StreamFork2(cmdPreFork.haltWhen(hazard)) + axi.sharedCmd.arbitrationFrom(cmdFork) + axi.sharedCmd.write := cmdFork.wr + axi.sharedCmd.prot := "010" + axi.sharedCmd.cache := "1111" + axi.sharedCmd.size := cmdFork.size.resized + axi.sharedCmd.addr := cmdFork.address + + val dataStage = dataFork.throwWhen(!dataFork.wr) + axi.writeData.arbitrationFrom(dataStage) + axi.writeData.last := True + axi.writeData.data := dataStage.data + axi.writeData.strb := genMask(dataStage).resized + + + rsp.ready := axi.r.valid + rsp.error := !axi.r.isOKAY() + rsp.data := axi.r.data + + axi.r.ready := True + axi.b.ready := True + axi + } + + def toAxi4(stageCmd : Boolean = true) = this.toAxi4Shared(stageCmd).toAxi4() + + + + def toAvalon(stageCmd : Boolean = true): AvalonMM = { + val avalonConfig = DBusSimpleBus.getAvalonConfig() + val mm = AvalonMM(avalonConfig) + val cmdStage = if(stageCmd) cmd.stage else cmd + mm.read := cmdStage.valid && !cmdStage.wr + mm.write := cmdStage.valid && cmdStage.wr + mm.address := (cmdStage.address >> 2) @@ U"00" + mm.writeData := cmdStage.data(31 downto 0) + mm.byteEnable := genMask(cmdStage).resized + + + cmdStage.ready := mm.waitRequestn + rsp.ready :=mm.readDataValid + rsp.error := mm.response =/= AvalonMM.Response.OKAY + rsp.data := mm.readData + + mm + } + + def toWishbone(): Wishbone = { + val wishboneConfig = DBusSimpleBus.getWishboneConfig() + val bus = Wishbone(wishboneConfig) + val cmdStage = cmd.halfPipe() + + bus.ADR := cmdStage.address >> 2 + bus.CTI :=B"000" + bus.BTE := "00" + bus.SEL := genMask(cmdStage).resized + when(!cmdStage.wr) { + bus.SEL := "1111" + } + bus.WE := cmdStage.wr + bus.DAT_MOSI := cmdStage.data + + cmdStage.ready := cmdStage.valid && bus.ACK + bus.CYC := cmdStage.valid + bus.STB := cmdStage.valid + + rsp.ready := cmdStage.valid && !bus.WE && bus.ACK + rsp.data := bus.DAT_MISO + rsp.error := False //TODO + bus + } + + def toPipelinedMemoryBus() : PipelinedMemoryBus = { + val pipelinedMemoryBusConfig = DBusSimpleBus.getPipelinedMemoryBusConfig() + val bus = PipelinedMemoryBus(pipelinedMemoryBusConfig) + bus.cmd.valid := cmd.valid + bus.cmd.write := cmd.wr + bus.cmd.address := cmd.address.resized + bus.cmd.data := cmd.data + bus.cmd.mask := genMask(cmd) + cmd.ready := bus.cmd.ready + + rsp.ready := bus.rsp.valid + rsp.data := bus.rsp.data + + bus + } + + def toAhbLite3Master(avoidWriteToReadHazard : Boolean): AhbLite3Master = { + val bus = AhbLite3Master(DBusSimpleBus.getAhbLite3Config()) + bus.HADDR := this.cmd.address + bus.HWRITE := this.cmd.wr + bus.HSIZE := B(this.cmd.size, 3 bits) + bus.HBURST := 0 + bus.HPROT := "1111" + bus.HTRANS := this.cmd.valid ## B"0" + bus.HMASTLOCK := False + bus.HWDATA := RegNextWhen(this.cmd.data, bus.HREADY) + this.cmd.ready := bus.HREADY + + val pending = RegInit(False) clearWhen(bus.HREADY) setWhen(this.cmd.fire && !this.cmd.wr) + this.rsp.ready := bus.HREADY && pending + this.rsp.data := bus.HRDATA + this.rsp.error := bus.HRESP + + if(avoidWriteToReadHazard) { + val writeDataPhase = RegNextWhen(bus.HTRANS === 2 && bus.HWRITE, bus.HREADY) init (False) + val potentialHazard = this.cmd.valid && !this.cmd.wr && writeDataPhase + when(potentialHazard) { + bus.HTRANS := 0 + this.cmd.ready := False + } + } + bus + } + + def toBmb() : Bmb = { + val pipelinedMemoryBusConfig = DBusSimpleBus.getBmbParameter() + val bus = Bmb(pipelinedMemoryBusConfig) + + bus.cmd.valid := cmd.valid + bus.cmd.last := True + bus.cmd.context(0) := cmd.wr + bus.cmd.opcode := (cmd.wr ? B(Bmb.Cmd.Opcode.WRITE) | B(Bmb.Cmd.Opcode.READ)) + bus.cmd.address := cmd.address.resized + bus.cmd.data := cmd.data + bus.cmd.length := cmd.size.mux( + 0 -> U"00", + 1 -> U"01", + default -> U"11" + ) + bus.cmd.mask := genMask(cmd) + + cmd.ready := bus.cmd.ready + + rsp.ready := bus.rsp.valid && !bus.rsp.context(0) + rsp.data := bus.rsp.data + rsp.error := bus.rsp.isError + bus.rsp.ready := True + + bus + } +} + + +class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, + catchAccessFault : Boolean = false, + earlyInjection : Boolean = false, /*, idempotentRegions : (UInt) => Bool = (x) => False*/ + emitCmdInMemoryStage : Boolean = false, + onlyLoadWords : Boolean = false, + withLrSc : Boolean = false, + val bigEndian : Boolean = false, + memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] with DBusAccessService { + + var dBus : DBusSimpleBus = null + assert(!(emitCmdInMemoryStage && earlyInjection)) + object MEMORY_ENABLE extends Stageable(Bool) + object MEMORY_READ_DATA extends Stageable(Bits(32 bits)) + object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits)) + object ALIGNEMENT_FAULT extends Stageable(Bool) + object MMU_FAULT extends Stageable(Bool) + object MEMORY_ATOMIC extends Stageable(Bool) + object ATOMIC_HIT extends Stageable(Bool) + object MEMORY_STORE extends Stageable(Bool) + + var memoryExceptionPort : Flow[ExceptionCause] = null + var rspStage : Stage = null + var mmuBus : MemoryTranslatorBus = null + var redoBranch : Flow[UInt] = null + val catchSomething = catchAccessFault || catchAddressMisaligned || memoryTranslatorPortConfig != null + + @dontName var dBusAccess : DBusAccess = null + override def newDBusAccess(): DBusAccess = { + assert(dBusAccess == null) + dBusAccess = DBusAccess() + dBusAccess + } + + override def setup(pipeline: VexRiscv): Unit = { + import Riscv._ + import pipeline.config._ + import pipeline._ + + val decoderService = pipeline.service(classOf[DecoderService]) + + val stdActions = List[(Stageable[_ <: BaseType],Any)]( + SRC1_CTRL -> Src1CtrlEnum.RS, + SRC_USE_SUB_LESS -> False, + MEMORY_ENABLE -> True, + RS1_USE -> True + ) ++ (if(catchAccessFault || catchAddressMisaligned) List(IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB) else Nil) //Used for access fault bad address in memory stage + + val loadActions = stdActions ++ List( + SRC2_CTRL -> Src2CtrlEnum.IMI, + REGFILE_WRITE_VALID -> True, + BYPASSABLE_EXECUTE_STAGE -> False, + BYPASSABLE_MEMORY_STAGE -> Bool(earlyInjection), + MEMORY_STORE -> False, + HAS_SIDE_EFFECT -> True + ) + + val storeActions = stdActions ++ List( + SRC2_CTRL -> Src2CtrlEnum.IMS, + RS2_USE -> True, + MEMORY_STORE -> True, + HAS_SIDE_EFFECT -> True + ) + + decoderService.addDefault(MEMORY_ENABLE, False) + decoderService.add( + (if(onlyLoadWords) List(LW) else List(LB, LH, LW, LBU, LHU, LWU)).map(_ -> loadActions) ++ + List(SB, SH, SW).map(_ -> storeActions) + ) + + + if(withLrSc){ + List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e => + decoderService.add(e, Seq(MEMORY_ATOMIC -> False)) + ) + decoderService.add( + key = LR, + values = loadActions.filter(_._1 != SRC2_CTRL) ++ Seq( + SRC_ADD_ZERO -> True, + MEMORY_ATOMIC -> True + ) + ) + + decoderService.add( + key = SC, + values = storeActions.filter(_._1 != SRC2_CTRL) ++ Seq( + SRC_ADD_ZERO -> True, + REGFILE_WRITE_VALID -> True, + BYPASSABLE_EXECUTE_STAGE -> False, + BYPASSABLE_MEMORY_STAGE -> False, + MEMORY_ATOMIC -> True + ) + ) + } + + decoderService.add(FENCE, Nil) + + rspStage = if(stages.last == execute) execute else (if(emitCmdInMemoryStage) writeBack else memory) + if(catchSomething) { + val exceptionService = pipeline.service(classOf[ExceptionService]) + memoryExceptionPort = exceptionService.newExceptionPort(rspStage) + } + + if(memoryTranslatorPortConfig != null) { + mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_DATA, memoryTranslatorPortConfig) + redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(if(pipeline.memory != null) pipeline.memory else pipeline.execute) + } + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline._ + import pipeline.config._ + + object MMU_RSP extends Stageable(MemoryTranslatorRsp(mmuBus.p)) + + dBus = master(DBusSimpleBus(bigEndian)).setName("dBus") + + + decode plug new Area { + import decode._ + + if(mmuBus != null) when(mmuBus.busy && arbitration.isValid && input(MEMORY_ENABLE)) { + arbitration.haltItself := True + } + } + + //Emit dBus.cmd request + val cmdSent = if(rspStage == execute) RegInit(False) setWhen(dBus.cmd.fire) clearWhen(!execute.arbitration.isStuck) else False + val cmdStage = if(emitCmdInMemoryStage) memory else execute + cmdStage plug new Area{ + import cmdStage._ + val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault()) + + + if (catchAddressMisaligned) + insert(ALIGNEMENT_FAULT) := (dBus.cmd.size === 2 && dBus.cmd.address(1 downto 0) =/= 0) || (dBus.cmd.size === 1 && dBus.cmd.address(0 downto 0) =/= 0) + else + insert(ALIGNEMENT_FAULT) := False + + + val skipCmd = False + skipCmd setWhen(input(ALIGNEMENT_FAULT)) + + dBus.cmd.valid := arbitration.isValid && input(MEMORY_ENABLE) && !arbitration.isStuckByOthers && !arbitration.isFlushed && !skipCmd && !cmdSent + dBus.cmd.wr := input(MEMORY_STORE) + dBus.cmd.size := input(INSTRUCTION)(13 downto 12).asUInt + dBus.cmd.payload.data := dBus.cmd.size.mux ( + U(0) -> input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0), + U(1) -> input(RS2)(15 downto 0) ## input(RS2)(15 downto 0), + default -> input(RS2)(31 downto 0) + ) + when(arbitration.isValid && input(MEMORY_ENABLE) && !dBus.cmd.ready && !skipCmd && !cmdSent){ + arbitration.haltItself := True + } + + insert(MEMORY_ADDRESS_LOW) := dBus.cmd.address(1 downto 0) + + //formal + val formalMask = dBus.genMask(dBus.cmd) + + insert(FORMAL_MEM_ADDR) := dBus.cmd.address & U"xFFFFFFFC" + insert(FORMAL_MEM_WMASK) := (dBus.cmd.valid && dBus.cmd.wr) ? formalMask | B"0000" + insert(FORMAL_MEM_RMASK) := (dBus.cmd.valid && !dBus.cmd.wr) ? formalMask | B"0000" + insert(FORMAL_MEM_WDATA) := dBus.cmd.payload.data + + val mmu = (mmuBus != null) generate new Area { + mmuBus.cmd.last.isValid := arbitration.isValid && input(MEMORY_ENABLE) + mmuBus.cmd.last.isStuck := arbitration.isStuck + mmuBus.cmd.last.virtualAddress := input(SRC_ADD).asUInt + mmuBus.cmd.last.bypassTranslation := False + mmuBus.end := !arbitration.isStuck || arbitration.isRemoved + dBus.cmd.address := mmuBus.rsp.physicalAddress + + //do not emit memory request if MMU refilling + insert(MMU_FAULT) := input(MMU_RSP).exception || (!input(MMU_RSP).allowWrite && input(MEMORY_STORE)) || (!input(MMU_RSP).allowRead && !input(MEMORY_STORE)) + skipCmd.setWhen(input(MMU_FAULT) || input(MMU_RSP).refilling) + + insert(MMU_RSP) := mmuBus.rsp + } + + val mmuLess = (mmuBus == null) generate new Area{ + dBus.cmd.address := input(SRC_ADD).asUInt + } + + + val atomic = withLrSc generate new Area{ + val reserved = RegInit(False) + insert(ATOMIC_HIT) := reserved + when(arbitration.isFiring && input(MEMORY_ENABLE) && (if(mmuBus != null) !input(MMU_FAULT) else True) && !skipCmd){ + reserved setWhen(input(MEMORY_ATOMIC)) + reserved clearWhen(input(MEMORY_STORE)) + } + when(input(MEMORY_STORE) && input(MEMORY_ATOMIC) && !input(ATOMIC_HIT)){ + skipCmd := True + } + } + } + + //Collect dBus.rsp read responses + rspStage plug new Area { + val s = rspStage; import s._ + + + insert(MEMORY_READ_DATA) := dBus.rsp.data + + arbitration.haltItself setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !input(MEMORY_STORE) && (!dBus.rsp.ready || (if(rspStage == execute) !cmdSent else False))) + + if(catchSomething) { + memoryExceptionPort.valid := False + memoryExceptionPort.code.assignDontCare() + memoryExceptionPort.badAddr := input(REGFILE_WRITE_DATA).asUInt + + if(catchAccessFault) when(dBus.rsp.ready && dBus.rsp.error && !input(MEMORY_STORE)) { + memoryExceptionPort.valid := True + memoryExceptionPort.code := 5 + } + + if(catchAddressMisaligned) when(input(ALIGNEMENT_FAULT)){ + memoryExceptionPort.code := (input(MEMORY_STORE) ? U(6) | U(4)).resized + memoryExceptionPort.valid := True + } + + if(memoryTranslatorPortConfig != null) { + redoBranch.valid := False + redoBranch.payload := input(PC) + + when(input(MMU_RSP).refilling){ + redoBranch.valid := True + memoryExceptionPort.valid := False + } elsewhen(input(MMU_FAULT)) { + memoryExceptionPort.valid := True + memoryExceptionPort.code := (input(MEMORY_STORE) ? U(15) | U(13)).resized + } + + arbitration.flushIt setWhen(redoBranch.valid) + arbitration.flushNext setWhen(redoBranch.valid) + } + + when(!(arbitration.isValid && input(MEMORY_ENABLE) && (Bool(cmdStage != rspStage) || !arbitration.isStuckByOthers))){ + if(catchSomething) memoryExceptionPort.valid := False + if(memoryTranslatorPortConfig != null) redoBranch.valid := False + } + + } + } + + //Reformat read responses, REGFILE_WRITE_DATA overriding + val injectionStage = if(earlyInjection) memory else stages.last + injectionStage plug new Area { + import injectionStage._ + + + val rspShifted = MEMORY_READ_DATA() + rspShifted := input(MEMORY_READ_DATA) + if(bigEndian) + switch(input(MEMORY_ADDRESS_LOW)){ + is(1){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(23 downto 16)} + is(2){rspShifted(31 downto 16) := input(MEMORY_READ_DATA)(15 downto 0)} + is(3){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(7 downto 0)} + } + else + switch(input(MEMORY_ADDRESS_LOW)){ + is(1){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(15 downto 8)} + is(2){rspShifted(15 downto 0) := input(MEMORY_READ_DATA)(31 downto 16)} + is(3){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(31 downto 24)} + } + + val rspFormated = + if(bigEndian) + input(INSTRUCTION)(13 downto 12).mux( + 0 -> B((31 downto 8) -> (rspShifted(31) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(31 downto 24)), + 1 -> B((31 downto 16) -> (rspShifted(31) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(31 downto 16)), + default -> rspShifted //W + ) + else + input(INSTRUCTION)(13 downto 12).mux( + 0 -> B((31 downto 8) -> (rspShifted(7) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(7 downto 0)), + 1 -> B((31 downto 16) -> (rspShifted(15) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(15 downto 0)), + default -> rspShifted //W + ) + + when(arbitration.isValid && input(MEMORY_ENABLE)) { + output(REGFILE_WRITE_DATA) := (if(!onlyLoadWords) rspFormated else input(MEMORY_READ_DATA)) + if(withLrSc){ + when(input(MEMORY_ATOMIC) && input(MEMORY_STORE)){ + output(REGFILE_WRITE_DATA) := (!input(ATOMIC_HIT)).asBits.resized + } + } + } + +// if(!earlyInjection && !emitCmdInMemoryStage && config.withWriteBackStage) +// assert(!(arbitration.isValid && input(MEMORY_ENABLE) && !input(MEMORY_STORE) && arbitration.isStuck),"DBusSimplePlugin doesn't allow writeback stage stall when read happend") + + //formal + insert(FORMAL_MEM_RDATA) := input(MEMORY_READ_DATA) + } + + //Share access to the dBus (used by self refilled MMU) + val dBusSharing = (dBusAccess != null) generate new Area{ + val state = Reg(UInt(2 bits)) init(0) + dBusAccess.cmd.ready := False + dBusAccess.rsp.valid := False + dBusAccess.rsp.data := dBus.rsp.data + dBusAccess.rsp.error := dBus.rsp.error + dBusAccess.rsp.redo := False + + switch(state){ + is(0){ + when(dBusAccess.cmd.valid){ + decode.arbitration.haltItself := True + when(!stages.dropWhile(_ != execute).map(_.arbitration.isValid).orR){ + state := 1 + } + } + } + is(1){ + decode.arbitration.haltItself := True + dBus.cmd.valid := True + dBus.cmd.address := dBusAccess.cmd.address + dBus.cmd.wr := dBusAccess.cmd.write + dBus.cmd.data := dBusAccess.cmd.data + dBus.cmd.size := dBusAccess.cmd.size + when(dBus.cmd.ready){ + state := (dBusAccess.cmd.write ? U(0) | U(2)) + dBusAccess.cmd.ready := True + } + } + is(2){ + decode.arbitration.haltItself := True + when(dBus.rsp.ready){ + dBusAccess.rsp.valid := True + state := 0 + } + } + } + } + } +} |