//---------------------------------------------------------
// SDRAM DDR1 BGA90: XJEase device file
// SDRAM_DDR1_BGA90.xje Revision: 1.2
// (C) Copyright 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 SDRAM using Test()(INT result)
//---------------------------------------------------------


DEVICE NAME := "SDRAM - DDR1 BGA90"

  PINS
    DATA := A2, B3, B2, C3, C2, D3, D2, E3, E7, D8, D7, C8, C7, B8, B7, A8, R2,
            P3, P2, N3, N2, M3, M2, L3, L7, M8, M7, N8, N7, P8, P7, R8;
    ADDR := H3, H2, J7, H1, J3, J2, J1, K3, K1, K9, K7, J9, J8;
    DQM := F2, F8, K2, K8;
    DQS := E2, E8, L2, L8;
    CKE := G1;
    CLK := G2;
    BANK := H9, H8;
    COMMAND := H7, G9, G8, G7; // nCS, nRAS, nCAS, nWE
    nCLK := G3;
  END;

  DISABLE DEVICE
    COMMAND := 1;
  END;

  TEST COVERAGE
    DATA := OPEN SHORTS LO HI;
    ADDR := OPEN SHORTS LO HI;
    DQM := OPEN SHORTS LO HI;
    DQS := OPEN LO HI;
    CKE := OPEN LO HI;
    CLK := OPEN LO HI;
    BANK := OPEN SHORTS LO HI;
    COMMAND := OPEN SHORTS LO HI;
  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 := 10;
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 DQS_BUS_WIDTH  WIDTH 32 := WIDTHOF(DQS);
CONST INT CLK_BUS_WIDTH  WIDTH 32 := WIDTHOF(CLK);

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) / 1048576)/8;
CONST INT CAS_LATENCY    WIDTH 3  := 2; // 6 means 2.5. 2 means 2. 3 means 3.

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;



//----------------------------------------------------------------------------------
// 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 := 0xAA;
  INT data3 := 0xBB;
  INT data4 := 0xCC;

  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=0b", BIN(colA), " rw=0b", BIN(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, DATA := data;
  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;
  SET CLK := 1, nCLK := 0, DQS := ~0[(DQM_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[(DQM_BUS_WIDTH-1)..0];
   SET DATA := data4;
   SET CLK := 0, nCLK := 1, DQS := 0, COMMAND := CMD_NOP;
  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;

  clock := 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 := clock, nCLK := !clock;
  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 := !clock, nCLK := clock;
   SET DQSval := DQS, data3 := DATA; IF DEBUG THEN PRINT("DQS value is ",DQSval," data is 0x",HEX(data3),"\n"); END;

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

  IF (CAS_LATENCY[2]) THEN
   SET CLK := 0, nCLK := 1, COMMAND := CMD_NOP;
  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;