
//******************************************** Включаемые файлы *********************************************

#include "submodule.h"
#include "fpga_cmn.h"
#include "dbg_printf.h"
#include "tcp.h"
#include "ltimer.h"

#include "lmcal_gpio.h"



//******************************************* Проверка параметров *******************************************

// Нет


//******************************************* Локальные константы *******************************************

// Таймаут ожидания занятости SPI в мс
#define SU_SPI_BUSY_TOUT            (1U)

// Таймаут ожидания завершения операции записи  в мс
// Для M93C46 "Erase or Write cycle time" = 5 ms (max)
#define SU_OP_WRITE_DONE_TOUT       (6U)

// Начальный адрес памяти субмодуля
#define SU_START_ADDR               (0x00)


//******************************************** Определения типов ********************************************


//************************************ Глобальные (публичные) переменные ************************************

// Нет


//************************************* Локальные (приватные) переменные ************************************

static bool b_init_done = false;

// Регистры управления CS
static u32 p_cs_reg[SU_SUBUNITS_MAX_QTY] = 
{
    FPGA_CLK_EN_CS_1_ON_MASK,
    FPGA_CLK_EN_CS_2_ON_MASK,
    FPGA_CLK_EN_CS_3_ON_MASK,
    FPGA_CLK_EN_CS_4_ON_MASK,
    FPGA_CLK_EN_CS_5_ON_MASK,
    FPGA_CLK_EN_CS_6_ON_MASK,
    FPGA_CLK_EN_CS_7_ON_MASK,
    FPGA_CLK_EN_CS_8_ON_MASK
};

// Промежуточный буфер для команды чтения TCP
static u8 p_mem_rdata[SU_MAX_MEM_SIZE];



//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************


//-----------------------------------------------------------------------------------------------------------
//! @brief      Проверка занятости SPI в течение указанного таймаута (в мс)
//! @param[in]  tout - таймаут в мс
//! @param[out] data - значение регистра приемника SPI
//! @retval     1 - выполнена, 0 - занят в течение таймаута
//-----------------------------------------------------------------------------------------------------------
static bool f_is_spi_busy(u16 tout, u8* data)
{
    bool res = false;
    t_ltimer tmr;
    u32 spi_rx_reg = 0U;

    ltimer_set_ms(&tmr, tout); 

    do {
        // чтение статус бита
        fpga_read_reg(FPGA_REG_SM_ADDR_SPI_RX, &spi_rx_reg);
        
        // Статус бит != 0
        if ((spi_rx_reg & FPGA_SPI_RX_STS_MASK) != false)
        {
            // Передача по SPI завершена
            res = true;

            if (data != NULL)
            {
                *data = (u8)(spi_rx_reg & FPGA_SPI_RX_DATA_MASK);
            }
            break;
        }
    } while((!ltimer_expired(&tmr)));

    return res;
}



//-----------------------------------------------------------------------------------------------------------
//! @brief      Выбор CS субмодуля для обмена по SPI
//! @param[in]  num - номер субмодуля (0..7)
//! @retval     -
//-----------------------------------------------------------------------------------------------------------
static void f_sel_cs(const u8 num)
{
    fpga_write_reg(FPGA_REG_SM_ADDR_CLKEN, p_cs_reg[num]);
}



//-----------------------------------------------------------------------------------------------------------
//! @brief       Чтение слова из микросхемы памяти субмодуля по SPI
//! @param[in]   addr - адрес ячейки памяти
//! @param[out]  data - данные ячейки памяти
//! @retval      -
//-----------------------------------------------------------------------------------------------------------
static void f_mem_read_word(u32 addr, u8* data)
{
    // Отправка команды на чтение байта
    fpga_write_reg(FPGA_REG_SM_ADDR_SPI_TX, (FPGA_SPI_OP_READ_DATA | (addr << 8U)) );

    // Ожидание завершения (чтение статус бита)
    f_is_spi_busy(SU_SPI_BUSY_TOUT, data);
}



//-----------------------------------------------------------------------------------------------------------
//! @brief      Разрешение\запрет записи в микросхему памяти субмодуля
//! @param[in]  state - управление (true - разрешить)
//! @retval     -
//-----------------------------------------------------------------------------------------------------------
static void f_mem_enable_wr(bool state)
{
    if (state == true)
    {
        // Отправка команды на разрешение записи
        fpga_write_reg(FPGA_REG_SM_ADDR_SPI_TX, FPGA_SPI_OP_WE);
        // Ожидание завершения (чтение статус бита)
        f_is_spi_busy(SU_SPI_BUSY_TOUT, NULL);
    }
    else
    {
        // Отправка команды на запрет записи
        fpga_write_reg(FPGA_REG_SM_ADDR_SPI_TX, FPGA_SPI_OP_WD);
        // Ожидание завершения (чтение статус бита)
        f_is_spi_busy(SU_SPI_BUSY_TOUT, NULL);
    }
}



//-----------------------------------------------------------------------------------------------------------
//! @brief      Проверка завершения записи в течение указанного таймаута (в мс)
//! @param[in]  tout - таймаут в мс
//! @retval     1 - выполнена, 0 - занят в течение таймаута
//-----------------------------------------------------------------------------------------------------------
static bool f_is_write_done(u16 tout)
{
    bool res = false;
    t_ltimer tmr;
    u8 spi_rx_data = 0U;

    ltimer_set_ms(&tmr, tout); 

    do {
        // Читаем ножку MISO до тех пор, пока она не станет = 1.
        // Команда отправки 18 нулей по SPI:
        fpga_write_reg(FPGA_REG_SM_ADDR_SPI_TX, (0x12000000)); 
        f_is_spi_busy(SU_SPI_BUSY_TOUT, &spi_rx_data);

        if (spi_rx_data == 0xFF)
        {
            res = true;
            break;
        }
    } while((!ltimer_expired(&tmr)));

    return res;
}



