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