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