From b47de8b0423e33c543c8177a0c12b689cdf04c90 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Tue, 22 Mar 2022 12:23:54 +0100 Subject: [PATCH] Implement MPC601 style RTC. --- core/timermanager.h | 3 +- cpu/ppc/ppcemu.h | 36 +++++++++++++---------- cpu/ppc/ppcexec.cpp | 13 +++++---- cpu/ppc/ppcopcodes.cpp | 56 ++++++++++++++++++++++++++++-------- machines/machinegossamer.cpp | 2 +- machines/machinepdm.cpp | 2 +- 6 files changed, 76 insertions(+), 36 deletions(-) diff --git a/core/timermanager.h b/core/timermanager.h index 9d4d80d..e1b0dd2 100644 --- a/core/timermanager.h +++ b/core/timermanager.h @@ -33,7 +33,8 @@ using namespace std; #define MIN_TIMEOUT_NS 200 -#define NS_PER_SEC 1E9 +#define NS_PER_SEC 1E9 +#define ONE_BILLION_NS 0x3B9ACA00UL #define USECS_TO_NSECS(us) (us) * 1000 diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index c25b7ee..2962d62 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -84,22 +84,27 @@ extern SetPRS ppc_state; /** symbolic names for frequently used SPRs */ enum SPR : int { - MQ = 0, - XER = 1, - LR = 8, - CTR = 9, - DSISR = 18, - DAR = 19, - DEC = 22, - SDR1 = 25, - SRR0 = 26, - SRR1 = 27, - PVR = 287 + MQ = 0, + XER = 1, + RTCU_U = 4, // user RTCU + RTCL_U = 5, // user RTCL + LR = 8, + CTR = 9, + DSISR = 18, + DAR = 19, + RTCU_S = 20, // supervisor RTCU + RTCL_S = 21, // supervisor RTCL + DEC = 22, + SDR1 = 25, + SRR0 = 26, + SRR1 = 27, + TBL_U = 268, // user mode TBL + TBU_U = 269, // user mode TBU + TBL_S = 284, // supervisor TBL + TBU_S = 285, // supervisor TBU + PVR = 287 }; -/** symbolic names for frequently used SPRs */ -enum TBR : int { TBL = 0, TBU = 1 }; - /** symbolic names for common PPC processors */ enum PPC_VER : uint32_t { MPC601 = 0x00010001, @@ -154,6 +159,7 @@ extern uint64_t timebase_counter; extern uint64_t tbr_wr_timestamp; extern uint64_t tbr_wr_value; extern uint64_t tbr_freq_hz; +extern uint32_t rtc_lo, rtc_hi; // Additional steps to prevent overflow? extern int32_t add_result; @@ -321,7 +327,7 @@ extern uint64_t exceptions_processed; #endif // Function prototypes -extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version); +extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq); extern void ppc_mmu_init(uint32_t cpu_version); void ppc_illegalop(); diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index 850a65f..bb7d7af 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -65,11 +65,13 @@ uint64_t g_icycles; int icnt_factor; /* global variables related to the timebase facility */ -uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR write +uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR/RTC write uint64_t tbr_wr_value; // last value written to the TBR -uint64_t tbr_freq_hz; // TBR driving frequency in Hz +uint64_t tbr_freq_hz; // TBR/RTC driving frequency in Hz uint64_t timebase_counter; // internal timebase counter uint32_t decr; // current value of PPC DEC register +uint32_t rtc_lo; // MPC601 RTC lower, counts nanoseconds +uint32_t rtc_hi; // MPC601 RTC upper, counts seconds #ifdef CPU_PROFILING @@ -734,13 +736,12 @@ void initialize_ppc_opcode_tables() { } } -void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) { +void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq) +{ int i; mem_ctrl_instance = mem_ctrl; - //test_timebase_update(); - initialize_ppc_opcode_tables(); // initialize emulator timers @@ -752,7 +753,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) { icnt_factor = 4; tbr_wr_timestamp = 0; tbr_wr_value = 0; - tbr_freq_hz = 16705000; // FIXME: this should be set properly during machine initialization + tbr_freq_hz = tb_freq; exec_flags = 0; diff --git a/cpu/ppc/ppcopcodes.cpp b/cpu/ppc/ppcopcodes.cpp index c79d3e2..ea0e3e6 100644 --- a/cpu/ppc/ppcopcodes.cpp +++ b/cpu/ppc/ppcopcodes.cpp @@ -847,6 +847,20 @@ void dppc_interpreter::ppc_mtmsr() { mmu_change_mode(); } +static inline uint64_t calc_rtcl_value() +{ + uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp; + uint64_t rtc_inc = diff * tbr_freq_hz / NS_PER_SEC; + uint64_t rtc_l = rtc_lo + (rtc_inc << 7); + if (rtc_l >= ONE_BILLION_NS) { // check RTCL overflow + rtc_hi += rtc_l / ONE_BILLION_NS; + rtc_lo = rtc_l % ONE_BILLION_NS; + tbr_wr_timestamp = get_virt_time_ns(); + rtc_l = rtc_lo; + } + return rtc_l & 0x3FFFFF80UL; +} + void dppc_interpreter::ppc_mfspr() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); @@ -855,8 +869,17 @@ void dppc_interpreter::ppc_mfspr() { num_supervisor_instrs++; } #endif - reg_d = (ppc_cur_instruction >> 21) & 31; - ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr]; + + switch (ref_spr) { + case SPR::RTCL_U: + ppc_state.spr[SPR::RTCL_U] = calc_rtcl_value(); + break; + case SPR::RTCU_U: + ppc_state.spr[SPR::RTCL_U] = rtc_hi; + break; + } + + ppc_state.gpr[(ppc_cur_instruction >> 21) & 31] = ppc_state.spr[ref_spr]; } static inline uint64_t calc_tbr_value() @@ -871,7 +894,6 @@ static void update_timebase(uint64_t mask, uint64_t new_val) uint64_t tbr_value = calc_tbr_value(); tbr_wr_value = (tbr_value & mask) | new_val; tbr_wr_timestamp = get_virt_time_ns(); - LOG_F(9, "New TBR value = 0x%llu", tbr_wr_value); } void dppc_interpreter::ppc_mtspr() { @@ -884,20 +906,30 @@ void dppc_interpreter::ppc_mtspr() { } #endif - if (ref_spr != 287) { - ppc_state.spr[ref_spr] = ppc_state.gpr[reg_s]; + uint32_t val = ppc_state.gpr[reg_s]; + + if (ref_spr != SPR::PVR) { // prevent writes to the read-only PVR + ppc_state.spr[ref_spr] = val; } - if (ref_spr == SPR::SDR1) { + if (ref_spr == SPR::SDR1) { // adapt to SDR1 changes mmu_pat_ctx_changed(); } switch (ref_spr) { - case 284: - update_timebase(0xFFFFFFFF00000000ULL, ppc_state.gpr[reg_s]); + case SPR::RTCL_S: + rtc_lo = val & 0x3FFFFF80UL; + tbr_wr_timestamp = get_virt_time_ns(); break; - case 285: - update_timebase(0x00000000FFFFFFFFULL, (uint64_t)(ppc_state.gpr[reg_s]) << 32); + case SPR::RTCU_S: + rtc_hi = val; + tbr_wr_timestamp = get_virt_time_ns(); + break; + case SPR::TBL_S: + update_timebase(0xFFFFFFFF00000000ULL, val); + break; + case SPR::TBU_S: + update_timebase(0x00000000FFFFFFFFULL, (uint64_t)(val) << 32); break; case 528: case 529: @@ -928,10 +960,10 @@ void dppc_interpreter::ppc_mftb() { uint64_t tbr_value = calc_tbr_value(); switch (ref_spr) { - case 268: + case SPR::TBL_U: ppc_state.gpr[reg_d] = tbr_value & 0xFFFFFFFFUL; break; - case 269: + case SPR::TBU_U: ppc_state.gpr[reg_d] = (tbr_value >> 32) & 0xFFFFFFFFUL; break; default: diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp index bc5a2a3..0c7d7f0 100644 --- a/machines/machinegossamer.cpp +++ b/machines/machinegossamer.cpp @@ -101,7 +101,7 @@ int create_gossamer(std::string& id) { 18, dynamic_cast(gMachineObj->get_comp_by_name("ATIRage"))); /* Init virtual CPU and request MPC750 CPU aka G3 */ - ppc_cpu_init(grackle_obj, PPC_VER::MPC750); + ppc_cpu_init(grackle_obj, PPC_VER::MPC750, 16705000ULL); // post-initialize all devices if (gMachineObj->postinit_devices()) { diff --git a/machines/machinepdm.cpp b/machines/machinepdm.cpp index f8ab534..6c244c6 100644 --- a/machines/machinepdm.cpp +++ b/machines/machinepdm.cpp @@ -89,7 +89,7 @@ int create_pdm(std::string& id) { gMachineObj->add_component("SCSI0", new ScsiBus); /* Init virtual CPU and request MPC601 */ - ppc_cpu_init(hmc_obj, PPC_VER::MPC601); + ppc_cpu_init(hmc_obj, PPC_VER::MPC601, 7812500ULL); // post-initialize all devices if (gMachineObj->postinit_devices()) {