//---------------------------------------------------------
// SST39VFxxxx Flash: XJEase device file
// SST39VFxxxx.xje Revision: 1.13
// (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 := "SST39VFxxxx"

  PINS
  /*
    // Standard 48-pin TSOP (SST39VF1601/1602)
    ADDR   := 9, 16, 17, 48, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23, 24, 25;
    DATA   := 45, 43, 41, 39, 36, 34, 32, 30, 44, 42, 40, 38, 35, 33, 31, 29;
    NCE    := 26;
    NOE    := 28;
    NWE    := 11;
    NRESET := 12;
    nWP    := 14;
 
    // Standard 48-pin TSOP (SST39VF3201/3202)
    ADDR   := 10, 9, 16, 17, 48, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23, 24, 25;
    DATA   := 45, 43, 41, 39, 36, 34, 32, 30, 44, 42, 40, 38, 35, 33, 31, 29;
    NCE    := 26;
    NOE    := 28;
    NWE    := 11;
    NRESET := 12;
    nWP    := 14;
 
    // Standard 48-pin TSOP (SST39VF6401/6402)
    ADDR   := 13, 10, 9, 16, 17, 48, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23, 24, 25;
    DATA   := 45, 43, 41, 39, 36, 34, 32, 30, 44, 42, 40, 38, 35, 33, 31, 29;
    NCE    := 26;
    NOE    := 28;
    NWE    := 11;
    NRESET := 12;
    nWP    := 14;
 
    // 64-ball fine-pitch BGA (SST39VF1601/1602)
    ADDR := D4, C3, B2, E6, D6, C6, A6, B6, D5, C5, A5, B5, A2, C2, D2, B1, A1, C1, D1, E1;
    DATA := G6, F5, G5, F4, G3, F3, G2, F2, E5, H5, E4, H4, H3, E3, H2, E2;
    NCE  := F1;
    NOE  := G1;
    NWE  := B3;
    NRESET := B4;
    nWP  := B3;
 */
    // 64-ball fine-pitch BGA (SST39VF3201/3202)
    ADDR := D3, D4, C3, B2, E6, D6, C6, A6, B6, D5, C5, A5, B5, A2, C2, D2, B1, A1, C1, D1, E1;
    DATA := G6, F5, G5, F4, G3, F3, G2, F2, E5, H5, E4, H4, H3, E3, H2, E2;
    NCE  := F1;
    NOE  := G1;
    NWE  := B3;
    NRESET := B4;
    nWP  := B3;
 /*
    // 64-ball fine-pitch BGA (SST39VF6401/6402)
    ADDR := C4, D3, D4, C3, B2, E6, D6, C6, A6, B6, D5, C5, A5, B5, A2, C2, D2, B1, A1, C1, D1, E1;
    DATA := G6, F5, G5, F4, G3, F3, G2, F2, E5, H5, E4, H4, H3, E3, H2, E2;
    NCE  := F1;
    NOE  := G1;
    NWE  := B3;
    NRESET := B4;
    nWP  := B3;
  */
  END;

  DISABLE DEVICE
    NCE := 1;
    NOE := 1;
  END;

  TEST COVERAGE
    ADDR   := SHORTS OPEN HI LO;
    DATA   := SHORTS OPEN HI LO;
    NCE    := OPEN HI LO;
    NOE    := OPEN HI LO;
    NWE    := OPEN HI LO;
  END;
  
  FILES
    "memtestFlash.xje";
    //"DataReader.xje";
  END;
END;

// Commands: address
INT ADDR_UNLOCK_1    := 0x5555;
INT ADDR_UNLOCK_2    := 0x2AAA;
INT ADDR_AUTOSELECT  := 0x5555;
INT ADDR_PROGRAM     := 0x5555;
INT ADDR_ERASE_SETUP := 0x5555;
INT ADDR_ERASE       := 0x5555;
INT ADDR_MAN_ID      := 0x0000;
INT ADDR_DEV_ID      := 0x0001;

INT DATA_UNLOCK_1    := 0xAA;
INT DATA_UNLOCK_2    := 0x55;
INT DATA_AUTOSELECT  := 0x90;
INT DATA_PROGRAM     := 0xA0;
INT DATA_ERASE_SETUP := 0x80;
INT DATA_ERASE_ALL   := 0x10;
INT DATA_ERASE_BLOCK := 0x50;
INT DATA_ERASE_SEC   := 0x30;


// SST39VFxxxx IDs
INT MANUFACTURER_ID  := 0xBF;
//INT DEVICE_ID        := 0x234B;    // SST39VF1601
//INT DEVICE_ID        := 0x234A;    // SST39VF1602
//INT DEVICE_ID        := 0x235B;    // SST39VF3201
INT DEVICE_ID        := 0x235A;      // SST39VF3202
//INT DEVICE_ID        := 0x236B;    // SST39VF6401
//INT DEVICE_ID        := 0x236A;    // SST39VF6402

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 BLOCK_LENGTH := (0x10000/DATA_BUS_WIDTH)*8; // For boot sector devices, use larger block size

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;


