mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Hook up KTimer to scheduler
This commit is contained in:
parent
fa82dad38d
commit
0be099d1ea
7 changed files with 96 additions and 27 deletions
|
@ -126,7 +126,7 @@ class CPU {
|
||||||
Emulator& emu;
|
Emulator& emu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr u64 ticksPerSec = 268111856;
|
static constexpr u64 ticksPerSec = Scheduler::arm11Clock;
|
||||||
|
|
||||||
CPU(Memory& mem, Kernel& kernel, Emulator& emu);
|
CPU(Memory& mem, Kernel& kernel, Emulator& emu);
|
||||||
void reset();
|
void reset();
|
||||||
|
@ -175,6 +175,10 @@ class CPU {
|
||||||
return scheduler.currentTimestamp;
|
return scheduler.currentTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scheduler& getScheduler() {
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
void clearCache() { jit->ClearCache(); }
|
void clearCache() { jit->ClearCache(); }
|
||||||
void runFrame();
|
void runFrame();
|
||||||
};
|
};
|
|
@ -70,6 +70,7 @@ public:
|
||||||
Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services
|
Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services
|
||||||
Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port
|
Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port
|
||||||
Handle makeTimer(ResetType resetType);
|
Handle makeTimer(ResetType resetType);
|
||||||
|
void pollTimers();
|
||||||
|
|
||||||
// Signals an event, returns true on success or false if the event does not exist
|
// Signals an event, returns true on success or false if the event does not exist
|
||||||
bool signalEvent(Handle e);
|
bool signalEvent(Handle e);
|
||||||
|
@ -94,7 +95,6 @@ public:
|
||||||
void releaseMutex(Mutex* moo);
|
void releaseMutex(Mutex* moo);
|
||||||
void cancelTimer(Timer* timer);
|
void cancelTimer(Timer* timer);
|
||||||
void signalTimer(Handle timerHandle, Timer* timer);
|
void signalTimer(Handle timerHandle, Timer* timer);
|
||||||
void updateTimer(Handle timerHandle, Timer* timer);
|
|
||||||
|
|
||||||
// Wake up the thread with the highest priority out of all threads in the waitlist
|
// Wake up the thread with the highest priority out of all threads in the waitlist
|
||||||
// Returns the index of the woken up thread
|
// Returns the index of the woken up thread
|
||||||
|
|
|
@ -177,13 +177,12 @@ struct Timer {
|
||||||
u64 waitlist; // Refer to the getWaitlist function below for documentation
|
u64 waitlist; // Refer to the getWaitlist function below for documentation
|
||||||
ResetType resetType = ResetType::OneShot;
|
ResetType resetType = ResetType::OneShot;
|
||||||
|
|
||||||
u64 startTick; // CPU tick the timer started
|
u64 fireTick; // CPU tick the timer will be fired
|
||||||
u64 currentDelay; // Number of ns until the timer fires next time
|
|
||||||
u64 interval; // Number of ns until the timer fires for the second and future times
|
u64 interval; // Number of ns until the timer fires for the second and future times
|
||||||
bool fired; // Has this timer been signalled?
|
bool fired; // Has this timer been signalled?
|
||||||
bool running; // Is this timer running or stopped?
|
bool running; // Is this timer running or stopped?
|
||||||
|
|
||||||
Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {}
|
Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MemoryBlock {
|
struct MemoryBlock {
|
||||||
|
|
|
@ -5,14 +5,17 @@
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
struct Scheduler {
|
struct Scheduler {
|
||||||
enum class EventType {
|
enum class EventType {
|
||||||
VBlank = 0, // End of frame event
|
VBlank = 0, // End of frame event
|
||||||
Panic = 1, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
|
UpdateTimers = 1, // Update kernel timer objects
|
||||||
|
Panic = 2, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
|
||||||
TotalNumberOfEvents // How many event types do we have in total?
|
TotalNumberOfEvents // How many event types do we have in total?
|
||||||
};
|
};
|
||||||
static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);
|
static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);
|
||||||
|
static constexpr u64 arm11Clock = 268111856;
|
||||||
|
|
||||||
template <typename Key, typename Val, usize size>
|
template <typename Key, typename Val, usize size>
|
||||||
using EventMap = boost::container::flat_multimap<Key, Val, std::less<Key>, boost::container::static_vector<std::pair<Key, Val>, size>>;
|
using EventMap = boost::container::flat_multimap<Key, Val, std::less<Key>, boost::container::static_vector<std::pair<Key, Val>, size>>;
|
||||||
|
@ -46,4 +49,37 @@ struct Scheduler {
|
||||||
// Add a dummy event to always keep the scheduler non-empty
|
// Add a dummy event to always keep the scheduler non-empty
|
||||||
addEvent(EventType::Panic, std::numeric_limits<u64>::max());
|
addEvent(EventType::Panic, std::numeric_limits<u64>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / arm11Clock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Function for converting time units to cycles for various kernel functions
|
||||||
|
// Thank you Citra
|
||||||
|
static constexpr s64 nsToCycles(float ns) { return s64(arm11Clock * (0.000000001f) * ns); }
|
||||||
|
static constexpr s64 nsToCycles(int ns) { return arm11Clock * s64(ns) / 1000000000; }
|
||||||
|
|
||||||
|
static constexpr s64 nsToCycles(s64 ns) {
|
||||||
|
if (ns / 1000000000 > static_cast<s64>(MAX_VALUE_TO_MULTIPLY)) {
|
||||||
|
return std::numeric_limits<s64>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns > static_cast<s64>(MAX_VALUE_TO_MULTIPLY)) {
|
||||||
|
return arm11Clock * (ns / 1000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (arm11Clock * ns) / 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr s64 nsToCycles(u64 ns) {
|
||||||
|
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||||
|
return std::numeric_limits<s64>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns > MAX_VALUE_TO_MULTIPLY) {
|
||||||
|
return arm11Clock * (s64(ns) / 1000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (arm11Clock * s64(ns)) / 1000000000;
|
||||||
|
}
|
||||||
};
|
};
|
|
@ -36,7 +36,7 @@ void CPU::runFrame() {
|
||||||
|
|
||||||
while (!emu.frameDone) {
|
while (!emu.frameDone) {
|
||||||
// Run CPU until the next scheduler event
|
// Run CPU until the next scheduler event
|
||||||
env.ticksLeft = scheduler.nextTimestamp;
|
env.ticksLeft = scheduler.nextTimestamp - scheduler.currentTimestamp;
|
||||||
|
|
||||||
execute:
|
execute:
|
||||||
const auto exitReason = jit->Run();
|
const auto exitReason = jit->Run();
|
||||||
|
@ -54,6 +54,8 @@ void CPU::runFrame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("CPU END!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CPU_DYNARMIC
|
#endif // CPU_DYNARMIC
|
|
@ -1,5 +1,8 @@
|
||||||
#include "kernel.hpp"
|
#include <limits>
|
||||||
|
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
|
#include "kernel.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
Handle Kernel::makeTimer(ResetType type) {
|
Handle Kernel::makeTimer(ResetType type) {
|
||||||
Handle ret = makeObject(KernelObjectType::Timer);
|
Handle ret = makeObject(KernelObjectType::Timer);
|
||||||
|
@ -13,30 +16,48 @@ Handle Kernel::makeTimer(ResetType type) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::updateTimer(Handle handle, Timer* timer) {
|
void Kernel::pollTimers() {
|
||||||
|
u64 currentTick = cpu.getTicks();
|
||||||
|
|
||||||
|
// Find the next timestamp we'll poll KTimers on. To do this, we find the minimum tick one of our timers will fire
|
||||||
|
u64 nextTimestamp = std::numeric_limits<u64>::max();
|
||||||
|
// Do we have any active timers anymore? If not, then we won't need to schedule a new timer poll event
|
||||||
|
bool haveActiveTimers = false;
|
||||||
|
|
||||||
|
for (auto handle : timerHandles) {
|
||||||
|
KernelObject* object = getObject(handle, KernelObjectType::Timer);
|
||||||
|
if (object != nullptr) {
|
||||||
|
Timer* timer = object->getData<Timer>();
|
||||||
|
|
||||||
if (timer->running) {
|
if (timer->running) {
|
||||||
const u64 currentTicks = cpu.getTicks();
|
// If timer has fired, signal it and set the tick it will next time
|
||||||
u64 elapsedTicks = currentTicks - timer->startTick;
|
if (currentTick >= timer->fireTick) {
|
||||||
|
|
||||||
constexpr double ticksPerSec = double(CPU::ticksPerSec);
|
|
||||||
constexpr double nsPerTick = ticksPerSec / 1000000000.0;
|
|
||||||
const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick);
|
|
||||||
|
|
||||||
// Timer has fired
|
|
||||||
if (elapsedNs >= timer->currentDelay) {
|
|
||||||
timer->startTick = currentTicks;
|
|
||||||
timer->currentDelay = timer->interval;
|
|
||||||
signalTimer(handle, timer);
|
signalTimer(handle, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update our next timer fire timestamp and mark that we should schedule a new event to poll timers
|
||||||
|
// We recheck timer->running because signalling a timer stops it if interval == 0
|
||||||
|
if (timer->running) {
|
||||||
|
nextTimestamp = std::min<u64>(nextTimestamp, timer->fireTick);
|
||||||
|
haveActiveTimers = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still have active timers, schedule next poll event
|
||||||
|
if (haveActiveTimers) {
|
||||||
|
Scheduler& scheduler = cpu.getScheduler();
|
||||||
|
scheduler.addEvent(Scheduler::EventType::UpdateTimers, nextTimestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::cancelTimer(Timer* timer) {
|
void Kernel::cancelTimer(Timer* timer) {
|
||||||
timer->running = false;
|
timer->running = false;
|
||||||
// TODO: When we have a scheduler this should properly cancel timer events in the scheduler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::signalTimer(Handle timerHandle, Timer* timer) {
|
void Kernel::signalTimer(Handle timerHandle, Timer* timer) {
|
||||||
|
printf("DEEPFRIED\n");
|
||||||
timer->fired = true;
|
timer->fired = true;
|
||||||
requireReschedule();
|
requireReschedule();
|
||||||
|
|
||||||
|
@ -54,6 +75,8 @@ void Kernel::signalTimer(Handle timerHandle, Timer* timer) {
|
||||||
|
|
||||||
if (timer->interval == 0) {
|
if (timer->interval == 0) {
|
||||||
cancelTimer(timer);
|
cancelTimer(timer);
|
||||||
|
} else {
|
||||||
|
timer->fireTick = cpu.getTicks() + Scheduler::nsToCycles(timer->interval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,16 +110,18 @@ void Kernel::svcSetTimer() {
|
||||||
|
|
||||||
Timer* timer = object->getData<Timer>();
|
Timer* timer = object->getData<Timer>();
|
||||||
cancelTimer(timer);
|
cancelTimer(timer);
|
||||||
timer->currentDelay = initial;
|
|
||||||
timer->interval = interval;
|
timer->interval = interval;
|
||||||
timer->running = true;
|
timer->running = true;
|
||||||
timer->startTick = cpu.getTicks();
|
timer->fireTick = cpu.getTicks() + Scheduler::nsToCycles(initial);
|
||||||
|
|
||||||
|
Scheduler& scheduler = cpu.getScheduler();
|
||||||
|
// Signal an event to poll timers as soon as possible
|
||||||
|
scheduler.removeEvent(Scheduler::EventType::UpdateTimers);
|
||||||
|
scheduler.addEvent(Scheduler::EventType::UpdateTimers, cpu.getTicks() + 1);
|
||||||
|
|
||||||
// If the initial delay is 0 then instantly signal the timer
|
// If the initial delay is 0 then instantly signal the timer
|
||||||
if (initial == 0) {
|
if (initial == 0) {
|
||||||
signalTimer(handle, timer);
|
signalTimer(handle, timer);
|
||||||
} else {
|
|
||||||
// This should schedule an event in the scheduler when we have one
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = Result::Success;
|
regs[0] = Result::Success;
|
||||||
|
|
|
@ -120,7 +120,7 @@ void Emulator::pollScheduler() {
|
||||||
auto& events = scheduler.events;
|
auto& events = scheduler.events;
|
||||||
|
|
||||||
// Pop events until there's none pending anymore
|
// Pop events until there's none pending anymore
|
||||||
while (scheduler.currentTimestamp > scheduler.nextTimestamp) {
|
while (scheduler.currentTimestamp >= scheduler.nextTimestamp) {
|
||||||
// Read event timestamp and type, pop it from the scheduler and handle it
|
// Read event timestamp and type, pop it from the scheduler and handle it
|
||||||
auto [time, eventType] = std::move(*events.begin());
|
auto [time, eventType] = std::move(*events.begin());
|
||||||
events.erase(events.begin());
|
events.erase(events.begin());
|
||||||
|
@ -129,6 +129,7 @@ void Emulator::pollScheduler() {
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case Scheduler::EventType::VBlank: {
|
case Scheduler::EventType::VBlank: {
|
||||||
|
printf("VBLANK!!!!!!\n");
|
||||||
// Signal that we've reached the end of a frame
|
// Signal that we've reached the end of a frame
|
||||||
frameDone = true;
|
frameDone = true;
|
||||||
lua.signalEvent(LuaEvent::Frame);
|
lua.signalEvent(LuaEvent::Frame);
|
||||||
|
@ -143,6 +144,8 @@ void Emulator::pollScheduler() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast<int>(eventType));
|
Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast<int>(eventType));
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue