ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SimpleMemSystem.cpp
Go to the documentation of this file.
1
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
67using namespace etiss;
68using namespace ELFIO;
69
70std::unordered_map<std::string, uint32_t> map_messageCounter;
71uint32_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
79MemSegment::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
104void 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
170void 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
178bool MemSegment::addr_in_range(etiss::uint64 addr) const
179{
180 return ((addr >= start_addr_ && addr <= end_addr_) ? true : false);
181}
182
183bool MemSegment::payload_in_range(etiss::uint64 addr, etiss::uint64 payload_size) const
184{
185 if (addr_in_range(addr))
186 {
187 return (((addr + payload_size - 1) <= end_addr_) ? true : false);
188 }
189 return false;
190}
191
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) {
336 mode |= MemSegment::READ;
337 modestr += "R";
338 }
339 if (seg->get_flags() & PF_W) {
340 mode |= MemSegment::WRITE;
341 modestr += "W";
342 }
343 if (seg->get_flags() & PF_X) {
344 mode |= MemSegment::EXEC;
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
389void 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{
409 {
410 trace_file_dbus_.open(etiss::cfg().get<std::string>("etiss.output_path_prefix", "") + "dBusAccess.csv",
411 std::ios::binary);
412 }
413}
414
415void 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
425etiss::int32 SimpleMemSystem::iread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len)
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
434etiss::int32 SimpleMemSystem::iwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
435{
436 access_error(cpu, addr, len, "ibus write blocked", etiss::ERROR);
437 return RETURNCODE::IBUS_WRITE_ERROR;
438}
439
440static 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
465template <bool write>
466etiss::int32 SimpleMemSystem::dbus_access(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len) {
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
494etiss::int32 SimpleMemSystem::dread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
495{
496 return dbus_access<false>(cpu, addr, buf, len);
497}
498
499etiss::int32 SimpleMemSystem::dwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
500{
501 return dbus_access<true>(cpu, addr, buf, len);
502}
503
504etiss::int32 SimpleMemSystem::dbg_read(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
505{
506 return dread(nullptr, addr, buf, len);
507}
508
509etiss::int32 SimpleMemSystem::dbg_write(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
510{
511 return dwrite(nullptr, addr, buf, len);
512}
513
514extern 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_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
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.
Page Table Entry (PTE) defines the composition of Page Frame Number (PFN) and relavant flags.
Definition Benchmark.h:53
std::string Configuration::get< std::string >(const std::string &key, std::string default_, bool *default_used)
Definition Misc.cpp:255
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()
Definition Misc.cpp:577
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 + ...)
#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