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