/***************************************************************************** Licensed to Accellera Systems Initiative Inc. (Accellera) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Accellera licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *****************************************************************************/ #ifndef __SIMPLESWITCHAT_H__ #define __SIMPLESWITCHAT_H__ #include "tlm.h" #include "tlm_utils/multi_passthrough_initiator_socket.h" #include "tlm_utils/multi_passthrough_target_socket.h" #include "simpleAddressMap.h" #include "extensionPool.h" #include "tlm_utils/instance_specific_extensions.h" #include "tlm_utils/peq_with_cb_and_phase.h" /* This class is a simple crossbar switch through which an arbitrary number of initiators may communicate in parallel as long as they do not talk to the same target. If two masters address the same target at the same point of time, the choice who will be allowed to communicate is done non-deterministically (based on the SystemC process exectution order). This could be avoided by changing the fwPEQ into a priority PEQ of some kind. The switch ensures that the end_req and end_resp rules are not violated when many initiator talk to the same target. */ class MultiSocketSimpleSwitchAT : public sc_core::sc_module, public tlm::tlm_mm_interface { public: typedef tlm::tlm_generic_payload transaction_type; typedef tlm::tlm_phase phase_type; typedef tlm::tlm_sync_enum sync_enum_type; typedef tlm_utils::multi_passthrough_target_socket target_socket_type; typedef tlm_utils::multi_passthrough_initiator_socket initiator_socket_type; public: target_socket_type target_socket; //the target multi socket private: initiator_socket_type initiator_socket; //the initiator multi socket (private to enforce use of bindTarget function) SimpleAddressMap m_addrMap; //a pretty simple address map std::vector > m_pendingReqs; //list of pending reqs per target std::vector > m_pendingResps; //list of pending resps per initiator std::vector m_masks; //address masks for each target tlm_utils::instance_specific_extension_accessor accessMySpecificExtensions; //extension accessor to access private extensions tlm_utils::peq_with_cb_and_phase m_bwPEQ; //PEQ in the fw direction tlm_utils::peq_with_cb_and_phase m_fwPEQ; //PEQ in the bw direction //an instance specific extension that tells us whether we are in a wrapped b_transport or not class BTag : public tlm_utils::instance_specific_extension{ public: sc_core::sc_event event; //trigger this event when transaction is done }; //an instance specific extension that holds information about source and sink of a txn // as well as information if the req still has to be cleared and if the txn is already // complete on the target side class ConnectionInfo : public tlm_utils::instance_specific_extension{ public: unsigned int fwID; //socket number of sink unsigned int bwID; //socket number of source bool clearReq; //is the txn still in req phase? bool alreadyComplete; //has the txn already completed on the target side? }; class internalPEQTypes{ //use the tpPEQ to delay connection infos public: typedef ConnectionInfo tlm_payload_type; typedef tlm::tlm_phase tlm_phase_type; }; ExtensionPool m_connInfoPool; //our pool of extensions unsigned int m_target_count; //number of connected targets (see bindTargetSocket for explanation) public: SC_HAS_PROCESS(MultiSocketSimpleSwitchAT); MultiSocketSimpleSwitchAT(sc_core::sc_module_name name) : sc_core::sc_module(name), target_socket("target_socket"), initiator_socket("initiator_socket"), m_bwPEQ(this, &MultiSocketSimpleSwitchAT::bwPEQcb), m_fwPEQ(this, &MultiSocketSimpleSwitchAT::fwPEQcb), m_connInfoPool(10), m_target_count(0) { target_socket.register_nb_transport_fw(this, &MultiSocketSimpleSwitchAT::initiatorNBTransport); target_socket.register_b_transport(this, &MultiSocketSimpleSwitchAT::b_transport); initiator_socket.register_nb_transport_bw(this, &MultiSocketSimpleSwitchAT::targetNBTransport); } void bindTargetSocket(initiator_socket_type::base_target_socket_type& target ,sc_dt::uint64 low ,sc_dt::uint64 high ,sc_dt::uint64 mask = 0xffffffffffffffffULL){ initiator_socket(target); //bind sockets //insert into address map and increase target count // (we have to count the targets manually, because target_socket.size() is only reliable during simulation // as it gets evaluated during end_of_elaboration) m_addrMap.insert(low, high, m_target_count++); m_masks.push_back(mask); //add the mask for this target } unsigned int decode(const sc_dt::uint64& address) { return m_addrMap.decode(address); } void start_of_simulation(){ //initialize the lists of pending reqs and resps m_pendingReqs.resize(initiator_socket.size()); m_pendingResps.resize(target_socket.size()); } void b_transport(int initiator_id, transaction_type& trans, sc_core::sc_time& t){ //first make sure that there is no BTag (just for debugging) BTag* btag; accessMySpecificExtensions(trans).get_extension(btag); sc_assert(!btag); BTag tag; //now add our BTag bool added_mm=!trans.has_mm(); //in case there is no MM in we add it now if (added_mm){ trans.set_mm(this); trans.acquire(); //acquire the txn } accessMySpecificExtensions(trans).set_extension(&tag); phase_type phase=tlm::BEGIN_REQ; //then simply use our nb implementation (respects all the rules) initiatorNBTransport(initiator_id, trans, phase, t); wait(tag.event); //and wait for the event to be triggered if (added_mm){ //if we added MM trans.release(); //we release our reference (this will not delete the txn but trigger the tag.event as soon as the ref count is zero) if (trans.get_ref_count()) wait(tag.event); //wait for the ref count to get to zero trans.set_mm(NULL); //remove the MM } //don't forget to remove the extension (instance specific extensions are not cleared off by MM) accessMySpecificExtensions(trans).clear_extension(&tag); } void free(transaction_type* txn){ BTag* btag; accessMySpecificExtensions(*txn).get_extension(btag); sc_assert(btag); txn->reset(); //clean off all extension that were added down stream btag->event.notify(); } //do a fw transmission void initiatorNBTransport_core(transaction_type& trans, phase_type& phase, sc_core::sc_time& t, unsigned int tgtSocketNumber){ switch (initiator_socket[tgtSocketNumber]->nb_transport_fw(trans, phase, t)) { case tlm::TLM_ACCEPTED: case tlm::TLM_UPDATED: // Transaction not yet finished if (phase != tlm::BEGIN_REQ) { sc_assert(phase!=tlm::END_RESP); m_bwPEQ.notify(trans,phase,t); } break; case tlm::TLM_COMPLETED: // Transaction finished ConnectionInfo* connInfo; accessMySpecificExtensions(trans).get_extension(connInfo); sc_assert(connInfo); connInfo->alreadyComplete=true; phase=tlm::BEGIN_RESP; m_bwPEQ.notify(trans, phase, t); break; default: sc_assert(0); exit(1); }; } //nb_transport_fw sync_enum_type initiatorNBTransport(int initiator_id, transaction_type& trans, phase_type& phase, sc_core::sc_time& t) { ConnectionInfo* connInfo; accessMySpecificExtensions(trans).get_extension(connInfo); m_fwPEQ.notify(trans,phase,t); if (phase==tlm::BEGIN_REQ){ //add our private information to the txn sc_assert(!connInfo); connInfo=m_connInfoPool.construct(); connInfo->fwID=decode(trans.get_address()); connInfo->bwID=initiator_id; connInfo->clearReq=true; connInfo->alreadyComplete=false; accessMySpecificExtensions(trans).set_extension(connInfo); } else if (phase==tlm::END_RESP){ return tlm::TLM_COMPLETED; } else {sc_assert(0); exit(1);} return tlm::TLM_ACCEPTED; } sync_enum_type targetNBTransport(int portId, transaction_type& trans, phase_type& phase, sc_core::sc_time& t) { if (phase != tlm::END_REQ && phase != tlm::BEGIN_RESP) { std::cout << "ERROR: '" << name() << "': Illegal phase received from target." << std::endl; sc_assert(false); exit(1); } //simply stuff it into the bw PEQ m_bwPEQ.notify(trans,phase,t); return tlm::TLM_ACCEPTED; } void bwPEQcb(transaction_type& trans, const phase_type& phase){ //first get our private info from the txn ConnectionInfo* connInfo; accessMySpecificExtensions(trans).get_extension(connInfo); sc_assert(connInfo); phase_type p=phase; sc_core::sc_time t=sc_core::SC_ZERO_TIME; BTag* btag; accessMySpecificExtensions(trans).get_extension(btag); bool doCall=btag==NULL; //we only will do a bw call if we are not in a wrapped b_transport if ((phase==tlm::END_REQ) | (connInfo->clearReq)){ //in case the target left out end_req clearReq reminds us to unlock the req port sc_assert(m_pendingReqs[connInfo->fwID].size()); sc_assert(m_pendingReqs[connInfo->fwID].front()==&trans); m_pendingReqs[connInfo->fwID].pop_front(); //allow another req to start at this target if (m_pendingReqs[connInfo->fwID].size()){ //there was a pending req phase_type ph=tlm::BEGIN_REQ; initiatorNBTransport_core(*m_pendingReqs[connInfo->fwID].front(), ph, t,connInfo->fwID); } connInfo->clearReq=false; } //no else here, since we might clear the req AND begin a resp if (phase==tlm::BEGIN_RESP){ m_pendingResps[connInfo->bwID].push_back(&trans); doCall=m_pendingResps[connInfo->bwID].size()==1; //do a call in case the response socket was free } if (doCall){ //we have to do a call on the bw of fw path if (btag){ //only possible if BEGIN_RESP and resp socket was free phase_type ph=tlm::END_RESP; m_fwPEQ.notify(trans, ph, t); } else switch (target_socket[connInfo->bwID]->nb_transport_bw(trans, p, t)){ case tlm::TLM_ACCEPTED: case tlm::TLM_UPDATED: break; case tlm::TLM_COMPLETED:{ //covers a piggy bagged END_RESP to START_RESP phase_type ph=tlm::END_RESP; m_fwPEQ.notify(trans, ph, t); } break; default: sc_assert(0); exit(1); }; } } //the following two functions (fwPEQcb and clearPEQcb) could be one, if we were allowed // to stick END_RESP into a PEQ void fwPEQcb(transaction_type& trans, const phase_type& phase){ ConnectionInfo* connInfo; accessMySpecificExtensions(trans).get_extension(connInfo); sc_assert(connInfo); phase_type ph=phase; sc_core::sc_time t=sc_core::SC_ZERO_TIME; if (phase==tlm::BEGIN_REQ){ trans.set_address(trans.get_address()&m_masks[connInfo->fwID]); //mask address m_pendingReqs[connInfo->fwID].push_back(&trans); if (m_pendingReqs[connInfo->fwID].size()==1){ //the socket is free initiatorNBTransport_core(trans, ph, t, connInfo->fwID); } } else { //phase is always END_RESP BTag* btag; accessMySpecificExtensions(trans).get_extension(btag); accessMySpecificExtensions(trans).clear_extension(connInfo); //remove our specific extension as it is not needed any more if (!connInfo->alreadyComplete) { sync_enum_type tmp=initiator_socket[connInfo->fwID]->nb_transport_fw(trans, ph, t); sc_assert(tmp==tlm::TLM_COMPLETED); } sc_assert(m_pendingResps[connInfo->bwID].size()); m_pendingResps[connInfo->bwID].pop_front(); //remove current response if (m_pendingResps[connInfo->bwID].size()){ //if there was one pending ph=tlm::BEGIN_RESP; //schedule its transmission m_bwPEQ.notify(*m_pendingResps[connInfo->bwID].front(),ph,t); } m_connInfoPool.free(connInfo); //release connInfo if (btag) btag->event.notify(t); //release b_transport } } void dump_status(){ std::cout<<"At "<