//---------------------------------------------------------
// SDRAM/DDR Optimised Memory Test: XJEase file
// memtestSDRAM.xje Revision: 1.1
// (c) 2007 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 SDRAM using Test()(INT result)
//---------------------------------------------------------


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

CONST INT MEMTEST_DEBUG_ALL    := 0;
CONST INT MEMTEST_DEBUG_SOME   := 1;
CONST INT MEMTEST_DEBUG_NONE   := 2;

CONST INT MEMTEST_DEBUG_LEVEL := MEMTEST_DEBUG_SOME;

CONST INT TOPADDR := ~0[ADDR_WIDTH-1..0]; // address with all bits of the row and col address set high
CONST INT TOPDATA := ~0[(DATA_BUS_WIDTH-1)..0]; // data with all bits set high

// how many databus test values there are. Note the extra 1 added if DATA_PINS is not a power of 2
CONST INT DATABUSTESTNUM := LOG(DATA_BUS_WIDTH) + 1 + (1 << (LOG(DATA_BUS_WIDTH)) < DATA_BUS_WIDTH);



//----------------------------------------------------------------------------------
// Memory test. The following code implements a memory test module.
// Test returns testFail = 1 if the test fails and 0 if it passes.
//
// It expects to have two functions WriteCycle and ReadCycle defined, as follows:
// WriteCycle(INT address, INT data, INT dqm)()    which writes "data" to "address" with
// DQM bus set to "dqm". If the chip is DDR SDRAM, the burst write behaviour of this cycle
// doesn't matter (can write any data to subequent burst addresses).
// ReadCycle(INT address)(INT data)   which reads from "address", outputting "data".
//
// DATA_BUS_WIDTH = width of data bus, DQM_BUS_WIDTH = width of dqm bus, BANK_BUS_WIDTH = width of bank bus,
// DQS_BUS_WIDTH = width of dqm bus ( set 0 if no dqs bus ),
// COL_WIDTH = width of column address, ROW_WIDTH = width of row address.
//
// Requires functions PrintData(INT i), PrintAddr(INT i), PrintBank(INT i), PrintDQM(INT i), PRINTDQS(INT i)
// which print the pin number of "data line i", "address line i", "bank line i", "DQM line i", "DQS line i"
// respectively. If no DQS bus, leave PrintDQS(INT i) EMPTY.
//
// Note that in devices with multiple DQM pins, the least significant DQM pin in the DQM
// bus should control the least significant byte of the data bus, etc.
//
// Note that you can set the level of debug via MEMTEST_DEBUG_LEVEL. This is MEMTEST_DEBUG_SOME by default,
// whose output will include diagnosis of failed pins if the chip fails.
//----------------------------------------------------------------------------------
MemTest()(INT testFail)
//----------------------------------------------------------------------------------

  INT failTestA; // firstly set to 1 if Test A fails. Used further in TestDetail function.
  INT tempResult; // set to 1 if a test fails.
  testFail := 0;

  IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
    PRINT("Starting Optimised Address Bus & Data Bus Test..\n");
  END;
  OptimisedTestAddrData()(failTestA,tempResult);

  IF tempResult THEN
    testFail := 1;

    IF (MEMTEST_DEBUG_LEVEL <=1) THEN PRINT("Optimised Address & Data Bus Test FAILED... Moving on to Detailed test...\n"); END;
    TestDetail(failTestA)();

  ELSE

    IF (MEMTEST_DEBUG_LEVEL <=1) THEN PRINT("Optimised Addr & Data test Passed\nMoving on to DQM test...\n"); END;
    DQMTest()(tempResult);

    IF (tempResult) THEN
      testFail := 1;
    END;

  END;

END;


//----------------------------------------------------------------------------------
// Optimised Test for address and databuses
//----------------------------------------------------------------------------------
// Let's be clear here - this is NOT a test of the memory chip, this is a
// test of the chip's connections. It assumes the chip is functioning.

// Therefore we can optimise the test to make certain assumptions and thus run quicker
// than if we were to test every address... we merely have to make sure we test enough
// combinations of address/data/control lines to be sure they are all working and not
// shorted to ground, power or each other.

