//------------------------------------------------------
// AT49BVxxxAx: XJEase device file
// (c)2001-2008 XJTAG Limited Revision: 1.13
//
// Disclaimer: XJTAG makes no guarantees whatsoever
// about this code.  You use it at your own risk ...
// This file requires XJTAG version 1.4 or later.
//------------------------------------------------------

DEVICE NAME := "AT49BVxxxAx"
  PINS
    /*
    //Byte Mode
    ADDR := 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;
    */
    //Word mode
    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;
    
    nOE    := 28;
    nWE    := 11;
    NRESET := 12;
    nCE    := 26;  
    RDY    := 15;
    BYTE   := 47;
  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: Word
INT ADDR_UNLOCK_1    := 0x555;
INT ADDR_UNLOCK_2    := 0xAAA;
INT ADDR_AUTOSELECT  := 0x555;
INT ADDR_PROGRAM     := 0x555;
INT ADDR_ERASE_SETUP := 0x555;
INT ADDR_ERASE       := 0x555;
INT ADDR_DEV_ID      := 0x01;

/*
// Commands: Byte
INT ADDR_UNLOCK_1    := 0xAAA;
INT ADDR_UNLOCK_2    := 0x1554;
INT ADDR_AUTOSELECT  := 0xAAA;
INT ADDR_PROGRAM     := 0xAAA;
INT ADDR_ERASE_SETUP := 0xAAA;
INT ADDR_ERASE       := 0xAAA;
INT ADDR_DEV_ID      := 0x02;
*/

// Commands: 
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 ADDR_SEC_PROTECT := 0x002;

// IDs : 
INT MANUFACTURER_ID  := 0x1F;   
INT DEVICE_ID        := 0xC0;   // AT49BV16xA
//INT DEVICE_ID        := 0xC2;   // AT49BV16xAT
//INT DEVICE_ID        := 0xC8    // AT49BV32xA
//INT DEVICE_ID        := 0xC9    // AT49BV32xAT


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 DEBUG := FALSE;

CONST INT BLOCK_LENGTH := (0x10000/DATA_BUS_WIDTH)*8;

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 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;
  
  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... ");
  
  Init()();
  result := RESULT_FAIL;

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

  ResetFlash()();

  manufacturer := manufacturer[7..0];

  IF DEBUG THEN
    PRINT("Manufacturer ID = 0x", HEX(manufacturer), "\n");
    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("\nFlash ", DEVICE_REF, " ID codes Failed.\n\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  PRINT("\nFlash ", DEVICE_REF, " ID codes Passed.\n\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;

  Init()();

  // 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, 0xffff)(result);
  IF (result != RESULT_PASS) THEN
    PRINT("Flash not ready after EraseAll\n");
    RETURN;
  END;

  Init()();

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

END;


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

EraseBlock(INT address)(INT result)
  INT bIsLocked;

  IsLocked(address)(bIsLocked);
  IF bIsLocked THEN
    PRINT("Erase failed: Block is protected");
    result := RESULT_FAIL;
    RETURN;
  END;

  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;

  result := RESULT_FAIL;
  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, 200)(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)

  Init()();
  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;

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

Init()()

  IF DEBUG THEN PRINT("\nInitialising flash...\n")(); END;
  ResetFlash()();
  IF DEBUG THEN PRINT("Initialised\n"); END;

END;

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

ReadID()(INT manufacturer, INT device)

  INT timeout;

  // Go into autoselect mode.
  //HOLDOFF 
  WriteCycle(ADDR_UNLOCK_1 , DATA_UNLOCK_1);
  WriteCycle(ADDR_UNLOCK_2, DATA_UNLOCK_2);
  WriteCycle(ADDR_AUTOSELECT, DATA_AUTOSELECT);
  //END;
  // Read the manufacturer ID.
  ReadCycle(ADDR_MAN_ID)(manufacturer);
  // Read the device ID.
  ReadCycle(ADDR_DEV_ID)(device);

END;


//------------------------------------------------------
// IsLocked
//   Check if a block is locked
//------------------------------------------------------

IsLocked(INT address)(INT value)

  INT SA WIDTH 7;
  INT timeout;
  SA := address[18..12];
  IF DEBUG THEN PRINT("Checking block protection....\n"); END;

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

  // Is the sector locked?
  ReadCycle(SA:ADDR_SEC_PROTECT[11..0])(value);
  value := value[7..0];         // higher byte is xx

  // Reset the device.
  ResetFlash()();

  // Wait 20 ms
  SLEEP(20);

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], nWE := 1, nCE := 1, nOE := 1;
  SET nWE := 0, nCE := 0, DATA := code[DATA_BUS_WIDTH-1..0];
  SET nCE := 1, nWE := 1, DATA := I;

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, nWE := 1, nCE := 1, nOE := 1;
  SET nCE := 0, nOE := 0;
  SET result := DATA;
  SET 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 dataSet;
  INT addrLow;
  INT addrHigh;
  
  result := RESULT_FAIL;

  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 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;

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