IPU: Adjust DMA timings, improve internal calling

[SAVEVERSION+]
This commit is contained in:
refractionpcsx2 2023-09-14 12:51:29 +01:00
parent 2947e11b9b
commit ae5cd7b3c3
7 changed files with 94 additions and 68 deletions

View File

@ -27,6 +27,7 @@
alignas(16) tIPU_cmd ipu_cmd; alignas(16) tIPU_cmd ipu_cmd;
alignas(16) tIPU_BP g_BP; alignas(16) tIPU_BP g_BP;
alignas(16) decoder_t decoder; alignas(16) decoder_t decoder;
IPUStatus IPUCoreStatus;
static void (*IPUWorker)(); static void (*IPUWorker)();
@ -92,15 +93,8 @@ void tIPU_cmd::clear()
__fi void IPUProcessInterrupt() __fi void IPUProcessInterrupt()
{ {
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued) if (ipuRegs.ctrl.BUSY)
IPUWorker(); IPUWorker();
if (ipuRegs.ctrl.BUSY && !IPU1Status.DataRequested && !(cpuRegs.interrupt & 1 << IPU_PROCESS))
{
CPU_INT(IPU_PROCESS, ProcessedData ? ProcessedData : 64);
}
else
ProcessedData = 0;
} }
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
@ -112,6 +106,9 @@ void ipuReset()
std::memset(&ipuRegs, 0, sizeof(ipuRegs)); std::memset(&ipuRegs, 0, sizeof(ipuRegs));
std::memset(&g_BP, 0, sizeof(g_BP)); std::memset(&g_BP, 0, sizeof(g_BP));
std::memset(&decoder, 0, sizeof(decoder)); std::memset(&decoder, 0, sizeof(decoder));
IPUCoreStatus.DataRequested = false;
IPUCoreStatus.WaitingOnIPUFrom= false;
IPUCoreStatus.WaitingOnIPUTo = false;
decoder.picture_structure = FRAME_PICTURE; //default: progressive...my guess:P decoder.picture_structure = FRAME_PICTURE; //default: progressive...my guess:P
@ -149,6 +146,7 @@ bool SaveStateBase::ipuFreeze()
Freeze(coded_block_pattern); Freeze(coded_block_pattern);
Freeze(decoder); Freeze(decoder);
Freeze(ipu_cmd); Freeze(ipu_cmd);
Freeze(IPUCoreStatus);
return IsOkay(); return IsOkay();
} }
@ -471,7 +469,6 @@ __fi void IPUCMD_WRITE(u32 val)
{ {
// don't process anything if currently busy // don't process anything if currently busy
//if (ipuRegs.ctrl.BUSY) Console.WriteLn("IPU BUSY!"); // wait for thread //if (ipuRegs.ctrl.BUSY) Console.WriteLn("IPU BUSY!"); // wait for thread
ProcessedData = 0;
ipuRegs.ctrl.ECD = 0; ipuRegs.ctrl.ECD = 0;
ipuRegs.ctrl.SCD = 0; ipuRegs.ctrl.SCD = 0;
ipu_cmd.clear(); ipu_cmd.clear();
@ -538,9 +535,10 @@ __fi void IPUCMD_WRITE(u32 val)
// Have a short delay immitating the time it takes to run IDEC/BDEC, other commands are near instant. // Have a short delay immitating the time it takes to run IDEC/BDEC, other commands are near instant.
// Mana Khemia/Metal Saga start IDEC then change IPU0 expecting there to be a delay before IDEC sends data. // Mana Khemia/Metal Saga start IDEC then change IPU0 expecting there to be a delay before IDEC sends data.
if (!CommandExecuteQueued && (ipu_cmd.CMD == SCE_IPU_IDEC || ipu_cmd.CMD == SCE_IPU_BDEC)) if (ipu_cmd.CMD == SCE_IPU_IDEC || ipu_cmd.CMD == SCE_IPU_BDEC)
{ {
CommandExecuteQueued = true; IPUCoreStatus.WaitingOnIPUFrom = false;
IPUCoreStatus.WaitingOnIPUTo = false;
CPU_INT(IPU_PROCESS, 64); CPU_INT(IPU_PROCESS, 64);
} }
else else

View File

@ -132,7 +132,7 @@ struct alignas(16) tIPU_BP {
// be possible -- so if the fill fails we'll only return 0 if we don't have enough // be possible -- so if the fill fails we'll only return 0 if we don't have enough
// remaining bits in the FIFO to fill the request. // remaining bits in the FIFO to fill the request.
// Used to do ((FP!=0) && (BP + bits) <= 128) if we get here there's defo not enough data now though // Used to do ((FP!=0) && (BP + bits) <= 128) if we get here there's defo not enough data now though
IPUCoreStatus.WaitingOnIPUTo = true;
return false; return false;
} }
@ -293,8 +293,6 @@ extern bool EnableFMV;
alignas(16) extern tIPU_cmd ipu_cmd; alignas(16) extern tIPU_cmd ipu_cmd;
extern uint eecount_on_last_vdec; extern uint eecount_on_last_vdec;
extern bool CommandExecuteQueued;
extern u32 ProcessedData;
extern void ipuReset(); extern void ipuReset();

View File

@ -40,7 +40,7 @@ void IPU_Fifo_Input::clear()
writepos = 0; writepos = 0;
// Because the FIFO is drained it will request more data immediately // Because the FIFO is drained it will request more data immediately
IPU1Status.DataRequested = true; IPUCoreStatus.DataRequested = true;
if (ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999) if (ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999)
{ {
@ -91,9 +91,7 @@ int IPU_Fifo_Input::write(const u32* pMem, int size)
g_BP.IFC += transfer_size; g_BP.IFC += transfer_size;
if (g_BP.IFC == 8) if (g_BP.IFC == 8)
IPU1Status.DataRequested = false; IPUCoreStatus.DataRequested = false;
CPU_INT(IPU_PROCESS, transfer_size * BIAS);
return transfer_size; return transfer_size;
} }
@ -104,7 +102,7 @@ int IPU_Fifo_Input::read(void *value)
if (g_BP.IFC <= 1) if (g_BP.IFC <= 1)
{ {
// IPU FIFO is empty and DMA is waiting so lets tell the DMA we are ready to put data in the FIFO // IPU FIFO is empty and DMA is waiting so lets tell the DMA we are ready to put data in the FIFO
IPU1Status.DataRequested = true; IPUCoreStatus.DataRequested = true;
if(ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999) if(ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999)
{ {
@ -142,7 +140,7 @@ int IPU_Fifo_Output::write(const u32 *value, uint size)
ipuRegs.ctrl.OFC += transfer_size; ipuRegs.ctrl.OFC += transfer_size;
if(ipu0ch.chcr.STR) if(ipu0ch.chcr.STR)
IPU_INT_FROM(ipuRegs.ctrl.OFC * BIAS); IPU_INT_FROM(1);
return transfer_size; return transfer_size;
} }
@ -181,12 +179,13 @@ void WriteFIFO_IPUin(const mem128_t* value)
IPU_LOG( "WriteFIFO/IPUin <- 0x%08X.%08X.%08X.%08X", value->_u32[0], value->_u32[1], value->_u32[2], value->_u32[3]); IPU_LOG( "WriteFIFO/IPUin <- 0x%08X.%08X.%08X.%08X", value->_u32[0], value->_u32[1], value->_u32[2], value->_u32[3]);
//committing every 16 bytes //committing every 16 bytes
if( ipu_fifo.in.write(value->_u32, 1) == 0 ) if( ipu_fifo.in.write(value->_u32, 1) > 0 )
{ {
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued) if (ipuRegs.ctrl.BUSY && IPUCoreStatus.WaitingOnIPUTo)
{ {
CommandExecuteQueued = false; IPUCoreStatus.WaitingOnIPUFrom = false;
CPU_INT(IPU_PROCESS, 8); IPUCoreStatus.WaitingOnIPUTo = false;
CPU_INT(IPU_PROCESS, 2 * BIAS);
} }
} }
} }

