tlm_endian_conv.h
来自「SystemC Transaction Level Modelling. 是基于」· C头文件 代码 · 共 786 行 · 第 1/2 页
H
786 行
/***************************************************************************** The following code is derived, directly or indirectly, from the SystemC source code Copyright (c) 1996-2008 by all Contributors. All Rights reserved. The contents of this file are subject to the restrictions and limitations set forth in the SystemC Open Source License Version 3.0 (the "License"); You may not use this file except in compliance with such restrictions and limitations. You may obtain instructions on how to receive a copy of the License at http://www.systemc.org/. Software distributed by Contributors under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.*****************************************************************************/#ifndef __TLM_ENDIAN_CONV_H__#define __TLM_ENDIAN_CONV_H__#include <systemc>#include "tlm_gp.h"namespace tlm {/*Tranaction-Level ModellingEndianness Helper FunctionsDESCRIPTIONA set of functions for helping users to get the endiannessright in their TLM models of system initiators. These functions arefor use within an initiator. They can not be used as-is outsidean initiator because the extension used to store context will not workif cascaded, and they do not respect the generic payload mutabilityrules. However this code may be easily copied and adapted for usein bridges, etc..These functions are not compulsory. There are other legitimate ways toachieve the same functionality. If extra information is available atcompile time about the nature of an initiator's transactions, this canbe exploited to accelerate simulations by creating further functionssimilar to those in this file. In general a functional transaction can bedescribed in more than one way by a TLM-2 GP object.The functions convert the endianness of a GP object, either on request orresponse. They should only be used when the initiator's endiannessdoes not match the host's endianness. They assume 'arithmetic mode'meaning that within a data word the byte order is always host-endian.For non-arithmetic mode initiators they can be used with a data wordsize of 1 byte.All the functions are templates, for example:template<class DATAWORD> inline void to_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus)The template parameter provides the data word width. Having this as a classmakes it easy to use it for copy and swap operations within the functions.If the assignment operator for this class is overloaded, the endiannessconversion function may not have the desired effect.All the functions have the same signature except for different names.The principle is that a function to_hostendian_convtype() is called when theinitiator-endian transaction is created, and the matching functionfrom_hostendian_convtype() is called when the transaction is completed, forexample before read data can be used. In some cases the from_ function isredundant but an empty function is provided anyway. It is stronglyrecommended that the from_ function is called, in case it ceases to beredundant in future versions of this code.No context needs to be managed outside the two functions, except that theymust be called with the same template parameter and the same bus width.For initiator models that can not easily manage this context information,a single entry point for the from_ function is provided, which will bea little slower than calling the correct from_ function directly, asit can not be inlined.All functions assume power-of-2 bus and data word widths.Functions offered:0) A pair of functions that work for almost all TLM2 GP transactions. Theonly limitations are that data and bus widths should be powers of 2, and thatthe data length should be an integer number of streaming widths and that thestreaming width should be an integer number of data words.These functions always allocate new data and byte enable buffers and copydata one byte at a time. tlm_to_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus) tlm_from_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus)1) A pair of functions that work for all transactions regardless of data andbus data sizes and address alignment except for the the followinglimitations:- byte-enables are supported only when byte-enable granularity is no finerthan the data word (every data word is wholly enabled or wholly disabled)- byte-enable-length is not supported (if byte enables are present, the byteenable length must be equal to the data length).- streaming width is not supported- data word wider than bus word is not supportedA new data buffer and a new byte enable buffer are always allocated. Byteenables are assumed to be needed even if not required for the original(unconverted) transaction. Data is copied to the new buffer on request(for writes) or on response (for reads). Copies are done word-by-wordwhere possible. tlm_to_hostendian_word(tlm_generic_payload *txn, int sizeof_databus) tlm_from_hostendian_word(tlm_generic_payload *txn, int sizeof_databus)2) If the original transaction is both word and bus-aligned then this pair offunctions can be used. It will complete faster than the generic functionbecause the data reordering function is much simpler and no addressconversion is required.The following limitations apply:- byte-enables are supported only when byte-enable granularity is no finerthan the data word (every data word is wholly enabled or wholly disabled)- byte-enable-length is not supported (if byte enables are present, the byteenable length must be equal to the data length).- streaming width is not supported- data word wider than bus word is not supported- the transaction must be an integer number of bus words- the address must be aligned to the bus width tlm_to_hostendian_aligned(tlm_generic_payload *txn, int sizeof_databus) tlm_from_hostendian_aligned(tlm_generic_payload *txn, int sizeof_databus)3) For single word transactions that don't cross a bus word boundary itis always safe to work in-place and the conversion is very simple. Again,streaming width and byte-enable length are not supported, and byte-enablesmay not changes within a data word. tlm_to_hostendian_single(tlm_generic_payload *txn, int sizeof_databus) tlm_from_hostendian_single(tlm_generic_payload *txn, int sizeof_databus)4) A single entry point for accessing the correct from_ function withoutneeding to store context. tlm_from_hostendian(tlm_generic_payload *txn)*/#ifndef uchar#define uchar unsigned char#else#define TLM_END_CONV_DONT_UNDEF_UCHAR#endif///////////////////////////////////////////////////////////////////////////////// Generic Utilities// a pool for uchar* buffers of arbitrary but bounded size. the pool contains// buffers with a fixed size - the largest so far requested.class tlm_buffer_pool { int max_buffer_size; uchar* pool_head; public: tlm_buffer_pool(): max_buffer_size(32), pool_head(0) {}; uchar *get_a_buffer(int size) { if(size > max_buffer_size) { max_buffer_size = size; // empty the pool - it will have to grow again naturally for(uchar *p = pool_head; p != 0; ) { uchar *q = p; p = *((uchar **)(p + sizeof(int))); delete [] q; } pool_head = 0; } if(pool_head == 0) { // do a real malloc because pool is empty // allocate 2 spare spaces, one for the size and the other for the // next-pointer pool_head = new uchar[max_buffer_size + sizeof(int) + sizeof(uchar *)]; *((int *)pool_head) = max_buffer_size; *((uchar **)(pool_head + sizeof(int))) = 0; } // now pop the pool and return the old head uchar *retval = pool_head + sizeof(int) + sizeof(uchar *); pool_head = *((uchar **)(pool_head + sizeof(int))); return retval; }; void return_buffer(uchar *p) { // calculate the start of the actual buffer uchar *q = p - sizeof(int) - sizeof(uchar *); if(*((int *)q) != max_buffer_size) { // this buffer's size is out of date. throw it away delete [] q; } else { // push a buffer into the pool if it has the right size *((uchar **)(q + sizeof(int))) = pool_head; pool_head = q; } } int get_pool_size() { int s = 0; for(uchar *p = pool_head; p != 0; p = *((uchar **)(p + sizeof(int))), s++) {} return s; } int get_buffer_size() {return max_buffer_size;}};static tlm_buffer_pool local_buffer_pool;// an extension to keep the information needed for reconversion of responseclass tlm_endian_context : public tlm_extension<tlm_endian_context> { public: sc_dt::uint64 address; // used by generic, word sc_dt::uint64 new_address; // used by generic uchar *data_ptr; // used by generic, word, aligned uchar *byte_enable; // used by word int length; // used by generic, word int stream_width; // used by generic // used by common entry point on response void (*from_f)(tlm_generic_payload *txn, unsigned int sizeof_databus); int sizeof_databus; // required for extension management tlm_extension_base* clone() const {return 0;} void free() {delete this;} void copy_from(tlm_extension_base const &) {return;}};// Assumptions about transaction contexts:// 1) only the address attribute of a transaction// is mutable. all other attributes are unchanged from the request to// response side conversion.// 2) the conversion functions in this file do not respect the mutability// rules and do not put the transaction back into its original state after// completion. so if the initiator has any cleaning up to do (eg of byte// enable buffers), it needs to store its own context. the transaction// returned to the initiator may contain pointers to data and byte enable// that can/must not be deleted.// 3) the conversion functions in this file use an extension to store// context information. they do not remove this extension. the initiator// should not remove it unless it deletes the generic payload// object.inline tlm_endian_context *establish_context(tlm_generic_payload *txn) { tlm_endian_context *tc = txn->get_extension<tlm_endian_context>(); if(tc == 0) { tc = new tlm_endian_context; txn->set_extension(tc); } return tc;}// a set of constants for efficient filling of byte enablestemplate<class D> class tlm_bool { public: static D TLM_TRUE; static D TLM_FALSE; static D make_uchar_array(uchar c) { D d; uchar *tmp = (uchar *)(&d); for(unsigned int i=0; i<sizeof(D); i++) tmp[i] = c; return d; } // also provides an syntax-efficient tester, using a // copy constuctor and an implicit cast to boolean tlm_bool(D &d) : b(*((uchar*)&d) != TLM_BYTE_DISABLED) {} operator bool() const {return b;} private: bool b;};template<class D> D tlm_bool<D>::TLM_TRUE = tlm_bool<D>::make_uchar_array(TLM_BYTE_ENABLED);template<class D> D tlm_bool<D>::TLM_FALSE = tlm_bool<D>::make_uchar_array(TLM_BYTE_DISABLED);///////////////////////////////////////////////////////////////////////////////// function set (0): Utilitiesinline void copy_db0(uchar *src1, uchar *src2, uchar *dest1, uchar *dest2) { *dest1 = *src1; *dest2 = *src2;}inline void copy_dbtrue0(uchar *src1, uchar *src2, uchar *dest1, uchar *dest2) { *dest1 = *src1; *dest2 = TLM_BYTE_ENABLED;}inline void copy_btrue0(uchar *src1, uchar *src2, uchar *dest1, uchar *dest2) { *dest2 = TLM_BYTE_ENABLED;}inline void copy_b0(uchar *src1, uchar *src2, uchar *dest1, uchar *dest2) { *dest2 = *src2;}inline void copy_dbyb0(uchar *src1, uchar *src2, uchar *dest1, uchar *dest2) { if(*dest2 == TLM_BYTE_ENABLED) *src1 = *dest1;}template<class D, void COPY(uchar *he_d, uchar *he_b, uchar *ie_d, uchar *ie_b)>inline void loop_generic0(int new_len, int new_stream_width, int orig_stream_width, int sizeof_databus, sc_dt::uint64 orig_start_address, sc_dt::uint64 new_start_address, int be_length, uchar *ie_data, uchar *ie_be, uchar *he_data, uchar *he_be) { for(int orig_sword = 0, new_sword = 0; new_sword < new_len; new_sword += new_stream_width, orig_sword += orig_stream_width) { sc_dt::uint64 ie_addr = orig_start_address; for(int orig_dword = orig_sword; orig_dword < orig_sword + orig_stream_width; orig_dword += sizeof(D)) { for(int curr_byte = orig_dword + sizeof(D) - 1; curr_byte >= orig_dword; curr_byte--) { int he_index = ((ie_addr++) ^ (sizeof_databus - 1)) - new_start_address + new_sword; COPY(ie_data+curr_byte, ie_be+(curr_byte % be_length), he_data+he_index, he_be+he_index); } } }}///////////////////////////////////////////////////////////////////////////////// function set (0): Responsetemplate<class DATAWORD> inline voidtlm_from_hostendian_generic(tlm_generic_payload *txn, unsigned int sizeof_databus) { if(txn->is_read()) { tlm_endian_context *tc = txn->template get_extension<tlm_endian_context>(); loop_generic0<DATAWORD, ©_dbyb0>(txn->get_data_length(), txn->get_streaming_width(), tc->stream_width, sizeof_databus, tc->address, tc->new_address, txn->get_data_length(), tc->data_ptr, 0, txn->get_data_ptr(), txn->get_byte_enable_ptr()); } local_buffer_pool.return_buffer(txn->get_byte_enable_ptr()); local_buffer_pool.return_buffer(txn->get_data_ptr());}///////////////////////////////////////////////////////////////////////////////// function set (0): Requesttemplate<class DATAWORD> inline voidtlm_to_hostendian_generic(tlm_generic_payload *txn, unsigned int sizeof_databus) { tlm_endian_context *tc = establish_context(txn); tc->from_f = &(tlm_from_hostendian_generic<DATAWORD>); tc->sizeof_databus = sizeof_databus; // calculate new size: nr stream words multiplied by big enough stream width int s_width = txn->get_streaming_width(); int length = txn->get_data_length(); if(s_width >= length) s_width = length; int nr_stream_words = length/s_width; // find out in which bus word the stream word starts and ends sc_dt::uint64 new_address = (txn->get_address() & ~(sizeof_databus - 1)); sc_dt::uint64 end_address = ((txn->get_address() + s_width - 1) & ~(sizeof_databus - 1)); int new_stream_width = end_address - new_address + sizeof_databus; int new_length = new_stream_width * nr_stream_words; // store context tc->data_ptr = txn->get_data_ptr(); tc->address = txn->get_address(); tc->new_address = new_address; tc->stream_width = s_width; uchar *orig_be = txn->get_byte_enable_ptr(); int orig_be_length = txn->get_byte_enable_length(); // create data and byte-enable buffers txn->set_address(new_address); txn->set_data_ptr(local_buffer_pool.get_a_buffer(new_length)); txn->set_byte_enable_ptr(local_buffer_pool.get_a_buffer(new_length)); memset(txn->get_byte_enable_ptr(), TLM_BYTE_DISABLED, new_length); txn->set_streaming_width(new_stream_width); txn->set_data_length(new_length); txn->set_byte_enable_length(new_length); // copy data and/or byte enables if(txn->is_write()) { if(orig_be == 0) { loop_generic0<DATAWORD, ©_dbtrue0>(new_length, new_stream_width, s_width, sizeof_databus, tc->address, new_address, new_length, tc->data_ptr, 0, txn->get_data_ptr(), txn->get_byte_enable_ptr()); } else { loop_generic0<DATAWORD, ©_db0>(new_length, new_stream_width, s_width, sizeof_databus, tc->address, new_address, orig_be_length, tc->data_ptr, orig_be, txn->get_data_ptr(),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?