//---------------------------------------------------------
// Spansion S29GLxxxM MirrorBit Flash: XJEase device file
// S29GLxxxM.xje Revision: 1.21
// (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)   
//---------------------------------------------------------
// This file covers the whole S29GLxxxM family of Flash
// devices. Select all the appropriate package options
// for your device before running the code.
//---------------------------------------------------------

DEVICE NAME := "S29GLxxxM"

  PINS
/*
    // 40-pin TSOP
    ADDR    := 29, 38, 37, 13, 40, 1, 2, 3, 4, 5, 6, 36, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21;
    DATA    := 35, 34, 33, 32, 28, 27, 26, 25;
    NWE     := 9;
    NCE     := 22;
    NOE     := 24;
    RDY     := 12;
    NRESET := 10;


    // 48-pin TSOP
    // word mode
    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;
    // byte mode : remember to set NYBTE low and change to byte mode commands.
    //ADDR   := 13, 10, 9, 16, 17, 48, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23, 24, 25, 45;
    //DATA   := 44, 42, 40, 38, 35, 33, 31, 29;
    NWE    := 11;
    NCE    := 26;
    NOE    := 28;
    RDY    := 15;
    NRESET := 12;
    BYTE  := 47;

    // 48-pin TSOP (model R0)
    ADDR   := 2, 35, 44, 43, 15, 46, 3, 4, 5, 6, 7, 8, 42, 9, 10, 16, 17, 18, 19, 20, 21, 22, 27;
    DATA   := 41, 40, 39, 38, 34, 33, 32, 31;
    NWE    := 11;
    NCE    := 28;
    NOE    := 30;
    RDY    := 14;
    NRESET := 12;

    // 56-pin TSOP
    // word mode
    ADDR   := 1, 2, 15, 12, 11, 18, 19, 54, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22, 23, 24, 25, 26, 31;
    DATA   := 51, 49, 47, 45, 42, 40, 38, 36, 50, 48, 46, 44, 41, 39, 37, 35;
    // byte mode : remember to set NYBTE low and change to byte mode commands.
    //ADDR   := 1, 2, 15, 12, 11, 18, 19, 54, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22, 23, 24, 25, 26, 31, 51;
    //DATA   := 50, 48, 46, 44, 41, 39, 37, 35;
    NWE    := 13;
    NCE    := 32;
    NOE    := 34;
    RDY    := 17;
    NRESET := 14;
    BYTE  := 53;
*/
    // 64-ball fortified BGA
    // word mode
    ADDR   := C8, B8, C5, D4, D5, C4, B3, E7, D7, C7, A7, B7, D6, C6, A6, B6, A3, C3, D3, B2, A2, C2, D2, E2;
    DATA   := G7, F6, G6, F5, G4, F4, G3, F3, E6, H6, E5, H5, H4, E4, H3, E3;
    // byte mode : remember to set NYBTE low and change to byte mode commands.
    //ADDR   := C8, B8, C5, D4, D5, C4, B3, E7, D7, C7, A7, B7, D6, C6, A6, B6, A3, C3, D3, B2, A2, C2, D2, E2, G7;
    //DATA   := E6, H6, E5, H5, H4, E4, H3, E3;
    NWE    := A5;
    NCE    := F2;
    NOE    := G2;
    RDY    := A4;
    NRESET := B5;
    BYTE  := F7;
/*
    // 63-ball fine-pitch BGA
    // word mode
    ADDR   := E5, F4, F5, E4, D3, G7, F7, E7, C7, D7, F6, E6, C6, D6, C3, E3, F3, D2, C2, E2, F2, G2;
    DATA   := J7, H6, J6, H5, J4, H4, J3, H3, G6, K6, G5, K5, K4, G4, K3, G3;
    // byte mode : remember to set NYBTE low and change to byte mode commands.
    //ADDR   := E5, F4, F5, E4, D3, G7, F7, E7, C7, D7, F6, E6, C6, D6, C3, E3, F3, D2, C2, E2, F2, G2, J7;
    //DATA   := G6, K6, G5, K5, K4, G4, K3, G3;
    NWE    := C5;
    NCE    := H2;
    NOE    := J2;
    RDY    := C4;
    NRESET := D5;
    BYTE  := H7;
    
    // 48 ball fine-pitch BGA
    // word mode
    ADDR   := 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;
    // byte mode : remember to set NYBTE low and change to byte mode commands.
    //ADDR   := B2, E6, D6, C6, A6, B6, D5, C5, A5, B5, A2, C2, D2, B1, A1, C1, D1, E1, G6;
    //DATA   := E5, H5, E4, H4, H3, E3, H2, E2;
    NWE    := A4;
    NCE    := F1;
    NOE    := G1;
    RDY    := A3;
    NRESET := B4;
    BYTE  := F6;
    
    // 48 ball fine-pitch BGA (model R0)
    ADDR   := E6, D6, C6, A6, B6, D5, C5, F5, A5, B5, A2, C2, D2, B1, A1, C1, D1, E1;
    DATA   := H5, G5, E4, H4, F3, E3, H2, E2;
    NWE    := A4;
    NCE    := F1;
    NOE    := G1;
    RDY    := A3;
    NRESET := B4;
*/
END;

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

  TEST COVERAGE
    ADDR := OPEN SHORTS LO HI;
    DATA := OPEN SHORTS LO HI;
    NWE := OPEN LO HI;
    NCE := OPEN LO HI;
    NOE := OPEN LO HI;
  END;

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

