| /***************************************************************************** |
| |
| Licensed to Accellera Systems Initiative Inc. (Accellera) under one or |
| more contributor license agreements. See the NOTICE file distributed |
| with this work for additional information regarding copyright ownership. |
| Accellera licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with the |
| License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| implied. See the License for the specific language governing |
| permissions and limitations under the License. |
| |
| *****************************************************************************/ |
| |
| // ***************************************************************************** |
| // Modified by John Aynsley, Doulos, Feb 2009, |
| // Fix a bug in simple_target_socket and simple_target_socket_tagged |
| // with the addition of one new line of code in each: wait(*e); |
| // ***************************************************************************** |
| |
| // ***************************************************************************** |
| // Modified by John Aynsley on behalf of Robert Guenzel, May 2011, |
| // Fix a bug in simple_target_socket and simple_target_socket_tagged |
| // with the addition of one new line of code in each: wait(t); |
| // ***************************************************************************** |
| |
| |
| #ifndef TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_ |
| #define TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_ |
| |
| #ifndef SC_INCLUDE_DYNAMIC_PROCESSES // needed for sc_spawn |
| # define SC_INCLUDE_DYNAMIC_PROCESSES |
| #endif |
| |
| #include <systemc> |
| #include <tlm> |
| #include "tlm_utils/convenience_socket_bases.h" |
| #include "tlm_utils/peq_with_get.h" |
| |
| namespace tlm_utils { |
| |
| template< typename MODULE, unsigned int BUSWIDTH, typename TYPES |
| , sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND > |
| class simple_target_socket_b |
| : public tlm::tlm_target_socket<BUSWIDTH, TYPES, 1, POL> |
| , protected simple_socket_base |
| { |
| friend class fw_process; |
| friend class bw_process; |
| public: |
| typedef typename TYPES::tlm_payload_type transaction_type; |
| typedef typename TYPES::tlm_phase_type phase_type; |
| typedef tlm::tlm_sync_enum sync_enum_type; |
| typedef tlm::tlm_fw_transport_if<TYPES> fw_interface_type; |
| typedef tlm::tlm_bw_transport_if<TYPES> bw_interface_type; |
| typedef tlm::tlm_target_socket<BUSWIDTH,TYPES,1,POL> base_type; |
| |
| public: |
| static const char* default_name() |
| { return sc_core::sc_gen_unique_name("simple_target_socket"); } |
| |
| explicit simple_target_socket_b(const char* n = default_name()) |
| : base_type(n) |
| , m_fw_process(this) |
| , m_bw_process(this) |
| { |
| bind(m_fw_process); |
| } |
| |
| using base_type::bind; |
| |
| // bw transport must come thru us. |
| tlm::tlm_bw_transport_if<TYPES> * operator ->() {return &m_bw_process;} |
| |
| // REGISTER_XXX |
| void register_nb_transport_fw(MODULE* mod, |
| sync_enum_type (MODULE::*cb)(transaction_type&, |
| phase_type&, |
| sc_core::sc_time&)) |
| { |
| elaboration_check("register_nb_transport_fw"); |
| m_fw_process.set_nb_transport_ptr(mod, cb); |
| } |
| |
| void register_b_transport(MODULE* mod, |
| void (MODULE::*cb)(transaction_type&, |
| sc_core::sc_time&)) |
| { |
| elaboration_check("register_b_transport"); |
| m_fw_process.set_b_transport_ptr(mod, cb); |
| } |
| |
| void register_transport_dbg(MODULE* mod, |
| unsigned int (MODULE::*cb)(transaction_type&)) |
| { |
| elaboration_check("register_transport_dbg"); |
| m_fw_process.set_transport_dbg_ptr(mod, cb); |
| } |
| |
| void register_get_direct_mem_ptr(MODULE* mod, |
| bool (MODULE::*cb)(transaction_type&, |
| tlm::tlm_dmi&)) |
| { |
| elaboration_check("register_get_direct_mem_ptr"); |
| m_fw_process.set_get_direct_mem_ptr(mod, cb); |
| } |
| |
| protected: |
| void start_of_simulation() |
| { |
| base_type::start_of_simulation(); |
| m_fw_process.start_of_simulation(); |
| } |
| |
| private: |
| //make call on bw path. |
| sync_enum_type bw_nb_transport(transaction_type &trans, phase_type &phase, sc_core::sc_time &t) |
| { |
| return base_type::operator ->()->nb_transport_bw(trans, phase, t); |
| } |
| |
| void bw_invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e) |
| { |
| base_type::operator ->()->invalidate_direct_mem_ptr(s, e); |
| } |
| |
| //Helper class to handle bw path calls |
| // Needed to detect transaction end when called from b_transport. |
| class bw_process : public tlm::tlm_bw_transport_if<TYPES> |
| { |
| public: |
| bw_process(simple_target_socket_b *p_own) : m_owner(p_own) |
| { |
| } |
| |
| sync_enum_type nb_transport_bw(transaction_type &trans, phase_type &phase, sc_core::sc_time &t) |
| { |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(&trans); |
| |
| if(it == m_owner->m_pending_trans.end()) { |
| // Not a blocking call, forward. |
| return m_owner->bw_nb_transport(trans, phase, t); |
| |
| } |
| |
| if (phase == tlm::END_REQ) { |
| m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); |
| return tlm::TLM_ACCEPTED; |
| } |
| if (phase == tlm::BEGIN_RESP) { |
| if (m_owner->m_current_transaction == &trans) { |
| m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); |
| } |
| //TODO: add response-accept delay? |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| return tlm::TLM_COMPLETED; |
| } |
| m_owner->display_error("invalid phase received"); |
| return tlm::TLM_COMPLETED; |
| } |
| |
| void invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e) |
| { |
| return m_owner->bw_invalidate_direct_mem_ptr(s, e); |
| } |
| |
| private: |
| simple_target_socket_b *m_owner; |
| }; |
| |
| class fw_process : public tlm::tlm_fw_transport_if<TYPES>, |
| public tlm::tlm_mm_interface |
| { |
| public: |
| typedef sync_enum_type (MODULE::*NBTransportPtr)(transaction_type&, |
| phase_type&, |
| sc_core::sc_time&); |
| typedef void (MODULE::*BTransportPtr)(transaction_type&, |
| sc_core::sc_time&); |
| typedef unsigned int (MODULE::*TransportDbgPtr)(transaction_type&); |
| typedef bool (MODULE::*GetDirectMemPtr)(transaction_type&, |
| tlm::tlm_dmi&); |
| |
| fw_process(simple_target_socket_b *p_own) : |
| m_owner(p_own), |
| m_mod(0), |
| m_nb_transport_ptr(0), |
| m_b_transport_ptr(0), |
| m_transport_dbg_ptr(0), |
| m_get_direct_mem_ptr(0), |
| m_peq(sc_core::sc_gen_unique_name("m_peq")), |
| m_response_in_progress(false) |
| {} |
| |
| void start_of_simulation() |
| { |
| if (!m_b_transport_ptr && m_nb_transport_ptr) { // only spawn b2nb_thread, if needed |
| sc_core::sc_spawn_options opts; |
| opts.set_sensitivity(&m_peq.get_event()); |
| opts.dont_initialize(); |
| sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this), |
| sc_core::sc_gen_unique_name("b2nb_thread"), &opts); |
| } |
| } |
| |
| void set_nb_transport_ptr(MODULE* mod, NBTransportPtr p) |
| { |
| if (m_nb_transport_ptr) { |
| m_owner->display_warning("non-blocking callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_nb_transport_ptr = p; |
| } |
| |
| void set_b_transport_ptr(MODULE* mod, BTransportPtr p) |
| { |
| if (m_b_transport_ptr) { |
| m_owner->display_warning("blocking callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_b_transport_ptr = p; |
| } |
| |
| void set_transport_dbg_ptr(MODULE* mod, TransportDbgPtr p) |
| { |
| if (m_transport_dbg_ptr) { |
| m_owner->display_warning("debug callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_transport_dbg_ptr = p; |
| } |
| |
| void set_get_direct_mem_ptr(MODULE* mod, GetDirectMemPtr p) |
| { |
| if (m_get_direct_mem_ptr) { |
| m_owner->display_warning("get DMI pointer callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_get_direct_mem_ptr = p; |
| } |
| // Interface implementation |
| sync_enum_type nb_transport_fw(transaction_type& trans, |
| phase_type& phase, |
| sc_core::sc_time& t) |
| { |
| if (m_nb_transport_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_nb_transport_ptr)(trans, phase, t); |
| } |
| |
| // nb->b conversion |
| if (m_b_transport_ptr) { |
| if (phase == tlm::BEGIN_REQ) { |
| // prepare thread to do blocking call |
| process_handle_class * ph = m_process_handle.get_handle(&trans); |
| |
| if (!ph) { // create new dynamic process |
| ph = new process_handle_class(&trans); |
| m_process_handle.put_handle(ph); |
| |
| sc_core::sc_spawn_options opts; |
| opts.dont_initialize(); |
| opts.set_sensitivity(&ph->m_e); |
| |
| sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread,this, ph), |
| sc_core::sc_gen_unique_name("nb2b_thread"), &opts); |
| } |
| |
| ph->m_e.notify(t); |
| return tlm::TLM_ACCEPTED; |
| } |
| if (phase == tlm::END_RESP) { |
| m_response_in_progress = false; |
| m_end_response.notify(t); |
| return tlm::TLM_COMPLETED; |
| } |
| m_owner->display_error("invalid phase received"); |
| return tlm::TLM_COMPLETED; |
| } |
| m_owner->display_error("no non-blocking transport callback registered"); |
| return tlm::TLM_COMPLETED; |
| } |
| |
| void b_transport(transaction_type& trans, sc_core::sc_time& t) |
| { |
| if (m_b_transport_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| (m_mod->*m_b_transport_ptr)(trans, t); |
| return; |
| } |
| |
| // b->nb conversion |
| if (m_nb_transport_ptr) { |
| m_peq.notify(trans, t); |
| t = sc_core::SC_ZERO_TIME; |
| |
| mm_end_event_ext mm_ext; |
| const bool mm_added = !trans.has_mm(); |
| |
| if (mm_added) { |
| trans.set_mm(this); |
| trans.set_auto_extension(&mm_ext); |
| trans.acquire(); |
| } |
| |
| // wait until transaction is finished |
| sc_core::sc_event end_event; |
| m_owner->m_pending_trans[&trans] = &end_event; |
| sc_core::wait(end_event); |
| |
| if (mm_added) { |
| // release will not delete the transaction, it will notify mm_ext.done |
| trans.release(); |
| if (trans.get_ref_count()) { |
| sc_core::wait(mm_ext.done); |
| } |
| trans.set_mm(0); |
| } |
| return; |
| } |
| |
| // should not be reached |
| m_owner->display_error("no blocking transport callback registered"); |
| } |
| |
| unsigned int transport_dbg(transaction_type& trans) |
| { |
| if (m_transport_dbg_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_transport_dbg_ptr)(trans); |
| } |
| // No debug support |
| return 0; |
| } |
| |
| bool get_direct_mem_ptr(transaction_type& trans, |
| tlm::tlm_dmi& dmi_data) |
| { |
| if (m_get_direct_mem_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_get_direct_mem_ptr)(trans, dmi_data); |
| } |
| // No DMI support |
| dmi_data.allow_read_write(); |
| dmi_data.set_start_address(0x0); |
| dmi_data.set_end_address((sc_dt::uint64)-1); |
| return false; |
| } |
| |
| private: |
| |
| // dynamic process handler for nb2b conversion |
| |
| class process_handle_class { |
| public: |
| explicit process_handle_class(transaction_type * trans) |
| : m_trans(trans),m_suspend(false) {} |
| |
| transaction_type* m_trans; |
| sc_core::sc_event m_e; |
| bool m_suspend; |
| }; |
| |
| class process_handle_list { |
| public: |
| process_handle_list() {} |
| |
| ~process_handle_list() { |
| for( typename std::vector<process_handle_class*>::iterator |
| it=v.begin(), end = v.end(); it != end; ++it ) |
| delete *it; |
| } |
| |
| process_handle_class* get_handle(transaction_type *trans) |
| { |
| typename std::vector<process_handle_class*>::iterator it; |
| |
| for(it = v.begin(); it != v.end(); it++) { |
| if ((*it)->m_suspend) { // found suspended dynamic process, re-use it |
| (*it)->m_trans = trans; // replace to new one |
| (*it)->m_suspend = false; |
| return *it; |
| } |
| } |
| return NULL; // no suspended process |
| } |
| |
| void put_handle(process_handle_class* ph) |
| { |
| v.push_back(ph); |
| } |
| |
| private: |
| std::vector<process_handle_class*> v; |
| }; |
| |
| process_handle_list m_process_handle; |
| |
| |
| void nb2b_thread(process_handle_class* h) |
| { |
| |
| while(1) { |
| transaction_type *trans = h->m_trans; |
| sc_core::sc_time t = sc_core::SC_ZERO_TIME; |
| |
| // forward call |
| sc_assert(m_mod); |
| (m_mod->*m_b_transport_ptr)(*trans, t); |
| |
| sc_core::wait(t); |
| |
| // return path |
| while (m_response_in_progress) { |
| sc_core::wait(m_end_response); |
| } |
| t = sc_core::SC_ZERO_TIME; |
| phase_type phase = tlm::BEGIN_RESP; |
| sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t); |
| if ( !(sync == tlm::TLM_COMPLETED || |
| (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP)) ) { |
| m_response_in_progress = true; |
| } |
| |
| // suspend until next transaction |
| h->m_suspend = true; |
| sc_core::wait(); |
| } |
| } |
| |
| void b2nb_thread() |
| { |
| while (true) { |
| transaction_type* trans; |
| while ((trans = m_peq.get_next_transaction())!=0) { |
| sc_assert(m_mod); |
| sc_assert(m_nb_transport_ptr); |
| phase_type phase = tlm::BEGIN_REQ; |
| sc_core::sc_time t = sc_core::SC_ZERO_TIME; |
| |
| switch ((m_mod->*m_nb_transport_ptr)(*trans, phase, t)) { |
| case tlm::TLM_COMPLETED: |
| { |
| // notify transaction is finished |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(trans); |
| sc_assert(it != m_owner->m_pending_trans.end()); |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| break; |
| } |
| |
| case tlm::TLM_ACCEPTED: |
| case tlm::TLM_UPDATED: |
| switch (phase) { |
| case tlm::BEGIN_REQ: |
| m_owner->m_current_transaction = trans; |
| sc_core::wait(m_owner->m_end_request); |
| m_owner->m_current_transaction = 0; |
| break; |
| |
| case tlm::END_REQ: |
| sc_core::wait(t); |
| break; |
| |
| case tlm::BEGIN_RESP: |
| { |
| phase = tlm::END_RESP; |
| sc_core::wait(t); // This line is a bug fix added in TLM-2.0.2 |
| t = sc_core::SC_ZERO_TIME; |
| (m_mod->*m_nb_transport_ptr)(*trans, phase, t); |
| |
| // notify transaction is finished |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(trans); |
| sc_assert(it != m_owner->m_pending_trans.end()); |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| break; |
| } |
| |
| default: |
| m_owner->display_error("invalid phase received"); |
| } |
| break; |
| |
| default: |
| m_owner->display_error("invalid sync value received"); |
| } |
| } |
| sc_core::wait(); |
| } |
| } |
| |
| void free(tlm::tlm_generic_payload* trans) |
| { |
| mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>(); |
| sc_assert(ext); |
| // notif event first before freeing extensions (reset) |
| ext->done.notify(); |
| trans->reset(); |
| } |
| |
| private: |
| struct mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext> |
| { |
| tlm::tlm_extension_base* clone() const { return NULL; } |
| void free() {} |
| void copy_from(tlm::tlm_extension_base const &) {} |
| sc_core::sc_event done; |
| }; |
| |
| private: |
| simple_target_socket_b *m_owner; |
| MODULE* m_mod; |
| NBTransportPtr m_nb_transport_ptr; |
| BTransportPtr m_b_transport_ptr; |
| TransportDbgPtr m_transport_dbg_ptr; |
| GetDirectMemPtr m_get_direct_mem_ptr; |
| peq_with_get<transaction_type> m_peq; |
| bool m_response_in_progress; |
| sc_core::sc_event m_end_response; |
| }; |
| |
| private: |
| const sc_core::sc_object* get_socket() const { return this; } |
| private: |
| fw_process m_fw_process; |
| bw_process m_bw_process; |
| std::map<transaction_type*, sc_core::sc_event *> m_pending_trans; |
| sc_core::sc_event m_end_request; |
| transaction_type* m_current_transaction; |
| }; |
| |
| template< typename MODULE, unsigned int BUSWIDTH = 32 |
| , typename TYPES = tlm::tlm_base_protocol_types > |
| class simple_target_socket |
| : public simple_target_socket_b<MODULE,BUSWIDTH,TYPES> |
| { |
| typedef simple_target_socket_b<MODULE,BUSWIDTH,TYPES> socket_b; |
| public: |
| simple_target_socket() : socket_b() {} |
| explicit simple_target_socket(const char* name) : socket_b(name) {} |
| }; |
| |
| template< typename MODULE, unsigned int BUSWIDTH = 32 |
| , typename TYPES = tlm::tlm_base_protocol_types > |
| class simple_target_socket_optional |
| : public simple_target_socket_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> |
| { |
| typedef simple_target_socket_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> socket_b; |
| public: |
| simple_target_socket_optional() : socket_b() {} |
| explicit simple_target_socket_optional(const char* name) : socket_b(name) {} |
| }; |
| |
| //ID Tagged version |
| template< typename MODULE, unsigned int BUSWIDTH, typename TYPES |
| , sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND > |
| class simple_target_socket_tagged_b |
| : public tlm::tlm_target_socket<BUSWIDTH, TYPES, 1, POL> |
| , protected simple_socket_base |
| { |
| friend class fw_process; |
| friend class bw_process; |
| public: |
| typedef typename TYPES::tlm_payload_type transaction_type; |
| typedef typename TYPES::tlm_phase_type phase_type; |
| typedef tlm::tlm_sync_enum sync_enum_type; |
| typedef tlm::tlm_fw_transport_if<TYPES> fw_interface_type; |
| typedef tlm::tlm_bw_transport_if<TYPES> bw_interface_type; |
| typedef tlm::tlm_target_socket<BUSWIDTH,TYPES,1,POL> base_type; |
| |
| public: |
| static const char* default_name() |
| { return sc_core::sc_gen_unique_name("simple_target_socket_tagged"); } |
| |
| explicit simple_target_socket_tagged_b(const char* n = default_name()) |
| : base_type(n) |
| , m_fw_process(this) |
| , m_bw_process(this) |
| { |
| bind(m_fw_process); |
| } |
| |
| using base_type::bind; |
| |
| // bw transport must come thru us. |
| tlm::tlm_bw_transport_if<TYPES> * operator ->() {return &m_bw_process;} |
| |
| // REGISTER_XXX |
| void register_nb_transport_fw(MODULE* mod, |
| sync_enum_type (MODULE::*cb)(int id, |
| transaction_type&, |
| phase_type&, |
| sc_core::sc_time&), |
| int id) |
| { |
| elaboration_check("register_nb_transport_fw"); |
| m_fw_process.set_nb_transport_ptr(mod, cb); |
| m_fw_process.set_nb_transport_user_id(id); |
| } |
| |
| void register_b_transport(MODULE* mod, |
| void (MODULE::*cb)(int id, |
| transaction_type&, |
| sc_core::sc_time&), |
| int id) |
| { |
| elaboration_check("register_b_transport"); |
| m_fw_process.set_b_transport_ptr(mod, cb); |
| m_fw_process.set_b_transport_user_id(id); |
| } |
| |
| void register_transport_dbg(MODULE* mod, |
| unsigned int (MODULE::*cb)(int id, |
| transaction_type&), |
| int id) |
| { |
| elaboration_check("register_transport_dbg"); |
| m_fw_process.set_transport_dbg_ptr(mod, cb); |
| m_fw_process.set_transport_dbg_user_id(id); |
| } |
| |
| void register_get_direct_mem_ptr(MODULE* mod, |
| bool (MODULE::*cb)(int id, |
| transaction_type&, |
| tlm::tlm_dmi&), |
| int id) |
| { |
| elaboration_check("register_get_direct_mem_ptr"); |
| m_fw_process.set_get_direct_mem_ptr(mod, cb); |
| m_fw_process.set_get_dmi_user_id(id); |
| } |
| |
| protected: |
| void start_of_simulation() |
| { |
| base_type::start_of_simulation(); |
| m_fw_process.start_of_simulation(); |
| } |
| |
| private: |
| //make call on bw path. |
| sync_enum_type bw_nb_transport(transaction_type &trans, phase_type &phase, sc_core::sc_time &t) |
| { |
| return base_type::operator ->()->nb_transport_bw(trans, phase, t); |
| } |
| |
| void bw_invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e) |
| { |
| base_type::operator ->()->invalidate_direct_mem_ptr(s, e); |
| } |
| |
| //Helper class to handle bw path calls |
| // Needed to detect transaction end when called from b_transport. |
| class bw_process : public tlm::tlm_bw_transport_if<TYPES> |
| { |
| public: |
| bw_process(simple_target_socket_tagged_b *p_own) : m_owner(p_own) |
| { |
| } |
| |
| sync_enum_type nb_transport_bw(transaction_type &trans, phase_type &phase, sc_core::sc_time &t) |
| { |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(&trans); |
| |
| if(it == m_owner->m_pending_trans.end()) { |
| // Not a blocking call, forward. |
| return m_owner->bw_nb_transport(trans, phase, t); |
| } |
| if (phase == tlm::END_REQ) { |
| m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); |
| return tlm::TLM_ACCEPTED; |
| } |
| if (phase == tlm::BEGIN_RESP) { |
| if (m_owner->m_current_transaction == &trans) { |
| m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); |
| } |
| //TODO: add response-accept delay? |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| return tlm::TLM_COMPLETED; |
| } |
| m_owner->display_error("invalid phase received"); |
| return tlm::TLM_COMPLETED; |
| } |
| |
| void invalidate_direct_mem_ptr(sc_dt::uint64 s,sc_dt::uint64 e) |
| { |
| return m_owner->bw_invalidate_direct_mem_ptr(s, e); |
| } |
| |
| private: |
| simple_target_socket_tagged_b *m_owner; |
| }; |
| |
| class fw_process : public tlm::tlm_fw_transport_if<TYPES>, |
| public tlm::tlm_mm_interface |
| { |
| public: |
| typedef sync_enum_type (MODULE::*NBTransportPtr)(int id, |
| transaction_type&, |
| phase_type&, |
| sc_core::sc_time&); |
| typedef void (MODULE::*BTransportPtr)(int id, |
| transaction_type&, |
| sc_core::sc_time&); |
| typedef unsigned int (MODULE::*TransportDbgPtr)(int id, |
| transaction_type&); |
| typedef bool (MODULE::*GetDirectMemPtr)(int id, |
| transaction_type&, |
| tlm::tlm_dmi&); |
| |
| fw_process(simple_target_socket_tagged_b *p_own) : |
| m_owner(p_own), |
| m_mod(0), |
| m_nb_transport_ptr(0), |
| m_b_transport_ptr(0), |
| m_transport_dbg_ptr(0), |
| m_get_direct_mem_ptr(0), |
| m_nb_transport_user_id(0), |
| m_b_transport_user_id(0), |
| m_transport_dbg_user_id(0), |
| m_get_dmi_user_id(0), |
| m_peq(sc_core::sc_gen_unique_name("m_peq")), |
| m_response_in_progress(false) |
| {} |
| |
| void start_of_simulation() |
| { |
| if (!m_b_transport_ptr && m_nb_transport_ptr) { // only spawn b2nb_thread, if needed |
| sc_core::sc_spawn_options opts; |
| opts.set_sensitivity(&m_peq.get_event()); |
| opts.dont_initialize(); |
| sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this), |
| sc_core::sc_gen_unique_name("b2nb_thread"), &opts); |
| } |
| } |
| |
| void set_nb_transport_user_id(int id) { m_nb_transport_user_id = id; } |
| void set_b_transport_user_id(int id) { m_b_transport_user_id = id; } |
| void set_transport_dbg_user_id(int id) { m_transport_dbg_user_id = id; } |
| void set_get_dmi_user_id(int id) { m_get_dmi_user_id = id; } |
| |
| void set_nb_transport_ptr(MODULE* mod, NBTransportPtr p) |
| { |
| if (m_nb_transport_ptr) { |
| m_owner->display_warning("non-blocking callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_nb_transport_ptr = p; |
| } |
| |
| void set_b_transport_ptr(MODULE* mod, BTransportPtr p) |
| { |
| if (m_b_transport_ptr) { |
| m_owner->display_warning("blocking callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_b_transport_ptr = p; |
| } |
| |
| void set_transport_dbg_ptr(MODULE* mod, TransportDbgPtr p) |
| { |
| if (m_transport_dbg_ptr) { |
| m_owner->display_warning("debug callback already registered"); |
| return; |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_transport_dbg_ptr = p; |
| } |
| |
| void set_get_direct_mem_ptr(MODULE* mod, GetDirectMemPtr p) |
| { |
| if (m_get_direct_mem_ptr) { |
| m_owner->display_warning("get DMI pointer callback already registered"); |
| } |
| sc_assert(!m_mod || m_mod == mod); |
| m_mod = mod; |
| m_get_direct_mem_ptr = p; |
| } |
| // Interface implementation |
| sync_enum_type nb_transport_fw(transaction_type& trans, |
| phase_type& phase, |
| sc_core::sc_time& t) |
| { |
| if (m_nb_transport_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, trans, phase, t); |
| } |
| |
| // nb->b conversion |
| if (m_b_transport_ptr) { |
| if (phase == tlm::BEGIN_REQ) { |
| |
| // prepare thread to do blocking call |
| process_handle_class * ph = m_process_handle.get_handle(&trans); |
| |
| if (!ph) { // create new dynamic process |
| ph = new process_handle_class(&trans); |
| m_process_handle.put_handle(ph); |
| |
| sc_core::sc_spawn_options opts; |
| opts.dont_initialize(); |
| opts.set_sensitivity(&ph->m_e); |
| |
| sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph), |
| sc_core::sc_gen_unique_name("nb2b_thread"), &opts); |
| } |
| |
| ph->m_e.notify(t); |
| return tlm::TLM_ACCEPTED; |
| } |
| if (phase == tlm::END_RESP) { |
| m_response_in_progress = false; |
| m_end_response.notify(t); |
| return tlm::TLM_COMPLETED; |
| } |
| m_owner->display_error("invalid phase"); |
| return tlm::TLM_COMPLETED; |
| } |
| |
| m_owner->display_error("no non-blocking transport callback registered"); |
| return tlm::TLM_COMPLETED; |
| } |
| |
| void b_transport(transaction_type& trans, sc_core::sc_time& t) |
| { |
| if (m_b_transport_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, trans, t); |
| return; |
| } |
| |
| // b->nb conversion |
| if (m_nb_transport_ptr) { |
| m_peq.notify(trans, t); |
| t = sc_core::SC_ZERO_TIME; |
| |
| mm_end_event_ext mm_ext; |
| const bool mm_added = !trans.has_mm(); |
| |
| if (mm_added){ |
| trans.set_mm(this); |
| trans.set_auto_extension(&mm_ext); |
| trans.acquire(); |
| } |
| |
| // wait until transaction is finished |
| sc_core::sc_event end_event; |
| m_owner->m_pending_trans[&trans] = &end_event; |
| sc_core::wait(end_event); |
| |
| if (mm_added) { |
| // release will not delete the transaction, it will notify mm_ext.done |
| trans.release(); |
| if (trans.get_ref_count()) { |
| sc_core::wait(mm_ext.done); |
| } |
| trans.set_mm(0); |
| } |
| return; |
| } |
| |
| m_owner->display_error("no transport callback registered"); |
| } |
| |
| unsigned int transport_dbg(transaction_type& trans) |
| { |
| if (m_transport_dbg_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_transport_dbg_ptr)(m_transport_dbg_user_id, trans); |
| } |
| // No debug support |
| return 0; |
| } |
| |
| bool get_direct_mem_ptr(transaction_type& trans, |
| tlm::tlm_dmi& dmi_data) |
| { |
| if (m_get_direct_mem_ptr) { |
| // forward call |
| sc_assert(m_mod); |
| return (m_mod->*m_get_direct_mem_ptr)(m_get_dmi_user_id, trans, dmi_data); |
| } |
| // No DMI support |
| dmi_data.allow_read_write(); |
| dmi_data.set_start_address(0x0); |
| dmi_data.set_end_address((sc_dt::uint64)-1); |
| return false; |
| } |
| |
| private: |
| // dynamic process handler for nb2b conversion |
| |
| class process_handle_class { |
| public: |
| explicit process_handle_class(transaction_type * trans) |
| : m_trans(trans),m_suspend(false){} |
| |
| transaction_type* m_trans; |
| sc_core::sc_event m_e; |
| bool m_suspend; |
| }; |
| |
| class process_handle_list { |
| public: |
| process_handle_list() {} |
| |
| ~process_handle_list() { |
| for( typename std::vector<process_handle_class*>::iterator |
| it=v.begin(), end = v.end(); it != end; ++it ) |
| delete *it; |
| } |
| |
| process_handle_class* get_handle(transaction_type *trans) |
| { |
| typename std::vector<process_handle_class*>::iterator it; |
| |
| for(it = v.begin(); it != v.end(); it++) { |
| if ((*it)->m_suspend) { // found suspended dynamic process, re-use it |
| (*it)->m_trans = trans; // replace to new one |
| (*it)->m_suspend = false; |
| return *it; |
| } |
| } |
| return NULL; // no suspended process |
| } |
| |
| void put_handle(process_handle_class* ph) |
| { |
| v.push_back(ph); |
| } |
| |
| private: |
| std::vector<process_handle_class*> v; |
| }; |
| |
| process_handle_list m_process_handle; |
| |
| void nb2b_thread(process_handle_class* h) |
| { |
| |
| while(1) { |
| transaction_type * trans = h->m_trans; |
| sc_core::sc_time t = sc_core::SC_ZERO_TIME; |
| |
| // forward call |
| sc_assert(m_mod); |
| (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, *trans, t); |
| |
| sc_core::wait(t); |
| |
| // return path |
| while (m_response_in_progress) { |
| sc_core::wait(m_end_response); |
| } |
| t = sc_core::SC_ZERO_TIME; |
| phase_type phase = tlm::BEGIN_RESP; |
| sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t); |
| if ( !(sync == tlm::TLM_COMPLETED || |
| (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP)) ) { |
| m_response_in_progress = true; |
| } |
| |
| // suspend until next transaction |
| h->m_suspend = true; |
| sc_core::wait(); |
| } |
| } |
| |
| void b2nb_thread() |
| { |
| while (true) { |
| transaction_type* trans; |
| while ((trans = m_peq.get_next_transaction())!=0) { |
| sc_assert(m_mod); |
| sc_assert(m_nb_transport_ptr); |
| phase_type phase = tlm::BEGIN_REQ; |
| sc_core::sc_time t = sc_core::SC_ZERO_TIME; |
| |
| switch ((m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, *trans, phase, t)) { |
| case tlm::TLM_COMPLETED: |
| { |
| // notify transaction is finished |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(trans); |
| sc_assert(it != m_owner->m_pending_trans.end()); |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| break; |
| } |
| |
| case tlm::TLM_ACCEPTED: |
| case tlm::TLM_UPDATED: |
| switch (phase) { |
| case tlm::BEGIN_REQ: |
| m_owner->m_current_transaction = trans; |
| sc_core::wait(m_owner->m_end_request); |
| m_owner->m_current_transaction = 0; |
| break; |
| |
| case tlm::END_REQ: |
| sc_core::wait(t); |
| break; |
| |
| case tlm::BEGIN_RESP: |
| { |
| phase = tlm::END_RESP; |
| sc_core::wait(t); // This line is a bug fix added in TLM-2.0.2 |
| t = sc_core::SC_ZERO_TIME; |
| (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, *trans, phase, t); |
| |
| // notify transaction is finished |
| typename std::map<transaction_type*, sc_core::sc_event *>::iterator it = |
| m_owner->m_pending_trans.find(trans); |
| sc_assert(it != m_owner->m_pending_trans.end()); |
| it->second->notify(t); |
| m_owner->m_pending_trans.erase(it); |
| break; |
| } |
| |
| default: |
| m_owner->display_error("invalid phase received"); |
| }; |
| break; |
| |
| default: |
| m_owner->display_error("invalid sync value received"); |
| } |
| } |
| sc_core::wait(); |
| } |
| } |
| |
| void free(tlm::tlm_generic_payload* trans) |
| { |
| mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>(); |
| sc_assert(ext); |
| // notif event first before freeing extensions (reset) |
| ext->done.notify(); |
| trans->reset(); |
| } |
| |
| private: |
| struct mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext> |
| { |
| tlm::tlm_extension_base* clone() const { return NULL; } |
| void free() {} |
| void copy_from(tlm::tlm_extension_base const &) {} |
| sc_core::sc_event done; |
| }; |
| |
| private: |
| simple_target_socket_tagged_b *m_owner; |
| MODULE* m_mod; |
| NBTransportPtr m_nb_transport_ptr; |
| BTransportPtr m_b_transport_ptr; |
| TransportDbgPtr m_transport_dbg_ptr; |
| GetDirectMemPtr m_get_direct_mem_ptr; |
| int m_nb_transport_user_id; |
| int m_b_transport_user_id; |
| int m_transport_dbg_user_id; |
| int m_get_dmi_user_id; |
| peq_with_get<transaction_type> m_peq; |
| bool m_response_in_progress; |
| sc_core::sc_event m_end_response; |
| }; |
| |
| private: |
| const sc_core::sc_object* get_socket() const { return this; } |
| private: |
| fw_process m_fw_process; |
| bw_process m_bw_process; |
| std::map<transaction_type*, sc_core::sc_event *> m_pending_trans; |
| sc_core::sc_event m_end_request; |
| transaction_type* m_current_transaction; |
| }; |
| |
| template< typename MODULE, unsigned int BUSWIDTH = 32 |
| , typename TYPES = tlm::tlm_base_protocol_types > |
| class simple_target_socket_tagged |
| : public simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES> |
| { |
| typedef simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES> socket_b; |
| public: |
| simple_target_socket_tagged() : socket_b() {} |
| explicit simple_target_socket_tagged(const char* name) : socket_b(name) {} |
| }; |
| |
| template< typename MODULE, unsigned int BUSWIDTH = 32 |
| , typename TYPES = tlm::tlm_base_protocol_types > |
| class simple_target_socket_tagged_optional |
| : public simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> |
| { |
| typedef simple_target_socket_tagged_b<MODULE,BUSWIDTH,TYPES,sc_core::SC_ZERO_OR_MORE_BOUND> socket_b; |
| public: |
| simple_target_socket_tagged_optional() : socket_b() {} |
| explicit simple_target_socket_tagged_optional(const char* name) : socket_b(name) {} |
| }; |
| |
| } // namespace tlm_utils |
| #endif // TLM_UTILS_SIMPLE_TARGET_SOCKET_H_INCLUDED_ |