/* * simm_electrical_test.c * * Created on: Nov 26, 2011 * Author: Doug * * Copyright (C) 2011-2023 Doug Brown * * 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 "simm_electrical_test.h" #include "../hal/parallel_bus.h" #include "hardware.h" /// The index of the highest SIMM address pin #define SIMM_HIGHEST_ADDRESS_LINE 20 /// Mask that represents every SIMM address pin #define SIMM_ADDRESS_PINS_MASK ((1UL << (SIMM_HIGHEST_ADDRESS_LINE + 1)) - 1) /// The index of the highest SIMM data pin #define SIMM_HIGHEST_DATA_LINE 31 /// Mask that represents every SIMM data pin #define SIMM_DATA_PINS_MASK 0xFFFFFFFFUL /// Milliseconds to wait before testing for shorts #define DELAY_SETTLE_TIME_MS 20 /// The index reported as a short when it's a ground short #define GROUND_FAIL_INDEX 0xFF /// The index reported when A0 is shorted #define FIRST_ADDRESS_LINE_FAIL_INDEX 0 /// The index reported when A20 is shorted #define LAST_ADDRESS_LINE_FAIL_INDEX (FIRST_ADDRESS_LINE_FAIL_INDEX + SIMM_HIGHEST_ADDRESS_LINE) /// The index reported when D0 is shorted #define FIRST_DATA_LINE_FAIL_INDEX (LAST_ADDRESS_LINE_FAIL_INDEX + 1) /// The index reported when D31 is shorted #define LAST_DATA_LINE_FAIL_INDEX (FIRST_DATA_LINE_FAIL_INDEX + SIMM_HIGHEST_DATA_LINE) /// The index reported when CS is shorted #define CS_FAIL_INDEX (LAST_DATA_LINE_FAIL_INDEX + 1) /// The index reported when OE is shorted #define OE_FAIL_INDEX (CS_FAIL_INDEX + 1) /// The index reported when WE is shorted #define WE_FAIL_INDEX (OE_FAIL_INDEX + 1) /// Enum representing the step we're on during the electrical test typedef enum ElectricalTestStage { TestingAddressLines,//!< We are testing an address pin TestingDataLines, //!< We are testing a data pin TestingCS, //!< We are testing the CS pin TestingOE, //!< We are testing the OE pin TestingWE, //!< We are testing the WE pin DoneTesting //!< We completed the test } ElectricalTestStage; static void SIMMElectricalTest_ResetGroundShorts(void); static void SIMMElectricalTest_AddGroundShort(uint8_t index); static bool SIMMElectricalTest_IsGroundShort(uint8_t index); /// Pin indexes that were detected as shorted to ground. They have to be saved, /// because they end up being detected as a short against every other pin, /// so we have to be able to filter them out when testing non-ground shorts. static uint32_t groundShorts[2]; /** Runs the electrical test * * @param errorHandler Pointer to function to call when a short is detected. * @return The number of errors we found * * The two parameters to errorHandler are the two indexes that are shorted. * (See the _FAIL_INDEX defines at the top of this file) */ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t)) { // Returns number of errors found int numErrors = 0; // Reset the pins we have determined that are shorted to ground // (We have to ignore them during the second phase of the test) SIMMElectricalTest_ResetGroundShorts(); // First check for anything shorted to ground. Set all lines as inputs with // a weak pull-up resistor. Then read the values back and check for any // zeros. This would indicate a short to ground. ParallelBus_SetAddressDir(0); ParallelBus_SetDataDir(0); ParallelBus_SetCSDir(false); ParallelBus_SetOEDir(false); ParallelBus_SetWEDir(false); ParallelBus_SetAddressPullups(SIMM_ADDRESS_PINS_MASK); ParallelBus_SetDataPullups(SIMM_DATA_PINS_MASK); ParallelBus_SetCSPullup(true); ParallelBus_SetOEPullup(true); ParallelBus_SetWEPullup(true); // Wait a brief moment... DelayMS(DELAY_SETTLE_TIME_MS); // Now loop through every pin and check it. uint8_t curPin = 0; uint8_t i; // Read the address pins back first uint32_t readback = ParallelBus_ReadAddress(); // Check each bit for a LOW which would indicate a short to ground for (i = 0; i <= SIMM_HIGHEST_ADDRESS_LINE; i++) { // Did we find a low bit? if (!(readback & 1)) { // That means this pin is shorted to ground. // So notify the caller that we have a ground short on this pin if (errorHandler) { errorHandler(curPin, GROUND_FAIL_INDEX); } // Add it to our internal list of ground shorts also. SIMMElectricalTest_AddGroundShort(curPin); // And of course increment the error counter. numErrors++; } // No matter what, though, move on to the next bit and pin. readback >>= 1; curPin++; } // Repeat the exact same process for the data pins readback = ParallelBus_ReadData(); for (i = 0; i <= SIMM_HIGHEST_DATA_LINE; i++) { if (!(readback & 1)) { if (errorHandler) { errorHandler(curPin, GROUND_FAIL_INDEX); } SIMMElectricalTest_AddGroundShort(curPin); numErrors++; } readback >>= 1; curPin++; } // Check chip select in the same way... if (!ParallelBus_ReadCS()) { if (errorHandler) { errorHandler(curPin, GROUND_FAIL_INDEX); } SIMMElectricalTest_AddGroundShort(curPin); numErrors++; } curPin++; // Output enable... if (!ParallelBus_ReadOE()) { if (errorHandler) { errorHandler(curPin, GROUND_FAIL_INDEX); } SIMMElectricalTest_AddGroundShort(curPin); numErrors++; } curPin++; // Write enable... if (!ParallelBus_ReadWE()) { if (errorHandler) { errorHandler(curPin, GROUND_FAIL_INDEX); } SIMMElectricalTest_AddGroundShort(curPin); numErrors++; } curPin++; // Doesn't need to be here, but for consistency I'm leaving it. // OK, now we know which lines are shorted to ground. We need to keep that // in mind, because those lines will now show as shorted to ALL other // lines...ignore them during tests to find other independent shorts // Now, check each individual line vs. all other lines on the SIMM for any // shorts between them ElectricalTestStage curStage = TestingAddressLines; // Counter of what address or data pin we're on. Not used for control lines. uint8_t addrDataPin = 0; // What pin we are currently testing all other pins against. uint8_t testPin = 0; // More explanation: addrDataPin is only a counter inside the address or // data pins. testPin is a total counter of ALL pins. while (curStage != DoneTesting) { // Set one pin to output a 0. // Set all other pins as inputs with pull-ups. // Then read back all the other pins. If any of them read back as 0, // it means they are shorted to the pin we set as an output. // If we're testing address lines right now, set the current address // line as an output (and make it output a LOW). Set all other address // lines as inputs with pullups. if (curStage == TestingAddressLines) { // Mask of the address pin we're testing uint32_t addressLineMask = (1UL << addrDataPin); // Set it as an output and all other address pins as inputs. ParallelBus_SetAddressDir(addressLineMask); ParallelBus_SetAddress(0); ParallelBus_SetAddressPullups(~addressLineMask); } else { // If not testing an address line, set all address pins as inputs // with pullups. All the other stages follow the same pattern so I // won't bother commenting them. ParallelBus_SetAddressDir(0); ParallelBus_SetAddressPullups(SIMM_ADDRESS_PINS_MASK); } // Do the same thing for data lines... if (curStage == TestingDataLines) { uint32_t dataLineMask = (1UL << addrDataPin); ParallelBus_SetDataDir(dataLineMask); ParallelBus_SetData(0); ParallelBus_SetDataPullups(~dataLineMask); } else { ParallelBus_SetDataDir(0); ParallelBus_SetDataPullups(SIMM_DATA_PINS_MASK); } // Chip select... if (curStage == TestingCS) { ParallelBus_SetCSDir(true); ParallelBus_SetCS(false); } else { ParallelBus_SetCSDir(false); ParallelBus_SetCSPullup(true); } // Output enable... if (curStage == TestingOE) { ParallelBus_SetOEDir(true); ParallelBus_SetOE(false); } else { ParallelBus_SetOEDir(false); ParallelBus_SetOEPullup(true); } // And write enable. if (curStage == TestingWE) { ParallelBus_SetWEDir(true); ParallelBus_SetWE(false); } else { ParallelBus_SetWEDir(false); ParallelBus_SetWEPullup(true); } // OK, so now we have set up all lines as needed. Exactly one pin is // outputting a 0, and all other pins are inputs with pull-ups enabled. // Read back all the lines, and if any pin reads back as 0, it means // that pin is shorted to the pin we are testing (overpowering its // pullup). However, because we test each pin against every other pin, // any short would appear twice. Once as "pin 1 is shorted to pin 2" // and once as "pin 2 is shorted to pin 1". To avoid that annoyance, I // only check each combination of two pins once. You'll see below how I // do this by testing to ensure curPin > testPin. // Allow everything to settle DelayMS(DELAY_SETTLE_TIME_MS); // Now keep a count of how many pins we have actually checked during // THIS test iteration. This is the "fail index" of the current pin. curPin = 0; // Read back the address data to see if any shorts were found readback = ParallelBus_ReadAddress(); // Count any shorted pins for (i = 0; i <= SIMM_HIGHEST_ADDRESS_LINE; i++) { // Failure here? if ((curPin > testPin) && // We haven't already checked this combination of pins (don't test pin against itself either) !(readback & 1) && // It's showing as low (which indicates a short) !SIMMElectricalTest_IsGroundShort(curPin)) // And it's not recorded as a short to ground { // Send it out as an error notification and increase error counter if (errorHandler) { errorHandler(testPin, curPin); } numErrors++; } // No matter what, move on to the next bit and pin readback >>= 1; curPin++; } // Same thing for data pins readback = ParallelBus_ReadData(); // Count any shorted pins for (i = 0; i <= SIMM_HIGHEST_DATA_LINE; i++) { // Failure here? if ((curPin > testPin) && // We haven't already checked this combination of pins (don't test pin against itself either) !(readback & 1) && // It's showing as low (which indicates a short) !SIMMElectricalTest_IsGroundShort(curPin)) // And it's not recorded as a short to ground { if (errorHandler) { errorHandler(testPin, curPin); } numErrors++; } readback >>= 1; curPin++; } // And chip select... if ((curPin > testPin) && !ParallelBus_ReadCS() && !SIMMElectricalTest_IsGroundShort(curPin)) { if (errorHandler) { errorHandler(testPin, curPin); } numErrors++; } curPin++; // Output enable... if ((curPin > testPin) && !ParallelBus_ReadOE() && !SIMMElectricalTest_IsGroundShort(curPin)) { if (errorHandler) { errorHandler(testPin, curPin); } numErrors++; } curPin++; // And write enable if ((curPin > testPin) && !ParallelBus_ReadWE() && !SIMMElectricalTest_IsGroundShort(curPin)) { if (errorHandler) { errorHandler(testPin, curPin); } numErrors++; } curPin++; // Not needed, kept for consistency // Finally, move on to the next stage if needed. if (curStage == TestingAddressLines) { // If we've exhausted all address lines, move on to the next stage // (and reset the pin counter to 0) if (++addrDataPin > SIMM_HIGHEST_ADDRESS_LINE) { curStage++; addrDataPin = 0; } } else if (curStage == TestingDataLines) { // If we've exhausted all data lines, move on to the next stage // (don't bother resetting the pin counter -- the other stages don't use it) if (++addrDataPin > SIMM_HIGHEST_DATA_LINE) { curStage++; } } else { curStage++; } // Move on to test the next pin testPin++; } // Restore to a normal state by calling ParallelBus_Init again. ParallelBus_Init(); // Now that the final state is restored, return the number of errors found return numErrors; } /** Resets our list of pins that are shorted to ground * */ static void SIMMElectricalTest_ResetGroundShorts(void) { groundShorts[0] = 0; groundShorts[1] = 0; } /** Adds a pin to the list of ground shorts * * @param index The index of the pin in the electrical test */ static void SIMMElectricalTest_AddGroundShort(uint8_t index) { if (index < 32) { groundShorts[0] |= (1UL << index); } else if (index < 64) { groundShorts[1] |= (1UL << (index - 32)); } // None are >= 64, no further handling needed } /** Determines if a pin has already been saved as a ground short * * @param index The index of the pin in the electrical test * @return True if this pin has been determined as a short to GND, false if not */ static bool SIMMElectricalTest_IsGroundShort(uint8_t index) { if (index < 32) { return ((groundShorts[0] & (1UL << index)) != 0); } else if (index < 64) { return ((groundShorts[1] & (1UL << (index - 32))) != 0); } else { return false; } }