xref: /XiangShan/src/main/scala/device/AXI4Memory.scala (revision b03c55a5df5dc8793cb44b42dd60141566e57e78)
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 utility._
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