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