Implement MPC601 style RTC.

This commit is contained in:
Maxim Poliakovski 2022-03-22 12:23:54 +01:00
parent 4525fd50cc
commit 1d86375061
6 changed files with 76 additions and 36 deletions

View File

@ -33,7 +33,8 @@ using namespace std;
#define MIN_TIMEOUT_NS 200 #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 #define USECS_TO_NSECS(us) (us) * 1000

View File

@ -84,22 +84,27 @@ extern SetPRS ppc_state;
/** symbolic names for frequently used SPRs */ /** symbolic names for frequently used SPRs */
enum SPR : int { enum SPR : int {
MQ = 0, MQ = 0,
XER = 1, XER = 1,
LR = 8, RTCU_U = 4, // user RTCU
CTR = 9, RTCL_U = 5, // user RTCL
DSISR = 18, LR = 8,
DAR = 19, CTR = 9,
DEC = 22, DSISR = 18,
SDR1 = 25, DAR = 19,
SRR0 = 26, RTCU_S = 20, // supervisor RTCU
SRR1 = 27, RTCL_S = 21, // supervisor RTCL
PVR = 287 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 */ /** symbolic names for common PPC processors */
enum PPC_VER : uint32_t { enum PPC_VER : uint32_t {
MPC601 = 0x00010001, MPC601 = 0x00010001,
@ -154,6 +159,7 @@ extern uint64_t timebase_counter;
extern uint64_t tbr_wr_timestamp; extern uint64_t tbr_wr_timestamp;
extern uint64_t tbr_wr_value; extern uint64_t tbr_wr_value;
extern uint64_t tbr_freq_hz; extern uint64_t tbr_freq_hz;
extern uint32_t rtc_lo, rtc_hi;
// Additional steps to prevent overflow? // Additional steps to prevent overflow?
extern int32_t add_result; extern int32_t add_result;
@ -321,7 +327,7 @@ extern uint64_t exceptions_processed;
#endif #endif
// Function prototypes // 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); extern void ppc_mmu_init(uint32_t cpu_version);
void ppc_illegalop(); void ppc_illegalop();

View File

@ -65,11 +65,13 @@ uint64_t g_icycles;
int icnt_factor; int icnt_factor;
/* global variables related to the timebase facility */ /* 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_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 uint64_t timebase_counter; // internal timebase counter
uint32_t decr; // current value of PPC DEC register 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 #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; int i;
mem_ctrl_instance = mem_ctrl; mem_ctrl_instance = mem_ctrl;
//test_timebase_update();
initialize_ppc_opcode_tables(); initialize_ppc_opcode_tables();
// initialize emulator timers // initialize emulator timers
@ -752,7 +753,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) {
icnt_factor = 4; icnt_factor = 4;
tbr_wr_timestamp = 0; tbr_wr_timestamp = 0;
tbr_wr_value = 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; exec_flags = 0;

View File

@ -847,6 +847,20 @@ void dppc_interpreter::ppc_mtmsr() {
mmu_change_mode(); 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() { void dppc_interpreter::ppc_mfspr() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); 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++; num_supervisor_instrs++;
} }
#endif #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() 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(); uint64_t tbr_value = calc_tbr_value();
tbr_wr_value = (tbr_value & mask) | new_val; tbr_wr_value = (tbr_value & mask) | new_val;
tbr_wr_timestamp = get_virt_time_ns(); tbr_wr_timestamp = get_virt_time_ns();
LOG_F(9, "New TBR value = 0x%llu", tbr_wr_value);
} }
void dppc_interpreter::ppc_mtspr() { void dppc_interpreter::ppc_mtspr() {
@ -884,20 +906,30 @@ void dppc_interpreter::ppc_mtspr() {
} }
#endif #endif
if (ref_spr != 287) { uint32_t val = ppc_state.gpr[reg_s];
ppc_state.spr[ref_spr] = 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(); mmu_pat_ctx_changed();
} }
switch (ref_spr) { switch (ref_spr) {
case 284: case SPR::RTCL_S:
update_timebase(0xFFFFFFFF00000000ULL, ppc_state.gpr[reg_s]); rtc_lo = val & 0x3FFFFF80UL;
tbr_wr_timestamp = get_virt_time_ns();
break; break;
case 285: case SPR::RTCU_S:
update_timebase(0x00000000FFFFFFFFULL, (uint64_t)(ppc_state.gpr[reg_s]) << 32); 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; break;
case 528: case 528:
case 529: case 529:
@ -928,10 +960,10 @@ void dppc_interpreter::ppc_mftb() {
uint64_t tbr_value = calc_tbr_value(); uint64_t tbr_value = calc_tbr_value();
switch (ref_spr) { switch (ref_spr) {
case 268: case SPR::TBL_U:
ppc_state.gpr[reg_d] = tbr_value & 0xFFFFFFFFUL; ppc_state.gpr[reg_d] = tbr_value & 0xFFFFFFFFUL;
break; break;
case 269: case SPR::TBU_U:
ppc_state.gpr[reg_d] = (tbr_value >> 32) & 0xFFFFFFFFUL; ppc_state.gpr[reg_d] = (tbr_value >> 32) & 0xFFFFFFFFUL;
break; break;
default: default:

View File

@ -101,7 +101,7 @@ int create_gossamer(std::string& id) {
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ATIRage"))); 18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ATIRage")));
/* Init virtual CPU and request MPC750 CPU aka G3 */ /* 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 // post-initialize all devices
if (gMachineObj->postinit_devices()) { if (gMachineObj->postinit_devices()) {

View File

@ -89,7 +89,7 @@ int create_pdm(std::string& id) {
gMachineObj->add_component("SCSI0", new ScsiBus); gMachineObj->add_component("SCSI0", new ScsiBus);
/* Init virtual CPU and request MPC601 */ /* 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 // post-initialize all devices
if (gMachineObj->postinit_devices()) { if (gMachineObj->postinit_devices()) {