| /***************************************************************************** | |
| 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<MultiSocketSimpleSwitchAT> target_socket_type; | |
| typedef tlm_utils::multi_passthrough_initiator_socket<MultiSocketSimpleSwitchAT> 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<std::deque<transaction_type*> > m_pendingReqs; //list of pending reqs per target | |
| std::vector<std::deque<transaction_type*> > m_pendingResps; //list of pending resps per initiator | |
| std::vector<sc_dt::uint64> 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<MultiSocketSimpleSwitchAT> m_bwPEQ; //PEQ in the fw direction | |
| tlm_utils::peq_with_cb_and_phase<MultiSocketSimpleSwitchAT> 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<BTag>{ | |
| 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<ConnectionInfo>{ | |
| 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<ConnectionInfo> 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 "<<sc_core::sc_time_stamp()<<" status of "<<name()<<" is "<<std::endl | |
| <<" Number of connected initiators: "<<target_socket.size()<<std::endl | |
| <<" Number of connected targets: "<<initiator_socket.size()<<std::endl | |
| <<" Pending requests:"<<std::endl; | |
| for (unsigned int i=0; i<m_pendingReqs.size(); i++) | |
| std::cout<<" "<<m_pendingReqs[i].size()<<" pending requests for target number "<<i<<std::endl; | |
| std::cout<<" Pending responses:"<<std::endl; | |
| for (unsigned int i=0; i<m_pendingResps.size(); i++) | |
| std::cout<<" "<<m_pendingResps[i].size()<<" pending responses for initiator number "<<i<<std::endl; | |
| std::cout<<" The address map is:"<<std::endl; | |
| m_addrMap.dumpMap(); | |
| } | |
| }; | |
| #endif |