/**
 * \file    drv_bist.c
 * \brief   
 * 
 * \par     Copyright (c) 2019 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2019-11-21
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2019-11-21  Original Creation
 * 
 */


#include "drivers.h"
#include "libc.h"
#include "drv_bist.h"


#define BIST_WR_CMD         (0xA5)
#define BIST_RD_CMD         (0x5B)
#define BIST_RESP_CMD       (0x55)

#define TEST_CLK_PIN        DRV_GPIO_1
#define TDO_PIN             DRV_GPIO_10
#define TDI_PIN             DRV_GPIO_9
#define TEST_EXT_POR_N      DRV_GPIO_21

#if 1  /* modified by James Gan - 2019-11-21 */
#define BIST_RESP_TIMEOUT   (5000000UL)
#define DELAY_ROUTINE       rtc_delay_us
#define CLK_DELAY           (2)
#define CLK_DELAY_HALF      (1)
#else
#define BIST_RESP_TIMEOUT   (1000000UL)
#define DELAY_ROUTINE       delay
#define CLK_DELAY           (5)
#define CLK_DELAY_HALF      (2)
#endif /* #if 0 */


void drv_bist_init()
{
    drv_gpio_val_set(TEST_CLK_PIN,0);
    drv_gpio_oe_set(TEST_CLK_PIN,TRUE);
    drv_gpio_en_set(TEST_CLK_PIN,TRUE);

    drv_gpio_oe_set(TDI_PIN,FALSE);
    drv_gpio_en_set(TDI_PIN,TRUE);

    drv_gpio_val_set(TDO_PIN,1);
    drv_gpio_oe_set(TDO_PIN,TRUE);
    drv_gpio_en_set(TDO_PIN,TRUE);

    drv_gpio_val_set(TEST_EXT_POR_N,1);
    drv_gpio_oe_set(TEST_EXT_POR_N,TRUE);
    drv_gpio_en_set(TEST_EXT_POR_N,TRUE);
}

void drv_bist_clk_gen(u32_t count)
{
    u32_t i;
    for(i = 0; i < count; i++)
	{
		DELAY_ROUTINE(CLK_DELAY);
        drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}
}

u8_t drv_bist_write(u32_t addr, u32_t val)
{
	u32_t i;
    u8_t cmd = BIST_WR_CMD;
    u8_t resp = 0;

    // write command
	for(i = 0; i < 8; i++)
	{
        drv_gpio_val_set(TDO_PIN, cmd & 1);
		cmd >>= 1;
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}

    DELAY_ROUTINE(CLK_DELAY);

    // write address
	for(i = 0; i < 32; i++)
	{
        drv_gpio_val_set(TDO_PIN, addr & 1);
		addr >>= 1;
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}

    DELAY_ROUTINE(CLK_DELAY);

    // write value
	for(i = 0; i < 32; i++)
	{
        drv_gpio_val_set(TDO_PIN, val & 1);
		val >>= 1;
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}

    DELAY_ROUTINE(CLK_DELAY);

    sys_tick_t t = now();
    // read 8 bits response
    do
	{
        drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY_HALF);
        resp >>= 1;
		resp |= (drv_gpio_val_get(TDI_PIN) << 7);
		DELAY_ROUTINE(CLK_DELAY_HALF);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
        DELAY_ROUTINE(CLK_DELAY);
	} while ((resp != BIST_RESP_CMD) && ((u32_t)(now() - t) < BIST_RESP_TIMEOUT));

    DELAY_ROUTINE(CLK_DELAY);
	drv_gpio_val_set(TEST_CLK_PIN, 1);
	DELAY_ROUTINE(CLK_DELAY);
	drv_gpio_val_set(TEST_CLK_PIN, 0);

	return resp;
}

u8_t drv_bist_read(u32_t addr, u32_t *p_val)
{
	u32_t i;
    u8_t cmd = BIST_RD_CMD;
    u8_t resp = 0;

    *p_val = 0;

    // write command
	for(i = 0; i < 8; i++)
	{
        drv_gpio_val_set(TDO_PIN, cmd & 1);
		cmd >>= 1;
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}

    DELAY_ROUTINE(CLK_DELAY);

    // write address
	for(i = 0; i < 32; i++)
	{
        drv_gpio_val_set(TDO_PIN, addr & 1);
		addr >>= 1;
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
	}

    DELAY_ROUTINE(CLK_DELAY);

    sys_tick_t t = now();
    // read 8 bits response
    do
	{
        drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY_HALF);
        resp >>= 1;
		resp |= (drv_gpio_val_get(TDI_PIN) << 7);
		DELAY_ROUTINE(CLK_DELAY_HALF);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
        DELAY_ROUTINE(CLK_DELAY);
	} while ((resp != BIST_RESP_CMD) && ((u32_t)(now() - t) < BIST_RESP_TIMEOUT));

    // read 32 bits response
    for(i = 0; i < 32; i++)
	{
        drv_gpio_val_set(TEST_CLK_PIN, 1);
		DELAY_ROUTINE(CLK_DELAY_HALF);
		*p_val |= (drv_gpio_val_get(TDI_PIN) << i);
		DELAY_ROUTINE(CLK_DELAY_HALF);
		drv_gpio_val_set(TEST_CLK_PIN, 0);
        DELAY_ROUTINE(CLK_DELAY);
	}

    DELAY_ROUTINE(CLK_DELAY);
	drv_gpio_val_set(TEST_CLK_PIN, 1);
	DELAY_ROUTINE(CLK_DELAY);
	drv_gpio_val_set(TEST_CLK_PIN, 0);

    return resp;
}