//-----------------------------------------------------------------------------------------------------------
//! @brief       Запись слова в микросхему памяти субмодуля по SPI
//! @param[in]   addr - адрес ячейки памяти
//! @param[in]   data - данные ячейки памяти
//! @retval      -
//-----------------------------------------------------------------------------------------------------------
static void f_mem_write_word(u32 addr, u8 data)
{
    // Отправка команды на запись байта
    fpga_write_reg(FPGA_REG_SM_ADDR_SPI_TX, (FPGA_SPI_OP_WRITE_MASK | (addr << 8U) | data));
    // Ожидание завершения
    f_is_spi_busy(SU_SPI_BUSY_TOUT, NULL);

    // Проверка статуса записи
    f_is_write_done(SU_OP_WRITE_DONE_TOUT);
}



// Команда записи в память субмодуля
static tcp_resp_errs_t f_cmd_write_to_subm(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;

    // Пишем всю память субмодуля
    err = (length != SU_MAX_MEM_SIZE) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    TCP_CHECK_VAL(err);
    err = (param > SU_SUBUNITS_MAX_QTY) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    TCP_CHECK_VAL(err);

    subm_write((u8)param, SU_START_ADDR, (u8*)buf, (u8)length);

    TCP_EXIT;
    return err;
}



// Команда чтения памяти субмодуля
static tcp_resp_errs_t f_cmd_read_subm(u32 param, u32 resp_len, u8** buf,  u32* length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
    L_UNUSED(param);
    L_UNUSED(resp_len);

    // Читаем всю память субмодуля
    err = (resp_len != SU_MAX_MEM_SIZE) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    TCP_CHECK_VAL(err);
    err = (param > SU_SUBUNITS_MAX_QTY) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    TCP_CHECK_VAL(err);

    // Выполнить чтение в промежуточный буфер и передать указатель на буфер верхнему уровню
    subm_read((u8)param, SU_START_ADDR, p_mem_rdata, resp_len);

    *buf = p_mem_rdata;
    *length = resp_len;

    TCP_EXIT;
    return err;
}



//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************


//-----------------------------------------------------------------------------------------------------------
fun_res_t subm_init(void)
{
    fun_res_t err = ERR_OK;
    err = L_ASSERT(false == b_init_done);
    L_CHECK_ERR(err);
    err = L_ASSERT(true == fpga_is_init());
    L_CHECK_ERR(err);

    b_init_done = true;

    // Инициализация команд TCP для текущего модуля

    // Команда записи в память субмодуля
    tcp_cmd_set_rx_callback(TCP_CMD_SM_WRITE_MEM, f_cmd_write_to_subm);

    // Команда чтения памяти субмодуля
    tcp_cmd_set_tx_callback(TCP_CMD_SM_READ_MEM, f_cmd_read_subm);


    //! trig
    // lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, true);

    //! test
    #if 1
    extern void subm_tst_get_info(void);
    extern void subm_tst_read_write_erase(void);

    // Чтение информации из встроенной памяти для идентификации установленных субмодулей.
    subm_tst_get_info();
    #endif

    L_EXIT;
    return err;
}




//-----------------------------------------------------------------------------------------------------------
fun_res_t subm_read(subm_num_t num, 
                           u8 addr,
                           u8* rdata,
                           u8  size)
{
    fun_res_t err = ERR_OK;

    err = L_ASSERT(true == b_init_done);
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_SUBUNITS_MAX_QTY > num));
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_MAX_MEM_SIZE > addr));
    L_CHECK_ERR(err);
    err = L_ASSERT(rdata != NULL);
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_MAX_MEM_SIZE >= size));
    L_CHECK_ERR(err);

    u16 buf_cnt = 0U;
    u32 addr_tmp = 0U;
    
    // Настройка (выбор субмодуля)
    f_sel_cs(num);
            
    addr_tmp = addr;

    while(buf_cnt != size)
    {
        // Проверка выхода за диапазон адресов
        if (addr_tmp >= SU_MAX_MEM_SIZE)
        {
            // Ошибка использования функции
            break;
        }

        // Запись значения в буфер
        f_mem_read_word(addr_tmp, &rdata[buf_cnt]);

        buf_cnt++;
        addr_tmp++;
    } // while

    L_EXIT;
    return err;
}



//-----------------------------------------------------------------------------------------------------------
fun_res_t subm_write(subm_num_t num, 
                            u8 addr,
                            u8* wdata,
                            u8  size)
{
    fun_res_t err = ERR_OK;

    err = L_ASSERT(true == b_init_done);
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_SUBUNITS_MAX_QTY > num));
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_MAX_MEM_SIZE > addr));
    L_CHECK_ERR(err);
    err = L_ASSERT(wdata != NULL);
    L_CHECK_ERR(err);
    err = L_ASSERT((SU_MAX_MEM_SIZE >= size));
    L_CHECK_ERR(err);

    u16 buf_cnt = 0U;
    u32 addr_tmp = 0U;
    
    // Настройка (выбор субмодуля)
    f_sel_cs(num);
            
    addr_tmp = addr;

    // Разрешение записи
    f_mem_enable_wr(true);
    
    while(buf_cnt != size)
    {
        // Проверка выхода за диапазон адресов
        if (addr_tmp >= SU_MAX_MEM_SIZE)
        {
            // Ошибка использования функции
            break;
        }
        
        // Запись ячейки
        f_mem_write_word(addr_tmp, wdata[buf_cnt]);

        buf_cnt++;
        addr_tmp++;
    } // while

    // Запрет записи
    f_mem_enable_wr(false);
    
    L_EXIT;
    return err;
}



//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
