/* 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 . */ #define _BSD_SOURCE 1 #include #include #include #include #include "cvg.h" #include #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 * const devInfo, FxU32 * const sst, SstRegs * const sstregs, const char ucNumTMU, const FxU32 mem, const FxU32 value1, const FxU32 value2, const unsigned char RamSizeMB, 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(SST_TREX(sstregs,ucNumTMU)->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 2x2 rectangle */ drawRect(sstregs, ucNumTMU, 0, 0, 2, 2); sst1InitIdle(sst); const double dScoreFront = (mem >= 0x200000) ? 1.0 / 2 : 1.0 / 1; const double dScoreBack = (mem >= 0x200000) ? 1.0 / 1 : 1.0 / 2; /* reading back first line pixels addresses */ const uint32_t L1_1 = IGET(sst[(SST_LFB_ADDR >> 2) + 0]); const uint32_t ErrorMark_L1 = L1_1 ^ ( value1 + 0 ); if(ErrorMark_L1) { NbErr++; logD("===========================================================\n"); logD( "# fault on 1st line, value : 0x%04X [expected 0x%04X] !\n", value1, L1_1); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U9_TMU0_TF_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U3_FBI_TF_DATA_0); RGBToBus_Fault_Quartets(pFaultSrcCtx, ErrorMark_L1, 1.0/2, RA10_1, RA11_1, RA12_1, RA13_1); if(ucNumTMU == 0) { RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/ 2, U9_TMU0_FT_DATA_0); MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreFront, U14, U12); if(RamSizeMB>2) MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreBack, U23, U24); LWordBitFaultSet(pFaultSrcCtx,ErrorMark_L1, 1.0 / 1, U9_TMU0 + 1, U9_TMU0 + 16 + 1); } else { /* during TMU1 test, TMU0 doesnt receive any textures => ignoring these RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U9_TMU0_FT_DATA_0); */ /* U9_TMU0_TT_DATA_x is only tested when using TMU1 */ RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U9_TMU0_TT_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, TT_TDATA_R131); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U8_TMU1_TF_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L1, 1.0/2, U8_TMU1_FT_DATA_0); MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreFront, U13, U11); if(RamSizeMB>2) MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L1, dScoreBack, U27, U28); LWordBitFaultSet(pFaultSrcCtx,ErrorMark_L1, 1.0 / 1, U8_TMU1 + 1, U8_TMU1 + 16 + 1); } logD("## overall fault LW bit-map : \n"); if(sOptions.eLogLevel >= E_LOGLEVEL__DEBUG) { sprintbin32Info(szBuff,ErrorMark_L1, 31, 0); logD("%s\n",szBuff); } } /* reading back second line pixels addresses */ const uint32_t L2_1 = IGET(sst[(SST_LFB_ADDR >> 2) + (2048 >> 2) + 0]); const uint32_t ErrorMark_L2 = L2_1 ^ ( value2 + 0 ); if(ErrorMark_L2) { NbErr++; logD("===========================================================\n"); logD( "# fault on 2nd line, value : 0x%04X [expected 0x%04X] !\n", value2, L2_1); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U9_TMU0_TF_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U3_FBI_TF_DATA_0); RGBToBus_Fault_Quartets(pFaultSrcCtx, ErrorMark_L2, 1.0/2, RA10_1, RA11_1, RA12_1, RA13_1); if(ucNumTMU == 0) { RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U9_TMU0_FT_DATA_0); MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreFront, U18, U17); if(RamSizeMB>2) MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreBack, U25, U26); LWordBitFaultSet(pFaultSrcCtx,ErrorMark_L2, 1.0 / 1, U9_TMU0 + 32 + 1, U9_TMU0 + 48 + 1); } else { /* during TMU1 test, TMU0 doesnt receive any textures => ignoring these RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U9_TMU0_FT_DATA_0); */ /* U9_TMU0_TT_DATA_x is only tested when using TMU1 */ RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U9_TMU0_TT_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, TT_TDATA_R131); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U8_TMU1_TF_DATA_0); RGBToBus_Fault(pFaultSrcCtx, ErrorMark_L2, 1.0/2, U8_TMU1_FT_DATA_0); MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreFront, U16, U15); if(RamSizeMB>2) MemChipDQFaultSet(pFaultSrcCtx, ErrorMark_L2, dScoreBack, U29, U30); LWordBitFaultSet(pFaultSrcCtx,ErrorMark_L2, 1.0 / 1, U8_TMU1 + 32 + 1, U8_TMU1 + 48 + 1); } 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 * const devInfo, FxU32 * const sst, SstRegs * const 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; itextureMode, 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; logT("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, RamSizeMB, 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; }