Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions benchmark/bench1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,16 @@ int main(int argc, char** argv) {
return -1;
}

constexpr uint64_t tbr_freq = 16705000;

ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, tbr_freq);
constexpr uint64_t bus_freq = 66'820'000ULL;
constexpr uint64_t tbr_freq = bus_freq / 4;
constexpr uint64_t core_freq = bus_freq * 7 / 2; // 233.87 MHz (CPU PLL ratio of 3.5)

ppc_cpu_init(grackle_obj, {
.version = PPC_VER::MPC750,
.timebase_freq_hz = tbr_freq,
.bus_freq_hz = bus_freq,
.core_freq_hz = core_freq,
});

/* load executable code into RAM at address 0 */
for (i = 0; i < sizeof(cs_code) / sizeof(cs_code[0]); i++) {
Expand Down
12 changes: 6 additions & 6 deletions core/hostevents_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,23 @@ void EventManager::poll_events() {
}
return;
}
// Control-Alt-Shift-+: speed up icnt_factor
// Control-Alt-Shift-+: increase instruction period
if (event.key.keysym.sym == SDLK_EQUALS &&
(event.key.keysym.mod & KMOD_ALL) == (KMOD_LCTRL | KMOD_LALT | KMOD_LSHIFT)
) {
if (event.type == SDL_KEYUP) {
int icnt_counter_val = increment_icnt_factor();
LOG_F(INFO, "Incremented icnt_factor: %d", icnt_counter_val);
uint64_t instruction_period = increment_instruction_period();
LOG_F(INFO, "Incremented instruction period: %d", (int)instruction_period);
}
return;
}
// Control-Alt-Shift--: slow down icnt_factor
// Control-Alt-Shift--: decrease instruction period
if (event.key.keysym.sym == SDLK_MINUS &&
(event.key.keysym.mod & KMOD_ALL) == (KMOD_LCTRL | KMOD_LALT | KMOD_LSHIFT)
) {
if (event.type == SDL_KEYUP) {
int icnt_counter_val = decrement_icnt_factor();
LOG_F(INFO, "Decremented icnt_factor: %d", icnt_counter_val);
uint64_t instruction_period = decrement_instruction_period();
LOG_F(INFO, "Decremented instruction period: %d", (int)instruction_period);
}
return;
}
Expand Down
17 changes: 12 additions & 5 deletions cpu/ppc/ppcemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,15 @@ typedef enum {
// Placeholder value for cases where we don't have a currently-executing instruction.
constexpr uint32_t NO_OPCODE = 0;

struct PPC_CPU_Config {
uint32_t version;
uint64_t timebase_freq_hz;
uint64_t bus_freq_hz;
uint64_t core_freq_hz;
};

// Function prototypes
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool include_601, uint64_t tb_freq);
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, const PPC_CPU_Config& config);
extern void ppc_mmu_init();

void ppc_illegalop(uint32_t opcode);
Expand Down Expand Up @@ -720,10 +727,10 @@ extern void ppc_change_endian(bool newLE);
uint64_t get_reg(std::string reg_name); /* get content of the register reg_name */
void set_reg(std::string reg_name, uint64_t val); /* set reg_name to val */

/* icnt_factor control */
extern int increment_icnt_factor();
extern int decrement_icnt_factor();
extern int get_icnt_factor();
/* instruction period control */
extern uint64_t increment_instruction_period();
extern uint64_t decrement_instruction_period();
extern uint64_t get_instruction_period();

/* toggle_g_realtime */
extern bool toggle_g_realtime();
Expand Down
138 changes: 93 additions & 45 deletions cpu/ppc/ppcexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ppcdisasm.h"

#include <algorithm>
#include <cinttypes>
#include <chrono>
#include <cstring>
#include <iostream>
Expand Down Expand Up @@ -132,8 +133,18 @@ bool dec_exception_pending = false;
/* variables related to virtual time */
bool g_realtime = false;
uint64_t g_nanoseconds_base;
uint64_t g_icycles;
int icnt_factor;
// Fixed-point nanoseconds, with four fractional bits. This provides enough
// precision for the supported CPU clocks and over 36 years before overflow.
using FixedNanoseconds = uint64_t;
static constexpr int VIRT_TIME_FRAC_BITS = 4;
static FixedNanoseconds g_virt_time;
static FixedNanoseconds g_instruction_period;

