//---------------------------------------------------------
// 72-bit SDRAM DIMM-ECC: XJEase device file
// SDRAM_DIMMECC.xje Revision: 1.3
// (c)2001-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)
//---------------------------------------------------------

DEVICE NAME := "72-bit SDRAM DIMM-ECC"

  PINS
    CLK     := 163, 79, 125, 42;
    CKE     := 63, 128;           // CKE1, CKE0
    DQM     := 131, 130, 113, 112, 47, 46, 29, 28;
    nCAS    := 111;
    nRAS    := 115;
    nWE     := 27;
    nCS     := 129, 45, 114, 30;
    COMMAND := 129, 45, 114, 30, 115, 111, 27;  // NCS3, NCS2, NCS1, NCS0, NRAS, NCAS, NWE
    COMMAND2:= 114, 30, 129, 45, 115, 111, 27;  // NCS1, NCS0, NCS3, NCS2, NRAS, NCAS, NWE
    ADDR    := 126, 123, 38, 121, 37, 120, 36, 119, 35, 118, 34, 117, 33; // pin 126 not always connected
    BANK    := 39, 122;
    SCL     := 83;
    SDA     := 82;
    SCB     := 137, 136, 106, 105, 53, 52, 22, 21;
    DATA    := 161, 160, 159, 158, 156, 155, 154, 153, 151, 150,
               149, 144, 142, 141, 140, 139, 104, 103, 101, 100,
               99,  98,  97,  95,  94,  93,  92,  91,  89,  88,
               87,  86,  77,  76,  75,  74,  72,  71,  70,  69,
               67,  66,  65,  60,  58,  57,  56,  55,  20,  19,
               17,  16,  15,  14,  13,  11,  10,  9,   8,   7,
               5,   4,   3,   2,
               137, 136, 106, 105, 53, 52, 22, 21;
    SSA     := 167, 166, 165;
  END;

  DISABLE DEVICE
    nCS := 1;
  END;

  TEST COVERAGE
    CLK     := OPEN HI LO;
    CKE     := OPEN LO;
    DQM     := SHORTS OPEN HI LO;
    ADDR    := SHORTS OPEN HI LO;
    BANK    := SHORTS OPEN HI LO;
    DATA    := SHORTS OPEN HI LO;
    COMMAND := SHORTS OPEN HI LO;
    SCL     := OPEN HI LO;
    SDA     := OPEN HI LO;
  END;


  FILES
    "memtestSDRAM.xje";
    "IIC.xje";
  END;

END;

//------------------------------------------------------------------------------------
// Constants & Static Data
//------------------------------------------------------------------------------------

// Make sure the column and row widths have been checked as they vary from chip to chip.
// Incorrect values can produce "bank failures" with a fully working device.
// Note these are printed at end of IIC test (if it passes).
CONST INT COL_WIDTH      WIDTH 32 := 9;
CONST INT ROW_WIDTH      WIDTH 32 := 13;

CONST INT DATA_BUS_WIDTH WIDTH 32 := WIDTHOF(DATA);
CONST INT DQM_BUS_WIDTH  WIDTH 32 := WIDTHOF(DQM);
CONST INT BANK_BUS_WIDTH WIDTH 32 := WIDTHOF(BANK);
CONST INT CLK_BUS_WIDTH  WIDTH 32 := WIDTHOF(CLK);
CONST INT DQS_BUS_WIDTH  WIDTH 32 := 0;

CONST INT ADDR_WIDTH     WIDTH 32 := (ROW_WIDTH + COL_WIDTH + BANK_BUS_WIDTH);
CONST INT AP_BIT         WIDTH 32 := 10;
CONST INT SIZE_MB        WIDTH 32 := (1 << (BANK_BUS_WIDTH + ROW_WIDTH + COL_WIDTH)) * (DATA_BUS_WIDTH / 8) / 1048576;
CONST INT CAS_LATENCY    WIDTH 3  := 0b010;

