xref: /XiangShan/src/main/scala/xiangshan/backend/rename/freelist/StdFreeList.scala (revision 3dfb64bde9e4072d834b83171c7d1d872909cb9b)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16
17package xiangshan.backend.rename.freelist
18
19import org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import xiangshan._
23import xiangshan.backend.rename._
24import utils._
25import utility._
26
27
28class StdFreeList(freeListSize: Int, numLogicRegs: Int, regType: RegType, realNumLogicRegs: Int = 32)(implicit p: Parameters) extends BaseFreeList(freeListSize, realNumLogicRegs) with HasPerfEvents {
29
30  val freeList = RegInit(VecInit(Seq.tabulate(freeListSize)( i => (i + numLogicRegs).U(PhyRegIdxWidth.W) )))
31  val lastTailPtr = RegInit(FreeListPtr(true, 0)) // tailPtr in the last cycle (need to add freeReqReg)
32  val tailPtr = Wire(new FreeListPtr) // this is the real tailPtr
33  val tailPtrOHReg = RegInit(0.U(freeListSize.W))
34
35  //
36  // free committed instructions' `old_pdest` reg
37  //
38  val freeReqReg = io.freeReq
39  for (i <- 0 until RabCommitWidth) {
40    val offset = if (i == 0) 0.U else PopCount(freeReqReg.take(i))
41    val enqPtr = lastTailPtr + offset
42
43    // Why RegNext (from RAT and Rename): for better timing
44    // Why we can RegNext: these free registers won't be used in the next cycle,
45    // since we set canAllocate only when the current free regs > RenameWidth.
46    when (freeReqReg(i)) {
47      freeList(enqPtr.value) := io.freePhyReg(i)
48    }
49    XSDebug(io.freeReq(i), p"req#$i free physical reg: ${io.freePhyReg(i)}\n")
50  }
51
52  tailPtr := lastTailPtr + PopCount(freeReqReg)
53  lastTailPtr := tailPtr
54
55  //
56  // allocate new physical registers for instructions at rename stage
57  //
58  val freeRegCnt = Wire(UInt()) // number of free registers in free list
59  io.canAllocate := GatedValidRegNext(freeRegCnt >= RenameWidth.U) // use RegNext for better timing
60  XSDebug(p"freeRegCnt: $freeRegCnt\n")
61
62  val phyRegCandidates = VecInit(headPtrOHVec.map(sel => Mux1H(sel, freeList)))
63
64  for(i <- 0 until RenameWidth) {
65    io.allocatePhyReg(i) := phyRegCandidates(PopCount(io.allocateReq.take(i)))
66    XSDebug(p"req:${io.allocateReq(i)} canAllocate:${io.canAllocate} pdest:${io.allocatePhyReg(i)}\n")
67  }
68  val doCommit = io.commit.isCommit
69  val archAlloc = io.commit.commitValid zip io.commit.info map { case (valid, info) =>
70    valid && (regType match {
71      case Reg_F => info.fpWen
72      case Reg_V => info.vecWen
73      case Reg_V0 => info.v0Wen
74      case Reg_Vl => info.vlWen
75    })
76  }
77  val numArchAllocate = PopCount(archAlloc)
78  val archHeadPtrNew  = archHeadPtr + numArchAllocate
79  val archHeadPtrNext = Mux(doCommit, archHeadPtrNew, archHeadPtr)
80  archHeadPtr := archHeadPtrNext
81
82  val isWalkAlloc = io.walk && io.doAllocate
83  val isNormalAlloc = io.canAllocate && io.doAllocate
84  val isAllocate = isWalkAlloc || isNormalAlloc
85  val numAllocate = Mux(io.walk, PopCount(io.walkReq), PopCount(io.allocateReq))
86  val headPtrAllocate = Mux(lastCycleRedirect, redirectedHeadPtr, headPtr + numAllocate)
87  val headPtrOHAllocate = Mux(lastCycleRedirect, redirectedHeadPtrOH, headPtrOHVec(numAllocate))
88  val headPtrNext = Mux(isAllocate, headPtrAllocate, headPtr)
89  freeRegCnt := Mux(isWalkAlloc && !lastCycleRedirect, distanceBetween(tailPtr, headPtr) - PopCount(io.walkReq),
90                Mux(isNormalAlloc,                     distanceBetween(tailPtr, headPtr) - PopCount(io.allocateReq),
91                                                       distanceBetween(tailPtr, headPtr)))
92
93  // priority: (1) exception and flushPipe; (2) walking; (3) mis-prediction; (4) normal dequeue
94  val realDoAllocate = !io.redirect && isAllocate
95  headPtr := Mux(realDoAllocate, headPtrAllocate, headPtr)
96  headPtrOH := Mux(realDoAllocate, headPtrOHAllocate, headPtrOH)
97
98  XSDebug(p"head:$headPtr tail:$tailPtr\n")
99
100  XSError(!isFull(tailPtr, archHeadPtr), s"${regType}ArchFreeList should always be full\n")
101
102  val enableFreeListCheck = false
103  if (enableFreeListCheck) {
104    for (i <- 0 until freeListSize) {
105      for (j <- i+1 until freeListSize) {
106        XSError(freeList(i) === freeList(j), s"Found same entry in free list! (i=$i j=$j)\n")
107      }
108    }
109  }
110
111  XSPerfAccumulate("utilization", PopCount(io.allocateReq))
112  XSPerfAccumulate("allocation_blocked_cycle", !io.canAllocate)
113  XSPerfAccumulate("can_alloc_wrong", !io.canAllocate && freeRegCnt >= RenameWidth.U)
114
115  val freeRegCntReg = RegNext(freeRegCnt)
116  val perfEvents = Seq(
117    ("std_freelist_1_4_valid", freeRegCntReg <  (freeListSize / 4).U                                            ),
118    ("std_freelist_2_4_valid", freeRegCntReg >= (freeListSize / 4).U && freeRegCntReg < (freeListSize / 2).U    ),
119    ("std_freelist_3_4_valid", freeRegCntReg >= (freeListSize / 2).U && freeRegCntReg < (freeListSize * 3 / 4).U),
120    ("std_freelist_4_4_valid", freeRegCntReg >= (freeListSize * 3 / 4).U                                        )
121  )
122
123  QueuePerf(size = freeListSize, utilization = freeRegCntReg, full = freeRegCntReg === 0.U)
124
125  generatePerfEvent()
126}
127