simple_target_socket.h revision 13511:dc5864c73df3
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// *****************************************************************************
21// Modified by John Aynsley, Doulos, Feb 2009,
22// Fix a bug in simple_target_socket and simple_target_socket_tagged
23// with the addition of one new line of code in each:  wait(*e);
24// *****************************************************************************
25
26// *****************************************************************************
27// Modified by John Aynsley on behalf of Robert Guenzel, May 2011,
28// Fix a bug in simple_target_socket and simple_target_socket_tagged
29// with the addition of one new line of code in each:  wait(t);
30// *****************************************************************************
31
32
33#ifndef TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_
34#define TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_
35
36#ifndef SC_INCLUDE_DYNAMIC_PROCESSES // needed for sc_spawn
37#  define SC_INCLUDE_DYNAMIC_PROCESSES
38#endif
39
40#include <systemc>
41#include <tlm>
42#include "tlm_utils/convenience_socket_bases.h"
43#include "tlm_utils/peq_with_get.h"
44
45namespace tlm_utils {
46
47template< typename MODULE, unsigned int BUSWIDTH, typename TYPES
48        , sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND >
49class simple_target_socket_b
50  : public tlm::tlm_target_socket<BUSWIDTH, TYPES, 1, POL>
51  , protected simple_socket_base
52{
53  friend class fw_process;
54  friend class bw_process;
55public:
56  typedef typename TYPES::tlm_payload_type              transaction_type;
57  typedef typename TYPES::tlm_phase_type                phase_type;
58  typedef tlm::tlm_sync_enum                            sync_enum_type;
59  typedef tlm::tlm_fw_transport_if<TYPES>               fw_interface_type;
60  typedef tlm::tlm_bw_transport_if<TYPES>               bw_interface_type;
61  typedef tlm::tlm_target_socket<BUSWIDTH,TYPES,1,POL>  base_type;
62
63public:
64  static const char* default_name()
65    { return sc_core::sc_gen_unique_name("simple_target_socket"); }
66
67  explicit simple_target_socket_b(const char* n = default_name())
68    : base_type(n)
69    , m_fw_process(this)
70    , m_bw_process(this)
71  {
72    bind(m_fw_process);
73  }
74
75  using base_type::bind;
76
77  // bw transport must come thru us.
78  tlm::tlm_bw_transport_if<TYPES> * operator ->() {return &m_bw_process;}
79
80  // REGISTER_XXX
81  void register_nb_transport_fw(MODULE* mod,
82                                sync_enum_type (MODULE::*cb)(transaction_type&,
83                                                             phase_type&,
84                                                             sc_core::sc_time&))
85  {
86    elaboration_check("register_nb_transport_fw");
87    m_fw_process.set_nb_transport_ptr(mod, cb);
88  }
89
90  void register_b_transport(MODULE* mod,
91                            void (MODULE::*cb)(transaction_type&,
92                                               sc_core::sc_time&))
93  {
94    elaboration_check("register_b_transport");
95    m_fw_process.set_b_transport_ptr(mod, cb);
96  }
97
98  void register_transport_dbg(MODULE* mod,
99                              unsigned int (MODULE::*cb)(transaction_type&))
100  {
101    elaboration_check("register_transport_dbg");
102    m_fw_process.set_transport_dbg_ptr(mod, cb);
103  }
104
105  void register_get_direct_mem_ptr(MODULE* mod,
106                                   bool (MODULE::*cb)(transaction_type&,
107                                                      tlm::tlm_dmi&))
108  {
109    elaboration_check("register_get_direct_mem_ptr");
110    m_fw_process.set_get_direct_mem_ptr(mod, cb);
111  }
112
113protected:
114  void start_of_simulation()
115  {
116    base_type::start_of_simulation();
117    m_fw_process.start_of_simulation();
118  }
119
120private:
121  //make call on bw path.
122  sync_enum_type bw_nb_transport(transaction_type &trans, phase_type &phase, sc_core::sc_time &t)
123  {
124    return base_type::operator ->()->nb_transport_bw(trans, phase, t);
125  }
126
127  void bw_invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e)
128  {
129    base_type::operator ->()->invalidate_direct_mem_ptr(s, e);
130  }
131
132  //Helper class to handle bw path calls
133  // Needed to detect transaction end when called from b_transport.
134  class bw_process : public tlm::tlm_bw_transport_if<TYPES>
135  {
136  public:
137    bw_process(simple_target_socket_b *p_own) : m_owner(p_own)
138    {
139    }
140
141    sync_enum_type nb_transport_bw(transaction_type &trans, phase_type &phase, sc_core::sc_time &t)
142    {
143      typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
144        m_owner->m_pending_trans.find(&trans);
145
146      if(it == m_owner->m_pending_trans.end()) {
147        // Not a blocking call, forward.
148        return m_owner->bw_nb_transport(trans, phase, t);
149
150      }
151
152      if (phase == tlm::END_REQ) {
153        m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
154        return tlm::TLM_ACCEPTED;
155      }
156      if (phase == tlm::BEGIN_RESP) {
157        if (m_owner->m_current_transaction == &trans) {
158          m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
159        }
160        //TODO: add response-accept delay?
161        it->second->notify(t);
162        m_owner->m_pending_trans.erase(it);
163        return tlm::TLM_COMPLETED;
164      }
165      m_owner->display_error("invalid phase received");
166      return tlm::TLM_COMPLETED;
167    }
168
169    void invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e)
170    {
171      return m_owner->bw_invalidate_direct_mem_ptr(s, e);
172    }
173
174  private:
175    simple_target_socket_b *m_owner;
176  };
177
178  class fw_process : public tlm::tlm_fw_transport_if<TYPES>,
179                    public tlm::tlm_mm_interface
180  {
181  public:
182    typedef sync_enum_type (MODULE::*NBTransportPtr)(transaction_type&,
183                                                     phase_type&,
184                                                     sc_core::sc_time&);
185    typedef void (MODULE::*BTransportPtr)(transaction_type&,
186                                          sc_core::sc_time&);
187    typedef unsigned int (MODULE::*TransportDbgPtr)(transaction_type&);
188    typedef bool (MODULE::*GetDirectMemPtr)(transaction_type&,
189                                            tlm::tlm_dmi&);
190
191    fw_process(simple_target_socket_b *p_own) :
192      m_owner(p_own),
193      m_mod(0),
194      m_nb_transport_ptr(0),
195      m_b_transport_ptr(0),
196      m_transport_dbg_ptr(0),
197      m_get_direct_mem_ptr(0),
198      m_peq(sc_core::sc_gen_unique_name("m_peq")),
199      m_response_in_progress(false)
200    {}
201
202    void start_of_simulation()
203    {
204      if (!m_b_transport_ptr && m_nb_transport_ptr) { // only spawn b2nb_thread, if needed
205        sc_core::sc_spawn_options opts;
206        opts.set_sensitivity(&m_peq.get_event());
207        opts.dont_initialize();
208        sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this),
209                          sc_core::sc_gen_unique_name("b2nb_thread"), &opts);
210      }
211    }
212
213    void set_nb_transport_ptr(MODULE* mod, NBTransportPtr p)
214    {
215      if (m_nb_transport_ptr) {
216        m_owner->display_warning("non-blocking callback already registered");
217        return;
218      }
219      sc_assert(!m_mod || m_mod == mod);
220      m_mod = mod;
221      m_nb_transport_ptr = p;
222    }
223
224    void set_b_transport_ptr(MODULE* mod, BTransportPtr p)
225    {
226      if (m_b_transport_ptr) {
227        m_owner->display_warning("blocking callback already registered");
228        return;
229      }
230      sc_assert(!m_mod || m_mod == mod);
231      m_mod = mod;
232      m_b_transport_ptr = p;
233    }
234
235    void set_transport_dbg_ptr(MODULE* mod, TransportDbgPtr p)
236    {
237      if (m_transport_dbg_ptr) {
238        m_owner->display_warning("debug callback already registered");
239        return;
240      }
241      sc_assert(!m_mod || m_mod == mod);
242      m_mod = mod;
243      m_transport_dbg_ptr = p;
244    }
245
246    void set_get_direct_mem_ptr(MODULE* mod, GetDirectMemPtr p)
247    {
248      if (m_get_direct_mem_ptr) {
249        m_owner->display_warning("get DMI pointer callback already registered");
250        return;
251      }
252      sc_assert(!m_mod || m_mod == mod);
253      m_mod = mod;
254      m_get_direct_mem_ptr = p;
255    }
256// Interface implementation
257    sync_enum_type nb_transport_fw(transaction_type& trans,
258                                   phase_type& phase,
259                                   sc_core::sc_time& t)
260    {
261      if (m_nb_transport_ptr) {
262        // forward call
263        sc_assert(m_mod);
264        return (m_mod->*m_nb_transport_ptr)(trans, phase, t);
265      }
266
267      // nb->b conversion
268      if (m_b_transport_ptr) {
269        if (phase == tlm::BEGIN_REQ) {
270          // prepare thread to do blocking call
271          process_handle_class * ph = m_process_handle.get_handle(&trans);
272
273          if (!ph) { // create new dynamic process
274            ph = new process_handle_class(&trans);
275            m_process_handle.put_handle(ph);
276
277            sc_core::sc_spawn_options opts;
278            opts.dont_initialize();
279            opts.set_sensitivity(&ph->m_e);
280
281            sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread,this, ph),
282                            sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
283          }
284
285          ph->m_e.notify(t);
286          return tlm::TLM_ACCEPTED;
287        }
288        if (phase == tlm::END_RESP) {
289          m_response_in_progress = false;
290          m_end_response.notify(t);
291          return tlm::TLM_COMPLETED;
292        }
293        m_owner->display_error("invalid phase received");
294        return tlm::TLM_COMPLETED;
295      }
296      m_owner->display_error("no non-blocking transport callback registered");
297      return tlm::TLM_COMPLETED;
298    }
299
300    void b_transport(transaction_type& trans, sc_core::sc_time& t)
301    {
302      if (m_b_transport_ptr) {
303        // forward call
304        sc_assert(m_mod);
305        (m_mod->*m_b_transport_ptr)(trans, t);
306        return;
307      }
308
309      // b->nb conversion
310      if (m_nb_transport_ptr) {
311        m_peq.notify(trans, t);
312        t = sc_core::SC_ZERO_TIME;
313
314        mm_end_event_ext mm_ext;
315        const bool mm_added = !trans.has_mm();
316
317        if (mm_added) {
318          trans.set_mm(this);
319          trans.set_auto_extension(&mm_ext);
320          trans.acquire();
321        }
322
323        // wait until transaction is finished
324        sc_core::sc_event end_event;
325        m_owner->m_pending_trans[&trans] = &end_event;
326        sc_core::wait(end_event);
327
328        if (mm_added) {
329          // release will not delete the transaction, it will notify mm_ext.done
330          trans.release();
331          if (trans.get_ref_count()) {
332            sc_core::wait(mm_ext.done);
333          }
334          trans.set_mm(0);
335        }
336        return;
337      }
338
339      // should not be reached
340      m_owner->display_error("no blocking transport callback registered");
341    }
342
343    unsigned int transport_dbg(transaction_type& trans)
344    {
345      if (m_transport_dbg_ptr) {
346        // forward call
347        sc_assert(m_mod);
348        return (m_mod->*m_transport_dbg_ptr)(trans);
349      }
350      // No debug support
351      return 0;
352    }
353
354    bool get_direct_mem_ptr(transaction_type& trans,
355                            tlm::tlm_dmi&  dmi_data)
356    {
357      if (m_get_direct_mem_ptr) {
358        // forward call
359        sc_assert(m_mod);
360        return (m_mod->*m_get_direct_mem_ptr)(trans, dmi_data);
361      }
362      // No DMI support
363      dmi_data.allow_read_write();
364      dmi_data.set_start_address(0x0);
365      dmi_data.set_end_address((sc_dt::uint64)-1);
366      return false;
367    }
368
369  private:
370
371// dynamic process handler for nb2b conversion
372
373    class process_handle_class {
374    public:
375      explicit process_handle_class(transaction_type * trans)
376        : m_trans(trans),m_suspend(false) {}
377
378      transaction_type*  m_trans;
379      sc_core::sc_event  m_e;
380      bool m_suspend;
381    };
382
383    class process_handle_list {
384    public:
385      process_handle_list() {}
386
387      ~process_handle_list() {
388        for( typename std::vector<process_handle_class*>::iterator
389               it=v.begin(), end = v.end(); it != end; ++it )
390          delete *it;
391      }
392
393      process_handle_class* get_handle(transaction_type *trans)
394      {
395        typename std::vector<process_handle_class*>::iterator it;
396
397        for(it = v.begin(); it != v.end(); it++) {
398          if ((*it)->m_suspend) {  // found suspended dynamic process, re-use it
399            (*it)->m_trans   = trans; // replace to new one
400            (*it)->m_suspend = false;
401            return *it;
402          }
403        }
404        return NULL; // no suspended process
405      }
406
407      void put_handle(process_handle_class* ph)
408      {
409        v.push_back(ph);
410      }
411
412    private:
413      std::vector<process_handle_class*> v;
414    };
415
416    process_handle_list m_process_handle;
417
418
419    void nb2b_thread(process_handle_class* h)
420    {
421
422      while(1) {
423        transaction_type *trans = h->m_trans;
424        sc_core::sc_time t = sc_core::SC_ZERO_TIME;
425
426        // forward call
427        sc_assert(m_mod);
428        (m_mod->*m_b_transport_ptr)(*trans, t);
429
430        sc_core::wait(t);
431
432        // return path
433        while (m_response_in_progress) {
434          sc_core::wait(m_end_response);
435        }
436        t = sc_core::SC_ZERO_TIME;
437        phase_type phase    = tlm::BEGIN_RESP;
438        sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
439        if ( !(sync == tlm::TLM_COMPLETED ||
440              (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP)) ) {
441          m_response_in_progress = true;
442        }
443
444        // suspend until next transaction
445        h->m_suspend = true;
446        sc_core::wait();
447      }
448    }
449
450    void b2nb_thread()
451    {
452      while (true) {
453        transaction_type* trans;
454        while ((trans = m_peq.get_next_transaction())!=0) {
455          sc_assert(m_mod);
456          sc_assert(m_nb_transport_ptr);
457          phase_type phase = tlm::BEGIN_REQ;
458          sc_core::sc_time t = sc_core::SC_ZERO_TIME;
459
460          switch ((m_mod->*m_nb_transport_ptr)(*trans, phase, t)) {
461          case tlm::TLM_COMPLETED:
462          {
463            // notify transaction is finished
464            typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
465              m_owner->m_pending_trans.find(trans);
466            sc_assert(it != m_owner->m_pending_trans.end());
467            it->second->notify(t);
468            m_owner->m_pending_trans.erase(it);
469            break;
470          }
471
472          case tlm::TLM_ACCEPTED:
473          case tlm::TLM_UPDATED:
474            switch (phase) {
475            case tlm::BEGIN_REQ:
476              m_owner->m_current_transaction = trans;
477              sc_core::wait(m_owner->m_end_request);
478              m_owner->m_current_transaction = 0;
479              break;
480
481            case tlm::END_REQ:
482              sc_core::wait(t);
483              break;
484
485            case tlm::BEGIN_RESP:
486            {
487              phase = tlm::END_RESP;
488              sc_core::wait(t);  // This line is a bug fix added in TLM-2.0.2
489              t = sc_core::SC_ZERO_TIME;
490              (m_mod->*m_nb_transport_ptr)(*trans, phase, t);
491
492              // notify transaction is finished
493              typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
494                m_owner->m_pending_trans.find(trans);
495              sc_assert(it != m_owner->m_pending_trans.end());
496              it->second->notify(t);
497              m_owner->m_pending_trans.erase(it);
498              break;
499            }
500
501            default:
502              m_owner->display_error("invalid phase received");
503            }
504            break;
505
506          default:
507            m_owner->display_error("invalid sync value received");
508          }
509        }
510        sc_core::wait();
511      }
512    }
513
514    void free(tlm::tlm_generic_payload* trans)
515    {
516      mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>();
517      sc_assert(ext);
518      // notif event first before freeing extensions (reset)
519      ext->done.notify();
520      trans->reset();
521    }
522
523  private:
524    struct mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext>
525    {
526      tlm::tlm_extension_base* clone() const { return NULL; }
527      void free() {}
528      void copy_from(tlm::tlm_extension_base const &) {}
529      sc_core::sc_event done;
530    };
531
532  private:
533    simple_target_socket_b *m_owner;
534    MODULE* m_mod;
535    NBTransportPtr m_nb_transport_ptr;
536    BTransportPtr m_b_transport_ptr;
537    TransportDbgPtr m_transport_dbg_ptr;
538    GetDirectMemPtr m_get_direct_mem_ptr;
539    peq_with_get<transaction_type> m_peq;
540    bool m_response_in_progress;
541    sc_core::sc_event m_end_response;
542  };
543
544private:
545  const sc_core::sc_object* get_socket() const { return this; }
546private:
547  fw_process m_fw_process;
548  bw_process m_bw_process;
549  std::map<transaction_type*, sc_core::sc_event *> m_pending_trans;
550  sc_core::sc_event m_end_request;
551  transaction_type* m_current_transaction;
552};
553
554template< typename MODULE, unsigned int BUSWIDTH = 32
555        , typename TYPES = tlm::tlm_base_protocol_types >
556class simple_target_socket
557  : public simple_target_socket_b<MODULE,BUSWIDTH,TYPES>
558{
559  typedef simple_target_socket_b<MODULE,BUSWIDTH,TYPES> socket_b;
560public:
561  simple_target_socket() : socket_b() {}
562  explicit simple_target_socket(const char* name) : socket_b(name) {}
563};
564
565template< typename MODULE, unsigned int BUSWIDTH = 32
566        , typename TYPES = tlm::tlm_base_protocol_types >
567class simple_target_socket_optional
568  : public simple_target_socket_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND>
569{
570  typedef simple_target_socket_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> socket_b;
571public:
572  simple_target_socket_optional() : socket_b() {}
573  explicit simple_target_socket_optional(const char* name) : socket_b(name) {}
574};
575
576//ID Tagged version
577template< typename MODULE, unsigned int BUSWIDTH, typename TYPES
578        , sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND >
579class simple_target_socket_tagged_b
580  : public tlm::tlm_target_socket<BUSWIDTH, TYPES, 1, POL>
581  , protected simple_socket_base
582{
583  friend class fw_process;
584  friend class bw_process;
585public:
586  typedef typename TYPES::tlm_payload_type              transaction_type;
587  typedef typename TYPES::tlm_phase_type                phase_type;
588  typedef tlm::tlm_sync_enum                            sync_enum_type;
589  typedef tlm::tlm_fw_transport_if<TYPES>               fw_interface_type;
590  typedef tlm::tlm_bw_transport_if<TYPES>               bw_interface_type;
591  typedef tlm::tlm_target_socket<BUSWIDTH,TYPES,1,POL>  base_type;
592
593public:
594  static const char* default_name()
595    { return sc_core::sc_gen_unique_name("simple_target_socket_tagged"); }
596
597  explicit simple_target_socket_tagged_b(const char* n = default_name())
598    : base_type(n)
599    , m_fw_process(this)
600    , m_bw_process(this)
601  {
602    bind(m_fw_process);
603  }
604
605  using base_type::bind;
606
607  // bw transport must come thru us.
608  tlm::tlm_bw_transport_if<TYPES> * operator ->() {return &m_bw_process;}
609
610  // REGISTER_XXX
611  void register_nb_transport_fw(MODULE* mod,
612                                sync_enum_type (MODULE::*cb)(int id,
613                                                             transaction_type&,
614                                                             phase_type&,
615                                                             sc_core::sc_time&),
616                                int id)
617  {
618    elaboration_check("register_nb_transport_fw");
619    m_fw_process.set_nb_transport_ptr(mod, cb);
620    m_fw_process.set_nb_transport_user_id(id);
621  }
622
623  void register_b_transport(MODULE* mod,
624                            void (MODULE::*cb)(int id,
625                                               transaction_type&,
626                                               sc_core::sc_time&),
627                            int id)
628  {
629    elaboration_check("register_b_transport");
630    m_fw_process.set_b_transport_ptr(mod, cb);
631    m_fw_process.set_b_transport_user_id(id);
632  }
633
634  void register_transport_dbg(MODULE* mod,
635                              unsigned int (MODULE::*cb)(int id,
636                                                         transaction_type&),
637                              int id)
638  {
639    elaboration_check("register_transport_dbg");
640    m_fw_process.set_transport_dbg_ptr(mod, cb);
641    m_fw_process.set_transport_dbg_user_id(id);
642  }
643
644  void register_get_direct_mem_ptr(MODULE* mod,
645                                   bool (MODULE::*cb)(int id,
646                                                      transaction_type&,
647                                                      tlm::tlm_dmi&),
648                                   int id)
649  {
650    elaboration_check("register_get_direct_mem_ptr");
651    m_fw_process.set_get_direct_mem_ptr(mod, cb);
652    m_fw_process.set_get_dmi_user_id(id);
653  }
654
655protected:
656  void start_of_simulation()
657  {
658    base_type::start_of_simulation();
659    m_fw_process.start_of_simulation();
660  }
661
662private:
663  //make call on bw path.
664  sync_enum_type bw_nb_transport(transaction_type &trans, phase_type &phase, sc_core::sc_time &t)
665  {
666    return base_type::operator ->()->nb_transport_bw(trans, phase, t);
667  }
668
669  void bw_invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e)
670  {
671    base_type::operator ->()->invalidate_direct_mem_ptr(s, e);
672  }
673
674  //Helper class to handle bw path calls
675  // Needed to detect transaction end when called from b_transport.
676  class bw_process : public tlm::tlm_bw_transport_if<TYPES>
677  {
678  public:
679    bw_process(simple_target_socket_tagged_b *p_own) : m_owner(p_own)
680    {
681    }
682
683    sync_enum_type nb_transport_bw(transaction_type &trans, phase_type &phase, sc_core::sc_time &t)
684    {
685      typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
686        m_owner->m_pending_trans.find(&trans);
687
688      if(it == m_owner->m_pending_trans.end()) {
689        // Not a blocking call, forward.
690        return m_owner->bw_nb_transport(trans, phase, t);
691      }
692      if (phase == tlm::END_REQ) {
693        m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
694        return tlm::TLM_ACCEPTED;
695      }
696      if (phase == tlm::BEGIN_RESP) {
697        if (m_owner->m_current_transaction == &trans) {
698          m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
699        }
700        //TODO: add response-accept delay?
701        it->second->notify(t);
702        m_owner->m_pending_trans.erase(it);
703        return tlm::TLM_COMPLETED;
704      }
705      m_owner->display_error("invalid phase received");
706      return tlm::TLM_COMPLETED;
707    }
708
709    void invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e)
710    {
711      return m_owner->bw_invalidate_direct_mem_ptr(s, e);
712    }
713
714  private:
715    simple_target_socket_tagged_b *m_owner;
716  };
717
718  class fw_process : public tlm::tlm_fw_transport_if<TYPES>,
719                     public tlm::tlm_mm_interface
720  {
721  public:
722    typedef sync_enum_type (MODULE::*NBTransportPtr)(int id,
723                                                     transaction_type&,
724                                                     phase_type&,
725                                                     sc_core::sc_time&);
726    typedef void (MODULE::*BTransportPtr)(int id,
727                                          transaction_type&,
728                                          sc_core::sc_time&);
729    typedef unsigned int (MODULE::*TransportDbgPtr)(int id,
730                                                    transaction_type&);
731    typedef bool (MODULE::*GetDirectMemPtr)(int id,
732                                            transaction_type&,
733                                            tlm::tlm_dmi&);
734
735    fw_process(simple_target_socket_tagged_b *p_own) :
736      m_owner(p_own),
737      m_mod(0),
738      m_nb_transport_ptr(0),
739      m_b_transport_ptr(0),
740      m_transport_dbg_ptr(0),
741      m_get_direct_mem_ptr(0),
742      m_nb_transport_user_id(0),
743      m_b_transport_user_id(0),
744      m_transport_dbg_user_id(0),
745      m_get_dmi_user_id(0),
746      m_peq(sc_core::sc_gen_unique_name("m_peq")),
747      m_response_in_progress(false)
748    {}
749
750    void start_of_simulation()
751    {
752      if (!m_b_transport_ptr && m_nb_transport_ptr) { // only spawn b2nb_thread, if needed
753        sc_core::sc_spawn_options opts;
754        opts.set_sensitivity(&m_peq.get_event());
755        opts.dont_initialize();
756        sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this),
757                          sc_core::sc_gen_unique_name("b2nb_thread"), &opts);
758      }
759    }
760
761    void set_nb_transport_user_id(int id) { m_nb_transport_user_id = id; }
762    void set_b_transport_user_id(int id) { m_b_transport_user_id = id; }
763    void set_transport_dbg_user_id(int id) { m_transport_dbg_user_id = id; }
764    void set_get_dmi_user_id(int id) { m_get_dmi_user_id = id; }
765
766    void set_nb_transport_ptr(MODULE* mod, NBTransportPtr p)
767    {
768      if (m_nb_transport_ptr) {
769        m_owner->display_warning("non-blocking callback already registered");
770        return;
771      }
772      sc_assert(!m_mod || m_mod == mod);
773      m_mod = mod;
774      m_nb_transport_ptr = p;
775    }
776
777    void set_b_transport_ptr(MODULE* mod, BTransportPtr p)
778    {
779      if (m_b_transport_ptr) {
780        m_owner->display_warning("blocking callback already registered");
781        return;
782      }
783      sc_assert(!m_mod || m_mod == mod);
784      m_mod = mod;
785      m_b_transport_ptr = p;
786    }
787
788    void set_transport_dbg_ptr(MODULE* mod, TransportDbgPtr p)
789    {
790      if (m_transport_dbg_ptr) {
791        m_owner->display_warning("debug callback already registered");
792        return;
793      }
794      sc_assert(!m_mod || m_mod == mod);
795      m_mod = mod;
796      m_transport_dbg_ptr = p;
797    }
798
799    void set_get_direct_mem_ptr(MODULE* mod, GetDirectMemPtr p)
800    {
801      if (m_get_direct_mem_ptr) {
802        m_owner->display_warning("get DMI pointer callback already registered");
803      }
804      sc_assert(!m_mod || m_mod == mod);
805      m_mod = mod;
806      m_get_direct_mem_ptr = p;
807    }
808// Interface implementation
809    sync_enum_type nb_transport_fw(transaction_type& trans,
810                                   phase_type& phase,
811                                   sc_core::sc_time& t)
812    {
813      if (m_nb_transport_ptr) {
814        // forward call
815        sc_assert(m_mod);
816        return (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, trans, phase, t);
817      }
818
819      // nb->b conversion
820      if (m_b_transport_ptr) {
821        if (phase == tlm::BEGIN_REQ) {
822
823          // prepare thread to do blocking call
824          process_handle_class * ph = m_process_handle.get_handle(&trans);
825
826          if (!ph) { // create new dynamic process
827            ph = new process_handle_class(&trans);
828            m_process_handle.put_handle(ph);
829
830            sc_core::sc_spawn_options opts;
831            opts.dont_initialize();
832            opts.set_sensitivity(&ph->m_e);
833
834            sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph),
835                            sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
836          }
837
838          ph->m_e.notify(t);
839          return tlm::TLM_ACCEPTED;
840        }
841        if (phase == tlm::END_RESP) {
842          m_response_in_progress = false;
843          m_end_response.notify(t);
844          return tlm::TLM_COMPLETED;
845        }
846        m_owner->display_error("invalid phase");
847        return tlm::TLM_COMPLETED;
848      }
849
850      m_owner->display_error("no non-blocking transport callback registered");
851      return tlm::TLM_COMPLETED;
852    }
853
854    void b_transport(transaction_type& trans, sc_core::sc_time& t)
855    {
856      if (m_b_transport_ptr) {
857        // forward call
858        sc_assert(m_mod);
859        (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, trans, t);
860        return;
861      }
862
863      // b->nb conversion
864      if (m_nb_transport_ptr) {
865        m_peq.notify(trans, t);
866        t = sc_core::SC_ZERO_TIME;
867
868        mm_end_event_ext mm_ext;
869        const bool mm_added = !trans.has_mm();
870
871        if (mm_added){
872          trans.set_mm(this);
873          trans.set_auto_extension(&mm_ext);
874          trans.acquire();
875        }
876
877        // wait until transaction is finished
878        sc_core::sc_event end_event;
879        m_owner->m_pending_trans[&trans] = &end_event;
880        sc_core::wait(end_event);
881
882        if (mm_added) {
883          // release will not delete the transaction, it will notify mm_ext.done
884          trans.release();
885          if (trans.get_ref_count()) {
886            sc_core::wait(mm_ext.done);
887          }
888          trans.set_mm(0);
889        }
890        return;
891      }
892
893      m_owner->display_error("no transport callback registered");
894    }
895
896    unsigned int transport_dbg(transaction_type& trans)
897    {
898      if (m_transport_dbg_ptr) {
899        // forward call
900        sc_assert(m_mod);
901        return (m_mod->*m_transport_dbg_ptr)(m_transport_dbg_user_id, trans);
902      }
903      // No debug support
904      return 0;
905    }
906
907    bool get_direct_mem_ptr(transaction_type& trans,
908                            tlm::tlm_dmi&  dmi_data)
909    {
910      if (m_get_direct_mem_ptr) {
911        // forward call
912        sc_assert(m_mod);
913        return (m_mod->*m_get_direct_mem_ptr)(m_get_dmi_user_id, trans, dmi_data);
914      }
915      // No DMI support
916      dmi_data.allow_read_write();
917      dmi_data.set_start_address(0x0);
918      dmi_data.set_end_address((sc_dt::uint64)-1);
919      return false;
920    }
921
922  private:
923// dynamic process handler for nb2b conversion
924
925    class process_handle_class {
926    public:
927      explicit process_handle_class(transaction_type * trans)
928        : m_trans(trans),m_suspend(false){}
929
930      transaction_type*  m_trans;
931      sc_core::sc_event  m_e;
932      bool m_suspend;
933    };
934
935    class process_handle_list {
936    public:
937      process_handle_list() {}
938
939      ~process_handle_list() {
940        for( typename std::vector<process_handle_class*>::iterator
941               it=v.begin(), end = v.end(); it != end; ++it )
942          delete *it;
943      }
944
945      process_handle_class* get_handle(transaction_type *trans)
946      {
947        typename std::vector<process_handle_class*>::iterator it;
948
949        for(it = v.begin(); it != v.end(); it++) {
950          if ((*it)->m_suspend) {  // found suspended dynamic process, re-use it
951            (*it)->m_trans   = trans; // replace to new one
952            (*it)->m_suspend = false;
953            return *it;
954          }
955        }
956        return NULL; // no suspended process
957      }
958
959      void put_handle(process_handle_class* ph)
960      {
961        v.push_back(ph);
962      }
963
964    private:
965      std::vector<process_handle_class*> v;
966    };
967
968    process_handle_list m_process_handle;
969
970    void nb2b_thread(process_handle_class* h)
971    {
972
973      while(1) {
974        transaction_type * trans = h->m_trans;
975        sc_core::sc_time t = sc_core::SC_ZERO_TIME;
976
977        // forward call
978        sc_assert(m_mod);
979        (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, *trans, t);
980
981        sc_core::wait(t);
982
983        // return path
984        while (m_response_in_progress) {
985          sc_core::wait(m_end_response);
986        }
987        t = sc_core::SC_ZERO_TIME;
988        phase_type phase    = tlm::BEGIN_RESP;
989        sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
990        if ( !(sync == tlm::TLM_COMPLETED ||
991              (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP)) ) {
992          m_response_in_progress = true;
993        }
994
995        // suspend until next transaction
996        h->m_suspend = true;
997        sc_core::wait();
998      }
999    }
1000
1001    void b2nb_thread()
1002    {
1003      while (true) {
1004        transaction_type* trans;
1005        while ((trans = m_peq.get_next_transaction())!=0) {
1006          sc_assert(m_mod);
1007          sc_assert(m_nb_transport_ptr);
1008          phase_type phase = tlm::BEGIN_REQ;
1009          sc_core::sc_time t = sc_core::SC_ZERO_TIME;
1010
1011          switch ((m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, *trans, phase, t)) {
1012          case tlm::TLM_COMPLETED:
1013          {
1014            // notify transaction is finished
1015            typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
1016              m_owner->m_pending_trans.find(trans);
1017            sc_assert(it != m_owner->m_pending_trans.end());
1018            it->second->notify(t);
1019            m_owner->m_pending_trans.erase(it);
1020            break;
1021          }
1022
1023          case tlm::TLM_ACCEPTED:
1024          case tlm::TLM_UPDATED:
1025            switch (phase) {
1026            case tlm::BEGIN_REQ:
1027              m_owner->m_current_transaction = trans;
1028              sc_core::wait(m_owner->m_end_request);
1029              m_owner->m_current_transaction = 0;
1030              break;
1031
1032            case tlm::END_REQ:
1033              sc_core::wait(t);
1034              break;
1035
1036            case tlm::BEGIN_RESP:
1037            {
1038              phase = tlm::END_RESP;
1039              sc_core::wait(t);  // This line is a bug fix added in TLM-2.0.2
1040              t = sc_core::SC_ZERO_TIME;
1041              (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, *trans, phase, t);
1042
1043              // notify transaction is finished
1044              typename std::map<transaction_type*, sc_core::sc_event *>::iterator it =
1045                m_owner->m_pending_trans.find(trans);
1046              sc_assert(it != m_owner->m_pending_trans.end());
1047              it->second->notify(t);
1048              m_owner->m_pending_trans.erase(it);
1049              break;
1050            }
1051
1052            default:
1053              m_owner->display_error("invalid phase received");
1054            };
1055            break;
1056
1057          default:
1058            m_owner->display_error("invalid sync value received");
1059          }
1060        }
1061        sc_core::wait();
1062      }
1063    }
1064
1065    void free(tlm::tlm_generic_payload* trans)
1066    {
1067      mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>();
1068      sc_assert(ext);
1069      // notif event first before freeing extensions (reset)
1070      ext->done.notify();
1071      trans->reset();
1072    }
1073
1074  private:
1075    struct mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext>
1076    {
1077      tlm::tlm_extension_base* clone() const { return NULL; }
1078      void free() {}
1079      void copy_from(tlm::tlm_extension_base const &) {}
1080      sc_core::sc_event done;
1081    };
1082
1083  private:
1084    simple_target_socket_tagged_b *m_owner;
1085    MODULE* m_mod;
1086    NBTransportPtr m_nb_transport_ptr;
1087    BTransportPtr m_b_transport_ptr;
1088    TransportDbgPtr m_transport_dbg_ptr;
1089    GetDirectMemPtr m_get_direct_mem_ptr;
1090    int m_nb_transport_user_id;
1091    int m_b_transport_user_id;
1092    int m_transport_dbg_user_id;
1093    int m_get_dmi_user_id;
1094    peq_with_get<transaction_type> m_peq;
1095    bool m_response_in_progress;
1096    sc_core::sc_event m_end_response;
1097  };
1098
1099private:
1100  const sc_core::sc_object* get_socket() const { return this; }
1101private:
1102  fw_process m_fw_process;
1103  bw_process m_bw_process;
1104  std::map<transaction_type*, sc_core::sc_event *> m_pending_trans;
1105  sc_core::sc_event m_end_request;
1106  transaction_type* m_current_transaction;
1107};
1108
1109template< typename MODULE, unsigned int BUSWIDTH = 32
1110        , typename TYPES = tlm::tlm_base_protocol_types >
1111class simple_target_socket_tagged
1112  : public simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES>
1113{
1114  typedef simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES> socket_b;
1115public:
1116  simple_target_socket_tagged() : socket_b() {}
1117  explicit simple_target_socket_tagged(const char* name) : socket_b(name) {}
1118};
1119
1120template< typename MODULE, unsigned int BUSWIDTH = 32
1121        , typename TYPES = tlm::tlm_base_protocol_types >
1122class simple_target_socket_tagged_optional
1123  : public simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND>
1124{
1125  typedef simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> socket_b;
1126public:
1127  simple_target_socket_tagged_optional() : socket_b() {}
1128  explicit simple_target_socket_tagged_optional(const char* name) : socket_b(name) {}
1129};
1130
1131} // namespace tlm_utils
1132#endif // TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_
1133