DMA: Refactoring, support split block transfers

This commit is contained in:
Connor McLaughlin 2019-10-13 14:16:49 +10:00
parent 2d9d999713
commit 88ec178380
7 changed files with 169 additions and 106 deletions

View File

@ -2,6 +2,7 @@
#include "YBaseLib/Log.h" #include "YBaseLib/Log.h"
#include "common/cd_image.h" #include "common/cd_image.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "dma.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "system.h" #include "system.h"
Log_SetChannel(CDROM); Log_SetChannel(CDROM);
@ -394,6 +395,8 @@ void CDROM::UpdateStatusRegister()
m_status.RSLRRDY = !m_response_fifo.IsEmpty(); m_status.RSLRRDY = !m_response_fifo.IsEmpty();
m_status.DRQSTS = !m_data_fifo.IsEmpty(); m_status.DRQSTS = !m_data_fifo.IsEmpty();
m_status.BUSYSTS = m_command_state == CommandState::WaitForExecute; m_status.BUSYSTS = m_command_state == CommandState::WaitForExecute;
m_dma->SetRequest(DMA::Channel::CDROM, m_status.DRQSTS);
} }
u32 CDROM::GetAckDelayForCommand() const u32 CDROM::GetAckDelayForCommand() const

View File

@ -30,7 +30,7 @@ bool DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_co
void DMA::Reset() void DMA::Reset()
{ {
m_transfer_ticks = 0; m_transfer_ticks = 0;
m_transfer_pending = false; m_transfer_in_progress = false;
m_state = {}; m_state = {};
m_DPCR.bits = 0x07654321; m_DPCR.bits = 0x07654321;
m_DICR.bits = 0; m_DICR.bits = 0;
@ -99,6 +99,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
{ {
Log_TracePrintf("DMA channel %u block control <- 0x%08X", channel_index, value); Log_TracePrintf("DMA channel %u block control <- 0x%08X", channel_index, value);
state.block_control.bits = value; state.block_control.bits = value;
Transfer();
return; return;
} }
@ -107,9 +108,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) | state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
(value & ChannelState::ChannelControl::WRITE_MASK); (value & ChannelState::ChannelControl::WRITE_MASK);
Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits); Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
if (CanRunChannel(static_cast<Channel>(channel_index))) Transfer();
UpdateTransferPending();
return; return;
} }
@ -125,7 +124,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
{ {
Log_TracePrintf("DPCR <- 0x%08X", value); Log_TracePrintf("DPCR <- 0x%08X", value);
m_DPCR.bits = value; m_DPCR.bits = value;
UpdateTransferPending(); Transfer();
return; return;
} }
@ -153,65 +152,70 @@ void DMA::SetRequest(Channel channel, bool request)
return; return;
cs.request = request; cs.request = request;
UpdateTransferPending(); if (request)
Transfer();
} }
void DMA::Execute(TickCount ticks) bool DMA::CanTransferChannel(Channel channel) const
{
if (!m_transfer_pending)
return;
m_transfer_ticks -= ticks;
if (m_transfer_ticks <= 0)
{
m_transfer_pending = false;
for (u32 i = 0; i < NUM_CHANNELS; i++)
{
const Channel channel = static_cast<Channel>(i);
if (CanRunChannel(channel))
{
RunDMA(channel);
m_transfer_pending |= CanRunChannel(channel);
}
}
if (m_transfer_pending)
{
m_transfer_ticks += TRANSFER_TICKS;
m_system->SetDowncount(m_transfer_ticks);
}
}
else
{
m_system->SetDowncount(m_transfer_ticks);
}
}
bool DMA::CanRunChannel(Channel channel) const
{ {
if (!m_DPCR.GetMasterEnable(channel)) if (!m_DPCR.GetMasterEnable(channel))
return false; return false;
const ChannelState& cs = m_state[static_cast<u32>(channel)]; const ChannelState& cs = m_state[static_cast<u32>(channel)];
if (cs.channel_control.start_trigger) if (!cs.channel_control.enable_busy)
return true; return false;
return (cs.channel_control.enable_busy && cs.request); if (!cs.request && channel != Channel::OTC)
return false;
if (cs.channel_control.sync_mode == SyncMode::Manual && !cs.channel_control.start_trigger)
return false;
return true;
} }
bool DMA::CanRunAnyChannels() const bool DMA::CanRunAnyChannels() const
{ {
for (u32 i = 0; i < NUM_CHANNELS; i++) for (u32 i = 0; i < NUM_CHANNELS; i++)
{ {
if (CanRunChannel(static_cast<Channel>(i))) if (CanTransferChannel(static_cast<Channel>(i)))
return true; return true;
} }
return false; return false;
} }
void DMA::RunDMA(Channel channel) void DMA::Transfer()
{
if (m_transfer_in_progress)
return;
// prevent recursive calls
m_transfer_in_progress = true;
// keep going until all transfers are done. one channel can start others (e.g. MDEC)
for (;;)
{
bool any_channels_active = false;
for (u32 i = 0; i < NUM_CHANNELS; i++)
{
const Channel channel = static_cast<Channel>(i);
if (CanTransferChannel(channel))
{
TransferChannel(channel);
any_channels_active = true;
}
}
if (!any_channels_active)
break;
}
m_transfer_in_progress = false;
}
void DMA::TransferChannel(Channel channel)
{ {
ChannelState& cs = m_state[static_cast<u32>(channel)]; ChannelState& cs = m_state[static_cast<u32>(channel)];
const bool copy_to_device = cs.channel_control.copy_to_device; const bool copy_to_device = cs.channel_control.copy_to_device;
@ -305,37 +309,56 @@ void DMA::RunDMA(Channel channel)
case SyncMode::Request: case SyncMode::Request:
{ {
const u32 block_size = cs.block_control.request.GetBlockSize(); Log_DebugPrintf("DMA%u: Copying %u blocks of size %u %s 0x%08X", static_cast<u32>(channel),
const u32 block_count = cs.block_control.request.GetBlockCount(); cs.block_control.request.GetBlockCount(), cs.block_control.request.GetBlockSize(),
Log_DebugPrintf("DMA%u: Copying %u blocks of size %u %s 0x%08X", static_cast<u32>(channel), block_count, copy_to_device ? "from" : "to", current_address);
block_size, copy_to_device ? "from" : "to", current_address);
u32 blocks_remaining = cs.block_control.request.block_count;
if (copy_to_device) if (copy_to_device)
{ {
u32 words_remaining = block_size * block_count;
do do
{ {
words_remaining--; blocks_remaining--;
u32 value = 0; u32 words_remaining = cs.block_control.request.block_size;
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(current_address, value); do
DMAWrite(channel, value, current_address, words_remaining); {
words_remaining--;
current_address = (current_address + increment) & ADDRESS_MASK; u32 value = 0;
} while (words_remaining > 0); m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(current_address, value);
DMAWrite(channel, value, current_address, words_remaining);
current_address = (current_address + increment) & ADDRESS_MASK;
} while (words_remaining > 0);
} while (cs.request && blocks_remaining > 0);
} }
else else
{ {
u32 words_remaining = block_size * block_count;
do do
{ {
words_remaining--; blocks_remaining--;
u32 value = DMARead(channel, current_address, words_remaining); u32 words_remaining = cs.block_control.request.block_size;
m_bus->DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(current_address, value); do
{
words_remaining--;
current_address = (current_address + increment) & ADDRESS_MASK; u32 value = DMARead(channel, current_address, words_remaining);
} while (words_remaining > 0); m_bus->DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(current_address, value);
current_address = (current_address + increment) & ADDRESS_MASK;
} while (words_remaining > 0);
} while (cs.request && blocks_remaining > 0);
} }
cs.base_address = current_address;
cs.block_control.request.block_count = blocks_remaining;
// finish transfer later if the request was cleared
if (blocks_remaining > 0)
return;
} }
break; break;
@ -412,22 +435,3 @@ void DMA::DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address
break; break;
} }
} }
void DMA::UpdateTransferPending()
{
if (CanRunAnyChannels())
{
if (m_transfer_pending)
return;
m_system->Synchronize();
m_transfer_pending = true;
m_transfer_ticks = TRANSFER_TICKS;
m_system->SetDowncount(m_transfer_ticks);
}
else
{
m_transfer_pending = false;
m_transfer_ticks = 0;
}
}

