Sync push

This commit is contained in:
OmniaX-Dev 2026-05-22 21:28:02 +02:00
parent ad190f9f4f
commit 678bbf6381
2 changed files with 279 additions and 62 deletions

View file

@ -24,41 +24,178 @@ namespace dragon
{ {
namespace hw namespace hw
{ {
CPU::CPU(BUS& bus) : m_bus(bus) // =====================================================================
// Core
// =====================================================================
Core::Core(CPU& cpu, BUS& bus, u32 core_id)
: m_cpu(cpu)
, m_bus(bus)
, m_coreId(core_id)
, m_mmu(bus, &m_accessMode)
{ {
reset();
}
void Core::reset(void)
{
// Architectural reset state. At power-on every core sits here;
// the BIOS / kernel then differentiates them by CORE_ID and wakes
// secondaries via IPI.
m_pc = 0;
m_sp = 0;
m_fl = 0;
m_iv = 0;
m_epc = 0;
m_cause = 0;
m_badaddr = 0;
m_estatus = 0;
m_scratch = 0;
m_faultingPc = 0;
m_gpr.fill(0);
m_accessMode = MMUAccessMode::Privileged;
m_interruptsEnabled = false;
m_mmu.disable();
m_mmu.setPtb(0);
m_mmu.setCurrentAsid(0);
m_irqPending.store(false, std::memory_order_release);
// Core 0 wakes at reset; all others stay halted until IPI'd.
m_halted.store(m_coreId != 0, std::memory_order_release);
}
void Core::run(void)
{
// Unhalt this core if it was halted (e.g. after IPI wakes us from HALT).
m_halted.store(false, std::memory_order_release);
while (!m_halted.load(std::memory_order_acquire))
{
// Check for pending interrupts before each instruction.
// Only take the interrupt if IE is set; otherwise leave it
// pending so the kernel can decide when to handle it.
if (m_irqPending.load(std::memory_order_acquire) && m_interruptsEnabled)
{
m_irqPending.store(false, std::memory_order_release);
take_exception(ExceptionCause::HardwareInterrupt, 0);
continue;
}
// Save the PC of the instruction about to execute, so that if
// it faults we know what to put in EPC.
m_faultingPc = m_pc;
try
{
step();
}
catch (const GuestException& e)
{
take_exception(e.cause, e.fault_addr);
}
}
}
void Core::halt(void)
{
m_halted.store(true, std::memory_order_release);
}
void Core::signalInterrupt(void)
{
m_irqPending.store(true, std::memory_order_release);
// Future: also wake from HALT via condition variable.
}
void Core::step(void)
{
// TODO: fetch instruction at m_pc, decode, execute.
// For now, a placeholder that halts immediately to avoid infinite loops.
m_halted.store(true, std::memory_order_release);
}
void Core::take_exception(ExceptionCause cause, VirtualAddress fault_addr)
{
// Pack current mode + IE into ESTATUS for later restoration by iret.
// Layout: bit 0 = previous IE, bit 1 = previous mode (0=priv, 1=user).
u32 prev_mode_bit = (m_accessMode == MMUAccessMode::User) ? 1u : 0u;
u32 prev_ie_bit = m_interruptsEnabled ? 1u : 0u;
m_estatus = (prev_mode_bit << 1) | prev_ie_bit;
// Save fault information.
m_epc = m_faultingPc;
m_cause = static_cast<u32>(cause);
m_badaddr = fault_addr;
// Switch to supervisor mode, disable interrupts.
m_accessMode = MMUAccessMode::Privileged;
m_interruptsEnabled = false;
// Jump to the single exception entry point.
m_pc = m_iv;
}
u32 Core::readSpecialReg(u8 num)
{
// TODO: privilege check based on register's access policy.
// TODO: dispatch by register number.
// For MMU-owned registers (PTB, ASID, MMU enable), forward to m_mmu.
(void)num;
return 0;
}
void Core::writeSpecialReg(u8 num, u32 value)
{
// TODO: privilege check based on register's access policy.
// TODO: dispatch by register number, applying side effects.
(void)num;
(void)value;
}
// =====================================================================
// CPU
// =====================================================================
CPU::CPU(BUS& bus, u32 num_cores)
: m_bus(bus)
{
m_cores.reserve(num_cores);
for (u32 i = 0; i < num_cores; ++i)
m_cores.emplace_back(std::make_unique<Core>(*this, bus, i));
} }
bool CPU::run(void) bool CPU::run(void)
{ {
// Should probably be moved to Core? m_stopping.store(false, std::memory_order_release);
// while (!m_halted)
// { // Phase A: single-threaded. Run core 0 only.
// try // (Secondary cores start halted; nothing to do until SMP wakeup.)
// { if (!m_cores.empty())
// step(); // fetch + decode + execute one instruction m_cores[0]->run();
// }
// catch (const GuestException& e) // Phase B (future): spawn one host thread per core.
// { // std::vector<std::thread> threads;
// take_exception(e.cause, e.fault_addr); // for (auto& c : m_cores)
// } // threads.emplace_back([&c]() { c->run(); });
// } // for (auto& t : threads) t.join();
return true; return true;
} }
void CPU::take_exception(ExceptionCause cause, VirtualAddress fault_addr) void CPU::stop(void)
{ {
// m_epc = m_faulting_pc; // saved before/during the faulting instr m_stopping.store(true, std::memory_order_release);
// m_cause = static_cast<u32>(cause); for (auto& c : m_cores)
// m_badaddr = fault_addr; c->halt();
// m_estatus = pack_mode_and_ie(); // save mode/IE
// set_mode_supervisor();
// set_interrupts_enabled(false);
// m_pc = m_iv; // jump to single entry point
} }
void CPU::step(void) void CPU::sendIpi(u32 target_core)
{ {
if (target_core < m_cores.size())
m_cores[target_core]->signalInterrupt();
} }
} }
} }

