Merge pull request #6409 from leoetlino/timing

IOS: Minor IPC fixes
This commit is contained in:
Pierre Bourdon 2018-03-16 18:54:01 +01:00 committed by GitHub
commit 6fdac685d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 12 deletions

View File

@ -153,6 +153,10 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
mmio->Register(base | IPC_PPCCTRL, MMIO::ComplexRead<u32>([](u32) { return ctrl.ppc(); }), mmio->Register(base | IPC_PPCCTRL, MMIO::ComplexRead<u32>([](u32) { return ctrl.ppc(); }),
MMIO::ComplexWrite<u32>([](u32, u32 val) { MMIO::ComplexWrite<u32>([](u32, u32 val) {
ctrl.ppc(val); ctrl.ppc(val);
// The IPC interrupt is triggered when IY1/IY2 is set and
// Y1/Y2 is written to -- even when this results in clearing the bit.
if ((val >> 2 & 1 && ctrl.IY1) || (val >> 1 & 1 && ctrl.IY2))
ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
if (ctrl.X1) if (ctrl.X1)
HLE::GetIOS()->EnqueueIPCRequest(ppc_msg); HLE::GetIOS()->EnqueueIPCRequest(ppc_msg);
HLE::GetIOS()->UpdateIPC(); HLE::GetIOS()->UpdateIPC();
@ -207,13 +211,19 @@ static void UpdateInterrupts(u64 userdata, s64 cyclesLate)
!!(ppc_irq_flags & ppc_irq_masks)); !!(ppc_irq_flags & ppc_irq_masks));
} }
void ClearX1()
{
ctrl.X1 = 0;
}
void GenerateAck(u32 _Address) void GenerateAck(u32 _Address)
{ {
arm_msg = _Address; // dunno if it's really set here, but HLE needs to stay in context
ctrl.Y2 = 1; ctrl.Y2 = 1;
DEBUG_LOG(WII_IPC, "GenerateAck: %08x | %08x [R:%i A:%i E:%i]", ppc_msg, _Address, ctrl.Y1, DEBUG_LOG(WII_IPC, "GenerateAck: %08x | %08x [R:%i A:%i E:%i]", ppc_msg, _Address, ctrl.Y1,
ctrl.Y2, ctrl.X1); ctrl.Y2, ctrl.X1);
CoreTiming::ScheduleEvent(1000, updateInterrupts, 0); // Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
// after Y2 is seen in the control register.
CoreTiming::ScheduleEvent(100 * SystemTimers::TIMER_RATIO, updateInterrupts);
} }
void GenerateReply(u32 _Address) void GenerateReply(u32 _Address)
@ -222,7 +232,9 @@ void GenerateReply(u32 _Address)
ctrl.Y1 = 1; ctrl.Y1 = 1;
DEBUG_LOG(WII_IPC, "GenerateReply: %08x | %08x [R:%i A:%i E:%i]", ppc_msg, _Address, ctrl.Y1, DEBUG_LOG(WII_IPC, "GenerateReply: %08x | %08x [R:%i A:%i E:%i]", ppc_msg, _Address, ctrl.Y1,
ctrl.Y2, ctrl.X1); ctrl.Y2, ctrl.X1);
UpdateInterrupts(); // Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
// after Y1 is seen in the control register.
CoreTiming::ScheduleEvent(100 * SystemTimers::TIMER_RATIO, updateInterrupts);
} }
bool IsReady() bool IsReady()

View File

@ -42,6 +42,7 @@ void DoState(PointerWrap& p);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base); void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
void ClearX1();
void GenerateAck(u32 _Address); void GenerateAck(u32 _Address);
void GenerateReply(u32 _Address); void GenerateReply(u32 _Address);

View File

@ -184,10 +184,18 @@ IPCCommandResult Device::Unsupported(const Request& request)
return GetDefaultReply(IPC_EINVAL); return GetDefaultReply(IPC_EINVAL);
} }
// Returns an IPCCommandResult for a reply that takes 25 us (based on ES::GetTicketViews) // Returns an IPCCommandResult for a reply with an average reply time for devices
// Please avoid using this function if more accurate timings are known.
IPCCommandResult Device::GetDefaultReply(const s32 return_value) IPCCommandResult Device::GetDefaultReply(const s32 return_value)
{ {
return {return_value, true, SystemTimers::GetTicksPerSecond() / 40000}; // Based on a hardware test, a device takes at least ~2700 ticks to reply to an IPC request.
// Depending on how much work a command performs, this can take much longer (10000+)
// especially if the NAND filesystem is accessed.
//
// Because we currently don't emulate timing very accurately, we should not return
// the minimum possible reply time (~960 ticks from the kernel or ~2700 from devices)
// but an average time, otherwise we are going to be much too fast in most cases.
return {return_value, true, 4000 * SystemTimers::TIMER_RATIO};
} }
// Returns an IPCCommandResult with no reply. Useful for async commands that will generate a reply // Returns an IPCCommandResult with no reply. Useful for async commands that will generate a reply