// Function outputs OpTestFail as 1 if the test fails, failTestA as 1 if test
// A fails.
//----------------------------------------------------------------------------------
OptimisedTestAddrData()(INT failTestA, INT OpTestFail)
// ----------------------------------------------------------------------------------

  INT result; // indicates if test has failed
  INT address WIDTH ROW_WIDTH;
  INT data WIDTH DATA_BUS_WIDTH;
  INT correctData WIDTH DATA_BUS_WIDTH;
  INT i;
  INT dummyvar;


  // TEST A
  // Writes a unique value to each memory location with 1 bit set high.
  // An open, stuck or shorted (apart from shorted floating high) address pin will cause
  // The value at 0 to be read as non zero.
  // To make life complicated we are also testing the data bus at the same time. Our input values
  // correspond to those generated by the "databustestnum" function.
  IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test A : Primarily testing Address Bus\n"); END;
  WriteCycle(0,0,0);   // Be sure address 0 starts at 0.

  address := 1<< COL_WIDTH;
  FOR i:=0 FOR (ROW_WIDTH+BANK_BUS_WIDTH)
    Databustestvalue(i)(data);  // Get value to write.
    WriteCycle(address, data,0);
    address := address << 1;
  END;
  CheckMemValue(0,0)(result,dummyvar);
  IF result THEN failTestA := TRUE; END;

   // TEST B: Read values back from the lower end of the addresses (where our pattern tests the data bus optimally).
   // A different output to input will occur for one of these inputs if there is a fault in the data bus.
   IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test B : Primarily testing Data Bus\n"); END;
   address := 1<<COL_WIDTH;
   FOR i := 0 FOR DATABUSTESTNUM
     Databustestvalue(i)(correctData);   // Get correct value to check against.
     CheckMemValue(address,correctData)(result,dummyvar);
     address := address << 1;
   END;

  // TEST C
  // We will only carry this test out if tests A and B pass.
  // This test will fail if there are any floating high shorts in the address bus, or shorts between the address and data bus.

  IF !result THEN
    IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test C : Testing for shorts between address lines, and shorts betweeen address and data lines\n"); END;
    InvertedAddressTest()(result);
  END;

  OpTestFail := result;


END;




//----------------------------------------------------------------------------------
// If Optimised Test fails, this test finds more details by calling on the functions below.
//----------------------------------------------------------------------------------
TestDetail(INT failTestA)()
//----------------------------------------------------------------------------------
  INT dataFailures WIDTH DATA_BUS_WIDTH; // bit set to 1 if there is a data bus fault on this bit
  INT addFailures WIDTH ROW_WIDTH; // bit set to 1 if there is an address bus fault on this bit.
  INT bankFailures WIDTH BANK_BUS_WIDTH; // bit set to 1 if there is an address bus fault on this bit.
  INT addrdataFailures WIDTH ROW_WIDTH; // bit set to 1 if the corresponding address bit shorted to a data line.
  INT result;
  INT failByteEnable;
  INT controlFailure;

  IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 1 : Checks potential data bus errors\n"); END;
  DataBusTest()(dataFailures);
  IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("End of databus test. Concrete diagnosis not possible until further testing done\n"); END;

  IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 2 : Analysis of Test 1 results\n"); END;
  ControlCheck(dataFailures)(failByteEnable, controlFailure);

  IF failByteEnable THEN
    IF CLK_BUS_WIDTH > 1 THEN
      PRINT("NOTE: With multiple clock signals, errors that appear to be due to DQM/DQS failures may actually be due to CLK errors\n");
    END;
    RETURN;
  END;

  IF controlFailure THEN
    IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
      PRINT("Large failures found. Check Command lines (CS/RAS/CAS/WE)");
      IF (DQM_BUS_WIDTH = 1) THEN PRINT(", DQM");
        IF (DQS_BUS_WIDTH) THEN PRINT("/DQS"); END;
      END;
      PRINT(" pin, address/bank/clock-enable pins...\n");
    END;
    RETURN;
  END;

  IF (dataFailures != 0) THEN
    IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 3 : Checking for shorts between the address and data bus\n"); END;
    AddrDataShortTestDetail()(result);
    IF result THEN RETURN; END;

    IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 4 : Checking for shorted data lines\n"); END;
    DataBusShortTest(dataFailures)();
  ELSE
    IF !failTestA THEN
      IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 5 : Checking for shorted address lines\n"); END;
      AddShortHighTest()();
    ELSE
      IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 6 : Checking for address line failures\n"); END;
      AddressTest()(addFailures,bankFailures);
      IF ((addFailures!=0)||(bankFailures!=0)) THEN
  IF (MEMTEST_DEBUG_LEVEL = 0) THEN PRINT("Starting Test 7 : Checking for shorted address lines\n"); END;
  AddShortLowTest(addFailures)();
      END;
    END;
  END;


