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