//---------------------------------------------------------
// 28FxxxP: XJEase device file
// 28FxxxP.xje Revision: 1.12
// (c)2001-2008 XJTAG Limited
//
// Disclaimer: XJTAG makes no guarantees whatsoever
// about this code.  You use it at your own risk ...
// This code requires XJTAG version 1.4 or later.
//
// This file tests Non-JTAG Flash using TestNonDestructive()(INT result)
//                                      TestDestructive()(INT result)   
//---------------------------------------------------------

DEVICE NAME := "28FxxxP"

    PINS
  /*  
      // TSOP 56 (32/64/128Mbit)
      ADDR  := 56, 30, 1,  3,  4,  5,  6,  7,
               8,  10, 11, 12, 13, 17, 18, 19,
               20, 22, 23, 24, 25, 26, 27, 28;
      DATA  := 52, 50, 47, 45, 41, 39, 36, 34,
               51, 49, 46, 44, 40, 38, 35, 33;
      nWE   := 55;
      nCE  := 14;
      nOE   := 54;
      ADV   := 46;
      nWAIT := 56;
      WP    := 15;
      RST   := 44;
      CLK   := 45;

  */
  
      // Easy BGA (32/64/12/256Mbit)
      ADDR  := H8 /* 256M */, G1 /* 128M */, A8, C8, C7, B7, A7, D8,
               D7, C5, B5, A5, C4, D3, C3, B3,
               A3, C2, A2, D2, D1, C1, B1, A1;
      DATA  := E7, G7, H5, F5, F4, F3, E3, E1,
               H7, G6, G5, E5, E4, G3, E2, F2;
      nWE   := G8;
      nCE   := B4;
      nOE   := F8;
      ADV   := F6;
      nWAIT := G7;
      WP    := E4;
      RST   := F4;
      CLK   := C6;
  END;

  DISABLE DEVICE
    nOE  := 1;
    nCE := 1;
  END;

  TEST COVERAGE
    ADDR  := SHORTS OPEN HI LO;
    DATA  := SHORTS OPEN HI LO;
    nWE   := OPEN HI LO;
    nCE   := OPEN HI LO;
    nOE   := OPEN HI LO;
    ADV   := OPEN LO;
    WP    := OPEN HI;

  END;
  
  FILES
    "memtestFlash.xje";
    //"DataReader.xje";
  END;

END;

//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------

CONST INT READ_ARRAY        := 0x00FF;
CONST INT READ_ID           := 0x0090;
CONST INT READ_QUERY        := 0x0098;
CONST INT READ_STATUS       := 0x0070;
CONST INT CLEAR_STATUS      := 0x0050;
CONST INT WRITE_BUFFER      := 0x00E8;
CONST INT WORD_PROGRAM      := 0x0040;
CONST INT BLOCK_ERASE       := 0x0020;
CONST INT BLOCK_E_SUSP      := 0x00B0;
CONST INT BLOCK_E_RESM      := 0x00D0;
CONST INT CONFIG            := 0x00B8;
CONST INT SET_LOCK          := 0x0060;
CONST INT CLEAR_LOCK        := 0x0060;
CONST INT CONFIRM           := 0x00D0;
CONST INT PROTECT           := 0x00C0;

CONST INT MANUFACTURER_ADDR := 0x0000;
CONST INT DEVICE_ADDR       := 0x0001;

CONST INT MANUFACTURER_ID   := 0x0089;

//CONST INT DEVICE_ID         := 0x881A; // 64mb bottom parameter
//CONST INT DEVICE_ID         := 0x8817; // 64mb top parameter
//CONST INT DEVICE_ID         := 0x881B; // 128mb bottom parameter
//CONST INT DEVICE_ID         := 0x8818; // 128mb top parameter
CONST INT DEVICE_ID         := 0x891C; // 256mb bottom parameter
//CONST INT DEVICE_ID         := 0x8919; // 256mb top parameter

CONST INT BLOCK_LENGTH      := 0x00008000;
CONST INT BUFFER_LENGTH     := 0x10;

