//---------------------------------------------------------
// DDR SDRAM TQFP 100: XJEase device file
// SDRAM_DDR1_TQFP100.xje Revision: 1.3
// (C) Copyright 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 := "DDR1 TQFP 100"

  PINS
    DATA     := 84, 83, 81, 80, 78, 77, 75, 74, 21, 20, 18, 17, 13, 12, 10, 9,
                72, 71, 69, 68, 64, 63, 61, 60, 7, 6, 4, 3, 1, 100, 98, 97;
    ADDR     := 37, 36, 45, 51, 50, 49, 48, 47, 34, 33, 32, 31;
    BANK     := 30, 29;
    DQM      := 57, 24, 56, 23;
    DQS      := 94;
    CLK      := 55;
    nCLK     := 54;
    CKE      := 53;
    COMMAND  := 28, 27, 26, 25;  // nCS, nRAS, nCAS, nWE
  END;

  DISABLE DEVICE
    COMMAND := 1;
  END;

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

  FILES
    "memtestSDRAM.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.
CONST INT COL_WIDTH      WIDTH 32 := 8;
CONST INT ROW_WIDTH      WIDTH 32 := 12;

CONST INT AP_BIT         WIDTH 32 := 8;
CONST INT CAS_LATENCY    WIDTH 3  := 3; // 3 means 3, 4 means 4.

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

CONST INT ADDR_WIDTH     WIDTH 32 := (ROW_WIDTH + COL_WIDTH + BANK_BUS_WIDTH);
CONST INT SIZE_MB        WIDTH 32 := ((1 << (BANK_BUS_WIDTH + ROW_WIDTH + COL_WIDTH)) * (DATA_BUS_WIDTH) / 1048576)/8;


CONST INT BURST_LENGTH_2 WIDTH 3  := 0b001;
CONST INT BURST_LENGTH_4 WIDTH 3  := 0b010;
CONST INT BURST_LENGTH_8 WIDTH 3  := 0b011; //not supported by this file at present.
CONST INT BURST_LENGTH   WIDTH 3  := BURST_LENGTH_4;

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 4  := 0b1111;
CONST INT CMD_NOP        WIDTH 4  := 0b0111;
CONST INT CMD_BTERM      WIDTH 4  := 0b0110;
CONST INT CMD_READ       WIDTH 4  := 0b0101;
CONST INT CMD_WRITE      WIDTH 4  := 0b0100;
CONST INT CMD_ACTIVE     WIDTH 4  := 0b0011;
CONST INT CMD_PRE        WIDTH 4  := 0b0010;
CONST INT CMD_REF        WIDTH 4  := 0b0001;
CONST INT CMD_MODE       WIDTH 4  := 0b0000;

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



//----------------------------------------------------------------------------------
// Memory Test. Returns 1 for a failure, 0 for a pass.
// Calls MemTest (memtestSDRAM.xje) and tests CKE and nCS.
//----------------------------------------------------------------------------------
Test()(INT testFail)
//----------------------------------------------------------------------------------

  PRINT("\nTesting memory chip ", DEVICE_REF, "...\n");
  testFail := 0;

  Initialise();
  MemTest()(testFail);

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

  IF WRITEABLE(CKE) THEN
    PRINT("Testing CKE...\n");
    CKETest()(testFail);
    IF testFail = 1 THEN
      PRINT("Memory chip ", DEVICE_REF, " FAILED...\n\n");
      Deselect()();
      RETURN;
    END;
  END;

  IF WRITEABLE(COMMAND) THEN
    PRINT("Testing NCS...\n");
    NCSTest()(testFail);
    IF testFail = 1 THEN
      PRINT("Memory chip ", DEVICE_REF, " FAILED...\n\n");
      Deselect()();
      RETURN;
    END;
  END;

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

END;



//----------------------------------------------------------------------------------
// Checks that the CKE pin isn't stuck high
//----------------------------------------------------------------------------------
CKETest()(INT result)
//----------------------------------------------------------------------------------

  INT data WIDTH DATA_BUS_WIDTH;
  result := 0;

  SET CKE := 0;

  WriteCycle(0xAA, 0xBB, 0);
  SET DATA := 0;
  ReadCycle(0xAA)(data);

  IF (DEBUG) THEN
    PRINT("Wrote 0xBB with CKE set low, and read back 0x", HEX(data), "\n");
  END;

  IF (data = 0xBB) THEN
    PRINT("ERROR: CKE stuck high\n");
    result := 1;
  END;

  SET CKE := 1;

END;

