mac-rom-simm-programmer/tests/simm_electrical_test.c
2012-05-25 20:21:04 -07:00

434 lines
12 KiB
C

/*
* simm_electrical_test.c
*
* Created on: Nov 26, 2011
* Author: Doug
*
* Copyright (C) 2011-2012 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "simm_electrical_test.h"
#include "../ports.h"
#include "../delay.h"
#define SIMM_HIGHEST_ADDRESS_LINE 20
#define SIMM_ADDRESS_PINS_MASK ((1UL << (SIMM_HIGHEST_ADDRESS_LINE + 1)) - 1)
#define SIMM_HIGHEST_DATA_LINE 31
#define SIMM_DATA_PINS_MASK (0xFFFFFFFFUL)
#define DELAY_SETTLE_TIME_MS 20
typedef enum ElectricalTestStage
{
TestingAddressLines,
TestingDataLines,
TestingCS,
TestingOE,
TestingWE,
DoneTesting
} ElectricalTestStage;
// Private functions
void SIMMElectricalTest_ResetGroundShorts(void);
void SIMMElectricalTest_AddGroundShort(uint8_t index);
bool SIMMElectricalTest_IsGroundShort(uint8_t index);
int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
{
// Returns number of errors found
int numErrors = 0;
// Pins we have determined are shorted to ground
// (We have to ignore them during the second phase of the test)
SIMMElectricalTest_ResetGroundShorts();
Ports_Init();
// Give everything a bit of time to settle down
DelayMS(DELAY_SETTLE_TIME_MS);
// 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.
Ports_SetAddressDDR(0);
Ports_SetDataDDR(0);
Ports_SetCSDDR(false);
Ports_SetOEDDR(false);
Ports_SetWEDDR(false);
Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, SIMM_ADDRESS_PINS_MASK);
Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, SIMM_DATA_PINS_MASK);
Ports_SetCSPullup(true);
Ports_SetOEPullup(true);
Ports_SetWEPullup(true);
DelayMS(DELAY_SETTLE_TIME_MS);
uint8_t curPin = 0;
uint8_t i;
// Read the address pins back first of all
uint32_t readback = Ports_ReadAddress();
if (readback != SIMM_ADDRESS_PINS_MASK)
{
// 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
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 = Ports_ReadData();
if (readback != SIMM_DATA_PINS_MASK)
{
for (i = 0; i <= SIMM_HIGHEST_DATA_LINE; i++)
{
if (!(readback & 1)) // failure here?
{
errorHandler(curPin, GROUND_FAIL_INDEX);
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
readback >>= 1;
curPin++;
}
}
// Check chip select in the same way...
if (!Ports_ReadCS())
{
errorHandler(curPin, GROUND_FAIL_INDEX);
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
curPin++;
// Output enable...
if (!Ports_ReadOE())
{
errorHandler(curPin, GROUND_FAIL_INDEX);
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
curPin++;
// Write enable...
if (!Ports_ReadWE())
{
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;
int x = 0; // Counter of what address or data pin we're on. Not used for control lines.
uint8_t testPin = 0; // What pin we are currently testing all other pins against.
// x 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)
{
uint32_t addressLineMask = (1UL << x); // mask of the address pin we're testing
Ports_SetAddressDDR(addressLineMask); // set it as an output and all other address pins as inputs
Ports_AddressOut_RMW(0, addressLineMask); // set the output pin to output "0" without affecting the input pins
Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, ~addressLineMask); // turn on the pullups on all input pins
}
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.
Ports_SetAddressDDR(0);
Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, SIMM_ADDRESS_PINS_MASK);
}
// Do the same thing for data lines...
if (curStage == TestingDataLines)
{
uint32_t dataLineMask = (1UL << x);
Ports_SetDataDDR(dataLineMask);
Ports_DataOut_RMW(0, dataLineMask);
Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, ~dataLineMask);
}
else
{
Ports_SetDataDDR(0);
Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, SIMM_DATA_PINS_MASK);
}
// Chip select...
if (curStage == TestingCS)
{
Ports_SetCSDDR(true);
Ports_SetCSOut(false);
}
else
{
Ports_SetCSDDR(false);
Ports_SetCSPullup(true);
}
// Output enable...
if (curStage == TestingOE)
{
Ports_SetOEDDR(true);
Ports_SetOEOut(false);
}
else
{
Ports_SetOEDDR(false);
Ports_SetOEPullup(true);
}
// And write enable.
if (curStage == TestingWE)
{
Ports_SetWEDDR(true);
Ports_SetWEOut(false);
}
else
{
Ports_SetWEDDR(false);
Ports_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.
DelayMS(DELAY_SETTLE_TIME_MS);
// Now keep a count of how many pins we have actually checked during THIS test.
curPin = 0;
// Read back the address data to see if any shorts were found
readback = Ports_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
errorHandler(testPin, curPin);
numErrors++;
}
// No matter what, move on to the next bit and pin
readback >>= 1;
curPin++;
}
// Same thing for data pins
readback = Ports_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
{
errorHandler(testPin, curPin);
numErrors++;
}
readback >>= 1;
curPin++;
}
// And chip select...
if ((curPin > testPin) &&
!Ports_ReadCS() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
errorHandler(testPin, curPin);
numErrors++;
}
curPin++;
// Output enable...
if ((curPin > testPin) &&
!Ports_ReadOE() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
errorHandler(testPin, curPin);
numErrors++;
}
curPin++;
// And write enable
if ((curPin > testPin) &&
!Ports_ReadWE() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
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 (++x > SIMM_HIGHEST_ADDRESS_LINE)
{
curStage++;
x = 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 (++x > SIMM_HIGHEST_DATA_LINE)
{
curStage++;
}
}
else
{
curStage++;
}
// Move on to test the next pin
testPin++;
}
// Restore to a normal state. Disable any pull-ups, return CS/OE/WE to outputs,
// deassert them all, set data as an input, set address as an input,
// assert CS and OE. Should be a pretty normal state.
// To start, make everything an input so we can disable pullups.
Ports_SetCSDDR(false);
Ports_SetOEDDR(false);
Ports_SetWEDDR(false);
Ports_SetAddressDDR(0);
Ports_SetDataDDR(0);
// Disable pullups
Ports_SetCSPullup(false);
Ports_SetOEPullup(false);
Ports_SetWEPullup(false);
Ports_AddressPullups_RMW(0, SIMM_ADDRESS_PINS_MASK);
Ports_DataPullups_RMW(0, SIMM_DATA_PINS_MASK);
// Set control lines to deasserted outputs (remember ON is deasserted)
Ports_SetCSDDR(true);
Ports_SetOEDDR(true);
Ports_SetWEDDR(true);
Ports_SetCSOut(true);
Ports_SetOEOut(true);
Ports_SetWEOut(true);
// Set address lines to outputs and set the outputted address to zero
Ports_SetAddressDDR(SIMM_ADDRESS_PINS_MASK);
Ports_SetAddressOut(0);
// Leave data lines as inputs...(they're already inputs so do nothing...)
// Now assert CS and OE so we're in normal "read" mode
Ports_SetCSOut(false);
Ports_SetOEOut(false);
// Now that the final state is restored, return the number of errors found
return numErrors;
}
// Stuff for handling shorts to ground
// (They have to be remembered because the ground shorts will be repeated
// when you test each individual pin against all other pins)
static uint32_t groundShorts[2];
void SIMMElectricalTest_ResetGroundShorts(void)
{
groundShorts[0] = 0;
groundShorts[1] = 0;
}
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
}
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;
}
}