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