/*
// Commands: x8
INT ADDR_UNLOCK_1    := 0xAAA;
INT ADDR_UNLOCK_2    := 0x555;
INT ADDR_AUTOSELECT  := 0xAAA;
INT ADDR_PROGRAM     := 0xAAA;
INT ADDR_ERASE_SETUP := 0xAAA;
INT ADDR_ERASE       := 0xAAA;
INT ADDR_ABORT_PROG  := 0xAAA;
INT ADDR_DEV_ID0 := 0x02;
INT ADDR_DEV_ID1 := 0x1C;
INT ADDR_DEV_ID2 := 0x1E;
*/
// Commands: x16
INT ADDR_UNLOCK_1    := 0x555;
INT ADDR_UNLOCK_2    := 0x2AA;
INT ADDR_AUTOSELECT  := 0x555;
INT ADDR_PROGRAM     := 0x555;
INT ADDR_ERASE_SETUP := 0x555;
INT ADDR_ERASE       := 0x555;
INT ADDR_ABORT_PROG  := 0x555;
INT ADDR_DEV_ID0 := 0x01;
INT ADDR_DEV_ID1 := 0x0E;
INT ADDR_DEV_ID2 := 0x0F;


// Commands: x8 and x16
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_SEC   := 0x30;
INT ADDR_MAN_ID      := 0x00;
INT DATA_WRITETOBUF  := 0x25;
INT DATA_PROGBUF     := 0x29;
INT ABORT_PROGBUF    := 0xF0;

//INT MANUFACTURER_ID  := 0x0001; // Spansion
INT MANUFACTURER_ID  := 0x0020;  //  ST

// M29W128FX IDs: x8
//INT DEVICE_ID        := 0x7E128A;  // M29W128FH
//INT DEVICE_ID        := 0x7E128B;  // M29W128FL

// M29W128FX IDs: x16
//INT DEVICE_ID        := 0x227E2212228A;  // M29W128FH
INT DEVICE_ID        := 0x227E2212228B;  // M29W128FL

/*
// S29GLxxxM IDs: x8
INT DEVICE_ID        := 0x7E1C00;  // S26GL032M  R0
//INT DEVICE_ID        := 0x7E1D00;  // S26GL032M  R1, R2
//INT DEVICE_ID        := 0x7E1A00;  // S26GL032M  R4
//INT DEVICE_ID        := 0x7E1A01;  // S26GL032M  R3
//INT DEVICE_ID        := 0x7E1C00;  // S26GL032M  R6
//INT DEVICE_ID        := 0x7E1C01;  // S26GL032M  R5
//INT DEVICE_ID        := 0x7E1300;  // S26GL064M  R0
//INT DEVICE_ID        := 0x7E0C01;  // S26GL064M  R1, R2, R8, R9
//INT DEVICE_ID        := 0x7E1000;  // S26GL064M  R4
//INT DEVICE_ID        := 0x7E1001;  // S26GL064M  R3
//INT DEVICE_ID        := 0x7E1301;  // S26GL064M  R5, R6, R7
//INT DEVICE_ID        := 0x7E1200;  // S26GL128M
//INT DEVICE_ID        := 0x7E1201;  // S26GL256M
*/

