test_endian_conv.cpp revision 12855:588919e0e4aa
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/*
22This C++ programme runs single transactions through a single
23endianness conversion function, then through a simple memory model,
24then converts it back.
25Takes the initial memory state as input and provides the final
26memory state as output.
27*/
28
29
30#define BUFFER_SIZE 2048
31
32#include<systemc>
33#include "tlm.h"
34#include<iostream>
35#include<time.h>
36#include <fstream>
37
38using namespace std;
39using namespace tlm;
40using namespace sc_dt;
41
42
43// simple set of types with known sizeof(), for testing //
44template<int SIZE> class dt {
45  char content[SIZE];
46};
47
48
49#define convert(function) \
50  switch(data_width) { \
51    case 1:  function<dt<1> >(&txn,bus_width); break; \
52    case 2:  function<dt<2> >(&txn,bus_width); break; \
53    case 4:  function<dt<4> >(&txn,bus_width); break; \
54    case 8:  function<dt<8> >(&txn,bus_width); break; \
55    case 16:  function<dt<16> >(&txn,bus_width); break; \
56    case 32:  function<dt<32> >(&txn,bus_width); break; \
57    default:  cout << "bad data width\n"; \
58    exit(1); \
59  }
60
61
62// forward declarations - see below
63template<class DATAWORD> inline void
64local_single_tohe(tlm_generic_payload *txn, unsigned int sizeof_databus);
65template<class DATAWORD> inline void
66local_single_fromhe(tlm_generic_payload *txn, unsigned int sizeof_databus);
67
68
69void test_a_conversion(char cmd, tlm_generic_payload &txn, std::ifstream & fin) {
70
71  if(cmd == 'R') txn.set_read();
72  else txn.set_write();
73
74  fin.ignore(10000,'=');
75  uint64 a;
76  fin >> a;
77  txn.set_address(a);
78
79  fin.ignore(10000,'=');
80  int l;
81  fin >> l;
82  txn.set_data_length(l);
83
84  int bus_width;
85  fin.ignore(10000,'=');  fin >> bus_width;
86
87  int data_width;
88  fin.ignore(10000,'=');  fin >> data_width;
89
90  int initiator_offset;
91  fin.ignore(10000,'=');  fin >> initiator_offset;
92
93  unsigned char *original_byte_enable = 0;
94  unsigned char *byte_enable_legible =
95    new unsigned char[txn.get_data_length() + 1];
96  memset(byte_enable_legible, 0, txn.get_data_length() + 1);
97  fin.ignore(10000,'=');
98  for(unsigned b=0; b<txn.get_data_length(); b++) {
99    char tmp; fin >> tmp;
100    if((tmp=='0')||(tmp=='1')||(tmp=='x')) byte_enable_legible[b]=tmp;
101    else break;
102  }
103  if((byte_enable_legible[0] == '1') || (byte_enable_legible[0] == '0')) {
104    txn.set_byte_enable_ptr(new unsigned char[txn.get_data_length()]);
105    txn.set_byte_enable_length(txn.get_data_length());
106    original_byte_enable = txn.get_byte_enable_ptr();
107    for(unsigned int i=0; i<txn.get_data_length(); i++) {
108      if(byte_enable_legible[i] == '0') {
109        txn.get_byte_enable_ptr()[i] = TLM_BYTE_DISABLED;
110      } else if(byte_enable_legible[i] == '1') {
111        txn.get_byte_enable_ptr()[i] = TLM_BYTE_ENABLED;
112      } else {
113        // not enough byte enables
114        txn.set_byte_enable_length(i);
115        break;
116      }
117    }
118  } else {
119    txn.set_byte_enable_ptr(0);
120    txn.set_byte_enable_length(0);
121  }
122
123  int stream_width;
124  fin.ignore(10000,'=');  fin >> stream_width;
125  txn.set_streaming_width(stream_width);
126
127  cout << "enter initiator memory state = ("<< BUFFER_SIZE << " characters)\n";
128  unsigned char initiator_mem[BUFFER_SIZE+1];
129  memset(initiator_mem, 0, BUFFER_SIZE+1);
130  fin.ignore(10000,'=');  fin >> initiator_mem;
131
132  txn.set_data_ptr(initiator_mem + initiator_offset);
133
134  cout << "enter target memory state = ("<< BUFFER_SIZE << " characters)\n";
135  unsigned char target_mem[BUFFER_SIZE+1];
136  memset(target_mem, 0, BUFFER_SIZE+1);
137  fin.ignore(10000,'=');  fin >> target_mem;
138
139  cout << "enter converter choice = (0 => generic, 1 => word, 2 => aligned, 3 => single)\n";
140  int converter;
141  fin.ignore(10000,'=');  fin >> converter;
142
143  cout << "Initiator Intent\n";
144  cout << "  Cmd = " << cmd << endl;
145  cout << "  Addr = " << txn.get_address() << endl;
146  cout << "  Len = " << txn.get_data_length() << endl;
147  cout << "  Bus Width = " << bus_width << endl;
148  cout << "  Data Word = " << data_width << endl;
149#ifdef VERBOSE
150  cout << "  Initiator offset and txn data pointer = " << initiator_offset << ", " << int(txn.get_data_ptr()) << endl;
151  cout << "  Byte enables and byte enable pointer = " << byte_enable_legible << ", " << int(txn.get_byte_enable_ptr()) << endl;
152#else
153  cout << "  Initiator offset = " << initiator_offset << endl;
154  cout << "  Byte enables = " << byte_enable_legible << endl;
155#endif
156  cout << "  Byte enable length = " << txn.get_byte_enable_length() << endl;
157  cout << "  Streaming width = " << txn.get_streaming_width() << endl;
158  cout << "  Initiator memory = " << initiator_mem << endl;
159  cout << "  Target memory = " << target_mem << endl;
160  cout << "  Converter = " << converter << endl << endl;
161
162  // initiator //
163  switch(converter) {
164    case 0:  convert(tlm_to_hostendian_generic); break;
165    case 1:  convert(tlm_to_hostendian_word); break;
166    case 2:  convert(tlm_to_hostendian_aligned); break;
167    case 3:  convert(tlm_to_hostendian_single); break;
168    case 4:  convert(local_single_tohe); break;
169    default:  cout << "no such converter as " << converter << endl;
170    exit(1);
171  }
172
173  cout << "Converted Transaction\n";
174  cout << "  Addr = " << txn.get_address() << endl;
175  cout << "  Len = " << txn.get_data_length() << endl;
176#ifdef VERBOSE
177  cout << "  Txn data pointer = " << int(txn.get_data_ptr()) << endl;
178  if(txn.get_byte_enable_ptr() != 0) {
179    cout << "  Byte enables and byte enable pointer = ";
180    for(unsigned int i=0; i<txn.get_data_length(); i++)
181      cout << (txn.get_byte_enable_ptr()[i] ? '1' : '0');
182    cout << ", " << int(txn.get_byte_enable_ptr()) << endl;
183  }
184#else
185  cout << "  Txn data pointer = " <<
186    (txn.get_data_ptr() == initiator_mem+initiator_offset ? "unchanged" : "changed") << endl;
187  if(txn.get_byte_enable_ptr() != 0) {
188    cout << "  Byte enables and byte enable pointer = ";
189    for(unsigned int i=0; i<txn.get_data_length(); i++)
190      cout << (txn.get_byte_enable_ptr()[i] ? '1' : '0');
191    cout << ", " <<
192     (txn.get_byte_enable_ptr() == original_byte_enable ? "unchanged" : "changed") << endl;
193  }
194#endif
195  cout << "  Byte enable length = " << txn.get_byte_enable_length() << endl;
196  cout << "  Streaming width = " << txn.get_streaming_width() << endl;
197  cout << endl;
198
199  // target //
200  int sw = txn.get_streaming_width();
201  if((txn.get_data_length()/sw)*sw != txn.get_data_length()) {
202    cout << "ERROR: Data length not a multiple of streaming width\n";
203    exit(1);
204  }
205  for(unsigned int ss = 0; ss < txn.get_data_length(); ss += sw) {
206    if(txn.get_byte_enable_ptr() == 0) {
207      // simple transaction can be processed by mem-copy
208      if(txn.is_read())
209        memcpy(ss+txn.get_data_ptr(), target_mem+txn.get_address(), sw);
210      else
211        memcpy(target_mem+txn.get_address(), ss+txn.get_data_ptr(), sw);
212    } else {
213      // complex transaction, byte enables, maybe shorter than data
214      int bel = txn.get_byte_enable_length();
215      if(txn.is_read()) {
216        for(int j=0; j<sw; j++) {
217          if(txn.get_byte_enable_ptr()[(ss+j) % bel])
218            (txn.get_data_ptr())[ss+j] = target_mem[j+txn.get_address()];
219        }
220      } else {
221        for(int j=0; j<sw; j++) {
222          if(txn.get_byte_enable_ptr()[(ss+j) % bel])
223            target_mem[j+txn.get_address()] = (txn.get_data_ptr())[ss+j];
224        }
225      }
226    }
227  }
228
229  // initiator again //
230  if((rand() & 0x100) && (converter < 4)) {
231#ifdef VERBOSE
232    cout << "using single entry point for response\n";
233#endif
234    tlm_from_hostendian(&txn);
235  } else {
236#ifdef VERBOSE
237    cout << "using specific entry point for response\n";
238#endif
239    switch(converter) {
240      case 0:  convert(tlm_from_hostendian_generic); break;
241      case 1:  convert(tlm_from_hostendian_word); break;
242      case 2:  convert(tlm_from_hostendian_aligned); break;
243      case 3:  convert(tlm_from_hostendian_single); break;
244      case 4:  convert(local_single_fromhe); break;
245      default:  cout << "no such converter as " << converter << endl;
246      exit(1);
247    }
248  }
249
250  // print the results //
251  cout << "Memory States after Transaction\n";
252  cout << "  initiator = " << initiator_mem << endl;
253  cout << "  target = " << target_mem << endl << endl;
254
255  // clean up
256  delete [] byte_enable_legible;
257  if(original_byte_enable != 0) delete [] original_byte_enable;
258}
259
260
261void pool_status() {
262  cout << "Pool status: ";
263  tlm_endian_context *f = global_tlm_endian_context_pool.first;
264  while(f!=0) {
265    cout << "(" << f->dbuf_size << "," << f->bebuf_size << ") ";
266    f = f->next;
267  }
268  cout << endl;
269}
270
271
272int sc_main(int argc, char **argv) {
273
274  #include <string>
275
276  // no command line parameters //
277  // get everything from stdin and build transaction object //
278  cout << "\nTLM-2 Endianness Conversion Helper Functions Tester\n";
279  cout << "March 2008\n";
280  cout << "January 2012 Updated to read from endian_conv/input.txt\n\n";
281
282  std::string filename;
283  std::ifstream fin;
284
285  if (1 == argc)
286      filename = "endian_conv/input.txt";
287  else if (2 == argc)
288      filename = argv[1];
289  else {
290    std::cerr << "Too many input arguments" << std::endl;
291	return 1;
292  }
293
294
295  fin.open(filename.c_str(), ios_base::in);
296  if (!fin) {
297    std::cerr << "Could not open input filename " << filename << std::endl;
298    return 1;
299  }
300
301  srand(time(NULL));
302  const int nr_txns_in_pool = 7;
303  const int txn_to_cycle = 4;
304  tlm_generic_payload *txns[nr_txns_in_pool];
305  for(int i=0; i < nr_txns_in_pool; i++) txns[i] = new tlm_generic_payload;
306
307  for(int i=0; true; i = ((i+1) % nr_txns_in_pool)) {
308    cout << i << " enter {R|W}, addr=a, len=l, bus width=b, word width=w, initiator offset=i, be={x|01}, stream width=s\n";
309    pool_status();
310    char command;
311    fin >> command;
312    if(fin.eof()) break;
313    if((command != 'R') && (command != 'W')) break;
314    if(i==txn_to_cycle) {
315      // should cause 2 extensions to get pushed to the pool once they've been used
316      delete txns[i];
317      pool_status();
318      delete txns[i-1];
319      pool_status();
320      // and popped back later when these new ones establish contexts
321      txns[i] = new tlm_generic_payload;
322      txns[i-1] = new tlm_generic_payload;
323      pool_status();
324    }
325    test_a_conversion(command, *txns[i], fin);
326  }
327
328  for(int i=0; i < nr_txns_in_pool; i++) {
329    delete txns[i];
330    pool_status();
331  }
332  return 0;
333}
334
335
336// converter functions for non-aligned single transactions
337// included here for validation only.  not designed for general use.
338
339unsigned char *original_dptr;
340sc_dt::uint64 original_addr;
341
342template<class DATAWORD> inline void
343local_single_tohe(tlm_generic_payload *txn, unsigned int sizeof_databus) {
344  if(txn->get_data_length() != sizeof(DATAWORD)) {
345    cout << "Error:  local_single_tohe() wrongly called\n";
346    exit(1);
347  }
348
349  sc_dt::uint64 mask = sizeof_databus - 1;
350
351  // set up new buffers, length and address
352  if(sizeof(DATAWORD) > sizeof_databus)
353    txn->set_data_length(sizeof_databus + sizeof(DATAWORD));
354  else
355    txn->set_data_length(2 * sizeof_databus);
356  txn->set_streaming_width(txn->get_data_length());
357  unsigned char *new_data = new unsigned char[txn->get_data_length()];
358  unsigned char *new_be = new unsigned char[txn->get_data_length()];
359  // drive all BEs to zero initially
360  for(unsigned int i=0; i<txn->get_data_length(); i++) new_be[i] = 0;
361  sc_dt::uint64 new_addr = txn->get_address() & ~mask;
362
363  // Comments assume arithmetic mode big endian initiator modelled on little
364  // endian host (but the functionality is the same for LE initiator on BE host)
365
366  // iterate over the initiator word byte by byte, MSB first
367  unsigned char *curr_d = txn->get_data_ptr() + sizeof(DATAWORD) - 1;
368  unsigned char *curr_b = txn->get_byte_enable_ptr() + sizeof(DATAWORD) - 1;
369
370  // initiator intent is to put the MSB at the address given in the transaction
371  sc_dt::uint64 curr_a = txn->get_address();
372
373  // iterate copying data and byte enables
374  for( ; curr_d >= txn->get_data_ptr(); curr_d--, curr_b--, curr_a++) {
375    // work out the address in the TLM interpretation of the initiator's intent
376    sc_dt::uint64 he_addr = curr_a ^ mask;
377    int idx = he_addr - new_addr;
378    if(txn->is_write()) new_data[idx] = *curr_d;
379    if(txn->get_byte_enable_ptr() == 0) new_be[idx] = 1;
380    else new_be[idx] = *curr_b;
381  }
382
383  // replace the pointers
384  original_dptr = txn->get_data_ptr();
385  txn->set_data_ptr(new_data);
386  txn->set_byte_enable_ptr(new_be);
387  txn->set_byte_enable_length(txn->get_data_length());
388  original_addr = txn->get_address();
389  txn->set_address(new_addr);
390}
391
392
393template<class DATAWORD> inline void
394local_single_fromhe(tlm_generic_payload *txn, unsigned int sizeof_databus) {
395  sc_dt::uint64 mask = sizeof_databus - 1;
396
397  // Comments assume arithmetic mode big endian initiator modelled on little
398  // endian host (but the functionality is the same for LE initiator on BE host)
399
400  // iterate over the initiator word byte by byte, MSB first
401  unsigned char *curr_d = original_dptr + sizeof(DATAWORD) - 1;
402
403  // initiator intent is to put the MSB at the address given in the transaction
404  sc_dt::uint64 curr_a = original_addr;
405
406  // iterate copying data and byte enables
407  for( ; curr_d >= original_dptr; curr_d--, curr_a++) {
408    // work out the address in the TLM interpretation of the initiator's intent
409    sc_dt::uint64 he_addr = curr_a ^ mask;
410    int idx = he_addr - txn->get_address();
411    if((txn->is_read()) && (txn->get_byte_enable_ptr()[idx] != 0))
412      *curr_d = txn->get_data_ptr()[idx];
413  }
414
415  // clean up
416  delete [] txn->get_data_ptr();
417  delete [] txn->get_byte_enable_ptr();
418}
419
420