/* 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 . */ #include #include #include "cvg.h" #include #include "sst1init.h" #include "fxpci.h" #include "Utils.h" #include "V2MemTest.h" #include "FaultSources.h" #include "Utils.h" #include "Draw.h" #include "Test_Address.h" typedef struct _def_sTestAddress { FxU32 u32Addr; unsigned char nBank; /* RAS0 and RAS1 (Front/Back) */ unsigned char nColBit; unsigned char nRowBit; }def_sTestAddress; unsigned long RenderTestAddress( sst1DeviceInfoStruct * const devInfo, FxU32 * const sst, SstRegs * const sstregs, const char ucNumTMU, const unsigned char RamSizeMB, def_sFaultSourceScoreRec* const pFaultSrcCtx) { unsigned long NbErr=0; 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); /* set downstream TMUs to passthrough */ for (int i=0; itextureMode, SST_TC_PASS | SST_TCA_PASS); const def_sTestAddress add_list[] = { /* Bank0 */ {0x000000,0,0,0}, /* not addressable because its a 64bit bus {0x000001,0,0,0,0} {0x000002,0,0,0,0} {0x000004,0,0,0,0} */ {0x000008,0,1,0}, /* {target pin} */ {0x000010,0,2,0}, {0x000020,0,3,0}, {0x000040,0,4,0}, {0x000080,0,5,0}, {0x000100,0,6,0}, {0x000200,0,7,0}, {0x000400,0,8,0}, {0x000800,0,9,0}, {0x001000,0,0,1}, {0x002000,0,0,2}, {0x004000,0,0,3}, {0x008000,0,0,4}, {0x010000,0,0,5}, {0x020000,0,0,6}, {0x040000,0,0,7}, {0x080000,0,0,8}, {0x100000,0,0,9}, /* Bank1 */ {0x200000,1,0,0}, /* not addressable because its a 64bit bus {0x200001,1,0,0,0} {0x200002,1,0,0,0} {0x200004,1,0,0,0} */ {0x200008,1,1,0}, /* {target pin} */ {0x200010,1,2,0}, {0x200020,1,3,0}, {0x200040,1,4,0}, {0x200080,1,5,0}, {0x200100,1,6,0}, {0x200200,1,7,0}, {0x200400,1,8,0}, {0x200800,1,9,0}, {0x201000,1,0,1}, {0x202000,1,0,2}, {0x204000,1,0,3}, {0x208000,1,0,4}, {0x210000,1,0,5}, {0x220000,1,0,6}, {0x240000,1,0,7}, {0x280000,1,0,8}, {0x300000,1,0,9}, }; logT("testing %d MB of memory\n",RamSizeMB); for(unsigned char idx=0; idx < (sizeof(add_list)/sizeof(def_sTestAddress)); idx++) { /* Skipping unsupported addresses */ if(RamSizeMB<4 && add_list[idx].u32Addr >= 0x300000) continue; if(RamSizeMB<3 && add_list[idx].u32Addr >= 0x200000) continue; if(RamSizeMB<2 && add_list[idx].u32Addr >= 0x100000) continue; logT("idx = %d\n",idx); logT("u32Addr = 0x%08X\n",add_list[idx].u32Addr); const uint32_t TestVal1 = get_notnull_random_balanced_mByte(); logT("TestVal1 = %08X\n",TestVal1); uint32_t TestVal2 = 0; do { TestVal2 = get_notnull_random_balanced_mByte(); } while((count_bit32((TestVal2 ^ TestVal1)) < 12)); logT("TestVal2 = %08X\n",TestVal2); uint32_t TestValBlank1 = 0; do { TestValBlank1 = get_notnull_random_balanced_mByte(); } while( (count_bit32((TestValBlank1 ^ TestVal1) & 0x0000FFFF) < 6) || (count_bit32((TestValBlank1 ^ TestVal1) & 0xFFFF0000) < 6) || (count_bit32((TestValBlank1 ^ TestVal2)) < 12)); logT("TestValBlank1 = %08X\n",TestValBlank1); uint32_t TestValBlank2 = 0; do { TestValBlank2 = get_notnull_random_balanced_mByte(); } while( (count_bit32((TestValBlank2 ^ TestVal2) & 0x0000FFFF) < 6) || (count_bit32((TestValBlank2 ^ TestVal2) & 0xFFFF0000) < 6) || (count_bit32((TestValBlank2 ^ TestValBlank1)) < 12) || (count_bit32((TestValBlank2 ^ TestVal1)) < 12)); logT("TestValBlank2 = %08X\n",TestValBlank2); /* Clearing memory targets */ for(unsigned char idxclr=0; idxclr < sizeof(add_list)/sizeof(def_sTestAddress); idxclr++) { /* Skipping unsupported addresses */ if(RamSizeMB<4 && add_list[idxclr].u32Addr >= 0x300000) continue; if(RamSizeMB<3 && add_list[idxclr].u32Addr >= 0x200000) continue; if(RamSizeMB<2 && add_list[idxclr].u32Addr >= 0x100000) continue; /* set base mem @ */ ISET(SST_TREX(sstregs,ucNumTMU)->texBaseAddr, (add_list[idxclr].u32Addr>>3)); /* set @ to first line, using bits 00..31*/ volatile const FxU32 *texAddrBlank = (ucNumTMU<<(21-2)) + (((FxU32)0)<<(17-2)) /*LOD0*/ + (FxU32 *)SST_TEX_ADDRESS(sst); /* write the value */ ISET(texAddrBlank[0], TestValBlank1); /* set @ to second line, to use bits 32..63*/ volatile const FxU32 *texAddrBlank2 = (ucNumTMU<<(21-2)) + (((FxU32)0)<<(17-2)) /*LOD0*/ + (1<<(9-2)) + (FxU32 *)SST_TEX_ADDRESS(sst); /* write the value */ ISET(texAddrBlank2[0], TestValBlank2); } /* set base mem @ */ ISET(SST_TREX(sstregs,ucNumTMU)->texBaseAddr, (add_list[idx].u32Addr>>3)); /* set @ to first line, using bits 00..31*/ volatile const FxU32 *texAddr = (ucNumTMU<<(21-2)) + (((FxU32)0)<<(17-2)) /*LOD0*/ + (FxU32 *)SST_TEX_ADDRESS(sst); /* write the value */ ISET(texAddr[0], TestVal1); /* set @ to second line, to use bits 32..63*/ volatile const FxU32 *texAddr2 = (ucNumTMU<<(21-2)) + (((FxU32)0)<<(17-2)) /*LOD0*/ + (1<<(9-2)) + (FxU32 *)SST_TEX_ADDRESS(sst); /* write the value */ ISET(texAddr2[0], TestVal2); /* Checking expected memory map is there */ for(unsigned char idxdraw=0; idxdraw < sizeof(add_list) / sizeof(def_sTestAddress); idxdraw++) { if(RamSizeMB<4 && add_list[idxdraw].u32Addr >= 0x300000) continue; if(RamSizeMB<3 && add_list[idxdraw].u32Addr >= 0x200000) continue; if(RamSizeMB<2 && add_list[idxdraw].u32Addr >= 0x100000) continue; logT("idxdraw = %d, row = %d; col = %d\n",idxdraw,add_list[idxdraw].nRowBit,add_list[idxdraw].nColBit); //if(idxdraw > idx) break; clearScreen(sstregs,0x00000000,2,2); /* set to mem addr */ ISET(SST_TREX(sstregs,ucNumTMU)->texBaseAddr, (add_list[idxdraw].u32Addr >> 3)); /* draw a 2x2 square */ drawSquare(sstregs, ucNumTMU, 0, 0, 2); sst1InitIdle(sst); /* first line, to use bits 00..31 */ const uint32_t L1 = IGET(sst[(SST_LFB_ADDR>>2) + 0]); /* second line, to use bits 32..63 */ const uint32_t L2 = IGET(sst[(SST_LFB_ADDR>>2) + (2048>>2) + 0]); const uint32_t ErrorMark_L1 = (idxdraw == idx) ? (L1 ^ TestVal1) : (L1 ^ TestValBlank1); const uint32_t ErrorMark_L2 = (idxdraw == idx) ? (L2 ^ TestVal2) : (L2 ^ TestValBlank2); const uint32_t ErrorMarkOther_L1 = (idxdraw == idx) ? (L1 ^ TestValBlank1) : (L1 ^ TestVal1); const uint32_t ErrorMarkOther_L2 = (idxdraw == idx) ? (L2 ^ TestValBlank2) : (L2 ^ TestVal2); if(ErrorMark_L1 || ErrorMark_L2) { const def_eFaultSource TMUTexADDR_0_0 = (ucNumTMU == 0) ? U9_TMU0_TEX_ADDR_0_0 : U8_TMU1_TEX_ADDR_0_0; const def_eFaultSource TMUTexADDR_1_0 = (ucNumTMU == 0) ? U9_TMU0_TEX_ADDR_1_0 : U8_TMU1_TEX_ADDR_1_0; const def_eFaultSource TMUTexADDR_2_0 = (ucNumTMU == 0) ? U9_TMU0_TEX_ADDR_2_0 : U8_TMU1_TEX_ADDR_2_0; const def_eFaultSource TMUTexADDR_3_0 = (ucNumTMU == 0) ? U9_TMU0_TEX_ADDR_3_0 : U8_TMU1_TEX_ADDR_3_0; const def_eFaultSource _MEMChip_B0_0_A0 = (ucNumTMU == 0) ? U14_A0 : U13_A0; const def_eFaultSource _MEMChip_B0_1_A0 = (ucNumTMU == 0) ? U12_A0 : U11_A0; const def_eFaultSource _MEMChip_B0_2_A0 = (ucNumTMU == 0) ? U18_A0 : U16_A0; const def_eFaultSource _MEMChip_B0_3_A0 = (ucNumTMU == 0) ? U17_A0 : U15_A0; const def_eFaultSource _MEMChip_B1_0_A0 = (ucNumTMU == 0) ? U23_A0 : U27_A0; const def_eFaultSource _MEMChip_B1_1_A0 = (ucNumTMU == 0) ? U24_A0 : U28_A0; const def_eFaultSource _MEMChip_B1_2_A0 = (ucNumTMU == 0) ? U25_A0 : U29_A0; const def_eFaultSource _MEMChip_B1_3_A0 = (ucNumTMU == 0) ? U26_A0 : U30_A0; const def_eFaultSource MEMChip_0_A0 = (add_list[idxdraw].nBank == 0) ? _MEMChip_B0_0_A0 : _MEMChip_B1_0_A0; const def_eFaultSource MEMChip_1_A0 = (add_list[idxdraw].nBank == 0) ? _MEMChip_B0_1_A0 : _MEMChip_B1_1_A0; const def_eFaultSource MEMChip_2_A0 = (add_list[idxdraw].nBank == 0) ? _MEMChip_B0_2_A0 : _MEMChip_B1_2_A0; const def_eFaultSource MEMChip_3_A0 = (add_list[idxdraw].nBank == 0) ? _MEMChip_B0_3_A0 : _MEMChip_B1_3_A0; const def_eFaultSource MEMChip_0_A0_Other = (add_list[idxdraw].nBank == 1) ? _MEMChip_B0_0_A0 : _MEMChip_B1_0_A0; const def_eFaultSource MEMChip_1_A0_Other = (add_list[idxdraw].nBank == 1) ? _MEMChip_B0_1_A0 : _MEMChip_B1_1_A0; const def_eFaultSource MEMChip_2_A0_Other = (add_list[idxdraw].nBank == 1) ? _MEMChip_B0_2_A0 : _MEMChip_B1_2_A0; const def_eFaultSource MEMChip_3_A0_Other = (add_list[idxdraw].nBank == 1) ? _MEMChip_B0_3_A0 : _MEMChip_B1_3_A0; const def_eFaultSource RES_TEXADDR_0_L = (ucNumTMU == 0) ? RA35 : RA31; const def_eFaultSource RES_TEXADDR_0_H = (ucNumTMU == 0) ? RA34 : RA30; const def_eFaultSource RES_TEXADDR_0_8 = (ucNumTMU == 0) ? R116 : R113; const def_eFaultSource RES_TEXADDR_1_L = (ucNumTMU == 0) ? RA26 : RA25; const def_eFaultSource RES_TEXADDR_1_H = (ucNumTMU == 0) ? RA24 : RA23; const def_eFaultSource RES_TEXADDR_1_8 = (ucNumTMU == 0) ? R102 : R101; const def_eFaultSource RES_TEXADDR_2_L = (ucNumTMU == 0) ? RA33 : RA29; const def_eFaultSource RES_TEXADDR_2_H = (ucNumTMU == 0) ? RA28 : RA27; const def_eFaultSource RES_TEXADDR_2_8 = (ucNumTMU == 0) ? R112 : R111; const def_eFaultSource RES_TEXADDR_3_L = (ucNumTMU == 0) ? RA22 : RA20; const def_eFaultSource RES_TEXADDR_3_H = (ucNumTMU == 0) ? RA21 : RA19; const def_eFaultSource RES_TEXADDR_3_8 = (ucNumTMU == 0) ? R98 : R97; /* This test is simpler than I wanteed it to be. It focuses * only on address lines. I thought that reading a totally * unknown value can mean we have a control line issue but it * is more complex. In case of 2 addresses lines shorted, * the value we will get wont necessary be the one we wrote, * at any place. I am not 100% sure why, but it might be * because of EDO Ram Page mode... Or maybe lines are also used * internally by the TMU.. or maybe I just missed something ! * * Anyway, this test should do the job for @ lines (including * shorted). And the Control lines will be tested in a different * Module where @ lines wont change. */ /* Considering error only if more than 3 over 16 bits * are wrong, because we are only testing addresses * lines here */ if(count_bit32(ErrorMark_L1 & 0x0000FFFF) > 3) { NbErr++; /* If it matches the Other value with less than * 3 error bits */ if(count_bit32(ErrorMarkOther_L1 & 0x0000FFFF) < 3) { if(add_list[idx].nColBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_0_A0 + add_list[idx].nColBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_0_A0_Other + add_list[idx].nColBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_0_0 + add_list[idx].nColBit - 1, 1.0 / 2); if(add_list[idx].nColBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_L + add_list[idx].nColBit - 2, 1.0 / 2); else if(add_list[idx].nColBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_H + add_list[idx].nColBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_8, 1.0 / 2); } if(add_list[idx].nRowBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_0_A0 + add_list[idx].nRowBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_0_A0_Other + add_list[idx].nRowBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_0_0 + add_list[idx].nRowBit - 1, 1.0 / 2); if(add_list[idx].nRowBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_L + add_list[idx].nRowBit - 2, 1.0 / 2); else if(add_list[idx].nRowBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_H + add_list[idx].nRowBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_0_8, 1.0 / 2); } } } if(count_bit32(ErrorMark_L1 & 0xFFFF0000) > 3) { NbErr++; if(count_bit32(ErrorMarkOther_L1 & 0xFFFF0000) < 3) { if(add_list[idx].nColBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_1_A0 + add_list[idx].nColBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_1_A0_Other + add_list[idx].nColBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_1_0 + add_list[idx].nColBit - 1, 1.0 / 2); if(add_list[idx].nColBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_L + add_list[idx].nColBit - 2, 1.0 / 2); else if(add_list[idx].nColBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_H + add_list[idx].nColBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_8, 1.0 / 2); } if(add_list[idx].nRowBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_1_A0 + add_list[idx].nRowBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_1_A0_Other + add_list[idx].nRowBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_1_0 + add_list[idx].nRowBit - 1, 1.0 / 2); if(add_list[idx].nRowBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_L + add_list[idx].nRowBit - 2, 1.0 / 2); else if(add_list[idx].nRowBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_H + add_list[idx].nRowBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_1_8, 1.0 / 2); } } } if(count_bit32(ErrorMark_L2 & 0x0000FFFF) > 3) { NbErr++; if(count_bit32(ErrorMarkOther_L2 & 0x0000FFFF) < 3) { if(add_list[idx].nColBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_2_A0 + add_list[idx].nColBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_2_A0_Other + add_list[idx].nColBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_2_0 + add_list[idx].nColBit - 1, 1.0 / 2); if(add_list[idx].nColBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_L + add_list[idx].nColBit - 2, 1.0 / 2); else if(add_list[idx].nColBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_H + add_list[idx].nColBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_8, 1.0 / 2); } if(add_list[idx].nRowBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_2_A0 + add_list[idx].nRowBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_2_A0_Other + add_list[idx].nRowBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_2_0 + add_list[idx].nRowBit - 1, 1.0 / 2); if(add_list[idx].nRowBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_L + add_list[idx].nRowBit - 2, 1.0 / 2); else if(add_list[idx].nRowBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_H + add_list[idx].nRowBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_2_8, 1.0 / 2); } } } if(count_bit32(ErrorMark_L2 & 0xFFFF0000) > 3) { NbErr++; if(count_bit32(ErrorMarkOther_L2 & 0xFFFF0000) < 3) { if(add_list[idx].nColBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_3_A0 + add_list[idx].nColBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_3_A0_Other + add_list[idx].nColBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_3_0 + add_list[idx].nColBit - 1, 1.0 / 2); if(add_list[idx].nColBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_L + add_list[idx].nColBit - 2, 1.0 / 2); else if(add_list[idx].nColBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_H + add_list[idx].nColBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_8, 1.0 / 2); } if(add_list[idx].nRowBit!=0) { FaultSource_addScore(pFaultSrcCtx, MEMChip_3_A0 + add_list[idx].nRowBit - 1, 1.0 / 1); if(RamSizeMB>=2) FaultSource_addScore(pFaultSrcCtx, MEMChip_3_A0_Other + add_list[idx].nRowBit - 1, 1.0 / 2); FaultSource_addScore(pFaultSrcCtx, TMUTexADDR_3_0 + add_list[idx].nRowBit - 1, 1.0 / 2); if(add_list[idx].nRowBit < 4) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_L + add_list[idx].nRowBit - 2, 1.0 / 2); else if(add_list[idx].nRowBit < 8) FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_H + add_list[idx].nRowBit - 6, 1.0 / 2); else FaultSource_addScore(pFaultSrcCtx, RES_TEXADDR_3_8, 1.0 / 2); } } } } } } clearScreen(sstregs,0x00000000,2,2); sst1InitIdle(sst); devInfo->tmuInit0[(int)ucNumTMU] = _trexInit0; ISET(SST_TREX(sst,ucNumTMU)->trexInit0, devInfo->tmuInit0[(int)ucNumTMU]); sst1InitIdle(sst); return NbErr; }