diff --git a/src/main/scala/lltriscv/core/Core.scala b/src/main/scala/lltriscv/core/Core.scala index 2ba0a05..cd05e91 100644 --- a/src/main/scala/lltriscv/core/Core.scala +++ b/src/main/scala/lltriscv/core/Core.scala @@ -37,6 +37,7 @@ import decode.Decode import lltriscv.cache.Parallel2Flusher import lltriscv.core.execute.OutOfOrderedExecuteQueue import lltriscv.core.fetch.BranchPredictorUpdateIO +import lltriscv.core.execute.UpdateLoadReservationIO /* * LLT RISC-V Core Exquisite integration @@ -124,6 +125,7 @@ class LLTRISCVCoreExq(config: CoreConfig) extends Module { coreBackend.io.iCacheFlush.empty := true.B coreBackend.io.tlbFlush <> tlbFlusher.io.in coreBackend.io.update <> coreFrontend.io.update + coreBackend.io.updateLoadReservation <> coreExecute.io.updateLoadReservation coreBackend.io.predictorUpdate <> coreFrontend.io.predictorUpdate coreBackend.io.store <> coreExecute.io.retire @@ -233,6 +235,7 @@ class CoreExecute(config: CoreConfig) extends Module { val dTLBFlush = Flipped(new FlushCacheIO()) val dCacheFlush = Flipped(new FlushCacheIO()) + val updateLoadReservation = Flipped(new UpdateLoadReservationIO()) val retire = Flipped(new StoreQueueRetireIO()) @@ -313,6 +316,7 @@ class CoreExecute(config: CoreConfig) extends Module { memory.io.dtlb <> dtlb.io.request memory.io.sma <> smaWithStoreQueueInterconnect.io.in memory.io.alloc <> storeQueue.io.alloc + memory.io.updateLoadReservation <> io.updateLoadReservation memory.io.recover := io.recover memoryExecuteQueue.io.broadcast <> io.broadcast memoryExecuteQueue.io.recover := io.recover @@ -355,6 +359,8 @@ class CoreBackend(config: CoreConfig) extends Module { val dCacheFlush = new FlushCacheIO() val iCacheFlush = new FlushCacheIO() val tlbFlush = new FlushCacheIO() + + val updateLoadReservation = new UpdateLoadReservationIO() }) private val broadcaster = Module(new RoundRobinBroadcaster(config.executeQueueWidth)) private val instructionRetire = Module(new InstructionRetire(config.robDepth)) @@ -370,6 +376,7 @@ class CoreBackend(config: CoreConfig) extends Module { instructionRetire.io.retired <> rob.io.retired instructionRetire.io.tableRetire <> rob.io.tableRetire instructionRetire.io.update <> io.update + instructionRetire.io.updateLoadReservation <> io.updateLoadReservation instructionRetire.io.predictorUpdate <> io.predictorUpdate instructionRetire.io.store <> io.store io.recover := instructionRetire.io.recover diff --git a/src/main/scala/lltriscv/core/execute/ALU.scala b/src/main/scala/lltriscv/core/execute/ALU.scala index 0dc6020..2c1305d 100644 --- a/src/main/scala/lltriscv/core/execute/ALU.scala +++ b/src/main/scala/lltriscv/core/execute/ALU.scala @@ -344,16 +344,16 @@ class ALUExecuteStage extends Module { io.out.bits.result := (inReg.op1 * inReg.op2)(63, 32) } is(ALUOperationType.div) { - io.out.bits.result := (inReg.op1.asSInt / inReg.op2.asSInt).asUInt + io.out.bits.result := Mux(inReg.op2 === 0.U, "hffffffff".U, (inReg.op1.asSInt / inReg.op2.asSInt).asUInt) } is(ALUOperationType.divu) { - io.out.bits.result := inReg.op1 / inReg.op2 + io.out.bits.result := Mux(inReg.op2 === 0.U, "hffffffff".U, inReg.op1 / inReg.op2) } is(ALUOperationType.rem) { - io.out.bits.result := (inReg.op1.asSInt % inReg.op2.asSInt).asUInt + io.out.bits.result := Mux(inReg.op2 === 0.U, inReg.op1, (inReg.op1.asSInt % inReg.op2.asSInt).asUInt) } is(ALUOperationType.remu) { - io.out.bits.result := inReg.op1 % inReg.op2 + io.out.bits.result := Mux(inReg.op2 === 0.U, inReg.op1, inReg.op1 % inReg.op2) } is(ALUOperationType.csrrw) { io.out.bits.result := inReg.op1 diff --git a/src/main/scala/lltriscv/core/execute/ExecuteEntry.scala b/src/main/scala/lltriscv/core/execute/ExecuteEntry.scala index 361b24f..6d2fc3e 100644 --- a/src/main/scala/lltriscv/core/execute/ExecuteEntry.scala +++ b/src/main/scala/lltriscv/core/execute/ExecuteEntry.scala @@ -88,6 +88,11 @@ class ExecuteResultEntry extends Bundle { // Predictor field val branch = Bool() + // LRSC field + val lr = Bool() + val lrAddress = DataType.address + val sc = Bool() + val rd = DataType.receipt // Destination receipt val pc = DataType.address // Corresponding PC val next = DataType.address // Next PC @@ -117,6 +122,11 @@ class ExecuteResultEntry extends Bundle { csrData := wdata } + def resultLR(addr: UInt) = { + lr := true.B + lrAddress := addr + } + def noMemory() = { write := false.B writeID := 0.U @@ -133,12 +143,19 @@ class ExecuteResultEntry extends Bundle { flushTLB := false.B } + def noLRSC() = { + lr := false.B + sc := false.B + lrAddress := 0.U + } + def noResult() = { result := 0.U noException() noCSR() noMemory() noFlush() + noLRSC() xret := false.B rd := 0.U pc := 0.U diff --git a/src/main/scala/lltriscv/core/execute/Memory.scala b/src/main/scala/lltriscv/core/execute/Memory.scala index 2caa04c..85bdf87 100644 --- a/src/main/scala/lltriscv/core/execute/Memory.scala +++ b/src/main/scala/lltriscv/core/execute/Memory.scala @@ -35,6 +35,7 @@ class Memory extends Module { val sma = new SMAReaderIO() // Store queue interface val alloc = new StoreQueueAllocIO() + val updateLoadReservation = Flipped(new UpdateLoadReservationIO()) // Recovery logic val recover = Input(Bool()) }) @@ -53,6 +54,7 @@ class Memory extends Module { memoryTLBStage.io.dtlb <> io.dtlb memoryReadWriteStage.io.sma <> io.sma memoryReadWriteStage.io.alloc <> io.alloc + memoryReadWriteStage.io.updateLoadReservation <> io.updateLoadReservation memoryDecodeStage.io.recover := io.recover memoryExecuteStage.io.recover := io.recover @@ -109,8 +111,8 @@ class MemoryDecodeStage extends Module { } is(InstructionType.R) { switch(inReg.func7(6, 2)) { - is("b00010".U) { io.out.bits.op := MemoryOperationType.lw } - is("b00011".U) { io.out.bits.op := MemoryOperationType.sw } + is("b00010".U) { io.out.bits.op := MemoryOperationType.lr } + is("b00011".U) { io.out.bits.op := MemoryOperationType.sc } is("b00001".U) { io.out.bits.op := MemoryOperationType.amoswap } is("b00000".U) { io.out.bits.op := MemoryOperationType.amoadd } is("b00100".U) { io.out.bits.op := MemoryOperationType.amoxor } @@ -289,10 +291,14 @@ class MemoryReadWriteStage extends Module { val sma = new SMAReaderIO() // Store queue interface val alloc = new StoreQueueAllocIO() + val updateLoadReservation = Flipped(new UpdateLoadReservationIO()) // Recovery interface val recover = Input(Bool()) }) + private val loadReservation = RegInit(new LoadReservationEntry().zero) + private val recoveryLoadReservation = RegInit(new LoadReservationEntry().zero) + private val statusReg = RegInit(Status.idle) private object Status extends ChiselEnum { val idle, read, write = Value @@ -303,10 +309,18 @@ class MemoryReadWriteStage extends Module { private val readResult = RegInit(DataType.operation.zeroAsUInt) private val readError = RegInit(false.B) private val allocID = RegInit(DataType.receipt.zeroAsUInt) + private val scSuccess = inReg.vaddress === loadReservation.address && loadReservation.valid + io.in.ready := statusReg === Status.idle && io.out.ready // Idle when(io.out.fire) { // Stall inReg.valid := false.B + + // SC + when(inReg.op === MemoryOperationType.sc) { + // Clear load reservation + loadReservation.valid := false.B + } } when(io.in.fire) { // Sample @@ -315,7 +329,9 @@ class MemoryReadWriteStage extends Module { when(io.in.bits.op in MemoryOperationType.readValues) { statusReg := Status.read }.elsewhen(io.in.bits.op in MemoryOperationType.writeValues) { - statusReg := Status.write + when(io.in.bits.op =/= MemoryOperationType.sc || scSuccess) { // SC + statusReg := Status.write + } } } } @@ -342,6 +358,11 @@ class MemoryReadWriteStage extends Module { readResult := io.sma.data readError := io.sma.error + when(inReg.op === MemoryOperationType.lr && !readError) { // lr + loadReservation.address := inReg.vaddress + loadReservation.valid := true.B + } + when((inReg.op in MemoryOperationType.amoValues) && !readError) { // AMO statusReg := Status.write }.otherwise { @@ -401,6 +422,10 @@ class MemoryReadWriteStage extends Module { // Result io.out.bits.noResult() + when(inReg.op === MemoryOperationType.lr) { + io.out.bits.resultLR(inReg.vaddress) + } + when(inReg.op === MemoryOperationType.lb) { io.out.bits.result := CoreUtils.signExtended(readResult, 7) }.elsewhen(inReg.op === MemoryOperationType.lbu) { @@ -409,8 +434,11 @@ class MemoryReadWriteStage extends Module { io.out.bits.result := CoreUtils.signExtended(readResult, 15) }.elsewhen(inReg.op === MemoryOperationType.lhu) { io.out.bits.result := readResult(15, 0) - }.elsewhen(inReg.op === MemoryOperationType.lw || (inReg.op in MemoryOperationType.amoValues)) { + }.elsewhen(inReg.op in (MemoryOperationType.lw :: MemoryOperationType.lr :: MemoryOperationType.amoValues)) { io.out.bits.result := readResult + }.elsewhen(inReg.op === MemoryOperationType.sc) { + io.out.bits.result := Mux(scSuccess, 0.U, 1.U) + io.out.bits.sc := true.B } when(inReg.op in MemoryOperationType.writeValues) { @@ -458,10 +486,22 @@ class MemoryReadWriteStage extends Module { io.out.valid := statusReg === Status.idle + // Update load reservation + when(io.updateLoadReservation.valid) { + when(io.updateLoadReservation.load) { // LR + recoveryLoadReservation.address := io.updateLoadReservation.address + recoveryLoadReservation.valid := true.B + }.otherwise { // SC + recoveryLoadReservation.valid := false.B + } + } + // Recovery logic when(io.recover) { inReg.valid := false.B // Undo write FSM to prevent writing to store queue when(statusReg === Status.write) { statusReg := Status.idle } + + loadReservation := recoveryLoadReservation } } diff --git a/src/main/scala/lltriscv/core/execute/MemoryEntry.scala b/src/main/scala/lltriscv/core/execute/MemoryEntry.scala index 85a6b13..0bdde3d 100644 --- a/src/main/scala/lltriscv/core/execute/MemoryEntry.scala +++ b/src/main/scala/lltriscv/core/execute/MemoryEntry.scala @@ -33,18 +33,22 @@ object MemoryOperationType extends ChiselEnum { * - amoand: read and and * - amomax(u): read and max(unsigned) * - amomin(u): read and min(unsigned) + * + * LR/SC operations: + * - lr: load reserved + * - sc: store conditional */ - val none, undefined, lb, lh, lw, lbu, lhu, sb, sh, sw, amoswap, amoadd, amoxor, amoand, amoor, amomin, amomax, amominu, amomaxu = Value + val none, undefined, lb, lh, lw, lbu, lhu, sb, sh, sw, amoswap, amoadd, amoxor, amoand, amoor, amomin, amomax, amominu, amomaxu, lr, sc = Value // By type val amoValues = List(amoswap, amoadd, amoxor, amoand, amoor, amomin, amomax, amominu, amomaxu) - val readValues = List(lb, lh, lw, lbu, lhu) ::: amoValues - val writeValues = List(sb, sh, sw) ::: amoValues + val readValues = List(lb, lh, lw, lbu, lhu, lr) ::: amoValues + val writeValues = List(sb, sh, sw, sc) ::: amoValues // By width val byteValues = List(lb, lbu, sb) val halfValues = List(lh, lhu, sh) - val wordValues = List(lw, sw) ::: amoValues + val wordValues = List(lw, sw, lr, sc) ::: amoValues } /** Memory access length @@ -64,6 +68,17 @@ object MemoryErrorCode extends ChiselEnum { val none, misaligned, pageFault, memoryFault = Value } +class LoadReservationEntry extends Bundle { + val address = DataType.address + val valid = Bool() +} + +class UpdateLoadReservationIO extends Bundle { + val load = Output(Bool()) + val address = Output(DataType.address) + val valid = Output(Bool()) +} + /** Memory execute stage entry * * The input entry of MemoryExecuteStage diff --git a/src/main/scala/lltriscv/core/retire/InstructionRetire.scala b/src/main/scala/lltriscv/core/retire/InstructionRetire.scala index 8db096e..c9f4c97 100644 --- a/src/main/scala/lltriscv/core/retire/InstructionRetire.scala +++ b/src/main/scala/lltriscv/core/retire/InstructionRetire.scala @@ -10,6 +10,7 @@ import lltriscv.core.record.ExceptionRequestIO import lltriscv.core.record.StoreQueueRetireIO import lltriscv.cache.FlushCacheIO import lltriscv.core.fetch.BranchPredictorUpdateIO +import lltriscv.core.execute.UpdateLoadReservationIO /* * Instruction retire @@ -48,6 +49,8 @@ class InstructionRetire(depth: Int) extends Module { // Exception interface val exception = new ExceptionRequestIO() + val updateLoadReservation = new UpdateLoadReservationIO() + // Flush interface val dCacheFlush = new FlushCacheIO() val iCacheFlush = new FlushCacheIO() @@ -107,6 +110,10 @@ class InstructionRetire(depth: Int) extends Module { io.iCacheFlush.req := false.B io.tlbFlush.req := false.B + io.updateLoadReservation.load := false.B + io.updateLoadReservation.address := 0.U + io.updateLoadReservation.valid := false.B + private def gotoExceptionHandler(id: Int) = { io.recover := true.B @@ -196,6 +203,17 @@ class InstructionRetire(depth: Int) extends Module { io.csr.data := retireEntries(id).executeResult.csrData } + private def updateLoadReservation(id: Int) = { + when(retireEntries(id).executeResult.sc) { + io.updateLoadReservation.load := false.B + io.updateLoadReservation.valid := true.B + }.elsewhen(retireEntries(id).executeResult.lr) { + io.updateLoadReservation.load := true.B + io.updateLoadReservation.address := retireEntries(id).executeResult.lrAddress + io.updateLoadReservation.valid := true.B + } + } + io.retired.ready := false.B when(statusReg === Status.retire) { when(io.retired.valid && retireValid(0) && retireValid(1)) { @@ -216,6 +234,7 @@ class InstructionRetire(depth: Int) extends Module { }.otherwise { // Normal 0 when(retireEntries(0).valid) { updateRegister(0) + updateLoadReservation(0) retireStoreQueue(0) } @@ -235,6 +254,7 @@ class InstructionRetire(depth: Int) extends Module { flushID := 1.U }.otherwise { // Normal 1 when(retireEntries(1).valid) { + updateLoadReservation(1) updateRegister(1) retireStoreQueue(1) } diff --git a/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala b/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala index ce6d38c..0b5caae 100644 --- a/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala +++ b/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala @@ -39,8 +39,10 @@ class RV32PTest extends AnyFlatSpec with ChiselScalatestTester { // Collect tests private val uiTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ui-p-.*\.bin")) private val ucTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32uc-p-.*\.bin")) - private val uaTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ua-p-amoadd_w\.bin")) - private val needToTest = uiTests + private val uaTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ua-p-.*\.bin")) + private val umTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32um-p-.*\.bin")) + + private val needToTest = uiTests ++ ucTests ++ uaTests ++ umTests private def expectPass(memory: MemoryMock) = { assert(memory.loadInt(hostAddress) == passTestNum)