//----------------------------------------------------------------------------------
// Checks that the nCS pin isn't stuck low
//----------------------------------------------------------------------------------
NCSTest()(INT result)
//----------------------------------------------------------------------------------

  INT data WIDTH DATA_BUS_WIDTH;
  result := 0;

  // write with NCS high
  SET CLK := 0, nCLK := 1, COMMAND := 0b1011, ADDR := 0, BANK := 0, DATA := 0xAA;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := 0b1111;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := 0b1100, ADDR := 0, DQS := 0;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := 0b1111, DQM := 0;
  SET CLK := 1, nCLK := 0, DQS := ~0[(DQS_BUS_WIDTH-1)..0];

  SET CLK := 0, nCLK := 1, DQS := 0;

  IF BURST_LENGTH > BURST_LENGTH_2 THEN   //assume burst length 4 for now... implement 8 later maybe.
    SET CLK := 1, nCLK := 0, DQS := ~0[(DQS_BUS_WIDTH-1)..0];
    SET CLK := 0, nCLK := 1, DQS := 0, COMMAND := 0b1111;
  END;

  SET DQS := I, DATA := I;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1;
  SET CLK := 1, nCLK := 0;

  ReadCycle(0)(data);

  IF (DEBUG) THEN
    PRINT("Wrote 0xAA with NCS high, and read back 0x", HEX(data), "\n");
  END;

  IF (data = 0xAA) THEN
    PRINT("ERROR : NCS stuck low\n");
    result := 1;
  END;

END;



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

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

