xref: /XiangShan/src/main/scala/device/AXI4Memory.scala (revision 04ac809e6df52b85378bdf2065d2e91e490dc351)
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 chipsalliance.rocketchip.config.Parameters
19import chisel3._
20import chisel3.experimental.ExtModule
21import chisel3.util._
22import freechips.rocketchip.amba.axi4.{AXI4MasterNode, AXI4Parameters, AXI4SlaveNode}
23import freechips.rocketchip.diplomacy.{AddressSet, InModuleBody, LazyModule, LazyModuleImp}
24import utils._
25
26class MemoryRWHelper extends ExtModule with HasExtModuleInline {
27  val DataBits = 64
28
29  val clock = IO(Input(Clock()))
30  val reset = IO(Input(Reset()))
31  val ren   = IO(Input(Bool()))
32  val rIdx  = IO(Input(UInt(DataBits.W)))
33  val rdata = IO(Output(UInt(DataBits.W)))
34  val wen   = IO(Input(Bool()))
35  val wIdx  = IO(Input(UInt(DataBits.W)))
36  val wdata = IO(Input(UInt(DataBits.W)))
37  val wmask = IO(Input(UInt(DataBits.W)))
38
39  def read(enable: Bool, address: UInt): UInt = {
40    ren := enable
41    rIdx := address
42    rdata
43  }
44  def write(enable: Bool, address: UInt, data: UInt, mask: UInt): Unit = {
45    wen := enable
46    wIdx := address
47    wdata := data
48    wmask := mask
49  }
50
51  val verilogLines = Seq(
52    "module MemoryRWHelper(",
53    "  input         clock,",
54    "  input         reset,",
55    "  input         ren,",
56    "  input  [63:0] rIdx,",
57    "  output [63:0] rdata,",
58    "  input  [63:0] wIdx,",
59    "  input  [63:0] wdata,",
60    "  input  [63:0] wmask,",
61    "  input         wen",
62    ");",
63    "",
64    "  assign rdata = (!reset && ren) ? ram_read_helper(1, rIdx) : 64'b0;",
65    "",
66    "  always @(posedge clock) begin",
67    "    if (!reset && wen) begin",
68    "      ram_write_helper(wIdx, wdata, wmask, 1);",
69    "    end",
70    "  end",
71    "",
72    "endmodule"
73  )
74  setInline(s"$desiredName.v", verilogLines.mkString("\n"))
75}
76
77object MemoryRWHelper {
78  def apply(clock: Clock, reset: Reset): MemoryRWHelper = {
79    val helper = Module(new MemoryRWHelper)
80    helper.clock := clock
81    helper.reset := reset
82    helper
83  }
84}
85
86class MemoryRequestHelper(requestType: Int)
87  extends ExtModule(Map("REQUEST_TYPE" -> requestType))
88  with HasExtModuleInline
89{
90  val clock     = IO(Input(Clock()))
91  val reset     = IO(Input(Reset()))
92  val io = IO(new Bundle {
93    val req = Flipped(ValidIO(new Bundle {
94      val addr = UInt(64.W)
95      val id   = UInt(32.W)
96    }))
97    val response = Output(Bool())
98  })
99
100  val verilogLines = Seq(
101    "import \"DPI-C\" function bit memory_request (",
102    "  input longint address,",
103    "  input int id,",
104    "  input bit isWrite",
105    ");",
106    "",
107    "module MemoryRequestHelper #(",
108    "  parameter REQUEST_TYPE",
109    ")(",
110    "  input             clock,",
111    "  input             reset,",
112    "  input             io_req_valid,",
113    "  input      [63:0] io_req_bits_addr,",
114    "  input      [31:0] io_req_bits_id,",
115    "  output reg        io_response",
116    ");",
117    "",
118    "always @(posedge clock or posedge reset) begin",
119    "  if (reset) begin",
120    "    io_response <= 1'b0;",
121    "  end",
122    "  else if (io_req_valid) begin",
123    "    io_response <= memory_request(io_req_bits_addr, io_req_bits_id, REQUEST_TYPE);",
124    "  end" +
125    "  else begin",
126    "    io_response <= 1'b0;",
127    "  end",
128    "end",
129    "",
130    "endmodule"
131  )
132  setInline(s"$desiredName.v", verilogLines.mkString("\n"))
133}
134
135class MemoryResponseHelper(requestType: Int)
136  extends ExtModule(Map("REQUEST_TYPE" -> requestType))
137  with HasExtModuleInline
138{
139  val clock    = IO(Input(Clock()))
140  val reset    = IO(Input(Reset()))
141  val enable   = IO(Input(Bool()))
142  val response = IO(Output(UInt(64.W)))
143
144  val verilogLines = Seq(
145    "import \"DPI-C\" function longint memory_response (",
146    "  input bit isWrite",
147    ");",
148    "",
149    "module MemoryResponseHelper #(",
150    "  parameter REQUEST_TYPE",
151    ")(",
152    "  input             clock,",
153    "  input             reset,",
154    "  input             enable,",
155    "  output reg [63:0] response",
156    ");",
157    "",
158    "always @(posedge clock or posedge reset) begin",
159    "  if (reset) begin",
160    "    response <= 64'b0;",
161    "  end",
162    "  else if (!reset && enable) begin",
163    "    response <= memory_response(REQUEST_TYPE);",
164    "  end",
165    " else begin",
166    "    response <= 64'b0;",
167    "  end",
168    "end",
169    "",
170    "endmodule"
171  )
172  setInline(s"$desiredName.v", verilogLines.mkString("\n"))
173}
174
175trait MemoryHelper { this: Module =>
176  private def requestType(isWrite: Boolean): Int = if (isWrite) 1 else 0
177  private def request(valid: Bool, addr: UInt, id: UInt, isWrite: Boolean): Bool = {
178    val helper = Module(new MemoryRequestHelper(requestType(isWrite)))
179    helper.clock := clock
180    helper.reset := reset
181    helper.io.req.valid := valid
182    helper.io.req.bits.addr := addr
183    helper.io.req.bits.id := id
184    helper.io.response
185  }
186  protected def readRequest(valid: Bool, addr: UInt, id: UInt): Bool =
187    request(valid, addr, id, false)
188  protected def writeRequest(valid: Bool, addr: UInt, id: UInt): Bool =
189    request(valid, addr, id, true)
190  private def response(enable: Bool, isWrite: Boolean): (Bool, UInt) = {
191    val helper = Module(new MemoryResponseHelper(requestType(isWrite)))
192    helper.clock := clock
193    helper.reset := reset
194    helper.enable := enable
195    (helper.response(32), helper.response(31, 0))
196  }
197  protected def readResponse(enable: Bool): (Bool, UInt) =
198    response(enable, false)
199  protected def writeResponse(enable: Bool): (Bool, UInt) =
200    response(enable, true)
201}
202
203class AXI4MemoryImp[T <: Data](outer: AXI4Memory) extends AXI4SlaveModuleImp(outer) with MemoryHelper {
204  val ramWidth = 8
205  val ramSplit = outer.beatBytes / ramWidth
206  val ramBaseAddr = outer.address.head.base
207  val ramOffsetBits = log2Ceil(outer.memByte)
208  def ramIndex(addr: UInt) = ((addr - ramBaseAddr.U)(ramOffsetBits - 1, 0) >> log2Ceil(ramWidth)).asUInt
209  val ramHelper = Seq.fill(ramSplit)(MemoryRWHelper(clock, reset))
210
211  val numOutstanding = 1 << in.ar.bits.id.getWidth
212  val addressMem = Mem(numOutstanding, UInt((in.ar.bits.addr.getWidth - log2Ceil(ramWidth)).W))
213  val arlenMem = Mem(numOutstanding, UInt(in.ar.bits.len.getWidth.W))
214
215  // accept a read request and send it to the external model
216  val pending_read_req_valid = RegInit(false.B)
217  val pending_read_req_bits  = RegEnable(in.ar.bits, in.ar.fire)
218  val pending_read_req_ready = Wire(Bool())
219  val pending_read_need_req = pending_read_req_valid && !pending_read_req_ready
220  val read_req_valid = pending_read_need_req || in.ar.valid
221  val read_req_bits  = Mux(pending_read_need_req, pending_read_req_bits, in.ar.bits)
222  pending_read_req_ready := readRequest(read_req_valid, read_req_bits.addr, read_req_bits.id)
223
224  when (in.ar.fire) {
225    pending_read_req_valid := true.B
226    addressMem.write(read_req_bits.id, ramIndex(read_req_bits.addr))
227    arlenMem.write(read_req_bits.id, read_req_bits.len)
228  }.elsewhen (pending_read_req_ready) {
229    pending_read_req_valid := false.B
230  }
231  in.ar.ready := !pending_read_req_valid || pending_read_req_ready
232
233  // accept a write request (including address and data) and send it to the external model
234  val pending_write_req_valid = RegInit(VecInit.fill(2)(false.B))
235  val pending_write_req_bits  = RegEnable(in.aw.bits, in.aw.fire)
236  val pending_write_req_data  = RegEnable(in.w.bits, in.w.fire)
237  XSError(in.aw.fire && in.aw.bits.len === 0.U, "data must have more than one beat now")
238  val pending_write_req_ready = Wire(Bool())
239  val pending_write_need_req = pending_write_req_valid.last && !pending_write_req_ready
240  val write_req_valid = pending_write_req_valid.head && (pending_write_need_req || in.w.valid && in.w.bits.last)
241  pending_write_req_ready := writeRequest(write_req_valid, pending_write_req_bits.addr, pending_write_req_bits.id)
242
243  when (in.aw.fire) {
244    pending_write_req_valid.head := true.B
245  }.elsewhen (pending_write_req_ready) {
246    pending_write_req_valid.head := false.B
247  }
248  val write_req_last = in.w.fire && in.w.bits.last
249  when (write_req_last) {
250    pending_write_req_valid.last := true.B
251  }.elsewhen (pending_write_req_ready) {
252    pending_write_req_valid.last := false.B
253  }
254  in.aw.ready := !pending_write_req_valid.head || pending_write_req_ready
255  in.w.ready := in.aw.ready || !pending_write_req_valid.last
256
257  // ram is written when write data fire
258  val wdata_cnt = Counter(outer.burstLen)
259  val write_req_addr = Mux(in.aw.fire, in.aw.bits.addr, pending_write_req_bits.addr)
260  val write_req_index = ramIndex(write_req_addr) + Cat(wdata_cnt.value, 0.U(log2Ceil(ramSplit).W))
261  for ((ram, i) <- ramHelper.zipWithIndex) {
262    val enable = in.w.fire
263    val address = write_req_index + i.U
264    val data = in.w.bits.data(ramWidth * 8 * i + 63, ramWidth * 8 * i)
265    val mask = MaskExpand(in.w.bits.strb(i * 8 + 7, i * 8))
266    ram.write(enable, address, data, mask)
267  }
268  when (write_req_last) {
269    wdata_cnt.reset()
270  }.elsewhen (in.w.fire) {
271    wdata_cnt.inc()
272  }
273
274  // read data response
275  val pending_read_resp_valid = RegInit(false.B)
276  val pending_read_resp_id = Reg(UInt(in.r.bits.id.getWidth.W))
277  val has_read_resp = Wire(Bool())
278  val read_resp_last = in.r.fire && in.r.bits.last
279  val (read_resp_valid, read_resp_id) = readResponse(!has_read_resp || read_resp_last)
280  has_read_resp := (read_resp_valid && !read_resp_last) || pending_read_resp_valid
281  val rdata_cnt = Counter(outer.burstLen)
282  val read_resp_addr = addressMem(in.r.bits.id) + Cat(rdata_cnt.value, 0.U(log2Ceil(ramSplit).W))
283  val read_resp_len = arlenMem(in.r.bits.id)
284  in.r.valid := read_resp_valid || pending_read_resp_valid
285  in.r.bits.id := Mux(pending_read_resp_valid, pending_read_resp_id, read_resp_id)
286  val rdata = ramHelper.zipWithIndex.map{ case (ram, i) => ram.read(in.r.valid, read_resp_addr + i.U) }
287  in.r.bits.data := VecInit(rdata).asUInt
288  in.r.bits.resp := AXI4Parameters.RESP_OKAY
289  in.r.bits.last := (rdata_cnt.value === read_resp_len)
290
291  when (!pending_read_resp_valid && read_resp_valid && !read_resp_last) {
292    pending_read_resp_valid := true.B
293    pending_read_resp_id := read_resp_id
294  }.elsewhen (pending_read_resp_valid && !read_resp_valid && read_resp_last) {
295    pending_read_resp_valid := false.B
296  }
297  when (read_resp_last) {
298    rdata_cnt.reset()
299  }.elsewhen (in.r.fire) {
300    rdata_cnt.inc()
301  }
302
303  // write response
304  val pending_write_resp_valid = RegInit(false.B)
305  val pending_write_resp_id = Reg(UInt(in.b.bits.id.getWidth.W))
306  val has_write_resp = Wire(Bool())
307  val (write_resp_valid, write_resp_id) = writeResponse(!has_write_resp || in.b.fire)
308  has_write_resp := write_resp_valid || pending_write_resp_valid
309  in.b.valid := write_resp_valid || pending_write_resp_valid
310  in.b.bits.id := Mux(pending_write_resp_valid, pending_write_resp_id, write_resp_id)
311  in.b.bits.resp := AXI4Parameters.RESP_OKAY
312
313  when (!pending_write_resp_valid && write_resp_valid && !in.b.ready) {
314    pending_write_resp_valid := true.B
315    pending_write_resp_id := write_resp_id
316  }.elsewhen (pending_write_resp_valid && !write_resp_valid && in.b.ready) {
317    pending_write_resp_valid := false.B
318  }
319}
320
321class AXI4Memory
322(
323  val address: Seq[AddressSet],
324  val memByte: Long,
325  val useBlackBox: Boolean = false,
326  val executable: Boolean = true,
327  val beatBytes: Int,
328  val burstLen: Int,
329)(implicit p: Parameters)
330  extends AXI4SlaveModule(address, executable, beatBytes, burstLen)
331{
332  override lazy val module = new AXI4MemoryImp(this)
333}
334
335class AXI4MemoryWrapper (
336  slave: AXI4SlaveNode,
337  memByte: Long,
338  useBlackBox: Boolean = false
339)(implicit p: Parameters) extends AXI4MemorySlave(slave, memByte, useBlackBox) {
340  val ram = LazyModule(new AXI4Memory(
341    slaveParam.address,
342    memByte,
343    useBlackBox,
344    slaveParam.executable,
345    portParam.beatBytes,
346    burstLen
347  ))
348  ram.node := master
349}
350
351abstract class AXI4MemorySlave (
352  slave: AXI4SlaveNode,
353  memByte: Long,
354  useBlackBox: Boolean = false
355)(implicit p: Parameters) extends LazyModule {
356  val master = AXI4MasterNode(List(slave.in.head._2.master))
357
358  val portParam = slave.portParams.head
359  val slaveParam = portParam.slaves.head
360  val burstLen = portParam.maxTransfer / portParam.beatBytes
361
362  val io_axi4 = InModuleBody{ master.makeIOs() }
363
364  lazy val module = new LazyModuleImp(this) { }
365}
366
367object AXI4MemorySlave {
368  def apply(
369    slave: AXI4SlaveNode,
370    memByte: Long,
371    useBlackBox: Boolean = false,
372    dynamicLatency: Boolean = false
373  )(implicit p: Parameters): AXI4MemorySlave = {
374    val memory = if (dynamicLatency) {
375      LazyModule(new AXI4MemoryWrapper(slave, memByte, useBlackBox))
376    } else {
377      LazyModule(new AXI4RAMWrapper(slave, memByte, useBlackBox))
378    }
379    memory
380  }
381}
382