test(vifcmd12): initial upload

This commit is contained in:
Ty Lamontagne 2022-05-29 01:15:27 -04:00
parent e146193258
commit d8a7a89aec
No known key found for this signature in database
GPG Key ID: E643F9981E983088
5 changed files with 206 additions and 0 deletions

29
vifcmd12/Makefile Normal file
View File

@ -0,0 +1,29 @@
EE_OBJS = vifcmd12.o micro/loop.o
EE_BIN = vifcmd12.elf
EE_LIBS = -lkernel
EE_DVP = dvp-as
EE_VCL = vcl
all: $(EE_BIN)
%.o: %.vsm
$(EE_DVP) $< -o $@
clean:
rm -f $(EE_BIN) $(EE_OBJS)
run: $(EE_BIN)
ps2client execee host:$(EE_BIN)
wsl: $(EE_BIN)
$(PCSX2) --elf="$(shell wslpath -w $(shell pwd))/$(EE_BIN)"
emu: $(EE_BIN)
$(PCSX2) --elf="$(shell pwd)/$(EE_BIN)"
reset:
ps2client reset
ps2client netdump
include $(PS2SDK)/samples/Makefile.pref
include $(PS2SDK)/samples/Makefile.eeglobal

47
vifcmd12/README.md Normal file
View File

@ -0,0 +1,47 @@
# VIFCMD 12
## Description
Checks the behaviour of an undefined VIF command (0x12)
This command is intriguing because it's CMD byte happens to be sandwiched between defined VIF FLUSH[X] CMD bytes
FLUSHE -> 0x10
FLUSH -> 0x11
??? -> 0x12
FLUSHA -> 0x13
The procedure for checking this behaviour is to upload a micro program to VU1 that loops indefinitely. Then see if the VIF will wait for the micro programs end.
## PCSX2 Behaviour
When coming across this command, PCSX2 will set `ER1` in `VIFSTAT` and emit the following message:
`Vif1: Unknown VifCmd! [12]`
This would happen to be the correct behaviour.
## Findings
When the VIF executes an instruction with CMD 0x12, it will _not_ wait for an E bit (end of micro program).
`ER1` in `VIFSTAT` will be set.
## Related PR(s) or Issue(s)
https://github.com/PCSX2/pcsx2/issues/4179
## Expected Results
(VIF1STAT ER1 = 2000 -> ER1 bit is set)
```
VIF1STAT ER1 = 0
VIF1 finished, VIF1STAT ER1 = 2000
```
In the case that you replace the 0x12 CMD with a proper FLUSH command, the result would be expected to be:
```
VIF1STAT ER1 = 0
waited 10 mil times (VIF1STAT ER1 = 0)
waited 20 mil times (VIF1STAT ER1 = 0)
waited 30 mil times (VIF1STAT ER1 = 0)
waited 40 mil times (VIF1STAT ER1 = 0)
...
```
In this case, we are waiting on the VIF to finish waiting on the micro program, which will complete during your next power outage. :^)

24
vifcmd12/micro/loop.vsm Normal file
View File

@ -0,0 +1,24 @@
# Loop the VU forever
.vu
.align 4
.global loop_start
.global loop_end
loop_start:
# Branching to a global seems to cause relocation issues
inner_loop:
# Set VI01 to 0
NOP IADDIU VI01, VI00, 0
NOP NOP
# Branch to inner_loop if VI01 is 0
NOP IBEQ VI01, VI00, inner_loop
NOP NOP
# This will never be hit
NOP B inner_loop
NOP NOP
NOP[E] NOP
NOP NOP
loop_end:

106
vifcmd12/vifcmd12.c Normal file
View File

@ -0,0 +1,106 @@
#include <stdio.h>
#include <kernel.h>
// VIF commands
#define VIFNOP 0x00000000
#define VIFMPG(size, pos) ((0x4A000000) | (size << 16) | pos)
#define VIFFLUSHE 0x10000000
#define VIFFLUSH 0x11000000
#define VIFFLUSH12 0x12000000
#define VIFFLUSHA 0x13000000
#define VIFMSCAL(execaddr) (0x14000000 | execaddr)
// VIF1 DMA registers
#define VIF1CHCR (*(volatile u32 *)0x10009000)
#define VIF1MADR (*(volatile u32 *)0x10009010)
#define VIF1QWC (*(volatile u32 *)0x10009020)
#define VIF1STAT (*(volatile u32 *)0x10003c00)
// The micro program gets linked into the .vudata section
extern u64 loop_start __attribute__((section(".vudata")));
extern u64 loop_end __attribute__((section(".vudata")));
// Uploads a micro program to the VU1
// offset holds where the micro program is stored in the VU1 code memory
// start and end point to the start and end of the micro program
// flushwith is the last instruction of the _vifcode_
// Usually you would use VIFFLUSH(10h) or VIFFLUSHE(11h) or VIFFLUSHA(13h)
// For this experiement we use (12h)
void uploadMicroProgram(const u32 offset, const u64 *start, const u64 *end, u32 flushwith)
{
u32 vp[0xFFF] __attribute__((aligned(128)));
u32 vpi = 0;
u32 progInstructions = (end - start);
u32 mpgBlocks = 0;
vp[vpi++] = VIFFLUSHE;
for (u32 instruction = 0; instruction < progInstructions; instruction++)
{
if (!(instruction % 256))
{
u32 size = progInstructions - (mpgBlocks * 256); // Get the remaining amount of instructions to send
if (size >= 256) // If the remaining instructions to be sent to VIF is >= 256
size = 0; // Then send the maximum amount in this mpg (0, but is interpreted as 256)
// If we are in our first mpg block then set the offset to be zero
vp[vpi++] = VIFMPG((size + offset), (mpgBlocks == 0 ? 0 : (mpgBlocks * 256)));
mpgBlocks++;
}
vp[vpi++] = start[instruction];
vp[vpi++] = start[instruction] >> 32;
}
vp[vpi++] = VIFMSCAL(0); // Set the microprogram to be executed
vp[vpi++] = flushwith;
while (vpi % 4) // Align the packet
vp[vpi++] = VIFNOP;
VIF1MADR = (u32)&vp[0];
VIF1QWC = vpi / 4;
FlushCache(0);
VIF1CHCR = 0x101;
FlushCache(0);
while (VIF1CHCR == 0x100)
{
}
return;
}
int main()
{
printf("VIF1STAT ER1 = %x\n", VIF1STAT & 0x2000);
// Upload a micro program to the VU1
// Replace VIFFLUSH12 with VIFFLUSHE to wait for an E bit
uploadMicroProgram(0, &loop_start, &loop_end, VIFFLUSH12);
u32 cnt = 0;
u32 cnt2 = 0;
// Loop if the VIF is:
// Not idle (bit 0)
// E bit waiting (bit 1)
// Waiting for a GIF transfer end (bit 2)
while ((VIF1STAT & 0x7))
{
if (cnt == 10000000)
{
cnt2++;
printf("waited %d mil times (VIF1STAT ER1 = %x)\n", cnt2 * 10, VIF1STAT & 8192);
cnt = 0;
}
cnt++;
}
printf("VIF1 finished, VIF1STAT ER1 = %x\n", VIF1STAT & 0x2000);
SleepThread();
}

BIN
vifcmd12/vifcmd12.elf Normal file

Binary file not shown.