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