CONST INT DATA_BUS_WIDTH    := WIDTHOF(DATA);
CONST INT ADDR_BUS_WIDTH    := WIDTHOF(ADDR);

CONST INT FLASH_SIZE := (1<<ADDR_BUS_WIDTH)*DATA_BUS_WIDTH;

CONST INT RESULT_PASS       := FALSE;
CONST INT RESULT_FAIL       := TRUE;

CONST INT FLASH_PROG_LEVEL_NONE       := 0;  // Flash erased everywhere
CONST INT FLASH_PROG_LEVEL_SOME       := 1;  // Flash programmed enough to verify data/addr bus for opens/stucks and data bus 
                                             // for shorts. Ie. Data written to a single block, or small number of blocks.
CONST INT FLASH_PROG_LEVEL_LOTS       := 2;  // Flash programmed enough to data/addr bus for opens/stucks and shorts.
                                             // Ie. Data written to a large number of blocks.

//------------------------------------------------------------------------
// The higher this level is set, the more test coverage will be attained.
// The aim is to set the highest level  possible with the test still passing.
// If the flash is totally erased, the test will only pass if this variable
// is set to FLASH_PROG_LEVEL_NONE.
//------------------------------------------------------------------------
CONST INT FLASH_PROG_LEVEL := FLASH_PROG_LEVEL_NONE;

CONST INT DEBUG             := TRUE;


// Globals
INT ALL_ONES WIDTH FLASH_SIZE   := ~0[(FLASH_SIZE-1)..0];
INT dataSet WIDTH  FLASH_SIZE;


//------------------------------------------------------------------------
// Includes Read ID test and optional Readonly Data and Address bus tests. 
// The optional test only be used if the flash has been sufficiently 
// programmed (by setting FlASH_PROGRAMMED to TRUE).
// Much quicker alternative to Destructive test.
//------------------------------------------------------------------------
TestNonDestructive()(INT result)

  INT tempResult;
  
  result := RESULT_PASS;
  
  PRINT("\n\nStarting Read ID test...\n");
  ReadIDtest()(result);
  
  IF (FLASH_PROG_LEVEL > FLASH_PROG_LEVEL_NONE) THEN
    PRINT("\nStarting Non-Destructive memory test\n");
    MemTestNonDestructive()(tempResult);
    IF tempResult = RESULT_FAIL THEN 
      result := RESULT_FAIL;
    END;
  ELSE
    PRINT("FLASH_PROG_LEVEL  currently set to FLASH_PROG_LEVEL_NONE\nAlthough the ReadID test excersises the data/addr bus and control lines,\na more comprehensive test can be done by changing FLASH_PROG_LEVEL\nThis can only be done if the flash is progammed (see device file for more information).\n\n");
  END;
  
  IF result = RESULT_FAIL THEN 
    PRINT("Non Destructive Memory Test FAILED...\n\n");
  ELSE
    PRINT("Non Destructive Memory Test PASSED...\n\n");
  END;
  
END;


//------------------------------------------------------------------------
// Includes Read ID test, and optimised destructive  memory test. 
// Slower than non destructive test, but more reliable.
//------------------------------------------------------------------------
TestDestructive()(INT result)

  INT tempResult;
  
  result := RESULT_PASS;
  
  PRINT("\n\nStarting Read ID test...\n");
  ReadIDtest()(result);
  
  PRINT("\nStarting Destructive memory test\n");
  IF (result = RESULT_PASS) THEN
    MemTestDestructive()(tempResult);
    IF tempResult = RESULT_FAIL THEN 
      result := RESULT_FAIL;
    END;
  ELSE
    TestDetail(TRUE)();
  END;
  
  IF ((result = RESULT_PASS) && WRITEABLE(nCE)) THEN
    PRINT("\nTesting Chip Enable\n");
    TestChipEnable()(result);
    
    IF result = RESULT_FAIL THEN
      PRINT("Chip Enable - pin: ",PINNUM(nCE)," stuck low\n");
    END;
  END;
  
  IF result = RESULT_FAIL THEN 
    PRINT("Destructive Memory Test FAILED...\n");
  ELSE
    PRINT("Destructive Memory Test PASSED...\n");
  END;
  