static constexpr FixedNanoseconds instruction_period_for_frequency(uint64_t core_freq_hz)
{
return (((uint64_t)NS_PER_SEC << VIRT_TIME_FRAC_BITS) + core_freq_hz / 2) /
core_freq_hz;
}

/* global variables related to the timebase facility */
uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR write
Expand Down Expand Up @@ -356,7 +367,7 @@ uint64_t get_virt_time_ns()
if (g_realtime) {
return cpu_now_ns() - g_nanoseconds_base;
} else {
return g_icycles << icnt_factor;
return g_virt_time >> VIRT_TIME_FRAC_BITS;
}
}

Expand All @@ -365,7 +376,7 @@ void set_virt_time_ns(uint64_t time_now)
if (g_realtime) {
g_nanoseconds_base = cpu_now_ns() - time_now - 5000;
} else {
g_icycles = time_now >> icnt_factor;
g_virt_time = time_now << VIRT_TIME_FRAC_BITS;
}
uint64_t time_new = get_virt_time_ns();
if (g_realtime && time_new > time_now) {
Expand All @@ -375,16 +386,16 @@ void set_virt_time_ns(uint64_t time_now)
LOG_F(INFO, "time before: %lld after: %lld change: %lld", time_now, time_new, time_new - time_now);
}

static uint64_t process_events()
static FixedNanoseconds process_events()
{
exec_timer = false;
uint64_t slice_ns = TimerManager::get_instance()->process_timers();
if (slice_ns == 0) {
// execute 25.000 cycles
// execute 25,000 instructions
// if there are no pending timers
return g_icycles + 25000;
return g_virt_time + 25'000 * g_instruction_period;
}
return g_icycles + (slice_ns >> icnt_factor) + 1;
return g_virt_time + (slice_ns << VIRT_TIME_FRAC_BITS);
}

static void force_cycle_counter_reload()
Expand All @@ -393,29 +404,29 @@ static void force_cycle_counter_reload()
exec_timer = true;
}

int increment_icnt_factor()
uint64_t increment_instruction_period()
{
uint64_t time_now = get_virt_time_ns();
icnt_factor += 1;
g_instruction_period += 1;
set_virt_time_ns(time_now);
force_cycle_counter_reload();
return icnt_factor;
return g_instruction_period;
}

