/* * 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 "master_transactor.hh" #include "params/ExternalMaster.hh" #include "sc_ext.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_, Gem5SimControl& simControl) : ExternalMaster::Port(name_, owner_), peq(this, &SCMasterPort::peq_cb), waitForRetry(false), pendingRequest(nullptr), pendingPacket(nullptr), needToSendRetry(false), responseInProgress(false), transactor(nullptr), simControl(simControl) { system = dynamic_cast(owner_.params())->system; } void SCMasterPort::bindToTransactor(Gem5MasterTransactor* transactor) { sc_assert(this->transactor == nullptr); this->transactor = transactor; /* * 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"); transactor->socket.register_nb_transport_fw(this, &SCMasterPort::nb_transport_fw); } else if (system->isAtomicMode()) { SC_REPORT_INFO("SCMasterPort", "register blocking interface"); transactor->socket.register_b_transport(this, &SCMasterPort::b_transport); } else { panic("gem5 operates neither in Timing nor in Atomic mode"); } transactor->socket.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 trans.acquire(); 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 simControl.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 simControl.notify(); } void SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans) { sc_assert(!waitForRetry); sc_assert(pendingRequest == nullptr); sc_assert(pendingPacket == nullptr); trans.acquire(); PacketPtr pkt = nullptr; Gem5Extension* extension = nullptr; trans.get_extension(extension); // If there is an extension, this transaction was initiated by the gem5 // world and we can pipe through the original packet. Otherwise, we // generate a new packet based on the transaction. if (extension != nullptr) { extension->setPipeThrough(); pkt = extension->getPacket(); } else { pkt = generatePacket(trans); } auto tlmSenderState = new TlmSenderState(trans); pkt->pushSenderState(tlmSenderState); if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately sendEndReq(trans); trans.release(); } else { // port is blocked -> wait for retry before sending END_REQ waitForRetry = true; pendingRequest = &trans; pendingPacket = pkt; } } 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 = transactor->socket->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) { Gem5Extension* extension = nullptr; trans.get_extension(extension); PacketPtr pkt = nullptr; // If there is an extension, this transaction was initiated by the gem5 // world and we can pipe through the original packet. if (extension != nullptr) { extension->setPipeThrough(); pkt = extension->getPacket(); } else { pkt = generatePacket(trans); } Tick ticks = sendAtomic(pkt); // send an atomic request to gem5 panic_if(pkt->needsResponse() && !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; if (extension != nullptr) destroyPacket(pkt); trans.set_response_status(tlm::TLM_OK_RESPONSE); } unsigned int SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans) { Gem5Extension* extension = nullptr; trans.get_extension(extension); // If there is an extension, this transaction was initiated by the gem5 // world and we can pipe through the original packet. if (extension != nullptr) { extension->setPipeThrough(); sendFunctional(extension->getPacket()); } else { 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 annotated transport delays. * * See recvTimingReq in sc_slave_port.cc for a detailed description. */ auto delay = sc_core::sc_time::from_value(pkt->payloadDelay); // reset the delays pkt->payloadDelay = 0; pkt->headerDelay = 0; auto tlmSenderState = dynamic_cast(pkt->popSenderState()); sc_assert(tlmSenderState != nullptr); auto& trans = tlmSenderState->trans; Gem5Extension* extension = nullptr; trans.get_extension(extension); // clean up delete tlmSenderState; // If there is an extension the packet was piped through and we must not // delete it. The packet travels back with the transaction. if (extension == nullptr) destroyPacket(pkt); else sc_assert(extension->isPipeThrough()); 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 = transactor->socket->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); sc_assert(pendingPacket != nullptr); if (sendTimingReq(pendingPacket)) { waitForRetry = false; pendingPacket = nullptr; auto& trans = *pendingRequest; sendEndReq(trans); trans.release(); pendingRequest = nullptr; } } void SCMasterPort::recvRangeChange() { SC_REPORT_WARNING("SCMasterPort", "received address range change but ignored it"); } ExternalMaster::Port* SCMasterPortHandler::getExternalPort(const std::string &name, ExternalMaster &owner, const std::string &port_data) { // Create and register a new SystemC master port auto* port = new SCMasterPort(name, port_data, owner, control); control.registerMasterPort(port_data, port); return port; } } // namespace Gem5SystemC