END;


//----------------------------------------------------------------------------------
// Prints DQM line failures. If "bool" is true, states problem could also be DQS.
//----------------------------------------------------------------------------------
DQMLinePrint(INT bool, INT byteFailures)()
//----------------------------------------------------------------------------------

  INT i;

  FOR i:=0 FOR DQM_BUS_WIDTH
    IF byteFailures[i] = 1 THEN
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
        PRINT("DQM failure, line ",i," pin number: "); PrintDQM(i);
        IF (bool && DQS_BUS_WIDTH) THEN PRINT(" OR DQS failure, line ",i," pin number: "); PrintDQS(i); END;
        PRINT("\n");
      END;
    END;
  END;
END;



//----------------------------------------------------------------------------------
// Called only if optimised addr and data test passes. Checks for DQM lines stuck low,
// shorted.
//----------------------------------------------------------------------------------
DQMTest()(INT dqmTestFail)
//----------------------------------------------------------------------------------


  INT i,j;
  INT data WIDTH DATA_BUS_WIDTH;
  INT dqmFailures WIDTH DQM_BUS_WIDTH;
  INT count;
  INT correctData := 0;

  IF (DQM_BUS_WIDTH = 1) THEN
    WriteCycle(0,0xAA,1);
    ReadCycle(0)(data);
    IF (data = 0xAA) THEN
      dqmTestFail := 1;
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN PRINT("Error: DQM appears to be stuck low.\n"); END;
    END;
  ELSE

    FOR i:=0 FOR DQM_BUS_WIDTH
      WriteCycle(0,TOPDATA,0)(); // make sure no bytes set to 0 already so not too many shorted lows found
      WriteCycle(0,0,1<<i)();
      ReadCycle(0)(data);

      FOR j:= 0 FOR DQM_BUS_WIDTH
        IF ((i != j)&&(dqmFailures[j]=0)) THEN
          IF ( data[(8*(j+1)-1)..8*j] != 0 ) THEN
            dqmTestFail := 1;
            dqmFailures[j] := 1;
          END;

        ELSIF (( data[(8*(j+1)-1)..8*j] != ~0[7..0] )&&( dqmFailures[j] = 0 )) THEN
          dqmTestFail := 1;
          dqmFailures[j] := 1;
        END;
      END;
    END;

    IF (dqmFailures != ~(1<<DQM_BUS_WIDTH)) THEN
      DQMLinePrint(FALSE,dqmFailures)();
    ELSE
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN PRINT("DQM FAILURE\n"); END;
    END;
  END;

END;



//----------------------------------------------------------------------------------
// Finds which data bus pins are faulty. Will not pick up a shorted pin that always wins.
//----------------------------------------------------------------------------------
DataBusTest()(INT dataFailures)
//----------------------------------------------------------------------------------

  INT address WIDTH ROW_WIDTH;
  INT data WIDTH DATA_BUS_WIDTH;
  INT correctData WIDTH DATA_BUS_WIDTH;
  INT failData WIDTH DATA_BUS_WIDTH;
  INT i;


  WriteCycle(TOPADDR,TOPDATA,0); // Means precharge will occur even if A[10] shorted to Data or Address pin

  // Tests the first DATABUSTESTNUM-1 values of Databustestvalue, and each of these values inverted.
  // This ensures both sides of a short within the data bus are found (unless 1 pin wins). This also
  // increases the chance that a control line failure will produce such diagnosis.
  FOR i := 0 FOR 2*(DATABUSTESTNUM-1)
    IF (i >= DATABUSTESTNUM - 1) THEN
      Databustestvalue(i+1-DATABUSTESTNUM)(correctData);
      correctData := ~correctData[(DATA_BUS_WIDTH-1)..0];
      address := 0;
    ELSE
      Databustestvalue(i)(correctData);
      address := TOPADDR;
    END;

    WriteCycle(address, correctData,0);
    ReadCycle(address)(data);

    failData := data ^ correctData;  // Bit-wise XOR to determine the incorrect bits.
    IF ((MEMTEST_DEBUG_LEVEL = 0) && failData) THEN PRINT("Read 0x",HEX(data),", expected to read 0x",HEX(correctData),"\n"); END;
    DO WHILE failData                       // Loop to list all errors found.
      dataFailures[LOG(failData)] := 1;
      failData := failData ^ (1 << LOG(failData));
    END;

  END;

