// ============================================================================
// Copyright (c) 2024 by Terasic Technologies Inc.
// ============================================================================
//
// Permission:
//
//   Terasic grants permission to use and modify this code for use
//   in synthesis for all Terasic Development Boards and Altera Development
//   Kits made by Terasic.  Other use of this code, including the selling
//   ,duplication, or modification of any portion is strictly prohibited.
//
// Disclaimer:
//
//   This VHDL/Verilog or C/C++ source code is intended as a design reference
//   which illustrates how these types of functions can be implemented.
//   It is the user's responsibility to verify their design for
//   consistency and functionality through the use of formal
//   verification methods.  Terasic provides no warranty regarding the use
//   or functionality of this code.
//
// ============================================================================
//
//  Terasic Technologies Inc
//  No.80, Fenggong Rd., Hukou Township, Hsinchu County 303035. Taiwan
//
//
//                     web: http://www.terasic.com/
//                     email: support@terasic.com
//
// ============================================================================

#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/alt_alarm.h> // time tick function (alt_nticks(), alt_ticks_per_second())
#include "system.h"
#include "io.h"
#include "mem_verify.h"


typedef bool (*LP_VERIFY_FUNC)(void);


bool TEST_DDR4(void);
bool TEST_DDR4_QUICK(void);

typedef struct{
    LP_VERIFY_FUNC func;
    char szName[128];
}FUNC_INFO;

FUNC_INFO szFuncList[] = {
	{TEST_DDR4,         "DDR4 Full Test"},
	{TEST_DDR4_QUICK,   "DDR4 Quick Test"},

};


void GUI_ShowMenu(void){
    int nNum,i;

    nNum = sizeof(szFuncList)/sizeof(szFuncList[0]);
    printf("======= Agilex 5 NIOS DDR4 Program =======\r\n");
    for(i=0;i<nNum;i++){
        printf("[%d] %s\r\n", i, szFuncList[i].szName);
    }
    printf("Input your chioce:");
}

int GUI_QueryUser(void){
    int nChoice = 0;
    scanf("%d", &nChoice);
    printf("%d\r\n", nChoice);
    return nChoice;
}

//===============================================================
int main(void){
    int nChoice;
    int nNum;
    bool bPass;

    nNum = sizeof(szFuncList)/sizeof(szFuncList[0]);
    while(1){
    	GUI_ShowMenu();
        nChoice = GUI_QueryUser();
        if (nChoice >= 0 && nChoice < nNum){
            bPass = szFuncList[nChoice].func();
            printf("%s Test:%s\r\n", szFuncList[nChoice].szName, bPass?"PASS":"NG");
        }
    }

}


bool TEST_DDR4(void){
	bool bPass, bLoop;
	int TestIndex ;
	alt_u32 InitValue = 0x01;
	bool bShowMessage = true;
	alt_u32 TimeStart, TimeElapsed;
	alt_u16 ButtonStatus;
	const alt_u8 ButtonMask = 0x0f; // 4 key
	alt_u32 i;


	printf("===== DDR4 Test! Size=1GB =====\r\n");

        printf("\n==========================================================\n");

        printf("Press any BUTTON on the board to start test [BUTTON-0 for continued test] \n");
        printf("For continued test, press any BUTTON on the board to abort test.\n");
        ButtonStatus = ButtonMask;
        while((ButtonStatus & ButtonMask) == ButtonMask){
        	ButtonStatus = IORD(PIO_KEY_BASE, 0);
        	//ButtonStatus = 0x01;
        }

        if ((ButtonStatus & 0x01) == 0x00){
            bLoop = true;
        }else{
            bLoop = false;
            usleep(300*1000);
        }


		//
        bPass = true;
        TestIndex = 0;

        do{
        	TestIndex++;
        	printf("=====> DDR4 Testing, Iteration: %d\n", TestIndex);

        	TimeStart = alt_nticks();

        	////////////////////////////
        	// Testing //
        	// DDR4
        	TimeStart = alt_nticks();
        	printf("== DDR4 Testing...\r\n");
       		bPass = TMEM_Verify(EMIF_IO96B_DDR4COMP_BASE, EMIF_IO96B_DDR4COMP_SPAN, InitValue << i,  bShowMessage);

        	TimeElapsed = alt_nticks() - TimeStart;
        	printf("DDR4 test:%s, %d seconds\r\n", bPass ? "Pass" : "NG", (int)(TimeElapsed/alt_ticks_per_second()));


            if (bPass && bLoop){  // is abort loop?
            	ButtonStatus = IORD(PIO_KEY_BASE, 0);
            	if ((ButtonStatus & ButtonMask) != ButtonMask)
            		bLoop = false; // press any key to abort continued test
            }


        }while(bLoop && bPass);

    return bPass;
}



bool TEST_DDR4_QUICK(void){
	bool bPass, bLoop;
	int TestIndex ;
	alt_u32 TimeStart, TimeElapsed;
	alt_u16 ButtonStatus;
	const alt_u8 ButtonMask = 0x0f; // 4 key


	printf("===== DDR Test! Size= 1GB =====\r\n");

        printf("\n==========================================================\n");

        printf("Press any BUTTON on the board to start test [BUTTON-0 for continued test].\n");
        ButtonStatus = ButtonMask;
        while((ButtonStatus & ButtonMask) == ButtonMask){
        	ButtonStatus = IORD(PIO_KEY_BASE, 0);
        	//ButtonStatus = 0x01;
        }

        if ((ButtonStatus & 0x01) == 0x00){
            bLoop = true;
        }else{
            bLoop = false;
            usleep(300*1000);
        }


		//
        bPass = true;
        TestIndex = 0;

        do{
        	TestIndex++;
        	printf("=====> DDR4 Testing, Iteration: %d\n", TestIndex);


        	TimeStart = alt_nticks();


        	////////////////////////////
        	// Testing //
        	// DDR4
        	TimeStart = alt_nticks();
        	printf("== DDR4 Testing...\r\n");


            bPass = TMEM_QuickVerify(EMIF_PH2_DDR4_BASE, EMIF_PH2_DDR4_SPAN, 32,  28);

        	TimeElapsed = alt_nticks() - TimeStart;
        	printf("DDR4 test:%s, %d seconds\r\n", bPass ? "Pass" : "NG", (int)(TimeElapsed/alt_ticks_per_second()));


            if (bPass && bLoop){  // is abort loop?
            	ButtonStatus = IORD(PIO_KEY_BASE, 0);
            	if ((ButtonStatus & ButtonMask) != ButtonMask)
            		bLoop = false; // press any key to abort continued test
            }


        }while(bLoop && bPass);


    return bPass;
}
