171784e68SYinan Xu/*************************************************************************************** 271784e68SYinan Xu * Copyright (c) 2020-2022 Institute of Computing Technology, Chinese Academy of Sciences 371784e68SYinan Xu * 471784e68SYinan Xu * XiangShan is licensed under Mulan PSL v2. 571784e68SYinan Xu * You can use this software according to the terms and conditions of the Mulan PSL v2. 671784e68SYinan Xu * You may obtain a copy of Mulan PSL v2 at: 771784e68SYinan Xu * http://license.coscl.org.cn/MulanPSL2 871784e68SYinan Xu * 971784e68SYinan Xu * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 1071784e68SYinan Xu * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 1171784e68SYinan Xu * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 1271784e68SYinan Xu * 1371784e68SYinan Xu * See the Mulan PSL v2 for more details. 1471784e68SYinan Xu ***************************************************************************************/ 1571784e68SYinan Xu 1671784e68SYinan Xupackage device 1771784e68SYinan Xu 188891a219SYinan Xuimport org.chipsalliance.cde.config.Parameters 1971784e68SYinan Xuimport chisel3._ 209f659d72SKamimiaoimport chisel3.experimental.{ExtModule, prefix} 2171784e68SYinan Xuimport chisel3.util._ 229f659d72SKamimiaoimport difftest.common.DifftestMem 2371784e68SYinan Xuimport freechips.rocketchip.amba.axi4.{AXI4MasterNode, AXI4Parameters, AXI4SlaveNode} 2471784e68SYinan Xuimport freechips.rocketchip.diplomacy.{AddressSet, InModuleBody, LazyModule, LazyModuleImp} 25*bb2f3f51STang Haojinimport utility._ 2671784e68SYinan Xu 2771784e68SYinan Xuclass MemoryRequestHelper(requestType: Int) 2871784e68SYinan Xu extends ExtModule(Map("REQUEST_TYPE" -> requestType)) 2971784e68SYinan Xu with HasExtModuleInline 3071784e68SYinan Xu{ 3171784e68SYinan Xu val clock = IO(Input(Clock())) 3271784e68SYinan Xu val reset = IO(Input(Reset())) 3371784e68SYinan Xu val io = IO(new Bundle { 3471784e68SYinan Xu val req = Flipped(ValidIO(new Bundle { 3571784e68SYinan Xu val addr = UInt(64.W) 3671784e68SYinan Xu val id = UInt(32.W) 3771784e68SYinan Xu })) 3871784e68SYinan Xu val response = Output(Bool()) 3971784e68SYinan Xu }) 4071784e68SYinan Xu 4171784e68SYinan Xu val verilogLines = Seq( 4271784e68SYinan Xu "import \"DPI-C\" function bit memory_request (", 4371784e68SYinan Xu " input longint address,", 4471784e68SYinan Xu " input int id,", 4571784e68SYinan Xu " input bit isWrite", 4671784e68SYinan Xu ");", 4771784e68SYinan Xu "", 4871784e68SYinan Xu "module MemoryRequestHelper #(", 4971784e68SYinan Xu " parameter REQUEST_TYPE", 5071784e68SYinan Xu ")(", 5171784e68SYinan Xu " input clock,", 5271784e68SYinan Xu " input reset,", 5371784e68SYinan Xu " input io_req_valid,", 5471784e68SYinan Xu " input [63:0] io_req_bits_addr,", 5571784e68SYinan Xu " input [31:0] io_req_bits_id,", 5671784e68SYinan Xu " output reg io_response", 5771784e68SYinan Xu ");", 5871784e68SYinan Xu "", 5971784e68SYinan Xu "always @(posedge clock or posedge reset) begin", 6071784e68SYinan Xu " if (reset) begin", 6171784e68SYinan Xu " io_response <= 1'b0;", 6271784e68SYinan Xu " end", 6371784e68SYinan Xu " else if (io_req_valid) begin", 6471784e68SYinan Xu " io_response <= memory_request(io_req_bits_addr, io_req_bits_id, REQUEST_TYPE);", 6571784e68SYinan Xu " end" + 6671784e68SYinan Xu " else begin", 6771784e68SYinan Xu " io_response <= 1'b0;", 6871784e68SYinan Xu " end", 6971784e68SYinan Xu "end", 7071784e68SYinan Xu "", 7171784e68SYinan Xu "endmodule" 7271784e68SYinan Xu ) 7371784e68SYinan Xu setInline(s"$desiredName.v", verilogLines.mkString("\n")) 7471784e68SYinan Xu} 7571784e68SYinan Xu 7671784e68SYinan Xuclass MemoryResponseHelper(requestType: Int) 7771784e68SYinan Xu extends ExtModule(Map("REQUEST_TYPE" -> requestType)) 7871784e68SYinan Xu with HasExtModuleInline 7971784e68SYinan Xu{ 8071784e68SYinan Xu val clock = IO(Input(Clock())) 8171784e68SYinan Xu val reset = IO(Input(Reset())) 8271784e68SYinan Xu val enable = IO(Input(Bool())) 8371784e68SYinan Xu val response = IO(Output(UInt(64.W))) 8471784e68SYinan Xu 8571784e68SYinan Xu val verilogLines = Seq( 8671784e68SYinan Xu "import \"DPI-C\" function longint memory_response (", 8771784e68SYinan Xu " input bit isWrite", 8871784e68SYinan Xu ");", 8971784e68SYinan Xu "", 9071784e68SYinan Xu "module MemoryResponseHelper #(", 9171784e68SYinan Xu " parameter REQUEST_TYPE", 9271784e68SYinan Xu ")(", 9371784e68SYinan Xu " input clock,", 9471784e68SYinan Xu " input reset,", 9571784e68SYinan Xu " input enable,", 9671784e68SYinan Xu " output reg [63:0] response", 9771784e68SYinan Xu ");", 9871784e68SYinan Xu "", 9971784e68SYinan Xu "always @(posedge clock or posedge reset) begin", 10071784e68SYinan Xu " if (reset) begin", 10171784e68SYinan Xu " response <= 64'b0;", 10271784e68SYinan Xu " end", 10371784e68SYinan Xu " else if (!reset && enable) begin", 10471784e68SYinan Xu " response <= memory_response(REQUEST_TYPE);", 10571784e68SYinan Xu " end", 10671784e68SYinan Xu " else begin", 10771784e68SYinan Xu " response <= 64'b0;", 10871784e68SYinan Xu " end", 10971784e68SYinan Xu "end", 11071784e68SYinan Xu "", 11171784e68SYinan Xu "endmodule" 11271784e68SYinan Xu ) 11371784e68SYinan Xu setInline(s"$desiredName.v", verilogLines.mkString("\n")) 11471784e68SYinan Xu} 11571784e68SYinan Xu 11671784e68SYinan Xutrait MemoryHelper { this: Module => 11771784e68SYinan Xu private def requestType(isWrite: Boolean): Int = if (isWrite) 1 else 0 11871784e68SYinan Xu private def request(valid: Bool, addr: UInt, id: UInt, isWrite: Boolean): Bool = { 11971784e68SYinan Xu val helper = Module(new MemoryRequestHelper(requestType(isWrite))) 12071784e68SYinan Xu helper.clock := clock 12171784e68SYinan Xu helper.reset := reset 12271784e68SYinan Xu helper.io.req.valid := valid 12371784e68SYinan Xu helper.io.req.bits.addr := addr 12471784e68SYinan Xu helper.io.req.bits.id := id 12571784e68SYinan Xu helper.io.response 12671784e68SYinan Xu } 12771784e68SYinan Xu protected def readRequest(valid: Bool, addr: UInt, id: UInt): Bool = 12871784e68SYinan Xu request(valid, addr, id, false) 12971784e68SYinan Xu protected def writeRequest(valid: Bool, addr: UInt, id: UInt): Bool = 13071784e68SYinan Xu request(valid, addr, id, true) 13171784e68SYinan Xu private def response(enable: Bool, isWrite: Boolean): (Bool, UInt) = { 13271784e68SYinan Xu val helper = Module(new MemoryResponseHelper(requestType(isWrite))) 13371784e68SYinan Xu helper.clock := clock 13471784e68SYinan Xu helper.reset := reset 13571784e68SYinan Xu helper.enable := enable 13671784e68SYinan Xu (helper.response(32), helper.response(31, 0)) 13771784e68SYinan Xu } 13871784e68SYinan Xu protected def readResponse(enable: Bool): (Bool, UInt) = 13971784e68SYinan Xu response(enable, false) 14071784e68SYinan Xu protected def writeResponse(enable: Bool): (Bool, UInt) = 14171784e68SYinan Xu response(enable, true) 14271784e68SYinan Xu} 14371784e68SYinan Xu 14471784e68SYinan Xuclass AXI4MemoryImp[T <: Data](outer: AXI4Memory) extends AXI4SlaveModuleImp(outer) with MemoryHelper { 14571784e68SYinan Xu val ramBaseAddr = outer.address.head.base 1469f659d72SKamimiao val (ramIndexBits, ramOffsetBits) = (log2Ceil(outer.beatBytes), log2Ceil(outer.memByte)) 1479f659d72SKamimiao def ramIndex(addr: UInt) = ((addr - ramBaseAddr.U)(ramOffsetBits - 1, 0) >> ramIndexBits).asUInt 1489f659d72SKamimiao val ramHelper = DifftestMem(outer.memByte, outer.beatBytes, 8, singlePort = false) 14971784e68SYinan Xu 15071784e68SYinan Xu val numOutstanding = 1 << in.ar.bits.id.getWidth 1519f659d72SKamimiao // Note: we are using in.ar.bits.addr.getWidth insead of ramOffsetBits here. 1529f659d72SKamimiao // Why: the CPU may access out-of-range addresses. Let the RAM helper deal with it. 1539f659d72SKamimiao val addressMem = Mem(numOutstanding, UInt((in.ar.bits.addr.getWidth - ramIndexBits).W)) 15471784e68SYinan Xu val arlenMem = Mem(numOutstanding, UInt(in.ar.bits.len.getWidth.W)) 15571784e68SYinan Xu 15671784e68SYinan Xu // accept a read request and send it to the external model 15771784e68SYinan Xu val pending_read_req_valid = RegInit(false.B) 15871784e68SYinan Xu val pending_read_req_bits = RegEnable(in.ar.bits, in.ar.fire) 15971784e68SYinan Xu val pending_read_req_ready = Wire(Bool()) 16071784e68SYinan Xu val pending_read_need_req = pending_read_req_valid && !pending_read_req_ready 16171784e68SYinan Xu val read_req_valid = pending_read_need_req || in.ar.valid 16271784e68SYinan Xu val read_req_bits = Mux(pending_read_need_req, pending_read_req_bits, in.ar.bits) 16371784e68SYinan Xu pending_read_req_ready := readRequest(read_req_valid, read_req_bits.addr, read_req_bits.id) 16471784e68SYinan Xu 16571784e68SYinan Xu when (in.ar.fire) { 16671784e68SYinan Xu pending_read_req_valid := true.B 16771784e68SYinan Xu addressMem.write(read_req_bits.id, ramIndex(read_req_bits.addr)) 16871784e68SYinan Xu arlenMem.write(read_req_bits.id, read_req_bits.len) 16971784e68SYinan Xu }.elsewhen (pending_read_req_ready) { 17071784e68SYinan Xu pending_read_req_valid := false.B 17171784e68SYinan Xu } 17271784e68SYinan Xu in.ar.ready := !pending_read_req_valid || pending_read_req_ready 17371784e68SYinan Xu 17471784e68SYinan Xu // accept a write request (including address and data) and send it to the external model 17571784e68SYinan Xu val pending_write_req_valid = RegInit(VecInit.fill(2)(false.B)) 17671784e68SYinan Xu val pending_write_req_bits = RegEnable(in.aw.bits, in.aw.fire) 17771784e68SYinan Xu val pending_write_req_data = RegEnable(in.w.bits, in.w.fire) 17871784e68SYinan Xu XSError(in.aw.fire && in.aw.bits.len === 0.U, "data must have more than one beat now") 17971784e68SYinan Xu val pending_write_req_ready = Wire(Bool()) 18071784e68SYinan Xu val pending_write_need_req = pending_write_req_valid.last && !pending_write_req_ready 18171784e68SYinan Xu val write_req_valid = pending_write_req_valid.head && (pending_write_need_req || in.w.valid && in.w.bits.last) 18271784e68SYinan Xu pending_write_req_ready := writeRequest(write_req_valid, pending_write_req_bits.addr, pending_write_req_bits.id) 18371784e68SYinan Xu 18471784e68SYinan Xu when (in.aw.fire) { 18571784e68SYinan Xu pending_write_req_valid.head := true.B 18671784e68SYinan Xu }.elsewhen (pending_write_req_ready) { 18771784e68SYinan Xu pending_write_req_valid.head := false.B 18871784e68SYinan Xu } 18971784e68SYinan Xu val write_req_last = in.w.fire && in.w.bits.last 19071784e68SYinan Xu when (write_req_last) { 19171784e68SYinan Xu pending_write_req_valid.last := true.B 19271784e68SYinan Xu }.elsewhen (pending_write_req_ready) { 19371784e68SYinan Xu pending_write_req_valid.last := false.B 19471784e68SYinan Xu } 19571784e68SYinan Xu in.aw.ready := !pending_write_req_valid.head || pending_write_req_ready 19671784e68SYinan Xu in.w.ready := in.aw.ready || !pending_write_req_valid.last 19771784e68SYinan Xu 19871784e68SYinan Xu // ram is written when write data fire 19971784e68SYinan Xu val wdata_cnt = Counter(outer.burstLen) 20071784e68SYinan Xu val write_req_addr = Mux(in.aw.fire, in.aw.bits.addr, pending_write_req_bits.addr) 2019f659d72SKamimiao val write_req_index = ramIndex(write_req_addr) + wdata_cnt.value 2029f659d72SKamimiao when (in.w.fire) { 2039f659d72SKamimiao ramHelper.write( 2049f659d72SKamimiao addr = write_req_index, 2059f659d72SKamimiao data = in.w.bits.data.asTypeOf(Vec(outer.beatBytes, UInt(8.W))), 2069f659d72SKamimiao mask = in.w.bits.strb.asBools 2079f659d72SKamimiao ) 20871784e68SYinan Xu } 20971784e68SYinan Xu when (write_req_last) { 21071784e68SYinan Xu wdata_cnt.reset() 21171784e68SYinan Xu }.elsewhen (in.w.fire) { 21271784e68SYinan Xu wdata_cnt.inc() 21371784e68SYinan Xu } 21471784e68SYinan Xu 2159f659d72SKamimiao // read data response: resp from DRAMsim3; read data and response to in.r 2169f659d72SKamimiao // This is the output of the last pipeline before in.r. This is not the pipeline registers. 2179f659d72SKamimiao val r_resp = Wire(Decoupled(chiselTypeOf(in.r.bits))) 2189f659d72SKamimiao 21971784e68SYinan Xu val pending_read_resp_valid = RegInit(false.B) 2209f659d72SKamimiao val pending_read_resp_id = Reg(UInt(r_resp.bits.id.getWidth.W)) 22171784e68SYinan Xu val has_read_resp = Wire(Bool()) 2229f659d72SKamimiao val read_resp_last = r_resp.fire && r_resp.bits.last 223021511b6SKamimiao val read_request_cnt = RegInit(0.U(8.W)) 224021511b6SKamimiao val read_have_req_cnt = read_request_cnt =/= 0.U 225021511b6SKamimiao val (read_resp_valid, read_resp_id) = readResponse((!has_read_resp || read_resp_last) && read_have_req_cnt) 22671784e68SYinan Xu has_read_resp := (read_resp_valid && !read_resp_last) || pending_read_resp_valid 22771784e68SYinan Xu val rdata_cnt = Counter(outer.burstLen) 2289f659d72SKamimiao val read_resp_addr = addressMem(r_resp.bits.id) + rdata_cnt.value 2299f659d72SKamimiao val read_resp_len = arlenMem(r_resp.bits.id) 2309f659d72SKamimiao r_resp.valid := read_resp_valid || pending_read_resp_valid 2319f659d72SKamimiao r_resp.bits.id := Mux(pending_read_resp_valid, pending_read_resp_id, read_resp_id) 2329f659d72SKamimiao // We cannot get the read data this cycle because the RAM helper has one-cycle latency. 2339f659d72SKamimiao r_resp.bits.data := DontCare 2349f659d72SKamimiao r_resp.bits.resp := AXI4Parameters.RESP_OKAY 2359f659d72SKamimiao r_resp.bits.last := (rdata_cnt.value === read_resp_len) 23671784e68SYinan Xu 237021511b6SKamimiao // The return values of DPI-C are used to determine whether a request has been made or completed 238021511b6SKamimiao // pending_read_req_ready ---> readRequest() 239021511b6SKamimiao // read_resp_valid <--- readResponse() 240021511b6SKamimiao when (pending_read_req_ready && !read_resp_valid) { 241021511b6SKamimiao read_request_cnt := read_request_cnt + 1.U 242021511b6SKamimiao }.elsewhen (read_resp_valid && !pending_read_req_ready) { 243021511b6SKamimiao read_request_cnt := read_request_cnt - 1.U 244021511b6SKamimiao } 24571784e68SYinan Xu when (!pending_read_resp_valid && read_resp_valid && !read_resp_last) { 24671784e68SYinan Xu pending_read_resp_valid := true.B 24771784e68SYinan Xu pending_read_resp_id := read_resp_id 24871784e68SYinan Xu }.elsewhen (pending_read_resp_valid && !read_resp_valid && read_resp_last) { 24971784e68SYinan Xu pending_read_resp_valid := false.B 25071784e68SYinan Xu } 25171784e68SYinan Xu when (read_resp_last) { 25271784e68SYinan Xu rdata_cnt.reset() 2539f659d72SKamimiao }.elsewhen (r_resp.fire) { 25471784e68SYinan Xu rdata_cnt.inc() 25571784e68SYinan Xu } 25671784e68SYinan Xu 2579f659d72SKamimiao // `r_pipe`: the extra pipeline registers for the read response `in.r` 2589f659d72SKamimiao prefix("r_pipe") { 2599f659d72SKamimiao val valid = RegInit(false.B) 2609f659d72SKamimiao when (r_resp.valid && in.r.ready) { 2619f659d72SKamimiao valid := true.B 2629f659d72SKamimiao }.elsewhen (in.r.ready) { 2639f659d72SKamimiao valid := false.B 2649f659d72SKamimiao } 2659f659d72SKamimiao in.r.valid := valid 2669f659d72SKamimiao in.r.bits := RegEnable(r_resp.bits, r_resp.valid && in.r.ready) 2679f659d72SKamimiao r_resp.ready := !valid || in.r.ready 2689f659d72SKamimiao 2699f659d72SKamimiao // the data should be auto-hold 2709f659d72SKamimiao in.r.bits.data := ramHelper.readAndHold(read_resp_addr, r_resp.fire).asUInt 2719f659d72SKamimiao } 2729f659d72SKamimiao 27371784e68SYinan Xu // write response 27471784e68SYinan Xu val pending_write_resp_valid = RegInit(false.B) 27571784e68SYinan Xu val pending_write_resp_id = Reg(UInt(in.b.bits.id.getWidth.W)) 27671784e68SYinan Xu val has_write_resp = Wire(Bool()) 277021511b6SKamimiao val write_request_cnt = RegInit(0.U(8.W)) 278021511b6SKamimiao val write_have_req_cnt = write_request_cnt =/= 0.U 279021511b6SKamimiao val (write_resp_valid, write_resp_id) = writeResponse((!has_write_resp || in.b.fire) && write_have_req_cnt) 28004ac809eSYinan Xu has_write_resp := write_resp_valid || pending_write_resp_valid 28171784e68SYinan Xu in.b.valid := write_resp_valid || pending_write_resp_valid 28271784e68SYinan Xu in.b.bits.id := Mux(pending_write_resp_valid, pending_write_resp_id, write_resp_id) 28371784e68SYinan Xu in.b.bits.resp := AXI4Parameters.RESP_OKAY 28471784e68SYinan Xu 285021511b6SKamimiao // The return values of DPI-C are used to determine whether a request has been made or completed 286021511b6SKamimiao // pending_write_req_ready ---> writeRequest() 287021511b6SKamimiao // write_resp_valid <--- writeResponse() 288021511b6SKamimiao when (pending_write_req_ready && !write_resp_valid) { 289021511b6SKamimiao write_request_cnt := write_request_cnt + 1.U 290021511b6SKamimiao }.elsewhen (write_resp_valid && !pending_write_req_ready) { 291021511b6SKamimiao write_request_cnt := write_request_cnt - 1.U 292021511b6SKamimiao } 29371784e68SYinan Xu when (!pending_write_resp_valid && write_resp_valid && !in.b.ready) { 29471784e68SYinan Xu pending_write_resp_valid := true.B 29504ac809eSYinan Xu pending_write_resp_id := write_resp_id 29671784e68SYinan Xu }.elsewhen (pending_write_resp_valid && !write_resp_valid && in.b.ready) { 29771784e68SYinan Xu pending_write_resp_valid := false.B 29871784e68SYinan Xu } 29971784e68SYinan Xu} 30071784e68SYinan Xu 30171784e68SYinan Xuclass AXI4Memory 30271784e68SYinan Xu( 30371784e68SYinan Xu val address: Seq[AddressSet], 30471784e68SYinan Xu val memByte: Long, 30571784e68SYinan Xu val useBlackBox: Boolean = false, 30671784e68SYinan Xu val executable: Boolean = true, 30771784e68SYinan Xu val beatBytes: Int, 30871784e68SYinan Xu val burstLen: Int, 30971784e68SYinan Xu)(implicit p: Parameters) 31071784e68SYinan Xu extends AXI4SlaveModule(address, executable, beatBytes, burstLen) 31171784e68SYinan Xu{ 31271784e68SYinan Xu override lazy val module = new AXI4MemoryImp(this) 31371784e68SYinan Xu} 31471784e68SYinan Xu 31571784e68SYinan Xuclass AXI4MemoryWrapper ( 31671784e68SYinan Xu slave: AXI4SlaveNode, 31771784e68SYinan Xu memByte: Long, 31871784e68SYinan Xu useBlackBox: Boolean = false 31971784e68SYinan Xu)(implicit p: Parameters) extends AXI4MemorySlave(slave, memByte, useBlackBox) { 32071784e68SYinan Xu val ram = LazyModule(new AXI4Memory( 32171784e68SYinan Xu slaveParam.address, 32271784e68SYinan Xu memByte, 32371784e68SYinan Xu useBlackBox, 32471784e68SYinan Xu slaveParam.executable, 32571784e68SYinan Xu portParam.beatBytes, 32671784e68SYinan Xu burstLen 32771784e68SYinan Xu )) 32871784e68SYinan Xu ram.node := master 32971784e68SYinan Xu} 33071784e68SYinan Xu 33171784e68SYinan Xuabstract class AXI4MemorySlave ( 33271784e68SYinan Xu slave: AXI4SlaveNode, 33371784e68SYinan Xu memByte: Long, 33471784e68SYinan Xu useBlackBox: Boolean = false 33571784e68SYinan Xu)(implicit p: Parameters) extends LazyModule { 33671784e68SYinan Xu val master = AXI4MasterNode(List(slave.in.head._2.master)) 33771784e68SYinan Xu 33871784e68SYinan Xu val portParam = slave.portParams.head 33971784e68SYinan Xu val slaveParam = portParam.slaves.head 34071784e68SYinan Xu val burstLen = portParam.maxTransfer / portParam.beatBytes 34171784e68SYinan Xu 34271784e68SYinan Xu val io_axi4 = InModuleBody{ master.makeIOs() } 34371784e68SYinan Xu 34471784e68SYinan Xu lazy val module = new LazyModuleImp(this) { } 34571784e68SYinan Xu} 34671784e68SYinan Xu 34771784e68SYinan Xuobject AXI4MemorySlave { 34871784e68SYinan Xu def apply( 34971784e68SYinan Xu slave: AXI4SlaveNode, 35071784e68SYinan Xu memByte: Long, 35171784e68SYinan Xu useBlackBox: Boolean = false, 35271784e68SYinan Xu dynamicLatency: Boolean = false 35371784e68SYinan Xu )(implicit p: Parameters): AXI4MemorySlave = { 35471784e68SYinan Xu val memory = if (dynamicLatency) { 35571784e68SYinan Xu LazyModule(new AXI4MemoryWrapper(slave, memByte, useBlackBox)) 35671784e68SYinan Xu } else { 35771784e68SYinan Xu LazyModule(new AXI4RAMWrapper(slave, memByte, useBlackBox)) 35871784e68SYinan Xu } 35971784e68SYinan Xu memory 36071784e68SYinan Xu } 36171784e68SYinan Xu} 362