END;



//----------------------------------------------------------------------------------
// Checks if the data bus errors are due to faults in the byte enable lines
//----------------------------------------------------------------------------------
ControlCheck(INT dataFailures WIDTH DATA_BUS_WIDTH)(INT failByteEnable, INT controlFailure)
//----------------------------------------------------------------------------------

  INT i,j;
  INT count := 0;
  INT byteFailures := 0;
  INT data1, data2;

  IF DQM_BUS_WIDTH > 1 THEN
    FOR i:=0 FOR DQM_BUS_WIDTH
      count := 0;
      FOR j:= 8*i FOR 8
        IF (dataFailures[j] = 1) THEN
          count := count + 1;
        END;
      END;
      IF (count > 4) THEN
        byteFailures[i] := 1;
      END;
    END;
  END;

  IF (byteFailures = ~0[(DQM_BUS_WIDTH-1)..0]) THEN
    controlFailure := TRUE; // not yet confirmed
  ELSIF (byteFailures !=0) THEN
    failByteEnable := TRUE;
    DQMLinePrint(TRUE, byteFailures)();
    RETURN;
  END;

  count := 0;
  controlFailure := FALSE;
  WriteCycle(0,0,0);
  ReadCycle(0)(data1);
  WriteCycle(TOPADDR,TOPDATA,0);
  ReadCycle(TOPADDR)(data2);

  FOR i:=0 FOR DATA_BUS_WIDTH
    count := count + (data1[i] = 1) + (data2[i] = 0);
  END;

  IF (count >= DATA_BUS_WIDTH/3) THEN
    controlFailure := TRUE;
  END;


END;


//----------------------------------------------------------------------------------
// First checks if such shorts exist via AddrDataShortTest. If they do, test for more details.
//----------------------------------------------------------------------------------
AddrDataShortTestDetail()(INT AddrDataShort)
//----------------------------------------------------------------------------------

  INT data WIDTH DATA_BUS_WIDTH;
  INT data2 WIDTH DATA_BUS_WIDTH;
  INT failData WIDTH DATA_BUS_WIDTH;
  INT address WIDTH ROW_WIDTH;
  INT i;
  INT count;
  INT pinsfound := FALSE;

  count := 0; // repeats test to eliminate the case of a floating open circuit in the data bus
  FOR i:=0 FOR 2
    // This makes sure that 0 isn't written to the top address previously. In this case, we would miss
    // biased low shorts.
    WriteCycle(TOPADDR,TOPDATA,0);
    AddrDataShortTest(0)(count);
    AddrDataShortTest(TOPDATA)(count);
  END;
  IF ((count = 2)||(count = 4)) THEN
    AddrDataShort := TRUE;
  END;


  // If find shorts between address pins and data pins, we can find which pins are involved.
  IF AddrDataShort THEN

    address := 1<<COL_WIDTH;
    FOR i:=0 FOR (ROW_WIDTH+BANK_BUS_WIDTH)
      WriteCycle(0,0,0);
      ReadCycle(0)(data);
      WriteCycle(address,0,0);
      ReadCycle(address)(failData);
      IF (data != failData) THEN
        IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
          IF (MEMTEST_DEBUG_LEVEL = 0) THEN
      PRINT("Wrote 0x0 to 0x",HEX(address)," read back 0x",HEX(failData)," expected to read 0x",HEX(data),"\n");
          END;
          IF (i<ROW_WIDTH) THEN
      PRINT("Address bus bit ",i," pin: ");
      PrintAddr(i)();
    ELSE
      PRINT("Bank bus bit ",i-ROW_WIDTH," pin: ");
      PrintBank(i-ROW_WIDTH);
    END;
    PRINT(" shorted to data bus pin\n");
        END;
        pinsfound := TRUE;
      END;
      address := address << 1;
    END;


    data := 1;
    FOR i:=0 FOR DATA_BUS_WIDTH
      WriteCycle(0,0,0);
      WriteCycle(0,data,0);
      ReadCycle(0)(failData);

      IF (failData[i] != data[i]) THEN
        WriteCycle(TOPADDR,(1<<i),0);
        ReadCycle(TOPADDR)(failData);
        IF (failData[i] = 1) THEN
          pinsfound := TRUE;
          IF (MEMTEST_DEBUG_LEVEL <= 1) THEN PRINT("Data line ",i," shorted to an address/bank line\n"); END;
        END;
      END;

      data := data << 1;
    END;

    IF pinsfound = FALSE THEN
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN PRINT("Possible RAS, AP BIT or address line failure\n"); END;
    END;

  END;



