325 lines
11 KiB
C
325 lines
11 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cvg.h"
|
|
#include <glide.h>
|
|
#include "sst1init.h"
|
|
#include "fxpci.h"
|
|
|
|
#include "FaultSources.h"
|
|
#include "Utils.h"
|
|
#include "Draw.h"
|
|
|
|
#include "Test_Common.h"
|
|
#include "Test_Data.h"
|
|
|
|
static unsigned long long
|
|
RenderTest( sst1DeviceInfoStruct* devInfo,
|
|
FxU32* sst,
|
|
SstRegs *sstregs,
|
|
const char ucNumTMU,
|
|
const FxU32 mem,
|
|
const FxU32 value1,
|
|
const FxU32 value2,
|
|
def_sFaultSourceScoreRec* const pFaultSrcCtx)
|
|
{
|
|
unsigned long long NbErr = 0;
|
|
static char szBuff[1024];
|
|
|
|
sst1InitIdle(sst);
|
|
unsigned long _trexInit0 = IGET(SST_TREX(sstregs,ucNumTMU)->trexInit0);
|
|
devInfo->tmuInit0[(int)ucNumTMU] = SST_TREXINIT0_DEFAULT ;
|
|
ISET(SST_TREX(sstregs,ucNumTMU)->trexInit0, devInfo->tmuInit0[(int)ucNumTMU]);
|
|
sst1InitIdle(sst);
|
|
|
|
ISET(sstregs->texBaseAddr, mem>>3);
|
|
|
|
/* Writing to first texture line, to use RAM bits 0..31 */
|
|
volatile FxU32 *texAddr
|
|
= (ucNumTMU<<(21-2)) /* NUM_TMU */
|
|
+ (((FxU32)0)<<(17-2)) /* LOD_0 */
|
|
+ (FxU32 *)SST_TEX_ADDRESS(sst); /* tex base address */
|
|
|
|
ISET(texAddr[0], value1);
|
|
|
|
/* writing to second texture line, to use RAM bits 32..63 */
|
|
texAddr += (1<<(9-2));
|
|
ISET(texAddr[0], value2);
|
|
|
|
clearScreen(sstregs,0x00000000,8,2);
|
|
|
|
/* draw a 8x2 rectangle */
|
|
drawRect(sstregs,0,0,2,2);
|
|
sst1InitIdle(sst);
|
|
|
|
/* reading back first line pixels addresses */
|
|
const uint32_t L1_1 = IGET(sst[(SST_LFB_ADDR >> 2) + 0]);
|
|
|
|
/* reading back second line pixels addresses */
|
|
const uint32_t L2_1 = IGET(sst[(SST_LFB_ADDR >> 2) + (2048 >> 2) + 0]);
|
|
|
|
const uint32_t ErrorMark_L1 = L1_1 ^ ( value1 + 0 );
|
|
const uint32_t ErrorMark_L2 = L2_1 ^ ( value2 + 0 );
|
|
|
|
const double dScoreFront = (mem >= 0x200000) ? 1.0 / 8 : 1.0 / 4;
|
|
const double dScoreBack = (mem >= 0x200000) ? 1.0 / 4 : 1.0 / 8;
|
|
|
|
if(ErrorMark_L1)
|
|
{
|
|
NbErr++;
|
|
logD("===========================================================\n");
|
|
logD( "# fault on 1st line, value : 0x%04X [expected 0x%04X] !\n",
|
|
value1, L1_1);
|
|
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, U9_TMU0_TF_DATA_0, U9_TMU0_TF_DATA_0);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 0, 1.0 / 16, RA10_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 4, 1.0 / 16, RA11_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 8, 1.0 / 16, RA12_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 12, 1.0 / 16, RA13_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 16, 1.0 / 16, RA10_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 20, 1.0 / 16, RA11_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 24, 1.0 / 16, RA12_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L1 >> 28, 1.0 / 16, RA13_1);
|
|
|
|
if(ucNumTMU == 0)
|
|
{
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, U9_TMU0_FT_DATA_0, U9_TMU0_FT_DATA_0);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreFront, U14, U12);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreBack, U23, U24);
|
|
WordBitFaultSet(pFaultSrcCtx,ErrorMark_L1, 1.0 / 4, U9_TMU0 + 1, U9_TMU0 + 16 + 1);
|
|
|
|
TMUTexDataCtrlFaultSet( pFaultSrcCtx,
|
|
ErrorMark_L1,
|
|
1.0 / 8,
|
|
U9_TMU0_TEX_CAS0,
|
|
U9_TMU0_TEX_CAS1,
|
|
mem < 0x200000 ?
|
|
U9_TMU0_TEX_RAS0
|
|
: U9_TMU0_TEX_RAS1,
|
|
U9_TMU0_TEX_WE);
|
|
}
|
|
else
|
|
{
|
|
/* during TMU1 test, TMU0 doesnt receive any textures => ignoring these
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 2, U9_TMU0_FT_DATA_0, U9_TMU0_FT_DATA_0);
|
|
*/
|
|
|
|
/* U9_TMU0_TT_DATA_x is only tested when using TMU1 */
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, U9_TMU0_TT_DATA_0, U9_TMU0_TT_DATA_0);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, TT_TDATA_R131, TT_TDATA_R131);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, U8_TMU1_TF_DATA_0, U8_TMU1_TF_DATA_0);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L1, 1.0 / 16, U8_TMU1_FT_DATA_0, U8_TMU1_FT_DATA_0);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreFront, U13, U11);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreBack, U27, U28);
|
|
WordBitFaultSet(pFaultSrcCtx,ErrorMark_L1, 1.0 / 4, U8_TMU1 + 1, U8_TMU1 + 16 + 1);
|
|
|
|
|
|
TMUTexDataCtrlFaultSet( pFaultSrcCtx,
|
|
ErrorMark_L1,
|
|
1.0 / 8,
|
|
U8_TMU1_TEX_CAS0,
|
|
U8_TMU1_TEX_CAS1,
|
|
mem < 0x200000 ?
|
|
U8_TMU1_TEX_RAS0
|
|
: U8_TMU1_TEX_RAS1,
|
|
U8_TMU1_TEX_WE);
|
|
}
|
|
|
|
|
|
|
|
logD("## overall fault LW bit-map : \n");
|
|
if(sOptions.eLogLevel >= E_LOGLEVEL__DEBUG)
|
|
{
|
|
sprintbin32Info(szBuff,ErrorMark_L1, 31, 0);
|
|
logD("%s\n",szBuff);
|
|
}
|
|
}
|
|
|
|
if(ErrorMark_L2)
|
|
{
|
|
NbErr++;
|
|
logD("===========================================================\n");
|
|
logD( "# fault on 2nd line, value : 0x%04X [expected 0x%04X] !\n",
|
|
value2, L2_1);
|
|
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, U9_TMU0_TF_DATA_0, U9_TMU0_TF_DATA_0);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 0, 1.0 / 16, RA10_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 4, 1.0 / 16, RA11_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 8, 1.0 / 16, RA12_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 12, 1.0 / 16, RA13_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 16, 1.0 / 16, RA10_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 20, 1.0 / 16, RA11_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 24, 1.0 / 16, RA12_1);
|
|
QuartetBitFaultSet(pFaultSrcCtx, ErrorMark_L2 >> 28, 1.0 / 16, RA13_1);
|
|
|
|
if(ucNumTMU == 0)
|
|
{
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, U9_TMU0_FT_DATA_0, U9_TMU0_FT_DATA_0);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreFront, U18, U17);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreBack, U25, U26);
|
|
WordBitFaultSet(pFaultSrcCtx,ErrorMark_L1, 1.0 / 4, U9_TMU0 + 32 + 1, U9_TMU0 + 48 + 1);
|
|
|
|
TMUTexDataCtrlFaultSet( pFaultSrcCtx,
|
|
ErrorMark_L2,
|
|
1.0 / 8,
|
|
U9_TMU0_TEX_CAS2,
|
|
U9_TMU0_TEX_CAS3,
|
|
mem < 0x200000 ? U9_TMU0_TEX_RAS0 : U9_TMU0_TEX_RAS1,
|
|
U9_TMU0_TEX_WE);
|
|
}
|
|
else
|
|
{
|
|
/* during TMU1 test, TMU0 doesnt receive any textures => ignoring these
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 2, U9_TMU0_FT_DATA_0, U9_TMU0_FT_DATA_0);
|
|
*/
|
|
|
|
/* U9_TMU0_TT_DATA_x is only tested when using TMU1 */
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, U9_TMU0_TT_DATA_0, U9_TMU0_TT_DATA_0);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, TT_TDATA_R131, TT_TDATA_R131);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, U8_TMU1_TF_DATA_0, U8_TMU1_TF_DATA_0);
|
|
WordBitFaultSet(pFaultSrcCtx, ErrorMark_L2, 1.0 / 16, U8_TMU1_FT_DATA_0, U8_TMU1_FT_DATA_0);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreFront, U16, U15);
|
|
MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreBack, U29, U30);
|
|
WordBitFaultSet(pFaultSrcCtx,ErrorMark_L2, 1.0 / 4, U8_TMU1 + 32 + 1, U8_TMU1 + 48 + 1);
|
|
|
|
TMUTexDataCtrlFaultSet( pFaultSrcCtx,
|
|
ErrorMark_L2,
|
|
1.0 / 8,
|
|
U8_TMU1_TEX_CAS2,
|
|
U8_TMU1_TEX_CAS3,
|
|
mem < 0x200000 ?
|
|
U8_TMU1_TEX_RAS0
|
|
: U8_TMU1_TEX_RAS1,
|
|
U8_TMU1_TEX_WE);
|
|
}
|
|
logD("## overall fault LW bit-map : \n");
|
|
if(sOptions.eLogLevel >= E_LOGLEVEL__DEBUG)
|
|
{
|
|
sprintbin32Info(szBuff,ErrorMark_L2, 31, 0);
|
|
logD("%s\n",szBuff);
|
|
}
|
|
}
|
|
|
|
clearScreen(sstregs,0x00000000,8,2);
|
|
|
|
/* reset the Init0 register back to its previous value */
|
|
sst1InitIdle(sst);
|
|
devInfo->tmuInit0[(int)ucNumTMU] = _trexInit0;
|
|
ISET(SST_TREX(sst,ucNumTMU)->trexInit0, devInfo->tmuInit0[(int)ucNumTMU]);
|
|
sst1InitIdle(sst);
|
|
|
|
return NbErr;
|
|
}
|
|
|
|
unsigned long long
|
|
test_TMU_datalines( sst1DeviceInfoStruct* devInfo,
|
|
FxU32* sst,
|
|
SstRegs *sstregs,
|
|
const unsigned char ucNumTMU,
|
|
const unsigned char bRandom,
|
|
const unsigned char RamSizeMB,
|
|
def_sFaultSourceScoreRec* const pFaultSrcCtx)
|
|
{
|
|
typedef struct _def_sMemBlock{
|
|
uint32_t ulAddStart;
|
|
uint32_t ulAddEnd;
|
|
uint32_t ulAddLength;
|
|
}def_sMemBlock;
|
|
|
|
const def_sMemBlock ar_memBlocks[] = {
|
|
{ .ulAddStart = 0x000000,
|
|
.ulAddLength = 0x100000
|
|
},
|
|
{ .ulAddStart = 0x100000,
|
|
.ulAddLength = 0x100000
|
|
},
|
|
{ .ulAddStart = 0x200000,
|
|
.ulAddLength = 0x100000
|
|
},
|
|
{ .ulAddStart = 0x300000,
|
|
.ulAddLength = 0x100000
|
|
},
|
|
};
|
|
|
|
unsigned long long ullNbErrorAll = 0;
|
|
|
|
/* set downstream TMUs to only passthrough */
|
|
for (int i=0; i<ucNumTMU; i++)
|
|
ISET(SST_TREX(sstregs,i)->textureMode, SST_TC_PASS | SST_TCA_PASS);
|
|
|
|
for( int iMemBlock = 0;
|
|
iMemBlock < sizeof(ar_memBlocks)/sizeof(def_sMemBlock);
|
|
iMemBlock++)
|
|
{
|
|
const def_sMemBlock* pMemBlk = &ar_memBlocks[iMemBlock];
|
|
if(RamSizeMB < 4 && pMemBlk->ulAddStart >= 0x300000) continue;
|
|
if(RamSizeMB < 3 && pMemBlk->ulAddStart >= 0x200000) continue;
|
|
if(RamSizeMB < 2 && pMemBlk->ulAddStart >= 0x100000) continue;
|
|
|
|
|
|
logD("Testing @ 0x%08x to 0x%08x ...\n",
|
|
pMemBlk->ulAddStart,
|
|
pMemBlk->ulAddStart + pMemBlk->ulAddLength -4);
|
|
|
|
unsigned char ucAddMovingBit = 0;
|
|
for( FxU32 addrTest = pMemBlk->ulAddStart ;
|
|
addrTest < (pMemBlk->ulAddStart + pMemBlk->ulAddLength);
|
|
ucAddMovingBit++)
|
|
{
|
|
FxU32 bitTest = 1u;
|
|
FxU32 bitTest2 = 1u << 31;
|
|
|
|
for(unsigned long nloop=0;; nloop++)
|
|
{
|
|
unsigned long ulNbError;
|
|
ulNbError = RenderTest( devInfo,
|
|
sst,
|
|
sstregs,
|
|
ucNumTMU,
|
|
addrTest,
|
|
bitTest,
|
|
bitTest2,
|
|
pFaultSrcCtx);
|
|
ullNbErrorAll += ulNbError;
|
|
|
|
if((bRandom && (nloop > 64)) || bitTest == 0)
|
|
break;
|
|
|
|
bitTest = bRandom ?
|
|
get_notnull_random_balanced_mByte()
|
|
: (bitTest << 1);
|
|
bitTest2 = bRandom ?
|
|
get_notnull_random_balanced_mByte()
|
|
: (bitTest2 >> 1);
|
|
}
|
|
|
|
logT("Done @ 0x%08x\n",addrTest);
|
|
|
|
const FxU32 new_addrTest = pMemBlk->ulAddStart | (1u << ucAddMovingBit);
|
|
if(new_addrTest == pMemBlk->ulAddStart) break;
|
|
addrTest = new_addrTest;
|
|
}
|
|
}
|
|
|
|
return ullNbErrorAll;
|
|
}
|