1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20
21#ifndef __SYSTEMC_EXT_TLM_CORE_2_GENERIC_PAYLOAD_ENDIAN_CONV_HH__
22#define __SYSTEMC_EXT_TLM_CORE_2_GENERIC_PAYLOAD_ENDIAN_CONV_HH__
23
24#include <cstring> // std::memset
25
26#include "gp.hh"
27
28namespace tlm
29{
30
31/*
32Tranaction-Level Modelling
33Endianness Helper Functions
34
35DESCRIPTION
36A set of functions for helping users to get the endianness
37right in their TLM models of system initiators.  These functions are
38for use within an initiator.  They can not be used as-is outside
39an initiator because the extension used to store context will not work
40if cascaded, and they do not respect the generic payload mutability
41rules.  However this code may be easily copied and adapted for use
42in bridges, etc..
43
44These functions are not compulsory.  There are other legitimate ways to
45achieve the same functionality.  If extra information is available at
46compile time about the nature of an initiator's transactions, this can
47be exploited to accelerate simulations by creating further functions
48similar to those in this file.  In general a functional transaction can be
49described in more than one way by a TLM-2 GP object.
50
51The functions convert the endianness of a GP object, either on request or
52response.  They should only be used when the initiator's endianness
53does not match the host's endianness.  They assume 'arithmetic mode'
54meaning that within a data word the byte order is always host-endian.
55For non-arithmetic mode initiators they can be used with a data word
56size of 1 byte.
57
58All the functions are templates, for example:
59
60template<class DATAWORD> inline void
61  to_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus)
62
63The template parameter provides the data word width.  Having this as a class
64makes it easy to use it for copy and swap operations within the functions.
65If the assignment operator for this class is overloaded, the endianness
66conversion function may not have the desired effect.
67
68All the functions have the same signature except for different names.
69
70The principle is that a function to_hostendian_convtype() is called when the
71initiator-endian transaction is created, and the matching function
72from_hostendian_convtype() is called when the transaction is completed, for
73example before read data can be used.  In some cases the from_ function is
74redundant but an empty function is provided anyway.  It is strongly
75recommended that the from_ function is called, in case it ceases to be
76redundant in future versions of this code.
77
78No context needs to be managed outside the two functions, except that they
79must be called with the same template parameter and the same bus width.
80
81For initiator models that can not easily manage this context information,
82a single entry point for the from_ function is provided, which will be
83a little slower than calling the correct from_ function directly, as
84it can not be inlined.
85
86All functions assume power-of-2 bus and data word widths.
87
88Functions offered:
89
900) A pair of functions that work for almost all TLM2 GP transactions.  The
91only limitations are that data and bus widths should be powers of 2, and that
92the data length should be an integer number of streaming widths and that the
93streaming width should be an integer number of data words.
94These functions always allocate new data and byte enable buffers and copy
95data one byte at a time.
96  tlm_to_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus)
97  tlm_from_hostendian_generic(tlm_generic_payload *txn, int sizeof_databus)
98
991) A pair of functions that work for all transactions regardless of data and
100bus data sizes and address alignment except for the the following
101limitations:
102- byte-enables are supported only when byte-enable granularity is no finer
103than the data word (every data word is wholly enabled or wholly disabled)
104- byte-enable-length is not supported (if byte enables are present, the byte
105enable length must be equal to the data length).
106- streaming width is not supported
107- data word wider than bus word is not supported
108A new data buffer and a new byte enable buffer are always allocated.  Byte
109enables are assumed to be needed even if not required for the original
110(unconverted) transaction.  Data is copied to the new buffer on request
111(for writes) or on response (for reads).  Copies are done word-by-word
112where possible.
113  tlm_to_hostendian_word(tlm_generic_payload *txn, int sizeof_databus)
114  tlm_from_hostendian_word(tlm_generic_payload *txn, int sizeof_databus)
115
1162) If the original transaction is both word and bus-aligned then this pair of
117functions can be used.  It will complete faster than the generic function
118because the data reordering function is much simpler and no address
119conversion is required.
120The following limitations apply:
121- byte-enables are supported only when byte-enable granularity is no finer
122than the data word (every data word is wholly enabled or wholly disabled)
123- byte-enable-length is not supported (if byte enables are present, the byte
124enable length must be equal to the data length).
125- streaming width is not supported
126- data word wider than bus word is not supported
127- the transaction must be an integer number of bus words
128- the address must be aligned to the bus width
129  tlm_to_hostendian_aligned(tlm_generic_payload *txn, int sizeof_databus)
130  tlm_from_hostendian_aligned(tlm_generic_payload *txn, int sizeof_databus)
131
1323) For single word transactions that don't cross a bus word boundary it
133is always safe to work in-place and the conversion is very simple.  Again,
134streaming width and byte-enable length are not supported, and byte-enables
135may not changes within a data word.
136  tlm_to_hostendian_single(tlm_generic_payload *txn, int sizeof_databus)
137  tlm_from_hostendian_single(tlm_generic_payload *txn, int sizeof_databus)
138
1394) A single entry point for accessing the correct from_ function without
140needing to store context.
141  tlm_from_hostendian(tlm_generic_payload *txn)
142*/
143
144///////////////////////////////////////////////////////////////////////////////
145// Generic Utilities
146
147class tlm_endian_context;
148
149class tlm_endian_context_pool
150{
151  public:
152    tlm_endian_context *first;
153    inline tlm_endian_context_pool();
154    inline ~tlm_endian_context_pool();
155    inline tlm_endian_context *pop();
156    inline void push(tlm_endian_context *c);
157};
158
159static tlm_endian_context_pool global_tlm_endian_context_pool;
160
161// an extension to keep the information needed for reconversion of response
162class tlm_endian_context : public tlm_extension<tlm_endian_context>
163{
164  public:
165    tlm_endian_context() : dbuf_size(0), bebuf_size(0) {}
166
167    ~tlm_endian_context() {
168        if (dbuf_size > 0)
169            delete [] new_dbuf;
170        if (bebuf_size > 0)
171            delete [] new_bebuf;
172    }
173
174    sc_dt::uint64 address; // Used by generic, word.
175    sc_dt::uint64 new_address; // Used by generic.
176    unsigned char *data_ptr; // Used by generic, word, aligned.
177    unsigned char *byte_enable; // Used by word.
178    int length; // Used by generic, word.
179    int stream_width; // Used by generic.
180
181    // Used by common entry point on response.
182    void (*from_f)(tlm_generic_payload *txn, unsigned int sizeof_databus);
183    int sizeof_databus;
184
185    // Reordering buffers for data and byte-enables.
186    unsigned char *new_dbuf, *new_bebuf;
187    int dbuf_size, bebuf_size;
188
189    void
190    establish_dbuf(int len)
191    {
192        if (len <= dbuf_size)
193            return;
194        if (dbuf_size > 0)
195            delete [] new_dbuf;
196        new_dbuf = new unsigned char[len];
197        dbuf_size = len;
198    }
199
200    void
201    establish_bebuf(int len)
202    {
203        if (len <= bebuf_size)
204            return;
205        if (bebuf_size > 0)
206            delete [] new_bebuf;
207        new_bebuf = new unsigned char[len];
208        bebuf_size = len;
209    }
210
211    // Required for extension management.
212    void free() { global_tlm_endian_context_pool.push(this); }
213    tlm_extension_base *clone() const { return 0; }
214    void copy_from(tlm_extension_base const &) { return; }
215
216    // For pooling.
217    tlm_endian_context *next;
218};
219
220// Assumptions about transaction contexts:
221// 1) only the address attribute of a transaction
222// is mutable.  all other attributes are unchanged from the request to
223// response side conversion.
224// 2) the conversion functions in this file do not respect the mutability
225// rules and do not put the transaction back into its original state after
226// completion.  so if the initiator has any cleaning up to do (eg of byte
227// enable buffers), it needs to store its own context.  the transaction
228// returned to the initiator may contain pointers to data and byte enable
229// that can/must not be deleted.
230// 3) the conversion functions in this file use an extension to store
231// context information.  they do not remove this extension.  the initiator
232// should not remove it unless it deletes the generic payload
233// object.
234
235inline tlm_endian_context *
236establish_context(tlm_generic_payload *txn)
237{
238    tlm_endian_context *tc = txn->get_extension<tlm_endian_context>();
239    if (tc == 0) {
240        tc = global_tlm_endian_context_pool.pop();
241        txn->set_extension(tc);
242    }
243    return tc;
244}
245
246inline tlm_endian_context_pool::tlm_endian_context_pool() : first(0) {}
247
248inline tlm_endian_context_pool::~tlm_endian_context_pool()
249{
250    while (first != 0) {
251        tlm_endian_context *next = first->next;
252        delete first;
253        first = next;
254    }
255}
256
257tlm_endian_context *
258tlm_endian_context_pool::pop()
259{
260    if (first == 0)
261        return new tlm_endian_context;
262    tlm_endian_context *r = first;
263    first = first->next;
264    return r;
265}
266
267void tlm_endian_context_pool::push(tlm_endian_context *c)
268{
269    c->next = first;
270    first = c;
271}
272
273
274// A set of constants for efficient filling of byte enables.
275template <class D>
276class tlm_bool
277{
278  public:
279    static D TLM_TRUE;
280    static D TLM_FALSE;
281    static D
282    make_uchar_array(unsigned char c)
283    {
284        D d;
285        unsigned char *tmp = (unsigned char *)(&d);
286        for (ptrdiff_t i = 0; i != sizeof(D); i++)
287            tmp[i] = c; // 64BITFIX negligable risk but easy fix.
288        return d;
289    }
290
291    // Also provides an syntax-efficient tester, using a
292    // copy constuctor and an implicit cast to boolean.
293    tlm_bool(D &d) : b(*((unsigned char *)&d) != TLM_BYTE_DISABLED) {}
294    operator bool() const { return b; }
295  private:
296    bool b;
297};
298
299template<class D>
300D tlm_bool<D>::TLM_TRUE = tlm_bool<D>::make_uchar_array(TLM_BYTE_ENABLED);
301template<class D>
302D tlm_bool<D>::TLM_FALSE = tlm_bool<D>::make_uchar_array(TLM_BYTE_DISABLED);
303
304
305
306inline void
307copy_db0(unsigned char *src1, unsigned char *src2,
308         unsigned char *dest1, unsigned char *dest2)
309{
310    *dest1 = *src1;
311    *dest2 = *src2;
312}
313
314inline void
315copy_dbtrue0(unsigned char *src1, unsigned char * /* src2 */,
316             unsigned char *dest1, unsigned char *dest2)
317{
318    *dest1 = *src1;
319    *dest2 = TLM_BYTE_ENABLED;
320}
321
322inline void
323copy_btrue0(unsigned char * /* src1 */, unsigned char * /* src2 */,
324            unsigned char * /* dest1 */, unsigned char *dest2)
325{
326    *dest2 = TLM_BYTE_ENABLED;
327}
328
329inline void
330copy_b0(unsigned char * /* src1 */, unsigned char *src2,
331        unsigned char * /* dest1 */, unsigned char *dest2)
332{
333    *dest2 = *src2;
334}
335
336inline void
337copy_dbyb0(unsigned char *src1, unsigned char * /* src2 */,
338           unsigned char *dest1, unsigned char *dest2)
339{
340    if (*dest2 == TLM_BYTE_ENABLED)
341        *src1 = *dest1;
342}
343
344
345template <class D,
346          void COPY(unsigned char *he_d, unsigned char *he_b,
347                    unsigned char *ie_d, unsigned char *ie_b)>
348inline void
349loop_generic0(int new_len, int new_stream_width, int orig_stream_width,
350              int sizeof_databus, sc_dt::uint64 orig_start_address,
351              sc_dt::uint64 new_start_address, int be_length,
352              unsigned char *ie_data, unsigned char *ie_be,
353              unsigned char *he_data, unsigned char *he_be)
354{
355    for (int orig_sword = 0, new_sword = 0; new_sword < new_len;
356            new_sword += new_stream_width, orig_sword += orig_stream_width) {
357        sc_dt::uint64 ie_addr = orig_start_address;
358        for (int orig_dword = orig_sword;
359                orig_dword < orig_sword + orig_stream_width;
360                orig_dword += sizeof(D)) {
361            for (int curr_byte = orig_dword + sizeof(D) - 1;
362                    curr_byte >= orig_dword; curr_byte--) {
363                ptrdiff_t he_index = ((ie_addr++) ^ (sizeof_databus - 1)) -
364                    new_start_address + new_sword; // 64BITFIX
365                COPY(ie_data + curr_byte,
366                        // 64BITRISK no risk of overflow, always positive.
367                        ie_be + (curr_byte % be_length),
368                        he_data + he_index, he_be + he_index);
369            }
370        }
371    }
372}
373
374
375///////////////////////////////////////////////////////////////////////////////
376// function set (0): Response
377///////////////////////////////////////////////////////////////////////////////
378
379template <class DATAWORD>
380inline void
381tlm_from_hostendian_generic(tlm_generic_payload *txn,
382                            unsigned int sizeof_databus)
383{
384    if (txn->is_read()) {
385        tlm_endian_context *tc =
386            txn->template get_extension<tlm_endian_context>();
387        loop_generic0<DATAWORD, &copy_dbyb0>(txn->get_data_length(),
388                txn->get_streaming_width(), tc->stream_width, sizeof_databus,
389                tc->address, tc->new_address, txn->get_data_length(),
390                tc->data_ptr, 0, txn->get_data_ptr(),
391                txn->get_byte_enable_ptr());
392    }
393}
394
395
396///////////////////////////////////////////////////////////////////////////////
397// function set (0): Request
398template <class DATAWORD>
399inline void
400tlm_to_hostendian_generic(tlm_generic_payload *txn,
401        unsigned int sizeof_databus)
402{
403    tlm_endian_context *tc = establish_context(txn);
404    tc->from_f = &(tlm_from_hostendian_generic<DATAWORD>);
405    tc->sizeof_databus = sizeof_databus;
406
407    // Calculate new size: nr stream words multiplied by big enough stream
408    // width.
409    int s_width = txn->get_streaming_width();
410    int length = txn->get_data_length();
411    if (s_width >= length)
412        s_width = length;
413    int nr_stream_words = length / s_width;
414
415    // Find out in which bus word the stream word starts and ends.
416    sc_dt::uint64 new_address = (txn->get_address() & ~(sizeof_databus - 1));
417    sc_dt::uint64 end_address = ((txn->get_address() + s_width - 1) &
418            ~(sizeof_databus - 1));
419
420    int new_stream_width = end_address - new_address + sizeof_databus;
421    int new_length = new_stream_width * nr_stream_words;
422
423    // Store context.
424    tc->data_ptr = txn->get_data_ptr();
425    tc->address = txn->get_address();
426    tc->new_address = new_address;
427    tc->stream_width = s_width;
428    unsigned char *orig_be = txn->get_byte_enable_ptr();
429    int orig_be_length = txn->get_byte_enable_length();
430
431    // Create data and byte-enable buffers.
432    txn->set_address(new_address);
433    tc->establish_dbuf(new_length);
434    txn->set_data_ptr(tc->new_dbuf);
435    tc->establish_bebuf(new_length);
436    txn->set_byte_enable_ptr(tc->new_bebuf);
437    std::memset(txn->get_byte_enable_ptr(), TLM_BYTE_DISABLED, new_length);
438    txn->set_streaming_width(new_stream_width);
439    txn->set_data_length(new_length);
440    txn->set_byte_enable_length(new_length);
441
442    // Copy data and/or byte enables.
443    if (txn->is_write()) {
444        if (orig_be == 0) {
445            loop_generic0<DATAWORD, &copy_dbtrue0>(
446                    new_length, new_stream_width, s_width, sizeof_databus,
447                    tc->address, new_address, new_length, tc->data_ptr, 0,
448                    txn->get_data_ptr(), txn->get_byte_enable_ptr());
449        } else {
450            loop_generic0<DATAWORD, &copy_db0>(new_length, new_stream_width,
451                    s_width, sizeof_databus, tc->address, new_address,
452                    orig_be_length, tc->data_ptr, orig_be,
453                    txn->get_data_ptr(), txn->get_byte_enable_ptr());
454        }
455    } else {
456        // Read transaction.
457        if (orig_be == 0) {
458            loop_generic0<DATAWORD, &copy_btrue0>(new_length,
459                    new_stream_width, s_width, sizeof_databus, tc->address,
460                    new_address, new_length, tc->data_ptr, 0,
461                    txn->get_data_ptr(), txn->get_byte_enable_ptr());
462        } else {
463            loop_generic0<DATAWORD, &copy_b0>(new_length, new_stream_width,
464                    s_width, sizeof_databus, tc->address, new_address,
465                    orig_be_length, tc->data_ptr, orig_be,
466                    txn->get_data_ptr(), txn->get_byte_enable_ptr());
467        }
468    }
469}
470
471
472///////////////////////////////////////////////////////////////////////////////
473// function set (1): Utilities
474///////////////////////////////////////////////////////////////////////////////
475
476template <class D>
477inline void
478copy_d1(unsigned char *src1, unsigned char *src2,
479        unsigned char *dest1, unsigned char *dest2)
480{
481    *((D *)dest1) = *((D *)src1);
482    *((D *)dest2) = tlm_bool<D>::TLM_TRUE;
483}
484
485template <class D>
486inline void
487copy_db1(unsigned char *src1, unsigned char *src2,
488         unsigned char *dest1, unsigned char *dest2)
489{
490    *((D *)dest1) = *((D *)src1);
491    *((D *)dest2) = *((D *)src2);
492}
493
494template <class D>
495inline void
496true_b1(unsigned char *src1, unsigned char *src2,
497        unsigned char *dest1, unsigned char *dest2)
498{
499    *((D *)dest2) = tlm_bool<D>::TLM_TRUE;
500}
501
502template <class D>
503inline void
504copy_b1(unsigned char *src1, unsigned char *src2,
505        unsigned char *dest1, unsigned char *dest2)
506{
507    *((D *)dest2) = *((D *)src2);
508}
509
510template <class D>
511inline void
512copy_dbyb1(unsigned char *src1, unsigned char *src2,
513           unsigned char *dest1, unsigned char *dest2)
514{
515    if (*src2 != TLM_BYTE_DISABLED)
516        *((D *)src1) = *((D *)dest1);
517}
518
519template <class D>
520inline void
521copy_dbytrue1(unsigned char *src1, unsigned char *src2,
522              unsigned char *dest1, unsigned char *dest2)
523{
524    *((D *)src1) = *((D *)dest1);
525}
526
527template<class D>
528inline void
529false_b1(unsigned char *dest1)
530{
531    *((D *)dest1) = tlm_bool<D>::TLM_FALSE;
532}
533
534template<class D>
535inline void
536no_b1(unsigned char *dest1)
537{}
538
539template<class D,
540         void COPY(unsigned char *src1, unsigned char *src2,
541                   unsigned char *dest1, unsigned char *dest2),
542         void COPYuchar(unsigned char *src1, unsigned char *src2,
543                        unsigned char *dest1, unsigned char *dest2),
544         void FILLFALSE(unsigned char *dest1),
545         void FILLFALSEuchar(unsigned char *dest1)>
546inline int
547loop_word1(int bytes_left, int len0, int lenN, int sizeof_databus,
548           unsigned char *start, unsigned char *end,
549           unsigned char *src, unsigned char *bsrc,
550           unsigned char *dest, unsigned char *bdest)
551{
552    ptrdiff_t d2b_src = bsrc - src; // 64BITFIX was int
553    ptrdiff_t d2b_dest = bdest - dest; // 64BITFIX was int
554    unsigned char *original_dest = dest;
555
556    while (true) {
557        // len0 bytes at start of a bus word.
558        if ((src >= start) && (src < end)) {
559            for (int i = 0; i < len0; i++) {
560                COPYuchar(src, src + d2b_src, dest, dest + d2b_dest);
561                src++;
562                dest++;
563            }
564            bytes_left -= len0;
565            if (bytes_left <= 0)
566                return int(dest - original_dest);
567        } else {
568            for (int i = 0; i < len0; i++) {
569                FILLFALSEuchar(dest + d2b_dest);
570                src++;
571                dest++;
572            }
573        }
574        src -= 2 * sizeof(D);
575
576        // Sequence of full data word fragments.
577        for (unsigned int i = 1; i < sizeof_databus / sizeof(D); i++) {
578            if ((src >= start) && (src < end)) {
579                COPY(src, src + d2b_src, dest, dest + d2b_dest);
580                bytes_left -= sizeof(D);
581            } else {
582                FILLFALSE(dest + d2b_dest);
583            }
584            dest += sizeof(D);
585            if (bytes_left <= 0)
586                return int(dest - original_dest);
587            src -= sizeof(D);
588        }
589
590        // lenN bytes at end of bus word.
591        if ((src >= start) && (src < end)) {
592            for (int i = 0; i < lenN; i++) {
593                COPYuchar(src, src + d2b_src, dest, dest + d2b_dest);
594                src++;
595                dest++;
596            }
597            bytes_left -= lenN;
598            if (bytes_left <= 0)
599                return int(dest - original_dest);
600        } else {
601            for (int i = 0; i < lenN; i++) {
602                FILLFALSEuchar(dest + d2b_dest);
603                src++;
604                dest++;
605            }
606        }
607        src += 2 * sizeof_databus;
608    }
609}
610
611
612///////////////////////////////////////////////////////////////////////////////
613// function set (1): Response
614///////////////////////////////////////////////////////////////////////////////
615
616template <class DATAWORD>
617inline void
618tlm_from_hostendian_word(tlm_generic_payload *txn, unsigned int sizeof_databus)
619{
620    if (txn->is_read()) {
621        tlm_endian_context *tc =
622            txn->template get_extension<tlm_endian_context>();
623        sc_dt::uint64 b_mask = sizeof_databus - 1;
624        int d_mask = sizeof(DATAWORD) - 1;
625        int a_offset = static_cast<int>(tc->address & b_mask);
626        int len0 = (sizeof_databus - a_offset) & d_mask;
627        int lenN = sizeof(DATAWORD) - len0;
628        unsigned char *d_start = tc->data_ptr;
629        unsigned char *d_end =
630            ptrdiff_t(tc->length) + d_start; // 64BITFIX probably redundant
631        unsigned char *d =
632            ptrdiff_t(((sizeof_databus - a_offset) & ~d_mask) + lenN) +
633            d_start; // 64BITFIX probably redundant
634
635        // Iterate over transaction copying data qualified by byte-enables.
636        if (tc->byte_enable == 0) {
637            loop_word1<DATAWORD, &copy_dbytrue1<DATAWORD>,
638                       &copy_dbytrue1<unsigned char>, &no_b1<DATAWORD>,
639                       &no_b1<unsigned char>>(
640                               tc->length, len0, lenN, sizeof_databus,
641                               d_start, d_end, d, 0, txn->get_data_ptr(), 0);
642        } else {
643            loop_word1<DATAWORD, &copy_dbyb1<DATAWORD>,
644                       &copy_dbyb1<unsigned char>, &no_b1<DATAWORD>,
645                       &no_b1<unsigned char>>(
646                               tc->length, len0, lenN, sizeof_databus,
647                               d_start, d_end, d,
648                               tc->byte_enable - d_start + d,
649                               txn->get_data_ptr(), 0);
650        }
651    }
652}
653
654
655///////////////////////////////////////////////////////////////////////////////
656// function set (1): Request
657///////////////////////////////////////////////////////////////////////////////
658
659template <class DATAWORD>
660inline void
661tlm_to_hostendian_word(tlm_generic_payload *txn, unsigned int sizeof_databus)
662{
663    tlm_endian_context *tc = establish_context(txn);
664    tc->from_f = &(tlm_from_hostendian_word<DATAWORD>);
665    tc->sizeof_databus = sizeof_databus;
666
667    sc_dt::uint64 b_mask = sizeof_databus - 1;
668    int d_mask = sizeof(DATAWORD) - 1;
669    sc_dt::uint64 a_aligned = txn->get_address() & ~b_mask;
670    int a_offset = static_cast<int>(txn->get_address() & b_mask);
671    int len0 = (sizeof_databus - a_offset) & d_mask;
672    int lenN = sizeof(DATAWORD) - len0;
673    unsigned char *d_start = txn->get_data_ptr();
674    unsigned char *d_end =
675        ptrdiff_t(txn->get_data_length()) + d_start;
676            // 64BITFIX probably redundant.
677    unsigned char *d =
678        ptrdiff_t(((sizeof_databus - a_offset) & ~d_mask) + lenN) + d_start;
679            // 64BITFIX probably redundant.
680
681    // Create new data and byte enable buffers.
682    int long_enough = txn->get_data_length() + 2 * sizeof_databus;
683    tc->establish_dbuf(long_enough);
684    unsigned char *new_data = tc->new_dbuf;
685    tc->establish_bebuf(long_enough);
686    unsigned char *new_be = tc->new_bebuf;
687
688    if (txn->is_read()) {
689        tc->data_ptr = d_start;
690        tc->address = txn->get_address();
691        tc->byte_enable = txn->get_byte_enable_ptr();
692        tc->length = txn->get_data_length();
693        if (txn->get_byte_enable_ptr() == 0) {
694            // Iterate over transaction creating new byte enables from all-true
695            txn->set_data_length(
696                    loop_word1<DATAWORD, &true_b1<DATAWORD>,
697                               &true_b1<unsigned char>, &false_b1<DATAWORD>,
698                               &false_b1<unsigned char>>(
699                                   txn->get_data_length(), len0, lenN,
700                                   sizeof_databus, d_start, d_end, d, 0,
701                                   new_data, new_be));
702        } else {
703            // iterate over transaction copying byte enables
704            txn->set_data_length(
705                    loop_word1<DATAWORD, &copy_b1<DATAWORD>,
706                               &copy_b1<unsigned char>, &false_b1<DATAWORD>,
707                               &false_b1<unsigned char>>(
708                                   txn->get_data_length(), len0, lenN,
709                                   sizeof_databus, d_start, d_end, d,
710                                   txn->get_byte_enable_ptr() - d_start + d,
711                                   new_data, new_be));
712        }
713    } else {
714        // WRITE
715        if (txn->get_byte_enable_ptr() == 0) {
716            // Iterate over transaction copying data and creating new
717            // byte-enables.
718            txn->set_data_length(
719                    loop_word1<DATAWORD, &copy_d1<DATAWORD>,
720                               &copy_d1<unsigned char>, &false_b1<DATAWORD>,
721                               &false_b1<unsigned char>>(
722                                   txn->get_data_length(), len0, lenN,
723                                   sizeof_databus, d_start, d_end, d, 0,
724                                   new_data, new_be));
725        } else {
726            // Iterate over transaction copying data and byte-enables.
727            txn->set_data_length(
728                    loop_word1<DATAWORD, &copy_db1<DATAWORD>,
729                               &copy_db1<unsigned char>, &false_b1<DATAWORD>,
730                                   &false_b1<unsigned char>>(
731                                   txn->get_data_length(), len0, lenN,
732                                   sizeof_databus, d_start, d_end, d,
733                                   txn->get_byte_enable_ptr() - d_start + d,
734                                   new_data, new_be));
735        }
736    }
737    txn->set_byte_enable_length(txn->get_data_length());
738    txn->set_streaming_width(txn->get_data_length());
739    txn->set_data_ptr(new_data);
740    txn->set_byte_enable_ptr(new_be);
741    txn->set_address(a_aligned);
742}
743
744
745
746///////////////////////////////////////////////////////////////////////////////
747// function set (2): Utilities
748///////////////////////////////////////////////////////////////////////////////
749
750template <class D>
751inline void copy_d2(D *src1, D *src2, D *dest1, D *dest2) { *dest1 = *src1; }
752
753template <class D>
754inline void
755copy_db2(D *src1, D *src2, D *dest1, D *dest2)
756{
757    *dest1 = *src1;
758    *dest2 = *src2;
759}
760
761template <class D>
762inline void
763copy_dbyb2(D *src1, D *src2, D *dest1, D *dest2)
764{
765    if (tlm_bool<D>(*src2))
766        *dest1 = *src1;
767}
768
769template <class D, void COPY(D *src1, D *src2, D *dest1, D *dest2)>
770inline void
771loop_aligned2(D *src1, D *src2, D *dest1, D *dest2, int words,
772        int words_per_bus)
773{
774    // 64BITFIX was int and operands were cast to int.
775    ptrdiff_t src1to2 = (char *)src2 - (char *)src1;
776    // 64BITFIX was int and operands were cast to int.
777    ptrdiff_t dest1to2 = (char *)dest2 - (char *)dest1;
778
779    D *done = src1 + ptrdiff_t(words); // 64BITFIX.
780    D *bus_start = src1;
781    src1 += ptrdiff_t(words_per_bus - 1); // 64BITFIX.
782
783    while (true) {
784        COPY(src1, (D *)(src1to2 + (char *)src1), dest1,
785                (D *)(dest1to2 + (char *)dest1)); // 64BITFIX.
786        dest1++;
787        if ((--src1) < bus_start) {
788            bus_start += ptrdiff_t(words_per_bus); // 64BITFIX.
789            if (bus_start == done)
790                break;
791            src1 = bus_start + ptrdiff_t(words_per_bus - 1); // 64BITFIX.
792        }
793    }
794}
795
796
797///////////////////////////////////////////////////////////////////////////////
798// function set (2): Response
799///////////////////////////////////////////////////////////////////////////////
800
801template <class DATAWORD>
802inline void
803tlm_from_hostendian_aligned(
804        tlm_generic_payload *txn, unsigned int sizeof_databus)
805{
806    int words_per_bus = sizeof_databus / sizeof(DATAWORD);
807    if (words_per_bus == 1)
808        return;
809    int words = (txn->get_data_length()) / sizeof(DATAWORD);
810    tlm_endian_context *tc = txn->template get_extension<tlm_endian_context>();
811
812    if (txn->get_byte_enable_ptr() == 0) {
813        // no byte enables
814        if (txn->is_read()) {
815            // RD without byte enables. Copy data to original buffer.
816            loop_aligned2<DATAWORD, &copy_d2<DATAWORD>>(
817                    (DATAWORD *)(txn->get_data_ptr()), 0,
818                    (DATAWORD *)(tc->data_ptr), 0, words, words_per_bus);
819        }
820    } else {
821        // byte enables present
822        if (txn->is_read()) {
823            // RD with byte enables. Copy data qualified by byte-enables.
824            loop_aligned2<DATAWORD, &copy_dbyb2<DATAWORD>>(
825                    (DATAWORD *)(txn->get_data_ptr()),
826                    (DATAWORD *)(txn->get_byte_enable_ptr()),
827                    (DATAWORD *)(tc->data_ptr), 0, words, words_per_bus);
828        }
829    }
830}
831
832
833///////////////////////////////////////////////////////////////////////////////
834// function set (2): Request
835///////////////////////////////////////////////////////////////////////////////
836
837template <class DATAWORD>
838inline void
839tlm_to_hostendian_aligned(
840        tlm_generic_payload *txn, unsigned int sizeof_databus)
841{
842    tlm_endian_context *tc = establish_context(txn);
843    tc->from_f = &(tlm_from_hostendian_aligned<DATAWORD>);
844    tc->sizeof_databus = sizeof_databus;
845
846    int words_per_bus = sizeof_databus / sizeof(DATAWORD);
847    if (words_per_bus == 1)
848        return;
849    int words = (txn->get_data_length()) / sizeof(DATAWORD);
850
851    DATAWORD *original_be = (DATAWORD *)(txn->get_byte_enable_ptr());
852    DATAWORD *original_data = (DATAWORD *)(txn->get_data_ptr());
853
854    // Always allocate a new data buffer.
855    tc->establish_dbuf(txn->get_data_length());
856    txn->set_data_ptr(tc->new_dbuf);
857
858    if (original_be == 0) {
859        // No byte enables.
860        if (txn->is_write()) {
861            // WR no byte enables. Copy data.
862            loop_aligned2<DATAWORD, &copy_d2<DATAWORD>>(
863                    original_data, 0, (DATAWORD *)(txn->get_data_ptr()), 0,
864                    words, words_per_bus);
865        } else {
866            // RD no byte enables. Save original data pointer.
867            tc->data_ptr = (unsigned char *)original_data;
868        }
869    } else {
870        // Byte enables present.
871        // Allocate a new buffer for them.
872        tc->establish_bebuf(txn->get_data_length());
873        txn->set_byte_enable_ptr(tc->new_bebuf);
874        txn->set_byte_enable_length(txn->get_data_length());
875
876        if (txn->is_write()) {
877            // WR with byte enables. Copy data and BEs.
878            loop_aligned2<DATAWORD, &copy_db2<DATAWORD>>(
879                    original_data, original_be,
880                    (DATAWORD *)(txn->get_data_ptr()),
881                    (DATAWORD *)(txn->get_byte_enable_ptr()),
882                    words, words_per_bus);
883        } else {
884            // RD with byte enables. Save original data pointer.
885            tc->data_ptr = (unsigned char *)original_data;
886            // Copy byte enables to new buffer.
887            loop_aligned2<DATAWORD, &copy_d2<DATAWORD>>(
888                    original_be, 0, (DATAWORD *)(txn->get_byte_enable_ptr()),
889                    0, words, words_per_bus);
890        }
891    }
892}
893
894
895
896///////////////////////////////////////////////////////////////////////////////
897// function set (3): Response
898///////////////////////////////////////////////////////////////////////////////
899
900template <class DATAWORD>
901inline void
902tlm_from_hostendian_single(
903        tlm_generic_payload *txn, unsigned int sizeof_databus)
904{}
905
906
907///////////////////////////////////////////////////////////////////////////////
908// function set (3): Request
909///////////////////////////////////////////////////////////////////////////////
910
911template <class DATAWORD>
912inline void
913tlm_to_hostendian_single(tlm_generic_payload *txn, unsigned int sizeof_databus)
914{
915    tlm_endian_context *tc = establish_context(txn);
916    tc->from_f = &(tlm_from_hostendian_single<DATAWORD>);
917    tc->sizeof_databus = sizeof_databus;
918
919    // Only need to change the address, always safe to work in-place.
920    sc_dt::uint64 mask = sizeof_databus - 1;
921    sc_dt::uint64 a = txn->get_address();
922    txn->set_address((a & ~mask) |
923            (sizeof_databus - (a & mask) - sizeof(DATAWORD)));
924}
925
926
927
928///////////////////////////////////////////////////////////////////////////////
929// helper function which works for all responses
930///////////////////////////////////////////////////////////////////////////////
931
932inline void
933tlm_from_hostendian(tlm_generic_payload *txn)
934{
935    tlm_endian_context *tc = txn->get_extension<tlm_endian_context>();
936    (*(tc->from_f))(txn, tc->sizeof_databus);
937}
938
939}  // namespace tlm
940
941#endif /* __SYSTEMC_EXT_TLM_CORE_2_GENERIC_PAYLOAD_ENDIAN_CONV_HH__ */
942