Browse Source
Merge pull request #3353 from FernandoS27/aries
Merge pull request #3353 from FernandoS27/aries
System: Refactor CPU Core management and move ARMInterface and Schedulers to Kernelpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 541 additions and 515 deletions
-
10src/core/CMakeLists.txt
-
2src/core/arm/dynarmic/arm_dynarmic.cpp
-
14src/core/arm/exclusive_monitor.cpp
-
9src/core/arm/exclusive_monitor.h
-
75src/core/core.cpp
-
10src/core/core.h
-
127src/core/core_cpu.cpp
-
120src/core/core_cpu.h
-
70src/core/core_manager.cpp
-
63src/core/core_manager.h
-
152src/core/cpu_core_manager.cpp
-
62src/core/cpu_core_manager.h
-
83src/core/cpu_manager.cpp
-
50src/core/cpu_manager.h
-
2src/core/gdbstub/gdbstub.cpp
-
1src/core/hle/kernel/address_arbiter.cpp
-
52src/core/hle/kernel/kernel.cpp
-
19src/core/hle/kernel/kernel.h
-
52src/core/hle/kernel/physical_core.cpp
-
74src/core/hle/kernel/physical_core.h
-
1src/core/hle/kernel/scheduler.cpp
-
2src/core/hle/kernel/svc.cpp
-
3src/core/hle/kernel/thread.cpp
-
3src/core/hle/kernel/wait_object.cpp
@ -1,127 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <condition_variable>
|
|||
#include <mutex>
|
|||
|
|||
#include "common/logging/log.h"
|
|||
#ifdef ARCHITECTURE_x86_64
|
|||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
|||
#endif
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/arm/unicorn/arm_unicorn.h"
|
|||
#include "core/core.h"
|
|||
#include "core/core_cpu.h"
|
|||
#include "core/core_timing.h"
|
|||
#include "core/hle/kernel/scheduler.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
#include "core/hle/lock.h"
|
|||
#include "core/settings.h"
|
|||
|
|||
namespace Core { |
|||
|
|||
void CpuBarrier::NotifyEnd() { |
|||
std::unique_lock lock{mutex}; |
|||
end = true; |
|||
condition.notify_all(); |
|||
} |
|||
|
|||
bool CpuBarrier::Rendezvous() { |
|||
if (!Settings::values.use_multi_core) { |
|||
// Meaningless when running in single-core mode
|
|||
return true; |
|||
} |
|||
|
|||
if (!end) { |
|||
std::unique_lock lock{mutex}; |
|||
|
|||
--cores_waiting; |
|||
if (!cores_waiting) { |
|||
cores_waiting = NUM_CPU_CORES; |
|||
condition.notify_all(); |
|||
return true; |
|||
} |
|||
|
|||
condition.wait(lock); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, |
|||
std::size_t core_index) |
|||
: cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()}, |
|||
core_timing{system.CoreTiming()}, core_index{core_index} { |
|||
#ifdef ARCHITECTURE_x86_64
|
|||
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); |
|||
#else
|
|||
arm_interface = std::make_unique<ARM_Unicorn>(system); |
|||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
|||
#endif
|
|||
|
|||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); |
|||
} |
|||
|
|||
Cpu::~Cpu() = default; |
|||
|
|||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( |
|||
[[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { |
|||
#ifdef ARCHITECTURE_x86_64
|
|||
return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores); |
|||
#else
|
|||
// TODO(merry): Passthrough exclusive monitor
|
|||
return nullptr; |
|||
#endif
|
|||
} |
|||
|
|||
void Cpu::RunLoop(bool tight_loop) { |
|||
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
|
|||
if (!cpu_barrier.Rendezvous()) { |
|||
// If rendezvous failed, session has been killed
|
|||
return; |
|||
} |
|||
|
|||
Reschedule(); |
|||
|
|||
// If we don't have a currently active thread then don't execute instructions,
|
|||
// instead advance to the next event and try to yield to the next thread
|
|||
if (Kernel::GetCurrentThread() == nullptr) { |
|||
LOG_TRACE(Core, "Core-{} idling", core_index); |
|||
core_timing.Idle(); |
|||
} else { |
|||
if (tight_loop) { |
|||
arm_interface->Run(); |
|||
} else { |
|||
arm_interface->Step(); |
|||
} |
|||
// We are stopping a run, exclusive state must be cleared
|
|||
arm_interface->ClearExclusiveState(); |
|||
} |
|||
core_timing.Advance(); |
|||
|
|||
Reschedule(); |
|||
} |
|||
|
|||
void Cpu::SingleStep() { |
|||
return RunLoop(false); |
|||
} |
|||
|
|||
void Cpu::PrepareReschedule() { |
|||
arm_interface->PrepareReschedule(); |
|||
} |
|||
|
|||
void Cpu::Reschedule() { |
|||
// Lock the global kernel mutex when we manipulate the HLE state
|
|||
std::lock_guard lock(HLE::g_hle_lock); |
|||
|
|||
global_scheduler.SelectThread(core_index); |
|||
scheduler->TryDoContextSwitch(); |
|||
} |
|||
|
|||
void Cpu::Shutdown() { |
|||
scheduler->Shutdown(); |
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -1,120 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <atomic> |
|||
#include <condition_variable> |
|||
#include <cstddef> |
|||
#include <memory> |
|||
#include <mutex> |
|||
#include "common/common_types.h" |
|||
|
|||
namespace Kernel { |
|||
class GlobalScheduler; |
|||
class Scheduler; |
|||
} // namespace Kernel |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} |
|||
|
|||
namespace Core::Timing { |
|||
class CoreTiming; |
|||
} |
|||
|
|||
namespace Memory { |
|||
class Memory; |
|||
} |
|||
|
|||
namespace Core { |
|||
|
|||
class ARM_Interface; |
|||
class ExclusiveMonitor; |
|||
|
|||
constexpr unsigned NUM_CPU_CORES{4}; |
|||
|
|||
class CpuBarrier { |
|||
public: |
|||
bool IsAlive() const { |
|||
return !end; |
|||
} |
|||
|
|||
void NotifyEnd(); |
|||
|
|||
bool Rendezvous(); |
|||
|
|||
private: |
|||
unsigned cores_waiting{NUM_CPU_CORES}; |
|||
std::mutex mutex; |
|||
std::condition_variable condition; |
|||
std::atomic<bool> end{}; |
|||
}; |
|||
|
|||
class Cpu { |
|||
public: |
|||
Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, |
|||
std::size_t core_index); |
|||
~Cpu(); |
|||
|
|||
void RunLoop(bool tight_loop = true); |
|||
|
|||
void SingleStep(); |
|||
|
|||
void PrepareReschedule(); |
|||
|
|||
ARM_Interface& ArmInterface() { |
|||
return *arm_interface; |
|||
} |
|||
|
|||
const ARM_Interface& ArmInterface() const { |
|||
return *arm_interface; |
|||
} |
|||
|
|||
Kernel::Scheduler& Scheduler() { |
|||
return *scheduler; |
|||
} |
|||
|
|||
const Kernel::Scheduler& Scheduler() const { |
|||
return *scheduler; |
|||
} |
|||
|
|||
bool IsMainCore() const { |
|||
return core_index == 0; |
|||
} |
|||
|
|||
std::size_t CoreIndex() const { |
|||
return core_index; |
|||
} |
|||
|
|||
void Shutdown(); |
|||
|
|||
/** |
|||
* Creates an exclusive monitor to handle exclusive reads/writes. |
|||
* |
|||
* @param memory The current memory subsystem that the monitor may wish |
|||
* to keep track of. |
|||
* |
|||
* @param num_cores The number of cores to assume about the CPU. |
|||
* |
|||
* @returns The constructed exclusive monitor instance, or nullptr if the current |
|||
* CPU backend is unable to use an exclusive monitor. |
|||
*/ |
|||
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, |
|||
std::size_t num_cores); |
|||
|
|||
private: |
|||
void Reschedule(); |
|||
|
|||
std::unique_ptr<ARM_Interface> arm_interface; |
|||
CpuBarrier& cpu_barrier; |
|||
Kernel::GlobalScheduler& global_scheduler; |
|||
std::unique_ptr<Kernel::Scheduler> scheduler; |
|||
Timing::CoreTiming& core_timing; |
|||
|
|||
std::atomic<bool> reschedule_pending = false; |
|||
std::size_t core_index; |
|||
}; |
|||
|
|||
} // namespace Core |
|||
@ -0,0 +1,70 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <condition_variable>
|
|||
#include <mutex>
|
|||
|
|||
#include "common/logging/log.h"
|
|||
#ifdef ARCHITECTURE_x86_64
|
|||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
|||
#endif
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/arm/unicorn/arm_unicorn.h"
|
|||
#include "core/core.h"
|
|||
#include "core/core_manager.h"
|
|||
#include "core/core_timing.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/physical_core.h"
|
|||
#include "core/hle/kernel/scheduler.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
#include "core/hle/lock.h"
|
|||
#include "core/settings.h"
|
|||
|
|||
namespace Core { |
|||
|
|||
CoreManager::CoreManager(System& system, std::size_t core_index) |
|||
: global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( |
|||
core_index)}, |
|||
core_timing{system.CoreTiming()}, core_index{core_index} {} |
|||
|
|||
CoreManager::~CoreManager() = default; |
|||
|
|||
void CoreManager::RunLoop(bool tight_loop) { |
|||
Reschedule(); |
|||
|
|||
// If we don't have a currently active thread then don't execute instructions,
|
|||
// instead advance to the next event and try to yield to the next thread
|
|||
if (Kernel::GetCurrentThread() == nullptr) { |
|||
LOG_TRACE(Core, "Core-{} idling", core_index); |
|||
core_timing.Idle(); |
|||
} else { |
|||
if (tight_loop) { |
|||
physical_core.Run(); |
|||
} else { |
|||
physical_core.Step(); |
|||
} |
|||
} |
|||
core_timing.Advance(); |
|||
|
|||
Reschedule(); |
|||
} |
|||
|
|||
void CoreManager::SingleStep() { |
|||
return RunLoop(false); |
|||
} |
|||
|
|||
void CoreManager::PrepareReschedule() { |
|||
physical_core.Stop(); |
|||
} |
|||
|
|||
void CoreManager::Reschedule() { |
|||
// Lock the global kernel mutex when we manipulate the HLE state
|
|||
std::lock_guard lock(HLE::g_hle_lock); |
|||
|
|||
global_scheduler.SelectThread(core_index); |
|||
|
|||
physical_core.Scheduler().TryDoContextSwitch(); |
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -0,0 +1,63 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <atomic> |
|||
#include <cstddef> |
|||
#include <memory> |
|||
#include "common/common_types.h" |
|||
|
|||
namespace Kernel { |
|||
class GlobalScheduler; |
|||
class PhysicalCore; |
|||
} // namespace Kernel |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} |
|||
|
|||
namespace Core::Timing { |
|||
class CoreTiming; |
|||
} |
|||
|
|||
namespace Memory { |
|||
class Memory; |
|||
} |
|||
|
|||
namespace Core { |
|||
|
|||
constexpr unsigned NUM_CPU_CORES{4}; |
|||
|
|||
class CoreManager { |
|||
public: |
|||
CoreManager(System& system, std::size_t core_index); |
|||
~CoreManager(); |
|||
|
|||
void RunLoop(bool tight_loop = true); |
|||
|
|||
void SingleStep(); |
|||
|
|||
void PrepareReschedule(); |
|||
|
|||
bool IsMainCore() const { |
|||
return core_index == 0; |
|||
} |
|||
|
|||
std::size_t CoreIndex() const { |
|||
return core_index; |
|||
} |
|||
|
|||
private: |
|||
void Reschedule(); |
|||
|
|||
Kernel::GlobalScheduler& global_scheduler; |
|||
Kernel::PhysicalCore& physical_core; |
|||
Timing::CoreTiming& core_timing; |
|||
|
|||
std::atomic<bool> reschedule_pending = false; |
|||
std::size_t core_index; |
|||
}; |
|||
|
|||
} // namespace Core |
|||
@ -1,152 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/core.h"
|
|||
#include "core/core_cpu.h"
|
|||
#include "core/core_timing.h"
|
|||
#include "core/cpu_core_manager.h"
|
|||
#include "core/gdbstub/gdbstub.h"
|
|||
#include "core/settings.h"
|
|||
|
|||
namespace Core { |
|||
namespace { |
|||
void RunCpuCore(const System& system, Cpu& cpu_state) { |
|||
while (system.IsPoweredOn()) { |
|||
cpu_state.RunLoop(true); |
|||
} |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
CpuCoreManager::CpuCoreManager(System& system) : system{system} {} |
|||
CpuCoreManager::~CpuCoreManager() = default; |
|||
|
|||
void CpuCoreManager::Initialize() { |
|||
barrier = std::make_unique<CpuBarrier>(); |
|||
exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); |
|||
|
|||
for (std::size_t index = 0; index < cores.size(); ++index) { |
|||
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); |
|||
} |
|||
} |
|||
|
|||
void CpuCoreManager::StartThreads() { |
|||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
|||
// CPU core 0 is run on the main thread
|
|||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); |
|||
if (!Settings::values.use_multi_core) { |
|||
return; |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < core_threads.size(); ++index) { |
|||
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system), |
|||
std::ref(*cores[index + 1])); |
|||
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get(); |
|||
} |
|||
} |
|||
|
|||
void CpuCoreManager::Shutdown() { |
|||
barrier->NotifyEnd(); |
|||
if (Settings::values.use_multi_core) { |
|||
for (auto& thread : core_threads) { |
|||
thread->join(); |
|||
thread.reset(); |
|||
} |
|||
} |
|||
|
|||
thread_to_cpu.clear(); |
|||
for (auto& cpu_core : cores) { |
|||
cpu_core->Shutdown(); |
|||
cpu_core.reset(); |
|||
} |
|||
|
|||
exclusive_monitor.reset(); |
|||
barrier.reset(); |
|||
} |
|||
|
|||
Cpu& CpuCoreManager::GetCore(std::size_t index) { |
|||
return *cores.at(index); |
|||
} |
|||
|
|||
const Cpu& CpuCoreManager::GetCore(std::size_t index) const { |
|||
return *cores.at(index); |
|||
} |
|||
|
|||
ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() { |
|||
return *exclusive_monitor; |
|||
} |
|||
|
|||
const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const { |
|||
return *exclusive_monitor; |
|||
} |
|||
|
|||
Cpu& CpuCoreManager::GetCurrentCore() { |
|||
if (Settings::values.use_multi_core) { |
|||
const auto& search = thread_to_cpu.find(std::this_thread::get_id()); |
|||
ASSERT(search != thread_to_cpu.end()); |
|||
ASSERT(search->second); |
|||
return *search->second; |
|||
} |
|||
|
|||
// Otherwise, use single-threaded mode active_core variable
|
|||
return *cores[active_core]; |
|||
} |
|||
|
|||
const Cpu& CpuCoreManager::GetCurrentCore() const { |
|||
if (Settings::values.use_multi_core) { |
|||
const auto& search = thread_to_cpu.find(std::this_thread::get_id()); |
|||
ASSERT(search != thread_to_cpu.end()); |
|||
ASSERT(search->second); |
|||
return *search->second; |
|||
} |
|||
|
|||
// Otherwise, use single-threaded mode active_core variable
|
|||
return *cores[active_core]; |
|||
} |
|||
|
|||
void CpuCoreManager::RunLoop(bool tight_loop) { |
|||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
|||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); |
|||
|
|||
if (GDBStub::IsServerEnabled()) { |
|||
GDBStub::HandlePacket(); |
|||
|
|||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
|||
// execute. Otherwise, get out of the loop function.
|
|||
if (GDBStub::GetCpuHaltFlag()) { |
|||
if (GDBStub::GetCpuStepFlag()) { |
|||
tight_loop = false; |
|||
} else { |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
auto& core_timing = system.CoreTiming(); |
|||
core_timing.ResetRun(); |
|||
bool keep_running{}; |
|||
do { |
|||
keep_running = false; |
|||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { |
|||
core_timing.SwitchContext(active_core); |
|||
if (core_timing.CanCurrentContextRun()) { |
|||
cores[active_core]->RunLoop(tight_loop); |
|||
} |
|||
keep_running |= core_timing.CanCurrentContextRun(); |
|||
} |
|||
} while (keep_running); |
|||
|
|||
if (GDBStub::IsServerEnabled()) { |
|||
GDBStub::SetCpuStepFlag(false); |
|||
} |
|||
} |
|||
|
|||
void CpuCoreManager::InvalidateAllInstructionCaches() { |
|||
for (auto& cpu : cores) { |
|||
cpu->ArmInterface().ClearInstructionCache(); |
|||
} |
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -1,62 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <map> |
|||
#include <memory> |
|||
#include <thread> |
|||
|
|||
namespace Core { |
|||
|
|||
class Cpu; |
|||
class CpuBarrier; |
|||
class ExclusiveMonitor; |
|||
class System; |
|||
|
|||
class CpuCoreManager { |
|||
public: |
|||
explicit CpuCoreManager(System& system); |
|||
CpuCoreManager(const CpuCoreManager&) = delete; |
|||
CpuCoreManager(CpuCoreManager&&) = delete; |
|||
|
|||
~CpuCoreManager(); |
|||
|
|||
CpuCoreManager& operator=(const CpuCoreManager&) = delete; |
|||
CpuCoreManager& operator=(CpuCoreManager&&) = delete; |
|||
|
|||
void Initialize(); |
|||
void StartThreads(); |
|||
void Shutdown(); |
|||
|
|||
Cpu& GetCore(std::size_t index); |
|||
const Cpu& GetCore(std::size_t index) const; |
|||
|
|||
Cpu& GetCurrentCore(); |
|||
const Cpu& GetCurrentCore() const; |
|||
|
|||
ExclusiveMonitor& GetExclusiveMonitor(); |
|||
const ExclusiveMonitor& GetExclusiveMonitor() const; |
|||
|
|||
void RunLoop(bool tight_loop); |
|||
|
|||
void InvalidateAllInstructionCaches(); |
|||
|
|||
private: |
|||
static constexpr std::size_t NUM_CPU_CORES = 4; |
|||
|
|||
std::unique_ptr<ExclusiveMonitor> exclusive_monitor; |
|||
std::unique_ptr<CpuBarrier> barrier; |
|||
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores; |
|||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads; |
|||
std::size_t active_core{}; ///< Active core, only used in single thread mode |
|||
|
|||
/// Map of guest threads to CPU cores |
|||
std::map<std::thread::id, Cpu*> thread_to_cpu; |
|||
|
|||
System& system; |
|||
}; |
|||
|
|||
} // namespace Core |
|||
@ -0,0 +1,83 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/core.h"
|
|||
#include "core/core_manager.h"
|
|||
#include "core/core_timing.h"
|
|||
#include "core/cpu_manager.h"
|
|||
#include "core/gdbstub/gdbstub.h"
|
|||
#include "core/settings.h"
|
|||
|
|||
namespace Core { |
|||
|
|||
CpuManager::CpuManager(System& system) : system{system} {} |
|||
CpuManager::~CpuManager() = default; |
|||
|
|||
void CpuManager::Initialize() { |
|||
for (std::size_t index = 0; index < core_managers.size(); ++index) { |
|||
core_managers[index] = std::make_unique<CoreManager>(system, index); |
|||
} |
|||
} |
|||
|
|||
void CpuManager::Shutdown() { |
|||
for (auto& cpu_core : core_managers) { |
|||
cpu_core.reset(); |
|||
} |
|||
} |
|||
|
|||
CoreManager& CpuManager::GetCoreManager(std::size_t index) { |
|||
return *core_managers.at(index); |
|||
} |
|||
|
|||
const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { |
|||
return *core_managers.at(index); |
|||
} |
|||
|
|||
CoreManager& CpuManager::GetCurrentCoreManager() { |
|||
// Otherwise, use single-threaded mode active_core variable
|
|||
return *core_managers[active_core]; |
|||
} |
|||
|
|||
const CoreManager& CpuManager::GetCurrentCoreManager() const { |
|||
// Otherwise, use single-threaded mode active_core variable
|
|||
return *core_managers[active_core]; |
|||
} |
|||
|
|||
void CpuManager::RunLoop(bool tight_loop) { |
|||
if (GDBStub::IsServerEnabled()) { |
|||
GDBStub::HandlePacket(); |
|||
|
|||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
|||
// execute. Otherwise, get out of the loop function.
|
|||
if (GDBStub::GetCpuHaltFlag()) { |
|||
if (GDBStub::GetCpuStepFlag()) { |
|||
tight_loop = false; |
|||
} else { |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
auto& core_timing = system.CoreTiming(); |
|||
core_timing.ResetRun(); |
|||
bool keep_running{}; |
|||
do { |
|||
keep_running = false; |
|||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { |
|||
core_timing.SwitchContext(active_core); |
|||
if (core_timing.CanCurrentContextRun()) { |
|||
core_managers[active_core]->RunLoop(tight_loop); |
|||
} |
|||
keep_running |= core_timing.CanCurrentContextRun(); |
|||
} |
|||
} while (keep_running); |
|||
|
|||
if (GDBStub::IsServerEnabled()) { |
|||
GDBStub::SetCpuStepFlag(false); |
|||
} |
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -0,0 +1,50 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <memory> |
|||
|
|||
namespace Core { |
|||
|
|||
class CoreManager; |
|||
class System; |
|||
|
|||
class CpuManager { |
|||
public: |
|||
explicit CpuManager(System& system); |
|||
CpuManager(const CpuManager&) = delete; |
|||
CpuManager(CpuManager&&) = delete; |
|||
|
|||
~CpuManager(); |
|||
|
|||
CpuManager& operator=(const CpuManager&) = delete; |
|||
CpuManager& operator=(CpuManager&&) = delete; |
|||
|
|||
void Initialize(); |
|||
void Shutdown(); |
|||
|
|||
CoreManager& GetCoreManager(std::size_t index); |
|||
const CoreManager& GetCoreManager(std::size_t index) const; |
|||
|
|||
CoreManager& GetCurrentCoreManager(); |
|||
const CoreManager& GetCurrentCoreManager() const; |
|||
|
|||
std::size_t GetActiveCoreIndex() const { |
|||
return active_core; |
|||
} |
|||
|
|||
void RunLoop(bool tight_loop); |
|||
|
|||
private: |
|||
static constexpr std::size_t NUM_CPU_CORES = 4; |
|||
|
|||
std::array<std::unique_ptr<CoreManager>, NUM_CPU_CORES> core_managers; |
|||
std::size_t active_core{}; ///< Active core, only used in single thread mode |
|||
|
|||
System& system; |
|||
}; |
|||
|
|||
} // namespace Core |
|||
@ -0,0 +1,52 @@ |
|||
// Copyright 2020 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/logging/log.h"
|
|||
#include "core/arm/arm_interface.h"
|
|||
#ifdef ARCHITECTURE_x86_64
|
|||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
|||
#endif
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/arm/unicorn/arm_unicorn.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/physical_core.h"
|
|||
#include "core/hle/kernel/scheduler.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
PhysicalCore::PhysicalCore(Core::System& system, KernelCore& kernel, std::size_t id, |
|||
Core::ExclusiveMonitor& exclusive_monitor) |
|||
: core_index{id}, kernel{kernel} { |
|||
#ifdef ARCHITECTURE_x86_64
|
|||
arm_interface = std::make_shared<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index); |
|||
#else
|
|||
arm_interface = std::make_shared<Core::ARM_Unicorn>(system); |
|||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
|||
#endif
|
|||
|
|||
scheduler = std::make_shared<Kernel::Scheduler>(system, *arm_interface, core_index); |
|||
} |
|||
|
|||
PhysicalCore::~PhysicalCore() = default; |
|||
|
|||
void PhysicalCore::Run() { |
|||
arm_interface->Run(); |
|||
arm_interface->ClearExclusiveState(); |
|||
} |
|||
|
|||
void PhysicalCore::Step() { |
|||
arm_interface->Step(); |
|||
} |
|||
|
|||
void PhysicalCore::Stop() { |
|||
arm_interface->PrepareReschedule(); |
|||
} |
|||
|
|||
void PhysicalCore::Shutdown() { |
|||
scheduler->Shutdown(); |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -0,0 +1,74 @@ |
|||
// Copyright 2020 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <cstddef> |
|||
#include <memory> |
|||
|
|||
namespace Kernel { |
|||
class Scheduler; |
|||
} // namespace Kernel |
|||
|
|||
namespace Core { |
|||
class ARM_Interface; |
|||
class ExclusiveMonitor; |
|||
class System; |
|||
} // namespace Core |
|||
|
|||
namespace Kernel { |
|||
|
|||
class PhysicalCore { |
|||
public: |
|||
PhysicalCore(Core::System& system, KernelCore& kernel, std::size_t id, |
|||
Core::ExclusiveMonitor& exclusive_monitor); |
|||
|
|||
~PhysicalCore(); |
|||
|
|||
/// Execute current jit state |
|||
void Run(); |
|||
/// Execute a single instruction in current jit. |
|||
void Step(); |
|||
/// Stop JIT execution/exit |
|||
void Stop(); |
|||
|
|||
// Shutdown this physical core. |
|||
void Shutdown(); |
|||
|
|||
Core::ARM_Interface& ArmInterface() { |
|||
return *arm_interface; |
|||
} |
|||
|
|||
const Core::ARM_Interface& ArmInterface() const { |
|||
return *arm_interface; |
|||
} |
|||
|
|||
bool IsMainCore() const { |
|||
return core_index == 0; |
|||
} |
|||
|
|||
bool IsSystemCore() const { |
|||
return core_index == 3; |
|||
} |
|||
|
|||
std::size_t CoreIndex() const { |
|||
return core_index; |
|||
} |
|||
|
|||
Kernel::Scheduler& Scheduler() { |
|||
return *scheduler; |
|||
} |
|||
|
|||
const Kernel::Scheduler& Scheduler() const { |
|||
return *scheduler; |
|||
} |
|||
|
|||
private: |
|||
std::size_t core_index; |
|||
KernelCore& kernel; |
|||
std::shared_ptr<Core::ARM_Interface> arm_interface; |
|||
std::shared_ptr<Kernel::Scheduler> scheduler; |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue