summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/arch/arm/linux/system.cc24
-rw-r--r--src/arch/arm/linux/system.hh13
-rw-r--r--src/cpu/simple/atomic.cc3
-rw-r--r--src/cpu/simple/timing.cc4
-rw-r--r--src/kern/linux/events.cc25
-rw-r--r--src/kern/linux/events.hh27
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