int decrement_icnt_factor()
uint64_t decrement_instruction_period()
{
if (icnt_factor > 0) {
if (g_instruction_period > 0) {
uint64_t time_now = get_virt_time_ns();
icnt_factor -= 1;
g_instruction_period -= 1;
set_virt_time_ns(time_now);
force_cycle_counter_reload();
}
return icnt_factor;
return g_instruction_period;
}

int get_icnt_factor()
uint64_t get_instruction_period()
{
return icnt_factor;
return g_instruction_period;
}

bool toggle_g_realtime()
Expand All @@ -437,7 +448,10 @@ typedef enum {
template <ppc_exec_type_t exec_type, endian_switch endian>
static void ppc_exec_inner(uint32_t start_addr, uint32_t size)
{
uint64_t max_cycles = 0;
FixedNanoseconds event_deadline = 0;
// Keep these hot-loop values in registers.
FixedNanoseconds virt_time = g_virt_time;
FixedNanoseconds instruction_period = g_instruction_period;
uint32_t page_start, eb_start, eb_end = 0;
uint32_t opcode;
PPCOpcode* opcode_grabber = ppc_opcode_grabber;
Expand All @@ -460,21 +474,29 @@ static void ppc_exec_inner(uint32_t start_addr, uint32_t size)

opcode = ppc_read_instruction(pc_real);
ppc_main_opcode(opcode_grabber, opcode);
if (g_icycles++ >= max_cycles || exec_timer) [[unlikely]]
max_cycles = process_events();
virt_time += instruction_period;
g_virt_time = virt_time;
if (virt_time >= event_deadline || exec_timer) [[unlikely]] {
event_deadline = process_events();
virt_time = g_virt_time;
instruction_period = g_instruction_period;
}

if (exec_flags) {
if ((exec_flags & EXEF_SLEEP) && !(exec_flags & EXEF_EXCEPTION)) [[unlikely]] {
while (power_on && (exec_flags & EXEF_SLEEP)) {
max_cycles = process_events();
event_deadline = process_events();
virt_time = g_virt_time;
instruction_period = g_instruction_period;
if (!(exec_flags & EXEF_SLEEP)) {
break;
}
if (max_cycles > g_icycles) {
g_icycles = max_cycles;
if (event_deadline > virt_time) {
virt_time = event_deadline;
} else {
g_icycles++;
virt_time += instruction_period;
}
g_virt_time = virt_time;
}
}
if (exec_flags & EXEF_OPC_DECODER) [[unlikely]] {
Expand Down Expand Up @@ -552,7 +574,7 @@ void ppc_exec_single()
uint8_t* pc_real = mmu_translate_imem(ppc_state.pc ATPCP); // &pcp
uint32_t opcode = ppc_read_instruction(pc_real);
ppc_main_opcode(ppc_opcode_grabber, opcode);
g_icycles++;
g_virt_time += g_instruction_period;
process_events();

if (exec_flags) {
Expand Down Expand Up @@ -993,21 +1015,59 @@ void initialize_ppc_opcode_table() {
}
}

void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool do_include_601, uint64_t tb_freq)
static uint32_t get_cpu_pll_value(const PPC_CPU_Config& config)
{
switch (config.version) {
case PPC_VER::MPC603EV:
if (config.core_freq_hz * 2 == config.bus_freq_hz * 9)
return 7; // 4.5:1 ratio
if (config.core_freq_hz == config.bus_freq_hz * 5)
return 11; // 5:1 ratio
if (config.core_freq_hz * 2 == config.bus_freq_hz * 11)
return 9; // 5.5:1 ratio
break;
case PPC_VER::MPC750:
if (config.core_freq_hz * 2 == config.bus_freq_hz * 7)
return 14; // 3.5:1 ratio
if (config.core_freq_hz == config.bus_freq_hz * 6)
return 13; // 6:1 ratio
if (config.core_freq_hz == config.bus_freq_hz * 8)
return 12; // 8:1 ratio
break;
default:
break;
}

ABORT_F("Unsupported CPU/bus frequency combination: %" PRIu64 "/%" PRIu64 " Hz",
config.core_freq_hz, config.bus_freq_hz);
}

void ppc_cpu_init(MemCtrlBase* mem_ctrl, const PPC_CPU_Config& config)
{
if (!config.timebase_freq_hz || !config.bus_freq_hz || !config.core_freq_hz)
ABORT_F("CPU clock frequencies must be non-zero");

mem_ctrl_instance = mem_ctrl;

int_pin = false;
std::memset(&ppc_state, 0, sizeof(ppc_state));
set_host_rounding_mode(0);

ppc_state.spr[SPR::PVR] = cpu_version;
is_601 = (cpu_version >> 16) == 1;
include_601 = !is_601 & do_include_601;
ppc_state.spr[SPR::PVR] = config.version;
switch (config.version) {
case PPC_VER::MPC603EV:
case PPC_VER::MPC750:
ppc_state.spr[SPR::HID1] = get_cpu_pll_value(config) << 28;
break;
default:
break;
}
is_601 = (config.version >> 16) == 1;
include_601 = is_601;
ppc_pow_mode = PPCPowMode::None;
ppc_pow_hid0_mask = 0;

switch (cpu_version) {
switch (config.version) {
case PPC_VER::MPC603:
case PPC_VER::MPC603E:
case PPC_VER::MPC603EV:
Expand Down Expand Up @@ -1037,25 +1097,13 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool do_include_6
mach_timebase_info(&timebase_info);
#endif
g_nanoseconds_base = cpu_now_ns();
g_icycles = 0;

// // // PDM cpu clock calculated at 0x403036CC in r3
// icnt_factor = 11; // 1 instruction = 2048 ns = 0.488 MHz // 00068034 = 0.426036 MHz = 2347.219 ns // floppy doesn't work
// icnt_factor = 10; // 1 instruction = 1024 ns = 0.977 MHz // 000D204C = 0.860236 MHz = 1162.471 ns // [0..10] MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 9; // 1 instruction = 512 ns = 1.953 MHz // 001A6081 = 1.728641 MHz = 578.489 ns // [0..10] MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 8; // 1 instruction = 256 ns = 3.906 MHz // 0034E477 = 3.466359 MHz = 288.487 ns // [0..10] MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 7; // 1 instruction = 128 ns = 7.813 MHz // 0069E54C = 6.939980 MHz = 144.092 ns // [0..10] MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 6; // 1 instruction = 64 ns = 15.625 MHz // 00D3E6F5 = 13.887221 MHz = 72.008 ns // (10..60] = 50, (60..73] = 66, (73..100] = 80 MHz
// icnt_factor = 5; // 1 instruction = 32 ns = 31.250 MHz // 01A7B672 = 27.768434 MHz = 36.012 ns //
icnt_factor = 4; // 1 instruction = 16 ns = 62.500 MHz // 034F0F0F = 55.512847 MHz = 18.013 ns // 6100/60 in Apple System Profiler
// icnt_factor = 3; // 1 instruction = 8 ns = 125.000 MHz // 069E1E1E = 111.025694 MHz = 9.006 ns // (100...) MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 2; // 1 instruction = 4 ns = 250.000 MHz // 0D3C3C3C = 222.051388 MHz = 4.503 ns // (100...) MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 1; // 1 instruction = 2 ns = 500.000 MHz // 1A611A7B = 442.571387 MHz = 2.259 ns // (100...) MHz = invalid clock for PDM gestalt calculation
// icnt_factor = 0; // 1 instruction = 1 ns = 1500.000 MHz // 3465B2D9 = 879.080153 MHz = 1.137 ns // (100...) MHz = invalid clock for PDM gestalt calculation
g_virt_time = 0;
g_instruction_period = instruction_period_for_frequency(config.core_freq_hz);

tbr_wr_timestamp = 0;
rtc_timestamp = 0;
tbr_wr_value = 0;
uint64_t tb_freq = config.timebase_freq_hz;
if (is_601)
tb_freq <<= 7;
tbr_freq_shift = 0;
Expand Down
15 changes: 9 additions & 6 deletions machines/machinealchemy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,19 @@ int MachineAlchemy::initialize(const std::string &id) {
psx_obj->map_phys_ram();

// configure CPU clocks
uint64_t bus_freq = 40000000ULL;
uint64_t bus_freq = 40'000'000ULL;
uint64_t timebase_freq = bus_freq / 4;
uint64_t core_freq = 180'000'000ULL;

psx_obj->set_bus_speed(PSX_BUS_SPEED_40);

// init virtual CPU and request MPC603ev
ppc_cpu_init(psx_obj, PPC_VER::MPC603E, false, timebase_freq);

// CPU frequency is hardcoded to 225 MHz for now
//ppc_state.spr[SPR::HID1] = get_cpu_pll_value(225000000) << 28;
// init virtual CPU and request MPC603e
ppc_cpu_init(psx_obj, {
.version = PPC_VER::MPC603E,
.timebase_freq_hz = timebase_freq,
.bus_freq_hz = bus_freq,
.core_freq_hz = core_freq,
});

return 0;
}
Expand Down
13 changes: 8 additions & 5 deletions machines/machinebondi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,17 @@ int MachineBondi::initialize(const std::string &id) {
setup_ram_slot("RAM_DIMM_2", 0x51, bank_2_size);

// configure CPU clocks
uint64_t bus_freq = 66820000ULL;
uint64_t bus_freq = 66'820'000ULL;
uint64_t timebase_freq = bus_freq / 4;
uint64_t core_freq = bus_freq * 7 / 2; // 233.87 MHz (CPU PLL ratio of 3.5)

// initialize virtual CPU and request MPC750 CPU aka G3
ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq);

// set CPU PLL ratio to 3.5
ppc_state.spr[SPR::HID1] = 0xE << 28;
ppc_cpu_init(grackle_obj, {
.version = PPC_VER::MPC750,
.timebase_freq_hz = timebase_freq,
.bus_freq_hz = bus_freq,
.core_freq_hz = core_freq,
});

return 0;
}
Expand Down
Loading
Loading