PrintDQS(INT i)()
  PRINT(PINNUM(DQS[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 := 0, nCLK := 1, COMMAND := CMD_DESELECT;
  SET CLK := 1, nCLK := 0;
END;

WriteNop()()
  SET CLK := 0, nCLK := 1, COMMAND := CMD_NOP;
  SET CLK := 1, nCLK := 0;
END;

PrechargeBank(INT bank WIDTH BANK_BUS_WIDTH)()
  SET CLK := 0, nCLK := 1, COMMAND := CMD_PRE, ADDR[AP_BIT] := 0, BANK := bank;
  SET CLK := 1, nCLK := 0;
END;

PrechargeAll()()
  SET CLK := 0, nCLK := 1, COMMAND := CMD_PRE, ADDR[AP_BIT] := 1, BANK := 0;
  SET CLK := 1, nCLK := 0;
END;

AutoRefresh()()
  SET CLK := 0, nCLK := 1, COMMAND := CMD_REF;
  SET CLK := 1, nCLK := 0;
END;

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


SetupModeRegister(INT resetDLL)()
   INT mode;

   // 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[12..9]    := 0b0000;

   IF resetDLL THEN
      mode[8] := 1;
   END;
   WriteMode(mode, 0b00);

   WriteNop()();

END;

SetupExtModeRegister(INT enableDLL)()

  IF (!enableDLL) THEN
    WriteMode(0b01, 0b01)();
          //BA -> 0b01 - select extended mode register.
          //mode 0b01 here should mean normal drive strength + dll disabled.
    WriteNop()();
  ELSE
    WriteMode(0, 0b01)();  //mode 0b00 means dll enabled, normal drive strength
  END;

END;


//------------------------------------------------------------------------------------
// Initialise DDR DRAM
//
// This section initialises the DDR DRAM in a safe manner whilst
// also disabling the internal DLL.
//------------------------------------------------------------------------------------

Initialise()()

  INT i;
  INT mode WIDTH ROW_WIDTH;
  INT resetDLL := TRUE;
  INT enableDLL := FALSE;

  SET CLK := 0, nCLK := 1, CKE := 1, DQM := ~0[DQM_BUS_WIDTH-1..0], COMMAND := CMD_NOP, ADDR := 0, DATA := 0, BANK := 0;

  Deselect();

  // Begin by writing a few NOP cycles.
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

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

  //Configure extended mode register
  SetupExtModeRegister(enableDLL)();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

  // Write the MODE register.
  SetupModeRegister(resetDLL)();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

  // Write the MODE register back to normal operation
  // not required by micron but recommended by JEDEC spec
  resetDLL := FALSE;
  SetupModeRegister(resetDLL)();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

  //Precharge all (again)
  PrechargeAll();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

  // Write 2 auto refresh cycles.
  AutoRefresh();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();
  AutoRefresh();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();

  //Configure extended mode register to disable DLL
  enableDLL := FALSE;
  SetupExtModeRegister(enableDLL)();
  WriteNop();
  WriteNop();
  WriteNop();
  WriteNop();


END;


//------------------------------------------------------------------------------------
// WriteCycle : Calls WriteBurst but only cares about writing to 1 address
// It doesn't actually write a single address so be aware it writes BURST_LENGTH times.
// Sets the DQM bus to "byte" during write.
// 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 data2 := 0x55;
  INT data3 := 0xAA;
  INT data4 := 0xBB;

  WriteBurst(address, data, data2, data3, data4, dqm);

END;


//------------------------------------------------------------------------------------
// WriteBurst : Write a single burst of BURST_LENGTH addresses to memory.
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------
WriteBurst(INT address WIDTH ADDR_WIDTH, INT data WIDTH DATA_BUS_WIDTH, INT data2 WIDTH DATA_BUS_WIDTH,
            INT data3 WIDTH DATA_BUS_WIDTH, INT data4 WIDTH DATA_BUS_WIDTH, INT dqm WIDTH DQM_BUS_WIDTH)()

  // IMPORTANT: Note that if BURST_LENGTH = BURST_LENGTH_2 then only 2 values will actually be written.
  // BURST_LENGTH_8 not supported here.

  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 : A=0x", HEX(address), " [Ba=0x", HEX(bank), " cl=0x", HEX(colA), " rw=0x", HEX(rowA), "] D=0x", HEX(data), " ",HEX(data2)," ",HEX(data3)," ",HEX(data4),"\n");
  END;


  SET CLK := 0, nCLK := 1, COMMAND := CMD_ACTIVE, ADDR := rowA, BANK := bank;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := CMD_NOP;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := CMD_WRITE, ADDR := colA, DQS := 0;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := CMD_NOP, DQM := dqm, DATA := data;

  SET CLK := 1, nCLK := 0, DQS := ~0[DQS_BUS_WIDTH-1..0];

  SET DATA := data2;

  SET CLK := 0, nCLK := 1, DQS := 0;

  IF BURST_LENGTH > BURST_LENGTH_2 THEN   //assume burst length 4 for now... implement 8 later maybe.
   SET DATA := data3;
   SET CLK := 1, nCLK := 0, DQS := ~0[DQS_BUS_WIDTH-1..0];
   SET DATA := data4;
   SET CLK := 0, nCLK := 1, DQS := 0;
  END;
  SET DQS := I ,DATA := I;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1;
  SET CLK := 1, nCLK := 0;

END;


//------------------------------------------------------------------------------------
// ReadCycle : Reads from a single address (the remaining burst reads are ignored)
// 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 data2, data3, data4;
  ReadBurst(address)(data, data2, data3, data4);

END;

//------------------------------------------------------------------------------------
// ReadBurst
// Address:  BANK[BANK_BUS_WIDTH-1..0] : ROW[ROW_WIDTH-1..0] : COLUMN[COL_WIDTH-1..0]
//------------------------------------------------------------------------------------
ReadBurst(INT address WIDTH ADDR_WIDTH)(INT data WIDTH DATA_BUS_WIDTH, INT data2 WIDTH DATA_BUS_WIDTH,
                  INT data3 WIDTH DATA_BUS_WIDTH, INT data4 WIDTH DATA_BUS_WIDTH)

  // NB if BURST_LENGTH = BURST_LENGTH_2 then only 2 values will be populated.
  // BURST_LENGTH_8 not supported here

  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 DQSval := 1;
  INT oldDQSval;
  INT i;
  INT clock WIDTH 1;

  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

  SET CLK := 0, nCLK := 1, COMMAND := CMD_ACTIVE, ADDR := rowA, BANK := bank, DATA := I;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := CMD_NOP;
  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1, COMMAND := CMD_READ, ADDR := colA;
  SET CLK := 1, nCLK := 0;

  // Wait until the transition of DQS lines from low to high, then begin reading data.
  // With the DLL turned off, the read delay can vary with manufacturer.
  DO
    oldDQSval := DQSval;
    SET CLK := clock, nCLK := !clock, COMMAND := CMD_NOP;
    SET DQSval := DQS, data := DATA; IF DEBUG THEN PRINT("DQS value is ",DQSval," data is 0x",HEX(data),"\n"); END;
    clock := !clock;
    i := i + 1;
  UNTIL ((DQSval = ~0[(DQS_BUS_WIDTH-1)..0] && oldDQSval = 0) || i = 4*CAS_LATENCY)
  END;

  SET CLK := 0, nCLK := 1;
  SET DQSval := DQS, data2 := DATA; IF DEBUG THEN PRINT("DQS value is ",DQSval," data is 0x",HEX(data2),"\n"); END;

  IF BURST_LENGTH > BURST_LENGTH_2 THEN
   SET CLK := 1, nCLK := 0;
   SET DQSval := DQS, data3 := DATA; IF DEBUG THEN PRINT("DQS value is ",DQSval," data is 0x",HEX(data3),"\n"); END;

   SET CLK := 0, nCLK := 1;
   SET DQSval := DQS, data4 := DATA; IF DEBUG THEN PRINT("DQS value is ",DQSval," data is 0x",HEX(data4),"\n"); END;
  END;

  SET CLK := 1, nCLK := 0;

  SET CLK := 0, nCLK := 1;
  SET CLK := 1, nCLK := 0;

  IF (DEBUG) THEN
    PRINT("Read : A=0x", HEX(address), " [Ba=0x", HEX(bank), " cl=0x", HEX(colA), " rw=0x", HEX(rowA), "] D=0x", HEX(data), " ",HEX(data2)," ",HEX(data3)," ",HEX(data4),"\n");
  END;

END;