ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
CPUCore.cpp
Go to the documentation of this file.
1
34#include "etiss/CPUCore.h"
35#include "etiss/ETISS.h"
37
38using namespace etiss;
39
44{
46 if (!core)
47 {
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
88void 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
131std::mutex CPUCore::instances_mu_;
132std::list<std::weak_ptr<CPUCore>> CPUCore::instances_;
133
134int currID = 0;
136{
137 return currID;
138}
139CPUCore::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;
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
191void 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
223void 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
250std::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
276std::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
309static 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
449static 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_) {}
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
504etiss::int32 CPUCore::execute(ETISS_System &_system)
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 {
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
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
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
828loopexit:
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_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
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
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
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
const std::string & getName()
Get the name of the CPUCore instance.
Definition CPUCore.h:301
unsigned blockCounter
Definition CPUCore.h:400
int blockCacheLimit_
Definition CPUCore.h:402
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 * 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 * 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 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
virtual void removedFromCPUCore(etiss::CPUCore *core)
called as soon a plugin has been removed from its CPUCore.
Definition Plugin.h:215
InterruptListenerPlugin * getInterruptListenerPlugin()
Definition Plugin.h:108
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.
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:892
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()
Definition Misc.cpp:577
void log(Verbosity level, std::string msg)
write log message at the given level.
Definition Misc.cpp:125
STL namespace.
#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