diff options
-rw-r--r-- | src/arch/arm/linux/system.cc | 24 | ||||
-rw-r--r-- | src/arch/arm/linux/system.hh | 13 | ||||
-rw-r--r-- | src/cpu/simple/atomic.cc | 3 | ||||
-rw-r--r-- | src/cpu/simple/timing.cc | 4 | ||||
-rw-r--r-- | src/kern/linux/events.cc | 25 | ||||
-rw-r--r-- | src/kern/linux/events.hh | 27 |
6 files changed, 96 insertions, 0 deletions
diff --git a/src/arch/arm/linux/system.cc b/src/arch/arm/linux/system.cc index 38024c058..7aff2b6ef 100644 --- a/src/arch/arm/linux/system.cc +++ b/src/arch/arm/linux/system.cc @@ -47,9 +47,11 @@ #include "base/loader/object_file.hh" #include "base/loader/symtab.hh" #include "cpu/thread_context.hh" +#include "kern/linux/events.hh" #include "mem/physical.hh" using namespace ArmISA; +using namespace Linux; LinuxArmSystem::LinuxArmSystem(Params *p) : ArmSystem(p) @@ -96,6 +98,24 @@ LinuxArmSystem::LinuxArmSystem(Params *p) if (!kernelPanicEvent) panic("could not find kernel symbol \'panic\'"); #endif + + // With ARM udelay() is #defined to __udelay + Addr addr = 0; + if (kernelSymtab->findAddress("__udelay", addr)) { + uDelaySkipEvent = new UDelayEvent(&pcEventQueue, "__udelay", + fixFuncEventAddr(addr), 1000, 0); + } else { + panic("couldn't find kernel symbol \'udelay\'"); + } + + // constant arguments to udelay() have some precomputation done ahead of + // time. Constant comes from code. + if (kernelSymtab->findAddress("__const_udelay", addr)) { + constUDelaySkipEvent = new UDelayEvent(&pcEventQueue, "__const_udelay", + fixFuncEventAddr(addr), 1000, 107374); + } else { + panic("couldn't find kernel symbol \'udelay\'"); + } } void @@ -115,6 +135,10 @@ LinuxArmSystem::initState() LinuxArmSystem::~LinuxArmSystem() { + if (uDelaySkipEvent) + delete uDelaySkipEvent; + if (constUDelaySkipEvent) + delete constUDelaySkipEvent; } LinuxArmSystem * diff --git a/src/arch/arm/linux/system.hh b/src/arch/arm/linux/system.hh index 4e5ebcd73..2ef65fea2 100644 --- a/src/arch/arm/linux/system.hh +++ b/src/arch/arm/linux/system.hh @@ -74,6 +74,19 @@ class LinuxArmSystem : public ArmSystem /** Event to halt the simulator if the kernel calls panic() */ BreakPCEvent *kernelPanicEvent; #endif + /** + * PC based event to skip udelay(<time>) calls and quiesce the + * processor for the appropriate amount of time. This is not functionally + * required but does speed up simulation. + */ + Linux::UDelayEvent *uDelaySkipEvent; + + /** Another PC based skip event for const_udelay(). Similar to the udelay + * skip, but this function precomputes the first multiply that is done + * in the generic case since the parameter is known at compile time. + * Thus we need to do some division to get back to us. + */ + Linux::UDelayEvent *constUDelaySkipEvent; }; #endif // __ARCH_ARM_LINUX_SYSTEM_HH__ diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc index 27635d3ce..6aa0eb64e 100644 --- a/src/cpu/simple/atomic.cc +++ b/src/cpu/simple/atomic.cc @@ -641,6 +641,9 @@ AtomicSimpleCPU::tick() checkForInterrupts(); checkPcEventQueue(); + // We must have just got suspended by a PC event + if (_status == Idle) + return; Fault fault = NoFault; diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc index 632e83356..aca48e5d4 100644 --- a/src/cpu/simple/timing.cc +++ b/src/cpu/simple/timing.cc @@ -714,6 +714,10 @@ TimingSimpleCPU::fetch() checkPcEventQueue(); + // We must have just got suspended by a PC event + if (_status == Idle) + return; + TheISA::PCState pcState = thread->pcState(); bool needToFetch = !isRomMicroPC(pcState.microPC()) && !curMacroStaticInst; diff --git a/src/kern/linux/events.cc b/src/kern/linux/events.cc index 60aa857ac..75c2b6f7f 100644 --- a/src/kern/linux/events.cc +++ b/src/kern/linux/events.cc @@ -44,11 +44,13 @@ #include <sstream> #include "base/trace.hh" +#include "arch/utility.hh" #include "cpu/thread_context.hh" #include "kern/linux/events.hh" #include "kern/linux/printk.hh" #include "kern/system_events.hh" #include "sim/arguments.hh" +#include "sim/pseudo_inst.hh" #include "sim/system.hh" namespace Linux { @@ -66,4 +68,27 @@ DebugPrintkEvent::process(ThreadContext *tc) SkipFuncEvent::process(tc); } +void +UDelayEvent::process(ThreadContext *tc) +{ + int arg_num = 0; + + // Get the time in native size + uint64_t time = TheISA::getArgument(tc, arg_num, (uint16_t)-1, false); + + // convert parameter to ns + if (argDivToNs) + time /= argDivToNs; + + time *= argMultToNs; + + // Convert ns to ticks + time *= SimClock::Int::ns; + + SkipFuncEvent::process(tc); + + PseudoInst::quiesceNs(tc, time); +} + + } // namespace linux diff --git a/src/kern/linux/events.hh b/src/kern/linux/events.hh index e36a72dde..3f5f2526f 100644 --- a/src/kern/linux/events.hh +++ b/src/kern/linux/events.hh @@ -44,6 +44,33 @@ class DebugPrintkEvent : public SkipFuncEvent virtual void process(ThreadContext *xc); }; +/** A class to skip udelay() and related calls in the kernel. + * This class has two additional parameters that take the argument to udelay and + * manipulated it to come up with ns and eventually ticks to quiesce for. + * See descriptions of argDivToNs and argMultToNs below. + */ +class UDelayEvent : public SkipFuncEvent +{ + private: + /** value to divide arg by to create ns. This is present beacues the linux + * kernel code sometime precomputes the first multiply that is done in + * udelay() if the parameter is a constant. We need to undo it so here is + * how. */ + uint64_t argDivToNs; + + /** value to multiple arg by to create ns. Nominally, this is 1000 to + * convert us to ns, but since linux can do some preprocessing of constant + * values something else might be required. */ + uint64_t argMultToNs; + + public: + UDelayEvent(PCEventQueue *q, const std::string &desc, Addr addr, + uint64_t mult, uint64_t div) + : SkipFuncEvent(q, desc, addr), argDivToNs(div), argMultToNs(mult) {} + virtual void process(ThreadContext *xc); +}; + + } #endif |