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#ifndef __MULTI_PASSTHROUGH_TARGET_SOCKET_H__
20#define __MULTI_PASSTHROUGH_TARGET_SOCKET_H__
21
22#include "tlm_utils/multi_socket_bases.h"
23#include <sstream>
24
25namespace tlm_utils {
26
27/*
28This class implements a trivial multi target socket.
29The triviality refers to the fact that the socket does not
30do blocking to non-blocking or non-blocking to blocking conversions.
31
32It allows to connect multiple initiators to this socket.
33The user has to register callbacks for the fw interface methods
34he likes to use. The callbacks are basically equal to the fw interface
35methods but carry an additional integer that indicates to which
36index of this socket the calling initiator is connected.
37*/
38template <typename MODULE,
39          unsigned int BUSWIDTH = 32,
40          typename TYPES = tlm::tlm_base_protocol_types,
41          unsigned int N=0
42#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
43          ,sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND
44#endif
45          >
46class multi_passthrough_target_socket: public multi_target_base< BUSWIDTH,
47                                                        TYPES,
48                                                        N
49#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
50                                                        ,POL
51#endif
52                                                        >
53                              , public multi_to_multi_bind_base<TYPES>
54{
55
56public:
57
58  //typedefs
59  //  tlm 2.0 types for nb_transport
60  typedef typename TYPES::tlm_payload_type              transaction_type;
61  typedef typename TYPES::tlm_phase_type                phase_type;
62  typedef tlm::tlm_sync_enum                            sync_enum_type;
63
64  //  typedefs to keep the fn ptr notations short
65  typedef sync_enum_type (MODULE::*nb_cb)(int, transaction_type&, phase_type&, sc_core::sc_time&);
66  typedef void (MODULE::*b_cb)(int, transaction_type&, sc_core::sc_time&);
67  typedef unsigned int (MODULE::*dbg_cb)(int, transaction_type& txn);
68  typedef bool (MODULE::*dmi_cb)(int, transaction_type& txn, tlm::tlm_dmi& dmi);
69
70  typedef multi_target_base<BUSWIDTH,
71                        TYPES,
72                        N
73#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
74                        ,POL
75#endif
76                        > base_type;
77
78  typedef typename base_type::base_initiator_socket_type base_initiator_socket_type;
79
80  //CTOR
81  multi_passthrough_target_socket()
82      : base_type(sc_core::sc_gen_unique_name("multi_passthrough_target_socket"))
83      , m_hierarch_bind(0)
84      , m_eoe_disabled(false)
85      , m_export_callback_created(false)
86  {
87  }
88
89  //CTOR
90  multi_passthrough_target_socket(const char* name)
91      : base_type(name)
92      , m_hierarch_bind(0)
93      , m_eoe_disabled(false)
94      , m_export_callback_created(false)
95  {
96  }
97
98  ~multi_passthrough_target_socket(){
99    //clean up everything allocated by 'new'
100    for (unsigned int i=0; i<m_binders.size(); i++) delete m_binders[i];
101  }
102
103  //simple helpers for warnings an errors to shorten in code notation
104  void display_warning(const std::string& text) const {
105    std::stringstream s;
106    s<<"WARNING in instance "<<base_type::name()<<": "<<text;
107    SC_REPORT_WARNING("/OSCI_TLM-2/multi_socket", s.str().c_str());
108  }
109
110  void display_error(const std::string& text) const {
111    std::stringstream s;
112    s<<"ERROR in instance "<<base_type::name()<<": "<<text;
113    SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket", s.str().c_str());
114  }
115
116  void check_export_binding()
117  {
118    //if our export hasn't been bound yet (due to a hierarch binding)
119    //  we bind it now.
120    //We do that here as the user of the target port HAS to bind at least on callback,
121    //otherwise the socket was useless. Nevertheless, the target socket may still
122    // stay unbound afterwards.
123    if (!sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >::get_interface())
124    {
125      // We bind to a callback_binder that will be used as the first interface
126      // i.e. calls to the sc_export will have the same ID as calls from the first initator
127      // socket bound
128      callback_binder_fw<TYPES> * binder;
129
130      if (m_binders.size() == 0)
131      {
132        binder = new callback_binder_fw<TYPES>(m_binders.size());
133        m_binders.push_back(binder);
134        m_export_callback_created = true;
135      }
136      else
137      {
138        binder = m_binders[0];
139      }
140
141      sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >::bind(*binder);
142    }
143  }
144
145  //register callback for nb transport of fw interface
146  void register_nb_transport_fw(MODULE* mod,
147                              nb_cb cb)
148  {
149    check_export_binding();
150
151    //warn if there already is a callback
152    if (!m_nb_f.empty()){
153      display_warning("NBTransport_bw callback already registered.");
154      return;
155    }
156
157    //set the functor
158    m_nb_f.set_function(mod, cb);
159  }
160
161  //register callback for b transport of fw interface
162  void register_b_transport(MODULE* mod,
163                              b_cb cb)
164  {
165    check_export_binding();
166
167    //warn if there already is a callback
168    if (!m_b_f.empty()){
169      display_warning("BTransport callback already registered.");
170      return;
171    }
172
173    //set the functor
174    m_b_f.set_function(mod, cb);
175  }
176
177  //register callback for debug transport of fw interface
178  void register_transport_dbg(MODULE* mod,
179                              dbg_cb cb)
180  {
181    check_export_binding();
182
183    //warn if there already is a callback
184    if (!m_dbg_f.empty()){
185      display_warning("DebugTransport callback already registered.");
186      return;
187    }
188
189    //set the functor
190    m_dbg_f.set_function(mod, cb);
191  }
192
193  //register callback for DMI of fw interface
194  void register_get_direct_mem_ptr(MODULE* mod,
195                                   dmi_cb cb)
196  {
197    check_export_binding();
198
199    //warn if there already is a callback
200    if (!m_dmi_f.empty()){
201      display_warning("DMI callback already registered.");
202      return;
203    }
204
205    //set the functor
206    m_dmi_f.set_function(mod, cb);
207  }
208
209
210  //Override virtual functions of the tlm_target_socket:
211  // this function is called whenever an sc_port (as part of a init socket)
212  //  wants to bind to the export of the underlying tlm_target_socket
213  //At this time a callback binder is created an returned to the sc_port
214  // of the init socket, so that it binds to the callback binder
215  virtual tlm::tlm_fw_transport_if<TYPES>& get_base_interface()
216  {
217    //error if this socket is already bound hierarchically
218    if (m_hierarch_bind) display_error("Socket already bound hierarchically.");
219
220    if (!m_export_callback_created)
221      m_binders.push_back(new callback_binder_fw<TYPES>(m_binders.size()));
222    else
223      m_export_callback_created = false;
224
225    return *m_binders[m_binders.size()-1];
226  }
227
228  // const overload not allowed for multi-sockets
229  virtual const tlm::tlm_fw_transport_if<TYPES>& get_base_interface() const
230  {
231    display_error("'get_base_interface()' const not allowed for multi-sockets.");
232    return base_type::get_base_interface();
233  }
234
235  //just return the export of the underlying tlm_target_socket in case of a hierarchical bind
236  virtual sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >& get_base_export()
237  {
238    return *this;
239  }
240
241  //just return the export of the underlying tlm_target_socket in case of a hierarchical bind
242  virtual const sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >& get_base_export() const
243  {
244    return base_type::get_base_export();
245  }
246
247  //the standard end of elaboration callback
248  void end_of_elaboration(){
249    //'break' here if the socket was told not to do callback binding
250    if (m_eoe_disabled) return;
251
252    //get the callback binders and the multi binds of the top of the hierachical bind chain
253    // NOTE: this could be the same socket if there is no hierachical bind
254    std::vector<callback_binder_fw<TYPES>* >& binders=get_hierarch_bind()->get_binders();
255    std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*>&  multi_binds=get_hierarch_bind()->get_multi_binds();
256
257    //iterate over all binders
258    for (unsigned int i=0; i<binders.size(); i++) {
259      binders[i]->set_callbacks(m_nb_f, m_b_f, m_dmi_f, m_dbg_f); //set the callbacks for the binder
260      if (multi_binds.find(i)!=multi_binds.end()) //check if this connection is multi-multi
261        //if so remember the interface
262        m_sockets.push_back(multi_binds[i]);
263      else{ //if we are bound to a normal socket
264        //get the calling port and try to cast it into a tlm socket base
265        base_initiator_socket_type* test=dynamic_cast<base_initiator_socket_type*>(binders[i]->get_other_side());
266        if (!test){display_error("Not bound to tlm_socket.");}
267        m_sockets.push_back(&test->get_base_interface()); //remember the interface
268      }
269    }
270  }
271
272  //
273  // Bind multi target socket to multi target socket (hierarchical bind)
274  //
275  virtual void bind(base_type& s)
276  {
277    //warn if already bound hierarchically
278    if (m_eoe_disabled){
279      display_warning("Socket already bound hierarchically. Bind attempt ignored.");
280      return;
281    }
282
283    //disable our own end of elaboration call
284    disable_cb_bind();
285
286    //inform the bound target socket that it is bound hierarchically now
287    s.set_hierarch_bind((base_type*)this);
288    base_type::bind(s); //satisfy SystemC
289  }
290
291  //operator notation for hierarchical bind
292  void operator() (base_type& s)
293  {
294    bind(s);
295  }
296
297  //get access to sub port
298  tlm::tlm_bw_transport_if<TYPES>* operator[](int i){return m_sockets[i];}
299
300  //get number of bound initiators
301  // NOTE: this is only valid at end of elaboration!
302  unsigned int size(){return get_hierarch_bind()->get_binders().size();}
303
304protected:
305  //implementation of base class interface
306  base_type* get_hierarch_bind(){if (m_hierarch_bind) return m_hierarch_bind->get_hierarch_bind(); else return this;}
307  std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*>&  get_multi_binds(){return m_multi_binds;}
308  void set_hierarch_bind(base_type* h){m_hierarch_bind=h;}
309  tlm::tlm_fw_transport_if<TYPES>* get_last_binder(tlm::tlm_bw_transport_if<TYPES>* other){
310    m_multi_binds[m_binders.size()-1]=other;
311    return m_binders[m_binders.size()-1];
312  }
313
314  //map that stores to which index a multi init socket is connected
315  // and the interface of the multi init socket
316  std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*> m_multi_binds;
317
318  void disable_cb_bind(){ m_eoe_disabled=true;}
319  std::vector<callback_binder_fw<TYPES>* >& get_binders(){return m_binders;}
320  //vector of connected sockets
321  std::vector<tlm::tlm_bw_transport_if<TYPES>*> m_sockets;
322  //vector of binders that convert untagged interface into tagged interface
323  std::vector<callback_binder_fw<TYPES>*> m_binders;
324
325  base_type*  m_hierarch_bind; //pointer to hierarchical bound multi port
326  bool m_eoe_disabled; //bool that diables callback bindings at end of elaboration
327  bool m_export_callback_created; // bool to indicate that a callback has already been created for export binding
328
329  //callbacks as functors
330  // (allows to pass the callback to another socket that does not know the type of the module that owns
331  //  the callbacks)
332  typename callback_binder_fw<TYPES>::nb_func_type    m_nb_f;
333  typename callback_binder_fw<TYPES>::b_func_type     m_b_f;
334  typename callback_binder_fw<TYPES>::debug_func_type m_dbg_f;
335  typename callback_binder_fw<TYPES>::dmi_func_type   m_dmi_f;
336};
337
338}
339
340#endif
341