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