Files
V2TMUMemTester/main.c
2026-03-07 16:40:29 +01:00

378 lines
10 KiB
C

/* V2MemTest - A CLI Tool to test & fix Voodoo² TMU System
* Copyright (C) 2026 ChaCha
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define _BSD_SOURCE 1
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include "cvg.h"
#include <glide.h>
#include "sst1init.h"
#include "fxpci.h"
#include "V2MemTest.h"
#include "FaultSources.h"
#include "Utils.h"
#include "Draw.h"
#include "Test_Address.h"
#include "Test_Data.h"
#include "Test_Data_Huge.h"
const char* szErrorMsg[] =
{
"No Error", // E_ERROR__NO_ERROR
"Unknown Error", // E_ERROR__UNKNOWN_ERROR
"Unknown argument found in command line", // E_ERROR__UNKNOWN_ARGUMENT
"Wrong argument value", // E_ERROR__BAD_ARGUMENT_VALUE
"Error initializing Voodoo² Board", // E_ERROR__SST1_INIT,
"Error getting Voodoo² Board informations.", // E_ERROR__SST1_GET_INFO,
"Not enough FBI RAM to process TMU tests", // E_ERROR__NOT_ENOUGH_FBI_RAM,
};
const char szTitle[] =
"v2-tmu-memtester-%d.%d.%d - A CLI Tool to test & fix Voodoo² TMU System\n"
"Copyright (C) 2026 ChaCha\n";
const char szLicence[] =
"This program is free software : you can redistribute it and/or modify it under\n"
"the terms of the GNU General Public License as published by the Free Software\n"
"Foundation either version 3 of the License, or (at your option) any later version.\n"
"This program is distributed in the hope that it will be useful, but WITHOUT ANY\n"
"WARRANTY !\n";
const char szHelp[] =
"Usage: ./v2-tmu-memtester [options]\n"
"\n"
"General options:\n"
" -h, --help Show this help and exit.\n"
" --version Print version (MAJOR.MINOR.PATCH) and exit.\n"
" -n, --num <N> Number of loops (default: 1).\n"
" -l, --log <file> Log file path.\n"
" --silent Silent mode (suppress normal output).\n"
" -t, --tsv <file> TSV output file path.\n"
"\n"
"Verbosity:\n"
" -v Increase log level by one step (repeatable).\n"
" Levels: 0=ERR, 1=INFO, 2=WARN, 3=DEBUG, 4=TRACE\n"
" Default: 0 (ERR). Example: -vvv -> DEBUG.\n"
"\n"
"Target selection:\n"
" --tmu0 Enable only tests for TMU0.\n"
" --tmu1 Enable only tests for TMU1.\n"
"\n"
"Test selection [ all if none set ] :\n"
" --address Run address tests.\n"
" --data Run data tests.\n"
" --data-huge Run large/extended data tests.\n"
"\n"
"TMU RAM limit override:\n"
" --tmu0-ram <MB> Force TMU0 RAM size: -1=auto, 1..4=MB.\n"
" --tmu1-ram <MB> Force TMU1 RAM size: -1=auto, 1..4=MB.\n"
"\n"
"Examples:\n"
" ./v2memtest --tmu0 --address -vv --tsv out.tsv\n"
" ./v2memtest --tmu0 --tmu1 --data --tmu0-ram 2 --tmu1-ram -1 -n 10\n";
def_sOptions sOptions = {
.eLogLevel = E_LOGLEVEL__ERROR,
.bSilent = false,
.szLogFileName = {0},
.szTSVFile = {0},
.usNumLoops = 1,
.bTestTMU0 = false,
.eTMU0RamLimit = E_TMU_RAMSIZE__AUTO,
.bTestTMU1 = false,
.eTMU1RamLimit = E_TMU_RAMSIZE__AUTO,
.bTestTMUAddress = false,
.bTestTMUData = false,
.bTestTMUDataHuge = false,
};
static struct option long_args[] = {
/* {<NAME>, <HAS_ARG>, <FLAG>, <VAL> */
{"help", no_argument, NULL, 'h'},
{"num", required_argument, NULL, 'n'},
{"log", required_argument, NULL, 'l'},
{"silent", no_argument, &sOptions.bSilent, true},
{"tsv", required_argument, NULL, 't'},
{"tmu0", no_argument, &sOptions.bTestTMU0, true},
{"tmu1", no_argument, &sOptions.bTestTMU1, true},
{"address", no_argument, &sOptions.bTestTMUAddress, true},
{"data", no_argument, &sOptions.bTestTMUData, true},
{"data-huge", no_argument, &sOptions.bTestTMUDataHuge, true},
{"tmu0-ram", required_argument, NULL, 0},
{"tmu1-ram", required_argument, NULL, 1},
{"version", no_argument, NULL, 2},
};
int main(int argc, char **argv)
{
int Status = 0;
unsigned long long ullNbErrorAll = 0;
const unsigned char boardNum = 0;
int option_index = 0;
int opt;
bool bQuit = false;
FxU32* sst = NULL;
while((opt=getopt_long(argc,argv,"hvn:e:l:",long_args,&option_index)) != -1)
{
switch(opt)
{
case 2: // Version
printf("%d.%d.%d", V2MEMTEST__VERSION__MAJOR,
V2MEMTEST__VERSION__MINOR,
V2MEMTEST__VERSION__PATCH);
bQuit = true;
break;
case 0: // TMU0 long options
case 1: // TMU1 long options
{
def_eTMURamLimit newRAMLimit;
switch(strtol(optarg, NULL, 10))
{
case -1: newRAMLimit = E_TMU_RAMSIZE__AUTO; break;
case 1: newRAMLimit = E_TMU_RAMSIZE__1MB; break;
case 2: newRAMLimit = E_TMU_RAMSIZE__2MB; break;
case 3: newRAMLimit = E_TMU_RAMSIZE__3MB; break;
case 4: newRAMLimit = E_TMU_RAMSIZE__4MB; break;
default:
Status = E_ERROR__BAD_ARGUMENT_VALUE;
ErrorCheck_gotoCleanUp();
break;
}
if(opt==0) sOptions.eTMU0RamLimit = newRAMLimit;
else sOptions.eTMU1RamLimit = newRAMLimit;
}
break;
case '?':
puts(szHelp);
Status = E_ERROR__UNKNOWN_ARGUMENT;
ErrorCheck_gotoCleanUp();
break;
case 'l':
strcpy(sOptions.szLogFileName, optarg);
break;
case 't':
strcpy(sOptions.szTSVFile, optarg);
break;
case 'h':
puts(szHelp);
bQuit = true;
break;
case 'n':
sOptions.usNumLoops = strtol(optarg, NULL, 10);
break;
case 'v':
if(sOptions.eLogLevel < E_LOGLEVEL__TRACE)
sOptions.eLogLevel++;
break;
default:
Status = E_ERROR__UNKNOWN_ERROR;
ErrorCheck_gotoCleanUp();
break;
}
}
if(bQuit)
return 0;
if(!sOptions.bTestTMU0 && !sOptions.bTestTMU1)
{
sOptions.bTestTMU0 = true;
sOptions.bTestTMU1 = true;
}
if( !sOptions.bTestTMUAddress &&
!sOptions.bTestTMUData &&
!sOptions.bTestTMUDataHuge)
{
sOptions.bTestTMUAddress = true;
sOptions.bTestTMUData = true;
sOptions.bTestTMUDataHuge = true;
}
sst1DeviceInfoStruct devInfo;
memset(&devInfo,0,sizeof(sst1DeviceInfoStruct));
SstRegs *sstregs = NULL;
if(!sOptions.bSilent)
{
printf(szTitle, V2MEMTEST__VERSION__MAJOR,
V2MEMTEST__VERSION__MINOR,
V2MEMTEST__VERSION__PATCH);
putchar('\n');
puts(szLicence);
putchar('\n');
}
srandom(time(NULL));
FaultSource_Reset();
for(int j=0; j<100; j++)
{
if ((sst = sst1InitMapBoard(boardNum)) == NULL)
{
fprintf(stderr, "No Voodoo boards found\n");
Status = E_ERROR__SST1_INIT;
ErrorCheck_gotoCleanUp();
}
sst1InitRegisters(sst);
sstregs = (SstRegs *) sst;
if (sst1InitGetDeviceInfo(sst, &devInfo) == FXFALSE)
{
fprintf(stderr, "Couldn't get info for Voodoo # %d\n", boardNum);
Status = E_ERROR__SST1_GET_INFO;
ErrorCheck_gotoCleanUp();
}
printf("FBI detected Memory: %lu MB\n", devInfo.fbiMemSize);
if(devInfo.fbiMemSize < 2)
{
fprintf(stderr, "Couldn't test Voodoo2 TMUs without minimum 2MB of FBI memory\n");
Status = E_ERROR__NOT_ENOUGH_FBI_RAM;
ErrorCheck_gotoCleanUp();
}
for (int tmu = 0; tmu < devInfo.numberTmus; tmu++)
{
printf("TMU%d detected Memory: %lu MB\n", tmu, devInfo.tmuMemSize[tmu]);
}
putchar('\n');
/*
bTestTMU0 = true;
bTestTMU1 = true;
*/
const bool bTestTMU0 = (devInfo.numberTmus > 0)
&& (devInfo.tmuMemSize[0] > 0)
&& sOptions.bTestTMU0;
const bool bTestTMU1 = (devInfo.numberTmus > 1)
&& (devInfo.tmuMemSize[1] > 0)
&& sOptions.bTestTMU1;
if(!bTestTMU0 && !bTestTMU1)
{
printf("no testable TMU\n");
goto CleanUp;
}
if(bTestTMU1 && !bTestTMU0)
{
printf("Warning, testing TMU1 without TMU0 is not recommended !\n");
}
ISET(sstregs->lfbMode, SST_LFB_RGBALANES_ARGB | SST_LFB_READFRONTBUFFER);
ISET(sstregs->fbzMode, SST_DRAWBUFFER_FRONT | SST_RGBWRMASK);
ISET(sstregs->fbzColorPath, SST_RGBSEL_TREXOUT | SST_CC_PASS | SST_ENTEXTUREMAP);
ISET(sstregs->textureMode, SST_RGB565 | SST_TC_REPLACE | SST_TCA_REPLACE);
ISET(sstregs->tLOD, 0);
for (int tmu = 0; tmu < devInfo.numberTmus; tmu++)
{
printf("Testing Board %d, TMU %d, %luMB \n\n",boardNum,tmu,devInfo.tmuMemSize[tmu]);
puts("# address & control lines test - cumulated");
for(int j=0;j<100;j++)
{
const unsigned long long err
= RenderTestAddress(&devInfo,
sst,
sstregs,
tmu,
devInfo.tmuMemSize[tmu]);
ullNbErrorAll += err;
putchar( err ? 'E' : '-');
fflush(stdout);
}
putchar('\n');
puts("# data test - single bit move");
for(int j=0;j<100;j++)
{
const unsigned long long err
= test_TMU_datalines(&devInfo,
sst,
sstregs,
tmu ,
0, //bit shift
devInfo.tmuMemSize[tmu]);
ullNbErrorAll += err;
putchar( err ? 'E' : '-');
fflush(stdout);
}
putchar('\n');
puts("# data test - random pattern");
for(int j=0;j<100;j++)
{
const unsigned long long err
= test_TMU_datalines(&devInfo,
sst,
sstregs,
tmu ,
1, // random
devInfo.tmuMemSize[tmu]);
ullNbErrorAll += err;
putchar( err ? 'E' : '-');
fflush(stdout);
}
putchar('\n');
puts("# data test - huge data");
for(int j=0;j<10;j++)
{
const unsigned long long err
= test_TMU_datalines_Huge( &devInfo,
sst,
sstregs,
tmu ,
devInfo.tmuMemSize[tmu]);
ullNbErrorAll += err;
putchar( err ? 'E' : '-');
fflush(stdout);
}
putchar('\n');
}
}
FaultSource_Sort();
FaultSource_Display();
printf("Test Complete, ullNbErrorAll = %lld\n",ullNbErrorAll);
CleanUp:
if(sst)
sst1InitShutdown(sst);
return Status;
}