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