232d95b56e
Keeps the CPU-specific behavior from being spread throughout the main System class. This will also act as the home to contain member functions that perform operations on all cores. The reason for this being that the following pattern is sort of prevalent throughout sections of the codebase: If clearing the instruction cache for all 4 cores is necessary: Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); This is kind of... well, silly to copy around whenever it's needed. especially when it can be reduced down to a single line. This change also puts the basics in place to begin "ungrafting" all of the forwarding member functions from the System class that are used to access CPU state or invoke CPU-specific behavior. As such, this change itself makes no changes to the direct external interface of System. This will be covered by another changeset.
143 lines
4.1 KiB
C++
143 lines
4.1 KiB
C++
// 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/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() = default;
|
|
CpuCoreManager::~CpuCoreManager() = default;
|
|
|
|
void CpuCoreManager::Initialize(System& system) {
|
|
barrier = std::make_unique<CpuBarrier>();
|
|
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
|
|
|
for (std::size_t index = 0; index < cores.size(); ++index) {
|
|
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
|
|
}
|
|
|
|
// 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.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
|
cores[active_core]->RunLoop(tight_loop);
|
|
if (Settings::values.use_multi_core) {
|
|
// Cores 1-3 are run on other threads in this mode
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (GDBStub::IsServerEnabled()) {
|
|
GDBStub::SetCpuStepFlag(false);
|
|
}
|
|
}
|
|
|
|
void CpuCoreManager::InvalidateAllInstructionCaches() {
|
|
for (auto& cpu : cores) {
|
|
cpu->ArmInterface().ClearInstructionCache();
|
|
}
|
|
}
|
|
|
|
} // namespace Core
|