View File

@ -1010,6 +1010,7 @@ __ri static bool mpeg2sliceIDEC()
// IPU0 isn't ready for data, so let's wait for it to be // IPU0 isn't ready for data, so let's wait for it to be
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[1] <= 2) if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[1] <= 2)
{ {
IPUCoreStatus.WaitingOnIPUFrom = true;
return false; return false;
} }
macroblock_8& mb8 = decoder.mb8; macroblock_8& mb8 = decoder.mb8;
@ -1123,6 +1124,8 @@ __ri static bool mpeg2sliceIDEC()
if (ready_to_decode == true) if (ready_to_decode == true)
{ {
ready_to_decode = false; ready_to_decode = false;
IPUCoreStatus.WaitingOnIPUFrom = false;
IPUCoreStatus.WaitingOnIPUTo = false;
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now. CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
ipu_cmd.pos[1] = 2; ipu_cmd.pos[1] = 2;
return false; return false;
@ -1134,6 +1137,7 @@ __ri static bool mpeg2sliceIDEC()
if (decoder.ipu0_data != 0) if (decoder.ipu0_data != 0)
{ {
// IPU FIFO filled up -- Will have to finish transferring later. // IPU FIFO filled up -- Will have to finish transferring later.
IPUCoreStatus.WaitingOnIPUFrom = true;
ipu_cmd.pos[1] = 2; ipu_cmd.pos[1] = 2;
return false; return false;
} }
@ -1141,6 +1145,7 @@ __ri static bool mpeg2sliceIDEC()
mbaCount = 0; mbaCount = 0;
if (read) if (read)
{ {
IPUCoreStatus.WaitingOnIPUFrom = true;
ipu_cmd.pos[1] = 3; ipu_cmd.pos[1] = 3;
return false; return false;
} }
@ -1308,6 +1313,7 @@ __fi static bool mpeg2_slice()
// IPU0 isn't ready for data, so let's wait for it to be // IPU0 isn't ready for data, so let's wait for it to be
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[0] <= 3) if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[0] <= 3)
{ {
IPUCoreStatus.WaitingOnIPUFrom = true;
return false; return false;
} }
@ -1514,6 +1520,8 @@ __fi static bool mpeg2_slice()
{ {
ipu_cmd.pos[0] = 3; ipu_cmd.pos[0] = 3;
ready_to_decode = false; ready_to_decode = false;
IPUCoreStatus.WaitingOnIPUFrom = false;
IPUCoreStatus.WaitingOnIPUTo = false;
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now. CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
return false; return false;
} }
@ -1525,6 +1533,7 @@ __fi static bool mpeg2_slice()
if (decoder.ipu0_data != 0) if (decoder.ipu0_data != 0)
{ {
// IPU FIFO filled up -- Will have to finish transferring later. // IPU FIFO filled up -- Will have to finish transferring later.
IPUCoreStatus.WaitingOnIPUFrom = true;
ipu_cmd.pos[0] = 3; ipu_cmd.pos[0] = 3;
return false; return false;
} }
@ -1532,6 +1541,7 @@ __fi static bool mpeg2_slice()
mbaCount = 0; mbaCount = 0;
if (read) if (read)
{ {
IPUCoreStatus.WaitingOnIPUFrom = true;
ipu_cmd.pos[0] = 4; ipu_cmd.pos[0] = 4;
return false; return false;
} }
@ -1801,12 +1811,20 @@ __ri static bool ipuCSC(tIPU_CMD_CSC csc)
if (csc.OFM) if (csc.OFM)
{ {
ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]); ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]);
if (ipu_cmd.pos[1] < 32) return false; if (ipu_cmd.pos[1] < 32)
{
IPUCoreStatus.WaitingOnIPUFrom = true;
return false;
}
} }
else else
{ {
ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb32) + 4 * ipu_cmd.pos[1], 64 - ipu_cmd.pos[1]); ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb32) + 4 * ipu_cmd.pos[1], 64 - ipu_cmd.pos[1]);
if (ipu_cmd.pos[1] < 64) return false; if (ipu_cmd.pos[1] < 64)
{
IPUCoreStatus.WaitingOnIPUFrom = true;
return false;
}
} }
ipu_cmd.pos[0] = 0; ipu_cmd.pos[0] = 0;
@ -1834,12 +1852,20 @@ __ri static bool ipuPACK(tIPU_CMD_CSC csc)
if (csc.OFM) if (csc.OFM)
{ {
ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]); ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & decoder.rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]);
if (ipu_cmd.pos[1] < 32) return false; if (ipu_cmd.pos[1] < 32)
{
IPUCoreStatus.WaitingOnIPUFrom = true;
return false;
}
} }
else else
{ {
ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*)g_ipu_indx4) + 4 * ipu_cmd.pos[1], 8 - ipu_cmd.pos[1]); ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*)g_ipu_indx4) + 4 * ipu_cmd.pos[1], 8 - ipu_cmd.pos[1]);
if (ipu_cmd.pos[1] < 8) return false; if (ipu_cmd.pos[1] < 8)
{
IPUCoreStatus.WaitingOnIPUFrom = true;
return false;
}
} }
ipu_cmd.pos[0] = 0; ipu_cmd.pos[0] = 0;

