MultiSocketSimpleSwitchAT.h revision 12855:588919e0e4aa
1/***************************************************************************** 2 3 Licensed to Accellera Systems Initiative Inc. (Accellera) under one or 4 more contributor license agreements. See the NOTICE file distributed 5 with this work for additional information regarding copyright ownership. 6 Accellera licenses this file to you under the Apache License, Version 2.0 7 (the "License"); you may not use this file except in compliance with the 8 License. You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 implied. See the License for the specific language governing 16 permissions and limitations under the License. 17 18 *****************************************************************************/ 19 20#ifndef __SIMPLESWITCHAT_H__ 21#define __SIMPLESWITCHAT_H__ 22 23#include "tlm.h" 24 25#include "tlm_utils/multi_passthrough_initiator_socket.h" 26#include "tlm_utils/multi_passthrough_target_socket.h" 27#include "simpleAddressMap.h" 28#include "extensionPool.h" 29#include "tlm_utils/instance_specific_extensions.h" 30#include "tlm_utils/peq_with_cb_and_phase.h" 31 32 33/* 34This class is a simple crossbar switch through which an arbitrary number of initiators 35may communicate in parallel as long as they do not talk to the same target. 36 37If two masters address the same target at the same point of time, 38the choice who will be allowed to communicate 39is done non-deterministically (based on the SystemC process exectution order). 40 41This could be avoided by changing the fwPEQ into a priority PEQ of some kind. 42 43The switch ensures that the end_req and end_resp rules are not violated when 44many initiator talk to the same target. 45*/ 46class MultiSocketSimpleSwitchAT : public sc_core::sc_module, public tlm::tlm_mm_interface 47{ 48public: 49 typedef tlm::tlm_generic_payload transaction_type; 50 typedef tlm::tlm_phase phase_type; 51 typedef tlm::tlm_sync_enum sync_enum_type; 52 typedef tlm_utils::multi_passthrough_target_socket<MultiSocketSimpleSwitchAT> target_socket_type; 53 typedef tlm_utils::multi_passthrough_initiator_socket<MultiSocketSimpleSwitchAT> initiator_socket_type; 54 55public: 56 target_socket_type target_socket; //the target multi socket 57 58private: 59 initiator_socket_type initiator_socket; //the initiator multi socket (private to enforce use of bindTarget function) 60 SimpleAddressMap m_addrMap; //a pretty simple address map 61 std::vector<std::deque<transaction_type*> > m_pendingReqs; //list of pending reqs per target 62 std::vector<std::deque<transaction_type*> > m_pendingResps; //list of pending resps per initiator 63 std::vector<sc_dt::uint64> m_masks; //address masks for each target 64 tlm_utils::instance_specific_extension_accessor accessMySpecificExtensions; //extension accessor to access private extensions 65 tlm_utils::peq_with_cb_and_phase<MultiSocketSimpleSwitchAT> m_bwPEQ; //PEQ in the fw direction 66 tlm_utils::peq_with_cb_and_phase<MultiSocketSimpleSwitchAT> m_fwPEQ; //PEQ in the bw direction 67 68 69 //an instance specific extension that tells us whether we are in a wrapped b_transport or not 70 class BTag : public tlm_utils::instance_specific_extension<BTag>{ 71 public: 72 sc_core::sc_event event; //trigger this event when transaction is done 73 }; 74 75 //an instance specific extension that holds information about source and sink of a txn 76 // as well as information if the req still has to be cleared and if the txn is already 77 // complete on the target side 78 class ConnectionInfo : public tlm_utils::instance_specific_extension<ConnectionInfo>{ 79 public: 80 unsigned int fwID; //socket number of sink 81 unsigned int bwID; //socket number of source 82 bool clearReq; //is the txn still in req phase? 83 bool alreadyComplete; //has the txn already completed on the target side? 84 }; 85 86 class internalPEQTypes{ //use the tpPEQ to delay connection infos 87 public: 88 typedef ConnectionInfo tlm_payload_type; 89 typedef tlm::tlm_phase tlm_phase_type; 90 }; 91 ExtensionPool<ConnectionInfo> m_connInfoPool; //our pool of extensions 92 unsigned int m_target_count; //number of connected targets (see bindTargetSocket for explanation) 93 94public: 95 SC_HAS_PROCESS(MultiSocketSimpleSwitchAT); 96 MultiSocketSimpleSwitchAT(sc_core::sc_module_name name) : 97 sc_core::sc_module(name), 98 target_socket("target_socket"), 99 initiator_socket("initiator_socket"), 100 m_bwPEQ(this, &MultiSocketSimpleSwitchAT::bwPEQcb), 101 m_fwPEQ(this, &MultiSocketSimpleSwitchAT::fwPEQcb), 102 m_connInfoPool(10), 103 m_target_count(0) 104 { 105 target_socket.register_nb_transport_fw(this, &MultiSocketSimpleSwitchAT::initiatorNBTransport); 106 target_socket.register_b_transport(this, &MultiSocketSimpleSwitchAT::b_transport); 107 initiator_socket.register_nb_transport_bw(this, &MultiSocketSimpleSwitchAT::targetNBTransport); 108 } 109 110 void bindTargetSocket(initiator_socket_type::base_target_socket_type& target 111 ,sc_dt::uint64 low 112 ,sc_dt::uint64 high 113 ,sc_dt::uint64 mask = 0xffffffffffffffffULL){ 114 initiator_socket(target); //bind sockets 115 //insert into address map and increase target count 116 // (we have to count the targets manually, because target_socket.size() is only reliable during simulation 117 // as it gets evaluated during end_of_elaboration) 118 m_addrMap.insert(low, high, m_target_count++); 119 m_masks.push_back(mask); //add the mask for this target 120 } 121 122 unsigned int decode(const sc_dt::uint64& address) 123 { 124 return m_addrMap.decode(address); 125 } 126 127 void start_of_simulation(){ 128 //initialize the lists of pending reqs and resps 129 m_pendingReqs.resize(initiator_socket.size()); 130 m_pendingResps.resize(target_socket.size()); 131 } 132 133 134 void b_transport(int initiator_id, transaction_type& trans, sc_core::sc_time& t){ 135 //first make sure that there is no BTag (just for debugging) 136 BTag* btag; 137 accessMySpecificExtensions(trans).get_extension(btag); 138 sc_assert(!btag); 139 BTag tag; //now add our BTag 140 bool added_mm=!trans.has_mm(); //in case there is no MM in we add it now 141 if (added_mm){ 142 trans.set_mm(this); 143 trans.acquire(); //acquire the txn 144 } 145 accessMySpecificExtensions(trans).set_extension(&tag); 146 phase_type phase=tlm::BEGIN_REQ; //then simply use our nb implementation (respects all the rules) 147 initiatorNBTransport(initiator_id, trans, phase, t); 148 wait(tag.event); //and wait for the event to be triggered 149 if (added_mm){ //if we added MM 150 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) 151 if (trans.get_ref_count()) 152 wait(tag.event); //wait for the ref count to get to zero 153 trans.set_mm(NULL); //remove the MM 154 } 155 //don't forget to remove the extension (instance specific extensions are not cleared off by MM) 156 accessMySpecificExtensions(trans).clear_extension(&tag); 157 } 158 159 void free(transaction_type* txn){ 160 BTag* btag; 161 accessMySpecificExtensions(*txn).get_extension(btag); 162 sc_assert(btag); 163 txn->reset(); //clean off all extension that were added down stream 164 btag->event.notify(); 165 } 166 167 //do a fw transmission 168 void initiatorNBTransport_core(transaction_type& trans, 169 phase_type& phase, 170 sc_core::sc_time& t, 171 unsigned int tgtSocketNumber){ 172 switch (initiator_socket[tgtSocketNumber]->nb_transport_fw(trans, phase, t)) { 173 case tlm::TLM_ACCEPTED: 174 case tlm::TLM_UPDATED: 175 // Transaction not yet finished 176 if (phase != tlm::BEGIN_REQ) 177 { 178 sc_assert(phase!=tlm::END_RESP); 179 m_bwPEQ.notify(trans,phase,t); 180 } 181 break; 182 case tlm::TLM_COMPLETED: 183 // Transaction finished 184 ConnectionInfo* connInfo; 185 accessMySpecificExtensions(trans).get_extension(connInfo); 186 sc_assert(connInfo); 187 connInfo->alreadyComplete=true; 188 phase=tlm::BEGIN_RESP; 189 m_bwPEQ.notify(trans, phase, t); 190 break; 191 default: 192 sc_assert(0); exit(1); 193 }; 194 } 195 196 //nb_transport_fw 197 sync_enum_type initiatorNBTransport(int initiator_id, 198 transaction_type& trans, 199 phase_type& phase, 200 sc_core::sc_time& t) 201 { 202 ConnectionInfo* connInfo; 203 accessMySpecificExtensions(trans).get_extension(connInfo); 204 m_fwPEQ.notify(trans,phase,t); 205 if (phase==tlm::BEGIN_REQ){ 206 //add our private information to the txn 207 sc_assert(!connInfo); 208 connInfo=m_connInfoPool.construct(); 209 connInfo->fwID=decode(trans.get_address()); 210 connInfo->bwID=initiator_id; 211 connInfo->clearReq=true; 212 connInfo->alreadyComplete=false; 213 accessMySpecificExtensions(trans).set_extension(connInfo); 214 } 215 else 216 if (phase==tlm::END_RESP){ 217 return tlm::TLM_COMPLETED; 218 } 219 else 220 {sc_assert(0); exit(1);} 221 return tlm::TLM_ACCEPTED; 222 } 223 224 sync_enum_type targetNBTransport(int portId, 225 transaction_type& trans, 226 phase_type& phase, 227 sc_core::sc_time& t) 228 { 229 if (phase != tlm::END_REQ && phase != tlm::BEGIN_RESP) { 230 std::cout << "ERROR: '" << name() 231 << "': Illegal phase received from target." << std::endl; 232 sc_assert(false); exit(1); 233 } 234 //simply stuff it into the bw PEQ 235 m_bwPEQ.notify(trans,phase,t); 236 return tlm::TLM_ACCEPTED; 237 } 238 239 void bwPEQcb(transaction_type& trans, const phase_type& phase){ 240 //first get our private info from the txn 241 ConnectionInfo* connInfo; 242 accessMySpecificExtensions(trans).get_extension(connInfo); 243 sc_assert(connInfo); 244 phase_type p=phase; 245 sc_core::sc_time t=sc_core::SC_ZERO_TIME; 246 BTag* btag; 247 accessMySpecificExtensions(trans).get_extension(btag); 248 bool doCall=btag==NULL; //we only will do a bw call if we are not in a wrapped b_transport 249 if ((phase==tlm::END_REQ) | (connInfo->clearReq)){ //in case the target left out end_req clearReq reminds us to unlock the req port 250 sc_assert(m_pendingReqs[connInfo->fwID].size()); 251 sc_assert(m_pendingReqs[connInfo->fwID].front()==&trans); 252 m_pendingReqs[connInfo->fwID].pop_front(); //allow another req to start at this target 253 if (m_pendingReqs[connInfo->fwID].size()){ //there was a pending req 254 phase_type ph=tlm::BEGIN_REQ; 255 initiatorNBTransport_core(*m_pendingReqs[connInfo->fwID].front(), ph, t,connInfo->fwID); 256 } 257 connInfo->clearReq=false; 258 } 259 //no else here, since we might clear the req AND begin a resp 260 if (phase==tlm::BEGIN_RESP){ 261 m_pendingResps[connInfo->bwID].push_back(&trans); 262 doCall=m_pendingResps[connInfo->bwID].size()==1; //do a call in case the response socket was free 263 } 264 265 if (doCall){ //we have to do a call on the bw of fw path 266 if (btag){ //only possible if BEGIN_RESP and resp socket was free 267 phase_type ph=tlm::END_RESP; 268 m_fwPEQ.notify(trans, ph, t); 269 } 270 else 271 switch (target_socket[connInfo->bwID]->nb_transport_bw(trans, p, t)){ 272 case tlm::TLM_ACCEPTED: 273 case tlm::TLM_UPDATED: 274 break; 275 case tlm::TLM_COMPLETED:{ 276 //covers a piggy bagged END_RESP to START_RESP 277 phase_type ph=tlm::END_RESP; 278 m_fwPEQ.notify(trans, ph, t); 279 } 280 break; 281 default: 282 sc_assert(0); exit(1); 283 284 }; 285 } 286 } 287 288 //the following two functions (fwPEQcb and clearPEQcb) could be one, if we were allowed 289 // to stick END_RESP into a PEQ 290 void fwPEQcb(transaction_type& trans, const phase_type& phase){ 291 ConnectionInfo* connInfo; 292 accessMySpecificExtensions(trans).get_extension(connInfo); 293 sc_assert(connInfo); 294 phase_type ph=phase; 295 sc_core::sc_time t=sc_core::SC_ZERO_TIME; 296 if (phase==tlm::BEGIN_REQ){ 297 trans.set_address(trans.get_address()&m_masks[connInfo->fwID]); //mask address 298 m_pendingReqs[connInfo->fwID].push_back(&trans); 299 if (m_pendingReqs[connInfo->fwID].size()==1){ //the socket is free 300 initiatorNBTransport_core(trans, ph, t, connInfo->fwID); 301 } 302 } 303 else 304 { 305 //phase is always END_RESP 306 BTag* btag; 307 accessMySpecificExtensions(trans).get_extension(btag); 308 accessMySpecificExtensions(trans).clear_extension(connInfo); //remove our specific extension as it is not needed any more 309 if (!connInfo->alreadyComplete) { 310 sync_enum_type tmp=initiator_socket[connInfo->fwID]->nb_transport_fw(trans, ph, t); 311 sc_assert(tmp==tlm::TLM_COMPLETED); 312 } 313 sc_assert(m_pendingResps[connInfo->bwID].size()); 314 m_pendingResps[connInfo->bwID].pop_front(); //remove current response 315 if (m_pendingResps[connInfo->bwID].size()){ //if there was one pending 316 ph=tlm::BEGIN_RESP; //schedule its transmission 317 m_bwPEQ.notify(*m_pendingResps[connInfo->bwID].front(),ph,t); 318 } 319 m_connInfoPool.free(connInfo); //release connInfo 320 if (btag) btag->event.notify(t); //release b_transport 321 } 322 } 323 324 void dump_status(){ 325 std::cout<<"At "<<sc_core::sc_time_stamp()<<" status of "<<name()<<" is "<<std::endl 326 <<" Number of connected initiators: "<<target_socket.size()<<std::endl 327 <<" Number of connected targets: "<<initiator_socket.size()<<std::endl 328 <<" Pending requests:"<<std::endl; 329 for (unsigned int i=0; i<m_pendingReqs.size(); i++) 330 std::cout<<" "<<m_pendingReqs[i].size()<<" pending requests for target number "<<i<<std::endl; 331 std::cout<<" Pending responses:"<<std::endl; 332 for (unsigned int i=0; i<m_pendingResps.size(); i++) 333 std::cout<<" "<<m_pendingResps[i].size()<<" pending responses for initiator number "<<i<<std::endl; 334 std::cout<<" The address map is:"<<std::endl; 335 m_addrMap.dumpMap(); 336 337 } 338}; 339 340#endif 341