END;


//------------------------------------------------------
// ReadIDTest
//------------------------------------------------------

ReadIDtest()(INT result)

  INT manufacturer;
  INT device;
  INT status;
  INT i;
  INT data;

  PRINT ("Checking Flash ", DEVICE_REF, " ID codes... ");

  Init()();
  result := RESULT_FAIL;

  // Read Flash IDs.
  ReadID(MANUFACTURER_ADDR)(manufacturer);
  ReadID(DEVICE_ADDR)(device);
  ReadStatus()(status);


  IF DEBUG THEN
    PRINT("Manufacturer ID = 0x", HEX(manufacturer), ", ");
    PRINT("Device ID = 0x", HEX(device), ", ");
    PRINT("Status = 0x", HEX(status), ", ");
  END;

  // Check the IDs.
  IF DEBUG THEN
    PRINT("Check Flash ID...\n");
  END;

  IF ((manufacturer != MANUFACTURER_ID) || (device != DEVICE_ID)) THEN
    PRINT("Read Manufacturer ID : 0x", HEX(manufacturer), ", expected to read 0x",HEX(MANUFACTURER_ID),"\n");
    PRINT("Read Device ID = 0x", HEX(device), ", expected to read 0x", HEX(DEVICE_ID),"\n"); 
    PRINT("failed.\nStatus=0x",HEX(status), "\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  PRINT("IDs verified\n");
  result := RESULT_PASS;

END;



//-----------------------------------------------------------------------------
Erase(INT startAddress, INT endAddress)(INT result)
//-----------------------------------------------------------------------------

  INT i;
  INT address;

  result := RESULT_PASS;
  
  Init()();

  IF (DEBUG) THEN
    PRINT("Erasing blocks from 0x", HEX(startAddress), " to 0x", HEX(endAddress), "\n");
  END;

  address := startAddress;

  WriteCycle(address, CLEAR_LOCK); // Clear block locks.
  WriteCycle(address, CONFIRM);    // Confirm.

  DO WHILE (address < endAddress)
    EraseBlock(address)(result);
    IF result = RESULT_FAIL THEN
      PRINT("Erase flash failed\n");
      RETURN;
    END;
    address := address + BLOCK_LENGTH;
  END;

  PRINT("Erased.\n");

  Init()();

END;

//------------------------------------------------------
// EraseBlock
//------------------------------------------------------

EraseBlock(INT address)(INT result)

  IF (DEBUG) THEN
    PRINT ("Erase Block at 0x", HEX(address), "\n");
  ELSE
    PRINT(".");
  END;
  WriteCycle(address, CLEAR_LOCK); // Clear block locks.
  WriteCycle(address, CONFIRM);    // Confirm.
  WaitReady(5000)(result);
  WriteCycle(address, BLOCK_ERASE);  // Erase block.
  WriteCycle(address, CONFIRM);      // Confirm.
  WaitReady(5000)(result);

END;


//------------------------------------------------------
// Checks chip enable isnt stuck low
//------------------------------------------------------
TestChipEnable()(INT result)

  INT data;
  INT temp;

  result := RESULT_PASS;

  EraseBlock(0)(temp);
  
  WriteCycle(0, WORD_PROGRAM);

  // try programming with nCEO held high
  SET ADDR := 0, nWE := 0, nCE := 1, nOE := 1, DATA := 0;
  SET nCE := 1, nOE := 1, DATA := I, nWE := 1;
  WaitReady(100)(temp);
  
  ReadWord(0)(data);
  
  IF data = 0 THEN
    result := RESULT_FAIL;
  END;

END;
  


//-----------------------------------------------------------------------------
ProgramWord(INT address, INT data)(INT result)
//-----------------------------------------------------------------------------

  IF (DEBUG) THEN
    PRINT ("Program 0x", HEX(data), " to 0x", HEX(address), "\n");
  END;
  WriteCycle(address, WORD_PROGRAM);
  WriteCycle(address, data);
  WaitReady(100)(result);

END;



//------------------------------------------------------
// ReadWord
//   Read a word from the flash
//------------------------------------------------------

ReadWord(INT address)(INT data)

  ResetFlash()();
  ReadCycle(address)(data);
  IF DEBUG THEN PRINT ("Read addr 0x", HEX(address), " = 0x", HEX(data), "\n"); END;

END;


//-----------------------------------------------------
// ResetFlash
//-----------------------------------------------------

ResetFlash()()
  WriteCycle(0, READ_ARRAY); // Reset and switch to read array mode.
END;

//------------------------------------------------------
// Init
//------------------------------------------------------

Init()()

  INT temp;

  IF DEBUG THEN PRINT("Initialising flash...\n")(); END;

  SET ADV := 0, WP := 1;

  ClearStatus();
  WaitReady(100)(temp);

  WriteCycle(0, READ_ARRAY); // Read array command.
  WriteCycle(0, READ_ARRAY); // Read array command.

  IF DEBUG THEN PRINT("Initialised\n"); END;

END;

//-----------------------------------------------------------------------------
ReadID(INT address)(INT value)
//-----------------------------------------------------------------------------

  WriteCycle(0x0, READ_ID);
  ReadCycle(address)(value);

END;

//------------------------------------------------------
WaitReady (INT delay)(INT result)
//------------------------------------------------------

  INT status;
  INT timeout := NOW() + delay;
  
  result := RESULT_PASS;
  
  DO
    ReadStatus()(status);
  WHILE (status[7] = 0)
    IF NOW() > timeout THEN
      result := RESULT_FAIL;
      PRINT("\n*** Timeout waiting for flash ready ***\n");
      RETURN;
    END;
  END;

END;

//-----------------------------------------------------------------------------
ReadStatus()(INT status)
//-----------------------------------------------------------------------------
INT i;
  WriteCycle(0x0, READ_STATUS); // Read status command.
  ReadCycle(0x0)(status);
  
  IF DEBUG THEN
    IF !status[7] THEN

    ELSE
      // Full status check.
      IF status[6] THEN
        PRINT("Block erase suspended.\n");
      END;
      IF status[5] THEN
        PRINT("Block erase/Clear locks error.\n");
      END;
      IF status[4] THEN
        PRINT("Program/Set lock bit error.\n");
      END;
      IF status[3] THEN
        PRINT("Program Voltage Range Error.\n");
      END;
      IF status[2] THEN
        PRINT("Program suspended.\n");
      END;
      IF status[1] THEN
        PRINT("Block lock detected.\n");
      END;
      IF (status[6..1] != 0) THEN
        ClearStatus();
      END;
    END;
  END;
END;

//-----------------------------------------------------------------------------
ClearStatus()()
//-----------------------------------------------------------------------------

  WriteCycle(0x0, CLEAR_STATUS)();

END;


//-----------------------------------------------------------------------------
WriteCycle(INT address WIDTH ADDR_BUS_WIDTH, INT code WIDTH DATA_BUS_WIDTH)()
//-----------------------------------------------------------------------------

  SET ADDR := address[(ADDR_BUS_WIDTH-1)..0], nWE := 0, nCE := 0, nOE := 1, DATA := code;
  SET nCE := 1, nOE := 1, DATA := I, nWE := 1;

END;

//------------------------------------------------------

ReadCycle(INT address WIDTH ADDR_BUS_WIDTH)(INT result WIDTH DATA_BUS_WIDTH)
//------------------------------------------------------

  SET ADDR := address[(ADDR_BUS_WIDTH-1)..0], nWE := 1, nCE := 0, nOE := 0, DATA := I;
  SET nCE := 1, nOE := 1, nWE := 1, result := DATA;

END;


//----------------------------------------------------------------------------------
// Functions required for use of device file with memtestFlash.xje
//----------------------------------------------------------------------------------

PrintData(INT i)()
  PRINT(PINNUM(DATA[i]));
END;

PrintAddr(INT i)()
  PRINT(PINNUM(ADDR[i]));
END;




/*********************FLASH PROGRAMMING***********************************

//------------------------------------------------------
// ProgramFromSRecordFile
//    Program the flash from an S Record file
//------------------------------------------------------

ProgramFromSRecordFile(STRING fileName)(INT result)

  INT addrLow;
  INT addrHigh;
  INT address;
  
  PRINT("\n\nStarting flash programming from file ",fileName,"\n");

  dataSet := ALL_ONES;
  ReadSRecordFile(fileName, 0, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  ProgramFlash(addrLow, addrHigh - addrLow, dataSet)(result);
 
  // optional verification of memory written to flash
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);
  
END;


//------------------------------------------------------
// ProgramFromHexFile
//    Program the flash from a HEX file
//------------------------------------------------------

ProgramFromHexFile(STRING fileName)(INT result)

  INT addrLow;
  INT addrHigh;
  INT address;
  
  dataSet := ALL_ONES;
  ReadHEXFile(fileName, 0, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  ProgramFlash(addrLow, addrHigh - addrLow, dataSet)(result);
  
  // optional verification of memory written to flash
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);
  
END;


//------------------------------------------------------
// ProgramFromBinFile
//    Program the flash from a BIN file
//------------------------------------------------------

ProgramFromBINFile(STRING fileName)(INT result)

  INT addrLow;
  INT addrHigh;
  INT address;
  
  dataSet := ALL_ONES;
  ReadBINFile(fileName, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  ProgramFlash(addrLow, addrHigh - addrLow, dataSet)(result);
  
  // optional verification of memory written to flash
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);
  
END;


//-----------------------------------------------------------------------------
// Program Flash using "dataSet" variable. Optimised Programming  
//-----------------------------------------------------------------------------
ProgramFlash (INT addrLow, INT numAddresses, INT dataSet)(INT result)
//-----------------------------------------------------------------------------

  INT address    := addrLow;
  INT endAddress := addrLow + numAddresses;
  INT byteNum;
  INT value;
  INT count := 0;
  INT tempCount :=0;

  result := RESULT_PASS;

  // Initialise the flash.
  Init();

  IF DEBUG THEN
    PRINT("Programming from address 0x", HEX(addrLow), " for length 0x", HEX(numAddresses), "\n");
  END;

  DO WHILE (address < endAddress)
    // Notify flash that we are going to write to buffer.
    WriteCycle(address, WRITE_BUFFER);  // Write to buffer command

    value := (BUFFER_LENGTH-1)[DATA_BUS_WIDTH-1..0];

    WriteCycle(address,  value);        // Length of buffer
    FOR byteNum := 0 FOR BUFFER_LENGTH
      WriteCycle(address, dataSet[(address+1)*DATA_BUS_WIDTH -1..address*DATA_BUS_WIDTH]);        // Write data
      address := address + 1;
    END;
    WriteCycle(address, CONFIRM);       // Confirm write buffer.
    WaitReady(500)(result);
    IF (result = RESULT_FAIL) THEN
      PRINT("Write to buffer failed\n");
      RETURN;
    END;
    
    tempCount := ((address - addrLow)*100)/numAddresses;
    IF (tempCount != count && tempCount%5 = 0) THEN
      PRINT(tempCount, " percent programming complete\r");
      count := tempCount;
    END;

  END;

  Init();
  PRINT("Programming complete.                                \n");
END;  
  


//-----------------------------------------------------------------
// VerifyFlash with a given dataSet
//-----------------------------------------------------------------
VerifyFlash(INT dataSet, INT addrLow, INT numAddresses)(INT result)
//-----------------------------------------------------------------

  INT data WIDTH DATA_BUS_WIDTH;
  INT expectedData WIDTH DATA_BUS_WIDTH;
  INT address;
  INT count := 0;
  INT tempCount :=0;
  
  result := RESULT_FAIL;
  
  PRINT("Attempting to verify programmed data\n");
  
  ResetFlash()();
  
  FOR address := addrLow FOR numAddresses
    ReadCycle(address)(data);

    expectedData := dataSet[(address+1)*DATA_BUS_WIDTH-1..address*DATA_BUS_WIDTH];
    IF (expectedData != data) THEN
      result := RESULT_FAIL;
      PRINT("\n*** Verify error at address 0x", HEX(address), ", data=0x", HEX(data), ", expected data=0x", HEX(expectedData), " ***\n");
      RETURN;
    END;
    
    tempCount := ((address - addrLow)*100)/numAddresses;
    IF (tempCount != count && tempCount%5 = 0) THEN
      PRINT(tempCount, " percent verifying complete\r");
      count := tempCount;
    END;
    
  END;
  
  PRINT("Verified OK.                                    \n");
  result := RESULT_PASS;
  
END;

//------------------------------------------------------
// Verify flash from a BIN file
//------------------------------------------------------
VerifyFlashFromBINFile(STRING fileName)(INT result)
//------------------------------------------------------

  INT addrLow;
  INT addrHigh;
  
  result := RESULT_FAIL;

  dataSet := ALL_ONES;
  ReadBINFile(fileName, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  IF (result = RESULT_FAIL) THEN
    PRINT("BIN file failed to be read\n");
  END;
  
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);

END;    

//------------------------------------------------------
// Verify flash from a HEX file
//------------------------------------------------------
VerifyFlashFromHEXFile(STRING fileName)(INT result)
//------------------------------------------------------

  INT addrLow;
  INT addrHigh;
  
  result := RESULT_FAIL;

  dataSet := ALL_ONES;
  ReadHEXFile(fileName, 0, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  IF (result = RESULT_FAIL) THEN
    PRINT("HEX file failed to be read\n");
  END;
  
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);

END;   

//------------------------------------------------------
// Verify flash from an S Record file
//------------------------------------------------------
VerifyFlashFromSRecordFile(STRING fileName)(INT result)
//------------------------------------------------------

  INT addrLow;
  INT addrHigh;
  
  result := RESULT_FAIL;

  dataSet := ALL_ONES;
  ReadSRecordFile(fileName, 0, 0, 16, DATA_BUS_WIDTH)(dataSet, addrLow, addrHigh, result);
  IF (result = RESULT_FAIL) THEN
    PRINT("S-Record file failed to be read\n");
  END;
  
  VerifyFlash(dataSet, addrLow, addrHigh - addrLow)(result);

END; 


//------------------------------------------------------
// ReadFlash
//    Read the flash and write contents in binary
//    format to an open file
//------------------------------------------------------

ReadFlash(FILE fp, INT address, INT length)(INT result)

  INT endAddress := address + length;
  INT data;

  IF DEBUG THEN PRINT("Reading Flash\n"); END;

  ResetFlash()();
  result := RESULT_FAIL;

  DO WHILE address <= endAddress
    ReadCycle(address)(data);
    FWRITE(fp, data[0..(DATA_BUS_WIDTH-1)])();
    address := address + 1;
  END;

  result := RESULT_PASS;
  IF DEBUG THEN PRINT("Read completed OK\n"); END;

END;

//-----------------------------------------------------------------------------
QueryFlash()()
//-----------------------------------------------------------------------------

  INT data;
  INT i;
  INT byteAddress;
  INT wordAddress;

  WriteCycle(0, READ_QUERY); // Switch to read query command.
  PRINT("Query data :\n");
  byteAddress := 0;
  wordAddress := 0;
  FOR i := 0 TO 0x2f
    ReadCycle(byteAddress)(data);
    PRINT("0x", HEX(byteAddress), "(0x", HEX(wordAddress), ")=0x", HEX(data), "\n");
    byteAddress := byteAddress + 2; // 16-bit-wide flash devices
    wordAddress := wordAddress + 1;
  END;

  PRINT("\n");

  WriteCycle(0, READ_ARRAY); // Switch to read array command.
  PRINT("Array data :\n");
  byteAddress := 0;
  wordAddress := 0;
  FOR i := 0 TO 0x2f
    ReadCycle(byteAddress)(data);
    PRINT("0x", HEX(byteAddress), "(0x", HEX(wordAddress), ")=0x", HEX(data), "\n");
    byteAddress := byteAddress + 2; // 16-bit wide flash devices
    wordAddress := wordAddress + 1;
  END;

  PRINT("\n");

  Init()();
END;

*************************************************************************/