mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
runtime: restore DMA2 word-cost timing
This commit is contained in:
committed by
Alexandros Mandravillis
parent
99f775c839
commit
bc88c06c5f
@@ -2,13 +2,12 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* PSX DMA2 linked-list timing used by the clean-room state machine. The header
|
||||
* read costs eight guest clocks. A non-empty node then has five setup clocks,
|
||||
* followed by one clock per payload word. Keeping these as separate events is
|
||||
* important: the guest CPU can change a packet after it starts DMA and before
|
||||
* DMA reaches that packet. */
|
||||
#define DMA_GPU_LL_HEADER_CYCLES 8u
|
||||
#define DMA_GPU_LL_SETUP_CYCLES 5u
|
||||
/* Charge one guest clock for each 32-bit word read: one header per node and
|
||||
* one clock per payload word. There is no separate setup charge. Header and
|
||||
* payload reads remain separate events so guest writes can affect an active
|
||||
* transfer before DMA reaches the changed word. */
|
||||
#define DMA_GPU_LL_HEADER_CYCLES 1u
|
||||
#define DMA_GPU_LL_SETUP_CYCLES 0u
|
||||
|
||||
static uint32_t resolve_address(const DMAGPULinkedListOps *ops, void *opaque,
|
||||
uint32_t address) {
|
||||
@@ -130,6 +129,9 @@ void dma_gpu_ll_advance(DMAGPULinkedList *state, uint32_t cycles,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state->active || cycles == 0) return;
|
||||
if (!state->active) return;
|
||||
/* Consume a zero-cost phase transition in this service call. A later
|
||||
* read still waits for its own non-zero event. */
|
||||
if (cycles == 0 && state->cycles_remaining != 0) return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +72,7 @@ int main(void) {
|
||||
emitted_count = completed = hit_limit = 0u;
|
||||
|
||||
dma_gpu_ll_start(&state, 0x40u, 8u);
|
||||
dma_gpu_ll_advance(&state, 8u, &ops, NULL);
|
||||
CHECK(emitted_count == 0u);
|
||||
dma_gpu_ll_advance(&state, 5u, &ops, NULL);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(emitted_count == 0u);
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 1u);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
@@ -96,38 +94,58 @@ int main(void) {
|
||||
ram[0x24 / 4] = 0x22222222u;
|
||||
|
||||
dma_gpu_ll_start(&state, 0x00u, 8u);
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 8u);
|
||||
dma_gpu_ll_advance(&state, 7u, &ops, NULL);
|
||||
/* Each header and payload word costs one clock. Setup costs nothing. */
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 1u);
|
||||
CHECK(emitted_count == 0u);
|
||||
|
||||
/* Header becomes visible at cycle 8; payload waits for setup. */
|
||||
/* Setup occurs with the header service. The payload keeps its own event. */
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(emitted_count == 0u);
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 5u);
|
||||
dma_gpu_ll_advance(&state, 5u, &ops, NULL);
|
||||
CHECK(emitted_count == 0u);
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 1u);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(emitted_count == 1u && emitted[0] == 0x11111111u);
|
||||
|
||||
/* This is the regression: the CPU changes the later packet after DMA has
|
||||
* started. DMA must read the new value when it reaches that packet. */
|
||||
/* This is what the sliced walker exists for, and the cost change must not
|
||||
* weaken it: the CPU changes a later packet after DMA has started, and DMA
|
||||
* must read the NEW value when it reaches that packet. */
|
||||
ram[0x24 / 4] = 0xA5A5A5A5u;
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 8u);
|
||||
dma_gpu_ll_advance(&state, 8u, &ops, NULL);
|
||||
CHECK(emitted_count == 1u);
|
||||
dma_gpu_ll_advance(&state, 5u, &ops, NULL);
|
||||
CHECK(dma_gpu_ll_cycles_to_event(&state) == 1u);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(emitted_count == 1u);
|
||||
CHECK(completed == 0u);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(emitted_count == 2u && emitted[1] == 0xA5A5A5A5u);
|
||||
CHECK(completed == 1u && hit_limit == 0u && !state.active);
|
||||
|
||||
/* An empty ordering table costs one header clock per node. */
|
||||
{
|
||||
const uint32_t NODES = 12u;
|
||||
memset(ram, 0, sizeof(ram));
|
||||
for (uint32_t i = 0; i < NODES - 1u; i++)
|
||||
ram[i] = ((i + 1u) * 4u); /* empty node -> next */
|
||||
ram[NODES - 1u] = 0x00FFFFFFu; /* empty terminator */
|
||||
|
||||
completed = hit_limit = emitted_count = 0u;
|
||||
dma_gpu_ll_start(&state, 0x00u, 64u);
|
||||
uint32_t spent = 0u;
|
||||
while (state.active && spent < 1000u) {
|
||||
uint32_t d = dma_gpu_ll_cycles_to_event(&state);
|
||||
dma_gpu_ll_advance(&state, d, &ops, NULL);
|
||||
spent += d;
|
||||
}
|
||||
CHECK(completed == 1u && hit_limit == 0u);
|
||||
CHECK(state.nodes_processed == NODES);
|
||||
CHECK(state.total_words == NODES); /* header words only */
|
||||
CHECK(spent == NODES); /* nodes + 0 payload words */
|
||||
}
|
||||
|
||||
/* A malformed cycle is bounded and reports the safety stop. */
|
||||
memset(ram, 0, sizeof(ram));
|
||||
completed = hit_limit = 0u;
|
||||
ram[0x00 / 4] = 0x00000000u;
|
||||
dma_gpu_ll_start(&state, 0x00u, 1u);
|
||||
dma_gpu_ll_advance(&state, 8u, &ops, NULL);
|
||||
dma_gpu_ll_advance(&state, 8u, &ops, NULL);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
dma_gpu_ll_advance(&state, 1u, &ops, NULL);
|
||||
CHECK(completed == 1u && hit_limit == 1u && !state.active);
|
||||
|
||||
puts("dma_gpu_linked_list_timing_test: PASS");
|
||||
|
||||
Reference in New Issue
Block a user