| /***************************************************************************** |
| |
| 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 requestors 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 |