1/*************************************************************************************** 2 * Copyright (c) 2020-2022 Institute of Computing Technology, Chinese Academy of Sciences 3 * 4 * XiangShan is licensed under Mulan PSL v2. 5 * You can use this software according to the terms and conditions of the Mulan PSL v2. 6 * You may obtain a copy of Mulan PSL v2 at: 7 * http://license.coscl.org.cn/MulanPSL2 8 * 9 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 10 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 12 * 13 * See the Mulan PSL v2 for more details. 14 ***************************************************************************************/ 15 16package device 17 18import org.chipsalliance.cde.config.Parameters 19import chisel3._ 20import chisel3.experimental.{ExtModule, prefix} 21import chisel3.util._ 22import difftest.common.DifftestMem 23import freechips.rocketchip.amba.axi4.{AXI4MasterNode, AXI4Parameters, AXI4SlaveNode} 24import freechips.rocketchip.diplomacy.{AddressSet, InModuleBody, LazyModule, LazyModuleImp} 25import utils._ 26 27class MemoryRequestHelper(requestType: Int) 28 extends ExtModule(Map("REQUEST_TYPE" -> requestType)) 29 with HasExtModuleInline 30{ 31 val clock = IO(Input(Clock())) 32 val reset = IO(Input(Reset())) 33 val io = IO(new Bundle { 34 val req = Flipped(ValidIO(new Bundle { 35 val addr = UInt(64.W) 36 val id = UInt(32.W) 37 })) 38 val response = Output(Bool()) 39 }) 40 41 val verilogLines = Seq( 42 "import \"DPI-C\" function bit memory_request (", 43 " input longint address,", 44 " input int id,", 45 " input bit isWrite", 46 ");", 47 "", 48 "module MemoryRequestHelper #(", 49 " parameter REQUEST_TYPE", 50 ")(", 51 " input clock,", 52 " input reset,", 53 " input io_req_valid,", 54 " input [63:0] io_req_bits_addr,", 55 " input [31:0] io_req_bits_id,", 56 " output reg io_response", 57 ");", 58 "", 59 "always @(posedge clock or posedge reset) begin", 60 " if (reset) begin", 61 " io_response <= 1'b0;", 62 " end", 63 " else if (io_req_valid) begin", 64 " io_response <= memory_request(io_req_bits_addr, io_req_bits_id, REQUEST_TYPE);", 65 " end" + 66 " else begin", 67 " io_response <= 1'b0;", 68 " end", 69 "end", 70 "", 71 "endmodule" 72 ) 73 setInline(s"$desiredName.v", verilogLines.mkString("\n")) 74} 75 76class MemoryResponseHelper(requestType: Int) 77 extends ExtModule(Map("REQUEST_TYPE" -> requestType)) 78 with HasExtModuleInline 79{ 80 val clock = IO(Input(Clock())) 81 val reset = IO(Input(Reset())) 82 val enable = IO(Input(Bool())) 83 val response = IO(Output(UInt(64.W))) 84 85 val verilogLines = Seq( 86 "import \"DPI-C\" function longint memory_response (", 87 " input bit isWrite", 88 ");", 89 "", 90 "module MemoryResponseHelper #(", 91 " parameter REQUEST_TYPE", 92 ")(", 93 " input clock,", 94 " input reset,", 95 " input enable,", 96 " output reg [63:0] response", 97 ");", 98 "", 99 "always @(posedge clock or posedge reset) begin", 100 " if (reset) begin", 101 " response <= 64'b0;", 102 " end", 103 " else if (!reset && enable) begin", 104 " response <= memory_response(REQUEST_TYPE);", 105 " end", 106 " else begin", 107 " response <= 64'b0;", 108 " end", 109 "end", 110 "", 111 "endmodule" 112 ) 113 setInline(s"$desiredName.v", verilogLines.mkString("\n")) 114} 115 116trait MemoryHelper { this: Module => 117 private def requestType(isWrite: Boolean): Int = if (isWrite) 1 else 0 118 private def request(valid: Bool, addr: UInt, id: UInt, isWrite: Boolean): Bool = { 119 val helper = Module(new MemoryRequestHelper(requestType(isWrite))) 120 helper.clock := clock 121 helper.reset := reset 122 helper.io.req.valid := valid 123 helper.io.req.bits.addr := addr 124 helper.io.req.bits.id := id 125 helper.io.response 126 } 127 protected def readRequest(valid: Bool, addr: UInt, id: UInt): Bool = 128 request(valid, addr, id, false) 129 protected def writeRequest(valid: Bool, addr: UInt, id: UInt): Bool = 130 request(valid, addr, id, true) 131 private def response(enable: Bool, isWrite: Boolean): (Bool, UInt) = { 132 val helper = Module(new MemoryResponseHelper(requestType(isWrite))) 133 helper.clock := clock 134 helper.reset := reset 135 helper.enable := enable 136 (helper.response(32), helper.response(31, 0)) 137 } 138 protected def readResponse(enable: Bool): (Bool, UInt) = 139 response(enable, false) 140 protected def writeResponse(enable: Bool): (Bool, UInt) = 141 response(enable, true) 142} 143 144class AXI4MemoryImp[T <: Data](outer: AXI4Memory) extends AXI4SlaveModuleImp(outer) with MemoryHelper { 145 val ramBaseAddr = outer.address.head.base 146 val (ramIndexBits, ramOffsetBits) = (log2Ceil(outer.beatBytes), log2Ceil(outer.memByte)) 147 def ramIndex(addr: UInt) = ((addr - ramBaseAddr.U)(ramOffsetBits - 1, 0) >> ramIndexBits).asUInt 148 val ramHelper = DifftestMem(outer.memByte, outer.beatBytes, 8, singlePort = false) 149 150 val numOutstanding = 1 << in.ar.bits.id.getWidth 151 // Note: we are using in.ar.bits.addr.getWidth insead of ramOffsetBits here. 152 // Why: the CPU may access out-of-range addresses. Let the RAM helper deal with it. 153 val addressMem = Mem(numOutstanding, UInt((in.ar.bits.addr.getWidth - ramIndexBits).W)) 154 val arlenMem = Mem(numOutstanding, UInt(in.ar.bits.len.getWidth.W)) 155 156 // accept a read request and send it to the external model 157 val pending_read_req_valid = RegInit(false.B) 158 val pending_read_req_bits = RegEnable(in.ar.bits, in.ar.fire) 159 val pending_read_req_ready = Wire(Bool()) 160 val pending_read_need_req = pending_read_req_valid && !pending_read_req_ready 161 val read_req_valid = pending_read_need_req || in.ar.valid 162 val read_req_bits = Mux(pending_read_need_req, pending_read_req_bits, in.ar.bits) 163 pending_read_req_ready := readRequest(read_req_valid, read_req_bits.addr, read_req_bits.id) 164 165 when (in.ar.fire) { 166 pending_read_req_valid := true.B 167 addressMem.write(read_req_bits.id, ramIndex(read_req_bits.addr)) 168 arlenMem.write(read_req_bits.id, read_req_bits.len) 169 }.elsewhen (pending_read_req_ready) { 170 pending_read_req_valid := false.B 171 } 172 in.ar.ready := !pending_read_req_valid || pending_read_req_ready 173 174 // accept a write request (including address and data) and send it to the external model 175 val pending_write_req_valid = RegInit(VecInit.fill(2)(false.B)) 176 val pending_write_req_bits = RegEnable(in.aw.bits, in.aw.fire) 177 val pending_write_req_data = RegEnable(in.w.bits, in.w.fire) 178 XSError(in.aw.fire && in.aw.bits.len === 0.U, "data must have more than one beat now") 179 val pending_write_req_ready = Wire(Bool()) 180 val pending_write_need_req = pending_write_req_valid.last && !pending_write_req_ready 181 val write_req_valid = pending_write_req_valid.head && (pending_write_need_req || in.w.valid && in.w.bits.last) 182 pending_write_req_ready := writeRequest(write_req_valid, pending_write_req_bits.addr, pending_write_req_bits.id) 183 184 when (in.aw.fire) { 185 pending_write_req_valid.head := true.B 186 }.elsewhen (pending_write_req_ready) { 187 pending_write_req_valid.head := false.B 188 } 189 val write_req_last = in.w.fire && in.w.bits.last 190 when (write_req_last) { 191 pending_write_req_valid.last := true.B 192 }.elsewhen (pending_write_req_ready) { 193 pending_write_req_valid.last := false.B 194 } 195 in.aw.ready := !pending_write_req_valid.head || pending_write_req_ready 196 in.w.ready := in.aw.ready || !pending_write_req_valid.last 197 198 // ram is written when write data fire 199 val wdata_cnt = Counter(outer.burstLen) 200 val write_req_addr = Mux(in.aw.fire, in.aw.bits.addr, pending_write_req_bits.addr) 201 val write_req_index = ramIndex(write_req_addr) + wdata_cnt.value 202 when (in.w.fire) { 203 ramHelper.write( 204 addr = write_req_index, 205 data = in.w.bits.data.asTypeOf(Vec(outer.beatBytes, UInt(8.W))), 206 mask = in.w.bits.strb.asBools 207 ) 208 } 209 when (write_req_last) { 210 wdata_cnt.reset() 211 }.elsewhen (in.w.fire) { 212 wdata_cnt.inc() 213 } 214 215 // read data response: resp from DRAMsim3; read data and response to in.r 216 // This is the output of the last pipeline before in.r. This is not the pipeline registers. 217 val r_resp = Wire(Decoupled(chiselTypeOf(in.r.bits))) 218 219 val pending_read_resp_valid = RegInit(false.B) 220 val pending_read_resp_id = Reg(UInt(r_resp.bits.id.getWidth.W)) 221 val has_read_resp = Wire(Bool()) 222 val read_resp_last = r_resp.fire && r_resp.bits.last 223 val read_request_cnt = RegInit(0.U(8.W)) 224 val read_have_req_cnt = read_request_cnt =/= 0.U 225 val (read_resp_valid, read_resp_id) = readResponse((!has_read_resp || read_resp_last) && read_have_req_cnt) 226 has_read_resp := (read_resp_valid && !read_resp_last) || pending_read_resp_valid 227 val rdata_cnt = Counter(outer.burstLen) 228 val read_resp_addr = addressMem(r_resp.bits.id) + rdata_cnt.value 229 val read_resp_len = arlenMem(r_resp.bits.id) 230 r_resp.valid := read_resp_valid || pending_read_resp_valid 231 r_resp.bits.id := Mux(pending_read_resp_valid, pending_read_resp_id, read_resp_id) 232 // We cannot get the read data this cycle because the RAM helper has one-cycle latency. 233 r_resp.bits.data := DontCare 234 r_resp.bits.resp := AXI4Parameters.RESP_OKAY 235 r_resp.bits.last := (rdata_cnt.value === read_resp_len) 236 237 // The return values of DPI-C are used to determine whether a request has been made or completed 238 // pending_read_req_ready ---> readRequest() 239 // read_resp_valid <--- readResponse() 240 when (pending_read_req_ready && !read_resp_valid) { 241 read_request_cnt := read_request_cnt + 1.U 242 }.elsewhen (read_resp_valid && !pending_read_req_ready) { 243 read_request_cnt := read_request_cnt - 1.U 244 } 245 when (!pending_read_resp_valid && read_resp_valid && !read_resp_last) { 246 pending_read_resp_valid := true.B 247 pending_read_resp_id := read_resp_id 248 }.elsewhen (pending_read_resp_valid && !read_resp_valid && read_resp_last) { 249 pending_read_resp_valid := false.B 250 } 251 when (read_resp_last) { 252 rdata_cnt.reset() 253 }.elsewhen (r_resp.fire) { 254 rdata_cnt.inc() 255 } 256 257 // `r_pipe`: the extra pipeline registers for the read response `in.r` 258 prefix("r_pipe") { 259 val valid = RegInit(false.B) 260 when (r_resp.valid && in.r.ready) { 261 valid := true.B 262 }.elsewhen (in.r.ready) { 263 valid := false.B 264 } 265 in.r.valid := valid 266 in.r.bits := RegEnable(r_resp.bits, r_resp.valid && in.r.ready) 267 r_resp.ready := !valid || in.r.ready 268 269 // the data should be auto-hold 270 in.r.bits.data := ramHelper.readAndHold(read_resp_addr, r_resp.fire).asUInt 271 } 272 273 // write response 274 val pending_write_resp_valid = RegInit(false.B) 275 val pending_write_resp_id = Reg(UInt(in.b.bits.id.getWidth.W)) 276 val has_write_resp = Wire(Bool()) 277 val write_request_cnt = RegInit(0.U(8.W)) 278 val write_have_req_cnt = write_request_cnt =/= 0.U 279 val (write_resp_valid, write_resp_id) = writeResponse((!has_write_resp || in.b.fire) && write_have_req_cnt) 280 has_write_resp := write_resp_valid || pending_write_resp_valid 281 in.b.valid := write_resp_valid || pending_write_resp_valid 282 in.b.bits.id := Mux(pending_write_resp_valid, pending_write_resp_id, write_resp_id) 283 in.b.bits.resp := AXI4Parameters.RESP_OKAY 284 285 // The return values of DPI-C are used to determine whether a request has been made or completed 286 // pending_write_req_ready ---> writeRequest() 287 // write_resp_valid <--- writeResponse() 288 when (pending_write_req_ready && !write_resp_valid) { 289 write_request_cnt := write_request_cnt + 1.U 290 }.elsewhen (write_resp_valid && !pending_write_req_ready) { 291 write_request_cnt := write_request_cnt - 1.U 292 } 293 when (!pending_write_resp_valid && write_resp_valid && !in.b.ready) { 294 pending_write_resp_valid := true.B 295 pending_write_resp_id := write_resp_id 296 }.elsewhen (pending_write_resp_valid && !write_resp_valid && in.b.ready) { 297 pending_write_resp_valid := false.B 298 } 299} 300 301class AXI4Memory 302( 303 val address: Seq[AddressSet], 304 val memByte: Long, 305 val useBlackBox: Boolean = false, 306 val executable: Boolean = true, 307 val beatBytes: Int, 308 val burstLen: Int, 309)(implicit p: Parameters) 310 extends AXI4SlaveModule(address, executable, beatBytes, burstLen) 311{ 312 override lazy val module = new AXI4MemoryImp(this) 313} 314 315class AXI4MemoryWrapper ( 316 slave: AXI4SlaveNode, 317 memByte: Long, 318 useBlackBox: Boolean = false 319)(implicit p: Parameters) extends AXI4MemorySlave(slave, memByte, useBlackBox) { 320 val ram = LazyModule(new AXI4Memory( 321 slaveParam.address, 322 memByte, 323 useBlackBox, 324 slaveParam.executable, 325 portParam.beatBytes, 326 burstLen 327 )) 328 ram.node := master 329} 330 331abstract class AXI4MemorySlave ( 332 slave: AXI4SlaveNode, 333 memByte: Long, 334 useBlackBox: Boolean = false 335)(implicit p: Parameters) extends LazyModule { 336 val master = AXI4MasterNode(List(slave.in.head._2.master)) 337 338 val portParam = slave.portParams.head 339 val slaveParam = portParam.slaves.head 340 val burstLen = portParam.maxTransfer / portParam.beatBytes 341 342 val io_axi4 = InModuleBody{ master.makeIOs() } 343 344 lazy val module = new LazyModuleImp(this) { } 345} 346 347object AXI4MemorySlave { 348 def apply( 349 slave: AXI4SlaveNode, 350 memByte: Long, 351 useBlackBox: Boolean = false, 352 dynamicLatency: Boolean = false 353 )(implicit p: Parameters): AXI4MemorySlave = { 354 val memory = if (dynamicLatency) { 355 LazyModule(new AXI4MemoryWrapper(slave, memByte, useBlackBox)) 356 } else { 357 LazyModule(new AXI4RAMWrapper(slave, memByte, useBlackBox)) 358 } 359 memory 360 } 361} 362