END;

//----------------------------------------------------------------------------------
// Tests for an address - data line short. Output is 1 if a potential Address-Data bus
// short found. Repeating this and obtaining result 1 each timeeliminates the
// possibility of a floating open circuit in the data bus.
//----------------------------------------------------------------------------------
AddrDataShortTest(INT data)(INT count)
//----------------------------------------------------------------------------------

  INT data1 WIDTH DATA_BUS_WIDTH;
  INT data2 WIDTH DATA_BUS_WIDTH;

  WriteCycle(0,data,0);
  ReadCycle(0)(data1);
  WriteCycle(TOPADDR,data,0 );
  ReadCycle(TOPADDR)(data2);
  IF data1!=data2 THEN
    IF MEMTEST_DEBUG_LEVEL = 0 THEN
      PRINT("Wrote 0x",HEX(data)," to 0x0 and 0xFF..FF, read back 0x",HEX(data1)," and 0x",HEX(data2)," respectively\n");
    END;
    count := count + 1;
  END;

END;



//----------------------------------------------------------------------------------
// Prints the data bus lines that are faulty. This is not together with the test
// above since more tests are done to verify data bus errors are reliable.
//----------------------------------------------------------------------------------
DataBusTestPrint(INT dataFailures)()
//----------------------------------------------------------------------------------

  INT i;

  FOR i:=0 TO (DATA_BUS_WIDTH-1)
    IF ((dataFailures[i] = 1) && (MEMTEST_DEBUG_LEVEL <= 1)) THEN
      PRINT("Data bus failed on line ",i, " - pin: ");
      PrintData(i)(); PRINT("\n");
    END;
  END;
END;




//----------------------------------------------------------------------------------
// Tests each pair of faulty pins within the databus to look for shorts within it.
//----------------------------------------------------------------------------------
DataBusShortTest(INT dataFailures)()
//----------------------------------------------------------------------------------

  INT i;
  INT j;
  INT data WIDTH DATA_BUS_WIDTH;
  INT dataShortPossible WIDTH DATA_BUS_WIDTH;

  WriteCycle(0,0,0);
  ReadCycle(0)(data);

  FOR i:=0 FOR DATA_BUS_WIDTH
    IF (dataFailures[i] && !data[i]) THEN
      dataShortPossible[i] := 1;
    END;
  END;

  FOR i := 0 FOR DATA_BUS_WIDTH
    FOR j := (i+1) TO (DATA_BUS_WIDTH-1)
      IF (dataShortPossible[i] && dataShortPossible[j]) THEN
        WriteCycle(0,(1<<i) + (1<<j),0);
        ReadCycle(0)(data);
        IF (data[i] && data[j]) THEN
          dataFailures[i] := 0; // so that shorted data bus pins don't get printed twice
          dataFailures[j] := 0; // ""
      dataShortPossible[i] := 0;
      dataShortPossible[j] := 0;
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
        PRINT("Data bus line ",i," pin ");
        PrintData(i); PRINT(" shorted to Data bus line ",j," pin ");
        PrintData(j)(); PRINT("\n");
      END;
        END;
      END;
    END;
  END;

  IF (dataShortPossible != 0) THEN

    WriteCycle(TOPADDR,TOPDATA,0);
    ReadCycle(TOPADDR)(data);

    FOR i:=0 FOR DATA_BUS_WIDTH
      IF (dataShortPossible[i] && data[i]) THEN
        dataFailures[i] := 0;
        IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
      PRINT("Data bus line ",i," pin ");
      PrintData(i); PRINT(" shorted to another Data bus line\n");
        END;
      END;
    END;
  END;

  DataBusTestPrint(dataFailures)();

END;