// Globals
INT DEBUG := FALSE;
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;
  
  IF (WRITEABLE(NRESET)) THEN
    SET NRESET := 0;
    SET NRESET := 1;
  END;
  
  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;
  
  IF (WRITEABLE(NRESET)) THEN
    SET NRESET := 0;
    SET NRESET := 1;
  END;
  
  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;

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

  ResetFlash()();
  result := RESULT_FAIL;

  // Read Flash IDs.
  ReadID()(manufacturer, device);
  ResetFlash()();

  manufacturer := manufacturer[7..0];

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

  // Check the IDs.
  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.\n");
    result := RESULT_FAIL;
    RETURN;
  END;

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

END;

//------------------------------------------------------
// EraseAll
//------------------------------------------------------

EraseAll()(INT result)

  IF DEBUG THEN PRINT("Erasing all flash contents.\n"); END;

  ResetFlash()();

  // Two unlock cycles
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  // Set-up command
  WriteCycle(ADDR_ERASE_SETUP, DATA_ERASE_SETUP);
  // Two unlock write cycles
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  // Chip erase command
  WriteCycle(ADDR_ERASE, DATA_ERASE_ALL);

  // Poll for erase complete
  WaitReady(0xffff, 0xffffffff)(result);
  IF (result != RESULT_PASS) THEN
    PRINT("Flash not ready after EraseAll\n");
    RETURN;
  END;

  ResetFlash()();

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

END;


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

EraseBlock(INT address)(INT result)

  result := RESULT_FAIL;
  IF DEBUG THEN PRINT ("Erase flash block at 0x", HEX(address), "\n"); END;

  // Two unlock cycles
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  // Set-up command
  WriteCycle(ADDR_ERASE_SETUP, DATA_ERASE_SETUP);
  // Two unlock write cycles
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  // Chip erase command
  WriteCycle(address, DATA_ERASE_SEC);

  WaitReady(0xffff, 5000)(result); // data is effectively FF, since erasing sets to 1s
  IF (result != RESULT_PASS) THEN
    PRINT("Flash not ready after erase.\n");
    RETURN;
  END;

  result := RESULT_PASS;
  IF DEBUG THEN
    PRINT("Block erased.\n");
  END;

END;

//------------------------------------------------------
// Checks the chip enable pin isn't stuck low
//------------------------------------------------------
TestChipEnable()(INT result)

  INT data;
  INT temp;

  result := RESULT_PASS;

  EraseBlock(0)(temp);
  
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  WriteCycle(ADDR_PROGRAM, DATA_PROGRAM);
  
  // try programming with nCE held high
  SET ADDR := 0;
  SET NWE := 0, NCE := 1, NOE := 1, DATA := 0;
  SET NCE := 1, NOE := 1, DATA := I, NWE := 1;
  WaitReady(0x80, 100)(temp);
  
  ReadWord(0)(data);
  
  IF data = 0 THEN
    result := RESULT_FAIL;
  END;

END;


//------------------------------------------------------
// ProgramWord
//   Write a word to the flash
//------------------------------------------------------

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

  INT read_data;

  IF DEBUG THEN PRINT ("Program 0x", HEX(data), " to 0x", HEX(address), "\n"); END;

  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  WriteCycle(ADDR_PROGRAM, DATA_PROGRAM);
  WriteCycle(address, data);

  WaitReady(data, 100)(result);
  IF (result != RESULT_PASS) THEN
    PRINT("Flash not ready after program byte.\n");
    RETURN;
  END;

  result := RESULT_PASS;
  IF DEBUG THEN PRINT("Word programmed OK.\n"); END;

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, 0xF0); // Reset and switch to read array mode.
END;


//------------------------------------------------------
// ReadID
//   Read the manufacturer and device IDs.
//------------------------------------------------------

ReadID()(INT manufacturer, INT device)

  INT timeout;

  // Go into autoselect mode.
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  WriteCycle(ADDR_AUTOSELECT, DATA_AUTOSELECT);

  // Read the manufacturer ID.
  ReadCycle(ADDR_MAN_ID)(manufacturer);
  // Read the device ID.
  ReadCycle(ADDR_DEV_ID)(device);

END;


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

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

END;



//------------------------------------------------------
// WriteCycle
// Address latched on falling edge of nWE or nCE
// Data latched on rising edge of nWE
//------------------------------------------------------

WriteCycle(INT address, INT code)()

  SET ADDR := address[ADDR_BUS_WIDTH-1..0];
  SET NWE := 0, NCE := 0, NOE := 1, DATA := code[DATA_BUS_WIDTH-1..0];
  SET NCE := 1, NOE := 1, DATA := I, NWE := 1;

END;

//------------------------------------------------------
// ReadCycle
// Address latched on falling edge of NCE
// Data latched on rising edge of nCE
//------------------------------------------------------

ReadCycle(INT address)(INT result)

  SET ADDR := address[ADDR_BUS_WIDTH-1..0], DATA := I;
  SET NWE := 1, NCE := 0, NOE := 0;
  SET result := DATA, NCE := 1, NOE := 1, NWE := 1;

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.
//------------------------------------------------------
ProgramFlash(INT addrLow, INT numAddresses, INT dataSet)(INT result)
//------------------------------------------------------

  INT address;
  INT count := 0;
  INT tempCount :=0;
  
  result := RESULT_FAIL;
  address := addrLow;
  
  FOR address := addrLow FOR numAddresses
    ProgramWord(address, dataSet[(address+1)*DATA_BUS_WIDTH -1..address*DATA_BUS_WIDTH])(result);
    IF (result != RESULT_PASS) THEN
      PRINT("ProgramWord failed in ProgramFile\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;
  
  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;

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