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