//----------------------------------------------------------------------------------
// Finds faulty address pins assuming the data bus is functional.
//----------------------------------------------------------------------------------
AddressTest()(INT addFailures, INT bankFailures)
//----------------------------------------------------------------------------------

INT address WIDTH ROW_WIDTH;
INT data WIDTH DATA_BUS_WIDTH;
INT correctData WIDTH DATA_BUS_WIDTH;
INT tempresult;
INT i;
INT dummyvar;
INT count;

  WriteCycle(0,0,0);
  address := 1<<COL_WIDTH;
  FOR i:= 0 FOR (ROW_WIDTH+BANK_BUS_WIDTH)
    Databustestvalue(i)(correctData); // Get the value to read
    CheckMemValue(address,correctData)(dummyvar,tempresult);
    IF tempresult THEN
        count := count + 1;
        addFailures[i] := (i<ROW_WIDTH);
        IF (i >= ROW_WIDTH) THEN
          bankFailures[i-ROW_WIDTH] := 1;
        END;
    END;
    address := address << 1;
  END;

  IF (count > ROW_WIDTH/3) THEN
    IF (MEMTEST_DEBUG_LEVEL <= 1) THEN PRINT("Possible RAS, AP BIT or address line failure\n"); END;
    addFailures := 0;
  ELSE
    FOR i:=0 FOR ROW_WIDTH
      IF ((MEMTEST_DEBUG_LEVEL <= 1)&&(addFailures[i] = 1)) THEN
        PRINT("Address bus failure on line ",i, " - pin: ");
        PrintAddr(i)(); PRINT("\n");
      END;
    END;

    FOR i:=0 FOR BANK_BUS_WIDTH
      IF ((MEMTEST_DEBUG_LEVEL <= 1)&&(bankFailures[i] = 1)) THEN
        PRINT("Bank bus failure on line ",i, " - pin: ");
        PrintBank(i)(); PRINT("\n");
      END;
    END;
  END;

END;



//----------------------------------------------------------------------------------
// If walking AddressTest passes and this fails, we have found floating high AA or AD shorts
// If walking AddressTest fails, and this passes, we have found floating low AA or AD shorts.
// This could be used to diagnose Address-Data bus shorts, however we test for these fully
// elsewhere.
//----------------------------------------------------------------------------------
InvertedAddressTest()(INT result)
//----------------------------------------------------------------------------------

INT bitUnderTest;
INT data;
INT i,address;
INT tempresult;

  WriteCycle(TOPADDR,TOPDATA,0);
  WriteCycle(0,0,0);
  WriteCycle(0,TOPDATA,0);

  bitUnderTest := 1<<COL_WIDTH;
  FOR i := 0 FOR (ROW_WIDTH+BANK_BUS_WIDTH);
    address := ~ bitUnderTest[(ADDR_WIDTH-1)..0];
    WriteCycle(address, 0,0);
    bitUnderTest := bitUnderTest << 1;
  END;
  CheckMemValue( 0,TOPDATA )( result, tempresult );
  CheckMemValue( TOPADDR,TOPDATA )( result, tempresult);

END;


//----------------------------------------------------------------------------------
// Checks for shorts between address bus lines. Will only find them if floating high/low, and there are no other fault types.
//----------------------------------------------------------------------------------
AddShortHighTest()()
//----------------------------------------------------------------------------------

INT i,data;
INT failData;
INT address;
INT bitUnderTest WIDTH ADDR_WIDTH;

  WriteCycle(TOPADDR,TOPDATA,0);

  data := (1 << (DATA_BUS_WIDTH - 1));
  bitUnderTest := 1<<COL_WIDTH;
  FOR i := 0 FOR (ROW_WIDTH + BANK_BUS_WIDTH)
    address := ~ bitUnderTest;
    WriteCycle(address, data,0);
    ReadCycle(address)(data);
    ReadCycle(TOPADDR)(failData);
    IF (failData = data) THEN
      IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
        IF (i>=ROW_WIDTH) THEN
          PRINT("Bank line ",i-ROW_WIDTH," pin - ");
          PrintBank(i-ROW_WIDTH);
        ELSE
          PRINT("Address line ",failData - (1<<(DATA_BUS_WIDTH-1))," pin - ");
          PrintAddr(failData - (1<<(DATA_BUS_WIDTH-1)));
        END;
        PRINT(" is shorted to another Address/Bank line that is listed\n");
      END;
    END;
    data := data + 1;
    bitUnderTest := bitUnderTest << 1;
  END;

