diff options
Diffstat (limited to 'src/systemc/tests/tlm/multi_sockets/MultiSocketSimpleSwitchAT.h')
-rw-r--r-- | src/systemc/tests/tlm/multi_sockets/MultiSocketSimpleSwitchAT.h | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/systemc/tests/tlm/multi_sockets/MultiSocketSimpleSwitchAT.h b/src/systemc/tests/tlm/multi_sockets/MultiSocketSimpleSwitchAT.h new file mode 100644 index 000000000..3e03db312 --- /dev/null +++ b/src/systemc/tests/tlm/multi_sockets/MultiSocketSimpleSwitchAT.h @@ -0,0 +1,340 @@ +/***************************************************************************** + + 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 |