CONST INT BURST_LENGTH_1 WIDTH 3  := 0b000;
CONST INT BURST_LENGTH_2 WIDTH 3  := 0b001;
CONST INT BURST_LENGTH_4 WIDTH 3  := 0b010;
CONST INT BURST_LENGTH_8 WIDTH 3  := 0b011;
CONST INT BURST_LENGTH   WIDTH 3  := BURST_LENGTH_1;

CONST INT BURST_TYPE_SEQ WIDTH 1  := 0b0;
CONST INT BURST_TYPE_INT WIDTH 1  := 0b1;
CONST INT BURST_TYPE     WIDTH 1  := BURST_TYPE_SEQ;

CONST INT CMD_DESELECT   WIDTH 7  := 0b1111111;
CONST INT CMD_NOP        WIDTH 7  := 0b1010111;
CONST INT CMD_BTERM      WIDTH 7  := 0b1010110;
CONST INT CMD_READ       WIDTH 7  := 0b1010101;
CONST INT CMD_WRITE      WIDTH 7  := 0b1010100;
CONST INT CMD_ACTIVE     WIDTH 7  := 0b1010011;
CONST INT CMD_PRE        WIDTH 7  := 0b1010010;
CONST INT CMD_REF        WIDTH 7  := 0b1010001;
CONST INT CMD_MODE       WIDTH 7  := 0b1010000;

CONST INT DEBUG          WIDTH 1  := FALSE;
CONST INT RESULT_PASS    WIDTH 1  := FALSE;
CONST INT RESULT_FAIL    WIDTH 1  := TRUE;

CONST INT CKE_TIED_HIGH           := FALSE;  // Set FALSE if JTAG has control of CKE

CONST INT CLK_LO         WIDTH CLK_BUS_WIDTH := 0;
CONST INT CLK_HI         WIDTH CLK_BUS_WIDTH := ~0[CLK_BUS_WIDTH-1..0];

// The constants refer to the IIC part of the module.
// These may vary, and can be found in the data sheet
CONST INT IIC_ADDRESS             := 0xA0;
CONST INT IIC_ADDRESS_BYTES       := 1;     // number of address bytes there are in the IIC device
CONST INT IIC_WRITE_PAGE_SIZE     := 16;
CONST INT IIC_READ_PAGE_SIZE      := 16;




//------------------------------------------------------------------------------------
// Memory Test. Returns 1 for a failure, 0 for a pass. First tests IIC, then calls
// MemTest (memtestSDRAM.xje).
// Finally tests the chip-enable/clock/clock-enable pins that werent used.
//------------------------------------------------------------------------------------
Test()(INT result)
//------------------------------------------------------------------------------------

   PRINT("\nDDR memory test for chip, ",DEVICE_REF,"\n");
   result := RESULT_PASS;

   PRINT("Initialising DDR module...\n");
   Initialise()();

   PRINT("Testing I2C EEPROM...\n");
   TestIIC()(result);

   IF (result) THEN
     PRINT("memory chip ", DEVICE_REF, " FAILED...\n\n");
     Deselect()();
     RETURN;
   END;

   PRINT("Calling MemTest...\n");
   MemTest()(result);

   IF (result) THEN
     PRINT("Memory chip ", DEVICE_REF, " FAILED...\n\n");
     Deselect()();
     RETURN;
   END;

   PRINT("Testing nCS1/nCS3/CKE1...\n");
   TestCS()(result);

   IF (result) THEN
     PRINT("Memory chip ", DEVICE_REF, " FAILED...\n\n");
     Deselect()();
     RETURN;
   END;

   PRINT("Memory chip ", DEVICE_REF, " PASSED...\n\n");
   Deselect()();

END;


