| /* |
| * Copyright 2019 Google, Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * 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; |
| * neither the name of the copyright holders 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 |
| * OWNER 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. |
| * |
| * 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. |
| */ |
| |
| #include "systemc/tlm_bridge/tlm_to_gem5.hh" |
| |
| #include <utility> |
| |
| #include "params/TlmToGem5Bridge32.hh" |
| #include "params/TlmToGem5Bridge64.hh" |
| #include "params/TlmToGem5Bridge128.hh" |
| #include "params/TlmToGem5Bridge256.hh" |
| #include "params/TlmToGem5Bridge512.hh" |
| #include "sim/core.hh" |
| #include "sim/system.hh" |
| #include "systemc/ext/core/sc_module_name.hh" |
| #include "systemc/ext/core/sc_time.hh" |
| |
| using namespace gem5; |
| |
| namespace sc_gem5 |
| { |
| |
| namespace |
| { |
| /** |
| * Hold all the callbacks necessary to convert a tlm payload to gem5 packet. |
| */ |
| std::vector<PayloadToPacketConversionStep> extraPayloadToPacketSteps; |
| } // namespace |
| |
| /** |
| * Notify the Tlm2Gem5 bridge that we need an extra step to properly convert a |
| * tlm payload to gem5 packet. This can be useful when there exists a SystemC |
| * extension that carries extra information. For example, SystemC user might |
| * define an extension to store stream_id, the user may then add an extra step |
| * to set the generated request's stream_id accordingly. |
| */ |
| void |
| addPayloadToPacketConversionStep(PayloadToPacketConversionStep step) |
| { |
| extraPayloadToPacketSteps.push_back(std::move(step)); |
| } |
| |
| PacketPtr |
| payload2packet(RequestorID _id, tlm::tlm_generic_payload &trans) |
| { |
| MemCmd cmd; |
| RequestPtr req; |
| |
| Gem5SystemC::AtomicExtension *atomic_ex = nullptr; |
| trans.get_extension(atomic_ex); |
| if (atomic_ex) { |
| cmd = MemCmd::SwapReq; |
| Request::Flags flags = (atomic_ex->needReturn() ? |
| Request::ATOMIC_RETURN_OP : |
| Request::ATOMIC_NO_RETURN_OP); |
| AtomicOpFunctorPtr amo_op = AtomicOpFunctorPtr( |
| atomic_ex->getAtomicOpFunctor()->clone()); |
| // FIXME: correct the context_id and pc state. |
| req = std::make_shared<Request>( |
| trans.get_address(), trans.get_data_length(), flags, _id, |
| 0, 0, std::move(amo_op)); |
| req->setPaddr(trans.get_address()); |
| } else { |
| switch (trans.get_command()) { |
| case tlm::TLM_READ_COMMAND: |
| cmd = MemCmd::ReadReq; |
| break; |
| case tlm::TLM_WRITE_COMMAND: |
| cmd = MemCmd::WriteReq; |
| break; |
| case tlm::TLM_IGNORE_COMMAND: |
| return nullptr; |
| default: |
| SC_REPORT_FATAL("TlmToGem5Bridge", |
| "received transaction with unsupported " |
| "command"); |
| } |
| Request::Flags flags; |
| req = std::make_shared<Request>( |
| trans.get_address(), trans.get_data_length(), flags, _id); |
| } |
| |
| |
| /* |
| * 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()); |
| |
| // Apply all conversion steps necessary in this specific setup. |
| for (auto &step : extraPayloadToPacketSteps) { |
| step(pkt, trans); |
| } |
| |
| return pkt; |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::sendEndReq(tlm::tlm_generic_payload &trans) |
| { |
| tlm::tlm_phase phase = tlm::END_REQ; |
| auto delay = sc_core::SC_ZERO_TIME; |
| |
| auto status = socket->nb_transport_bw(trans, phase, delay); |
| panic_if(status != tlm::TLM_ACCEPTED, |
| "Unexpected status after sending END_REQ"); |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::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 = 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"); |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::handleBeginReq(tlm::tlm_generic_payload &trans) |
| { |
| sc_assert(!waitForRetry); |
| sc_assert(pendingRequest == nullptr); |
| sc_assert(pendingPacket == nullptr); |
| |
| trans.acquire(); |
| |
| PacketPtr pkt = nullptr; |
| |
| Gem5SystemC::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) { |
| pkt = extension->getPacket(); |
| } else { |
| pkt = payload2packet(_id, trans); |
| } |
| |
| auto tlmSenderState = new TlmSenderState(trans); |
| pkt->pushSenderState(tlmSenderState); |
| |
| // If the packet doesn't need a response, we should send BEGIN_RESP by |
| // ourselves. |
| bool needsResponse = pkt->needsResponse(); |
| if (bmp.sendTimingReq(pkt)) { // port is free -> send END_REQ immediately |
| sendEndReq(trans); |
| if (!needsResponse) { |
| auto delay = sc_core::SC_ZERO_TIME; |
| sendBeginResp(trans, delay); |
| } |
| trans.release(); |
| } else { // port is blocked -> wait for retry before sending END_REQ |
| waitForRetry = true; |
| pendingRequest = &trans; |
| pendingPacket = pkt; |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::handleEndResp(tlm::tlm_generic_payload &trans) |
| { |
| sc_assert(responseInProgress); |
| |
| responseInProgress = false; |
| |
| checkTransaction(trans); |
| |
| if (needToSendRetry) { |
| bmp.sendRetryResp(); |
| needToSendRetry = false; |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::destroyPacket(PacketPtr pkt) |
| { |
| delete pkt; |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::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()); |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::invalidateDmi(const gem5::MemBackdoor &backdoor) |
| { |
| socket->invalidate_direct_mem_ptr( |
| backdoor.range().start(), backdoor.range().end()); |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::peq_cb(tlm::tlm_generic_payload &trans, |
| const tlm::tlm_phase &phase) |
| { |
| switch (phase) { |
| case tlm::BEGIN_REQ: |
| handleBeginReq(trans); |
| break; |
| case tlm::END_RESP: |
| handleEndResp(trans); |
| break; |
| default: |
| panic("unimplemented phase in callback"); |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| tlm::tlm_sync_enum |
| TlmToGem5Bridge<BITWIDTH>::nb_transport_fw( |
| tlm::tlm_generic_payload &trans, tlm::tlm_phase &phase, |
| sc_core::sc_time &delay) |
| { |
| 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; |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::b_transport(tlm::tlm_generic_payload &trans, |
| sc_core::sc_time &t) |
| { |
| Gem5SystemC::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) { |
| pkt = extension->getPacket(); |
| } else { |
| pkt = payload2packet(_id, trans); |
| } |
| |
| MemBackdoorPtr backdoor = nullptr; |
| Tick ticks = bmp.sendAtomicBackdoor(pkt, backdoor); |
| if (backdoor) |
| trans.set_dmi_allowed(true); |
| |
| // send an atomic request to gem5 |
| panic_if(pkt->needsResponse() && !pkt->isResponse(), |
| "Packet sending failed!\n"); |
| |
| auto delay = |
| sc_core::sc_time((double)(ticks / sim_clock::as_int::ps), |
| sc_core::SC_PS); |
| |
| // update time |
| t += delay; |
| |
| if (extension == nullptr) |
| destroyPacket(pkt); |
| |
| trans.set_response_status(tlm::TLM_OK_RESPONSE); |
| } |
| |
| template <unsigned int BITWIDTH> |
| unsigned int |
| TlmToGem5Bridge<BITWIDTH>::transport_dbg(tlm::tlm_generic_payload &trans) |
| { |
| Gem5SystemC::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) { |
| bmp.sendFunctional(extension->getPacket()); |
| } else { |
| auto pkt = payload2packet(_id, trans); |
| if (pkt) { |
| bmp.sendFunctional(pkt); |
| destroyPacket(pkt); |
| } |
| } |
| |
| return trans.get_data_length(); |
| } |
| |
| template <unsigned int BITWIDTH> |
| bool |
| TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr(tlm::tlm_generic_payload &trans, |
| tlm::tlm_dmi &dmi_data) |
| { |
| Gem5SystemC::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) { |
| pkt = extension->getPacket(); |
| } else { |
| pkt = payload2packet(_id, trans); |
| pkt->req->setFlags(Request::NO_ACCESS); |
| } |
| |
| MemBackdoorPtr backdoor = nullptr; |
| bmp.sendAtomicBackdoor(pkt, backdoor); |
| if (backdoor) { |
| trans.set_dmi_allowed(true); |
| dmi_data.set_dmi_ptr(backdoor->ptr()); |
| dmi_data.set_start_address(backdoor->range().start()); |
| dmi_data.set_end_address(backdoor->range().end()); |
| |
| typedef tlm::tlm_dmi::dmi_access_e access_t; |
| access_t access = tlm::tlm_dmi::DMI_ACCESS_NONE; |
| if (backdoor->readable()) |
| access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_READ); |
| if (backdoor->writeable()) |
| access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_WRITE); |
| dmi_data.set_granted_access(access); |
| |
| backdoor->addInvalidationCallback( |
| [this](const MemBackdoor &backdoor) |
| { |
| invalidateDmi(backdoor); |
| } |
| ); |
| } |
| |
| if (extension == nullptr) |
| destroyPacket(pkt); |
| |
| trans.set_response_status(tlm::TLM_OK_RESPONSE); |
| |
| return backdoor != nullptr; |
| } |
| |
| template <unsigned int BITWIDTH> |
| bool |
| TlmToGem5Bridge<BITWIDTH>::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<TlmSenderState*>(pkt->popSenderState()); |
| sc_assert(tlmSenderState != nullptr); |
| |
| auto &trans = tlmSenderState->trans; |
| |
| Gem5SystemC::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); |
| |
| sendBeginResp(trans, delay); |
| trans.release(); |
| |
| return true; |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::recvReqRetry() |
| { |
| sc_assert(waitForRetry); |
| sc_assert(pendingRequest != nullptr); |
| sc_assert(pendingPacket != nullptr); |
| |
| // If the packet doesn't need a response, we should send BEGIN_RESP by |
| // ourselves. |
| bool needsResponse = pendingPacket->needsResponse(); |
| if (bmp.sendTimingReq(pendingPacket)) { |
| waitForRetry = false; |
| pendingPacket = nullptr; |
| |
| auto &trans = *pendingRequest; |
| sendEndReq(trans); |
| if (!needsResponse) { |
| auto delay = sc_core::SC_ZERO_TIME; |
| sendBeginResp(trans, delay); |
| } |
| trans.release(); |
| |
| pendingRequest = nullptr; |
| } |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::recvRangeChange() |
| { |
| SC_REPORT_WARNING("TlmToGem5Bridge", |
| "received address range change but ignored it"); |
| } |
| |
| template <unsigned int BITWIDTH> |
| gem5::Port & |
| TlmToGem5Bridge<BITWIDTH>::gem5_getPort(const std::string &if_name, int idx) |
| { |
| if (if_name == "gem5") |
| return bmp; |
| else if (if_name == "tlm") |
| return wrapper; |
| |
| return sc_core::sc_module::gem5_getPort(if_name, idx); |
| } |
| |
| template <unsigned int BITWIDTH> |
| TlmToGem5Bridge<BITWIDTH>::TlmToGem5Bridge( |
| const Params ¶ms, const sc_core::sc_module_name &mn) : |
| TlmToGem5BridgeBase(mn), peq(this, &TlmToGem5Bridge<BITWIDTH>::peq_cb), |
| waitForRetry(false), pendingRequest(nullptr), pendingPacket(nullptr), |
| needToSendRetry(false), responseInProgress(false), |
| bmp(std::string(name()) + "master", *this), socket("tlm_socket"), |
| wrapper(socket, std::string(name()) + ".tlm", InvalidPortID), |
| system(params.system), |
| _id(params.system->getGlobalRequestorId( |
| std::string("[systemc].") + name())) |
| { |
| } |
| |
| template <unsigned int BITWIDTH> |
| void |
| TlmToGem5Bridge<BITWIDTH>::before_end_of_elaboration() |
| { |
| /* |
| * 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("TlmToGem5Bridge", "register non-blocking interface"); |
| socket.register_nb_transport_fw( |
| this, &TlmToGem5Bridge<BITWIDTH>::nb_transport_fw); |
| } else if (system->isAtomicMode()) { |
| SC_REPORT_INFO("TlmToGem5Bridge", "register blocking interface"); |
| socket.register_b_transport( |
| this, &TlmToGem5Bridge<BITWIDTH>::b_transport); |
| socket.register_get_direct_mem_ptr( |
| this, &TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr); |
| } else { |
| panic("gem5 operates neither in Timing nor in Atomic mode"); |
| } |
| |
| socket.register_transport_dbg( |
| this, &TlmToGem5Bridge<BITWIDTH>::transport_dbg); |
| |
| sc_core::sc_module::before_end_of_elaboration(); |
| } |
| |
| } // namespace sc_gem5 |
| |
| sc_gem5::TlmToGem5Bridge<32> * |
| gem5::TlmToGem5Bridge32Params::create() const |
| { |
| return new sc_gem5::TlmToGem5Bridge<32>( |
| *this, sc_core::sc_module_name(name.c_str())); |
| } |
| |
| sc_gem5::TlmToGem5Bridge<64> * |
| gem5::TlmToGem5Bridge64Params::create() const |
| { |
| return new sc_gem5::TlmToGem5Bridge<64>( |
| *this, sc_core::sc_module_name(name.c_str())); |
| } |
| |
| sc_gem5::TlmToGem5Bridge<128> * |
| gem5::TlmToGem5Bridge128Params::create() const |
| { |
| return new sc_gem5::TlmToGem5Bridge<128>( |
| *this, sc_core::sc_module_name(name.c_str())); |
| } |
| |
| sc_gem5::TlmToGem5Bridge<256> * |
| gem5::TlmToGem5Bridge256Params::create() const |
| { |
| return new sc_gem5::TlmToGem5Bridge<256>( |
| *this, sc_core::sc_module_name(name.c_str())); |
| } |
| |
| sc_gem5::TlmToGem5Bridge<512> * |
| gem5::TlmToGem5Bridge512Params::create() const |
| { |
| return new sc_gem5::TlmToGem5Bridge<512>( |
| *this, sc_core::sc_module_name(name.c_str())); |
| } |