View File

@ -35,8 +35,8 @@ public:
DMA(); DMA();
~DMA(); ~DMA();
bool Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, bool Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, SPU* spu,
SPU* spu, MDEC* mdec); MDEC* mdec);
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -45,8 +45,6 @@ public:
void SetRequest(Channel channel, bool request); void SetRequest(Channel channel, bool request);
void Execute(TickCount ticks);
private: private:
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF); static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF);
static constexpr u32 TRANSFER_TICKS = 10; static constexpr u32 TRANSFER_TICKS = 10;
@ -60,10 +58,11 @@ private:
}; };
// is everything enabled for a channel to operate? // is everything enabled for a channel to operate?
bool CanRunChannel(Channel channel) const; bool CanTransferChannel(Channel channel) const;
bool CanRunAnyChannels() const; bool CanRunAnyChannels() const;
void RunDMA(Channel channel); void Transfer();
void TransferChannel(Channel channel);
// from device -> memory // from device -> memory
u32 DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words); u32 DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words);
@ -71,8 +70,6 @@ private:
// from memory -> device // from memory -> device
void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words); void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words);
void UpdateTransferPending();
System* m_system = nullptr; System* m_system = nullptr;
Bus* m_bus = nullptr; Bus* m_bus = nullptr;
InterruptController* m_interrupt_controller = nullptr; InterruptController* m_interrupt_controller = nullptr;
@ -82,7 +79,7 @@ private:
MDEC* m_mdec = nullptr; MDEC* m_mdec = nullptr;
TickCount m_transfer_ticks = 0; TickCount m_transfer_ticks = 0;
bool m_transfer_pending = false; bool m_transfer_in_progress = false;
struct ChannelState struct ChannelState
{ {

View File

@ -48,7 +48,6 @@ u32 MDEC::ReadRegister(u32 offset)
switch (offset) switch (offset)
{ {
case 0: case 0:
UpdateStatusRegister();
return ReadDataRegister(); return ReadDataRegister();
case 4: case 4:
@ -85,6 +84,7 @@ void MDEC::WriteRegister(u32 offset, u32 value)
m_enable_dma_in = cr.enable_dma_in; m_enable_dma_in = cr.enable_dma_in;
m_enable_dma_out = cr.enable_dma_out; m_enable_dma_out = cr.enable_dma_out;
UpdateStatusRegister();
UpdateDMARequest(); UpdateDMARequest();
return; return;
} }
@ -114,6 +114,7 @@ void MDEC::SoftReset()
m_enable_dma_out = false; m_enable_dma_out = false;
m_data_in_fifo.Clear(); m_data_in_fifo.Clear();
m_data_out_fifo.Clear(); m_data_out_fifo.Clear();
UpdateStatusRegister();
UpdateDMARequest(); UpdateDMARequest();
} }
@ -122,7 +123,7 @@ void MDEC::UpdateStatusRegister()
m_status.data_out_fifo_empty = m_data_out_fifo.IsEmpty(); m_status.data_out_fifo_empty = m_data_out_fifo.IsEmpty();
m_status.data_in_fifo_full = m_data_in_fifo.IsFull(); m_status.data_in_fifo_full = m_data_in_fifo.IsFull();
m_status.command_busy = m_command != Command::None; m_status.command_busy = false;
m_status.parameter_words_remaining = Truncate16(m_remaining_words - 1); m_status.parameter_words_remaining = Truncate16(m_remaining_words - 1);
m_status.current_block = (m_current_block + 4) % NUM_BLOCKS; m_status.current_block = (m_current_block + 4) % NUM_BLOCKS;
} }
@ -130,7 +131,7 @@ void MDEC::UpdateStatusRegister()
void MDEC::UpdateDMARequest() void MDEC::UpdateDMARequest()
{ {
// we always want data in if it's enabled // we always want data in if it's enabled
const bool data_in_request = m_enable_dma_in && !m_data_in_fifo.IsFull(); const bool data_in_request = m_enable_dma_in && m_data_in_fifo.GetSpace() >= (32 * 2) && !m_data_out_fifo.IsFull();
m_status.data_in_request = data_in_request; m_status.data_in_request = data_in_request;
m_dma->SetRequest(DMA::Channel::MDECin, data_in_request); m_dma->SetRequest(DMA::Channel::MDECin, data_in_request);
@ -144,13 +145,22 @@ u32 MDEC::ReadDataRegister()
{ {
if (m_data_out_fifo.IsEmpty()) if (m_data_out_fifo.IsEmpty())
{ {
Log_WarningPrintf("MDEC data out FIFO empty on read"); Execute();
return UINT32_C(0xFFFFFFFF);
if (m_data_out_fifo.IsEmpty())
{
Log_WarningPrintf("MDEC data out FIFO empty on read");
return UINT32_C(0xFFFFFFFF);
}
} }
const u32 value = m_data_out_fifo.Pop(); const u32 value = m_data_out_fifo.Pop();
UpdateStatusRegister(); if (m_data_out_fifo.IsEmpty())
UpdateDMARequest(); {
UpdateStatusRegister();
UpdateDMARequest();
}
return value; return value;
} }
@ -197,29 +207,53 @@ void MDEC::WriteCommandRegister(u32 value)
m_data_in_fifo.Push(Truncate16(value)); m_data_in_fifo.Push(Truncate16(value));
m_data_in_fifo.Push(Truncate16(value >> 16)); m_data_in_fifo.Push(Truncate16(value >> 16));
m_remaining_words--; m_remaining_words--;
UpdateDMARequest();
} }
Execute();
}
void MDEC::Execute()
{
switch (m_command) switch (m_command)
{ {
case Command::DecodeMacroblock: case Command::DecodeMacroblock:
{ {
if (!HandleDecodeMacroblockCommand()) if (!HandleDecodeMacroblockCommand())
{
UpdateStatusRegister();
UpdateDMARequest();
return; return;
}
} }
break; break;
case Command::SetIqTab: case Command::SetIqTab:
{ {
if (!HandleSetQuantTableCommand()) if (!HandleSetQuantTableCommand())
{
UpdateStatusRegister();
UpdateDMARequest();
return; return;
}
} }
break; break;
case Command::SetScale: case Command::SetScale:
{ {
if (!HandleSetScaleCommand()) if (!HandleSetScaleCommand())
{
UpdateStatusRegister();
UpdateDMARequest();
return; return;
}
}
break;
default:
{
UpdateStatusRegister();
UpdateDMARequest();
return;
} }
break; break;
} }
@ -229,6 +263,7 @@ void MDEC::WriteCommandRegister(u32 value)
m_current_block = 0; m_current_block = 0;
m_current_coefficient = 64; m_current_coefficient = 64;
m_current_q_scale = 0; m_current_q_scale = 0;
UpdateStatusRegister();
UpdateDMARequest(); UpdateDMARequest();
} }
@ -242,7 +277,7 @@ bool MDEC::HandleDecodeMacroblockCommand()
break; break;
} }
return m_remaining_words == 0; return m_data_in_fifo.IsEmpty() && m_remaining_words == 0;
} }
else else
{ {
@ -252,12 +287,24 @@ bool MDEC::HandleDecodeMacroblockCommand()
break; break;
} }
return m_remaining_words == 0; return m_data_in_fifo.IsEmpty() && m_remaining_words == 0;
} }
} }
bool MDEC::DecodeMonoMacroblock() bool MDEC::DecodeMonoMacroblock()
{ {
// sufficient space in output?
if (m_status.data_output_depth == DataOutputDepth_4Bit)
{
if (m_data_out_fifo.GetSpace() < (64 / 8))
return false;
}
else
{
if (m_data_out_fifo.GetSpace() < (64 / 4))
return false;
}
if (!rl_decode_block(m_blocks[0].data(), m_iq_y.data())) if (!rl_decode_block(m_blocks[0].data(), m_iq_y.data()))
return false; return false;
@ -310,7 +357,17 @@ bool MDEC::DecodeMonoMacroblock()
bool MDEC::DecodeColoredMacroblock() bool MDEC::DecodeColoredMacroblock()
{ {
std::array<u32, 256> out_rgb; // sufficient space in output?
if (m_status.data_output_depth == DataOutputDepth_24Bit)
{
if (m_data_out_fifo.GetSpace() < (256 - (256 / 4)))
return false;
}
else
{
if (m_data_out_fifo.GetSpace() < (256 / 2))
return false;
}
for (; m_current_block < NUM_BLOCKS; m_current_block++) for (; m_current_block < NUM_BLOCKS; m_current_block++)
{ {
@ -322,7 +379,9 @@ bool MDEC::DecodeColoredMacroblock()
// done decoding // done decoding
m_current_block = 0; m_current_block = 0;
Log_DebugPrintf("Decoded colored macroblock");
std::array<u32, 256> out_rgb;
yuv_to_rgb(0, 0, m_blocks[0], m_blocks[1], m_blocks[2], out_rgb); yuv_to_rgb(0, 0, m_blocks[0], m_blocks[1], m_blocks[2], out_rgb);
yuv_to_rgb(8, 0, m_blocks[0], m_blocks[1], m_blocks[3], out_rgb); yuv_to_rgb(8, 0, m_blocks[0], m_blocks[1], m_blocks[3], out_rgb);
yuv_to_rgb(0, 8, m_blocks[0], m_blocks[1], m_blocks[4], out_rgb); yuv_to_rgb(0, 8, m_blocks[0], m_blocks[1], m_blocks[4], out_rgb);
@ -612,8 +671,8 @@ void MDEC::DrawDebugWindow()
if (ImGui::CollapsingHeader("Status", ImGuiTreeNodeFlags_DefaultOpen)) if (ImGui::CollapsingHeader("Status", ImGuiTreeNodeFlags_DefaultOpen))
{ {
ImGui::Text("Data-Out FIFO Empty: %s", m_status.data_out_fifo_empty ? "Yes" : "No"); ImGui::Text("Data-Out FIFO Empty: %s", m_status.data_out_fifo_empty ? "Yes" : "No");
ImGui::Text("Data-In FIFO Empty: %s", m_status.data_in_fifo_full ? "Yes" : "No"); ImGui::Text("Data-In FIFO Full: %s", m_status.data_in_fifo_full ? "Yes" : "No");
ImGui::Text("Command Busy FIFO Empty: %s", m_status.command_busy ? "Yes" : "No"); ImGui::Text("Command Busy: %s", m_status.command_busy ? "Yes" : "No");
ImGui::Text("Data-In Request: %s", m_status.data_in_request ? "Yes" : "No"); ImGui::Text("Data-In Request: %s", m_status.data_in_request ? "Yes" : "No");
ImGui::Text("Output Depth: %s", output_depths[static_cast<u8>(m_status.data_output_depth.GetValue())]); ImGui::Text("Output Depth: %s", output_depths[static_cast<u8>(m_status.data_output_depth.GetValue())]);
ImGui::Text("Output Signed: %s", m_status.data_output_signed ? "Yes" : "No"); ImGui::Text("Output Signed: %s", m_status.data_output_signed ? "Yes" : "No");

View File

@ -91,6 +91,7 @@ private:
u32 ReadDataRegister(); u32 ReadDataRegister();
void WriteCommandRegister(u32 value); void WriteCommandRegister(u32 value);
void Execute();
bool HandleDecodeMacroblockCommand(); bool HandleDecodeMacroblockCommand();
bool HandleSetQuantTableCommand(); bool HandleSetQuantTableCommand();
@ -114,8 +115,8 @@ private:
bool m_enable_dma_out = false; bool m_enable_dma_out = false;
// Even though the DMA is in words, we access the FIFO as halfwords. // Even though the DMA is in words, we access the FIFO as halfwords.
InlineFIFOQueue<u16, DATA_IN_FIFO_SIZE> m_data_in_fifo; InlineFIFOQueue<u16, DATA_IN_FIFO_SIZE / sizeof(u16)> m_data_in_fifo;
InlineFIFOQueue<u32, DATA_OUT_FIFO_SIZE> m_data_out_fifo; InlineFIFOQueue<u32, DATA_OUT_FIFO_SIZE / sizeof(u32)> m_data_out_fifo;
Command m_command = Command::None; Command m_command = Command::None;
u32 m_remaining_words = 0; u32 m_remaining_words = 0;

View File

@ -288,7 +288,6 @@ void System::Synchronize()
m_timers->Execute(pending_ticks); m_timers->Execute(pending_ticks);
m_cdrom->Execute(pending_ticks); m_cdrom->Execute(pending_ticks);
m_pad->Execute(pending_ticks); m_pad->Execute(pending_ticks);
m_dma->Execute(pending_ticks);
m_spu->Execute(pending_ticks); m_spu->Execute(pending_ticks);
} }

View File

@ -96,7 +96,7 @@ int main(int argc, char* argv[])
#else #else
g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
// g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG);
g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController InterruptController", LOGLEVEL_DEBUG); g_pLog->SetConsoleOutputParams(true, "Pad DigitalController InterruptController", LOGLEVEL_DEBUG);
// g_pLog->SetFilterLevel(LOGLEVEL_TRACE); // g_pLog->SetFilterLevel(LOGLEVEL_TRACE);
g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_DEBUG);
// g_pLog->SetFilterLevel(LOGLEVEL_DEV); // g_pLog->SetFilterLevel(LOGLEVEL_DEV);