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 NS_PER_SEC 1E9
#define NS_PER_SEC 1E9
#define ONE_BILLION_NS 0x3B9ACA00UL
#define USECS_TO_NSECS(us) (us) * 1000

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -101,7 +101,7 @@ int create_gossamer(std::string& id) {
18, dynamic_cast<PCIDevice*>(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()) {

View File

@ -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()) {