View File

@ -19,16 +19,12 @@
#include "IPU/IPUdma.h" #include "IPU/IPUdma.h"
#include "IPU/IPU_MultiISA.h" #include "IPU/IPU_MultiISA.h"
IPUStatus IPU1Status; IPUDMAStatus IPU1Status;
bool CommandExecuteQueued;
u32 ProcessedData;
void ipuDmaReset() void ipuDmaReset()
{ {
IPU1Status.InProgress = false; IPU1Status.InProgress = false;
IPU1Status.DMAFinished = true; IPU1Status.DMAFinished = true;
CommandExecuteQueued = false;
ProcessedData = 0;
} }
bool SaveStateBase::ipuDmaFreeze() bool SaveStateBase::ipuDmaFreeze()
@ -37,7 +33,6 @@ bool SaveStateBase::ipuDmaFreeze()
return false; return false;
Freeze(IPU1Status); Freeze(IPU1Status);
Freeze(CommandExecuteQueued);
return IsOkay(); return IsOkay();
} }
@ -83,11 +78,18 @@ void IPU1dma()
return; return;
} }
if (IPU1Status.DataRequested == false) if (IPUCoreStatus.DataRequested == false)
{ {
// IPU isn't expecting any data, so put it in to wait mode. // IPU isn't expecting any data, so put it in to wait mode.
cpuRegs.eCycle[4] = 0x9999; cpuRegs.eCycle[4] = 0x9999;
CPU_SET_DMASTALL(DMAC_TO_IPU, true); CPU_SET_DMASTALL(DMAC_TO_IPU, true);
// Shouldn't Happen.
if (IPUCoreStatus.WaitingOnIPUTo)
{
IPUCoreStatus.WaitingOnIPUTo = false;
CPU_INT(IPU_PROCESS, 4 * BIAS);
}
return; return;
} }
@ -127,26 +129,22 @@ void IPU1dma()
if (IPU1Status.InProgress) if (IPU1Status.InProgress)
totalqwc += IPU1chain(); totalqwc += IPU1chain();
//Do this here to prevent double settings on Chain DMA's // Nothing has been processed except maybe a tag, or the DMA is ending
if((totalqwc == 0 && g_BP.IFC < 8) || (IPU1Status.DMAFinished && !IPU1Status.InProgress)) if(totalqwc == 0 || (IPU1Status.DMAFinished && !IPU1Status.InProgress) || IPUCoreStatus.DataRequested)
{ {
totalqwc = std::max(4, totalqwc) + tagcycles; totalqwc = std::max(4, totalqwc) + tagcycles;
IPU_INT_TO(totalqwc * BIAS); IPU_INT_TO(totalqwc * BIAS);
} }
else else
{ {
IPU1Status.DataRequested = false; cpuRegs.eCycle[4] = 0x9999;
if (!(IPU1Status.DMAFinished && !IPU1Status.InProgress))
{
cpuRegs.eCycle[4] = 0x9999;//IPU_INT_TO(2048);
CPU_SET_DMASTALL(DMAC_TO_IPU, true); CPU_SET_DMASTALL(DMAC_TO_IPU, true);
} }
else
{ if (IPUCoreStatus.WaitingOnIPUTo && g_BP.IFC >= 1)
totalqwc = std::max(4, totalqwc) + tagcycles; {
IPU_INT_TO(totalqwc * BIAS); IPUCoreStatus.WaitingOnIPUTo = false;
} CPU_INT(IPU_PROCESS, totalqwc * BIAS);
} }
IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr); IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr);
@ -156,8 +154,12 @@ void IPU0dma()
{ {
if(!ipuRegs.ctrl.OFC) if(!ipuRegs.ctrl.OFC)
{ {
if(!CommandExecuteQueued) // This shouldn't happen.
if (IPUCoreStatus.WaitingOnIPUFrom)
{
IPUCoreStatus.WaitingOnIPUFrom = false;
IPUProcessInterrupt(); IPUProcessInterrupt();
}
CPU_SET_DMASTALL(DMAC_FROM_IPU, true); CPU_SET_DMASTALL(DMAC_FROM_IPU, true);
return; return;
} }
@ -168,6 +170,12 @@ void IPU0dma()
if ((!(ipu0ch.chcr.STR) || (cpuRegs.interrupt & (1 << DMAC_FROM_IPU))) || (ipu0ch.qwc == 0)) if ((!(ipu0ch.chcr.STR) || (cpuRegs.interrupt & (1 << DMAC_FROM_IPU))) || (ipu0ch.qwc == 0))
{ {
DevCon.Warning("How??"); DevCon.Warning("How??");
// This shouldn't happen.
if (IPUCoreStatus.WaitingOnIPUFrom)
{
IPUCoreStatus.WaitingOnIPUFrom = false;
CPU_INT(IPU_PROCESS, ipuRegs.ctrl.OFC * BIAS);
}
return; return;
} }
@ -195,11 +203,12 @@ void IPU0dma()
if (!ipu0ch.qwc) if (!ipu0ch.qwc)
IPU_INT_FROM(readsize * BIAS); IPU_INT_FROM(readsize * BIAS);
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued) CPU_SET_DMASTALL(DMAC_FROM_IPU, true);
if (ipuRegs.ctrl.BUSY && IPUCoreStatus.WaitingOnIPUFrom)
{ {
CommandExecuteQueued = false; IPUCoreStatus.WaitingOnIPUFrom = false;
CPU_SET_DMASTALL(DMAC_FROM_IPU, true); CPU_INT(IPU_PROCESS, readsize * BIAS);
IPUProcessInterrupt();
} }
} }
@ -259,29 +268,19 @@ __fi void dmaIPU1() // toIPU
IPU1Status.DMAFinished = false; IPU1Status.DMAFinished = false;
} }
} }
if(IPU1Status.DataRequested)
IPU1dma();
else
cpuRegs.eCycle[4] = 0x9999;
} }
else // Normal Mode else // Normal Mode
{ {
IPU_LOG("Setting up IPU1 Normal mode"); IPU_LOG("Setting up IPU1 Normal mode");
IPU1Status.InProgress = true; IPU1Status.InProgress = true;
IPU1Status.DMAFinished = true; IPU1Status.DMAFinished = true;
if (IPU1Status.DataRequested)
IPU1dma();
else
cpuRegs.eCycle[4] = 0x9999;
} }
IPU1dma();
} }
void ipuCMDProcess() void ipuCMDProcess()
{ {
CommandExecuteQueued = false;
ProcessedData = 0;
IPUProcessInterrupt(); IPUProcessInterrupt();
} }

View File

@ -17,10 +17,15 @@
#include "IPU.h" #include "IPU.h"
struct IPUStatus { struct IPUDMAStatus {
bool InProgress; bool InProgress;
bool DMAFinished; bool DMAFinished;
};
struct IPUStatus {
bool DataRequested; bool DataRequested;
bool WaitingOnIPUFrom;
bool WaitingOnIPUTo;
}; };
extern void ipuCMDProcess(); extern void ipuCMDProcess();
@ -33,4 +38,5 @@ extern void IPU0dma();
extern void IPU1dma(); extern void IPU1dma();
extern void ipuDmaReset(); extern void ipuDmaReset();
extern IPUStatus IPU1Status; extern IPUDMAStatus IPU1Status;
extern IPUStatus IPUCoreStatus;

View File

@ -37,7 +37,7 @@ enum class FreezeAction
// [SAVEVERSION+] // [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated. // This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A40 << 16) | 0x0000; static const u32 g_SaveVersion = (0x9A41 << 16) | 0x0000;
// the freezing data between submodules and core // the freezing data between submodules and core