/* * Copyright (c) 2016, Dresden University of Technology (TU Dresden) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Christian Menard */ #include #include "params/ExternalMaster.hh" #include "sc_master_port.hh" #include "sim/system.hh" namespace Gem5SystemC { PacketPtr SCMasterPort::generatePacket(tlm::tlm_generic_payload& trans) { Request::Flags flags; auto req = new Request(trans.get_address(), trans.get_data_length(), flags, owner.masterId); MemCmd cmd; switch (trans.get_command()) { case tlm::TLM_READ_COMMAND: cmd = MemCmd::ReadReq; break; case tlm::TLM_WRITE_COMMAND: cmd = MemCmd::WriteReq; break; default: SC_REPORT_FATAL("SCMasterPort", "received transaction with unsupported command"); } /* * Allocate a new Packet. The packet will be deleted when it returns from * the gem5 world as a response. */ auto pkt = new Packet(req, cmd); pkt->dataStatic(trans.get_data_ptr()); return pkt; } void SCMasterPort::destroyPacket(PacketPtr pkt) { delete pkt; } SCMasterPort::SCMasterPort(const std::string& name_, const std::string& systemc_name, ExternalMaster& owner_, Module& module) : ExternalMaster::Port(name_, owner_), tSocket(systemc_name.c_str()), peq(this, &SCMasterPort::peq_cb), waitForRetry(false), pendingRequest(nullptr), needToSendRetry(false), responseInProgress(false), module(module) { auto system = dynamic_cast(owner_.params())->system; /* * Register the TLM non-blocking interface when using gem5 Timing mode and * the TLM blocking interface when using the gem5 Atomic mode. * Then the magic (TM) in simple_target_socket automatically transforms * non-blocking in blocking transactions and vice versa. * * NOTE: The mode may change during execution. */ if (system->isTimingMode()) { SC_REPORT_INFO("SCMasterPort", "register non-blocking interface"); tSocket.register_nb_transport_fw(this, &SCMasterPort::nb_transport_fw); } else if (system->isAtomicMode()) { SC_REPORT_INFO("SCMasterPort", "register blocking interface"); tSocket.register_b_transport(this, &SCMasterPort::b_transport); } else { panic("gem5 operates neither in Timing nor in Atomic mode"); } tSocket.register_transport_dbg(this, &SCMasterPort::transport_dbg); } void SCMasterPort::checkTransaction(tlm::tlm_generic_payload& trans) { if (trans.is_response_error()) { std::stringstream ss; ss << "Transaction returned with error, response status = " << trans.get_response_string(); SC_REPORT_ERROR("TLM-2", ss.str().c_str()); } } tlm::tlm_sync_enum SCMasterPort::nb_transport_fw(tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) { uint64_t adr = trans.get_address(); unsigned len = trans.get_data_length(); unsigned char* byteEnable = trans.get_byte_enable_ptr(); unsigned width = trans.get_streaming_width(); // check the transaction attributes for unsupported features ... if (byteEnable != 0) { trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE); return tlm::TLM_COMPLETED; } if (width < len) { // is this a burst request? trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE); return tlm::TLM_COMPLETED; } // ... and queue the valid transaction peq.notify(trans, phase, delay); return tlm::TLM_ACCEPTED; } void SCMasterPort::peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase) { // catch up with SystemC time module.catchup(); assert(curTick() == sc_core::sc_time_stamp().value()); switch (phase) { case tlm::BEGIN_REQ: handleBeginReq(trans); break; case tlm::END_RESP: handleEndResp(trans); break; default: panic("unimplemented phase in callback"); } // the functions called above may have scheduled gem5 events // -> notify the event loop handler module.notify(); } void SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans) { sc_assert(!waitForRetry); sc_assert(pendingRequest == nullptr); trans.acquire(); auto pkt = generatePacket(trans); auto tlmSenderState = new TlmSenderState(trans); pkt->pushSenderState(tlmSenderState); if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately sendEndReq(trans); } else { // port is blocked -> wait for retry before sending END_REQ waitForRetry = true; pendingRequest = &trans; } } void SCMasterPort::handleEndResp(tlm::tlm_generic_payload& trans) { sc_assert(responseInProgress); responseInProgress = false; checkTransaction(trans); if (needToSendRetry) { sendRetryResp(); needToSendRetry = false; } } void SCMasterPort::sendEndReq(tlm::tlm_generic_payload& trans) { tlm::tlm_phase phase = tlm::END_REQ; auto delay = sc_core::SC_ZERO_TIME; auto status = tSocket->nb_transport_bw(trans, phase, delay); panic_if(status != tlm::TLM_ACCEPTED, "Unexpected status after sending END_REQ"); } void SCMasterPort::b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& t) { auto pkt = generatePacket(trans); // send an atomic request to gem5 Tick ticks = sendAtomic(pkt); panic_if(!pkt->isResponse(), "Packet sending failed!\n"); // one tick is a pico second auto delay = sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS); // update time t += delay; destroyPacket(pkt); trans.set_response_status(tlm::TLM_OK_RESPONSE); } unsigned int SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans) { auto pkt = generatePacket(trans); sendFunctional(pkt); destroyPacket(pkt); return trans.get_data_length(); } bool SCMasterPort::get_direct_mem_ptr(tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data) { return false; } bool SCMasterPort::recvTimingResp(PacketPtr pkt) { // exclusion rule // We need to Wait for END_RESP before sending next BEGIN_RESP if (responseInProgress) { sc_assert(!needToSendRetry); needToSendRetry = true; return false; } sc_assert(pkt->isResponse()); // pay for annotaded transport delays auto delay = sc_core::sc_time::from_value(pkt->payloadDelay + pkt->headerDelay); auto tlmSenderState = dynamic_cast(pkt->popSenderState()); auto& trans = tlmSenderState->trans; // clean up delete tlmSenderState; destroyPacket(pkt); sendBeginResp(trans, delay); trans.release(); return true; } void SCMasterPort::sendBeginResp(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) { tlm::tlm_phase phase = tlm::BEGIN_RESP; trans.set_response_status(tlm::TLM_OK_RESPONSE); auto status = tSocket->nb_transport_bw(trans, phase, delay); if (status == tlm::TLM_COMPLETED || status == tlm::TLM_UPDATED && phase == tlm::END_RESP) { // transaction completed -> no need to wait for tlm::END_RESP responseInProgress = false; } else if (status == tlm::TLM_ACCEPTED) { // we need to wait for tlm::END_RESP responseInProgress = true; } else { panic("Unexpected status after sending BEGIN_RESP"); } } void SCMasterPort::recvReqRetry() { sc_assert(waitForRetry); sc_assert(pendingRequest != nullptr); auto& trans = *pendingRequest; waitForRetry = false; pendingRequest = nullptr; // retry handleBeginReq(trans); } void SCMasterPort::recvRangeChange() { SC_REPORT_WARNING("SCMasterPort", "received address range change but ignored it"); } class SCMasterPortHandler : public ExternalMaster::Handler { Module& module; public: SCMasterPortHandler(Module& module) : module(module) {} ExternalMaster::Port* getExternalPort(const std::string& name, ExternalMaster& owner, const std::string& port_data) { // This will make a new initiatiator port return new SCMasterPort(name, port_data, owner, module); } }; void SCMasterPort::registerPortHandler(Module& module) { ExternalMaster::registerHandler("tlm_master", new SCMasterPortHandler(module)); } } // namespace Gem5SystemC