END;

//----------------------------------------------------------------------------------
// We have already printed which address pins were faulty, and since we know also that there are only
// shorted low address bus errors, we know that the faulty pins are shorted to each other
//----------------------------------------------------------------------------------
AddShortLowTest(INT addFailures)()
//----------------------------------------------------------------------------------

  INT i;
  INT result;

  InvertedAddressTest()(result);

  IF (!result) THEN
    IF (MEMTEST_DEBUG_LEVEL <= 1) THEN
      PRINT("There are shorts between the listed faulty address and bank lines\n");
    END;
  END;

END;




//----------------------------------------------------------------------------------
// Test memory value against expected value at address input. Returns "tempresult" which identifies
// if this call of CheckMemValue failed. Also returns "result", which identifies
// whether any of the calls of CheckMemValue within scope have failed.
//----------------------------------------------------------------------------------
CheckMemValue(INT address,INT correctData WIDTH DATA_BUS_WIDTH)(INT result, INT tempresult)
//----------------------------------------------------------------------------------

  INT failData;

  ReadCycle(address)(failData);
  IF failData != correctData THEN
    result := 1;
    tempresult := 1;
    IF (MEMTEST_DEBUG_LEVEL = 0) THEN
      PRINT("Read 0x",HEX(failData)," from address: 0x",HEX(address), ", expected 0x",HEX(correctData),"\n");
    END;
  ELSE
    tempresult := 0;
  END;

END;


//----------------------------------------------------------------------------------
// Generates unique pattern of values which includes DATABUSTESTNUM from DatabustestvalueA,
// and the remaining from DatabustestvalueB so that there are ADDR_WIDTH values in total.
// Example: DATA_WDITH=8, ADDR_WIDTH=11..
// 01010101,00110011,00001111,10101010,00011110,00111100,01111000,11110000,11100001,11000011,10000111
//----------------------------------------------------------------------------------
Databustestvalue( INT Iteration )(INT data)
//----------------------------------------------------------------------------------

  IF (Iteration < DATABUSTESTNUM) THEN
    DatabustestvalueA(Iteration)(data);
  ELSE
    DatabustestvalueB(Iteration - DATABUSTESTNUM)(data);
  END;
END;


//----------------------------------------------------------------------------------
// Generates unique pattern of values in order to specifically test data bus.
// Example: DATA_WIDTH=8: 01010101,00110011,00001111,10101010
//----------------------------------------------------------------------------------
DatabustestvalueA( INT Iteration )(INT data)
//----------------------------------------------------------------------------------

  INT bitpos ;
  INT width := DATA_BUS_WIDTH;

  IF (Iteration>= DATABUSTESTNUM-1) THEN
       data := 2;
       bitpos := 2;
  ELSE
     data := (1<<(1<<(Iteration)))-1;
     bitpos :=  2<<Iteration ;
  END;

  DO WHILE (bitpos < width)
    data[(width-1)..bitpos] := data;
    bitpos := bitpos<<1;
  END;

END;


//----------------------------------------------------------------------------------
// Generates unique pattern of values (not same as any from DatabustestvalueA either)
// to ensure Databustestvalue has ADDR_WIDTH unique values.
//----------------------------------------------------------------------------------
DatabustestvalueB(INT Iteration)(INT data)
//----------------------------------------------------------------------------------

  INT width := DATA_BUS_WIDTH;

  IF Iteration = 0 THEN
    data :=  ( 1 << DATA_BUS_WIDTH/2 )  - 1;
    RotateLeft()(data);

  ELSIF ((Iteration+1)%width != 0) THEN
    RotateLeft()(data);
  ELSE
    data[(width-1)..(width-2)] := (data >> 1)[(width-1)..(width-2)];
  END;

END;


//----------------------------------------------------------------------------------
// Used to generate unique values in DatabustestvalueB to create unique values
// eg. RotateLeft(11110000) gives output 11100001
//----------------------------------------------------------------------------------
RotateLeft()(INT data)
//----------------------------------------------------------------------------------
  data := ( (data << 1)[(DATA_BUS_WIDTH-1)..1] : data[DATA_BUS_WIDTH -1] );
END;