//------------------------------------------------------------------------------------
// IIC SPD EEPROM Test
// Routine reads the first 63 bytes of the SPD EEPROM and confirms a correct
// checksum is held in byte 64.
//------------------------------------------------------------------------------------
TestIIC()(INT result)
//------------------------------------------------------------------------------------

  INT data WIDTH 8 := 0;
  INT checksum WIDTH 8:= 0;
  INT readFailure;
  result := RESULT_PASS;

  // find sum of first 63 addresses
  IIC_Read(0, 63)(data, readFailure);

  IF readFailure THEN
    PRINT("IIC read failure..\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  GetSum(data,63)(checksum);
  PRINT("Checksum (sum of first 63 addresses): 0x",HEX(checksum),"\n");

  // read address 63, should be the same as "sum"
  data := 0;
  IIC_Read(63, 1)(data, readFailure);
  PRINT("Checksum (value in 64th address): 0x",HEX(data),"\n");

  IF (data != checksum) THEN
    PRINT("\tSDRAM SPD data checksum error.\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  PRINT("\tSPD ROM checksum is correct.\n");

  IIC_Read(3,  1)(data, readFailure);
  PRINT("\tSDRAM columns : ", data, "\n");
  IIC_Read(4,  1)(data, readFailure);
  PRINT("\tSDRAM rows    : ", data, "\n");

  PRINT("IIC EEPROM test passed.\n");

END;


//------------------------------------------------------------------------------------
// Used in TestIIC to find the sum of the first "bytes" bytes of "data".
//------------------------------------------------------------------------------------
GetSum(INT data, INT bytes)(INT result)
//------------------------------------------------------------------------------------

  INT i;
  result := 0;
  FOR i := 0 FOR bytes
    result := (result + data[(i*8)+7..i*8])[7..0];
  END;

END;



//------------------------------------------------------------------------------------
// This is called if MemTest passes (thus we know the address/data bus is working)
// Thus this is simply testing the other chip-enable/clock-enable pins.
//------------------------------------------------------------------------------------
TestCS()(INT result)
//------------------------------------------------------------------------------------

  INT TestAddress := 0x55555555;
  INT TestData := 0x55555555;
  INT ReadData;

  WriteCycleCS(TestAddress, TestData)();  // writes to memory using different chip selects
  SET DATA := 0;
  ReadCycleCS(TestAddress)(ReadData);

  IF ReadData != TestData THEN
    result := RESULT_FAIL;
    PRINT("Invalid data read: nCS1,nCS3 or CKE1 failure.\n");
  ELSE
    PRINT("nCS test passed.\n");
  END;

END;



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

PrintDQM(INT i)()
  PRINT(PINNUM(DQM[i]));
END;

PrintDQS(INT i)()
END;

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

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

PrintBank(INT i)()
  PRINT(PINNUM(BANK[i]));
END;


//------------------------------------------------------------------------------------
// Low-level functions...
//------------------------------------------------------------------------------------

Deselect()()
  SET CLK := CLK_LO, COMMAND := CMD_DESELECT;
  SET CLK := CLK_HI;
END;

WriteNop()()
  SET CLK := CLK_LO, COMMAND := CMD_NOP;
  SET CLK := CLK_HI;
END;

PrechargeBank(INT bank WIDTH BANK_BUS_WIDTH)()
  SET CLK := CLK_LO, COMMAND := CMD_PRE, ADDR[10] := 0, BANK := bank;
  SET CLK := CLK_HI;
END;

PrechargeAll()()
  SET CLK := CLK_LO, COMMAND := CMD_PRE, ADDR[10] := 1, BANK := 0;
  SET CLK := CLK_HI;
END;

AutoRefresh()()
  SET CLK := CLK_LO, COMMAND := CMD_REF;
  SET CLK := CLK_HI;
END;

WriteMode(INT mode WIDTH ROW_WIDTH)()
  SET CLK := CLK_LO, COMMAND := CMD_MODE, ADDR := mode, BANK := 0;
  SET CLK := CLK_HI;
  IF (DEBUG) THEN PRINT("Mode set 0x", HEX(mode), "\n"); END;
END;



//------------------------------------------------------------------------------------
// Initialise SDRAM
//------------------------------------------------------------------------------------

Initialise()()

  INT i;
  INT mode WIDTH ROW_WIDTH;

  IF WRITEABLE(CKE) THEN
    SET CKE := ~0[(WIDTHOF(CKE)-1)..0];
  END;

  // Begin by writing a single NOP cycle.
  WriteNop();

  // Then write a PRECHARGE instruction on all banks.
  PrechargeAll();
  WriteNop();
  WriteNop();

  // Write auto refresh cycles.
  FOR i := 0 TO 15
    AutoRefresh();
    WriteNop();
    AutoRefresh();
    WriteNop();
  END;

  // Write the MODE register.
  mode       := 0;
  mode[2..0] := BURST_LENGTH;
  mode[3]    := BURST_TYPE;
  mode[6..4] := CAS_LATENCY;
  mode[8..7] := 0b00;
  mode[9]    := 1;
  WriteMode(mode);

END;



//------------------------------------------------------------------------------------
// WriteCycle
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------
WriteCycle(INT address WIDTH ADDR_WIDTH, INT data WIDTH DATA_BUS_WIDTH, INT dqm WIDTH DQM_BUS_WIDTH)()

  INT bank WIDTH BANK_BUS_WIDTH := address[(ADDR_WIDTH-1)..(ADDR_WIDTH-BANK_BUS_WIDTH)];
  INT rowA WIDTH ROW_WIDTH := address[(COL_WIDTH+ROW_WIDTH-1)..(COL_WIDTH)]; // Row address
  INT colA WIDTH COL_WIDTH := address[(COL_WIDTH-1)..0];   // Column address

  IF (COL_WIDTH>AP_BIT) THEN
    colA[(COL_WIDTH)..(AP_BIT+1)] := colA[(COL_WIDTH-1)..AP_BIT]; // create space for the Auto pre charge bit
  END;

  colA[AP_BIT] := 1;

  IF (DEBUG) THEN
    PRINT("Write : address=0x", HEX(address), " [Bank=0x", HEX(bank), " col=0x", HEX(colA), " row=0x", HEX(rowA), "] data=0x", HEX(data), "\n");
  END;

  SET CLK := CLK_LO, COMMAND := CMD_ACTIVE, ADDR := rowA, BANK := bank, DATA := data;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND := CMD_WRITE, DQM := dqm[DQM_BUS_WIDTH-1..0], ADDR := colA;
  SET CLK := CLK_HI, DATA := I;

  SET CLK := CLK_LO, COMMAND := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO;
  SET CLK := CLK_HI;

END;



//------------------------------------------------------------------------------------
// ReadCycle
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------

ReadCycle(INT address WIDTH ADDR_WIDTH)(INT data WIDTH DATA_BUS_WIDTH)

  INT bank WIDTH BANK_BUS_WIDTH := address[(ADDR_WIDTH-1)..(ADDR_WIDTH-BANK_BUS_WIDTH)];
  INT rowA WIDTH ROW_WIDTH := address[(COL_WIDTH+ROW_WIDTH-1)..(COL_WIDTH)]; // Row address
  INT colA WIDTH COL_WIDTH := address[(COL_WIDTH-1)..0];   // Column address
  INT casloop;

  IF (COL_WIDTH>AP_BIT) THEN
    colA[(COL_WIDTH)..(AP_BIT+1)] := colA[(COL_WIDTH-1)..AP_BIT]; // create space for the Auto pre charge bit
  END;

  colA[AP_BIT] := 1;

  SET CLK := CLK_LO, COMMAND := CMD_ACTIVE, ADDR := rowA, BANK := bank, DATA := I, DQM := 0;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND := CMD_NOP ;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND := CMD_READ, ADDR := colA;
  SET CLK := CLK_HI;

  FOR casloop := 0 FOR CAS_LATENCY - 1
    SET CLK := CLK_LO, COMMAND := CMD_NOP;
    SET CLK := CLK_HI;
  END;

  SET CLK := CLK_LO;
  SET CLK := CLK_HI, data := DATA;

  SET CLK := CLK_LO, COMMAND := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND := CMD_NOP;
  SET CLK := CLK_HI;

  IF (DEBUG) THEN
    PRINT("Read : address=0x", HEX(address), " [Bank=0x", HEX(bank), " col=0x", HEX(colA), " row=0x", HEX(rowA), "] data=0x", HEX(data), "\n");
  END;

END;





//------------------------------------------------------------------------------------
// WriteCycleCS : used in TestCS to test that the other chip enable isn't stuck inactive
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------
WriteCycleCS(INT address WIDTH ADDR_WIDTH, INT data WIDTH DATA_BUS_WIDTH)()

  INT bank WIDTH BANK_BUS_WIDTH := address[(ADDR_WIDTH-1)..(ADDR_WIDTH-BANK_BUS_WIDTH)];
  INT rowA WIDTH ROW_WIDTH := address[(COL_WIDTH+ROW_WIDTH-1)..(COL_WIDTH)]; // Row address
  INT colA WIDTH COL_WIDTH := address[(COL_WIDTH-1)..0];   // Column address

  IF (COL_WIDTH>AP_BIT) THEN
    colA[(COL_WIDTH)..(AP_BIT+1)] := colA[(COL_WIDTH-1)..AP_BIT]; // create space for the Auto pre charge bit
  END;

  colA[AP_BIT] := 1;                                     // Auto precharge

  IF (DEBUG) THEN
    PRINT("Write : address=0x", HEX(address), " [Bank=0x", HEX(bank), " col=0x", HEX(colA), " row=0x", HEX(rowA), "] data=0x", HEX(data), "\n");
  END;

  SET CLK := CLK_LO, COMMAND2 := CMD_ACTIVE, ADDR := rowA, BANK := bank, DATA := data;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND2 := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND2 := CMD_WRITE, DQM := 0, ADDR := colA;
  SET CLK := CLK_HI, DATA := I;

  SET CLK := CLK_LO, COMMAND2 := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO;
  SET CLK := CLK_HI;

END;


//------------------------------------------------------------------------------------
// ReadCycleCS : used in TestCS to test that the other chip enable isn't stuck inactive
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------

ReadCycleCS(INT address WIDTH ADDR_WIDTH)(INT data WIDTH DATA_BUS_WIDTH)

  INT bank WIDTH BANK_BUS_WIDTH := address[(ADDR_WIDTH-1)..(ADDR_WIDTH-BANK_BUS_WIDTH)];
  INT rowA WIDTH ROW_WIDTH := address[(COL_WIDTH+ROW_WIDTH-1)..(COL_WIDTH)]; // Row address
  INT colA WIDTH COL_WIDTH := address[(COL_WIDTH-1)..0];   // Column address
  INT casloop;

  IF (COL_WIDTH>AP_BIT) THEN
    colA[(COL_WIDTH)..(AP_BIT+1)] := colA[(COL_WIDTH-1)..AP_BIT]; // create space for the Auto pre charge bit
  END;

  colA[AP_BIT] := 1;

  SET CLK := CLK_LO, COMMAND2 := CMD_ACTIVE, ADDR := rowA, BANK := bank, DATA := I, DQM := 0;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND2 := CMD_NOP ;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND2 := CMD_READ, ADDR := colA;
  SET CLK := CLK_HI;

  FOR casloop := 0 FOR CAS_LATENCY - 1
    SET CLK := CLK_LO, COMMAND2 := CMD_NOP;
    SET CLK := CLK_HI;
  END;

  SET CLK := CLK_LO;
  SET CLK := CLK_HI, data := DATA;

  SET CLK := CLK_LO, COMMAND2 := CMD_NOP;
  SET CLK := CLK_HI;

  SET CLK := CLK_LO, COMMAND2 := CMD_NOP;
  SET CLK := CLK_HI;

  IF (DEBUG) THEN
    PRINT("Read : address=0x", HEX(address), " [Bank=0x", HEX(bank), " col=0x", HEX(colA), " row=0x", HEX(rowA), "] data=0x", HEX(data), "\n");
  END;

END;