ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
CPUCore.cpp
Go to the documentation of this file.
1 
34 #include "etiss/CPUCore.h"
35 #include "etiss/ETISS.h"
36 
37 using namespace etiss;
38 
43 {
44  CPUCore *core = (CPUCore *)cpu->_etiss_private_handle_;
45  if (!core)
46  {
47  etiss::log(
49  "CPUArchRegListenerInterface::signalChangedRegisterValue() called from outside etiss::CPUCore::execute(). "
50  "this should not happen and indicates a faultiy CPUArch (or Plugin) implementation. This function may have "
51  "been called indirectly from ETISS_signalChangedRegisterValue()");
52  return;
53  }
54  auto vs = core->getStruct();
55  if (!vs)
56  {
57  etiss::log(etiss::ERROR, "CPUArchRegListenerInterface::signalChangedRegisterValue() called but CPUArch didn't "
58  "provide a VirtualStruct.");
59  return;
60  }
61  auto field = vs->findName(registerName);
62  if (!field)
63  {
64  field = vs->findPrettyName(registerName);
65  if (!field)
66  {
68  "CPUArchRegListenerInterface::signalChangedRegisterValue() called but the associated "
69  "VirtualStruct has not the specified field.",
70  registerName);
71  return;
72  }
73  }
74  if (!(field->flags_ & etiss::VirtualStruct::Field::L))
75  {
77  "CPUArchRegListenerInterface::signalChangedRegisterValue() called but the field of the associated "
78  "VirtualStruct doesn't have the listerner flags set (etiss::VirtualStruct::Field::L).",
79  registerName);
80  return;
81  }
82 
83  field->signalWrite();
84 }
85 
87 void CPUCore::InterruptVectorWrapper::setBit(unsigned bit, bool state)
88 {
89  bool consumed = false;
90  // std::cout << "CPUCore::InterruptVectorWrapper::setBit called " << state << " " << bit << std::endl;
91  // iterate through the plugins which have been registered at the cpu
92  for (auto iter : parent_.plugins)
93  {
95  if (ilp)
96  consumed |= ilp->interruptWrite(bit, state);
97  }
98  if (!consumed)
99  {
100  consumed_by_interruptlistener_ = false;
101  // std::cout << "CPUCore::InterruptVectorWrapper::setBit NOT consumed by InterruptListenerPlugin" << std::endl;
102  parent_.intvector_->setBit(bit, state);
103  }
104  else
105  {
106  consumed_by_interruptlistener_ = true;
107  // std::cout << "CPUCore::InterruptVectorWrapper::setBit consumed by InterruptListenerPlugin" << std::endl;
108  }
109 }
111 {
112  return parent_.intvector_->getBit(bit);
113 }
115 {
116  return parent_.intvector_->width();
117 }
119 {
120  return parent_.intvector_->isActive();
121 }
123 {
124  for (unsigned i = 0; i < width(); i++)
125  {
126  setBit(i, false);
127  }
128 }
129 
130 std::mutex CPUCore::instances_mu_;
131 std::list<std::weak_ptr<CPUCore>> CPUCore::instances_;
132 
133 int currID = 0;
135 {
136  return currID;
137 }
138 CPUCore::CPUCore(std::shared_ptr<etiss::CPUArch> arch)
139  : arch_(arch)
140  , name_("core" + std::to_string(currID))
141  , id_(currID++)
142  , cpu_(arch->newCPU())
143  , vcpu_(arch->getVirtualStruct(cpu_))
144  , intvector_(arch->createInterruptVector(cpu_))
145  , intenable_(arch->createInterruptEnable(cpu_))
147 {
148  arch_->resetCPU(cpu_, 0);
149  timer_enabled_ = true;
150  bcc_ = 1;
152  blockCacheLimit_ = -1;
153  intwrapper_ = intvector_ ? new InterruptVectorWrapper(*this) : 0;
154  blockCounter = 0;
155 #if ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
156  instrcounter = 0;
157 #endif
158 
159  if (cpu_)
160  {
161  if (vcpu_)
162  {
163  if (!vcpu_->findName("instructionPointer"))
164  {
165  ETISS_CPU *cpu = cpu_;
166  vcpu_->addField((new etiss::VirtualStruct::Field(
167  *vcpu_, "instructionPointer", "",
169  [cpu]() { return (uint64_t)cpu->instructionPointer; }, nullptr))
170  ->setDeleteP(std::function<void(etiss::VirtualStruct::Field *)>(
171  [](etiss::VirtualStruct::Field *f) { delete f; })));
172  }
173 #if ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
174  if (!vcpu_->findName("instructionCounter"))
175  {
176  vcpu_->addField((new etiss::VirtualStruct::Field(
177  *vcpu_, "instructionCounter", "",
180  8, false, [this]() { return (uint64_t)instrcounter; },
181  [this](uint64_t v) { instrcounter = v; }))
182  ->setDeleteP(std::function<void(etiss::VirtualStruct::Field *)>(
183  [](etiss::VirtualStruct::Field *f) { delete f; })));
184  }
185 #endif
186  }
187  }
188 }
189 
190 void CPUCore::addPlugin(std::shared_ptr<etiss::Plugin> plugin)
191 {
192  if (plugin.get() != 0)
193  {
194  {
195  std::lock_guard<std::mutex> lock(mu_);
196  // check if the plugin is already present
197  for (const std::shared_ptr<etiss::Plugin> &p : plugins)
198  {
199  if (p.get() == plugin.get())
200  {
201  etiss::log(etiss::WARNING, "cannot add the same plugin multiple times", *this, *(plugin.get()));
202  return;
203  }
204  }
205  if (plugin->plugin_core_)
206  {
207  etiss::log(etiss::WARNING, "A plugin has been added to multiple cores. This could be a serve problem.",
208  *this, *(plugin.get()));
209  }
210  plugins.push_back(plugin);
211  }
212  plugin->plugin_core_ = this;
213  plugin->setCorrespondingCPUCoreName(this->getName());
214  plugin->addedToCPUCore(this);
215  }
216  else
217  {
218  etiss::log(etiss::WARNING, "etiss::CPUCore::addPlugin() called without passing a valid plugin pointer.", *this);
219  }
220 }
221 
222 void CPUCore::removePlugin(std::shared_ptr<etiss::Plugin> plugin)
223 {
224  etiss::Plugin *p = plugin.get();
225  bool removed = false;
226  {
227  std::lock_guard<std::mutex> lock(mu_);
228  for (auto iter = plugins.begin(); iter != plugins.end();)
229  {
230  if (iter->get() == plugin.get())
231  {
232  plugins.erase(iter++);
233  removed = true;
234  break;
235  }
236  else
237  {
238  iter++;
239  }
240  }
241  }
242  if (removed)
243  {
244  p->plugin_core_ = nullptr;
245  p->removedFromCPUCore(this);
246  }
247 }
248 
249 std::shared_ptr<CPUCore> CPUCore::create(std::string archname, std::string instancename,
250  std::map<std::string, std::string> archoptions)
251 {
252 
254 
255  // get arch -> constructs arch with used arch library
256  std::shared_ptr<etiss::CPUArch> arch = getCPUArch(archname, archoptions);
257 
258  if (arch.get() == 0)
259  {
260  etiss::log(etiss::FATALERROR, "Architecture not found.", archname);
261  return 0;
262  }
263 
264  // creat core
265  std::shared_ptr<CPUCore> ret(new CPUCore(arch));
266 
267  {
268  std::lock_guard<std::mutex> lock(instances_mu_);
269  instances_.emplace_back(ret);
270  }
271 
272  return ret;
273 }
274 
275 std::list<std::string> CPUCore::list()
276 {
277  std::list<std::string> ret;
278  {
279  std::lock_guard<std::mutex> lock(instances_mu_);
280  for (auto iter = instances_.begin(); iter != instances_.end(); iter++)
281  {
282  auto ptr = iter->lock();
283  if (ptr.get() != 0)
284  {
285  ret.push_back(ptr->getName() + " [" + ptr->getArch()->getArchName() + "," + ptr->getJITName() +
286  "]"); // "CORENAME [ARCHITECTURE,JIT]
287  }
288  }
289  }
290  return ret;
291 }
292 
294 {
295  arch_->deleteInterruptVector(intvector_, cpu_);
296  if (vcpu_)
297  {
298  vcpu_->close();
299  vcpu_.reset();
300  }
301  arch_->deleteCPU(cpu_);
302  delete intwrapper_;
303 }
304 
308 static bool verifyJITSizeOf(std::string structname, etiss::int32 expected_size, etiss::JIT *jit,
309  std::string prefix = std::string())
310 {
311  if (jit == 0)
312  return false;
313  // generate code
314  std::string error;
315  std::string code = std::string(prefix + "\n#include \"etiss/jit/CPU.h\"\n#include "
316  "\"etiss/jit/System.h\"\n#include \"etiss/jit/ReturnCode.h\"\n "
317  "#include \"etiss/jit/types.h\"\n#include "
318  "\"etiss/jit/fpu/softfloat.h\"\n etiss_int32 get_size(){ return "
319  "sizeof(") +
320  structname + ");}";
321 
322  std::set<std::string> headers;
323  headers.insert(etiss::jitFiles());
324  // compile
325  void *handle = jit->translate(code, headers, std::set<std::string>(), std::set<std::string>(), error, true);
326  if (handle == 0)
327  {
329  std::string("Failed to compile test code [") + code + "] to check struct size: " + error);
330  return false;
331  }
332  // check size
333  typedef etiss::int32 (*get_size)(void);
334  get_size gs = (get_size)jit->getFunction(handle, "get_size", error);
335  if (gs == 0)
336  {
337  jit->free(handle);
339  std::string("Failed to get compiled function [get_size] to check struct size: ") + error);
340  return false;
341  }
342  etiss::int32 r = gs();
343  jit->free(handle);
344  if (r != expected_size)
345  {
346  std::stringstream ss;
347  ss << "Unexpected size of " << structname << ";";
348  ss << " Expected: " << expected_size;
349  ss << " Is: " << r;
350  etiss::log(etiss::ERROR, ss.str());
351  return false;
352  }
353  return true;
354 }
359 {
360  if (jit == 0)
361  return false;
362  std::string error;
363  std::stringstream stru;
364  etiss::int32 expected_size = 0;
365 
366  // add some types to test
367  std::vector<const char *> types;
368  std::vector<etiss::int32> typeslen;
369  types.push_back("etiss_int8");
370  typeslen.push_back(sizeof(etiss_int8));
371  types.push_back("etiss_uint8");
372  typeslen.push_back(sizeof(etiss_uint8));
373  types.push_back("etiss_int16");
374  typeslen.push_back(sizeof(etiss_int16));
375  types.push_back("etiss_uint16");
376  typeslen.push_back(sizeof(etiss_uint16));
377  types.push_back("etiss_int32");
378  typeslen.push_back(sizeof(etiss_int32));
379  types.push_back("etiss_uint32");
380  typeslen.push_back(sizeof(etiss_uint32));
381  types.push_back("etiss_int64");
382  typeslen.push_back(sizeof(etiss_int64));
383  types.push_back("etiss_uint64");
384  typeslen.push_back(sizeof(etiss_uint64));
385  types.push_back("void*");
386  typeslen.push_back(sizeof(void *));
387 
388  stru << "struct _etiss_test_struct {\n";
389  // add eacht type followed by any other type to create different combinations
390  for (size_t i = 0; i < types.size(); i++)
391  {
392  stru << types[i] << " var_" << i << ";\n";
393  expected_size += typeslen[i];
394  for (size_t j = 0; j < types.size(); j++)
395  {
396  if (j != i)
397  {
398  stru << types[j] << " var_" << i << "_" << j << ";\n";
399  expected_size += typeslen[j];
400  }
401  }
402  }
403 
404  stru << "};";
405 
406  std::string code = std::string("\n#include \"etiss/jit/types.h\"\n#pragma pack(push, 1)\n") + stru.str() +
407  "\n#pragma pack(pop)\n etiss_int32 get_size(){ return sizeof(struct "
408  "_etiss_test_struct);}";
409 
410  std::set<std::string> headers;
411  headers.insert(etiss::jitFiles());
412  // compile
413  void *handle = jit->translate(code, headers, std::set<std::string>(), std::set<std::string>(), error, true);
414  if (handle == 0)
415  {
417  std::string("Failed to compile test code [") + code + "] to check struct size: " + error);
418  return false;
419  }
420  typedef etiss::int32 (*get_size)(void);
421  // check size
422  get_size gs = (get_size)jit->getFunction(handle, "get_size", error);
423  if (gs == 0)
424  {
425  jit->free(handle);
427  std::string("Failed to get compiled function [get_size] to check struct size: ") + error);
428  return false;
429  }
430  etiss::int32 r = gs();
431  jit->free(handle);
432  if (r != expected_size)
433  {
434  std::stringstream ss;
435  ss << "Unexpected size of test structure;";
436  ss << " Expected: " << expected_size;
437  ss << " Is: " << r;
438  etiss::log(etiss::ERROR, ss.str());
439  return false;
440  }
441  return true;
442 }
443 
448 static void etiss_CPUCore_handleException(ETISS_CPU *cpu, etiss::int32 &code, BlockLink *&block_ptr,
449  Translation &translator, CPUArch *arch)
450 {
451 
452 #if DEBUG
453  if (unlikely(code == RETURNCODE::NOERROR))
454  {
455  etiss::log(etiss::ERROR, "etiss_CPUCore_handleException may not be called with error code NOERROR");
456  }
457 #endif
458 
459  switch (code)
460  {
461  case RETURNCODE::RELOADBLOCKS:
462  block_ptr = 0; // doesn't hold a reference and thus might become invalid
463  translator.unloadBlocks();
464  code = RETURNCODE::NOERROR;
465  return;
466  case RETURNCODE::RELOADCURRENTBLOCK:
467  if (block_ptr)
468  block_ptr->valid = false; // invalidate but don't delete block
469  block_ptr = 0;
470  code = RETURNCODE::NOERROR;
471  return;
472  case RETURNCODE::GDBNOERROR:
473  code = RETURNCODE::NOERROR;
474  return;
475  case RETURNCODE::CPUFINISHED:
476  return;
477  default:
478  code = arch->handleException(code, cpu);
479  return;
480  }
481 }
482 
484 {
485  public:
486  std::list<etiss::RegisterDevicePlugin *> plugins;
487  LegacyRegisterDevicePluginListener(const std::list<etiss::RegisterDevicePlugin *> &plugins_) : plugins(plugins_) {}
489  virtual void write(etiss::VirtualStruct::Field &field, uint64_t val)
490  {
491  std::string name = field.name_;
492  const char *cname = name.c_str();
493  for (auto plugin : plugins)
494  {
495  if (plugin)
496  {
497  plugin->changedRegister(cname);
498  }
499  }
500  }
501 };
502 
504 {
505  ETISS_System *system = &_system; // change to pointer for reassignments
506 
507  if (!ETISS_System_isvalid(system))
508  { // check if required functions are present
509  return RETURNCODE::INVALIDSYSTEM;
510  }
511 
512  std::lock_guard<std::mutex> lock(mu_); // lock class fields from modification
513 
514  if (!arch_)
515  {
516  etiss::log(etiss::ERROR, "Could not find architecture!");
517  return RETURNCODE::GENERALERROR;
518  }
519 
520  if (!cpu_)
521  {
522  etiss::log(etiss::ERROR, "Could not find CPU struct!");
523  return RETURNCODE::GENERALERROR;
524  }
525 
527  (void *)this; // init pointer to execute RegisterDevicePlugins. the value of tis pointer may be invalid/subject
528  // to change and may not be used by external code
529 
530  // get JIT instance
531  std::shared_ptr<JIT> jiti = jit_; // copy jit because it may change
532  if (!jiti)
533  {
534  etiss::log(etiss::ERROR, std::string("No JIT available to ") + name_);
535  return RETURNCODE::JITERROR;
536  }
537 
538 
539  // verify jit
540  if (etiss::cfg().get<bool>("jit.verify", true))
541  {
542  if (!verifyJITSizeOf("etiss_int64", sizeof(etiss_int64), jiti.get()))
543  return RETURNCODE::JITCOMPILATIONERROR;
544  if (!verifyJITSizeOf("etiss_int32", sizeof(etiss_int32), jiti.get()))
545  return RETURNCODE::JITCOMPILATIONERROR;
546  if (!verifyJITSizeOf("etiss_int16", sizeof(etiss_int16), jiti.get()))
547  return RETURNCODE::JITCOMPILATIONERROR;
548  if (!verifyJITSizeOf("etiss_int8", sizeof(etiss_int8), jiti.get()))
549  return RETURNCODE::JITCOMPILATIONERROR;
550  if (!verifyJITSizeOf("ETISS_CPU", sizeof(ETISS_CPU), jiti.get()))
551  return RETURNCODE::JITCOMPILATIONERROR;
552  if (!verifyJITPragmaPack(jiti.get()))
553  return RETURNCODE::JITCOMPILATIONERROR;
554  etiss::log(etiss::INFO, std::string("JIT compiler ") + jiti->getName() +
555  " has passed the verification tests (tested by CPUCore " + name_ + ")");
556  }
557  // add default timer plugin from arch
558  if (timer_enabled_)
559  {
560  Plugin *timerInstance = arch_->newTimer(cpu_);
561  if (!timerInstance)
562  {
563  etiss::log(etiss::ERROR, "ERROR: default timer requested but not supported by architecture");
564  return RETURNCODE::GENERALERROR;
565  }
566  else
567  {
568  etiss::log(etiss::INFO, "Add Timer Plugin: " + timerInstance->getPluginName());
569  auto local_arch = arch_;
570  plugins.push_back(std::shared_ptr<etiss::Plugin>(timerInstance, [local_arch](etiss::Plugin *p) {
571  etiss::log(etiss::INFO, "Delete Timer Plugin.");
572  local_arch->deleteTimer(p);
573  }));
574  }
575  }
576 
577  // add MMU module from the arch
578  {
579  etiss::mm::MMU *new_mmu = arch_->newMMU(cpu_);
580  if (new_mmu)
581  {
582  mmu_enabled_ = true;
583  mmu_.reset(new_mmu);
584  etiss::log(etiss::INFO, "Add MMU module: " + mmu_->GetName());
585  }
586  }
587 
588  if (mmu_enabled_)
589  {
590  plugins.push_back(std::make_shared<etiss::mm::DMMUWrapper>(mmu_));
591  }
592 
593  // copy system wrapper plugins to list and update system (pre plugin init)
594  std::list<SystemWrapperPlugin *> syswrappers;
595  for (auto &plugin : plugins)
596  {
597  auto c = plugin->getSystemWrapperPlugin();
598  if (c)
599  {
600  ETISS_System *wsys = c->wrap(cpu_, system);
601  if (wsys)
602  {
603  syswrappers.push_front(c); // inverse order for easy iteration
604  system = wsys;
605  }
606  else
607  {
608  std::stringstream stream;
609  stream << "SystemWrapperPlugin \"" << c->getPluginName() << "\" failed to wrap ETISS_System instance";
610  etiss::log(etiss::WARNING, stream.str());
611  }
612  }
613  }
614 
615  // initialize plugins
616  for (auto &p : plugins)
617  {
618  if (!p)
619  etiss::log(etiss::FATALERROR, "Empty plugin");
620 
621  p->plugin_cpu_ = cpu_;
622  p->plugin_system_ = system;
623  p->plugin_arch_ = arch_.get();
624  p->init(cpu_, system, arch_.get());
625 
626  std::stringstream m;
627  m << "Init Plugin " << p->getPluginName();
628  etiss::log(etiss::INFO, m.str());
629  }
630 
631  // copy coroutine plugins to array
632  std::vector<CoroutinePlugin *> cor_array;
633  for (const auto &plugin : plugins)
634  {
635  auto c = plugin->getCoroutinePlugin();
636  if (c)
637  cor_array.push_back(c);
638  }
639 
640  // create translation object
641  Translation translation(arch_, jiti, plugins, *system, *cpu_);
642 
643  // Translation init returns a list of pluigins, at position 0 this is the arch plugin followed by all translation
644  // plugins
645  void **plugins_handle_ = translation.init();
646  if (!plugins_handle_)
647  {
648  etiss::log(etiss::FATALERROR, "Failed to initialize translation");
649  }
650 
651  // enable RegisterDevicePlugin listeneing by adding a listener to all fields of the VirtualStruct
653  {
654  std::list<RegisterDevicePlugin *> regdevices;
655  for (auto &plugin : plugins)
656  {
657  auto rdp = plugin->getRegisterDevicePlugin();
658  if (rdp)
659  regdevices.push_back(rdp);
660  }
661  if (!regdevices.empty())
662  {
663  etiss::log(
664  etiss::INFO,
665  "etiss::RegisterDevicePlugin is a legacy convenience plugin. it may become deprecated later on. "
666  "consider using etiss::VirtualStruct::Field::Listener to directly listen only for relevant fields.");
667 
668  if (vcpu_)
669  {
670  listener = new LegacyRegisterDevicePluginListener(regdevices);
671 
672  vcpu_->foreachField([listener](std::shared_ptr<etiss::VirtualStruct::Field> f) {
673  f->addListener(listener);
674  }); // add listener to all current field of struct
675 
676  // TODO: maybe later VirtualStruct will support a listener for added/removed fields. in that case the
677  // lisener of this function should also be added to new fields
678  }
679  else
680  {
682  "etiss::RegisterDevicePlugin added to a CPUCore that doesn't have a VirtualStruct.", name_);
683  }
684  }
685  }
686 
687  // start execution loop
688 
689  bool exit_on_loop = etiss::cfg().get<bool>("etiss.exit_on_loop", false);
690 
691  float startTime = (float)clock() / CLOCKS_PER_SEC; // TESTING
692 
693  BlockLink *blptr = 0; // pointer to the current block
694 
695  etiss::int32 exception = RETURNCODE::NOERROR;
696 
697  // sync time at the beginning (e.g. SystemC processes running at time 0)
698  system->syncTime(system->handle, cpu_);
699 
700  // execution loop
701  {
702 #if ETISS_DBG_ICOUNT_LIMIT > 0 && ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
703  while (likely(instrcounter < ETISS_DBG_ICOUNT_LIMIT))
704  {
705 #else
706  while (true)
707  {
708 #endif
709 
710  // execute coroutines
711  for (auto &cor_plugin : cor_array)
712  {
713  exception = cor_plugin->execute();
714  if (unlikely(exception != RETURNCODE::NOERROR)) // check exception
715  {
716  etiss_CPUCore_handleException(cpu_, exception, blptr, translation, arch_.get()); // handle exception
717  if (unlikely(exception != RETURNCODE::NOERROR)) // check if exception handling failed
718  {
719  goto loopexit; // return exception; terminate cpu
720  }
721  }
722  }
723  // std::cout << "blockCounter: " << blockCounter++ <<std::endl;
724  // std::cout << "instrcounter: " << instrcounter <<std::endl;
725  for (unsigned bc = 0; bc < bcc_; bc++)
726  {
727  // if not block internal jump // NOTE: removed since tests showed that this decreases performance
728  // if (!(blptr != 0 && blptr->valid && blptr->start<=cpu->instructionPointer && blptr->end >
729  // cpu->instructionPointer)){
730  // Transalte virtual address to physical address if MMU is enabled
732 
733  // remember pc and cpu time to check for loop to self instructions
734  uint64_t old_pc = cpu_->instructionPointer;
735  uint64_t old_time = cpu_->cpuTime_ps;
736 
737  if (mmu_enabled_)
738  {
739  if (mmu_->cache_flush_pending)
740  {
741  // FIXME: When flush required, current instruction cache has to be cleared. However, the
742  // unloadBlocks is much too time-comsuming than expected. It should be optimized later on.
743  // translation.unloadBlocks(0,(uint64_t)((int64_t)-1));
744  mmu_->cache_flush_pending = false;
745  blptr = nullptr;
746  }
747 
748  // If the exception could be handled by architecture, then continue translation
749  while ((exception = mmu_->Translate(cpu_->instructionPointer, &pma, etiss::mm::X_ACCESS)))
750  {
751  // translation.unloadBlocks();
752  if ((exception = arch_->handleException(exception, cpu_)))
753  goto loopexit;
754  // Update pma, in case pc is redirected to physical address space
755  pma = cpu_->instructionPointer;
756  }
757  }
758 
759  // FIXME: cpu->instructionPointer contains virtual address, getBlockFast should use physical address
760  // instead to realize physical cache.
761  blptr = translation.getBlockFast(
762  blptr, cpu_->instructionPointer); // IMPORTANT: no pointer reference is kept here. if the translator
763  // performs a cleanup then blptr must be set to 0
764  //}
765 
766  if (unlikely(blptr == 0)) // if no block function pointer could be acquired
767  {
768  if (false)
769  {
770  // emulation interface? switch jit?
771  exception = RETURNCODE::ARCHERROR;
772  goto loopexit;
773  }
774  else
775  {
776  std::stringstream stream;
777  stream << "CPU execution stopped: Cannot execute from instruction index " << std::hex
778  << cpu_->instructionPointer << std::dec << ": no translated code available" << std::endl;
779  etiss::log(etiss::WARNING, stream.str());
780  exception = RETURNCODE::JITCOMPILATIONERROR;
781  goto loopexit;
782  }
783  }
784  else
785  {
786  // etiss::log(etiss::FATALERROR,"disabled etiss iss");
787 #if ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
788  uint64 oldinstrptr = cpu_->instructionPointer; // TESTING
789 #endif
790  // plugins_handle_ has the pointer to all translation plugins,
791  // In the generated code these plugin handles are named "plugin_pointers" and can be used to access
792  // a variable of the plugin
793  exception = (*(blptr->execBlock))(cpu_, system, plugins_handle_);
794 
795  // exit simulator when a loop to self instruction is encountered
796  if (exit_on_loop && !exception &&
797  old_time + cpu_->cpuCycleTime_ps == cpu_->cpuTime_ps &&
798  old_pc == cpu_->instructionPointer)
799  {
800  exception = RETURNCODE::CPUFINISHED;
801  }
802 
803 #if ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
804  instrcounter +=
805  blptr->end - oldinstrptr; // TESTING ///TODO handle early exception exit? ///BUG:
806  // InstructionPointer increases more than 1 per instruction!!!
807 // std::cout << "blocksize: " << etiss::toString(blptr->end-oldinstrptr) << std::endl;
808 #endif
809  }
810 
811  // check for exception in executed block
812  if (unlikely(exception != RETURNCODE::NOERROR))
813  {
814  etiss_CPUCore_handleException(cpu_, exception, blptr, translation, arch_.get()); // handle exception
815  if (unlikely(exception != RETURNCODE::NOERROR)) // check if exception handling failed
816  {
817  goto loopexit; // exception; terminate cpu
818  }
819  }
820  }
821 
822  // sync time after block
823  system->syncTime(system->handle, cpu_);
824  }
825  }
826 
827 loopexit:
828 
829  float endTime = (float)clock() / CLOCKS_PER_SEC;
830 
831 
832  // execute coroutines end
833  for (auto &cor_plugin : cor_array)
834  {
835  cor_plugin->executionEnd(exception);
836  }
837 
838  // Defining the statistics of measurement and printing them
839  double cpu_time = cpu_->cpuTime_ps / 1.0E12;
840  double simulation_time = endTime - startTime;
841  double cpu_cycle = cpu_->cpuTime_ps / (float)cpu_->cpuCycleTime_ps;
842  double mips = cpu_->cpuTime_ps / (float)cpu_->cpuCycleTime_ps / simulation_time / 1.0E6;
843  std::cout << "CPU Time: " << (cpu_time) << "s Simulation Time: " << (simulation_time) << "s"
844  << std::endl;
845  std::cout << "CPU Cycles (estimated): " << (cpu_cycle) << std::endl;
846  std::cout << "MIPS (estimated): " << (mips) << std::endl;
847 
848 
849  // declaring path of writing the json file contaiing performance metrics and the boolean which approves of writing the json output
850  std::string valid_json_output_path = etiss::cfg().get<std::string>("vp.stats_file_path", "");
851  bool output_json = etiss::cfg().isSet("vp.stats_file_path");
852 
853  if(output_json==true)
854  {
855  std::ofstream json_output(valid_json_output_path);
856  json_output << "{\"mips\": " << mips << ", \"Simulation_Time\": " << simulation_time << ", \"CPU_Time\": " << cpu_time << ", \"CPU_cycle\": " << cpu_cycle << "}" << std::endl;
857  }
858 
859 
860  etiss_uint64 max = 0;
861  for (int i = 0; i < ETISS_MAX_RESOURCES; i++)
862  {
863  if (cpu_->resources[i])
864  {
865  if (cpu_->cycles[i] > max)
866  {
867  max = cpu_->cycles[i];
868  }
869  }
870  }
871  if (max != 0)
872  { // max=0: resource computation turned of
873  std::cout << "CPU Cycles (with pipeline): " << max << std::endl;
874  }
875  for (int i = 0; i < ETISS_MAX_RESOURCES; i++)
876  {
877  if (cpu_->resources[i])
878  {
879  std::cout << "Resource Usage " << cpu_->resources[i] << ": " << cpu_->resourceUsages[i] << " cycles, "
880  << ((cpu_->resourceUsages[i] / (double)max) * 100) << "%" << std::endl;
881  }
882  }
883 #if ETISS_CPUCORE_DBG_APPROXIMATE_INSTRUCTION_COUNTER
884  etiss::log(etiss::INFO, std::string("InstructionCounter: ") +
885  etiss::toString(instrcounter / ((double)cpu_->cpuTime_ps / 1000000.0)));
886  etiss::log(etiss::INFO, std::string("MIPS (good estimation): ") +
887  etiss::toString(instrcounter / ((double)cpu_->cpuTime_ps / 1000000.0)));
888 #endif
889 
890  // cleanup plugins
891  for (auto &p : plugins)
892  {
893  if (p)
894  {
895  p->cleanup();
896  p->plugin_cpu_ = nullptr;
897  p->plugin_system_ = nullptr;
898  p->plugin_arch_ = nullptr;
899  }
900  }
901 
902  // undo system wrapping
903  for (auto &syswrapper : syswrappers)
904  {
905  auto psys = syswrapper->unwrap(cpu_, system);
906  if (psys)
907  {
908  system = psys;
909  }
910  else
911  {
912  std::stringstream stream;
913  stream << "SERVE WARNING: SystemWrapperPlugin \"" << syswrapper->getPluginName()
914  << "\" failed to unwrap ETISS_System instance. Most likely results in a memory leak.";
915  etiss::log(etiss::WARNING, stream.str());
916  break;
917  }
918  }
919 
920  if (listener)
921  {
922  vcpu_->foreachField(
923  [listener](std::shared_ptr<etiss::VirtualStruct::Field> f) { f->removeListener(listener); });
924 
925  delete listener;
926  }
927 
928  return exception;
929 }
etiss_int32 int32
Definition: 386-GCC.h:81
etiss_uint64 uint64
Definition: 386-GCC.h:82
int currID
Definition: CPUCore.cpp:133
static void etiss_CPUCore_handleException(ETISS_CPU *cpu, etiss::int32 &code, BlockLink *&block_ptr, Translation &translator, CPUArch *arch)
small helper function to handle exceptions.
Definition: CPUCore.cpp:448
static bool verifyJITPragmaPack(etiss::JIT *jit)
generates test code to check alignment of structures in the just in time compiler
Definition: CPUCore.cpp:358
static bool verifyJITSizeOf(std::string structname, etiss::int32 expected_size, etiss::JIT *jit, std::string prefix=std::string())
generates and compiles test code for the just in time compiler to check size of basic types
Definition: CPUCore.cpp:308
defines main cpu core interface
Header file of the ETISS library.
__DEVICE__ int clock()
__DEVICE__ int max(int __a, int __b)
__device__ double
__device__ __2f16 float c
__device__ float
do v
Definition: arm_acle.h:76
if(__y==0) return __x
static __inline__ uint64_t
Definition: arm_cde.h:31
#define ETISS_MAX_RESOURCES
Definition: CPU.h:59
int ETISS_System_isvalid(ETISS_System *sys)
Definition: System.cpp:120
int16_t etiss_int16
Definition: types.h:89
#define likely(x)
Definition: types.h:73
uint64_t etiss_uint64
Definition: types.h:96
uint32_t etiss_uint32
Definition: types.h:93
int64_t etiss_int64
Definition: types.h:95
int8_t etiss_int8
Definition: types.h:86
#define unlikely(x)
Definition: types.h:74
uint8_t etiss_uint8
Definition: types.h:87
int32_t etiss_int32
Definition: types.h:92
uint16_t etiss_uint16
Definition: types.h:90
LegacyRegisterDevicePluginListener(const std::list< etiss::RegisterDevicePlugin * > &plugins_)
Definition: CPUCore.cpp:487
std::list< etiss::RegisterDevicePlugin * > plugins
Definition: CPUCore.cpp:486
virtual void write(etiss::VirtualStruct::Field &field, uint64_t val)
Definition: CPUCore.cpp:489
static void signalChangedRegisterValue(ETISS_CPU *cpu, const char *registerName)
call this function to inform RegisterDevicePlugins about changed special register values.
Definition: CPUCore.cpp:42
the interface to translate instructions of and processor architecture
Definition: CPUArch.h:162
virtual etiss::int32 handleException(etiss::int32 code, ETISS_CPU *cpu)
translate/process exceptions that occur at runtime
Definition: CPUArch.cpp:123
virtual void setBit(unsigned bit, bool state)
set the bit of an interrupt line to state (true = raised)
Definition: CPUCore.cpp:87
virtual unsigned width() const
number of interrupt bits
Definition: CPUCore.cpp:114
virtual bool getBit(unsigned bit) const
get the bit of an interrupt line
Definition: CPUCore.cpp:110
InterruptVectorWrapper(CPUCore &parent)
Definition: CPUCore.cpp:86
virtual void clear()
sets every bit to false
Definition: CPUCore.cpp:122
CPUCore is responsible for the simulation of a CPU core in ETISS.
Definition: CPUCore.h:113
CPUCore(std::shared_ptr< etiss::CPUArch > arch)
Private constructor of CPUCore.
Definition: CPUCore.cpp:138
std::shared_ptr< etiss::mm::MMU > mmu_
Definition: CPUCore.h:404
std::mutex mu_
JIT instance to use. may be 0 (etiss::getDefaultJIT() will be used in that case)
Definition: CPUCore.h:396
const int id_
name of the cpu core
Definition: CPUCore.h:387
etiss::InterruptVector * intvector_
Definition: CPUCore.h:390
uint64_t instrcounter
Definition: CPUCore.h:407
std::list< std::shared_ptr< Plugin > > plugins
mutex to lock the configuration of this cpu core.
Definition: CPUCore.h:398
unsigned exception_skip_count_
Definition: CPUCore.h:401
etiss::int32 execute(ETISS_System &system)
Start the simulation of the CPU core for the system model.
Definition: CPUCore.cpp:503
static std::mutex instances_mu_
this field is always present to maintain API compatibility but it is only used if ETISS_CPUCORE_DBG_A...
Definition: CPUCore.h:411
bool mmu_enabled_
TODO: possibility to limit the cache size.
Definition: CPUCore.h:403
std::shared_ptr< etiss::JIT > jit_
if true the a timer plugin allocated by arch_ will be added in CPUCore::execute
Definition: CPUCore.h:395
static std::list< std::string > list()
returns a list of currently present CPU cores
Definition: CPUCore.cpp:275
std::shared_ptr< etiss::CPUArch > arch_
Definition: CPUCore.h:385
InterruptVectorWrapper * intwrapper_
cpu interrupt vector derived from cpu_ and allocated by arch_
Definition: CPUCore.h:391
friend class InterruptVectorWrapper
Definition: CPUCore.h:115
static std::shared_ptr< CPUCore > create(std::string archname, std::string instancename="", std::map< std::string, std::string > archoptions=std::map< std::string, std::string >())
Create a CPUCore instance.
Definition: CPUCore.cpp:249
unsigned bcc_
list of all plugins
Definition: CPUCore.h:399
static int getNextID()
Definition: CPUCore.cpp:134
std::string name_
cpu architecture of this cpu core. may never be 0 or changed
Definition: CPUCore.h:386
ETISS_CPU * cpu_
ID of the cpu core.
Definition: CPUCore.h:388
void addPlugin(std::shared_ptr< etiss::Plugin > plugin)
Adds a plug-in to the core simulator.
Definition: CPUCore.cpp:190
std::shared_ptr< etiss::VirtualStruct > vcpu_
cpu state structure allocated by arch_
Definition: CPUCore.h:389
static std::list< std::weak_ptr< CPUCore > > instances_
mutext for access to a list of cpu core instances
Definition: CPUCore.h:413
void removePlugin(std::shared_ptr< etiss::Plugin > plugin)
Remove a plug-in from the core simulator.
Definition: CPUCore.cpp:222
virtual std::shared_ptr< VirtualStruct > getStruct()
Get the virtual structure of this CPUCore instance.
Definition: CPUCore.h:170
unsigned blockCounter
Definition: CPUCore.h:400
int blockCacheLimit_
Definition: CPUCore.h:402
const std::string & getName()
Get the name of the CPUCore instance.
Definition: CPUCore.h:301
etiss::InterruptEnable * intenable_
wrapped interrupt vector to allow interrupt listening
Definition: CPUCore.h:392
bool timer_enabled_
Definition: CPUCore.h:393
bool isSet(std::string val)
return true if the value of an configuration key has been set
Definition: Misc.cpp:382
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
virtual bool interruptWrite(unsigned bit, bool value)=0
gets called whenever an external write to the interrrupt vector takes place
compiler interface for just in time compilation of generated C code
Definition: JIT.h:67
virtual void * translate(std::string code, std::set< std::string > headerpaths, std::set< std::string > librarypaths, std::set< std::string > libraries, std::string &error, bool debug=true)=0
translate C code to executable code and return a handle/pointer that identifies the compilation resul...
virtual void * getFunction(void *handle, std::string name, std::string &error)=0
returns a function pointer to a compiled function from the handle returned by etiss::JIT::translate
virtual void free(void *handle)=0
clean up handled returned by etiss::JIT::translate
base plugin class that provides access to different plugin functions if present
Definition: Plugin.h:77
CPUCore * plugin_core_
holds a pointer to the associated CPUCore instance.
Definition: Plugin.h:200
InterruptListenerPlugin * getInterruptListenerPlugin()
Definition: Plugin.h:108
virtual void removedFromCPUCore(etiss::CPUCore *core)
called as soon a plugin has been removed from its CPUCore.
Definition: Plugin.h:215
std::string getPluginName() const
Definition: Plugin.h:130
void unloadBlocks(etiss::uint64 startindex=0, etiss::uint64 endindex=((etiss::uint64)((etiss::int64) -1)))
BlockLink * getBlockFast(BlockLink *prev, const etiss::uint64 &instructionindex)
CALL THIS function NOT getBlock(...) since getBlock will not check next/branch references.
Definition: Translation.h:165
NOTE: etiss::CPUArch should implement support for Listeners by either using the etiss::VirtualStruct:...
a Field instance represents e.g.
static const int W
write flag
const std::string name_
name of the field.
static const int L
supports listener plugins; used for etiss::RegisterDevicePlugins to determine access to a variable/fi...
static const int P
private field: this flag indicates that this field is an implementation specific field that e....
static const int R
read flag
std::shared_ptr< CPUArch > getCPUArch(std::string name, std::map< std::string, std::string > options=std::map< std::string, std::string >())
Get a present CPUArch plug-in by name.
Definition: ETISS.cpp:197
void forceInitialization()
Force the initialization of ETISS.
Definition: ETISS.cpp:886
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
std::string jitFiles()
Get ETISS JIT files path.
Definition: Misc.cpp:592
std::string toString(const T &val)
conversion of type T to std::string.
Definition: Misc.h:174
@ 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
void log(Verbosity level, std::string msg)
write log message at the given level.
Definition: Misc.cpp:125
#define false
Definition: stdbool.h:17
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
const char * resources[ETISS_MAX_RESOURCES]
names of resources
Definition: CPU.h:101
etiss_uint64 cpuCycleTime_ps
frequency of the cpu. use to allign e.g. memory delays
Definition: CPU.h:105
etiss_uint64 cpuTime_ps
simulation time of cpu
Definition: CPU.h:97
etiss_uint64 resourceUsages[ETISS_MAX_RESOURCES]
how many cycles each resource is used
Definition: CPU.h:99
void * _etiss_private_handle_
private helper handle for plugins
Definition: CPU.h:107
etiss_uint64 cycles[ETISS_MAX_RESOURCES]
how many cycles in each resource (including waiting)
Definition: CPU.h:103
memory access and time synchronization functions.
Definition: System.h:78
void * handle
custom handle that will be passed to the functions of this structure
Definition: System.h:116
void(* syncTime)(void *handle, ETISS_CPU *cpu)
called after a block to synchronize the time
Definition: System.h:114