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