fix(StoreQueue): not do nc store when having exception (#4661)
EMU Test / Changes Detection (push) Waiting to run Details
EMU Test / Generate Verilog (push) Blocked by required conditions Details
EMU Test / EMU - Basics (push) Blocked by required conditions Details
EMU Test / EMU - CHI (push) Blocked by required conditions Details
EMU Test / Docker Build (push) Blocked by required conditions Details
EMU Test / EMU - Performance (push) Blocked by required conditions Details
EMU Test / EMU - MC (push) Blocked by required conditions Details
EMU Test / SIMV - Basics (push) Blocked by required conditions Details
EMU Test / Upload Artifacts (push) Blocked by required conditions Details
EMU Test / Check Submodules (push) Blocked by required conditions Details
EMU Test / Check Format (push) Blocked by required conditions Details
Release Jobs / build-xsdev-image (push) Waiting to run Details

### Bug Description

The NC store does not handle exceptions properly. When an exception
occurs during its execution, the store queue (SQ) continues to process
it as if it were a normal nc store—this includes committing it and
sending it to the uncache—resulting in an invalid memory access.

### Bug Analysis

In the current SQ design, for regular stores, both `rdataPtr` and
`deqPtr` advance based on handshakes with `dataBuffer` and `SBuffer`,
respectively. Thus, even if an exception occurs, the store is marked as
`committed = true` and passed into both buffers. If an exception is
detected at the `SBuffer`, the actual write is skipped.

For NC stores, the `committed` logic is reused from regular stores.
However, there is no dedicated handling for exceptional cases in the
later pipeline stages—such as preventing handshake with the `UBuffer`,
or allowing the handshake but skipping the write. This fix adopts the
former approach: **NC stores will not perform a handshake with the
`UBuffer` when an exception is detected**, in order to preserve a clean
and consistent `UBuffer` interface.

### Solution

* NC stores continue to be marked as `committed = true`.
* Only when `!completed(rptr0) && allvalid(rptr0) &&
!hasException(rptr0)` is satisfied, the NC store is allowed to enter the
`UBuffer`.
* If the instruction causes an exception, the backend will flush it and
all subsequent instructions.
* When the flushed instruction reaches the NC store entry in the store
queue, `completed` is set to `true`.
* For `deqPtr`, since `deqPtr` advances based on the `completed` signal,
no additional changes are needed.
* For `rdataPtr`, behavior remains unchanged for regular stores, but for
NC stores, advancement is now triggered by `completed` instead of
`ncReadNextTrigger`.
This commit is contained in:
Yanqin Li 2025-05-19 18:51:33 +08:00 committed by GitHub
parent f96455c94b
commit 3c8fe1e213
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 37 additions and 13 deletions

View File

@ -285,6 +285,7 @@ class StoreQueue(implicit p: Parameters) extends XSModule
val enqPtr = enqPtrExt(0).value
val deqPtr = deqPtrExt(0).value
val cmtPtr = cmtPtrExt(0).value
val rdPtr = rdataPtrExt(0).value
val validCount = distanceBetween(enqPtrExt(0), deqPtrExt(0))
val allowEnqueue = validCount <= (StoreQueueSize - LSQStEnqWidth).U
@ -314,10 +315,23 @@ class StoreQueue(implicit p: Parameters) extends XSModule
assert(EnsbufferWidth <= 2)
// rdataPtrExtNext and rdataPtrExtNext+1 entry will be read from dataModule
val rdataPtrExtNext = Wire(Vec(EnsbufferWidth, new SqPtr))
rdataPtrExtNext := rdataPtrExt.map(i => i +
PopCount(dataBuffer.io.enq.map(x=> x.fire && x.bits.sqNeedDeq)) +
PopCount(ncReadNextTrigger || io.mmioStout.fire || io.vecmmioStout.fire)
)
val sqReadCnt = WireInit(0.U(log2Ceil(EnsbufferWidth + 1).W))
val readyReadGoVec = WireInit(VecInit((0 until EnsbufferWidth).map(i =>
if(i == 0) {
dataBuffer.io.enq(i).fire && dataBuffer.io.enq(i).bits.sqNeedDeq ||
allocated(rdataPtrExt(i).value) && completed(rdataPtrExt(i).value) && nc(rdataPtrExt(i).value) ||
io.mmioStout.fire || io.vecmmioStout.fire
} else {
dataBuffer.io.enq(i).fire && dataBuffer.io.enq(i).bits.sqNeedDeq ||
allocated(rdataPtrExt(i).value) && completed(rdataPtrExt(i).value) && nc(rdataPtrExt(i).value)
}
)))
for (i <- 0 until EnsbufferWidth) {
when(readyReadGoVec.take(i + 1).reduce(_ && _)) {
sqReadCnt := (i + 1).U // increase one by one
}
}
rdataPtrExtNext := rdataPtrExt.map(_ + sqReadCnt)
// deqPtrExtNext traces which inst is about to leave store queue
val deqPtrExtNext = Wire(Vec(EnsbufferWidth, new SqPtr))
@ -876,7 +890,10 @@ class StoreQueue(implicit p: Parameters) extends XSModule
val rptr0 = rdataPtrExt(0).value
switch(ncState){
is(nc_idle) {
when(nc(rptr0) && allocated(rptr0) && committed(rptr0) && !mmio(rptr0) && !isVec(rptr0)) {
when(
nc(rptr0) && allocated(rptr0) && !completed(rptr0) && committed(rptr0) &&
allvalid(rptr0) && !isVec(rptr0) && !hasException(rptr0) && !mmio(rptr0)
) {
ncState := nc_req
ncWaitRespPtrReg := rptr0
}
@ -1078,26 +1095,33 @@ class StoreQueue(implicit p: Parameters) extends XSModule
// TODO: Deal with vector store mmio
for (i <- 0 until CommitWidth) {
// don't mark misalign store as committed
val ptr = cmtPtrExt(i).value
val isCommit = WireInit(false.B)
when (
allocated(cmtPtrExt(i).value) &&
isNotAfter(uop(cmtPtrExt(i).value).robIdx, GatedRegNext(io.rob.pendingPtr)) &&
!needCancel(cmtPtrExt(i).value) &&
(!waitStoreS2(cmtPtrExt(i).value) || isVec(cmtPtrExt(i).value))) {
allocated(ptr) &&
isNotAfter(uop(ptr).robIdx, GatedRegNext(io.rob.pendingPtr)) &&
!needCancel(ptr) &&
(!waitStoreS2(ptr) || isVec(ptr))) {
if (i == 0){
// TODO: fixme for vector mmio
when ((mmioState === s_idle) || (mmioState === s_wait && scommit > 0.U)){
when ((isVec(cmtPtrExt(i).value) && vecMbCommit(cmtPtrExt(i).value)) || !isVec(cmtPtrExt(i).value)) {
committed(cmtPtrExt(0).value) := true.B
when ((isVec(ptr) && vecMbCommit(ptr)) || !isVec(ptr)) {
isCommit := true.B
committed(ptr) := true.B
commitVec(0) := true.B
}
}
} else {
when ((isVec(cmtPtrExt(i).value) && vecMbCommit(cmtPtrExt(i).value)) || !isVec(cmtPtrExt(i).value)) {
committed(cmtPtrExt(i).value) := commitVec(i - 1) || committed(cmtPtrExt(i).value)
when ((isVec(ptr) && vecMbCommit(ptr)) || !isVec(ptr)) {
isCommit := commitVec(i - 1) || committed(ptr)
committed(ptr) := commitVec(i - 1) || committed(ptr)
commitVec(i) := commitVec(i - 1)
}
}
}
when(isCommit && nc(ptr) && hasException(ptr)) {
completed(ptr) := true.B
}
}
commitCount := PopCount(commitVec)