diff options
Diffstat (limited to 'ext/systemc/src/tlm_utils')
-rw-r--r-- | ext/systemc/src/tlm_utils/README.txt | 85 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/instance_specific_extensions.h | 306 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/multi_passthrough_initiator_socket.h | 299 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/multi_passthrough_target_socket.h | 340 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/multi_socket_bases.h | 418 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/passthrough_target_socket.h | 479 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/peq_with_cb_and_phase.h | 301 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/peq_with_get.h | 94 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/simple_initiator_socket.h | 292 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/simple_target_socket.h | 1114 | ||||
-rwxr-xr-x | ext/systemc/src/tlm_utils/tlm2_base_protocol_checker.h | 1055 | ||||
-rw-r--r-- | ext/systemc/src/tlm_utils/tlm_quantumkeeper.h | 172 |
12 files changed, 4955 insertions, 0 deletions
diff --git a/ext/systemc/src/tlm_utils/README.txt b/ext/systemc/src/tlm_utils/README.txt new file mode 100644 index 000000000..8be587bdf --- /dev/null +++ b/ext/systemc/src/tlm_utils/README.txt @@ -0,0 +1,85 @@ + +TLM-2.0 standard utilities +========================== + +Dir: include/tlm_utils + +SubDirs: + +Files: README.txt + instance_specific_extensions.h + multi_passthrough_initiator_socket.h + multi_passthrough_target_socket.h + multi_socket_bases.h + peq_with_get.h + simple_initiator_socket.h + simple_target_socket.h + peq_with_cb_and_phase.h + passthrough_target_socket.h + tlm_quantumkeeper.h + + +Comments +======== + +This directory contains a number of ease-of-use and convenience implementations +for the interoperability standard. All objects defined in the header files of +this directory are contained in the tlm_util namespace. +There is no tlm_utils.h header files containing all includes in order to avoid +additional dependencies for TLM 2.0 + +Files: + simple_initiator_socket.h + version of an initiator socket that has a default implementation of all + interfaces and allows to register an implementation for any of the + interfaces to the socket, either unique interfaces or tagged interfaces + (carrying an additional id) + + simple_target_socket.h + version of a target socket that has a default implementation of all + interfaces and allows to register an implementation for any of the + interfaces to the socket, either unique interfaces or tagged interfaces + (carrying an additional id) + This socket allows to register only 1 of the transport interfaces + (blocking or non-blocking) and implements a conversion in case the + socket is used on the other interface + + passthrough_target_socket.h + version of a target socket that has a default implementation of all + interfaces and allows to register an implementation for any of the + interfaces to the socket. + + multi_passthrough_initiator_socket.h + an implementation of a socket that allows to bind multiple targets to the + same initiator socket. Implements a mechanism to allow to identify in the + backward path through which index of the socket the call passed through + + multi_passthrough_target_socket.h + an implementation of a socket that allows to bind multiple initiators to + the same target socket. Implements a mechanism to allow to identify in the + forward path through which index of the socket the call passed through + + multi_socket_bases.h + contains base class definitions used by the multi_passthrough sockets + + peq_with_get.h + payload event queue (PEQ) implementation using a pull interface. + Has a get_next_transaction API that returns the transaction that is + scheduled in the event queue + + peq_with_cb_and_phase.h + another payload event queue, this one with a push interface (callback + mechanism ). Allows to register a callback that will be called whenever + the event in the event queue is triggered, the callback gets transaction + and phase as arguments + + instance_specific_extensions.h + is an implementation for adding extentions in the generic payload that + are specific to an instance along the path of a transaction, to allow that + extentions of the same type can be used by the different blocks along + the path of the transaction + + tlm_quantumkeeper.h + is an convenience object used to keep track of the local time in + an initiator (how much it has run ahead of the SystemC time), to + synchronize with SystemC time etc. diff --git a/ext/systemc/src/tlm_utils/instance_specific_extensions.h b/ext/systemc/src/tlm_utils/instance_specific_extensions.h new file mode 100644 index 000000000..b17b86219 --- /dev/null +++ b/ext/systemc/src/tlm_utils/instance_specific_extensions.h @@ -0,0 +1,306 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +/* +Instance specific extensions, are extension that only a single instance of a module +may access. They are invisible to all other modules; they are private to this +instance so to speak. + +As they are only of value to a certain instance, this instance knows very well +when it needs them and when it does not need them any longer (usually when +a transaction passes through a module for the last time). +It does not have to care if anyone else in the system may still have a +reference to the transaction as this one is not able to access the extension +anyway. +Therefore the instance is obliged to call set_extension when it wants to add a +private extension and clear_extension when it does not need it any more. + +To get access to an instance specifc extension the module must own a so called +instance_specific_extension_accessor that provides the exclusive access rights. +Assuming the instance_specific_extension_accessor of a given module is called m_accessor +and the transaction of which the private extension is about to be accessed +is called txn, then the calls have to be + +m_accessor(txn).set_extension(...); +or +m_accessor(txn).clear_extension(...); + +The owner of the private extension is responsible to allocate/deallocate +the extension before/after setting/clearing the extension. +*/ + +#ifndef __INSTANCE_SPECIFIC_EXTENSIONS_H__ +#define __INSTANCE_SPECIFIC_EXTENSIONS_H__ + +#include <tlm> + +namespace tlm_utils { + +//Helper to do the numbering of private extension accessors +inline unsigned int max_num_ispex_accessors(bool increment=false) +{ + static unsigned int max_num = 0; + if (increment) ++max_num; + return max_num; +} + +//Helper to do the index generation for private extensions +inline unsigned int max_num_ispex(bool increment=false) +{ + static unsigned int max_num = 0; + if (increment) ++max_num; + return max_num; +} + +//The private extension base. Similar to normal extension base, but without clone and free +class ispex_base +{ +public: + virtual ~ispex_base() {} +protected: + static unsigned int register_private_extension() + { + return (max_num_ispex(true) - 1); + }; +}; + +//The templated private extension. Similar to normal extension +template <typename T> +class +instance_specific_extension : public ispex_base{ +public: + virtual ~instance_specific_extension() {} + const static unsigned int priv_id; +}; + +template <typename T> +const +unsigned int instance_specific_extension<T>::priv_id = ispex_base::register_private_extension(); + + +//this thing is basically a snippet of the generic_payload +// it contains all the extension specific code (the extension API so to speak) +// the differences are: +// - it calls back to its owner whenever a real (==non-NULL) extension gets set for the first time +// - it calls back to its owner whenever a living (==non-NULL) extension gets cleared +template<typename U> +class instance_specific_extensions_per_accessor{ +public: + + typedef void (U::*cb)(); + + instance_specific_extensions_per_accessor(U* container, cb inc, cb dec): m_container(container), m_inc(inc), m_dec(dec){ + } + + template <typename T> T* set_extension(T* ext) + { + resize_extensions(); + T* tmp = static_cast<T*>(m_extensions[T::priv_id]); + m_extensions[T::priv_id] = static_cast<ispex_base*>(ext); + if (!tmp && ext) (m_container->*m_inc)(); + return tmp; + } + // non-templatized version with manual index: + ispex_base* set_extension(unsigned int index, + ispex_base* ext) + { + resize_extensions(); + ispex_base* tmp = m_extensions[index]; + m_extensions[index] = ext; + if (!tmp && ext) (m_container->*m_inc)(); + return tmp; + } + + // Check for an extension, ext will point to 0 if not present + template <typename T> void get_extension(T*& ext) const + { + ext = static_cast<T*>(m_extensions[T::priv_id]); + } + // Non-templatized version: + ispex_base* get_extension(unsigned int index) const + { + return m_extensions[index]; + } + + // Clear extension, the argument is needed to find the right index: + template <typename T> void clear_extension(const T* ext) + { + resize_extensions(); + if (m_extensions[T::priv_id]) (m_container->*m_dec)(); + m_extensions[T::priv_id] = static_cast<ispex_base*>(0); + } + // Non-templatized version with manual index + void clear_extension(unsigned int index) + { + if (index < m_extensions.size()) + { + if (m_extensions[index]) (m_container->*m_dec)(); + m_extensions[index] = static_cast<ispex_base*>(0); + } + } + + // Make sure the extension array is large enough. Can be called once by + // an initiator module (before issuing the first transaction) to make + // sure that the extension array is of correct size. This is only needed + // if the initiator cannot guarantee that the generic payload object is + // allocated after C++ static construction time. + void resize_extensions() + { + m_extensions.expand(max_num_ispex()); + } + +private: + tlm::tlm_array<ispex_base*> m_extensions; + U* m_container; + cb m_inc, m_dec; + +}; + +class instance_specific_extension_container; + + +//the pool for the container, plain as can be +class instance_specific_extension_container_pool{ + friend class instance_specific_extension_carrier; + friend class instance_specific_extension_container; + instance_specific_extension_container_pool() : unused(NULL){} + inline ~instance_specific_extension_container_pool(); + inline static instance_specific_extension_container_pool& get_ispexcont_pool(){ static instance_specific_extension_container_pool tmp; return tmp;} + inline instance_specific_extension_container* create(); + inline void free(instance_specific_extension_container*); + + instance_specific_extension_container* unused; +}; + +class instance_specific_extension_carrier; + +//this thing contains the vector of extensions per accessor +//which can be really large so this one should be pool allocated +// therefore it keeps a use_count of itself to automatically free itself +// - to this end it provides callbacks to the extensions per accessor +// to increment and decrement the use_count +class instance_specific_extension_container{ + friend class instance_specific_extension_container_pool; + friend class instance_specific_extension_accessor; + friend class instance_specific_extension_carrier; + + instance_specific_extension_container(): use_count(0), next(NULL){resize();} + + void resize(){ + m_ispex_per_accessor.resize(max_num_ispex_accessors()); + for (unsigned int i=0; i<m_ispex_per_accessor.size(); i++) { + m_ispex_per_accessor[i]=new instance_specific_extensions_per_accessor<instance_specific_extension_container>(this, + &instance_specific_extension_container::inc_use_count, + &instance_specific_extension_container::dec_use_count + ); + m_ispex_per_accessor[i]->resize_extensions(); + } + } + + ~instance_specific_extension_container(){ + for (unsigned int i=0; i<m_ispex_per_accessor.size(); i++) delete m_ispex_per_accessor[i]; + } + + void inc_use_count(){use_count++;} + inline void dec_use_count(); + + std::vector<instance_specific_extensions_per_accessor<instance_specific_extension_container>* > m_ispex_per_accessor; + unsigned int use_count; + tlm::tlm_generic_payload* my_txn; + instance_specific_extension_carrier* my_carrier; + instance_specific_extension_container* next; //for pooling +}; + + +inline instance_specific_extension_container_pool::~instance_specific_extension_container_pool(){ + while(unused) { instance_specific_extension_container* tmp=unused; unused=unused->next; delete tmp;} +} + +instance_specific_extension_container* instance_specific_extension_container_pool::create(){ + if (!unused) {unused=new instance_specific_extension_container();} + instance_specific_extension_container* tmp=unused; + unused=unused->next; + return tmp; +} + +void instance_specific_extension_container_pool::free(instance_specific_extension_container* cont){ + cont->next=unused; + unused=cont; +} + +//This is the class that actually sits in the extension array +//we keep this small since that one gets allocated and deallocated all the times +class instance_specific_extension_carrier: public tlm::tlm_extension<instance_specific_extension_carrier>{ + friend class instance_specific_extension_accessor; + +public: + instance_specific_extension_carrier(){ + m_container=instance_specific_extension_container_pool::get_ispexcont_pool().create(); + m_container->my_carrier=this; + } + + virtual tlm::tlm_extension_base* clone() const { + //we don't clone since private info is instance specific and associated to a given txn (the original) + //so the deep copied txn will be virgin in terms of private info + return NULL; + } + void copy_from(tlm::tlm_extension_base const &){return;} + void free(){return;} +private: + instance_specific_extension_container* m_container; +}; + +inline void instance_specific_extension_container::dec_use_count(){ + if ((--use_count)==0) { //if this container isn't used any more + instance_specific_extension_container_pool::get_ispexcont_pool().free(this); //we send it back to our pool + //we have to do that manually, as we cannot rely on the fact that there is MM in the txn + my_txn->clear_extension(my_carrier); //and remove it from the transaction's extension array + delete my_carrier; + } +} + + +//This class 'hides' all the instance specific extension stuff from the user +// he instantiates one of those (e.g. instance_specific_extension_accessor extAcc;) and can then access +// the private extensions +// extAcc(txn).extensionAPIFnCall() +// where extensionAPIFnCall is set_extension, get_extension, clear_extension,... +class instance_specific_extension_accessor{ +public: + instance_specific_extension_accessor(): m_index(max_num_ispex_accessors(true)-1){} + + template<typename T> + inline instance_specific_extensions_per_accessor<instance_specific_extension_container>& operator()(T& txn){ + instance_specific_extension_carrier* carrier; + txn.get_extension(carrier); + if (!carrier){ + carrier=new instance_specific_extension_carrier(); + carrier->m_container->my_txn=&txn; + txn.set_extension(carrier); + } + return *(carrier->m_container->m_ispex_per_accessor[m_index]); + } + +protected: + unsigned int m_index; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/multi_passthrough_initiator_socket.h b/ext/systemc/src/tlm_utils/multi_passthrough_initiator_socket.h new file mode 100644 index 000000000..6404d84bb --- /dev/null +++ b/ext/systemc/src/tlm_utils/multi_passthrough_initiator_socket.h @@ -0,0 +1,299 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ +#ifndef __MULTI_PASSTHROUGH_INITIATOR_SOCKET_H__ +#define __MULTI_PASSTHROUGH_INITIATOR_SOCKET_H__ + +#include "multi_socket_bases.h" + +namespace tlm_utils { + +/* +This class implements a trivial multi initiator socket. +The triviality refers to the fact that the socket does not +do blocking to non-blocking or non-blocking to blocking conversions. + +It allows to connect multiple targets to this socket. +The user has to register callbacks for the bw interface methods +he likes to use. The callbacks are basically equal to the bw interface +methods but carry an additional integer that indicates to which +index of this socket the calling target is connected. +*/ +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types, + unsigned int N=0 +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND +#endif + > +class multi_passthrough_initiator_socket: public multi_init_base< BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > +{ + +public: + + //typedefs + // tlm 2.0 types for nb_transport + typedef typename TYPES::tlm_payload_type transaction_type; + typedef typename TYPES::tlm_phase_type phase_type; + typedef tlm::tlm_sync_enum sync_enum_type; + + // typedefs to keep the fn ptr notations short + typedef sync_enum_type (MODULE::*nb_cb)(int, + transaction_type&, + phase_type&, + sc_core::sc_time&); + typedef void (MODULE::*dmi_cb)(int, sc_dt::uint64, sc_dt::uint64); + + typedef multi_init_base<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > base_type; + + typedef typename base_type::base_target_socket_type base_target_socket_type; + + //CTOR + multi_passthrough_initiator_socket() + : base_type(sc_core::sc_gen_unique_name("multi_passthrough_initiator_socket")) + , m_hierarch_bind(0) + , m_beoe_disabled(false) + , m_dummy(42) + { + } + + //CTOR + multi_passthrough_initiator_socket(const char* name) + : base_type(name) + , m_hierarch_bind(0) + , m_beoe_disabled(false) + , m_dummy(42) + { + } + + ~multi_passthrough_initiator_socket(){ + //clean up everything allocated by 'new' + for (unsigned int i=0; i<m_binders.size(); i++) delete m_binders[i]; + } + + //simple helpers for warnings an errors to shorten in code notation + void display_warning(const std::string& text) const { + std::stringstream s; + s<<"WARNING in instance "<<base_type::name()<<": "<<text; + SC_REPORT_WARNING("/OSCI_TLM-2/multi_socket", s.str().c_str()); + } + + void display_error(const std::string& text) const { + std::stringstream s; + s<<"ERROR in instance "<<base_type::name()<<": "<<text; + SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket", s.str().c_str()); + } + + + //register callback for nb transport of bw interface + void register_nb_transport_bw(MODULE* mod, + sync_enum_type (MODULE::*cb)(int, + transaction_type&, + phase_type&, + sc_core::sc_time&)) + { + //warn if there already is a callback + if (!m_nb_f.empty()){ + display_warning("NBTransport_bw callback already registered."); + return; + } + + //set the functor + m_nb_f.set_function(mod, cb); + } + + //register callback for dmi function of bw interface + void register_invalidate_direct_mem_ptr(MODULE* mod, + void (MODULE::*cb)(int, sc_dt::uint64, sc_dt::uint64)) + { + //warn if there already is a callback + if (!m_dmi_f.empty()){ + display_warning("InvalidateDMI callback already registered."); + return; + } + + //set the functor + m_dmi_f.set_function(mod, cb); + } + + //Override virtual functions of the tlm_initiator_socket: + // this function is called whenever an sc_port (as part of a target socket) + // wants to bind to the export of the underlying tlm_initiator_socket + //At this time a callback binder is created an returned to the sc_port + // of the target socket, so that it binds to the callback binder + virtual tlm::tlm_bw_transport_if<TYPES>& get_base_interface() + { + m_binders.push_back(new callback_binder_bw<TYPES>(m_binders.size())); + return *m_binders[m_binders.size()-1]; + } + + // const overload not allowed for multi-sockets + virtual const tlm::tlm_bw_transport_if<TYPES>& get_base_interface() const + { + display_error("'get_base_interface()' const not allowed for multi-sockets."); + return base_type::get_base_interface(); + } + + //Override virtual functions of the tlm_initiator_socket: + // this function is called whenever an sc_export (as part of a initiator socket) + // wants to bind to the export of the underlying tlm_initiator_socket + // i.e. a hierarchical bind takes place + virtual sc_core::sc_export<tlm::tlm_bw_transport_if<TYPES> >& get_base_export() + { + if (!m_beoe_disabled) //we are not bound hierarchically + base_type::m_export.bind(m_dummy); //so we bind the dummy to avoid a SystemC error + return base_type::get_base_export(); //and then return our own export so that the hierarchical binding is set up properly + } + + virtual const sc_core::sc_export<tlm::tlm_bw_transport_if<TYPES> >& get_base_export() const + { + return base_type::get_base_export(); + } + + //bind against a target socket + virtual void bind(base_target_socket_type& s) + { + //error if this socket is already bound hierarchically + if (m_hierarch_bind) + display_error("Already hierarchically bound."); + + base_type::bind(s); //satisfy systemC, leads to a call to get_base_interface() + + //try to cast the target socket into a fw interface + sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >* p_ex_s=dynamic_cast<sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >*>(&s); + if (!p_ex_s) display_error("Multi socket not bound to tlm_socket."); + + //try a cast into a multi sockets + multi_to_multi_bind_base<TYPES>* test=dynamic_cast<multi_to_multi_bind_base<TYPES>*> (p_ex_s); + if (test) //did we just do a multi-multi bind?? + //if that is the case the multi target socket must have just created a callback binder + // which we want to get from it. + //Moreover, we also just created one, which we will pass to it. + m_sockets.push_back(test->get_last_binder(m_binders[m_binders.size()-1])); + else{ // if not just bind normally + sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >& ex_s=*p_ex_s; + m_sockets.push_back(&((tlm::tlm_fw_transport_if<TYPES>&)ex_s)); //store the interface we are bound against + } + } + + //operator notation for direct bind + void operator() (base_target_socket_type& s) + { + bind(s); + } + + //SystemC standard callback before end of elaboration + void before_end_of_elaboration(){ + //if our export hasn't been bound yet (due to a hierarch binding) + // we bind it now to avoid a SystemC error. + //We must do that, because it is legal not to register a callback on this socket + // as the user might only use b_transport + if (!base_type::m_export.get_interface()){ + base_type::m_export.bind(m_dummy); + } + + //'break' here if the socket was told not to do callback binding + if (m_beoe_disabled) return; + + //get the callback binders of the top of the hierachical bind chain + // NOTE: this could be the same socket if there is no hierachical bind + std::vector<callback_binder_bw<TYPES>* >& binders=get_hierarch_bind()->get_binders(); + + //get the interfaces bound to the top of the hierachical bind chain + // NOTE: this could be the same socket if there is no hierachical bind + m_used_sockets=get_hierarch_bind()->get_sockets(); + + //register the callbacks of this socket with the callback binders + // we just got from the top of the hierachical bind chain + for (unsigned int i=0; i<binders.size(); i++) { + binders[i]->set_callbacks(m_nb_f, m_dmi_f); + } + } + + // + // Bind multi initiator socket to multi initiator socket (hierarchical bind) + // + virtual void bind(base_type& s) + { + if (m_binders.size()) //a multi socket is either bound hierarchically or directly + display_error("Socket already directly bound."); + if (m_hierarch_bind){ + display_warning("Socket already bound hierarchically. Bind attempt ignored."); + return; + } + + //remember to which socket we are hierarchically bound and disable it, + // so that it won't try to register callbacks itself + s.disable_cb_bind(); + m_hierarch_bind=&s; + base_type::bind(s); //satisfy SystemC + } + + //operator notation for hierarchical bind + void operator() (base_type& s) + { + bind(s); + } + + //get access to sub port + tlm::tlm_fw_transport_if<TYPES>* operator[](int i){return m_used_sockets[i];} + + //get the number of bound targets + // NOTE: this is only valid at end of elaboration! + unsigned int size() {return get_hierarch_bind()->get_sockets().size();} + +protected: + //implementation of base class interface + base_type* get_hierarch_bind(){if (m_hierarch_bind) return m_hierarch_bind->get_hierarch_bind(); else return this;} + void disable_cb_bind(){ m_beoe_disabled=true;} + std::vector<callback_binder_bw<TYPES>* >& get_binders(){return m_binders;} + std::vector<tlm::tlm_fw_transport_if<TYPES>*>& get_sockets(){return m_sockets;} + //vector of connected sockets + std::vector<tlm::tlm_fw_transport_if<TYPES>*> m_sockets; + std::vector<tlm::tlm_fw_transport_if<TYPES>*> m_used_sockets; + //vector of binders that convert untagged interface into tagged interface + std::vector<callback_binder_bw<TYPES>*> m_binders; + + base_type* m_hierarch_bind; //pointer to hierarchical bound multi port + bool m_beoe_disabled; // bool that remembers whether this socket shall bind callbacks or not + callback_binder_bw<TYPES> m_dummy; //a callback binder that is bound to the underlying export + // in case there was no real bind + + //callbacks as functors + // (allows to pass the callback to another socket that does not know the type of the module that owns + // the callbacks) + typename callback_binder_bw<TYPES>::nb_func_type m_nb_f; + typename callback_binder_bw<TYPES>::dmi_func_type m_dmi_f; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/multi_passthrough_target_socket.h b/ext/systemc/src/tlm_utils/multi_passthrough_target_socket.h new file mode 100644 index 000000000..03270f7a1 --- /dev/null +++ b/ext/systemc/src/tlm_utils/multi_passthrough_target_socket.h @@ -0,0 +1,340 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ +#ifndef __MULTI_PASSTHROUGH_TARGET_SOCKET_H__ +#define __MULTI_PASSTHROUGH_TARGET_SOCKET_H__ + +#include "tlm_utils/multi_socket_bases.h" +#include <sstream> + +namespace tlm_utils { + +/* +This class implements a trivial multi target socket. +The triviality refers to the fact that the socket does not +do blocking to non-blocking or non-blocking to blocking conversions. + +It allows to connect multiple initiators to this socket. +The user has to register callbacks for the fw interface methods +he likes to use. The callbacks are basically equal to the fw interface +methods but carry an additional integer that indicates to which +index of this socket the calling initiator is connected. +*/ +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types, + unsigned int N=0 +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND +#endif + > +class multi_passthrough_target_socket: public multi_target_base< BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > + , public multi_to_multi_bind_base<TYPES> +{ + +public: + + //typedefs + // tlm 2.0 types for nb_transport + typedef typename TYPES::tlm_payload_type transaction_type; + typedef typename TYPES::tlm_phase_type phase_type; + typedef tlm::tlm_sync_enum sync_enum_type; + + // typedefs to keep the fn ptr notations short + typedef sync_enum_type (MODULE::*nb_cb)(int, transaction_type&, phase_type&, sc_core::sc_time&); + typedef void (MODULE::*b_cb)(int, transaction_type&, sc_core::sc_time&); + typedef unsigned int (MODULE::*dbg_cb)(int, transaction_type& txn); + typedef bool (MODULE::*dmi_cb)(int, transaction_type& txn, tlm::tlm_dmi& dmi); + + typedef multi_target_base<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > base_type; + + typedef typename base_type::base_initiator_socket_type base_initiator_socket_type; + + //CTOR + multi_passthrough_target_socket() + : base_type(sc_core::sc_gen_unique_name("multi_passthrough_target_socket")) + , m_hierarch_bind(0) + , m_eoe_disabled(false) + , m_export_callback_created(false) + { + } + + //CTOR + multi_passthrough_target_socket(const char* name) + : base_type(name) + , m_hierarch_bind(0) + , m_eoe_disabled(false) + , m_export_callback_created(false) + { + } + + ~multi_passthrough_target_socket(){ + //clean up everything allocated by 'new' + for (unsigned int i=0; i<m_binders.size(); i++) delete m_binders[i]; + } + + //simple helpers for warnings an errors to shorten in code notation + void display_warning(const std::string& text) const { + std::stringstream s; + s<<"WARNING in instance "<<base_type::name()<<": "<<text; + SC_REPORT_WARNING("/OSCI_TLM-2/multi_socket", s.str().c_str()); + } + + void display_error(const std::string& text) const { + std::stringstream s; + s<<"ERROR in instance "<<base_type::name()<<": "<<text; + SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket", s.str().c_str()); + } + + void check_export_binding() + { + //if our export hasn't been bound yet (due to a hierarch binding) + // we bind it now. + //We do that here as the user of the target port HAS to bind at least on callback, + //otherwise the socket was useless. Nevertheless, the target socket may still + // stay unbound afterwards. + if (!sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >::get_interface()) + { + // We bind to a callback_binder that will be used as the first interface + // i.e. calls to the sc_export will have the same ID as calls from the first initator + // socket bound + callback_binder_fw<TYPES> * binder; + + if (m_binders.size() == 0) + { + binder = new callback_binder_fw<TYPES>(m_binders.size()); + m_binders.push_back(binder); + m_export_callback_created = true; + } + else + { + binder = m_binders[0]; + } + + sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >::bind(*binder); + } + } + + //register callback for nb transport of fw interface + void register_nb_transport_fw(MODULE* mod, + nb_cb cb) + { + check_export_binding(); + + //warn if there already is a callback + if (!m_nb_f.empty()){ + display_warning("NBTransport_bw callback already registered."); + return; + } + + //set the functor + m_nb_f.set_function(mod, cb); + } + + //register callback for b transport of fw interface + void register_b_transport(MODULE* mod, + b_cb cb) + { + check_export_binding(); + + //warn if there already is a callback + if (!m_b_f.empty()){ + display_warning("BTransport callback already registered."); + return; + } + + //set the functor + m_b_f.set_function(mod, cb); + } + + //register callback for debug transport of fw interface + void register_transport_dbg(MODULE* mod, + dbg_cb cb) + { + check_export_binding(); + + //warn if there already is a callback + if (!m_dbg_f.empty()){ + display_warning("DebugTransport callback already registered."); + return; + } + + //set the functor + m_dbg_f.set_function(mod, cb); + } + + //register callback for DMI of fw interface + void register_get_direct_mem_ptr(MODULE* mod, + dmi_cb cb) + { + check_export_binding(); + + //warn if there already is a callback + if (!m_dmi_f.empty()){ + display_warning("DMI callback already registered."); + return; + } + + //set the functor + m_dmi_f.set_function(mod, cb); + } + + + //Override virtual functions of the tlm_target_socket: + // this function is called whenever an sc_port (as part of a init socket) + // wants to bind to the export of the underlying tlm_target_socket + //At this time a callback binder is created an returned to the sc_port + // of the init socket, so that it binds to the callback binder + virtual tlm::tlm_fw_transport_if<TYPES>& get_base_interface() + { + //error if this socket is already bound hierarchically + if (m_hierarch_bind) display_error("Socket already bound hierarchically."); + + if (!m_export_callback_created) + m_binders.push_back(new callback_binder_fw<TYPES>(m_binders.size())); + else + m_export_callback_created = false; + + return *m_binders[m_binders.size()-1]; + } + + // const overload not allowed for multi-sockets + virtual const tlm::tlm_fw_transport_if<TYPES>& get_base_interface() const + { + display_error("'get_base_interface()' const not allowed for multi-sockets."); + return base_type::get_base_interface(); + } + + //just return the export of the underlying tlm_target_socket in case of a hierarchical bind + virtual sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >& get_base_export() + { + return *this; + } + + //just return the export of the underlying tlm_target_socket in case of a hierarchical bind + virtual const sc_core::sc_export<tlm::tlm_fw_transport_if<TYPES> >& get_base_export() const + { + return base_type::get_base_export(); + } + + //the standard end of elaboration callback + void end_of_elaboration(){ + //'break' here if the socket was told not to do callback binding + if (m_eoe_disabled) return; + + //get the callback binders and the multi binds of the top of the hierachical bind chain + // NOTE: this could be the same socket if there is no hierachical bind + std::vector<callback_binder_fw<TYPES>* >& binders=get_hierarch_bind()->get_binders(); + std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*>& multi_binds=get_hierarch_bind()->get_multi_binds(); + + //iterate over all binders + for (unsigned int i=0; i<binders.size(); i++) { + binders[i]->set_callbacks(m_nb_f, m_b_f, m_dmi_f, m_dbg_f); //set the callbacks for the binder + if (multi_binds.find(i)!=multi_binds.end()) //check if this connection is multi-multi + //if so remember the interface + m_sockets.push_back(multi_binds[i]); + else{ //if we are bound to a normal socket + //get the calling port and try to cast it into a tlm socket base + base_initiator_socket_type* test=dynamic_cast<base_initiator_socket_type*>(binders[i]->get_other_side()); + if (!test){display_error("Not bound to tlm_socket.");} + m_sockets.push_back(&test->get_base_interface()); //remember the interface + } + } + } + + // + // Bind multi target socket to multi target socket (hierarchical bind) + // + virtual void bind(base_type& s) + { + //warn if already bound hierarchically + if (m_eoe_disabled){ + display_warning("Socket already bound hierarchically. Bind attempt ignored."); + return; + } + + //disable our own end of elaboration call + disable_cb_bind(); + + //inform the bound target socket that it is bound hierarchically now + s.set_hierarch_bind((base_type*)this); + base_type::bind(s); //satisfy SystemC + } + + //operator notation for hierarchical bind + void operator() (base_type& s) + { + bind(s); + } + + //get access to sub port + tlm::tlm_bw_transport_if<TYPES>* operator[](int i){return m_sockets[i];} + + //get number of bound initiators + // NOTE: this is only valid at end of elaboration! + unsigned int size(){return get_hierarch_bind()->get_binders().size();} + +protected: + //implementation of base class interface + base_type* get_hierarch_bind(){if (m_hierarch_bind) return m_hierarch_bind->get_hierarch_bind(); else return this;} + std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*>& get_multi_binds(){return m_multi_binds;} + void set_hierarch_bind(base_type* h){m_hierarch_bind=h;} + tlm::tlm_fw_transport_if<TYPES>* get_last_binder(tlm::tlm_bw_transport_if<TYPES>* other){ + m_multi_binds[m_binders.size()-1]=other; + return m_binders[m_binders.size()-1]; + } + + //map that stores to which index a multi init socket is connected + // and the interface of the multi init socket + std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*> m_multi_binds; + + void disable_cb_bind(){ m_eoe_disabled=true;} + std::vector<callback_binder_fw<TYPES>* >& get_binders(){return m_binders;} + //vector of connected sockets + std::vector<tlm::tlm_bw_transport_if<TYPES>*> m_sockets; + //vector of binders that convert untagged interface into tagged interface + std::vector<callback_binder_fw<TYPES>*> m_binders; + + base_type* m_hierarch_bind; //pointer to hierarchical bound multi port + bool m_eoe_disabled; //bool that diables callback bindings at end of elaboration + bool m_export_callback_created; // bool to indicate that a callback has already been created for export binding + + //callbacks as functors + // (allows to pass the callback to another socket that does not know the type of the module that owns + // the callbacks) + typename callback_binder_fw<TYPES>::nb_func_type m_nb_f; + typename callback_binder_fw<TYPES>::b_func_type m_b_f; + typename callback_binder_fw<TYPES>::debug_func_type m_dbg_f; + typename callback_binder_fw<TYPES>::dmi_func_type m_dmi_f; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/multi_socket_bases.h b/ext/systemc/src/tlm_utils/multi_socket_bases.h new file mode 100644 index 000000000..457398495 --- /dev/null +++ b/ext/systemc/src/tlm_utils/multi_socket_bases.h @@ -0,0 +1,418 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +#ifndef __MULTI_SOCKET_BASES_H__ +#define __MULTI_SOCKET_BASES_H__ + +#include <systemc> +#include <tlm> + +#include <map> +#include <sstream> + +namespace tlm_utils { + +template <typename signature> +struct fn_container{ + signature function; +}; + +#define TLM_DEFINE_FUNCTOR(name) \ +template <typename MODULE, typename TRAITS> \ +inline TLM_RET_VAL static_##name( void* mod \ + , void* fn \ + , int index \ + , TLM_FULL_ARG_LIST) \ +{ \ + typedef fn_container<TLM_RET_VAL (MODULE::*)(int, TLM_FULL_ARG_LIST)> fn_container_type; \ + MODULE* tmp_mod=static_cast<MODULE*>(mod); \ + fn_container_type* tmp_cb =static_cast<fn_container_type*> (fn); \ + return (tmp_mod->*(tmp_cb->function))(index, TLM_ARG_LIST_WITHOUT_TYPES); \ +}\ +\ +template <typename MODULE, typename TRAITS> \ +inline void delete_fn_container_of_##name(void* fn) \ +{ \ + typedef fn_container<TLM_RET_VAL (MODULE::*)(int, TLM_FULL_ARG_LIST)> fn_container_type; \ + fn_container_type* tmp_cb =static_cast<fn_container_type*> (fn); \ + if (tmp_cb) delete tmp_cb;\ +} \ +\ +template <typename TRAITS> \ +class name##_functor{ \ +public: \ + typedef typename TRAITS::tlm_payload_type payload_type; \ + typedef typename TRAITS::tlm_phase_type phase_type; \ + typedef TLM_RET_VAL (*call_fn)(void*,void*, int, TLM_FULL_ARG_LIST); \ + typedef void (*del_fn)(void*); \ +\ + name##_functor(): m_fn(0), m_del_fn(0), m_mod(0), m_mem_fn(0){} \ + ~name##_functor(){if (m_del_fn) (*m_del_fn)(m_mem_fn);} \ +\ + template <typename MODULE> \ + void set_function(MODULE* mod, TLM_RET_VAL (MODULE::*cb)(int, TLM_FULL_ARG_LIST)){ \ + typedef fn_container<TLM_RET_VAL (MODULE::*)(int, TLM_FULL_ARG_LIST)> fn_container_type; \ + m_fn=&static_##name<MODULE,TRAITS>;\ + m_del_fn=&delete_fn_container_of_##name<MODULE,TRAITS>;\ + m_del_fn(m_mem_fn); \ + fn_container_type* tmp= new fn_container_type(); \ + tmp->function=cb; \ + m_mod=static_cast<void*>(mod); \ + m_mem_fn=static_cast<void*>(tmp); \ + } \ + \ + TLM_RET_VAL operator()(int index, TLM_FULL_ARG_LIST){ \ + return m_fn(m_mod,m_mem_fn, index, TLM_ARG_LIST_WITHOUT_TYPES); \ + } \ +\ + bool empty(){return (m_mod==0 || m_mem_fn==0 || m_fn==0);}\ +\ +protected: \ + call_fn m_fn;\ + del_fn m_del_fn; \ + void* m_mod; \ + void* m_mem_fn; \ +private: \ + name##_functor& operator=(const name##_functor&); \ +} + + +#define TLM_RET_VAL tlm::tlm_sync_enum +#define TLM_FULL_ARG_LIST typename TRAITS::tlm_payload_type& txn, typename TRAITS::tlm_phase_type& ph, sc_core::sc_time& t +#define TLM_ARG_LIST_WITHOUT_TYPES txn,ph,t +TLM_DEFINE_FUNCTOR(nb_transport); +#undef TLM_RET_VAL +#undef TLM_FULL_ARG_LIST +#undef TLM_ARG_LIST_WITHOUT_TYPES + +#define TLM_RET_VAL void +#define TLM_FULL_ARG_LIST typename TRAITS::tlm_payload_type& txn, sc_core::sc_time& t +#define TLM_ARG_LIST_WITHOUT_TYPES txn,t +TLM_DEFINE_FUNCTOR(b_transport); +#undef TLM_RET_VAL +#undef TLM_FULL_ARG_LIST +#undef TLM_ARG_LIST_WITHOUT_TYPES + +#define TLM_RET_VAL unsigned int +#define TLM_FULL_ARG_LIST typename TRAITS::tlm_payload_type& txn +#define TLM_ARG_LIST_WITHOUT_TYPES txn +TLM_DEFINE_FUNCTOR(debug_transport); +#undef TLM_RET_VAL +#undef TLM_FULL_ARG_LIST +#undef TLM_ARG_LIST_WITHOUT_TYPES + +#define TLM_RET_VAL bool +#define TLM_FULL_ARG_LIST typename TRAITS::tlm_payload_type& txn, tlm::tlm_dmi& dmi +#define TLM_ARG_LIST_WITHOUT_TYPES txn,dmi +TLM_DEFINE_FUNCTOR(get_dmi_ptr); +#undef TLM_RET_VAL +#undef TLM_FULL_ARG_LIST +#undef TLM_ARG_LIST_WITHOUT_TYPES + +#define TLM_RET_VAL void +#define TLM_FULL_ARG_LIST sc_dt::uint64 l, sc_dt::uint64 u +#define TLM_ARG_LIST_WITHOUT_TYPES l,u +TLM_DEFINE_FUNCTOR(invalidate_dmi); +#undef TLM_RET_VAL +#undef TLM_FULL_ARG_LIST +#undef TLM_ARG_LIST_WITHOUT_TYPES + +#undef TLM_DEFINE_FUNCTOR + +/* +This class implements the fw interface. +It allows to register a callback for each of the fw interface methods. +The callbacks simply forward the fw interface call, but add the id (an int) +of the callback binder to the signature of the call. +*/ +template <typename TYPES> +class callback_binder_fw: public tlm::tlm_fw_transport_if<TYPES>{ + public: + //typedefs according to the used TYPES class + typedef typename TYPES::tlm_payload_type transaction_type; + typedef typename TYPES::tlm_phase_type phase_type; + typedef tlm::tlm_sync_enum sync_enum_type; + + //typedefs for the callbacks + typedef nb_transport_functor<TYPES> nb_func_type; + typedef b_transport_functor<TYPES> b_func_type; + typedef debug_transport_functor<TYPES> debug_func_type; + typedef get_dmi_ptr_functor<TYPES> dmi_func_type; + + //ctor: an ID is needed to create a callback binder + callback_binder_fw(int id): m_id(id), m_nb_f(0), m_b_f(0), m_dbg_f(0), m_dmi_f(0), m_caller_port(0) { + } + + //the nb_transport method of the fw interface + sync_enum_type nb_transport_fw(transaction_type& txn, + phase_type& p, + sc_core::sc_time& t){ + //check if a callback is registered + if ((m_nb_f == 0) || (m_nb_f && m_nb_f->empty())) { + //std::cerr<<"No function registered"<<std::endl; + SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket","Call to nb_transport_fw without a registered callback for nb_transport_fw."); + } + else + return (*m_nb_f)(m_id, txn, p, t); //do the callback + return tlm::TLM_ACCEPTED; //unreachable + } + + //the b_transport method of the fw interface + void b_transport(transaction_type& trans,sc_core::sc_time& t){ + //check if a callback is registered + if ((m_b_f == 0) || (m_b_f && m_b_f->empty())) { + SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket","Call to b_transport without a registered callback for b_transport."); + } + else + (*m_b_f)(m_id, trans,t); //do the callback + } + + //the DMI method of the fw interface + bool get_direct_mem_ptr(transaction_type& trans, tlm::tlm_dmi& dmi_data){ + //check if a callback is registered + if ((m_dmi_f == 0) && (m_dmi_f && m_dmi_f->empty())) { + dmi_data.allow_none(); + dmi_data.set_start_address(0x0); + dmi_data.set_end_address((sc_dt::uint64)-1); + return false; + } + else + return (*m_dmi_f)(m_id, trans,dmi_data); //do the callback + } + + //the debug method of the fw interface + unsigned int transport_dbg(transaction_type& trans){ + //check if a callback is registered + if ((m_dbg_f == 0) || (m_dbg_f && m_dbg_f->empty())) { + return 0; + } + else + return (*m_dbg_f)(m_id, trans); //do the callback + } + + //the SystemC standard callback register_port: + // - called when a port if bound to the interface + // - allowd to find out who is bound to that callback binder + void register_port(sc_core::sc_port_base& b, const char* name){ + m_caller_port=&b; + } + + //register callbacks for all fw interface methods at once + void set_callbacks(nb_func_type& cb1, b_func_type& cb2, dmi_func_type& cb3, debug_func_type& cb4){ + m_nb_f=&cb1; + m_b_f=&cb2; + m_dmi_f=&cb3; + m_dbg_f=&cb4; + } + + //getter method to get the port that is bound to that callback binder + // NOTE: this will only return a valid value at end of elaboration + // (but not before end of elaboration!) + sc_core::sc_port_base* get_other_side(){return m_caller_port;} + + private: + //the ID of the callback binder + int m_id; + + //the callbacks + nb_func_type* m_nb_f; + b_func_type* m_b_f; + debug_func_type* m_dbg_f; + dmi_func_type* m_dmi_f; + + //the port bound to that callback binder + sc_core::sc_port_base* m_caller_port; +}; + +/* +This class implements the bw interface. +It allows to register a callback for each of the bw interface methods. +The callbacks simply forward the bw interface call, but add the id (an int) +of the callback binder to the signature of the call. +*/ +template <typename TYPES> +class callback_binder_bw: public tlm::tlm_bw_transport_if<TYPES>{ + public: + //typedefs according to the used TYPES class + typedef typename TYPES::tlm_payload_type transaction_type; + typedef typename TYPES::tlm_phase_type phase_type; + typedef tlm::tlm_sync_enum sync_enum_type; + + //typedefs for the callbacks + typedef nb_transport_functor<TYPES> nb_func_type; + typedef invalidate_dmi_functor<TYPES> dmi_func_type; + + //ctor: an ID is needed to create a callback binder + callback_binder_bw(int id): m_id(id), m_nb_f(0), m_dmi_f(0) { + } + + //the nb_transport method of the bw interface + sync_enum_type nb_transport_bw(transaction_type& txn, + phase_type& p, + sc_core::sc_time& t){ + //check if a callback is registered + if ((m_nb_f == 0) || (m_nb_f && m_nb_f->empty())) { + SC_REPORT_ERROR("/OSCI_TLM-2/multi_socket","Call to nb_transport_bw without a registered callback for nb_transport_bw"); + } + else + return (*m_nb_f)(m_id, txn, p, t); //do the callback + return tlm::TLM_ACCEPTED; //unreachable + } + + //the DMI method of the bw interface + void invalidate_direct_mem_ptr(sc_dt::uint64 l, sc_dt::uint64 u){ + //check if a callback is registered + if ((m_dmi_f == 0) || (m_dmi_f && m_dmi_f->empty())) { + return; + } + else + (*m_dmi_f)(m_id,l,u); //do the callback + } + + //register callbacks for all bw interface methods at once + void set_callbacks(nb_func_type& cb1, dmi_func_type& cb2){ + m_nb_f=&cb1; + m_dmi_f=&cb2; + } + + private: + //the ID of the callback binder + int m_id; + //the callbacks + nb_func_type* m_nb_f; + dmi_func_type* m_dmi_f; +}; + + +/* +This class forms the base for multi initiator sockets. +It enforces a multi initiator socket to implement all functions +needed to do hierarchical bindings. +*/ +template <unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types, + unsigned int N=0 +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND +#endif + > +class multi_init_base: public tlm::tlm_initiator_socket<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + >{ +public: + //typedef for the base type: the standard tlm initiator socket + typedef tlm::tlm_initiator_socket<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > base_type; + + //this method shall disable the code that does the callback binding + // that registers callbacks to binders + virtual void disable_cb_bind()=0; + + //this method shall return the multi_init_base to which the + // multi_init_base is bound hierarchically + // If the base is not bound hierarchically it shall return a pointer to itself + virtual multi_init_base* get_hierarch_bind()=0; + + //this method shall return a vector of the callback binders of multi initiator socket + virtual std::vector<callback_binder_bw<TYPES>* >& get_binders()=0; + + //this method shall return a vector of all target interfaces bound to this multi init socket + virtual std::vector<tlm::tlm_fw_transport_if<TYPES>*>& get_sockets()=0; + + //ctor and dtor + virtual ~multi_init_base(){} + multi_init_base():base_type(sc_core::sc_gen_unique_name("multi_init_base")){} + multi_init_base(const char* name):base_type(name){} +}; + +/* +This class forms the base for multi target sockets. +It enforces a multi target socket to implement all functions +needed to do hierarchical bindings. +*/ +template <unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types, + unsigned int N=0 +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND +#endif + > +class multi_target_base: public tlm::tlm_target_socket<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + >{ +public: + //typedef for the base type: the standard tlm target socket + typedef tlm::tlm_target_socket<BUSWIDTH, + TYPES, + N +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + ,POL +#endif + > base_type; + + //this method shall return the multi_init_base to which the + // multi_init_base is bound hierarchically + // If the base is not bound hierarchically it shall return a pointer to itself + virtual multi_target_base* get_hierarch_bind()=0; + + //this method shall inform the multi target socket that it is bound + // hierarchically and to which other multi target socket it is bound hierarchically + virtual void set_hierarch_bind(multi_target_base*)=0; + + //this method shall return a vector of the callback binders of multi initiator socket + virtual std::vector<callback_binder_fw<TYPES>* >& get_binders()=0; + + //this method shall return a map of all multi initiator sockets that are bound to this multi target + // the key of the map is the index at which the multi initiator i bound, while the value + // is the interface of the multi initiator socket that is bound at that index + virtual std::map<unsigned int, tlm::tlm_bw_transport_if<TYPES>*>& get_multi_binds()=0; + + //ctor and dtor + virtual ~multi_target_base(){} + multi_target_base():base_type(sc_core::sc_gen_unique_name("multi_target_base")){} + multi_target_base(const char* name):base_type(name){} +}; + +/* +All multi sockets must additionally derive from this class. +It enforces a multi socket to implement a function +needed to do multi init to multi target bindings. +*/ +template <typename TYPES> +class multi_to_multi_bind_base{ +public: + virtual ~multi_to_multi_bind_base(){} + virtual tlm::tlm_fw_transport_if<TYPES>* get_last_binder(tlm::tlm_bw_transport_if<TYPES>*)=0; +}; + +} +#endif diff --git a/ext/systemc/src/tlm_utils/passthrough_target_socket.h b/ext/systemc/src/tlm_utils/passthrough_target_socket.h new file mode 100644 index 000000000..1a3b14df7 --- /dev/null +++ b/ext/systemc/src/tlm_utils/passthrough_target_socket.h @@ -0,0 +1,479 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +#ifndef __PASSTHROUGH_TARGET_SOCKET_H__ +#define __PASSTHROUGH_TARGET_SOCKET_H__ + +#include <tlm> +#include <sstream> + +namespace tlm_utils { + +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class passthrough_target_socket : + public tlm::tlm_target_socket<BUSWIDTH, TYPES> +{ +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> base_type; + +public: + passthrough_target_socket() : + base_type(sc_core::sc_gen_unique_name("passthrough_target_socket")), + m_process(this->name()) + { + bind(m_process); + } + + explicit passthrough_target_socket(const char* n) : + base_type(n), + m_process(this->name()) + { + bind(m_process); + } + + using tlm::tlm_target_socket<BUSWIDTH, TYPES>::bind; + + // REGISTER_XXX + void register_nb_transport_fw(MODULE* mod, + sync_enum_type (MODULE::*cb)(transaction_type&, + phase_type&, + sc_core::sc_time&)) + { + m_process.set_nb_transport_ptr(mod, cb); + } + + void register_b_transport(MODULE* mod, + void (MODULE::*cb)(transaction_type&, + sc_core::sc_time&)) + { + m_process.set_b_transport_ptr(mod, cb); + } + + void register_transport_dbg(MODULE* mod, + unsigned int (MODULE::*cb)(transaction_type&)) + { + m_process.set_transport_dbg_ptr(mod, cb); + } + + void register_get_direct_mem_ptr(MODULE* mod, + bool (MODULE::*cb)(transaction_type&, + tlm::tlm_dmi&)) + { + m_process.set_get_direct_mem_ptr(mod, cb); + } + +private: + class process : public tlm::tlm_fw_transport_if<TYPES> + { + 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::*GetDirectMem_ptr)(transaction_type&, + tlm::tlm_dmi&); + + process(const std::string& name) : + m_name(name), + m_mod(0), + m_nb_transport_ptr(0), + m_b_transport_ptr(0), + m_transport_dbg_ptr(0), + m_get_direct_mem_ptr(0) + { + } + + void set_nb_transport_ptr(MODULE* mod, NBTransportPtr p) + { + if (m_nb_transport_ptr) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + + } else { + 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) { + std::stringstream s; + s << m_name << ": blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": debug callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_transport_dbg_ptr = p; + } + } + + void set_get_direct_mem_ptr(MODULE* mod, GetDirectMem_ptr p) + { + if (m_get_direct_mem_ptr) { + std::stringstream s; + s << m_name << ": get DMI pointer callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_get_direct_mem_ptr = p; + } + } + + sync_enum_type nb_transport_fw(transaction_type& trans, + phase_type& phase, + sc_core::sc_time& t) + { + if (m_nb_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + + } else { + std::stringstream s; + s << m_name << ": no non-blocking callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void b_transport(transaction_type& trans, sc_core::sc_time& t) + { + if (m_b_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_b_transport_ptr)(trans, t); + + } else { + std::stringstream s; + s << m_name << ": no blocking callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } + } + + unsigned int transport_dbg(transaction_type& trans) + { + if (m_transport_dbg_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_dbg_ptr)(trans); + + } else { + // 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 + assert(m_mod); + return (m_mod->*m_get_direct_mem_ptr)(trans, dmi_data); + + } else { + // 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: + const std::string m_name; + MODULE* m_mod; + NBTransportPtr m_nb_transport_ptr; + BTransportPtr m_b_transport_ptr; + TransportDbgPtr m_transport_dbg_ptr; + GetDirectMem_ptr m_get_direct_mem_ptr; + }; + +private: + process m_process; +}; + +//ID Tagged version +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class passthrough_target_socket_tagged : + public tlm::tlm_target_socket<BUSWIDTH, TYPES> +{ +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> base_type; + +public: + passthrough_target_socket_tagged() : + base_type(sc_core::sc_gen_unique_name("passthrough_target_socket_tagged")), + m_process(this->name()) + { + bind(m_process); + } + + explicit passthrough_target_socket_tagged(const char* n) : + base_type(n), + m_process(this->name()) + { + bind(m_process); + } + + using tlm::tlm_target_socket<BUSWIDTH, TYPES>::bind; + + // 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) + { + m_process.set_nb_transport_ptr(mod, cb); + m_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) + { + m_process.set_b_transport_ptr(mod, cb); + m_process.set_b_transport_user_id(id); + } + + void register_transport_dbg(MODULE* mod, + unsigned int (MODULE::*cb)(int id, + transaction_type&), + int id) + { + m_process.set_transport_dbg_ptr(mod, cb); + m_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) + { + m_process.set_get_direct_mem_ptr(mod, cb); + m_process.set_get_dmi_user_id(id); + } + +private: + class process : public tlm::tlm_fw_transport_if<TYPES> + { + 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::*GetDirectMem_ptr)(int id, + transaction_type&, + tlm::tlm_dmi&); + + process(const std::string& name) : + m_name(name), + 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) + { + } + + 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) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": debug callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_transport_dbg_ptr = p; + } + } + + void set_get_direct_mem_ptr(MODULE* mod, GetDirectMem_ptr p) + { + if (m_get_direct_mem_ptr) { + std::stringstream s; + s << m_name << ": get DMI pointer callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_get_direct_mem_ptr = p; + } + } + + sync_enum_type nb_transport_fw(transaction_type& trans, + phase_type& phase, + sc_core::sc_time& t) + { + if (m_nb_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, trans, phase, t); + + } else { + std::stringstream s; + s << m_name << ": no non-blocking callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void b_transport(transaction_type& trans, sc_core::sc_time& t) + { + if (m_b_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, trans, t); + + } else { + std::stringstream s; + s << m_name << ": no blocking callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/passthrough_socket",s.str().c_str()); + } + } + + unsigned int transport_dbg(transaction_type& trans) + { + if (m_transport_dbg_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_dbg_ptr)(m_transport_dbg_user_id, trans); + + } else { + // 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 + assert(m_mod); + return (m_mod->*m_get_direct_mem_ptr)(m_get_dmi_user_id, trans, dmi_data); + + } else { + // 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: + const std::string m_name; + MODULE* m_mod; + NBTransportPtr m_nb_transport_ptr; + BTransportPtr m_b_transport_ptr; + TransportDbgPtr m_transport_dbg_ptr; + GetDirectMem_ptr 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; + }; + +private: + process m_process; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/peq_with_cb_and_phase.h b/ext/systemc/src/tlm_utils/peq_with_cb_and_phase.h new file mode 100644 index 000000000..60f96e6bd --- /dev/null +++ b/ext/systemc/src/tlm_utils/peq_with_cb_and_phase.h @@ -0,0 +1,301 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +// 12-Jan-2009 John Aynsley Bug fix. Phase argument to notify should be const +// 20-Mar-2009 John Aynsley Add cancel_all() method + + +#ifndef __PEQ_WITH_CB_AND_PHASE_H__ +#define __PEQ_WITH_CB_AND_PHASE_H__ + +#ifndef SC_INCLUDE_DYNAMIC_PROCESSES // needed for sc_spawn +# define SC_INCLUDE_DYNAMIC_PROCESSES +#endif + +#include <vector> +#include <systemc> +#include <tlm> + +namespace tlm_utils { + +template <typename PAYLOAD> +class time_ordered_list +{ +public: + struct element + { + struct element *next; + PAYLOAD p; + sc_core::sc_time t; + sc_dt::uint64 d; + element(PAYLOAD& p, sc_core::sc_time t, sc_dt::uint64 d): p(p),t(t),d(d) {} + element(){} + }; + + element *nill; + element *empties; + element *list; + unsigned int size; + + time_ordered_list() + : nill(new element()), + empties(NULL), + list(nill), + size(0) + { + } + + ~time_ordered_list() { + reset(); + while(empties){ + struct element *e=empties->next; + delete empties; + empties=e; + } + delete nill; + } + + void reset() { + while(size) { + delete_top(); + } + } + + void insert(const PAYLOAD& p, sc_core::sc_time t) { + if (!empties) { + empties=new struct element(); + empties->next=NULL; + } + + struct element *e=empties; + empties=empties->next; + e->p=p; + e->t=t; + e->d=sc_core::sc_delta_count(); + + struct element * ancestor=nill; + struct element * iterator=list; + while (iterator!=nill && iterator->t<=t){ + ancestor=iterator; + iterator=iterator->next; + } + if (ancestor==nill){ + e->next=list; + list=e; + } + else { + e->next=iterator; + ancestor->next=e; + } + size++; + } + + void delete_top(){ + if (list != nill) { + struct element *e=list; + list=list->next; + e->next=empties; + empties=e; + size--; + } + } + + unsigned int get_size() + { + return size; + } + + PAYLOAD &top() + { + return list->p; + } + sc_core::sc_time top_time() + { + return list->t; + } + + sc_dt::uint64& top_delta() + { + return list->d; + } + + sc_core::sc_time next_time() + { + return list->next->t; + } +}; + +//--------------------------------------------------------------------------- +/** + * An event queue that can contain any number of pending + * notifications. Each notification have an associate payload. + */ +//--------------------------------------------------------------------------- +template<typename OWNER,typename TYPES=tlm::tlm_base_protocol_types> +class peq_with_cb_and_phase: + public sc_core::sc_object +{ + + typedef typename TYPES::tlm_payload_type tlm_payload_type; + typedef typename TYPES::tlm_phase_type tlm_phase_type; + typedef std::pair<tlm_payload_type*, tlm_phase_type> PAYLOAD; + typedef void (OWNER::*cb)(tlm_payload_type&, const tlm_phase_type&); + + class delta_list{ + public: + delta_list(){ + reset(); + entries.resize(100); + } + + inline void insert(const PAYLOAD& p){ + if (size==entries.size()){ + entries.resize(entries.size()*2); + } + entries[size++]=p; + } + + inline PAYLOAD& get(){ + return entries[out++]; + } + + inline bool next(){ + return out<size; + } + + inline void reset(){ + size=0; + out=0; + } + public: + unsigned int size; + private: + std::vector<PAYLOAD> entries; + unsigned int out; + }; + +public: + + peq_with_cb_and_phase(OWNER* _owner, cb _cb) + :sc_core::sc_object( sc_core::sc_gen_unique_name( "peq_with_cb_and_phase" ) ) + ,m_owner(_owner) + ,m_cb(_cb) + { + sc_core::sc_spawn_options opts; + opts.spawn_method(); + opts.set_sensitivity(&m_e); + opts.dont_initialize(); + sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this), + sc_core::sc_gen_unique_name("fec"), &opts); + } + + peq_with_cb_and_phase(const char* _name, OWNER* _owner,cb _cb) + : sc_core::sc_object( _name ) + ,m_owner(_owner) + ,m_cb(_cb) + { + sc_core::sc_spawn_options opts; + opts.spawn_method(); + opts.set_sensitivity(&m_e); + opts.dont_initialize(); + sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this), + sc_core::sc_gen_unique_name("fec"), &opts); + } + + ~peq_with_cb_and_phase(){} + + void notify (tlm_payload_type& t, const tlm_phase_type& p, const sc_core::sc_time& when){ + //t.aquire(); + if (when==sc_core::SC_ZERO_TIME) { + if (sc_core::sc_delta_count() & (sc_dt::uint64)0x1) //uneven delta cycle so delta delay is for even cylce + m_even_delta.insert(PAYLOAD(&t,p)); + else + m_uneven_delta.insert(PAYLOAD(&t,p)); //even delta cycle so delta delay is for uneven delta + m_e.notify(sc_core::SC_ZERO_TIME); + } + else { + m_ppq.insert(PAYLOAD(&t,p), when + sc_core::sc_time_stamp() ); + m_e.notify(when); // note, this will only over-right the "newest" event. + } + } + + void notify (tlm_payload_type& t, const tlm_phase_type& p){ + m_immediate_yield.insert(PAYLOAD(&t,p)); + m_e.notify(); // immediate notification + } + + // Cancel all events from the event queue + void cancel_all() { + m_ppq.reset(); + m_uneven_delta.reset(); + m_even_delta.reset(); + m_immediate_yield.reset(); + m_e.cancel(); + } + +private: + + void fec(){ + //immediate yield notifications + while(m_immediate_yield.next()) {PAYLOAD& tmp=m_immediate_yield.get(); (m_owner->*m_cb)(*tmp.first, tmp.second);} //tmp.first->release();} + m_immediate_yield.reset(); + + //delta notifications + if (sc_core::sc_delta_count() & (sc_dt::uint64) 0x1) {//uneven delta so put out all payloads for uneven delta + while (m_uneven_delta.next()) {PAYLOAD& tmp=m_uneven_delta.get(); (m_owner->*m_cb)(*tmp.first, tmp.second);} //tmp.first->release();} + m_uneven_delta.reset(); + if (m_even_delta.size) m_e.notify(sc_core::SC_ZERO_TIME); + } + else { + while (m_even_delta.next()) {PAYLOAD& tmp=m_even_delta.get(); (m_owner->*m_cb)(*tmp.first, tmp.second);} //tmp.first->release();} + m_even_delta.reset(); + if (m_uneven_delta.size) m_e.notify(sc_core::SC_ZERO_TIME); + } + if (!m_ppq.get_size()) return; //there were only delta notification + + //timed notifications + const sc_core::sc_time now=sc_core::sc_time_stamp(); + sc_core::sc_time top=m_ppq.top_time(); + + while(m_ppq.get_size() && top==now) { // push all active ones into target + PAYLOAD& tmp=m_ppq.top(); + (m_owner->*m_cb)(*tmp.first, tmp.second); //tmp.first->release();} + m_ppq.delete_top(); + top=m_ppq.top_time(); + } + if ( m_ppq.get_size()) { + m_e.notify( top - now) ; + } + + } + + OWNER* m_owner; + cb m_cb; + + time_ordered_list<PAYLOAD> m_ppq; + delta_list m_uneven_delta; + delta_list m_even_delta; + delta_list m_immediate_yield; + + sc_core::sc_event m_e; // default event +}; + +} + +#endif // __PEQ_WITH_CB_AND_PHASE_H__ diff --git a/ext/systemc/src/tlm_utils/peq_with_get.h b/ext/systemc/src/tlm_utils/peq_with_get.h new file mode 100644 index 000000000..5c85f25d5 --- /dev/null +++ b/ext/systemc/src/tlm_utils/peq_with_get.h @@ -0,0 +1,94 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +// 12-Jan-2009 John Aynsley Bug fix. sc_time argument to notify should be const +// 20-Mar-2009 John Aynsley Add cancel_all() method + + +#ifndef __PEQ_WITH_GET_H__ +#define __PEQ_WITH_GET_H__ + +#include <systemc> +//#include <tlm> +#include <map> + +namespace tlm_utils { + +template <class PAYLOAD> +class peq_with_get : public sc_core::sc_object +{ +public: + typedef PAYLOAD transaction_type; + typedef std::pair<const sc_core::sc_time, transaction_type*> pair_type; + +public: + peq_with_get(const char* name) : sc_core::sc_object(name) + { + } + + void notify(transaction_type& trans, const sc_core::sc_time& t) + { + m_scheduled_events.insert(pair_type(t + sc_core::sc_time_stamp(), &trans)); + m_event.notify(t); + } + + void notify(transaction_type& trans) + { + m_scheduled_events.insert(pair_type(sc_core::sc_time_stamp(), &trans)); + m_event.notify(); // immediate notification + } + + // needs to be called until it returns 0 + transaction_type* get_next_transaction() + { + if (m_scheduled_events.empty()) { + return 0; + } + + sc_core::sc_time now = sc_core::sc_time_stamp(); + if (m_scheduled_events.begin()->first <= now) { + transaction_type* trans = m_scheduled_events.begin()->second; + m_scheduled_events.erase(m_scheduled_events.begin()); + return trans; + } + + m_event.notify(m_scheduled_events.begin()->first - now); + + return 0; + } + + sc_core::sc_event& get_event() + { + return m_event; + } + + // Cancel all events from the event queue + void cancel_all() { + m_scheduled_events.clear(); + m_event.cancel(); + } + +private: + std::multimap<const sc_core::sc_time, transaction_type*> m_scheduled_events; + sc_core::sc_event m_event; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/simple_initiator_socket.h b/ext/systemc/src/tlm_utils/simple_initiator_socket.h new file mode 100644 index 000000000..4609fd885 --- /dev/null +++ b/ext/systemc/src/tlm_utils/simple_initiator_socket.h @@ -0,0 +1,292 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +#ifndef __SIMPLE_INITIATOR_SOCKET_H__ +#define __SIMPLE_INITIATOR_SOCKET_H__ + +#include <tlm> +#include <sstream> + +namespace tlm_utils { + +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class simple_initiator_socket : + public tlm::tlm_initiator_socket<BUSWIDTH, TYPES> +{ +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_initiator_socket<BUSWIDTH, TYPES> base_type; + +public: + simple_initiator_socket() : + base_type(sc_core::sc_gen_unique_name("simple_initiator_socket")), + m_process(this->name()) + { + this->m_export.bind(m_process); + } + + explicit simple_initiator_socket(const char* n) : + base_type(n), + m_process(this->name()) + { + this->m_export.bind(m_process); + } + + void register_nb_transport_bw(MODULE* mod, + sync_enum_type (MODULE::*cb)(transaction_type&, + phase_type&, + sc_core::sc_time&)) + { + m_process.set_transport_ptr(mod, cb); + } + + void register_invalidate_direct_mem_ptr(MODULE* mod, + void (MODULE::*cb)(sc_dt::uint64, sc_dt::uint64)) + { + m_process.set_invalidate_direct_mem_ptr(mod, cb); + } + +private: + class process : public tlm::tlm_bw_transport_if<TYPES> + { + public: + typedef sync_enum_type (MODULE::*TransportPtr)(transaction_type&, + phase_type&, + sc_core::sc_time&); + typedef void (MODULE::*InvalidateDirectMemPtr)(sc_dt::uint64, + sc_dt::uint64); + + process(const std::string& name) : + m_name(name), + m_mod(0), + m_transport_ptr(0), + m_invalidate_direct_mem_ptr(0) + { + } + + void set_transport_ptr(MODULE* mod, TransportPtr p) + { + if (m_transport_ptr) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_transport_ptr = p; + } + } + + void set_invalidate_direct_mem_ptr(MODULE* mod, InvalidateDirectMemPtr p) + { + if (m_invalidate_direct_mem_ptr) { + std::stringstream s; + s << m_name << ": invalidate DMI callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_invalidate_direct_mem_ptr = p; + } + } + + sync_enum_type nb_transport_bw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) + { + if (m_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_ptr)(trans, phase, t); + + } else { + std::stringstream s; + s << m_name << ": no transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, + sc_dt::uint64 end_range) + { + if (m_invalidate_direct_mem_ptr) { + // forward call + assert(m_mod); + (m_mod->*m_invalidate_direct_mem_ptr)(start_range, end_range); + } + } + + private: + const std::string m_name; + MODULE* m_mod; + TransportPtr m_transport_ptr; + InvalidateDirectMemPtr m_invalidate_direct_mem_ptr; + }; + +private: + process m_process; +}; + +// Tagged version + +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class simple_initiator_socket_tagged : + public tlm::tlm_initiator_socket<BUSWIDTH, TYPES> +{ +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_initiator_socket<BUSWIDTH, TYPES> base_type; + +public: + simple_initiator_socket_tagged() : + base_type(sc_core::sc_gen_unique_name("simple_initiator_socket_tagged")), + m_process(this->name()) + { + this->m_export.bind(m_process); + } + + explicit simple_initiator_socket_tagged(const char* n) : + base_type(n), + m_process(this->name()) + { + this->m_export.bind(m_process); + } + + void register_nb_transport_bw(MODULE* mod, + sync_enum_type (MODULE::*cb)(int, + transaction_type&, + phase_type&, + sc_core::sc_time&), + int id) + { + m_process.set_transport_ptr(mod, cb); + m_process.set_transport_user_id(id); + } + + void register_invalidate_direct_mem_ptr(MODULE* mod, + void (MODULE::*cb)(int, sc_dt::uint64, sc_dt::uint64), + int id) + { + m_process.set_invalidate_direct_mem_ptr(mod, cb); + m_process.set_invalidate_dmi_user_id(id); + } + +private: + class process : public tlm::tlm_bw_transport_if<TYPES> + { + public: + typedef sync_enum_type (MODULE::*TransportPtr)(int, + transaction_type&, + phase_type&, + sc_core::sc_time&); + typedef void (MODULE::*InvalidateDirectMemPtr)(int, + sc_dt::uint64, + sc_dt::uint64); + + process(const std::string& name) : + m_name(name), + m_mod(0), + m_transport_ptr(0), + m_invalidate_direct_mem_ptr(0), + m_transport_user_id(0), + m_invalidate_direct_mem_user_id(0) + { + } + + void set_transport_user_id(int id) { m_transport_user_id = id; } + void set_invalidate_dmi_user_id(int id) { m_invalidate_direct_mem_user_id = id; } + + void set_transport_ptr(MODULE* mod, TransportPtr p) + { + if (m_transport_ptr) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_transport_ptr = p; + } + } + + void set_invalidate_direct_mem_ptr(MODULE* mod, InvalidateDirectMemPtr p) + { + if (m_invalidate_direct_mem_ptr) { + std::stringstream s; + s << m_name << ": invalidate DMI callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + assert(!m_mod || m_mod == mod); + m_mod = mod; + m_invalidate_direct_mem_ptr = p; + } + } + + sync_enum_type nb_transport_bw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) + { + if (m_transport_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_ptr)(m_transport_user_id, trans, phase, t); + + } else { + std::stringstream s; + s << m_name << ": no transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, + sc_dt::uint64 end_range) + { + if (m_invalidate_direct_mem_ptr) { + // forward call + assert(m_mod); + (m_mod->*m_invalidate_direct_mem_ptr)(m_invalidate_direct_mem_user_id, start_range, end_range); + } + } + + private: + const std::string m_name; + MODULE* m_mod; + TransportPtr m_transport_ptr; + InvalidateDirectMemPtr m_invalidate_direct_mem_ptr; + int m_transport_user_id; + int m_invalidate_direct_mem_user_id; + }; + +private: + process m_process; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/simple_target_socket.h b/ext/systemc/src/tlm_utils/simple_target_socket.h new file mode 100644 index 000000000..34ed1a7f4 --- /dev/null +++ b/ext/systemc/src/tlm_utils/simple_target_socket.h @@ -0,0 +1,1114 @@ +/***************************************************************************** + + 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 __SIMPLE_TARGET_SOCKET_H__ +#define __SIMPLE_TARGET_SOCKET_H__ + +#ifndef SC_INCLUDE_DYNAMIC_PROCESSES // needed for sc_spawn +# define SC_INCLUDE_DYNAMIC_PROCESSES +#endif + +#include <systemc> +#include <tlm> +#include "tlm_utils/peq_with_get.h" +#include <sstream> + +namespace tlm_utils { + +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class simple_target_socket : + public tlm::tlm_target_socket<BUSWIDTH, TYPES> +{ + 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> base_type; + +public: + simple_target_socket() : + base_type(sc_core::sc_gen_unique_name("simple_target_socket")), + m_fw_process(this), + m_bw_process(this) + { + bind(m_fw_process); + } + + explicit simple_target_socket(const char* n) : + base_type(n), + m_fw_process(this), + m_bw_process(this) + { + bind(m_fw_process); + } + + using tlm::tlm_target_socket<BUSWIDTH, TYPES>::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&)) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + m_fw_process.set_nb_transport_ptr(mod, cb); + } + + void register_b_transport(MODULE* mod, + void (MODULE::*cb)(transaction_type&, + sc_core::sc_time&)) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + m_fw_process.set_b_transport_ptr(mod, cb); + } + + void register_transport_dbg(MODULE* mod, + unsigned int (MODULE::*cb)(transaction_type&)) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + 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&)) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + m_fw_process.set_get_direct_mem_ptr(mod, cb); + } + +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 *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; + + 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); + + } else { + if (phase == tlm::END_REQ) { + m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); + return tlm::TLM_ACCEPTED; + + } else 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; + + } else { + assert(0); exit(1); + } + +// return tlm::TLM_COMPLETED; //Should not reach here + } + } + + 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 *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 *p_own) : + m_name(p_own->name()), + 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) + { + sc_core::sc_spawn_options opts; + opts.set_sensitivity(&m_peq.get_event()); + 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) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": debug callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": get DMI pointer callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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 + assert(m_mod); + return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + + } else 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; + + } else if (phase == tlm::END_RESP) { + m_response_in_progress = false; + m_end_response.notify(t); + return tlm::TLM_COMPLETED; + + } else { + assert(0); exit(1); +// return tlm::TLM_COMPLETED; ///< unreachable code + } + + } else { + std::stringstream s; + s << m_name << ": no non-blocking transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void b_transport(transaction_type& trans, sc_core::sc_time& t) + { + if (m_b_transport_ptr) { + // forward call + assert(m_mod); + (m_mod->*m_b_transport_ptr)(trans, t); + return; + + } else 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); + } + + } else { + std::stringstream s; + s << m_name << ": no blocking transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + } + + unsigned int transport_dbg(transaction_type& trans) + { + if (m_transport_dbg_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_dbg_ptr)(trans); + + } else { + // 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 + assert(m_mod); + return (m_mod->*m_get_direct_mem_ptr)(trans, dmi_data); + + } else { + // 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 + 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) { + sc_core::wait(m_peq.get_event()); + + transaction_type* trans; + while ((trans = m_peq.get_next_transaction())!=0) { + assert(m_mod); + 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); + 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); + assert(it != m_owner->m_pending_trans.end()); + it->second->notify(t); + m_owner->m_pending_trans.erase(it); + break; + } + + default: + assert(0); exit(1); + }; + break; + + default: + assert(0); exit(1); + }; + } + } + } + + void free(tlm::tlm_generic_payload* trans) + { + mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>(); + 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: + const std::string m_name; + simple_target_socket *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: + 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; +}; + +//ID Tagged version +template <typename MODULE, + unsigned int BUSWIDTH = 32, + typename TYPES = tlm::tlm_base_protocol_types> +class simple_target_socket_tagged : + public tlm::tlm_target_socket<BUSWIDTH, TYPES> +{ + 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> base_type; + +public: + simple_target_socket_tagged() : + base_type(sc_core::sc_gen_unique_name("simple_target_socket_tagged")), + m_fw_process(this), + m_bw_process(this) + { + bind(m_fw_process); + } + + explicit simple_target_socket_tagged(const char* n) : + base_type(n), + m_fw_process(this), + m_bw_process(this) + { + bind(m_fw_process); + } + + using tlm::tlm_target_socket<BUSWIDTH, TYPES>::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) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + 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) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + 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) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + 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) + { + assert(!sc_core::sc_get_curr_simcontext()->elaboration_done()); + m_fw_process.set_get_direct_mem_ptr(mod, cb); + m_fw_process.set_get_dmi_user_id(id); + } + +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 *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; + + 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); + + } else { + if (phase == tlm::END_REQ) { + m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME); + return tlm::TLM_ACCEPTED; + + } else 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; + + } else { + assert(0); exit(1); + } + +// return tlm::TLM_COMPLETED; //Should not reach here + } + } + + 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 *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 *p_own) : + m_name(p_own->name()), + 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) + { + sc_core::sc_spawn_options opts; + opts.set_sensitivity(&m_peq.get_event()); + 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) { + std::stringstream s; + s << m_name << ": non-blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": blocking callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": debug callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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) { + std::stringstream s; + s << m_name << ": get DMI pointer callback allready registered"; + SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } else { + 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 + assert(m_mod); + return (m_mod->*m_nb_transport_ptr)(m_nb_transport_user_id, trans, phase, t); + + } else 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; + + } else if (phase == tlm::END_RESP) { + m_response_in_progress = false; + m_end_response.notify(t); + return tlm::TLM_COMPLETED; + + } else { + assert(0); exit(1); +// return tlm::TLM_COMPLETED; ///< unreachable code + } + + } else { + std::stringstream s; + s << m_name << ": no non-blocking transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + return tlm::TLM_ACCEPTED; ///< unreachable code + } + + void b_transport(transaction_type& trans, sc_core::sc_time& t) + { + if (m_b_transport_ptr) { + // forward call + assert(m_mod); + (m_mod->*m_b_transport_ptr)(m_b_transport_user_id, trans, t); + return; + + } else 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); + } + + } else { + std::stringstream s; + s << m_name << ": no transport callback registered"; + SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket",s.str().c_str()); + } + } + + unsigned int transport_dbg(transaction_type& trans) + { + if (m_transport_dbg_ptr) { + // forward call + assert(m_mod); + return (m_mod->*m_transport_dbg_ptr)(m_transport_dbg_user_id, trans); + + } else { + // 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 + assert(m_mod); + return (m_mod->*m_get_direct_mem_ptr)(m_get_dmi_user_id, trans, dmi_data); + + } else { + // 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 + 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) { + sc_core::wait(m_peq.get_event()); + + transaction_type* trans; + while ((trans = m_peq.get_next_transaction())!=0) { + assert(m_mod); + 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); + 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); + assert(it != m_owner->m_pending_trans.end()); + it->second->notify(t); + m_owner->m_pending_trans.erase(it); + break; + } + + default: + assert(0); exit(1); + }; + break; + + default: + assert(0); exit(1); + }; + } + } + } + + void free(tlm::tlm_generic_payload* trans) + { + mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>(); + 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: + const std::string m_name; + simple_target_socket_tagged *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: + 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; +}; + +} + +#endif diff --git a/ext/systemc/src/tlm_utils/tlm2_base_protocol_checker.h b/ext/systemc/src/tlm_utils/tlm2_base_protocol_checker.h new file mode 100755 index 000000000..3dbca9e49 --- /dev/null +++ b/ext/systemc/src/tlm_utils/tlm2_base_protocol_checker.h @@ -0,0 +1,1055 @@ + +// Filename: tlm2_base_protocol_checker.h + +//---------------------------------------------------------------------- +// Copyright (c) 2008-2013 by Doulos Ltd. +// +// Licensed 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. +//---------------------------------------------------------------------- + +// Author: John Aynsley, Doulos + +// Version 1, 11 July 2008 +// Version 2, 16 July 2008 Only generate ref_count > 1 warning from 1st checker of path +// Version 3, 17 July 2008 Support compilation under SystemC 2.1.v1 +// Version 4, 12 Aug 2008 Add header #include <map> +// Version 5, 08 Sep 2008 Fix bugs in message text +// Version 6, 01 Aug 2010 Update messages to refer to OSCI TLM-2.0 LRM of July 2009 +// Version 7, 25 Oct 2011 Minor bug fix for certain compilers: replace u_char with uchar_t +// Version 8, 02 Nov 2011 Support the endianness conversion functions by excluding the +// tlm_endian_context extension from the protocol checks +// Version 9, 17 Aug 2012 Fix LRM reference on line 805 (should be 8.2.11 a) [NOT YET RELEASED] +// Version 10, 3 Jan 2013 Updated messages to refer to IEEE Std 1666-2011, the combined SystemC + TLM-2.0 LRM +// Added checks related to the generic payload option attribute +// Version 11, 14 Mar 2016 Fix minor bug - start_phase should be a copy, not a reference + +// TLM-2.0 Base Protocol Compliance Checker + +/* +Instantiate this checker module in-line between initiator and target, initiator and interconnect, +or interconnect and target by binding the target_socket and initiator_socket +Binding two checkers either side of an interconnect component, or interleaving a series of +checkers with interconnect components, will enable some deeper checks as against having just +a single checker + +For example + + Initiator *initiator; + Bus *bus; + Memory *memory; + ... + initiator->socket.bind(bus->target_socket); + bus->initiator_socket.bind(memory->socket); + +might become + + tlm_utils::tlm2_base_protocol_checker<32> *checker1; + tlm_utils::tlm2_base_protocol_checker<32> *checker2; + ... + initiator->socket.bind(checker1->target_socket); + checker1->initiator_socket.bind(bus->target_socket); + bus->initiator_socket.bind(checker2->target_socket); + checker2->initiator_socket.bind(memory->socket); + + +GENERAL FEATURES OF THE BASE PROTOCOL CHECKER + +The checks are relatively expensive, hence by default the number of checks is limited. +A maximum number can be set explicitly by calling set_num_checks(max) +Checking can be deactivated at any time by calling set_num_checks(0) +All checkers decrement a single global count, because having some checkers running and +others not can cause bogus violation reports +It is not permitted to turn checks on by calling set_num_checks() once checking has been +deactivated, because this could cause bogus violation reports + +The DMI and debug checks are unaffected by the num_checks count (because they are cheap) + +The error messages contain User Manual references + +The checker is designed to be used with a transaction pool: otherwise it could consume +a lot of memory. The checker keeps a local copy of each transaction object +Failures are reported with a severity of SC_ERROR. The actions may be overridden by calling: + sc_report_handler::set_actions("tlm2_protocol_checker", ...); + +SPECIFIC CHECKS + +nb_transport: phase sequence BEGIN_REQ -> END_REQ -> BEGIN_RESP -> END_RESP +Must not have two outstanding requests or responses (exclusion rules) +Must not have decreasing timing annotations on calls to or returns from nb_transport_fw/bw +Phase extensions permitted and ignored +Must not call b_transport during nb_transport phase sequence and vice-versa + +nb_transport: memory manager must be set +nb_transport: reference count must be non-zero +First checker in BEGIN_REQ path should see ref_count == 1 (warning only) +An interconnect component that sets a memory manager should also clear it +An interconnect component that sets extensions with no memory manager should also clear them +(Does not bother with these memory manager checks for DMI and debug) + +Transaction object must be properly initialized +Many generic payload attributes must not be modified during the transaction lifetime +Transaction object must not be re-allocated for a new transaction while still in use +DMI descriptor must be properly initialized +Debug transaction must be properly initialized +Debug byte count must be less than data_length + +Checks that require multiple checkers to be instantiated along a transaction path: +The BEGIN_RESP path must be the reverse of the BEGIN_REQ path +Transaction object must not be sent with BEGIN_REQ while participating in a previous response +Interconnect component must not set response status attribute to TLM_OK_RESPONSE +Interconnect component must not modify data array on the response path + +Generic payload option attribute (IEEE Std 1666-2011, SystemC 2.3.x) +gp_option must be properly initialized and only used for DMI and debug transport +When gp_option is used, other gp attributes must be initalized and used as per the transport interfaces +*/ + + +// ******************** PREAMBLE ******************** + + +#ifndef __tlm2_base_protocol_checker__ +#define __tlm2_base_protocol_checker__ + +#include "systemc" +using std::cout; +using std::endl; +using std::dec; +using std::hex; + +#include "tlm.h" +#include <sstream> +#include <map> + + +namespace tlm_utils { + + +// Number of checks remaining +const sc_dt::uint64 default_num_checks = 100000; +static sc_dt::uint64 num_checks = default_num_checks; + + +// Types used when building a trace of the transaction path +typedef unsigned char uchar_t; +typedef std::deque<sc_core::sc_module*> deque_t; + +struct path_t { + path_t () { response_in_progress = false; ok_response = false; resp_data_ptr = 0; } + + bool response_in_progress; + bool ok_response; + deque_t path; + uchar_t* resp_data_ptr; // Copy of data on response path +}; + +// Global variable used for checks involving multiple checkers along a transaction path +static std::map<tlm::tlm_generic_payload*, path_t> shared_map; + + +// ******************** CLASS DEFINITION ******************** + + +template <unsigned int BUSWIDTH = 32> +class tlm2_base_protocol_checker + +: public sc_core::sc_module +, public tlm::tlm_fw_transport_if<tlm::tlm_base_protocol_types> +, public tlm::tlm_bw_transport_if<tlm::tlm_base_protocol_types> +{ +public: + + // Instantiate and bind checker inline between an existing pair of initiator and target sockets + + tlm::tlm_target_socket <BUSWIDTH, tlm::tlm_base_protocol_types, 1> target_socket; + tlm::tlm_initiator_socket<BUSWIDTH, tlm::tlm_base_protocol_types, 1> initiator_socket; + + SC_CTOR(tlm2_base_protocol_checker) + : m_request_in_progress(0), m_response_in_progress(0) + { + target_socket .bind( *this ); + initiator_socket.bind( *this ); + } + + + // Access methods for num_checks count + + static void set_num_checks(sc_dt::uint64 n) { + if (num_checks == 0) + SC_REPORT_FATAL("tlm2_protocol_checker", "Method set_num_checks called after checking has stopped due to maximum number of checks being reached"); + num_checks = n; + } + + static sc_dt::uint64 get_num_checks() { return num_checks; } + + + // TLM-2.0 interface methods for initiator and target sockets, instrumented with checks + + virtual tlm::tlm_sync_enum nb_transport_fw( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) + { + tlm::tlm_phase start_phase = phase; + + if (num_checks) + nb_transport_fw_pre_checks( trans, phase, delay ); + + tlm::tlm_sync_enum status; + status = initiator_socket->nb_transport_fw( trans, phase, delay ); + + if (num_checks) + nb_transport_fw_post_checks( trans, start_phase, phase, delay, status ); + + return status; + } + + virtual tlm::tlm_sync_enum nb_transport_bw( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) + { + if (num_checks) + nb_transport_bw_pre_checks( trans, phase, delay ); + + tlm::tlm_sync_enum status; + status = target_socket->nb_transport_bw( trans, phase, delay ); + + if (num_checks) + nb_transport_bw_post_checks( trans, phase, delay, status ); + + return status; + } + + virtual void b_transport( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay ) + { + if (num_checks) + b_transport_pre_checks( trans, delay ); + + initiator_socket->b_transport( trans, delay ); + + if (num_checks) + b_transport_post_checks( trans, delay ); + } + + virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans, + tlm::tlm_dmi& dmi_data) + { + get_direct_mem_ptr_pre_checks( trans, dmi_data ); + + bool status; + status = initiator_socket->get_direct_mem_ptr( trans, dmi_data ); + + get_direct_mem_ptr_post_checks( trans, dmi_data ); + return status; + } + + virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, + sc_dt::uint64 end_range) + { + target_socket->invalidate_direct_mem_ptr(start_range, end_range); + } + + virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans) + { + transport_dbg_pre_checks( trans ); + + unsigned int count; + count = initiator_socket->transport_dbg( trans ); + + transport_dbg_post_checks( trans, count ); + return count; + } + + +private: + void b_transport_pre_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay); + + void b_transport_post_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay); + + void nb_transport_fw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay); + + void nb_transport_fw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase, + sc_core::sc_time& delay, tlm::tlm_sync_enum status); + + void nb_transport_bw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay); + + void nb_transport_bw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + tlm::tlm_sync_enum status); + + void nb_transport_response_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + const char* txt2, const char* txt3, const char* txt4); + + void check_initial_state( tlm::tlm_generic_payload& trans, const char* txt2 ); + void check_trans_not_modified( tlm::tlm_generic_payload& trans, const char* txt2 ); + void check_response_path( tlm::tlm_generic_payload& trans, const char* txt2 ); + void remember_gp_option( tlm::tlm_generic_payload& trans ); + + void get_direct_mem_ptr_pre_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ); + + void get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ); + + void transport_dbg_pre_checks( tlm::tlm_generic_payload& trans ); + + void transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count ); + + void tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning = false ); + +private: + + struct state_t { + state_t() { b_call = 0; ph = tlm::UNINITIALIZED_PHASE; gp = 0; } + + bool has_mm; + unsigned int b_call; // Number of b_transport calls in progress + tlm::tlm_phase ph; + sc_core::sc_time time; // Current time + annotated delay + tlm::tlm_generic_payload* gp; // Points to new data and byte enable buffers + uchar_t* data_ptr; // Stores original pointers + uchar_t* byte_enable_ptr; + }; + + // Transaction state for the specific hop where this checker is inlined + std::map<tlm::tlm_generic_payload*, state_t> m_map; + + // Flags for exclusion rules + tlm::tlm_generic_payload* m_request_in_progress; + tlm::tlm_generic_payload* m_response_in_progress; + + std::ostringstream txt; + +}; + + + +// ******************** MEMBER FUNCTION DEFINITIONS ******************** + + +#define BOILERPLATE \ +template <unsigned int BUSWIDTH> \ +void tlm2_base_protocol_checker<BUSWIDTH>:: + + +BOILERPLATE +b_transport_pre_checks( + tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) +{ + ++ m_map[&trans].b_call; + + if ( trans.has_mm() && trans.get_ref_count() == 0) + { + txt << "Transaction passed to b_transport with memory manager and reference count of 0"; + tlm2error(trans, "14.5 t)"); + } + check_initial_state(trans, "b_transport"); + +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + if (sc_core::sc_get_current_process_handle().proc_kind() == sc_core::SC_METHOD_PROC_) + { + txt << "b_transport called from method process"; + tlm2error(trans, "11.1.1.4 b)"); + } +#endif + + if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4) + { + txt << "b_transport called during a sequence of nb_transport calls"; + tlm2error(trans, "15.2.10 c)"); + } +} + + +BOILERPLATE +b_transport_post_checks( + tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) +{ + check_response_path(trans, "b_transport"); + check_trans_not_modified(trans, "b_transport"); + -- m_map[&trans].b_call; +} + + +BOILERPLATE +nb_transport_fw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) +{ + if ( !trans.has_mm() ) + { + txt << "Transaction passed to nb_transport_fw with no memory manager set"; + tlm2error(trans, "14.5 i)"); + } + if ( trans.get_ref_count() == 0) + { + txt << "Transaction passed to nb_transport_fw with reference count of 0"; + tlm2error(trans, "14.5 t)"); + } + + switch (phase) + { + case tlm::BEGIN_REQ: + check_initial_state(trans, "nb_transport_fw"); + + if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4) // END_RESP -> BEGIN_REQ is legal + { + txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw"; + tlm2error(trans, "15.2.4"); + } + + if (m_request_in_progress) + { + txt << "Transaction violates BEGIN_REQ exclusion rule, detected in nb_transport_fw"; + tlm2error(trans, "15.2.6 e)"); + } + m_request_in_progress = &trans; + + if (m_map[&trans].b_call) + { + txt << "nb_transport_fw called during a b_transport call"; + tlm2error(trans, "15.2.10 c)"); + } + break; + + case tlm::END_REQ: + case tlm::BEGIN_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on forward path, detected in nb_transport_fw"; + tlm2error(trans, " 15.2.3 c)"); + break; + + case tlm::END_RESP: + if (m_map[&trans].ph != tlm::BEGIN_RESP) + { + txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw"; + tlm2error(trans, "15.2.4"); + } + m_response_in_progress = 0; + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << "nb_transport_fw called with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "15.2.7 c)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; +} + + +BOILERPLATE +nb_transport_fw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase, + sc_core::sc_time& delay, tlm::tlm_sync_enum status) +{ + if (status == tlm::TLM_UPDATED) + { + nb_transport_response_checks( + trans, phase, delay, "(forward) return", "Return from nb_transport_fw", "nb_transport_fw"); + } + else if (status == tlm::TLM_COMPLETED) + { + if (start_phase == tlm::BEGIN_REQ) + check_response_path(trans, "nb_transport_fw"); + m_request_in_progress = 0; + m_map[&trans].ph = tlm::UNINITIALIZED_PHASE; + } + + // Transaction object should not be re-allocated, even during the END_RESP phase + //if (phase != tlm::END_RESP) + { + std::ostringstream txt; + txt << "nb_transport_fw, phase = " << phase; + check_trans_not_modified(trans, txt.str().c_str()); + } +} + + +BOILERPLATE +nb_transport_bw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) +{ + if ( !trans.has_mm() ) + { + txt << "Transaction passed to nb_transport_bw with no memory manager set"; + tlm2error(trans, "14.5 i)"); + } + if ( trans.get_ref_count() == 0) + { + txt << "Transaction passed to nb_transport_bw with reference count of 0"; + tlm2error(trans, "14.5 t)"); + } + nb_transport_response_checks( + trans, phase, delay, "backward", "nb_transport_bw called", "nb_transport_bw"); +} + + +BOILERPLATE +nb_transport_bw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + tlm::tlm_sync_enum status) +{ + if (status == tlm::TLM_UPDATED) + { + switch (phase) + { + case tlm::BEGIN_REQ: + txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "15.2.4"); + break; + + case tlm::END_REQ: + case tlm::BEGIN_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "15.2.3 c)"); + break; + + case tlm::END_RESP: + if (m_map[&trans].ph != tlm::BEGIN_RESP) + { + txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "15.2.4"); + } + + m_response_in_progress = 0; + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << "Return from nb_transport_bw with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "15.2.7 c)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; + } + else if (status == tlm::TLM_COMPLETED) + { + m_response_in_progress = 0; + m_map[&trans].ph = tlm::UNINITIALIZED_PHASE; + } + + // Transaction object should not be re-allocated, even during the END_RESP phase + //if (phase != tlm::END_RESP) + { + std::ostringstream txt; + txt << "nb_transport_bw, phase = " << phase; + check_trans_not_modified(trans, txt.str().c_str()); + } +} + + +BOILERPLATE +nb_transport_response_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + const char* txt2, const char* txt3, const char* txt4) +{ + if (trans.is_response_ok()) + if (shared_map[&trans].response_in_progress && !shared_map[&trans].ok_response) + { + txt << "Interconnect component sets response status attribute to TLM_OK_RESPONSE" + << ", detected in " << txt4; + tlm2error(trans, "14.7"); + + } + + switch (phase) + { + case tlm::BEGIN_REQ: + case tlm::END_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "15.2.3 c)"); + break; + + case tlm::END_REQ: + if (m_map[&trans].ph != tlm::BEGIN_REQ) + { + txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "15.2.4"); + } + + m_request_in_progress = 0; + break; + + case tlm::BEGIN_RESP: + if (m_map[&trans].ph != tlm::BEGIN_REQ && m_map[&trans].ph != tlm::END_REQ) + { + txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "15.2.4"); + } + + if (&trans == m_request_in_progress) + m_request_in_progress = 0; + + if (m_response_in_progress) + { + txt << "Transaction violates BEGIN_RESP exclusion rule" + << ", detected in " << txt4; + tlm2error(trans, "15.2.6 f)"); + } + m_response_in_progress = &trans; + + check_response_path(trans, txt4); + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << txt3 << " with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "15.2.7 c)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; +} + + +BOILERPLATE +check_initial_state( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + if (num_checks > 0) + { + --num_checks; + if (num_checks == 0) + SC_REPORT_INFO("tlm2_protocol_checker", "Checkers deactivated after executing the set number of checks"); + } + + if ( trans.has_mm() && trans.get_ref_count() > 1 && shared_map[&trans].path.empty() ) + { + txt << "New transaction passed to " << txt2 << " with reference count = " + << trans.get_ref_count(); + tlm2error(trans, "14.5 t)", true); + } + if (trans.get_data_ptr() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND) + { + txt << "Transaction not properly initialized: data_ptr == 0, detected in " << txt2; + tlm2error(trans, "14.11 e)"); + } + if (trans.get_data_length() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND) + { + txt << "Transaction not properly initialized: data_langth == 0, detected in " << txt2; + tlm2error(trans, "14.12 d)"); + } + if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0) + { + txt << "Transaction not properly initialized: " + << "byte_enable_ptr != 0 and byte_enable_length == 0, detected in " << txt2; + tlm2error(trans, "14.14 f)"); + } + if (trans.get_streaming_width() == 0) + { + txt << "Transaction not properly initialized: streaming_width == 0, detected in " << txt2; + tlm2error(trans, "14.15 f)"); + } + if (trans.is_dmi_allowed()) + { + txt << "Transaction not properly initialized: dmi_allowed == true, detected in " << txt2; + tlm2error(trans, "14.16"); + } + if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE) + { + txt << "Transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE, detected in " << txt2; + tlm2error(trans, "14.17 e)"); + } + if (trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD) + { + txt << "Transaction not properly initialized: gp_option != TLM_MIN_PAYLOAD, detected in " << txt2; + tlm2error(trans, "14.8 g)"); + } + + // Setup clones of transaction and buffers in map + tlm::tlm_generic_payload* gp = m_map[&trans].gp; + if (gp == 0) + gp = new tlm::tlm_generic_payload; // Memory leak: transactions are never cleared from map + else + { + delete [] gp->get_data_ptr(); + gp->free_all_extensions(); + } + gp->set_data_ptr( new uchar_t[trans.get_data_length()] ); + m_map[&trans].data_ptr = trans.get_data_ptr(); + + if (gp->get_byte_enable_ptr()) + delete [] gp->get_byte_enable_ptr(); + if (trans.get_byte_enable_ptr()) + gp->set_byte_enable_ptr( new uchar_t[trans.get_byte_enable_length()] ); + else + gp->set_byte_enable_ptr(0); + m_map[&trans].byte_enable_ptr = trans.get_byte_enable_ptr(); + + gp->deep_copy_from(trans); + m_map[&trans].gp = gp; + m_map[&trans].time = sc_core::SC_ZERO_TIME; + m_map[&trans].has_mm = trans.has_mm(); + + // Store request path checker sequence + if (shared_map[&trans].resp_data_ptr) + { + delete [] shared_map[&trans].resp_data_ptr; + shared_map[&trans].resp_data_ptr = 0; + } + if (shared_map[&trans].response_in_progress) + { + txt << "Transaction object sent with BEGIN_REQ while still being used on a previous response path, detected in " << txt2; + tlm2error(trans, "14.5 x)"); + } + shared_map[&trans].ok_response = false; + shared_map[&trans].path.push_back(this); +} + + +BOILERPLATE +remember_gp_option( + tlm::tlm_generic_payload& trans) +{ + // Setup clone of transaction in map in order to check gp_option only + tlm::tlm_generic_payload* gp = m_map[&trans].gp; + if (gp == 0) + gp = new tlm::tlm_generic_payload; // Memory leak: transactions are never cleared from map + gp->set_gp_option( trans.get_gp_option() ); + m_map[&trans].gp = gp; +} + + +BOILERPLATE +check_trans_not_modified( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + tlm::tlm_generic_payload* init = m_map[&trans].gp; + + if (trans.get_command() != init->get_command()) + { + txt << "Command attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_data_ptr() != m_map[&trans].data_ptr) + { + txt << "Data pointer attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_data_length() != init->get_data_length()) + { + txt << "Data length attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) + for (unsigned int i = 0; i < init->get_data_length(); i++) + if (trans.get_data_ptr()[i] != init->get_data_ptr()[i]) + { + txt << "Data array modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_byte_enable_ptr() != m_map[&trans].byte_enable_ptr) + { + txt << "Byte enable pointer attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_byte_enable_length() != init->get_byte_enable_length()) + { + txt << "Byte enable length attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_byte_enable_ptr()) + for (unsigned int i = 0; i < init->get_byte_enable_length(); i++) + if (trans.get_byte_enable_ptr()[i] != init->get_byte_enable_ptr()[i]) + { + txt << "Byte enable array modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (trans.get_streaming_width() != init->get_streaming_width()) + { + txt << "Streaming width attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.7"); + } + if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD) + { + txt << "Generic payload option attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "14.8 g)"); + } + if ( !m_map[&trans].has_mm ) + { + if (trans.has_mm()) + { + txt << "Interconnect component sets a memory manager, but does not clear it on return, detected in " << txt2; + tlm2error(trans, "14.5 aa)"); + } + + for (unsigned int i = 0; i < tlm::max_num_extensions(); i++) + // Exclude tlm_endian_context extension from the check because it is not cloned in m_map + if (i != tlm::tlm_endian_context::ID) + if (trans.get_extension(i)) + if ( !m_map[&trans].gp->get_extension(i) ) + { + txt << "Extension set (index = " << i << ") without also being deleted in the absence of a memory manager, detected in " << txt2; + tlm2error(trans, "14.5 aa)"); + } + } + + uchar_t* resp_data_ptr = shared_map[&trans].resp_data_ptr; + if (resp_data_ptr) + for (unsigned int i = 0; i < trans.get_data_length(); i++) + if (trans.get_data_ptr()[i] != resp_data_ptr[i]) + { + txt << "Transaction data array modified in interconnect component on response path, detected in " << txt2; + tlm2error(trans, "14.7"); + } +} + + +BOILERPLATE +check_response_path( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + if ( !shared_map[&trans].path.empty() ) + { + if ( this != shared_map[&trans].path.back() ) + { + txt << "BEGIN_RESP path is not the reverse of the BEGIN_REQ path."; + txt << "\nBEGIN_REQ path includes these checkers: -> "; + deque_t path = shared_map[&trans].path; + for (deque_t::iterator i = path.begin(); i < path.end(); i++) + txt << (*i)->name() << " -> "; + txt << "\nDetected in " << txt2; + tlm2error(trans, "15.2.11 a)"); + } + shared_map[&trans].path.pop_back(); + shared_map[&trans].response_in_progress = !shared_map[&trans].path.empty(); + shared_map[&trans].ok_response = trans.is_response_ok(); + + // Create a copy of the data array for comparison on the response path + if ( !shared_map[&trans].resp_data_ptr ) + { + shared_map[&trans].resp_data_ptr = new uchar_t[trans.get_data_length()]; + memcpy(shared_map[&trans].resp_data_ptr, trans.get_data_ptr(), trans.get_data_length()); + } + } +} + + +BOILERPLATE +get_direct_mem_ptr_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ) +{ + remember_gp_option(trans); + + if (dmi_data.get_dmi_ptr() != 0) + { + txt << "DMI descriptor not properly initialized: dmi_ptr != 0"; + tlm2error(trans, "11.2.5 f)"); + } + if (!dmi_data.is_none_allowed()) + { + txt << "DMI descriptor not properly initialized: granted_access != DMI_ACCESS_NONE"; + tlm2error(trans, "11.2.5 a)"); + } + if (dmi_data.get_start_address() != 0) + { + txt << "DMI descriptor not properly initialized: start_address != 0"; + tlm2error(trans, "11.2.5 u)"); + } + if (dmi_data.get_end_address() != (sc_dt::uint64)(-1)) + { + txt << "DMI descriptor not properly initialized: end_address != 0"; + tlm2error(trans, "11.2.5 u)"); + } + if (dmi_data.get_read_latency() != sc_core::SC_ZERO_TIME) + { + txt << "DMI descriptor not properly initialized: read_latency != SC_ZERO_TIME"; + tlm2error(trans, "11.2.5 ac)"); + } + if (dmi_data.get_write_latency() != sc_core::SC_ZERO_TIME) + { + txt << "DMI descriptor not properly initialized: write_latency != SC_ZERO_TIME"; + tlm2error(trans, "11.2.5 ac)"); + } + + if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD) + { + /* + if (trans.is_dmi_allowed()) // Would be rather brutal to flag dmi_allowed as an arror for a DMI transaction! + { + txt << "DMI transaction not properly initialized: dmi_allowed == true"; + tlm2error(trans, "14.8 e) & 14.16"); + } + */ + if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE) + { + txt << "DMI transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE"; + tlm2error(trans, "14.8 e) & 14.17 e)"); + } + } + else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED) + { + txt << "DMI transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED"; + tlm2error(trans, "14.8 c) & e) & j)"); + } +} + + +BOILERPLATE +get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ) +{ + tlm::tlm_generic_payload* init = m_map[&trans].gp; + + if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD) + { + txt << "DMI transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime"; + tlm2error(trans, "14.8 h)"); + } + else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD) + { + txt << "DMI transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD"; + tlm2error(trans, "14.8 j)"); + } +} + + +BOILERPLATE +transport_dbg_pre_checks( tlm::tlm_generic_payload& trans ) +{ + remember_gp_option(trans); + + if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0) + { + txt << "Debug transaction has data_ptr == 0"; + tlm2error(trans, "11.3.4 l)"); + } + + if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD) + { + if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0) + { + txt << "Debug transaction not properly initialized: " + << "byte_enable_ptr != 0 and byte_enable_length == 0"; + tlm2error(trans, "14.8 f) & 14.14 f)"); + } + if (trans.get_streaming_width() == 0) + { + txt << "Debug transaction not properly initialized: streaming_width == 0"; + tlm2error(trans, "14.8 f) & 14.15 f)"); + } + if (trans.is_dmi_allowed()) + { + txt << "Debug transaction not properly initialized: dmi_allowed == true"; + tlm2error(trans, "14.8 f) & 14.16"); + } + if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE) + { + txt << "Debug transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE"; + tlm2error(trans, "14.8 f) & 14.17 e)"); + } + } + else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED) + { + txt << "Debug transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED"; + tlm2error(trans, "14.8 c) & f) & l)"); + }} + + +BOILERPLATE +transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count ) +{ + tlm::tlm_generic_payload* init = m_map[&trans].gp; + + if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0) + { + txt << "Debug transaction has data_ptr == 0"; + tlm2error(trans, "11.3.4 l)"); + } + if (count > trans.get_data_length()) + { + txt << "Count returned from transport_dbg is greater than data_length"; + tlm2error(trans, "11.3.4 s)"); + } + + if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD) + { + txt << "Debug transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime"; + tlm2error(trans, "14.8 h)"); + } + else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD) + { + txt << "Debug transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD"; + tlm2error(trans, "14.8 l)"); + }} + + +BOILERPLATE +tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning ) +{ + txt << "\n\nRefer to IEEE Std 1666-2011, clause " << ref; + txt << "\n\nChecker instance: " << this->name(); + txt << "\n\nTransaction details:"; + txt << "\n has_mm = " << dec << trans.has_mm() << " (bool)"; + txt << "\n ref_count = " << dec << trans.get_ref_count() << " (int)"; + txt << "\n\n gp_option = " << + (trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD ? "TLM_MIN_PAYLOAD" + :trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD ? "TLM_FULL_PAYLOAD" + : "TLM_FULL_PAYLOAD_ACCEPTED"); + txt << "\n command = " << + (trans.get_command() == tlm::TLM_READ_COMMAND ? "TLM_READ_COMMAND" + :trans.get_command() == tlm::TLM_WRITE_COMMAND ? "TLM_WRITE_COMMAND" + : "TLM_IGNORE_COMMAND"); + txt << "\n address = " << hex << trans.get_address() << " (hex)"; + txt << "\n data_ptr = " << hex + << reinterpret_cast<int*>(trans.get_data_ptr()) << " (hex)"; + txt << "\n data_length = " << hex << trans.get_data_length() << " (hex)"; + txt << "\n streaming_width = " << hex << trans.get_streaming_width() << " (hex)"; + txt << "\n byte_enable_ptr = " << hex + << reinterpret_cast<int*>(trans.get_byte_enable_ptr()) << " (hex)"; + txt << "\n byte_enable_length = " << hex << trans.get_byte_enable_length() << " (hex)"; + txt << "\n dmi_allowed = " << dec << trans.is_dmi_allowed() << " (bool)"; + txt << "\n response_status = " << trans.get_response_string(); + + bool extensions_present = false; + for (unsigned int i = 0; i < tlm::max_num_extensions(); i++) + { + tlm::tlm_extension_base* ext = trans.get_extension(i); + if (ext) + { + if (!extensions_present) + txt << "\n\n extensions:"; + txt << "\n index = " << i << " type = " << typeid(*ext).name(); + extensions_present = true; + } + } + + txt << "\n\n"; + if (warning) + SC_REPORT_WARNING("tlm2_protocol_checker", txt.str().c_str()); + else + SC_REPORT_ERROR("tlm2_protocol_checker", txt.str().c_str()); +} + + + +} // namespace tlm_utils + +#endif // __tlm2_base_protocol_checker__ diff --git a/ext/systemc/src/tlm_utils/tlm_quantumkeeper.h b/ext/systemc/src/tlm_utils/tlm_quantumkeeper.h new file mode 100644 index 000000000..ed211665f --- /dev/null +++ b/ext/systemc/src/tlm_utils/tlm_quantumkeeper.h @@ -0,0 +1,172 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +// 20-Mar-2009 John Aynsley Add set_and_sync() method + + +#ifndef __TLM_QUANTUMKEEPER_H__ +#define __TLM_QUANTUMKEEPER_H__ + +#include "tlm_core/tlm_2/tlm_quantum/tlm_global_quantum.h" + +namespace tlm_utils { + + // + // tlm_quantumkeeper class + // + // The tlm_quantumkeeper class is used to keep track of the local time in + // an initiator (how much it has run ahead of the SystemC time), to + // synchronize with SystemC time etc. + // + class tlm_quantumkeeper + { + public: + // + // Static setters/getters for the global quantum value. + // + // The global quantum is the maximum time an initiator can run ahead of + // systemC time. All initiators will synchronize on timingpoints that are + // multiples of the global quantum value. + // + static void set_global_quantum(const sc_core::sc_time& t) + { + tlm::tlm_global_quantum::instance().set(t); + } + + static const sc_core::sc_time& get_global_quantum() + { + return tlm::tlm_global_quantum::instance().get(); + } + + public: + tlm_quantumkeeper() : + m_next_sync_point(sc_core::SC_ZERO_TIME), + m_local_time(sc_core::SC_ZERO_TIME) + { + } + + virtual ~tlm_quantumkeeper() {} + + // + // Increment the local time (the time the initiator is ahead of the + // systemC time) After incrementing the local time an initiator should + // check (with the need_sync method) if a sync is required. + // + virtual void inc(const sc_core::sc_time& t) + { + m_local_time += t; + } + + // + // Sets the local time (the time the initiator is ahead of the + // systemC time) After changing the local time an initiator should + // check (with the need_sync method) if a sync is required. + // + virtual void set(const sc_core::sc_time& t) + { + m_local_time = t; + } + + // + // Checks if a sync to systemC is required for this initiator. This will + // be the case if the local time becomes greater than the local (current) + // quantum value for this initiator. + // + virtual bool need_sync() const + { + return sc_core::sc_time_stamp() + m_local_time >= m_next_sync_point; + } + + // + // Synchronize to systemC. This call will do a wait for the time the + // initiator was running ahead of systemC time and reset the + // tlm_quantumkeeper. + // + virtual void sync() + { + sc_core::wait(m_local_time); + reset(); + } + + // + // Non-virtual convenience method to set the local time and sync only if needed + // + void set_and_sync(const sc_core::sc_time& t) + { + set(t); + if (need_sync()) + sync(); + } + + // + // Resets the local time to SC_ZERO_TIME and computes the value of the + // next local quantum. This method should be called by an initiator after + // a wait because of a synchronization request by a target (TLM_ACCEPTED, + // or TLM_UPDATED). + // + virtual void reset() + { + m_local_time = sc_core::SC_ZERO_TIME; + m_next_sync_point = sc_core::sc_time_stamp() + compute_local_quantum(); + } + + // + // Helper function to get the current systemC time, taken the local time + // into account. The current systemC time is calculated as the time + // returned by sc_time_stamp incremeneted with the time the initiator is + // running ahead. + // + virtual sc_core::sc_time get_current_time() const + { + return sc_core::sc_time_stamp() + m_local_time; + } + + // + // Helper functions to get the time the initiator is running ahead of + // systenC (local time). This time should be passed to a target in the + // nb_transport call + // + virtual sc_core::sc_time get_local_time() const + { + return m_local_time; + } + + protected: + // + // Calculate the next local quantum for this initiator. + // + // The method can be overloaded in a derived object if an initiator wants + // to use another local quantum. This derived object should also take the + // global quantum into account. It's local quantum should not be set to a + // value that is larger than the quantum returned by the + // compute_local_quantum of the tlm_global_quantum singleton. + // + virtual sc_core::sc_time compute_local_quantum() + { + return tlm::tlm_global_quantum::instance().compute_local_quantum(); + } + + protected: + sc_core::sc_time m_next_sync_point; + sc_core::sc_time m_local_time; + }; + +} // namespace tlm + +#endif |