| /***************************************************************************** |
| |
| 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. |
| |
| *****************************************************************************/ |
| |
| #include "tlm_core/tlm_2/tlm_generic_payload/tlm_gp.h" |
| #include "sysc/utils/sc_typeindex.h" // sc_typeindex |
| |
| #include <map> |
| #include <cstring> // std::memcpy et.al. |
| |
| using sc_core::sc_type_index; |
| |
| namespace tlm { |
| |
| template class SC_API tlm_array<tlm_extension_base*>; |
| |
| //--------------------------------------------------------------------------- |
| // Classes for the extension mechanism |
| //--------------------------------------------------------------------------- |
| |
| /* anonymous */ namespace { |
| class tlm_extension_registry |
| { |
| typedef unsigned int key_type; |
| typedef std::map<sc_core::sc_type_index, key_type> type_map; |
| public: |
| static tlm_extension_registry& instance() |
| { |
| if (!instance_) // don't cleanup registry |
| instance_ = new tlm_extension_registry(); |
| return *instance_; |
| } |
| |
| unsigned int register_extension(sc_type_index type) |
| { |
| type_map::const_iterator it = ids_.find( type ); |
| |
| if( it == ids_.end() ) { // new extension - generate/store ID |
| type_map::value_type v( type, static_cast<key_type>(ids_.size()) ); |
| ids_.insert( v ); |
| return v.second; |
| } |
| return it->second; |
| } |
| |
| static unsigned int max_num_extensions() |
| { return (instance_) ? instance().ids_.size() : 0; } |
| |
| private: |
| static tlm_extension_registry* instance_; |
| type_map ids_; |
| tlm_extension_registry() /* = default */ {} |
| |
| }; // class tlm_extension_registry |
| |
| tlm_extension_registry* tlm_extension_registry::instance_ = NULL; |
| |
| } // anonymous namespace |
| |
| unsigned int max_num_extensions() |
| { |
| return tlm_extension_registry::max_num_extensions(); |
| } |
| |
| unsigned int |
| tlm_extension_base::register_extension(const std::type_info& type) |
| { |
| return tlm_extension_registry::instance().register_extension(type); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // The generic payload class: |
| //--------------------------------------------------------------------------- |
| |
| //--------------- |
| // Constructors |
| //--------------- |
| |
| // Default constructor |
| tlm_generic_payload::tlm_generic_payload() |
| : m_address(0) |
| , m_command(TLM_IGNORE_COMMAND) |
| , m_data(0) |
| , m_length(0) |
| , m_response_status(TLM_INCOMPLETE_RESPONSE) |
| , m_dmi(false) |
| , m_byte_enable(0) |
| , m_byte_enable_length(0) |
| , m_streaming_width(0) |
| , m_gp_option(TLM_MIN_PAYLOAD) |
| , m_extensions(max_num_extensions()) |
| , m_mm(0) |
| , m_ref_count(0) |
| {} |
| |
| tlm_generic_payload::tlm_generic_payload(tlm_mm_interface* mm) |
| : m_address(0) |
| , m_command(TLM_IGNORE_COMMAND) |
| , m_data(0) |
| , m_length(0) |
| , m_response_status(TLM_INCOMPLETE_RESPONSE) |
| , m_dmi(false) |
| , m_byte_enable(0) |
| , m_byte_enable_length(0) |
| , m_streaming_width(0) |
| , m_gp_option(TLM_MIN_PAYLOAD) |
| , m_extensions(max_num_extensions()) |
| , m_mm(mm) |
| , m_ref_count(0) |
| {} |
| |
| |
| void tlm_generic_payload::reset() { |
| //should the other members be reset too? |
| m_gp_option = TLM_MIN_PAYLOAD; |
| m_extensions.free_entire_cache(); |
| }; |
| |
| |
| // non-virtual deep-copying of the object |
| void |
| tlm_generic_payload::deep_copy_from(const tlm_generic_payload & other) |
| { |
| m_command = other.get_command(); |
| m_address = other.get_address(); |
| m_length = other.get_data_length(); |
| m_response_status = other.get_response_status(); |
| m_byte_enable_length = other.get_byte_enable_length(); |
| m_streaming_width = other.get_streaming_width(); |
| m_gp_option = other.get_gp_option(); |
| m_dmi = other.is_dmi_allowed(); |
| |
| // deep copy data |
| // there must be enough space in the target transaction! |
| if(m_data && other.m_data) |
| { |
| std::memcpy(m_data, other.m_data, m_length); |
| } |
| // deep copy byte enables |
| // there must be enough space in the target transaction! |
| if(m_byte_enable && other.m_byte_enable) |
| { |
| std::memcpy(m_byte_enable, other.m_byte_enable, m_byte_enable_length); |
| } |
| // deep copy extensions (sticky and non-sticky) |
| if(m_extensions.size() < other.m_extensions.size()) { |
| m_extensions.expand(other.m_extensions.size()); |
| } |
| for(unsigned int i=0; i<other.m_extensions.size(); i++) |
| { |
| if(other.m_extensions[i]) |
| { //original has extension i |
| if(!m_extensions[i]) |
| { //We don't: clone. |
| tlm_extension_base *ext = other.m_extensions[i]->clone(); |
| if(ext) //extension may not be clonable. |
| { |
| if(has_mm()) |
| { //mm can take care of removing cloned extensions |
| set_auto_extension(i, ext); |
| } |
| else |
| { // no mm, user will call free_all_extensions(). |
| set_extension(i, ext); |
| } |
| } |
| } |
| else |
| { //We already have such extension. Copy original over it. |
| m_extensions[i]->copy_from(*other.m_extensions[i]); |
| } |
| } |
| } |
| } |
| |
| // To update the state of the original generic payload from a deep copy |
| // Assumes that "other" was created from the original by calling deep_copy_from |
| // Argument use_byte_enable_on_read determines whether to use or ignores byte enables |
| // when copying back the data array on a read command |
| |
| void |
| tlm_generic_payload::update_original_from(const tlm_generic_payload & other, |
| bool use_byte_enable_on_read) |
| { |
| // Copy back extensions that are present on the original |
| update_extensions_from(other); |
| |
| // Copy back the response status and DMI hint attributes |
| m_response_status = other.get_response_status(); |
| m_dmi = other.is_dmi_allowed(); |
| |
| // Copy back the data array for a read command only |
| // deep_copy_from allowed null pointers, and so will we |
| // We assume the arrays are the same size |
| // We test for equal pointers in case the original and the copy share the same array |
| |
| if(is_read() && m_data && other.m_data && m_data != other.m_data) |
| { |
| if (m_byte_enable && use_byte_enable_on_read) |
| { |
| if (m_byte_enable_length == 8 && m_length % 8 == 0 ) |
| { |
| // Optimized implementation copies 64-bit words by masking |
| for (unsigned int i = 0; i < m_length; i += 8) |
| { |
| typedef sc_dt::uint64* u; |
| *reinterpret_cast<u>(&m_data[i]) &= ~*reinterpret_cast<u>(m_byte_enable); |
| *reinterpret_cast<u>(&m_data[i]) |= *reinterpret_cast<u>(&other.m_data[i]) & |
| *reinterpret_cast<u>(m_byte_enable); |
| } |
| } |
| else if (m_byte_enable_length == 4 && m_length % 4 == 0 ) |
| { |
| // Optimized implementation copies 32-bit words by masking |
| for (unsigned int i = 0; i < m_length; i += 4) |
| { |
| typedef unsigned int* u; |
| *reinterpret_cast<u>(&m_data[i]) &= ~*reinterpret_cast<u>(m_byte_enable); |
| *reinterpret_cast<u>(&m_data[i]) |= *reinterpret_cast<u>(&other.m_data[i]) & |
| *reinterpret_cast<u>(m_byte_enable); |
| } |
| } |
| else |
| // Unoptimized implementation |
| for (unsigned int i = 0; i < m_length; i++) |
| if ( m_byte_enable[i % m_byte_enable_length] ) |
| m_data[i] = other.m_data[i]; |
| } |
| else |
| std::memcpy(m_data, other.m_data, m_length); |
| } |
| } |
| |
| void |
| tlm_generic_payload::update_extensions_from(const tlm_generic_payload & other) |
| { |
| // deep copy extensions that are already present |
| sc_assert(m_extensions.size() <= other.m_extensions.size()); |
| for(unsigned int i=0; i<m_extensions.size(); i++) |
| { |
| if(other.m_extensions[i]) |
| { //original has extension i |
| if(m_extensions[i]) |
| { //We have it too. copy. |
| m_extensions[i]->copy_from(*other.m_extensions[i]); |
| } |
| } |
| } |
| } |
| |
| // Free all extensions. Useful when reusing a cloned transaction that doesn't have memory manager. |
| // normal and sticky extensions are freed and extension array cleared. |
| void tlm_generic_payload::free_all_extensions() |
| { |
| m_extensions.free_entire_cache(); |
| for(unsigned int i=0; i<m_extensions.size(); i++) |
| { |
| if(m_extensions[i]) |
| { |
| m_extensions[i]->free(); |
| m_extensions[i] = 0; |
| } |
| } |
| } |
| |
| //-------------- |
| // Destructor |
| //-------------- |
| tlm_generic_payload::~tlm_generic_payload() { |
| for(unsigned int i=0; i<m_extensions.size(); i++) |
| if(m_extensions[i]) m_extensions[i]->free(); |
| } |
| |
| //---------------- |
| // API (including setters & getters) |
| //--------------- |
| |
| std::string |
| tlm_generic_payload::get_response_string() const |
| { |
| switch(m_response_status) |
| { |
| case TLM_OK_RESPONSE: return "TLM_OK_RESPONSE"; |
| case TLM_INCOMPLETE_RESPONSE: return "TLM_INCOMPLETE_RESPONSE"; |
| case TLM_GENERIC_ERROR_RESPONSE: return "TLM_GENERIC_ERROR_RESPONSE"; |
| case TLM_ADDRESS_ERROR_RESPONSE: return "TLM_ADDRESS_ERROR_RESPONSE"; |
| case TLM_COMMAND_ERROR_RESPONSE: return "TLM_COMMAND_ERROR_RESPONSE"; |
| case TLM_BURST_ERROR_RESPONSE: return "TLM_BURST_ERROR_RESPONSE"; |
| case TLM_BYTE_ENABLE_ERROR_RESPONSE: return "TLM_BYTE_ENABLE_ERROR_RESPONSE"; |
| } |
| return "TLM_UNKNOWN_RESPONSE"; |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Dynamic extension mechanism: */ |
| /* --------------------------------------------------------------------- */ |
| |
| tlm_extension_base* |
| tlm_generic_payload::set_extension(unsigned int index, |
| tlm_extension_base* ext) |
| { |
| sc_assert(index < m_extensions.size()); |
| tlm_extension_base* tmp = m_extensions[index]; |
| m_extensions[index] = ext; |
| return tmp; |
| } |
| |
| tlm_extension_base* |
| tlm_generic_payload::set_auto_extension(unsigned int index, |
| tlm_extension_base* ext) |
| { |
| sc_assert(index < m_extensions.size()); |
| tlm_extension_base* tmp = m_extensions[index]; |
| m_extensions[index] = ext; |
| if (!tmp) m_extensions.insert_in_cache(&m_extensions[index]); |
| sc_assert(m_mm != 0); |
| return tmp; |
| } |
| |
| tlm_extension_base* |
| tlm_generic_payload::get_extension(unsigned int index) const |
| { |
| sc_assert(index < m_extensions.size()); |
| return m_extensions[index]; |
| } |
| |
| void tlm_generic_payload::clear_extension(unsigned int index) |
| { |
| sc_assert(index < m_extensions.size()); |
| m_extensions[index] = static_cast<tlm_extension_base*>(0); |
| } |
| |
| void tlm_generic_payload::release_extension(unsigned int index) |
| { |
| sc_assert(index < m_extensions.size()); |
| if (m_mm) |
| { |
| m_extensions.insert_in_cache(&m_extensions[index]); |
| } |
| else |
| { |
| m_extensions[index]->free(); |
| m_extensions[index] = static_cast<tlm_extension_base*>(0); |
| } |
| } |
| |
| void tlm_generic_payload::resize_extensions() |
| { |
| m_extensions.expand(max_num_extensions()); |
| } |
| |
| } // namespace tlm |