ETISS 0.11.2
ExtendableTranslatingInstructionSetSimulator(version0.11.2)
Loading...
Searching...
No Matches
SimpleMemSystem.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2//
3// This file is part of ETISS. It is licensed under the BSD 3-Clause License; you may not use this file except in
4// compliance with the License. You should have received a copy of the license along with this project. If not, see the
5// LICENSE file.
15#include "etiss/jit/CPU.h"
16#include "etiss/Misc.h"
17#include <cstring>
18#include <iostream>
19#include <fstream>
20#include <unordered_map>
21
22#include "elfio/elfio.hpp"
23#include <memory>
24#include "etiss/Format.h"
25#include <iomanip>
26
27#define ARMv6M_DEBUG_PRINT 0
28#define MAX_MEMSEGS 99
29
30using namespace etiss;
31using namespace ELFIO;
32
33std::unordered_map<std::string, uint32_t> map_messageCounter;
34uint32_t printMessage(std::string key, std::string message, uint32_t maxCount)
35{
36 uint32_t count = map_messageCounter[key]++;
37 if (count < maxCount) // print only the first X messages of this type
38 std::cout << message << " (" << (count + 1) << "x)" << std::endl;
39 return count;
40}
41
42MemSegment::MemSegment(etiss::uint64 start_addr, etiss::uint64 size, access_t mode, const std::string name,
43 etiss::uint8 *mem, std::string initString, bool InitEleSet, uint64_t randomRoot)
44 : name_(name), start_addr_(start_addr), end_addr_(start_addr + size - 1), size_(size), mode_(mode)
45{
46 if (mem)
47 { // use reserved memory
48 mem_ = mem;
49 }
50 else
51 {
52 mem_ = new etiss::uint8[size];
53 if (InitEleSet)
54 {
55 memInit(initString, randomRoot);
56 }
57 else
58 {
60 etiss::fmt::format("The memory segment is allocated uninitialized with length {:#x} !", size_));
61 }
62 self_allocated_ = true;
63 }
64}
65
66void MemSegment::memInit(std::string initString, uint64_t randomRoot)
67{
68 if (initString.find("0x") == 0)
69 {
71 etiss::fmt::format("The memory segment is initialized with {:#x} elements with value: 0x{:s}", size_,
72 initString));
73
74 // actual conversion from hex string to corresponding hex val
75 initString.erase(initString.begin(), initString.begin() + 2);
76 size_t j{ 0 };
77
78 for (etiss::uint64 i = 0; i < size_; ++i)
79 {
80 std::string hexStr = "";
81 if (j != (initString.length() - 1))
82 {
83 hexStr = initString.substr(j, 2);
84 }
85 else
86 {
87 hexStr = initString.substr(j, 1);
88 }
89
90 j = (j + 2 <= initString.length() - 1) ? j + 2 : 0;
91
92 try
93 {
94 uint8_t hexVal = static_cast<uint8_t>(std::stoi(hexStr.c_str(), 0, 16));
95 mem_[i] = hexVal;
96 }
97 catch (std::invalid_argument const &exp)
98 {
99 std::stringstream memMsg;
100 memMsg << "\n Hex Value MemSegment input is erronous (typo?) at " << exp.what();
101 etiss::log(etiss::FATALERROR, memMsg.str());
102 break;
103 }
104 }
105 }
106 else if (initString.find("random") == 0 || initString.find("RANDOM") == 0)
107 {
109 etiss::fmt::format("The memory segment is initialized with {:#x} random bytes and root: {:d}", size_,
110 randomRoot));
111
112 static std::default_random_engine generator{ randomRoot };
113 std::uniform_int_distribution<int> random_char_{ 0, 255 };
114 for (etiss::uint64 i = 0; i < size_; ++i)
115 {
116 mem_[i] = random_char_(generator);
117 }
118 }
119
120 else
121 {
123 etiss::fmt::format("The memory segment is initialized with {:#x} elements with the string: {:s}",
124 size_, initString));
125
126 const char *data = initString.c_str();
127 for (etiss::uint64 i = 0; i < size_; ++i)
128 {
129 mem_[i] = data[i % strlen(data)];
130 }
131 }
132}
133
134void MemSegment::load(const void *data, size_t offset, size_t file_size_bytes)
135{
136 if (data != nullptr && (offset + file_size_bytes) <= size_)
137 {
138 memcpy(mem_ + offset, data, file_size_bytes);
139 }
140}
141
142bool MemSegment::addr_in_range(etiss::uint64 addr) const
143{
144 return ((addr >= start_addr_ && addr <= end_addr_) ? true : false);
145}
146
147bool MemSegment::payload_in_range(etiss::uint64 addr, etiss::uint64 payload_size) const
148{
149 if (addr_in_range(addr))
150 {
151 return (((addr + payload_size - 1) <= end_addr_) ? true : false);
152 }
153 return false;
154}
155
157{
159 load_elf();
160 std::sort(msegs_.begin(), msegs_.end(), [](std::unique_ptr<MemSegment> &a, std::unique_ptr<MemSegment> &b)
161 { return a->start_addr_ < b->start_addr_; });
162}
163
165{
166 std::stringstream ss;
167
168 for (int i = 0; i < MAX_MEMSEGS; ++i)
169 {
170 uint64_t origin =
171 etiss::cfg().get<uint64_t>(etiss::fmt::format("simple_mem_system.memseg_origin_{:02d}", i), -1);
172
174 etiss::cfg().get<uint64_t>(etiss::fmt::format("simple_mem_system.memseg_length_{:02d}", i), -1);
175 if (length == 0)
176 {
177 etiss::log(etiss::FATALERROR, "Empty memsegs are not allowed!");
178 }
179
180 auto cfgstr = etiss::fmt::format("simple_mem_system.memseg_initelement_{:02d}", i);
181 std::string initString = etiss::cfg().get<std::string>(cfgstr, "");
182 bool initEleSet = etiss::cfg().isSet(cfgstr);
183
184 uint64_t randomRoot = etiss::cfg().get<uint64_t>(
185 etiss::fmt::format("simple_mem_system.memseg_initelement_random_root_{:02d}", i), 0);
186
187 std::string image =
188 etiss::cfg().get<std::string>(etiss::fmt::format("simple_mem_system.memseg_image_{:02d}", i), "");
189
190 std::string mode =
191 etiss::cfg().get<std::string>(etiss::fmt::format("simple_mem_system.memseg_mode_{:02d}", i), "");
192
193 if (origin != (etiss::uint64)-1 && length != (etiss::uint64)-1)
194 {
196
197 int access = MemSegment::UNSET;
198 std::string modestr = "";
199 if (mode.find('R') != mode.npos)
200 {
201 access |= MemSegment::READ;
202 modestr += "R";
203 }
204 if (mode.find('W') != mode.npos)
205 {
206 access |= MemSegment::WRITE;
207 modestr += "W";
208 }
209 if (mode.find('X') != mode.npos)
210 {
211 access |= MemSegment::EXEC;
212 modestr += "X";
213 }
214
215 std::string sname =
216 etiss::fmt::format("{} - {:s}[{:#016x} - {:#016x}]", i + 1, modestr, origin + length - 1, origin);
217
218 etiss::uint8 *buf = nullptr;
219 size_t fsize = 0;
220
221 if (image != "")
222 {
223 std::ifstream ifs(image, std::ifstream::binary | std::ifstream::ate);
224 if (!ifs)
225 {
227 etiss::fmt::format("Error during read of segment image file {:s}!", image));
228 }
229 fsize = ifs.tellg();
230 ifs.seekg(0, std::ifstream::beg);
231
232 buf = new etiss::uint8[fsize];
233
234 ifs.read((char *)buf, fsize);
235
237 etiss::fmt::format(
238 "The memory segment {:d} is initialized with {:#x} bytes from input_image!", i, length)
239
240 );
241 }
242
243 auto mseg = std::make_unique<MemSegment>(origin, length, static_cast<MemSegment::access_t>(access), sname,
244 buf, initString, initEleSet, randomRoot);
245 add_memsegment(mseg, buf, fsize);
246 delete[] buf;
247 }
248 }
249}
250
252{
253 if (!etiss::cfg().isSet("vp.elf_file"))
254 return;
255
256 std::string elf_file = etiss::cfg().get<std::string>("vp.elf_file", "");
257
258 ELFIO::elfio reader;
259
260 if (!reader.load(elf_file))
261 {
262 etiss::log(etiss::FATALERROR, "ELF reader could not process file");
263 }
264
265 if (etiss::cfg().isSet("arch.cpu"))
266 {
267 std::stringstream ss;
268 ss << "Assuming CPU architecture " << etiss::cfg().get<std::string>("arch.cpu", "")
269 << " as set in configuration file. ELF architecture field will be ignored";
270 etiss::log(etiss::INFO, ss.str());
271 }
272 else
273 {
274 // set architecture automatically
275 if (reader.get_machine() == EM_RISCV)
276 {
277 if ((reader.get_class() == ELFCLASS64))
278 {
279 etiss::cfg().set<std::string>("arch.cpu", "RV64IMACFD"); // RISCV and OR1K work as well
280 }
281 else if ((reader.get_class() == ELFCLASS32))
282 {
283 etiss::cfg().set<std::string>("arch.cpu", "RV32IMACFD");
284 // add conditions
285 }
286 else
287 {
288 etiss::log(etiss::FATALERROR, "System architecture is neither 64 nor 32 bit!");
289 }
290 }
291 else
292 {
293 std::stringstream ss;
294 ss << "Target architecture with code 0x" << std::hex << std::setw(2) << std::setfill('0')
295 << reader.get_machine()
296 << " was not automatically recognized, please set the arch.cpu parameter manually!";
297 etiss::log(etiss::FATALERROR, ss.str());
298 }
299 std::stringstream ss;
300 ss << "Set ETISS architecture to " << etiss::cfg().get<std::string>("arch.cpu", "")
301 << " as specified in ELF-file.";
302 etiss::log(etiss::INFO, ss.str());
303 }
304
305 for (auto &seg : reader.segments)
306 {
307 etiss::uint64 start_addr = seg->get_physical_address();
308 etiss::uint64 size = seg->get_memory_size();
309 if (size == 0)
310 continue;
311 size_t file_size = seg->get_file_size();
312 if (seg->get_type() != PT_LOAD)
313 continue;
314
315 int mode = 0;
316 std::string modestr = "";
317 if (seg->get_flags() & PF_R)
318 {
319 mode |= MemSegment::READ;
320 modestr += "R";
321 }
322 if (seg->get_flags() & PF_W)
323 {
324 mode |= MemSegment::WRITE;
325 modestr += "W";
326 }
327 if (seg->get_flags() & PF_X)
328 {
329 mode |= MemSegment::EXEC;
330 modestr += "X";
331 }
332
333 std::string sname = etiss::fmt::format("{} - {:s}[{:#016x} - {:#016x}]", seg->get_index(), modestr,
334 start_addr + size - 1, start_addr);
335
336 auto mseg_it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(start_addr, size));
337
338 if (mseg_it != msegs_.end())
339 {
340 auto &mseg = *mseg_it;
341
342 mseg->name_ = sname;
343 mseg->mode_ = static_cast<MemSegment::access_t>(mode);
344
345 mseg->load(seg->get_data(), start_addr - mseg->start_addr_, file_size);
346
347 std::stringstream msg;
348 msg << "Initialized the memory segment " << mseg->name_ << " from ELF-file";
349 etiss::log(etiss::INFO, msg.str());
350
351 continue;
352 }
353
354 std::stringstream msg;
355 msg << etiss::fmt::format("Found no matching memory segments at {:#016x}", start_addr);
356
358 {
359 msg << "! As you turned on error_on_seg_mismatch, ETISS will now terminate.";
360 etiss::log(etiss::FATALERROR, msg.str());
361 }
362 else
363 {
364 msg << ", creating one. WARNING: the segment will be created with the size information present in the "
365 "ELF-file, the resulting segment may be too small to fit dynamic data (cache, heap)!";
366 etiss::log(etiss::WARNING, msg.str());
367 }
368
369 auto mseg = std::make_unique<MemSegment>(start_addr, size, static_cast<MemSegment::access_t>(mode), sname);
370 add_memsegment(mseg, seg->get_data(), file_size);
371 }
372
373 // read start or rather program boot address from ELF
374 start_addr_ = reader.get_entry();
375}
376
377void SimpleMemSystem::add_memsegment(std::unique_ptr<MemSegment> &mseg, const void *raw_data, size_t file_size_bytes)
378{
379 std::stringstream msg;
380 msg << "New Memory segment added: " << mseg->name_;
381 etiss::log(etiss::INFO, msg.str().c_str());
382
383 mseg->load(raw_data, 0, file_size_bytes);
384
385 msegs_.push_back(std::move(mseg));
386}
387
389 : print_ibus_access_(etiss::cfg().get<bool>("simple_mem_system.print_ibus_access", false))
390 , print_dbus_access_(etiss::cfg().get<bool>("simple_mem_system.print_dbus_access", false))
391 , print_dbgbus_access_(etiss::cfg().get<bool>("simple_mem_system.print_dbgbus_access", false))
392 , print_to_file_(etiss::cfg().get<bool>("simple_mem_system.print_to_file", false))
393 , error_on_seg_mismatch_(etiss::cfg().get<bool>("simple_mem_system.error_on_seg_mismatch", false))
394 , message_max_cnt_(etiss::cfg().get<int>("simple_mem_system.message_max_cnt", 100))
395{
397 {
398 trace_file_dbus_.open(etiss::cfg().get<std::string>("etiss.output_path_prefix", "") + "dBusAccess.csv",
399 std::ios::binary);
400 }
401}
402
403void access_error(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, std::string error, etiss::Verbosity verbosity)
404{
405 uint64 pc = cpu ? cpu->instructionPointer : 0;
407 etiss::fmt::format("{:s}, PC = {:#016x}, address {:#016x}, length {:#x}", error, pc, addr, len));
408}
409
410etiss::int32 SimpleMemSystem::iread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len)
411{
412 auto it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(addr, len));
413 if (it != msegs_.end())
414 return RETURNCODE::NOERROR;
415
416 access_error(cpu, addr, len, "ibus read error", etiss::ERROR);
417 return RETURNCODE::IBUS_READ_ERROR;
418}
419
420etiss::int32 SimpleMemSystem::iwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
421{
422 access_error(cpu, addr, len, "ibus write blocked", etiss::ERROR);
423 return RETURNCODE::IBUS_WRITE_ERROR;
424}
425
426static void trace(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, bool isWrite, bool toFile, std::ofstream &file)
427{
428 uint64 time = 0;
429 uint64 pc = 0;
430
431 if (cpu)
432 {
433 time = cpu->cpuTime_ps;
434 pc = cpu->instructionPointer;
435 }
436 auto text = etiss::fmt::format("{:d};{:#08x};{};{:#08x};{:#x}", time, pc, (isWrite ? 'w' : 'r'), addr, len);
437
438 if (toFile)
439 file << text << std::endl;
440 else
441 std::cout << text << std::endl;
442}
443
444template <bool write>
445etiss::int32 SimpleMemSystem::dbus_access(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
446{
447 auto mseg_it = std::find_if(msegs_.begin(), msegs_.end(), find_fitting_mseg(addr, len));
448
449 if (mseg_it != msegs_.end())
450 {
451 auto &mseg = *mseg_it;
453
454 if (!(mseg->mode_ & access))
455 {
456 access_error(cpu, addr, len, std::string("dbus ") + (write ? "write" : "read") + " forbidden",
458 }
459
460 size_t offset = addr - mseg->start_addr_;
461
462 void *dest = write ? mseg->mem_ + offset : buf;
463 const void *src = write ? buf : mseg->mem_ + offset;
464
465 memcpy(dest, src, len);
466
468 trace(cpu, addr, len, write, print_to_file_, trace_file_dbus_);
469
470 return RETURNCODE::NOERROR;
471 }
472
473 access_error(cpu, addr, len, std::string("dbus ") + (write ? "write" : "read") + " error", etiss::ERROR);
474
475 return write ? RETURNCODE::DBUS_WRITE_ERROR : RETURNCODE::DBUS_READ_ERROR;
476}
477
478etiss::int32 SimpleMemSystem::dread(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
479{
480 return dbus_access<false>(cpu, addr, buf, len);
481}
482
483etiss::int32 SimpleMemSystem::dwrite(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
484{
485 return dbus_access<true>(cpu, addr, buf, len);
486}
487
488etiss::int32 SimpleMemSystem::dbg_read(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
489{
490 return dread(nullptr, addr, buf, len);
491}
492
493etiss::int32 SimpleMemSystem::dbg_write(etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len)
494{
495 return dwrite(nullptr, addr, buf, len);
496}
497
498extern void global_sync_time(uint64 time_ps);
500{
501 // std::cout << "CPU time: " << cpu -> cpuTime_ps << "ps" << std::endl;
502 // global_sync_time(cpu->cpuTime_ps);
503}
general configuration and logging
#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
void access_error(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, std::string error, etiss::Verbosity verbosity)
__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:354
bool set(const std::string &key, T value)
template function to set the value of a configuration key.
Definition Misc.h:335
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:312
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.
forwards: include/jit/*
Definition Benchmark.h:17
std::string Configuration::get< std::string >(const std::string &key, std::string default_, bool *default_used)
Definition Misc.cpp:227
Verbosity
Enumeration type for the log levels.
Definition Misc.h:82
@ INFO
Definition Misc.h:87
@ WARNING
Definition Misc.h:86
@ ERROR
Definition Misc.h:85
@ FATALERROR
Definition Misc.h:84
Configuration & cfg()
Definition Misc.cpp:548
Verbosity & verbosity()
Get log level reference.
Definition Misc.cpp:84
void log(Verbosity level, std::string msg)
write log message at the given level.
Definition Misc.cpp:94
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:51
etiss_uint64 instructionPointer
pointer to next instruction.
Definition CPU.h:54
etiss_uint64 cpuTime_ps
simulation time of cpu
Definition CPU.h:59
#define exp(__x)
Definition tgmath.h:431