// S29GLxxxM IDs: x16
//INT DEVICE_ID        := 0x227E221C2200;  // S26GL032M  R0
//INT DEVICE_ID        := 0x227E221D2200;  // S26GL032M  R1, R2
//INT DEVICE_ID        := 0x227E221A2200;  // S26GL032M  R4
//INT DEVICE_ID        := 0x227E221A2201;  // S26GL032M  R3
//INT DEVICE_ID        := 0x227E221C2200;  // S26GL032M  R6
//INT DEVICE_ID        := 0x227E221C2201;  // S26GL032M  R5
//INT DEVICE_ID        := 0x227E22132200;  // S26GL064M  R0
//INT DEVICE_ID        := 0x227E220C2201;  // S26GL064M  R1, R2, R8, R9
//INT DEVICE_ID        := 0x227E22102200;  // S26GL064M  R4
//INT DEVICE_ID        := 0x227E22102201;  // S26GL064M  R3
//INT DEVICE_ID        := 0x227E22132201;  // S26GL064M  R5, R6, R7
//INT DEVICE_ID        := 0x227E22122200;  // S26GL128M
//INT DEVICE_ID        := 0x227E22122201;  // S26GL256M

CONST INT BUFFER_LENGTH := 0x10; // double if 8 bit wide data bus ?

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

CONST INT BLOCK_LENGTH := (0x10000/DATA_BUS_WIDTH)*8; // For boot sector devices, use larger block size

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

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 RESULT_PASS := FALSE;
CONST INT RESULT_FAIL := TRUE;

// 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(BYTE)) THEN
    SET BYTE := 1;
  END;
  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(BYTE)) THEN
    SET BYTE := 1;
  END;
  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;

  

//------------------------------------------------------
// 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(0, 0, 100)(temp);
  
  ReadWord(0)(data);
  
  IF data = 0 THEN
    result := RESULT_FAIL;
  END;

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(0x00, 0x80, 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(address, 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;

//------------------------------------------------------
// 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(address, 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, read;

  // 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_ID0)(read);
  device := read;
  ReadCycle(ADDR_DEV_ID1)(read);
  device := device:read;
  ReadCycle(ADDR_DEV_ID2)(read);
  device := device:read;

END;

//------------------------------------------------------
// WaitReady
//    waits until DQ[7] matches passed data[7] or times out
//    when DQ[5] goes high
//    also times out after user defined time incase a more
//    serious failure
//------------------------------------------------------

WaitReady (INT address, INT data, INT delay)(INT result)

  INT status;
  INT timeout := NOW() + delay;

  result := RESULT_FAIL;

  DO
    ReadCycle(address)(status);
    IF (status[7] = data[7]) THEN
      result := RESULT_PASS;
      RETURN;
    END;
  WHILE (status[5] != 1)
    IF delay != 0 THEN
      IF NOW() > timeout THEN
        PRINT("\n Timeout waiting for flash ready ***\n");
        RETURN;
      END;
    END;
  END;
  
  ReadCycle(address)(status);
  IF (status[7] != data[7]) THEN
    PRINT("\n*** Timeout waiting for flash ready ***\n");
    RETURN;
  END;
  
  result := RESULT_PASS;
  
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;



//-----------------------------------------------------------------------------
ProgramFlash (INT startAddress, INT length, INT dataSet)(INT result)
//-----------------------------------------------------------------------------

  INT address    := startAddress;
  INT endAddress := address + length;
  INT data;
  INT byteNum;
  INT value;
  INT count := 0;
  INT tempCount :=0;

  ResetFlash();

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

  DO WHILE (address < endAddress)

    // Two unlock cycles
    WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
    WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
    
    WriteCycle(address, DATA_WRITETOBUF);  // 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
      data := dataSet[(address+1)*DATA_BUS_WIDTH -1..address*DATA_BUS_WIDTH];
      WriteCycle(address, data);        // Write data
      address := address + 1;
    END;
    
    WriteCycle(address-1, DATA_PROGBUF);       // Program
    WaitReady(address-1, data, 500)(result);
    IF (result != RESULT_PASS) THEN
      PRINT("Flash not ready after program buffer.\n");
      AbortProgramBuffer()();
      RETURN;
    END;
    
    tempCount := ((address - startAddress)*100)/length;
    IF (tempCount != count && tempCount%5 = 0) THEN
      PRINT(tempCount, " percent programming complete\r");
      count := tempCount;
    END;
  END;

  ResetFlash();
  PRINT("Programming complete.                             \n");

END;

//-----------------------------------------------------------------
AbortProgramBuffer()()
//-----------------------------------------------------------------
  WriteCycle(ADDR_UNLOCK_1, DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  WriteCycle(ADDR_ABORT_PROG, ABORT_PROGBUF);
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;


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