@@ -194,6 +194,19 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
194
194
val cache_alias = req_acquire_s3 && dirResult_s3.hit && meta_s3.clients(0 ) &&
195
195
meta_s3.alias.getOrElse(0 .U ) =/= req_s3.alias.getOrElse(0 .U )
196
196
197
+ // *NOTICE: 'nestable_*' must not be used in A Channel related logics.
198
+ val nestable_dirResult_s3 = Wire (chiselTypeOf(dirResult_s3))
199
+ val nestable_meta_s3 = nestable_dirResult_s3.meta
200
+ val nestable_meta_has_clients_s3 = nestable_dirResult_s3.meta.clients.orR
201
+ nestable_dirResult_s3 := dirResult_s3
202
+ when (req_s3.snpHitRelease) {
203
+ // Meta states from MSHRs were considered as directory result here.
204
+ // Therefore, meta states were always inferred to be hit when nesting release, no matter the fact that directory
205
+ // was always non-hit on cache replacement subsequent release.
206
+ nestable_dirResult_s3.hit := true .B
207
+ nestable_dirResult_s3.meta := req_s3.snpHitReleaseMeta
208
+ }
209
+
197
210
val tagError_s3 = io.dirResp_s3.error || meta_s3.tagErr
198
211
val dataError_s3 = meta_s3.dataErr
199
212
val l2Error_s3 = io.dirResp_s3.error || mshr_req_s3 && req_s3.dataCheckErr.getOrElse(false .B )
@@ -206,6 +219,8 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
206
219
val need_repl = replResp_valid_hold && io.replResp.bits.meta.state =/= INVALID && req_s3.replTask
207
220
208
221
/* ======== Interact with MSHR ======== */
222
+ // *NOTICE: A Channel requests should be blocked by RequestBuffer when MSHR nestable,
223
+ // 'nestable_*' must not be used here.
209
224
val acquire_on_miss_s3 = req_acquire_s3 || req_prefetch_s3 || req_get_s3
210
225
val acquire_on_hit_s3 = meta_s3.state === BRANCH && req_needT_s3 && ! req_prefetch_s3
211
226
val need_acquire_s3_a = req_s3.fromA && (Mux (
@@ -244,9 +259,8 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
244
259
*/
245
260
// whether L2 should do forwarding or not
246
261
val expectFwd = isSnpXFwd(req_s3.chiOpcode.get)
247
- val canFwd = dirResult_s3 .hit
262
+ val canFwd = nestable_dirResult_s3 .hit
248
263
val doFwd = expectFwd && canFwd
249
- val doFwdHitRelease = expectFwd && req_s3.snpHitRelease && req_s3.snpHitReleaseWithData
250
264
val need_pprobe_s3_b_snpStable = req_s3.fromB && (
251
265
isSnpOnceX(req_s3.chiOpcode.get) || isSnpQuery(req_s3.chiOpcode.get) || isSnpStashX(req_s3.chiOpcode.get)
252
266
) && dirResult_s3.hit && meta_s3.state === TRUNK && meta_has_clients_s3
@@ -260,7 +274,7 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
260
274
isSnpMakeInvalidX(req_s3.chiOpcode.get)
261
275
) && dirResult_s3.hit && meta_has_clients_s3
262
276
val need_pprobe_s3_b = need_pprobe_s3_b_snpStable || need_pprobe_s3_b_snpToB || need_pprobe_s3_b_snpToN
263
- val need_dct_s3_b = doFwd || doFwdHitRelease // DCT
277
+ val need_dct_s3_b = doFwd // DCT
264
278
val need_mshr_s3_b = need_pprobe_s3_b || need_dct_s3_b
265
279
266
280
val need_mshr_s3 = need_mshr_s3_a || need_mshr_s3_b
@@ -269,7 +283,7 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
269
283
val alloc_state = WireInit (0 .U .asTypeOf(new FSMState ()))
270
284
alloc_state.elements.foreach(_._2 := true .B )
271
285
io.toMSHRCtl.mshr_alloc_s3.valid := task_s3.valid && ! mshr_req_s3 && need_mshr_s3
272
- io.toMSHRCtl.mshr_alloc_s3.bits.dirResult := dirResult_s3
286
+ io.toMSHRCtl.mshr_alloc_s3.bits.dirResult := nestable_dirResult_s3
273
287
io.toMSHRCtl.mshr_alloc_s3.bits.state := alloc_state
274
288
io.toMSHRCtl.mshr_alloc_s3.bits.task match { case task =>
275
289
task := req_s3
@@ -291,75 +305,86 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
291
305
isSnpQuery(req_s3.chiOpcode.get) ||
292
306
req_s3.chiOpcode.get === SnpOnceFwd ||
293
307
req_s3.chiOpcode.get === SnpUniqueFwd
294
- val shouldRespData_dirty = dirResult_s3.hit && (meta_s3.state === TIP || meta_s3.state === TRUNK ) && meta_s3.dirty
308
+ val shouldRespData_dirty = nestable_dirResult_s3.hit &&
309
+ (nestable_meta_s3.state === TIP || nestable_meta_s3.state === TRUNK ) && nestable_meta_s3.dirty
295
310
// For forwarding snoops, if the RetToSrc value is 1, must return a copy is the cache line is Dirty or Clean.
296
- val shouldRespData_retToSrc_fwd = dirResult_s3 .hit && retToSrc && isSnpXFwd(req_s3.chiOpcode.get)
311
+ val shouldRespData_retToSrc_fwd = nestable_dirResult_s3 .hit && retToSrc && isSnpXFwd(req_s3.chiOpcode.get)
297
312
// For non-forwarding snoops, ig the RetToSrc value is 1, must return a copy if the cache line is Shared Clean and
298
313
// snoopee retains a copy of the cache line.
299
- val shouldRespData_retToSrc_nonFwd = dirResult_s3 .hit && retToSrc && meta_s3 .state === BRANCH && (
314
+ val shouldRespData_retToSrc_nonFwd = nestable_dirResult_s3 .hit && retToSrc && nestable_meta_s3 .state === BRANCH && (
300
315
req_s3.chiOpcode.get === SnpOnce ||
301
316
req_s3.chiOpcode.get === SnpUnique ||
302
317
isSnpToBNonFwd(req_s3.chiOpcode.get)
303
318
)
304
319
val shouldRespData = shouldRespData_dirty || shouldRespData_retToSrc_fwd || shouldRespData_retToSrc_nonFwd
305
320
val doRespData = shouldRespData && ! neverRespData
306
- val doRespDataHitRelease = req_s3.snpHitRelease && req_s3.snpHitReleaseWithData && ! neverRespData
307
321
dontTouch(doRespData)
308
322
dontTouch(shouldRespData)
309
323
dontTouch(neverRespData)
310
324
325
+ // On directory hit under non-invalidating snoop nesting WriteCleanFull,
326
+ // excluding SnpStashX and SnpQuery:
327
+ // 1. SnpCleanShared[1-sink_resp] : UD -> UC_PD, UC -> UC, SC -> SC
328
+ // 2. SnpOnce*[2-sink_resp] : UD -> SC_PD, UC -> SC, SC -> SC
329
+ // 3. snpToB : UD -> SC_PD, UC -> SC, SC -> SC
330
+ //
331
+ // *NOTE[1-sink_resp]:
332
+ // UD -> SC transitions were not used on WriteCleanFull without nesting snoop, and
333
+ // only UD -> UC update could be observed on directory in this case
334
+ // Therefore, it was unnecessary to observe cache state from nested WriteCleanFull MSHRs, while
335
+ // extracting PassDirty from MSHRs
336
+ //
337
+ // *NOTE[2-sink_resp]:
338
+ // UD -> UC transitions were not allowed on SnpOnce*, while permitting UD -> UD and UC -> UC
339
+ // On SnpOnce*, UD/UC were turned into SC on nested WriteClean, on which directory must hit
340
+ // Otherwise, the cache state was fast forwarded to I by default
341
+ // Directory might be missing after multiple nesting snoops on WriteClean, indicating losing UD
342
+ //
311
343
// Resp[2: 0] = {PassDirty, CacheState[1: 0]}
312
344
val respCacheState = WireInit (I )
313
- val respPassDirty = dirResult_s3 .hit && meta_s3 .state === TIP && meta_s3 .dirty &&
314
- ! (neverRespData || req_s3.chiOpcode.get === SnpOnce ) &&
345
+ val respPassDirty = doRespData && nestable_dirResult_s3 .hit && isT(nestable_meta_s3 .state) && nestable_meta_s3 .dirty &&
346
+ ( req_s3.chiOpcode.get =/= SnpOnce || req_s3.snpHitRelease ) &&
315
347
! (isSnpStashX(req_s3.chiOpcode.get) || isSnpQuery(req_s3.chiOpcode.get))
316
348
317
- when (dirResult_s3 .hit) {
349
+ when (nestable_dirResult_s3 .hit) {
318
350
when (isSnpToB(req_s3.chiOpcode.get)) {
319
- respCacheState := SC
351
+ respCacheState := Mux (req_s3.snpHitReleaseToInval, I , SC )
320
352
}
321
353
when (isSnpOnceX(req_s3.chiOpcode.get) || isSnpStashX(req_s3.chiOpcode.get) || isSnpQuery(req_s3.chiOpcode.get)) {
354
+ /**
355
+ * NOTICE: On Stash and Query:
356
+ * the cache state must maintain unchanged on nested copy-back writes
357
+ */
322
358
respCacheState := Mux (
323
- meta_s3 .state === BRANCH ,
359
+ nestable_meta_s3 .state === BRANCH ,
324
360
SC ,
325
- Mux (meta_s3 .dirty, UD , UC )
361
+ Mux (nestable_meta_s3 .dirty, UD , UC )
326
362
)
327
363
}
328
- when (req_s3.chiOpcode.get === SnpCleanShared ) {
329
- respCacheState := Mux (isT(meta_s3.state), UC , SC )
330
- }
331
-
332
- when (req_s3.snpHitReleaseToClean) {
364
+ when (isSnpOnceX(req_s3.chiOpcode.get)) {
333
365
// On SnpOnce/SnpOnceFwd nesting WriteCleanFull, turn UD/UC to SC
334
- when (isSnpOnceX( req_s3.chiOpcode.get) ) {
366
+ when (req_s3.snpHitReleaseToClean ) {
335
367
respCacheState := SC
336
368
}
369
+ // On SnpOnce/SnpOnceFwd nesting WriteBack*/WriteEvict*, turn UD to I
370
+ when (req_s3.snpHitReleaseToInval) {
371
+ respCacheState := I
372
+ }
337
373
}
338
- }
339
-
340
- when (req_s3.snpHitRelease) {
341
- /**
342
- * NOTICE: On Stash and Query:
343
- * the cache state must maintain unchanged on nested copy-back writes
344
- */
345
- when (isSnpStashX(req_s3.chiOpcode.get) || isSnpQuery(req_s3.chiOpcode.get)) {
346
- respCacheState := Mux (
347
- req_s3.snpHitReleaseState === BRANCH ,
348
- SC ,
349
- Mux (req_s3.snpHitReleaseDirty, UD , UC )
350
- )
374
+ when (req_s3.chiOpcode.get === SnpCleanShared ) {
375
+ respCacheState := Mux (isT(nestable_meta_s3.state), UC , SC )
351
376
}
352
377
}
353
378
354
379
// FwdState[2: 0] = {PassDirty, CacheState[1: 0]}
355
380
val fwdCacheState = WireInit (I )
356
381
val fwdPassDirty = WireInit (false .B )
357
- when (dirResult_s3 .hit) {
382
+ when (nestable_dirResult_s3 .hit) {
358
383
when (isSnpToBFwd(req_s3.chiOpcode.get)) {
359
- fwdCacheState := SC
384
+ fwdCacheState := Mux (req_s3.snpHitReleaseToInval, I , SC )
360
385
}
361
386
when (req_s3.chiOpcode.get === SnpUniqueFwd ) {
362
- when (meta_s3 .state === TIP && meta_s3 .dirty) {
387
+ when (nestable_meta_s3 .state === TIP && nestable_meta_s3 .dirty) {
363
388
fwdCacheState := UD
364
389
fwdPassDirty := true .B
365
390
}.otherwise {
@@ -392,44 +417,17 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
392
417
sink_resp_s3.bits.dbID.foreach(_ := 0 .U )
393
418
sink_resp_s3.bits.pCrdType.foreach(_ := 0 .U ) // TODO
394
419
sink_resp_s3.bits.chiOpcode.foreach(_ := MuxLookup (
395
- Cat (doFwd || doFwdHitRelease , doRespData || doRespDataHitRelease ),
420
+ Cat (doFwd, doRespData),
396
421
SnpResp
397
422
)(Seq (
398
423
Cat (false .B , false .B ) -> SnpResp ,
399
424
Cat (true .B , false .B ) -> SnpRespFwded ,
400
425
Cat (false .B , true .B ) -> SnpRespData , // ignore SnpRespDataPtl for now
401
426
Cat (true .B , true .B ) -> SnpRespDataFwded
402
427
)))
403
- sink_resp_s3.bits.resp.foreach(_ := Mux (
404
- req_s3.snpHitRelease && ! (isSnpStashX(req_s3.chiOpcode.get) || isSnpQuery(req_s3.chiOpcode.get)),
405
- setPD(
406
- // On directory hit under non-invalidating snoop nesting WriteCleanFull,
407
- // excluding SnpStashX and SnpQuery:
408
- // 1. SnpCleanShared[1-sink_resp] : UD -> UC_PD, UC -> UC, SC -> SC
409
- // 2. SnpOnce*[2-sink_resp] : UD -> SC_PD, UC -> SC, SC -> SC
410
- // 3. snpToB : UD -> SC_PD, UC -> SC, SC -> SC
411
- //
412
- // *NOTE[1-sink_resp]:
413
- // UD -> SC transitions were not used on WriteCleanFull without nesting snoop, and
414
- // only UD -> UC update could be observed on directory in this case
415
- // Therefore, it was unnecessary to observe cache state from nested WriteCleanFull MSHRs, while
416
- // extracting PassDirty from MSHRs
417
- //
418
- // *NOTE[2-sink_resp]:
419
- // UD -> UC transitions were not allowed on SnpOnce*, while permitting UD -> UD and UC -> UC
420
- // On SnpOnce*, UD/UC were turned into SC on nested WriteClean, on which directory must hit
421
- // Otherwise, the cache state was fast forwarded to I by default
422
- // Directory might be missing after multiple nesting snoops on WriteClean, indicating losing UD
423
- Mux (req_s3.snpHitReleaseToClean && ! isSnpToN(req_s3.chiOpcode.get), respCacheState, I ),
424
- req_s3.snpHitReleaseWithData && req_s3.snpHitReleaseDirty && ! isSnpMakeInvalidX(req_s3.chiOpcode.get)),
425
- setPD(respCacheState, respPassDirty && (doRespData || doRespDataHitRelease))
426
- ))
428
+ sink_resp_s3.bits.resp.foreach(_ := setPD(respCacheState, respPassDirty && doRespData))
427
429
sink_resp_s3.bits.fwdState.foreach(_ := setPD(fwdCacheState, fwdPassDirty))
428
- sink_resp_s3.bits.txChannel := Cat (
429
- doRespData || doRespDataHitRelease,
430
- ! (doRespData || doRespDataHitRelease),
431
- false .B
432
- ) // TODO: parameterize this
430
+ sink_resp_s3.bits.txChannel := Cat (doRespData, ! doRespData, false .B ) // TODO: parameterize this
433
431
sink_resp_s3.bits.size := log2Ceil(blockBytes).U
434
432
sink_resp_s3.bits.meta := sink_resp_s3_b_meta
435
433
sink_resp_s3.bits.metaWen := sink_resp_s3_b_metaWen
@@ -451,9 +449,9 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
451
449
val hasData_s3 = hasData_s3_tl || hasData_s3_chi
452
450
453
451
val need_data_a = dirResult_s3.hit && (req_get_s3 || req_acquireBlock_s3)
454
- val need_data_b = sinkB_req_s3 && (doRespData || doFwd || dirResult_s3 .hit && meta_s3 .state === TRUNK )
452
+ val need_data_b = sinkB_req_s3 && (doRespData || doFwd || nestable_dirResult_s3 .hit && nestable_meta_s3 .state === TRUNK )
455
453
val need_data_mshr_repl = mshr_refill_s3 && need_repl && ! retry
456
- val need_data_cmo = cmo_cbo_s3 && dirResult_s3 .hit && meta_s3 .dirty
454
+ val need_data_cmo = cmo_cbo_s3 && nestable_dirResult_s3 .hit && nestable_meta_s3 .dirty
457
455
val ren = need_data_a || need_data_b || need_data_mshr_repl || need_data_cmo
458
456
459
457
val wen_c = sinkC_req_s3 && isParamFromT(req_s3.param) && req_s3.opcode(0 ) && dirResult_s3.hit
@@ -507,11 +505,13 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
507
505
val need_write_refillBuf = false .B
508
506
509
507
/* ======== Write Directory ======== */
508
+ // B, C: Requests from Channel B (RXSNP) and Channel C would only downgrade permission,
509
+ // so there is no need to use 'nestable_*'.
510
510
val metaW_valid_s3_a = sinkA_req_s3 && ! need_mshr_s3_a && ! req_get_s3 && ! req_prefetch_s3 && ! cmo_cbo_s3 // get & prefetch that hit will not write meta
511
511
// Also write directory on:
512
512
// 1. SnpOnce nesting WriteCleanFull under UD (SnpOnceFwd always needs MSHR) for UD -> SC
513
513
val metaW_valid_s3_b = sinkB_req_s3 && ! need_mshr_s3_b && dirResult_s3.hit &&
514
- (! isSnpOnce(req_s3.chiOpcode.get) || (req_s3.snpHitReleaseToClean && req_s3.snpHitReleaseDirty )) &&
514
+ (! isSnpOnce(req_s3.chiOpcode.get) || (req_s3.snpHitReleaseToClean && req_s3.snpHitReleaseMeta.dirty )) &&
515
515
! isSnpStashX(req_s3.chiOpcode.get) && ! isSnpQuery(req_s3.chiOpcode.get) && (
516
516
meta_s3.state === TIP || meta_s3.state === BRANCH && isSnpToN(req_s3.chiOpcode.get)
517
517
)
@@ -623,12 +623,14 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
623
623
val isTXDAT_s3 = Mux (
624
624
mshr_req_s3,
625
625
mshr_snpRespDataX_s3 || mshr_cbWrData_s3 || mshr_dct_s3,
626
- req_s3.fromB && ! need_mshr_s3 && (doRespDataHitRelease || doRespData && ! data_unready_s3)
626
+ req_s3.fromB && ! need_mshr_s3 &&
627
+ (doRespData && (! data_unready_s3 || req_s3.snpHitRelease && req_s3.snpHitReleaseWithData))
627
628
)
628
629
val isTXDAT_s3_ready = Mux (
629
630
mshr_req_s3,
630
631
mshr_snpRespDataX_s3 || mshr_cbWrData_s3 || mshr_dct_s3,
631
- req_s3.fromB && ! need_mshr_s3 && (doRespDataHitRelease || doRespData && ! data_unready_s3) && ! txdat_s3_latch.B
632
+ req_s3.fromB && ! need_mshr_s3 && ! txdat_s3_latch.B &&
633
+ (doRespData && (! data_unready_s3 || req_s3.snpHitRelease && req_s3.snpHitReleaseWithData))
632
634
)
633
635
val isTXREQ_s3 = mshr_req_s3 && (mshr_writeBackFull_s3 || mshr_writeCleanFull_s3 ||
634
636
mshr_writeEvictFull_s3 || mshr_writeEvictOrEvict_s3 || mshr_evict_s3)
@@ -897,7 +899,7 @@ class MainPipe(implicit p: Parameters) extends TL2CHIL2Module with HasCHIOpcodes
897
899
// TXDAT
898
900
! neverRespData,
899
901
// TXRSP
900
- ! doRespDataHitRelease ,
902
+ ! (doRespData && req_s3.snpHitRelease) ,
901
903
// TXREQ
902
904
task.bits.toTXREQ
903
905
)
0 commit comments