ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
SimpleMemSystem.cpp
Go to the documentation of this file.
1 
53 #include "etiss/SimpleMemSystem.h"
54 #include "etiss/CPUArch.h"
55 #include "etiss/Misc.h"
56 #include <cstring>
57 #include <iostream>
58 #include <fstream>
59 #include <unordered_map>
60 
61 #include "elfio/elfio.hpp"
62 #include <memory>
63 
64 #define ARMv6M_DEBUG_PRINT 0
65 #define MAX_MEMSEGS 99
66 
67 using namespace etiss;
68 using namespace ELFIO;
69 
70 std::unordered_map<std::string, uint32_t> map_messageCounter;
71 uint32_t printMessage(std::string key, std::string message, uint32_t maxCount)
72 {
73  uint32_t count = map_messageCounter[key]++;
74  if (count < maxCount) // print only the first X messages of this type
75  std::cout << message << " (" << (count + 1) << "x)" << std::endl;
76  return count;
77 }
78 
79 MemSegment::MemSegment(etiss::uint64 start_addr, etiss::uint64 size, access_t mode, const std::string name,
80  etiss::uint8 *mem, std::string initString, bool InitEleSet, uint64_t randomRoot)
81  : name_(name), start_addr_(start_addr), end_addr_(start_addr + size - 1), size_(size), mode_(mode)
82 {
83  if (mem)
84  { // use reserved memory
85  mem_ = mem;
86  }
87  else
88  {
89  mem_ = new etiss::uint8[size];
90  if (InitEleSet)
91  {
92  memInit(initString, randomRoot);
93  }
94  else
95  {
96  std::stringstream memMsg;
97  memMsg << "The memory segment is allocated uninitialized with length 0x" << std::hex << size_ << " !";
98  etiss::log(etiss::INFO, memMsg.str());
99  }
100  self_allocated_ = true;
101  }
102 }
103 
104 void MemSegment::memInit(std::string initString, uint64_t randomRoot) {
105  std::stringstream memMsg;
106 
107  if (initString.find("0x") == 0)
108  {
109  memMsg << "The memory segment is initialized with 0x" << std::hex << size_ << " elements with hex value: " << initString;
110  etiss::log(etiss::INFO, memMsg.str());
111 
112  // actual conversion from hex string to corresponding hex val
113  initString.erase(initString.begin(),initString.begin()+2);
114  const char* dataPtr;
115  size_t j{0};
116 
117  for (etiss::uint64 i = 0; i < size_; ++i)
118  {
119  if (j != (initString.length() - 1))
120  {
121  dataPtr = initString.substr(j, 2).c_str();
122  }
123  else
124  {
125  dataPtr = initString.substr(j, 1).c_str();
126  }
127 
128  j = (j + 2 <= initString.length() - 1) ? j + 2 : 0;
129 
130  try
131  {
132  uint8_t hexVal = static_cast<uint8_t>(std::stoi(dataPtr, 0 ,16));
133  mem_[i] = hexVal;
134  }
135  catch (std::invalid_argument const& exp)
136  {
137  memMsg << "\n Hex Value MemSegment input is erronous (typo?) at " << exp.what();
138  etiss::log(etiss::FATALERROR, memMsg.str());
139  }
140  }
141  }
142 
143  else if (initString.find("random") == 0 || initString.find("RANDOM") == 0)
144  {
145  memMsg << "The memory segment is initialized with 0x" << std::hex << size_ << " random bytes and root: " << randomRoot;
146  etiss::log(etiss::INFO, memMsg.str());
147 
148  static std::default_random_engine generator{randomRoot};
149  std::uniform_int_distribution<int> random_char_{ 0, 255 };
150  for (etiss::uint64 i = 0; i < size_; ++i)
151  {
152  mem_[i] = random_char_(generator);
153  }
154 
155  }
156 
157  else
158  {
159  memMsg << "The memory segment is initialized with 0x" << std::hex << size_ << " elements with the string: " << initString;
160  etiss::log(etiss::INFO, memMsg.str());
161 
162  const char* data = initString.c_str();
163  for (etiss::uint64 i = 0; i < size_; ++i)
164  {
165  mem_[i] = data[i%strlen(data)];
166  }
167  }
168 }
169 
170 void MemSegment::load(const void *data, size_t offset, size_t file_size_bytes)
171 {
172  if (data != nullptr && (offset + file_size_bytes) <= size_)
173  {
174  memcpy(mem_ + offset, data, file_size_bytes);
175  }
176 }
177 
179 {
180  return ((addr >= start_addr_ && addr <= end_addr_) ? true : false);
181 }
182 
184 {
185  if (addr_in_range(addr))
186  {
187  return (((addr + payload_size - 1) <= end_addr_) ? true : false);
188  }
189  return false;
190 }
191 
193  load_segments();
194  load_elf();
195  std::sort(msegs_.begin(), msegs_.end(), [](std::unique_ptr<MemSegment> & a, std::unique_ptr<MemSegment> & b) {return a->start_addr_ < b->start_addr_;});
196 }
197 
199  std::stringstream ss;
200 
201  for (int i = 0; i < MAX_MEMSEGS; ++i) {
202  ss << "simple_mem_system.memseg_origin_" << std::setw(2) << std::setfill('0') << i;
203  uint64_t origin = etiss::cfg().get<uint64_t>(ss.str(), -1);
204  std::stringstream().swap(ss);
205 
206  ss << "simple_mem_system.memseg_length_" << std::setw(2) << std::setfill('0') << i;
207  uint64_t length = etiss::cfg().get<uint64_t>(ss.str(), -1);
208  if (length == 0) {
209  etiss::log(etiss::FATALERROR, "Empty memsegs are not allowed!");
210  }
211  std::stringstream().swap(ss);
212 
213  ss << "simple_mem_system.memseg_initelement_" << std::setw(2) << std::setfill('0') << i;
214  std::string initString = etiss::cfg().get<std::string>(ss.str(), "");
215  bool initEleSet = etiss::cfg().isSet(ss.str());
216  std::stringstream().swap(ss);
217 
218  ss << "simple_mem_system.memseg_initelement_random_root_" << std::setw(2) << std::setfill('0') << i;
219  uint64_t randomRoot = etiss::cfg().get<uint64_t>(ss.str(), 0);
220  std::stringstream().swap(ss);
221 
222  ss << "simple_mem_system.memseg_image_" << std::setw(2) << std::setfill('0') << i;
223  std::string image = etiss::cfg().get<std::string>(ss.str(), "");
224  std::stringstream().swap(ss);
225 
226  ss << "simple_mem_system.memseg_mode_" << std::setw(2) << std::setfill('0') << i;
227  std::string mode = etiss::cfg().get<std::string>(ss.str(), "");
228  std::stringstream().swap(ss);
229 
230  if (origin != (etiss::uint64) -1 && length != (etiss::uint64) -1) {
232 
233  int access = MemSegment::UNSET;
234  std::string modestr = "";
235  if (mode.find('R') != mode.npos) {
236  access |= MemSegment::READ;
237  modestr += "R";
238  }
239  if (mode.find('W') != mode.npos) {
240  access |= MemSegment::WRITE;
241  modestr += "W";
242  }
243  if (mode.find('X') != mode.npos) {
244  access |= MemSegment::EXEC;
245  modestr += "X";
246  }
247 
248  std::stringstream sname;
249  sname << i + 1 << " - " << modestr
250  << "[0x" << std::hex << std::setfill('0') << std::setw(sizeof(etiss::uint64) * 2) << origin + length - 1 << " - "
251  << "0x" << std::hex << std::setfill('0') << std::setw(sizeof(etiss::uint64) * 2) << origin << "]";
252 
253  etiss::uint8 *buf = nullptr;
254  size_t fsize = 0;
255 
256 
257  if (image != "") {
258  std::ifstream ifs(image, std::ifstream::binary | std::ifstream::ate);
259  if (!ifs) {
260  std::stringstream msg;
261  msg << "Error during read of segment image file " << image << "!";
262  etiss::log(etiss::FATALERROR, msg.str());
263  }
264  fsize = ifs.tellg();
265  ifs.seekg(0, std::ifstream::beg);
266 
267  buf = new etiss::uint8[fsize];
268 
269  ifs.read((char*)buf, fsize);
270 
271  std::stringstream mem_msg;
272  mem_msg << "The memory segment " << i << " is initialized with 0x" << std::hex << length << " bytes from input_image !";
273  etiss::log(etiss::INFO, mem_msg.str());
274  }
275 
276  auto mseg = std::make_unique<MemSegment>(origin, length, static_cast<MemSegment::access_t>(access), sname.str(),
277  buf, initString, initEleSet, randomRoot);
278  add_memsegment(mseg, buf, fsize);
279  delete[] buf;
280  }
281  }
282 }
283 
285 {
286  if (!etiss::cfg().isSet("vp.elf_file")) return;
287 
288  std::string elf_file = etiss::cfg().get<std::string>("vp.elf_file", "");
289 
290  ELFIO::elfio reader;
291 
292  if (!reader.load(elf_file))
293  {
294  etiss::log(etiss::FATALERROR, "ELF reader could not process file");
295  }
296 
297  if (etiss::cfg().isSet("arch.cpu")) {
298  std::stringstream ss;
299  ss << "Assuming CPU architecture " << etiss::cfg().get<std::string>("arch.cpu", "") << " as set in configuration file. ELF architecture field will be ignored";
300  etiss::log(etiss::INFO, ss.str());
301  } else {
302  // set architecture automatically
303  if (reader.get_machine() == EM_RISCV)
304  {
305  if ((reader.get_class() == ELFCLASS64)) {
306  etiss::cfg().set<std::string>("arch.cpu", "RV64IMACFD"); // RISCV and OR1K work as well
307  } else if ((reader.get_class() == ELFCLASS32)) {
308  etiss::cfg().set<std::string>("arch.cpu", "RV32IMACFD");
309  // add conditions
310  } else {
311  etiss::log(etiss::FATALERROR, "System architecture is neither 64 nor 32 bit!");
312  }
313  }
314  else
315  {
316  std::stringstream ss;
317  ss << "Target architecture with code 0x" << std::hex << std::setw(2) << std::setfill('0') << reader.get_machine() << " was not automatically recognized, please set the arch.cpu parameter manually!";
318  etiss::log(etiss::FATALERROR, ss.str());
319  }
320  std::stringstream ss;
321  ss << "Set ETISS architecture to " << etiss::cfg().get<std::string>("arch.cpu", "") << " as specified in ELF-file.";
322  etiss::log(etiss::INFO, ss.str());
323  }
324 
325  for (auto &seg : reader.segments)
326  {
327  etiss::uint64 start_addr = seg->get_physical_address();
328  etiss::uint64 size = seg->get_memory_size();
329  if (size == 0) continue;
330  size_t file_size = seg->get_file_size();
331  if (seg->get_type() != PT_LOAD) continue;
332 
333  int mode = 0;
334  std::string modestr = "";
335  if (seg->get_flags() & PF_R) {
337  modestr += "R";
338  }
339  if (seg->get_flags() & PF_W) {
341  modestr += "W";
342  }
343  if (seg->get_flags() & PF_X) {
345  modestr += "X";
346  }
347 
348  std::stringstream sname;
349  sname << seg->get_index() << " - " << modestr
350  << "[0x" << std::hex << std::setfill('0') << std::setw(sizeof(etiss::uint64) * 2) << start_addr << " - "
351  << "0x" << std::hex << std::setfill('0') << std::setw(sizeof(etiss::uint64) * 2) << start_addr + size - 1 << "]";
352 
353  auto mseg_it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(start_addr, size));
354 
355  if (mseg_it != msegs_.end()) {
356  auto & mseg = *mseg_it;
357 
358  mseg->name_ = sname.str();
359  mseg->mode_ = static_cast<MemSegment::access_t>(mode);
360 
361  mseg->load(seg->get_data(), start_addr - mseg->start_addr_, file_size);
362 
363  std::stringstream msg;
364  msg << "Initialized the memory segment " << mseg->name_ << " from ELF-file";
365  etiss::log(etiss::INFO, msg.str());
366 
367  continue;
368  }
369 
370  std::stringstream msg;
371  msg << "Found no matching memory segments at 0x" << std::hex << std::setfill('0') << std::setw(8) << start_addr;
372 
374  msg << "! As you turned on error_on_seg_mismatch, ETISS will now terminate.";
375  etiss::log(etiss::FATALERROR, msg.str());
376  } else {
377  msg << ", creating one. WARNING: the segment will be created with the size information present in the ELF-file, the resulting segment may be too small to fit dynamic data (cache, heap)!";
378  etiss::log(etiss::WARNING, msg.str());
379  }
380 
381  auto mseg = std::make_unique<MemSegment>(start_addr, size, static_cast<MemSegment::access_t>(mode), sname.str());
382  add_memsegment(mseg, seg->get_data(), file_size);
383  }
384 
385  // read start or rather program boot address from ELF
386  start_addr_ = reader.get_entry();
387 }
388 
389 void SimpleMemSystem::add_memsegment(std::unique_ptr<MemSegment>& mseg, const void *raw_data, size_t file_size_bytes)
390 {
391  std::stringstream msg;
392  msg << "New Memory segment added: " << mseg->name_;
393  etiss::log(etiss::INFO, msg.str().c_str());
394 
395  mseg->load(raw_data, 0, file_size_bytes);
396 
397  msegs_.push_back(std::move(mseg));
398 }
399 
401  print_ibus_access_(etiss::cfg().get<bool>("simple_mem_system.print_ibus_access", false)),
402  print_dbus_access_(etiss::cfg().get<bool>("simple_mem_system.print_dbus_access", false)),
403  print_dbgbus_access_(etiss::cfg().get<bool>("simple_mem_system.print_dbgbus_access", false)),
404  print_to_file_(etiss::cfg().get<bool>("simple_mem_system.print_to_file", false)),
405  error_on_seg_mismatch_(etiss::cfg().get<bool>("simple_mem_system.error_on_seg_mismatch", false)),
406  message_max_cnt_(etiss::cfg().get<int>("simple_mem_system.message_max_cnt", 100))
407 {
408  if (print_dbus_access_)
409  {
410  trace_file_dbus_.open(etiss::cfg().get<std::string>("etiss.output_path_prefix", "") + "dBusAccess.csv",
411  std::ios::binary);
412  }
413 }
414 
415 void access_error(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, std::string error, etiss::Verbosity verbosity) {
416  uint64 pc = cpu ? cpu->instructionPointer : 0;
417  std::stringstream ss;
418 
419  ss << error << ", PC = 0x" << std::hex << std::setw(8) << std::setfill('0') << pc
420  << ", address 0x" << std::hex << std::setw(8) << std::setfill('0') << addr << ", length " << len;
421 
422  etiss::log(verbosity, ss.str());
423 }
424 
426 {
427  auto it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(addr, len));
428  if (it != msegs_.end()) return RETURNCODE::NOERROR;
429 
430  access_error(cpu, addr, len, "ibus read error", etiss::ERROR);
431  return RETURNCODE::IBUS_READ_ERROR;
432 }
433 
435 {
436  access_error(cpu, addr, len, "ibus write blocked", etiss::ERROR);
437  return RETURNCODE::IBUS_WRITE_ERROR;
438 }
439 
440 static void trace(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, bool isWrite, bool toFile, std::ofstream &file)
441 {
442  uint64 time = 0;
443  uint64 pc = 0;
444 
445  if (cpu)
446  {
447  time = cpu->cpuTime_ps;
448  pc = cpu->instructionPointer;
449  }
450 
451  std::stringstream text;
452  text << time << ";" // time
453  << std::setw(8) << std::setfill('0') << std::hex // (formatting)
454  << pc << ";" // pc
455  << (isWrite ? "w" : "r") << ";" // type
456  << addr << ";" // addr
457  << len << std::endl;
458 
459  if (toFile)
460  file << text.str();
461  else
462  std::cout << text.str();
463 }
464 
465 template <bool write>
467  auto mseg_it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(addr, len));
468 
469  if (mseg_it != msegs_.end()) {
470  auto & mseg = *mseg_it;
472 
473  if (!(mseg->mode_ & access)) {
474  access_error(cpu, addr, len, std::string("dbus ") + (write ? "write" : "read") + " forbidden", etiss::WARNING);
475  }
476 
477  size_t offset = addr - mseg->start_addr_;
478 
479  void * dest = write ? mseg->mem_ + offset : buf;
480  const void * src = write ? buf : mseg->mem_ + offset;
481 
482  memcpy(dest, src, len);
483 
484  if (print_dbus_access_) trace(cpu, addr, len, write, print_to_file_, trace_file_dbus_);
485 
486  return RETURNCODE::NOERROR;
487  }
488 
489  access_error(cpu, addr, len, std::string("dbus ") + (write ? "write" : "read") + " error", etiss::ERROR);
490 
491  return write ? RETURNCODE::DBUS_WRITE_ERROR : RETURNCODE::DBUS_READ_ERROR;
492 }
493 
495 {
496  return dbus_access<false>(cpu, addr, buf, len);
497 }
498 
500 {
501  return dbus_access<true>(cpu, addr, buf, len);
502 }
503 
505 {
506  return dread(nullptr, addr, buf, len);
507 }
508 
510 {
511  return dwrite(nullptr, addr, buf, len);
512 }
513 
514 extern void global_sync_time(uint64 time_ps);
516 {
517  // std::cout << "CPU time: " << cpu -> cpuTime_ps << "ps" << std::endl;
518  // global_sync_time(cpu->cpuTime_ps);
519 }
etiss_uint8 uint8
Definition: 386-GCC.h:76
etiss_int32 int32
Definition: 386-GCC.h:81
etiss_uint32 uint32
Definition: 386-GCC.h:80
etiss_uint64 uint64
Definition: 386-GCC.h:82
contains neccesary interfaces for instruction translation.
general configuration and logging
void access_error(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, std::string error, etiss::Verbosity verbosity)
#define MAX_MEMSEGS
std::unordered_map< std::string, uint32_t > map_messageCounter
void global_sync_time(uint64 time_ps)
uint32_t printMessage(std::string key, std::string message, uint32_t maxCount)
static void trace(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, bool isWrite, bool toFile, std::ofstream &file)
simple test system implementation
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
__device__ __2f16 b
__device__ int
static __inline__ uint32_t
Definition: arm_cde.h:25
static __inline__ uint64_t
Definition: arm_cde.h:31
static __inline__ uint8_t
Definition: arm_mve.h:323
bool isSet(std::string val)
return true if the value of an configuration key has been set
Definition: Misc.cpp:382
bool set(const std::string &key, T value)
template function to set the value of a configuration key.
Definition: Misc.h:372
T get(const std::string &key, T default_, bool *default_used=0)
template function to read the value of a configuration key.
Definition: Misc.h:349
bool payload_in_range(etiss::uint64 addr, etiss::uint64 payload_size) const
void memInit(std::string initString, uint64_t randomRoot=0)
bool addr_in_range(etiss::uint64 addr) const
const etiss::uint64 start_addr_
const etiss::uint64 end_addr_
void load(const void *data, size_t offset, size_t file_size_bytes)
MemSegment(etiss::uint64 start_addr, etiss::uint64 size, access_t mode, const std::string name, etiss::uint8 *mem=nullptr, std::string initString="", bool InitEleSet=false, uint64_t randomRoot=0)
Constructor of Memory Segment.
const etiss::uint64 size_
etiss::uint8 * mem_
std::map< etiss::uint64, etiss::uint64 > configured_address_spaces_
etiss::int32 dread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
Data read operation.
std::vector< std::unique_ptr< MemSegment > > msegs_
etiss::int32 dwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
Data write operation.
etiss::int32 dbus_access(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
void add_memsegment(std::unique_ptr< MemSegment > &mseg, const void *raw_data, size_t file_size_bytes)
etiss::int32 dbg_write(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
Debug write operation.
etiss::int32 dbg_read(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
Debug read operation.
etiss::int32 iread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len)
Instruction read operation.
etiss::int32 iwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
Instruction write operation.
std::ofstream trace_file_dbus_
void syncTime(ETISS_CPU *cpu)
Synchronize simulation time.
MM_EXPORT const int32_t NOERROR
Page Table Entry (PTE) defines the composition of Page Frame Number (PFN) and relavant flags.
Definition: Benchmark.h:53
Verbosity
Enumeration type for the log levels.
Definition: Misc.h:124
@ INFO
Definition: Misc.h:129
@ WARNING
Definition: Misc.h:128
@ ERROR
Definition: Misc.h:127
@ FATALERROR
Definition: Misc.h:126
Configuration & cfg(const std::string &cfgName)
Get reference of the global ETISS configuration object.
Definition: Misc.cpp:560
Verbosity & verbosity()
Get log level reference.
Definition: Misc.cpp:120
void log(Verbosity level, std::string msg)
write log message at the given level.
Definition: Misc.cpp:125
float __ovld __cnfn length(float p)
Return the length of vector p, i.e., sqrt(p.x2 + p.y 2 + ...)
void sort(I begin, I end, const Pred &pred)
Definition: pugixml.cpp:6511
#define false
Definition: stdbool.h:17
#define bool
Definition: stdbool.h:15
basic cpu state structure needed for execution of any cpu architecture.
Definition: CPU.h:89
etiss_uint64 instructionPointer
pointer to next instruction.
Definition: CPU.h:92
etiss_uint64 cpuTime_ps
simulation time of cpu
Definition: CPU.h:97
#define exp(__x)
Definition: tgmath.h:431