📄 post.c
字号:
//++
//post.c - power on self test routines
//
// COPYRIGHT (C) 2005 BY SPARE TIME GIZMOS, MILPITAS CA, USA.
// 1636 Fallen Leaf Drive / Milpitas CA 95035 USA / info@SpareTimeGizmos.com
//
// This file is part of the Spare Time Gizmos MP3 Player firmware. This firmware
// 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 firmware 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., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA.
//
//DESCRIPTION:
//
// The self test functions currently built in to the firmware are:
//
// #1 - CPU (P68C668) failure
// #2 - Flash ROM checksum fail
//
//
//REVISION HISTORY:
// dd-mmm-yy who description
// 15-May-05 RLA New file.
// 13-Oct-05 RLA Add the RAMSize() function.
//--
// Include files...
#include <stdio.h> // needed so DBGOUT(()) can find printf!
#include "standard.h" // standard types - BYTE, WORD, BOOL, etc
#include "reg66x.h" // declarations for Philips 89C66x processors
#include "player.h" // project wide (hardware configuration) declarations
#include "timer.h" // define the DELAYUS() macro...
#include "post.h" // POST codes for Fail()
//++
// This function will calculate the checksum of the program EPROM. The
// checksum is calculated so that the 16 bit unsigned sum of all the bytes
// in the ROM, _including_ the checksum in the last two bytes, is equal to
// the last two bytes. This slightly arcane algorithm is used because it
// gives the same value for the checksum as the Data I/O EEPROM programmer.
//
// Since the checksum is included in itself, we have to go to some lengths
// to prevent the checksum value from affecting its own calculation. The way
// that's done is to actually use the last _four_ bytes of the ROM - the last
// two contain the checksum and the two before that contain the complement of
// each byte in the checksum. The sum of a byte and its complement is always
// 0x0100, and since there are two such bytes, adding a checksum to the ROM in
// this way always adds 0x0200 to the original checksum _regardless_ of what
// the actual checksum value may be. The ROMCKSUM program that's used to
// calculate and store the checksum in the .HEX file takes this into account,
// so we can simply ignore the whole issue here.
//--
PUBLIC WORD ROMChecksum (WORD cbROM)
{
// cbROM is the size of the system EEPROM, in bytes (e.g. 8192 or 32768)...
WORD wChecksum; // checksum accumulator
WORD pcROM; // pointer to bytes in ROM
// Just sum up all the bytes, igoring overflows... Pretty easy.
for (wChecksum = pcROM = 0; pcROM != cbROM; ++pcROM) {
wChecksum += * (PCBYTE) pcROM;
// If the ROM size is 32K, then believe it or not the WDT will time out
// before we can finish our checksum. It's too much overhead to reset the
// WDT for every single byte, but we can do it every 4K or so...
//if ((pcROM & 0xFFF) == 0) ResetWDT();
}
//DBGOUT(("ROMChecksum: wChecksum=0x%04X, ROM=0x%04X, cbROM=%u ...\n",
// wChecksum, *((PCWORD) (cbROM-2)), cbROM));
return wChecksum;
}
//++
// This routine will size the external data RAM on this 8051. It's not a
// memory test in any way, and you shouldn't assume the memory works just
// because you get a number back from this routine. It's also a non-destructive
// test and restores the original RAM contents before it returns.
//
// Notice that this implicitly assumes that RAM is contiguous and can be
// addressed starting from location zero - if external RAM is mapped to
// another address or is non-contiguous, you're out of luck. Also note that
// it returns zero if no external RAM can be found; in the unlikely event that
// we actually have 64K of RAM, you'll only get back 65535!
//--
PUBLIC WORD RAMSize (void)
{
WORD cbRAM = 0; WORD T1;
// The algorithm is pretty simple - just modify a pair of bytes in external
// RAM until we find a word which can't be modified and then stop. We could
// test every pair of bytes, but in the interest of expediency we assume that
// memory occurs in multiples of 256 bytes and only test every page. Note that
// some microprocessors, notably the 89C66x family, have RAMs that are not
// multiples of 1K bytes (e.g. the 89C668 actually has 7936 bytes of "external"
// RAM).
//
// This routine has a few limitations, the most important of which is that
// it won't work correctly for memory which is incompletely decoded (for example,
// an 8K external SRAM where A15, A14 and A13 are unconnected). That isn't a
// problem for any of the embedded MCU's we're currently using.
do {
T1 = *((WORD volatile XDATA *) cbRAM);
*((WORD volatile XDATA *) cbRAM) = 0xA55A;
if (*((WORD volatile XDATA *) cbRAM) != 0xA55A) break;
*((WORD volatile XDATA *) cbRAM) = T1;
cbRAM += 256;
} while (cbRAM != 0);
return cbRAM;
}
#ifdef UNUSED
//++
// This routine performs a very simple test of external SRAM. External RAM
// is assumed to be mapped starting from address 0 and thru cbRAM-1. Pass 1
// of this test simply writes every pair of RAM bytes with its address and the
// next pair with the complement of its address. Pass 2 reads back and verifies
// the data from pass 1. If the SRAM fails, then error code PC_XRAM is POSTed.
//--
PUBLIC BOOL RAMTest (WORD cbRAM)
{
WORD pxRAM = cbRAM; WORD nCount=0; BOOL fPass = TRUE;
// Pass 1...
for (pxRAM = 0; pxRAM != cbRAM; pxRAM += 2) {
*((PXWORD) pxRAM) = pxRAM; pxRAM += 2;
*((PXWORD) pxRAM) = ~pxRAM;
}
// Pass 2...
for (pxRAM = 0; pxRAM != cbRAM; pxRAM += 2) {
if (*((PXWORD) pxRAM) != pxRAM) {
//DBGOUT(("RAM failure, addr=0x%04x, data=0x%04x\n", pxRAM, *((PXWORD) pxRAM)));
fPass = FALSE;
}
pxRAM += 2;
if (*((PXWORD) pxRAM) != ~pxRAM) {
//DBGOUT(("RAM failure, addr=0x%04x, data=0x%04x\n", pxRAM, *((PXWORD) pxRAM)));
fPass = FALSE;
}
}
return fPass;
}
#endif
//++
// Blink the single player diagnostic LED. Notice that this is a "terminal"
// routine - it disables interrupts and never returns control to the caller.
// It's used for debugging and in the case of POST failures...
//--
// Note that we want to use the DELAYUS macro here, not the more convenient
// DelayMS() subroutine, because the latter depends on timer 2 being initialized
// and working correctly. The DELAYUS macro generates a programmed delay loop
// directly inline and doesn't depend on anything else working...
#define SHORT_PAUSE {WORD j; for (j = 2000; j != 0; --j) DELAYUS(100); }
#define LONG_PAUSE {WORD j; for (j = 15000; j != 0; --j) DELAYUS(100); }
PUBLIC void BlinkLED (BYTE nCode)
{
BYTE nCount;
INT_OFF;
while (1) {
for (nCount = nCode; nCount > 0; --nCount) {
LED_ON; SHORT_PAUSE; LED_OFF; SHORT_PAUSE;
}
LONG_PAUSE;
}
}
//++
//--
PUBLIC void POST (BYTE nCode, BOOL fContinue)
{
// In the debug version, print a textual description of the error code...
#ifdef DEBUG
// If a CPU failure occurs then the serial port has not been initialized
// and we can't use DBGOUT - it would just hang forever!
if (nCode == PC_CPU) goto BLINK;
DBGOUT(("??FAIL: (%bu) ", nCode));
switch (nCode) {
case PC_CPU: DBGOUT(("CPU failure")); break;
case PC_CHECKSUM: DBGOUT(("ROM checksum error")); break;
case PC_SRAM: DBGOUT(("Internal SRAM failure")); break;
case PC_STA013: DBGOUT(("STA013 failure")); break;
case PC_DISPLAY: DBGOUT(("Display (VFD or LCD) failure")); break;
}
DBGOUT((" ...\n"));
#endif
if (fContinue) return;
DBGOUT(("\nSYSTEM HALTED. POWER CYCLE TO RESTART ...\n\n"));
#ifdef DEBUG
BLINK:
#endif
BlinkLED(nCode); HALT;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -