Skip to content

Commit d5b49d2

Browse files
committed
lsu performance optimizations
- allow for fast head update on smem path (same cycle as request sent out) - more aggressive combinational token reservation
1 parent bdd9700 commit d5b49d2

3 files changed

Lines changed: 47 additions & 21 deletions

File tree

src/main/scala/radiance/muon/InstBuffer.scala

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,6 @@ class InstBuffer(implicit p: Parameters) extends CoreModule()(p) {
115115
b.io.enq.bits := enq.uop.bits
116116
assert(!b.io.enq.valid || b.io.enq.ready, s"warp $wid ibuf full")
117117

118-
// this is slow (more latency), but safe
119-
// for memory instructions, we first acquire LSU token, then dequeue,
120-
// rather than trying to do both on the same cycle
121118
val inst = b.io.deq.bits.inst
122119
val needsLsuReserve = b.io.deq.valid && inst.b(UseLSUPipe)
123120
val opext = inst.opcode(8, 7)
@@ -130,7 +127,6 @@ class InstBuffer(implicit p: Parameters) extends CoreModule()(p) {
130127
when (needsLsuReserve) {
131128
val acquiredToken = RegInit(0.U.asTypeOf(new LsuQueueToken))
132129
val acquiredTokenValid = RegInit(false.B)
133-
134130
deq.valid := false.B
135131
b.io.deq.ready := false.B
136132
reserve.req.valid := true.B
@@ -142,6 +138,25 @@ class InstBuffer(implicit p: Parameters) extends CoreModule()(p) {
142138
acquiredTokenValid := true.B
143139
}
144140

141+
if (muonParams.combinationalTokenReserve) {
142+
// try to immediately dequeue upon successful LSU reservation
143+
// combinationally connects LSU and issue logic
144+
when (reserve.req.fire) {
145+
deq.valid := true.B
146+
b.io.deq.ready := deq.ready
147+
148+
deq.bits.uop := b.io.deq.bits
149+
deq.bits.token := reserve.resp.bits.token
150+
151+
when (deq.fire) {
152+
acquiredTokenValid := false.B
153+
}
154+
}
155+
}
156+
157+
// this is slower (more latency), but safer
158+
// token acquisition and inst buffer dequeue happen on different cycles
159+
// prevents back-to-back issuing of memory instructions
145160
when (acquiredTokenValid) {
146161
deq.valid := true.B
147162
b.io.deq.ready := deq.ready

src/main/scala/radiance/muon/LSU.scala

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ case class LoadStoreUnitParams(
2020
val storeDataEntries: Int = 8, // limited to 8 unissued store requests
2121
val addressEntries: Int = 16, // limited to 16 unissued memory requests
2222

23+
// purely performance optimizations; turn off if bad things are happening
2324
val smemDoesntReorder: Boolean = true, // see comment above LSQMemUpdate
25+
val fastHeadUpdate: Boolean = true, // allow logical head to update on same cycle as memUpdate / memResponse
2426
) {
2527
val globalLdqIndexBits = log2Up(numGlobalLdqEntries)
2628
val globalStqIndexBits = log2Up(numGlobalStqEntries)
@@ -380,9 +382,12 @@ class LoadStoreQueue(implicit p: Parameters) extends CoreModule()(p) {
380382
val storeDataIdx = RegInit(VecInit.fill(entries)(0.U(muonParams.lsu.storeDataIdxBits.W)))
381383
val loadDataIdx = RegInit(VecInit.fill(entries)(0.U(muonParams.lsu.loadDataIdxBits.W)))
382384
val loadPackets = RegInit(VecInit.fill(entries)(0.U(lsuDerived.packetBits.W)))
383-
val done = RegInit(VecInit.fill(entries)(false.B))
385+
val doneNext = Wire(Vec(entries, Bool()))
386+
val done = RegNext(doneNext, VecInit.fill(entries)(false.B))
384387
val writeback = RegInit(VecInit.fill(entries)(false.B))
385388

389+
doneNext := done
390+
386391
val debugId = lsuDerived.debugIdBits.map { bits =>
387392
RegInit(VecInit.fill(entries)(0.U(bits.W)))
388393
}
@@ -400,7 +405,7 @@ class LoadStoreQueue(implicit p: Parameters) extends CoreModule()(p) {
400405
storeDataIdx(idxBits(tail)) := io.storeDataIdx
401406
}
402407
loadPackets(idxBits(tail)) := 0.U(lsuDerived.packetBits.W)
403-
done(idxBits(tail)) := false.B
408+
doneNext(idxBits(tail)) := false.B
404409
if (lsuDerived.debugIdBits.isDefined) {
405410
debugId.get(idxBits(tail)) := io.debugId.get
406411
}
@@ -521,7 +526,7 @@ class LoadStoreQueue(implicit p: Parameters) extends CoreModule()(p) {
521526

522527
// set done when receiving mem update or mem response, allowing logical head to move forward
523528
when (io.receivedMemUpdate.valid) {
524-
done(localIndex(io.receivedMemUpdate.bits.token.index)) := true.B
529+
doneNext(localIndex(io.receivedMemUpdate.bits.token.index)) := true.B
525530
}
526531

527532
// Lookup interface: provides loadDataIdx without triggering state updates
@@ -539,7 +544,7 @@ class LoadStoreQueue(implicit p: Parameters) extends CoreModule()(p) {
539544
loadPackets(memResponseIndex) := loadPackets(memResponseIndex) + 1.U
540545

541546
when (loadPackets(memResponseIndex) === (lsuDerived.numPackets - 1).U) {
542-
done(memResponseIndex) := true.B
547+
doneNext(memResponseIndex) := true.B
543548

544549
if (loadQueue) {
545550
// every load needs to write back
@@ -569,8 +574,13 @@ class LoadStoreQueue(implicit p: Parameters) extends CoreModule()(p) {
569574

570575
// update logical head
571576
// update physical head
572-
// TODO: optimize this to be faster (multiple entries? probably want at least same cycle updates)
573-
when (logicalHead =/= tail && (!valid(idxBits(logicalHead)) || done(idxBits(logicalHead)))) {
577+
val advanceLogicalHead = {
578+
!valid(idxBits(logicalHead)) ||
579+
done(idxBits(logicalHead)) ||
580+
(muonParams.lsu.fastHeadUpdate.B && doneNext(idxBits(logicalHead))) // same-cycle update
581+
}
582+
583+
when (logicalHead =/= tail && advanceLogicalHead) {
574584
logicalHead := logicalHead + 1.U
575585
}
576586

@@ -1335,17 +1345,17 @@ class LoadStoreUnit(implicit p: Parameters) extends CoreModule()(p) {
13351345

13361346
// -- Memory Update --
13371347
if (muonParams.lsu.smemDoesntReorder) {
1338-
// if io.shmemReq fired last cycle, then send receivedMemUpdate to appropriate queue
1339-
// it might be possible to send it on the same cycle that it fires, but this seems safer
1340-
// if the response comes back the next cycle anyways, this should still be fine too
1341-
val shmemReqFire_d1 = RegNext(io.shmemReq.fire, false.B)
1342-
val shmemReqToken_d1 = RegNext(
1343-
io.shmemReq.bits.tag.asTypeOf(new LsuMemTag),
1344-
0.U.asTypeOf(new LsuMemTag)
1345-
)
1346-
1347-
loadStoreQueues.io.receivedMemUpdate.valid := shmemReqFire_d1
1348-
loadStoreQueues.io.receivedMemUpdate.bits.token := shmemReqToken_d1.token
1348+
// if queueRequest fired this cycle w/ shared mem request, then send receivedMemUpdate to appropriate queue
1349+
// it's critical to get this update back to load/store queues ASAP to ensure full shared memory store throughput
1350+
val queueRequest = loadStoreQueues.io.sendMemRequest.req
1351+
val queueRequestToken = queueRequest.bits.token
1352+
when (queueRequest.fire && queueRequestToken.addressSpace === AddressSpace.sharedMemory) {
1353+
loadStoreQueues.io.receivedMemUpdate.valid := true.B
1354+
loadStoreQueues.io.receivedMemUpdate.bits.token := queueRequestToken
1355+
}.otherwise {
1356+
loadStoreQueues.io.receivedMemUpdate.valid := false.B
1357+
loadStoreQueues.io.receivedMemUpdate.bits.token := DontCare
1358+
}
13491359
}
13501360
else {
13511361
loadStoreQueues.io.receivedMemUpdate.valid := false.B

src/main/scala/radiance/muon/MuonCore.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ case class MuonCoreParams(
2626
logRenameMinWarps: Int = 1, // minimum 2 warps share PRF
2727
numIPDOMEntries: Int = 8,
2828
ibufDepth: Int = 8,
29+
combinationalTokenReserve: Boolean = true, // perf: reserve token combinationally with dequeue from ibuf
2930
startAddress: BigInt = x"1000_0000",
3031
// issue
3132
numIssueQueueEntries: Int = 8, // RS

0 commit comments

Comments
 (0)