View File

@ -475,7 +475,7 @@ IPCCommandResult Kernel::OpenDevice(OpenRequest& request)
if (new_fd < 0 || new_fd >= IPC_MAX_FDS) if (new_fd < 0 || new_fd >= IPC_MAX_FDS)
{ {
ERROR_LOG(IOS, "Couldn't get a free fd, too many open files"); ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
return Device::Device::GetDefaultReply(FS_EFDEXHAUSTED); return IPCCommandResult{IPC_EMAX, true, 5000 * SystemTimers::TIMER_RATIO};
} }
request.fd = new_fd; request.fd = new_fd;
@ -497,7 +497,7 @@ IPCCommandResult Kernel::OpenDevice(OpenRequest& request)
if (!device) if (!device)
{ {
ERROR_LOG(IOS, "Unknown device: %s", request.path.c_str()); ERROR_LOG(IOS, "Unknown device: %s", request.path.c_str());
return Device::Device::GetDefaultReply(IPC_ENOENT); return {IPC_ENOENT, true, 3700 * SystemTimers::TIMER_RATIO};
} }
IPCCommandResult result = device->Open(request); IPCCommandResult result = device->Open(request);
@ -511,6 +511,9 @@ IPCCommandResult Kernel::OpenDevice(OpenRequest& request)
IPCCommandResult Kernel::HandleIPCCommand(const Request& request) IPCCommandResult Kernel::HandleIPCCommand(const Request& request)
{ {
if (request.command < IPC_CMD_OPEN || request.command > IPC_CMD_IOCTLV)
return IPCCommandResult{IPC_EINVAL, true, 978 * SystemTimers::TIMER_RATIO};
if (request.command == IPC_CMD_OPEN) if (request.command == IPC_CMD_OPEN)
{ {
OpenRequest open_request{request.address}; OpenRequest open_request{request.address};
@ -519,7 +522,7 @@ IPCCommandResult Kernel::HandleIPCCommand(const Request& request)
const auto device = (request.fd < IPC_MAX_FDS) ? m_fdmap[request.fd] : nullptr; const auto device = (request.fd < IPC_MAX_FDS) ? m_fdmap[request.fd] : nullptr;
if (!device) if (!device)
return Device::Device::GetDefaultReply(IPC_EINVAL); return IPCCommandResult{IPC_EINVAL, true, 550 * SystemTimers::TIMER_RATIO};
IPCCommandResult ret; IPCCommandResult ret;
u64 wall_time_before = Common::Timer::GetTimeUs(); u64 wall_time_before = Common::Timer::GetTimeUs();
@ -547,7 +550,7 @@ IPCCommandResult Kernel::HandleIPCCommand(const Request& request)
break; break;
default: default:
ASSERT_MSG(IOS, false, "Unexpected command: %x", request.command); ASSERT_MSG(IOS, false, "Unexpected command: %x", request.command);
ret = Device::Device::GetDefaultReply(IPC_EINVAL); ret = IPCCommandResult{IPC_EINVAL, true, 978 * SystemTimers::TIMER_RATIO};
break; break;
} }
@ -582,7 +585,11 @@ void Kernel::ExecuteIPCCommand(const u32 address)
// Happens AS SOON AS IPC gets a new pointer! // Happens AS SOON AS IPC gets a new pointer!
void Kernel::EnqueueIPCRequest(u32 address) void Kernel::EnqueueIPCRequest(u32 address)
{ {
CoreTiming::ScheduleEvent(1000, s_event_enqueue, address | ENQUEUE_REQUEST_FLAG); // Based on hardware tests, IOS takes between 5µs and 10µs to acknowledge an IPC request.
// Console 1: 456 TB ticks before ACK
// Console 2: 658 TB ticks before ACK
CoreTiming::ScheduleEvent(500 * SystemTimers::TIMER_RATIO, s_event_enqueue,
address | ENQUEUE_REQUEST_FLAG);
} }
// Called to send a reply to an IOS syscall // Called to send a reply to an IOS syscall
@ -615,8 +622,6 @@ void Kernel::HandleIPCEvent(u64 userdata)
UpdateIPC(); UpdateIPC();
} }
// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
// Takes care of routing ipc <-> ipc HLE
void Kernel::UpdateIPC() void Kernel::UpdateIPC()
{ {
if (!IsReady()) if (!IsReady())
@ -624,6 +629,7 @@ void Kernel::UpdateIPC()
if (m_request_queue.size()) if (m_request_queue.size())
{ {
ClearX1();
GenerateAck(m_request_queue.front()); GenerateAck(m_request_queue.front());
u32 command = m_request_queue.front(); u32 command = m_request_queue.front();
m_request_queue.pop_front(); m_request_queue.pop_front();