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