View file

@ -23,58 +23,138 @@
#include "../common/Data.hpp" #include "../common/Data.hpp"
#include "MMU.hpp" #include "MMU.hpp"
#include <array>
#include <atomic>
#include <memory>
#include <vector>
namespace dragon namespace dragon
{ {
namespace hw namespace hw
{ {
class BUS; class BUS;
class CPU class CPU; // forward declaration so Core can reference its parent
// One emulated CPU core. Each core has its own register file, its own
// MMU, its own privilege/interrupt state, and (eventually) its own host
// thread. Cores are independent except for the shared BUS / RAM.
class Core
{ {
public: struct Core
{
MMU m_mmu;
bool m_halted { true };
std::array<u32, 32> m_gpr;
u32 m_pc;
u32 m_sp;
u32 m_fl;
u32 m_mode;
u32 m_iv;
u32 m_epc;
u32 m_cause;
u32 m_badaddr;
u32 m_estatus;
u32 m_scratch;
u32 m_ptb;
u32 m_asid;
u32 m_coreid;
u32 m_cycle;
u32 m_timer_deadline;
inline Core(CPU& cpu) : m_mmu(cpu.m_bus, &(cpu.m_accessMode))
{
}
private:
};
public: public:
CPU(BUS& bus); Core(CPU& cpu, BUS& bus, u32 core_id);
bool run(void);
// Main execution loop. Runs until m_halted becomes true.
// In single-thread mode: called directly by CPU::run().
// In multi-thread mode: called by a host thread dedicated to this core.
void run(void);
// Stop the run loop. Called by another thread (CPU::stop, debugger, etc.).
// Returns when the core's run loop has noticed and is about to exit.
void halt(void);
// Wake this core from a HALT instruction. Set by another core sending
// an IPI. The flag is checked between instructions.
void signalInterrupt(void);
// Reset to the architectural reset state: PC=0, supervisor mode,
// interrupts disabled, MMU disabled, registers zeroed.
void reset(void);
// Inspection (used by debugger, tests, host orchestration).
inline u32 coreId(void) const { return m_coreId; }
inline bool isHalted(void) const { return m_halted.load(std::memory_order_acquire); }
inline MMU& mmu(void) { return m_mmu; }
private: private:
void take_exception(ExceptionCause cause, VirtualAddress fault_addr); // One iteration of the fetch-decode-execute cycle.
void step(void); void step(void);
// Take an exception: save state, switch to supervisor, jump to IV.
void take_exception(ExceptionCause cause, VirtualAddress fault_addr);
// GP register access — handles R0-as-zero invariant in one place.
inline u32 gpr(u8 idx) const { return idx == 0 ? 0 : m_gpr[idx]; }
inline void setGpr(u8 idx, u32 value) { if (idx != 0) m_gpr[idx] = value; }
// Special register access (used by mfsr/mtsr after privilege check).
u32 readSpecialReg(u8 num);
void writeSpecialReg(u8 num, u32 value);
private: private:
MMUAccessMode m_accessMode { MMUAccessMode::Privileged }; // Parent references. Lifetime-managed by CPU.
CPU& m_cpu;
BUS& m_bus; BUS& m_bus;
Core m_core0 { *this }; const u32 m_coreId;
Core m_core1 { *this };
Core m_core2 { *this }; // MMU lives in the Core — per-core translation state.
Core m_core3 { *this }; MMU m_mmu;
// === State observed by other threads ===
// These need to be atomic because peer cores or the host can
// read/write them while this core is running.
std::atomic<bool> m_halted { true }; // start halted; only core 0 wakes at reset
std::atomic<bool> m_irqPending { false }; // set externally on IPI delivery
// === State only this core's thread touches ===
// No atomicity needed; access is single-threaded by construction.
// Mode / interrupt enable. Owned by Core, referenced by MMU.
MMUAccessMode m_accessMode { MMUAccessMode::Privileged };
bool m_interruptsEnabled { false };
// GP registers (R0..R31). R0 invariant enforced by accessors above.
std::array<u32, 32> m_gpr {};
// Core control registers
u32 m_pc { 0 };
u32 m_sp { 0 };
u32 m_fl { 0 };
// Implementation detail: PC of the instruction currently executing.
// Saved into EPC if that instruction faults.
u32 m_faultingPc { 0 };
// Exception state registers
u32 m_iv { 0 }; // exception handler entry point
u32 m_epc { 0 }; // saved PC at fault
u32 m_cause { 0 }; // ExceptionCause value
u32 m_badaddr { 0 }; // faulting address (or other cause-specific data)
u32 m_estatus { 0 }; // saved mode/IE bits
u32 m_scratch { 0 }; // per-core OS scratch
// Note: PTB, ASID, MMU enable live in m_mmu — Core dispatches mfsr/mtsr
// to MMU accessors for those register numbers.
};
class CPU
{
public:
// Construct with a fixed core count. Cores are created in the
// constructor and live until CPU is destroyed.
CPU(BUS& bus, u32 num_cores);
// Run until all cores halt permanently. Phase A: runs core 0
// directly on the calling thread. Later: spawns one host thread
// per core and joins them.
bool run(void);
// Stop all cores. Each core checks its halt flag between instructions
// and exits cleanly. Call from another thread or from the host.
void stop(void);
// Access a specific core (for debugger, BIOS init, IPI delivery).
inline Core& core(u32 idx) { return *m_cores.at(idx); }
inline u32 numCores(void) const { return static_cast<u32>(m_cores.size()); }
// Deliver an IPI to the target core. Called by an MMIO write to the
// IPI controller region, or by host orchestration.
void sendIpi(u32 target_core);
private:
BUS& m_bus;
std::vector<std::unique_ptr<Core>> m_cores;
std::atomic<bool> m_stopping { false };
}; };
} }
} }