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