xref: /XiangShan/src/main/scala/xiangshan/frontend/WrBypass.scala (revision 211d620b07edb797ba35b635d24fef4e7294bae2)
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***************************************************************************************/
16package xiangshan.frontend
17
18import chisel3._
19import chisel3.util._
20import org.chipsalliance.cde.config.Parameters
21import utility._
22import xiangshan._
23
24class WrBypass[T <: Data](
25    gen:            T,
26    val numEntries: Int,
27    val idxWidth:   Int,
28    val numWays:    Int = 1,
29    val tagWidth:   Int = 0,
30    val extraPort:  Option[Boolean] = None
31)(implicit p: Parameters) extends XSModule {
32  require(numEntries >= 0)
33  require(idxWidth > 0)
34  require(numWays >= 1)
35  require(tagWidth >= 0)
36  def hasTag       = tagWidth > 0
37  def multipleWays = numWays > 1
38  val io = IO(new Bundle {
39    val wen            = Input(Bool())
40    val write_idx      = Input(UInt(idxWidth.W))
41    val write_tag      = if (hasTag) Some(Input(UInt(tagWidth.W))) else None
42    val write_data     = Input(Vec(numWays, gen))
43    val write_way_mask = if (multipleWays) Some(Input(Vec(numWays, Bool()))) else None
44
45    val conflict_valid      = if (extraPort.isDefined) Some(Input(Bool())) else None
46    val conflict_write_data = if (extraPort.isDefined) Some(Input(Vec(numWays, gen))) else None
47    val conflict_way_mask   = if (extraPort.isDefined) Some(Input(UInt(numBr.W))) else None
48
49    val hit             = Output(Bool())
50    val hit_data        = Vec(numWays, Valid(gen))
51    val has_conflict    = if (extraPort.isDefined) Some(Output(Bool())) else None
52    val update_idx      = if (extraPort.isDefined) Some(Output(UInt(idxWidth.W))) else None
53    val update_data     = if (extraPort.isDefined) Some(Output(Vec(numWays, gen))) else None
54    val update_way_mask = if (extraPort.isDefined) Some(Output(UInt(numBr.W))) else None
55
56    val conflict_clean = if (extraPort.isDefined) Some(Input(Bool())) else None
57  })
58
59  class Idx_Tag extends Bundle {
60    val idx = UInt(idxWidth.W)
61    val tag = if (hasTag) Some(UInt(tagWidth.W)) else None
62    def apply(idx: UInt, tag: UInt) = {
63      this.idx := idx
64      this.tag.map(_ := tag)
65    }
66  }
67
68  val idx_tag_cam = Module(new IndexableCAMTemplate(new Idx_Tag, numEntries, 1, isIndexable = extraPort.isDefined))
69  val data_mem    = Mem(numEntries, Vec(numWays, gen))
70
71  val valids       = RegInit(0.U.asTypeOf(Vec(numEntries, Vec(numWays, Bool()))))
72  val ever_written = RegInit(0.U.asTypeOf(Vec(numEntries, Bool())))
73
74  idx_tag_cam.io.r.req(0)(io.write_idx, io.write_tag.getOrElse(0.U))
75  val hits_oh = idx_tag_cam.io.r.resp(0).zip(ever_written).map { case (h, ew) => h && ew }
76  val hit_idx = OHToUInt(hits_oh)
77  val hit     = hits_oh.reduce(_ || _)
78
79  io.hit := hit
80  for (i <- 0 until numWays) {
81    io.hit_data(i).valid := Mux1H(hits_oh, valids)(i)
82    io.hit_data(i).bits  := data_mem.read(hit_idx)(i)
83  }
84
85  // Replacer
86  // Because data_mem can only write to one index
87  // Implementing a per-way replacer is meaningless
88  // So here use one replacer for all ways
89  val replacer            = ReplacementPolicy.fromString("plru", numEntries)  // numEntries in total
90  val replacer_touch_ways = Wire(Vec(1, Valid(UInt(log2Ceil(numEntries).W)))) // One index at a time
91  val enq_idx             = replacer.way
92  val full_mask           = Fill(numWays, 1.U(1.W)).asTypeOf(Vec(numWays, Bool()))
93  val update_way_mask     = io.write_way_mask.getOrElse(full_mask)
94
95  // write data on every request
96  when(io.wen) {
97    val data_write_idx = Mux(hit, hit_idx, enq_idx)
98    data_mem.write(data_write_idx, io.write_data, update_way_mask)
99  }
100  replacer_touch_ways(0).valid := io.wen
101  replacer_touch_ways(0).bits  := Mux(hit, hit_idx, enq_idx)
102  replacer.access(replacer_touch_ways)
103
104  // update valids
105  for (i <- 0 until numWays) {
106    when(io.wen) {
107      when(hit) {
108        when(update_way_mask(i)) {
109          valids(hit_idx)(i) := true.B
110        }
111      }.otherwise {
112        ever_written(enq_idx) := true.B
113        valids(enq_idx)(i)    := false.B
114        when(update_way_mask(i)) {
115          valids(enq_idx)(i) := true.B
116        }
117      }
118    }
119  }
120
121  val enq_en = io.wen && !hit
122  idx_tag_cam.io.w.valid      := enq_en
123  idx_tag_cam.io.w.bits.index := enq_idx
124  idx_tag_cam.io.w.bits.data(io.write_idx, io.write_tag.getOrElse(0.U))
125
126  // Extra ports are used to handle dual port read/write conflicts
127  if (extraPort.isDefined) {
128    val conflict_flags    = RegInit(0.U.asTypeOf(Vec(numEntries, Bool())))
129    val conflict_way_mask = RegInit(0.U.asTypeOf(io.conflict_way_mask.get))
130    val conflict_data     = RegInit(VecInit(Seq.tabulate(numWays)(i => 0.U.asTypeOf(gen))))
131    val conflict_idx      = OHToUInt(conflict_flags)
132
133    idx_tag_cam.io.ridx.get := conflict_idx
134
135    when(io.wen && io.conflict_valid.getOrElse(false.B)) {
136      conflict_flags(Mux(hit, hit_idx, enq_idx)) := true.B
137      conflict_way_mask                          := io.conflict_way_mask.get
138      conflict_data                              := io.conflict_write_data.get
139    }
140    when(io.conflict_clean.getOrElse(false.B)) {
141      conflict_flags(conflict_idx) := false.B
142    }
143    // for update the cached data
144    io.has_conflict.get    := conflict_flags.reduce(_ || _)
145    io.update_idx.get      := idx_tag_cam.io.rdata.get.idx
146    io.update_way_mask.get := conflict_way_mask
147    io.update_data.foreach(_ := conflict_data)
148  } else None
149
150  XSPerfAccumulate("wrbypass_hit", io.wen && hit)
151  XSPerfAccumulate("wrbypass_miss", io.wen && !hit)
152
153  XSDebug(
154    io.wen && hit,
155    p"wrbypass hit entry #${hit_idx}, idx ${io.write_idx}" +
156      p"tag ${io.write_tag.getOrElse(0.U)}data ${io.write_data}\n"
157  )
158  XSDebug(
159    io.wen && !hit,
160    p"wrbypass enq entry #${enq_idx}, idx ${io.write_idx}" +
161      p"tag ${io.write_tag.getOrElse(0.U)}data ${io.write_data}\n"
162  )
163}
164