xref: /XiangShan/src/main/scala/device/AXI4Memory.scala (revision 602aa9f1a8fb63310bea30e8b3e247e5aca5f123)
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  val pending_write_req_ready = Wire(Bool())
179  val pending_write_need_req = pending_write_req_valid.last && !pending_write_req_ready
180  val write_req_valid = pending_write_req_valid.head && (pending_write_need_req || in.w.valid && in.w.bits.last)
181  pending_write_req_ready := writeRequest(write_req_valid, pending_write_req_bits.addr, pending_write_req_bits.id)
182
183  when (in.aw.fire) {
184    pending_write_req_valid.head := true.B
185  }.elsewhen (pending_write_req_ready) {
186    pending_write_req_valid.head := false.B
187  }
188  val write_req_last = in.w.fire && in.w.bits.last
189  when (write_req_last) {
190    pending_write_req_valid.last := true.B
191  }.elsewhen (pending_write_req_ready) {
192    pending_write_req_valid.last := false.B
193  }
194  in.aw.ready := !pending_write_req_valid.head || pending_write_req_ready
195  in.w.ready := in.aw.ready || !pending_write_req_valid.last
196
197  // ram is written when write data fire
198  val wdata_cnt = Counter(outer.burstLen)
199  val write_req_addr = Mux(in.aw.fire, in.aw.bits.addr, pending_write_req_bits.addr)
200  val write_req_index = ramIndex(write_req_addr) + wdata_cnt.value
201  when (in.w.fire) {
202    ramHelper.write(
203      addr = write_req_index,
204      data = in.w.bits.data.asTypeOf(Vec(outer.beatBytes, UInt(8.W))),
205      mask = in.w.bits.strb.asBools
206    )
207  }
208  when (write_req_last) {
209    wdata_cnt.reset()
210  }.elsewhen (in.w.fire) {
211    wdata_cnt.inc()
212  }
213
214  // read data response: resp from DRAMsim3; read data and response to in.r
215  // This is the output of the last pipeline before in.r. This is not the pipeline registers.
216  val r_resp = Wire(Decoupled(chiselTypeOf(in.r.bits)))
217
218  val pending_read_resp_valid = RegInit(false.B)
219  val pending_read_resp_id = Reg(UInt(r_resp.bits.id.getWidth.W))
220  val has_read_resp = Wire(Bool())
221  val read_resp_last = r_resp.fire && r_resp.bits.last
222  val read_request_cnt = RegInit(0.U(8.W))
223  val read_have_req_cnt = read_request_cnt =/= 0.U
224  val (read_resp_valid, read_resp_id) = readResponse((!has_read_resp || read_resp_last) && read_have_req_cnt)
225  has_read_resp := (read_resp_valid && !read_resp_last) || pending_read_resp_valid
226  val rdata_cnt = Counter(outer.burstLen)
227  val read_resp_addr = addressMem(r_resp.bits.id) + rdata_cnt.value
228  val read_resp_len = arlenMem(r_resp.bits.id)
229  r_resp.valid := read_resp_valid || pending_read_resp_valid
230  r_resp.bits.id := Mux(pending_read_resp_valid, pending_read_resp_id, read_resp_id)
231  // We cannot get the read data this cycle because the RAM helper has one-cycle latency.
232  r_resp.bits.data := DontCare
233  r_resp.bits.resp := AXI4Parameters.RESP_OKAY
234  r_resp.bits.last := (rdata_cnt.value === read_resp_len)
235
236  // The return values of DPI-C are used to determine whether a request has been made or completed
237  // pending_read_req_ready ---> readRequest()
238  // read_resp_valid <--- readResponse()
239  when (pending_read_req_ready && !read_resp_valid) {
240    read_request_cnt := read_request_cnt + 1.U
241  }.elsewhen (read_resp_valid && !pending_read_req_ready) {
242    read_request_cnt := read_request_cnt - 1.U
243  }
244  when (!pending_read_resp_valid && read_resp_valid && !read_resp_last) {
245    pending_read_resp_valid := true.B
246    pending_read_resp_id := read_resp_id
247  }.elsewhen (pending_read_resp_valid && !read_resp_valid && read_resp_last) {
248    pending_read_resp_valid := false.B
249  }
250  when (read_resp_last) {
251    rdata_cnt.reset()
252  }.elsewhen (r_resp.fire) {
253    rdata_cnt.inc()
254  }
255
256  // `r_pipe`: the extra pipeline registers for the read response `in.r`
257  prefix("r_pipe") {
258    val valid = RegInit(false.B)
259    when (in.r.fire) { valid := false.B }
260    when (r_resp.fire) { valid := true.B }
261    in.r.valid := valid
262    in.r.bits := RegEnable(r_resp.bits, r_resp.fire)
263    r_resp.ready := !valid || in.r.ready
264
265    // the data should be auto-hold
266    in.r.bits.data := ramHelper.readAndHold(read_resp_addr, r_resp.fire).asUInt
267  }
268
269  // write response
270  val pending_write_resp_valid = RegInit(false.B)
271  val pending_write_resp_id = Reg(UInt(in.b.bits.id.getWidth.W))
272  val has_write_resp = Wire(Bool())
273  val write_request_cnt = RegInit(0.U(8.W))
274  val write_have_req_cnt = write_request_cnt =/= 0.U
275  val (write_resp_valid, write_resp_id) = writeResponse((!has_write_resp || in.b.fire) && write_have_req_cnt)
276  has_write_resp := write_resp_valid || pending_write_resp_valid
277  in.b.valid := write_resp_valid || pending_write_resp_valid
278  in.b.bits.id := Mux(pending_write_resp_valid, pending_write_resp_id, write_resp_id)
279  in.b.bits.resp := AXI4Parameters.RESP_OKAY
280
281  // The return values of DPI-C are used to determine whether a request has been made or completed
282  // pending_write_req_ready ---> writeRequest()
283  // write_resp_valid <--- writeResponse()
284  when (pending_write_req_ready && !write_resp_valid) {
285    write_request_cnt := write_request_cnt + 1.U
286  }.elsewhen (write_resp_valid && !pending_write_req_ready) {
287    write_request_cnt := write_request_cnt - 1.U
288  }
289  when (!pending_write_resp_valid && write_resp_valid && !in.b.ready) {
290    pending_write_resp_valid := true.B
291    pending_write_resp_id := write_resp_id
292  }.elsewhen (pending_write_resp_valid && !write_resp_valid && in.b.ready) {
293    pending_write_resp_valid := false.B
294  }
295}
296
297class AXI4Memory
298(
299  val address: Seq[AddressSet],
300  val memByte: Long,
301  val useBlackBox: Boolean = false,
302  val executable: Boolean = true,
303  val beatBytes: Int,
304  val burstLen: Int,
305)(implicit p: Parameters)
306  extends AXI4SlaveModule(address, executable, beatBytes, burstLen)
307{
308  override lazy val module = new AXI4MemoryImp(this)
309}
310
311class AXI4MemoryWrapper (
312  slave: AXI4SlaveNode,
313  memByte: Long,
314  useBlackBox: Boolean = false
315)(implicit p: Parameters) extends AXI4MemorySlave(slave, memByte, useBlackBox) {
316  val ram = LazyModule(new AXI4Memory(
317    slaveParam.address,
318    memByte,
319    useBlackBox,
320    slaveParam.executable,
321    portParam.beatBytes,
322    burstLen
323  ))
324  ram.node := master
325}
326
327abstract class AXI4MemorySlave (
328  slave: AXI4SlaveNode,
329  memByte: Long,
330  useBlackBox: Boolean = false
331)(implicit p: Parameters) extends LazyModule {
332  val master = AXI4MasterNode(List(slave.in.head._2.master))
333
334  val portParam = slave.portParams.head
335  val slaveParam = portParam.slaves.head
336  val burstLen = portParam.maxTransfer / portParam.beatBytes
337
338  val io_axi4 = InModuleBody{ master.makeIOs() }
339
340  lazy val module = new LazyModuleImp(this) { }
341}
342
343object AXI4MemorySlave {
344  def apply(
345    slave: AXI4SlaveNode,
346    memByte: Long,
347    useBlackBox: Boolean = false,
348    dynamicLatency: Boolean = false
349  )(implicit p: Parameters): AXI4MemorySlave = {
350    val memory = if (dynamicLatency) {
351      LazyModule(new AXI4MemoryWrapper(slave, memByte, useBlackBox))
352    } else {
353      LazyModule(new AXI4RAMWrapper(slave, memByte, useBlackBox))
354    }
355    memory
356  }
357}
358