#include "fpgaflashoperation.h"
#include <qstring.h>

#define DISABLE_TIMEOUT

//***********************************************************************
// Reset Transaction
// Input parameters:
// Returns:
//
// Note 1: bit 0 reset to 0, no request begin transaction
//         bit 1 reset to 0, no start previous transaction
//***********************************************************************
void FlashOperation::resetTransaction()
{
    cx.ctrl[r_spicr]=   //0
                spicr_req_no
                |(spicr_start_no<<1);
}

//***********************************************************************
// Prepare Transaction
// Input parameters:
// Returns:
//
// Note 1: bit 0 reset to 1, It requires to the IP to begin with a transaction
//         bit 1 reset to 0, no start previous transaction
//***********************************************************************
void FlashOperation::prepareTransaction()
{
    cx.ctrl[r_spicr]=   //1
                spicr_req_begin_trans
                |(spicr_start_no<<1);
}

//***********************************************************************
// Start Transaction
// Input parameters:
// Returns:
//
// Note 1: bit 0 reset to 1, It requires to the IP to begin with a transaction
//         bit 1 reset to 1, It starts a previously required transaction
//***********************************************************************
void FlashOperation::startTransaction()
{
    cx.ctrl[r_spicr]= //3;
                spicr_req_begin_trans
                |(spicr_start_prev_trans<<1);
}

//***********************************************************************
// End Transaction
// Input parameters:
// Returns:
//
// Note 1: bit 0 reset to 1, It requires to the IP to begin with a transaction
//         bit 1 reset to 1, //no start previous transaction
//***********************************************************************
void FlashOperation::endTransaction()
{
    cx.ctrl[r_spicr]=   //1;
                spicr_req_begin_trans
                |(spicr_start_no<<1);
}


//*****************************************
// INITIALIZE operations, 2.4.1.1 Inizializzazione
// Inizialize mode register, set engine
// Input parameters:    engine_ = fpga flash engine
//                      a_moder_spimode = SPI mode x1 mode,x2 mode,x4 mode
//                      a_moder_byteaddress = 3 bytes address/4 bytes address
//                      a_moder_qspiport = primary Spi Port/secondary Spi Port
// Returns:
//
// Note 1: set register r_moder with correct configuration
//***********************************************************************
void FlashOperation::init(FpgaFlashEngine* engine_,
                    reg_moder_spimode_t a_moder_spimode,
                    reg_moder_byteaddress_t a_moder_byteaddress,
                    reg_moder_qspiport_t a_moder_qspiport)
{
    memset(&cx, 0, sizeof cx);
    engine=engine_;

    moder_spimode = a_moder_spimode;
    moder_byteaddress = a_moder_byteaddress;
    moder_qspiport = a_moder_qspiport;

    //configure mode registert
    cx.ctrl[r_moder]=
                    moder_spimode<<0
                    |(moder_byteaddress<<2)
                    |(moder_qspiport<<3);
}

//*****************************************
// INITIALIZE operations, 2.4.1.1 Inizializzazione
// Inizialize mode register, set engine
// Input parameters:    a_moder_spimode = SPI mode x1 mode,x2 mode,x4 mode
//                      a_moder_byteaddress = 3 bytes address/4 bytes address
//                      a_moder_qspiport = primary Spi Port/secondary Spi Port
// Returns:
//
// Note 1: set register r_moder with correct configuration
//***********************************************************************
void FlashOperation::init(reg_moder_spimode_t a_moder_spimode,
                    reg_moder_byteaddress_t a_moder_byteaddress,
                    reg_moder_qspiport_t a_moder_qspiport)
{
    memset(&cx, 0, sizeof cx);

    moder_spimode = a_moder_spimode;
    moder_byteaddress = a_moder_byteaddress;
    moder_qspiport = a_moder_qspiport;

    //configure mode register
    cx.ctrl[r_moder]=
                moder_spimode<<0
                |(moder_byteaddress<<2)
                |(moder_qspiport<<3);
}

//***********************************************************************
// Confirm flash operation
// Input parameters:
// Returns: true = no errors, ready for another command
//          false = error, there is a problem or not responds.
//
// Note 1: set register r_moder with correct configuration
//***********************************************************************
bool FlashOperation::confirmFlashOperation()
{
    // Set the timeout value [system ticks]
    // max sector erase time from data sheet is 3[s]

    fif->log(type_log_info, "-> MACRO - Confirm flash operation");

    uint8_t sts = 0;

    //unsigned long t_start=fif->timeNowMs();
    //unsigned long t_stop=fif->timeNowMs();

    //char st[32];

    //int count = 5;
    do
    {
        cx.ctrl[r_addrr]=0;
        cx.ctrl[r_nbr]=0x01;
        bool ok=sendCommand(cmdr_read_st_reg,           //=0x05, read status register
                            //&st[0],        //SPI Receive FIFO Register
                            &cx.ctrl[r_spirr],
                            DIM_DATA_BUFFER ,                       //Number of elements into the "TX FIFO"
                            cmd_read);
        if (!ok)
        {
            fif->log(type_log_error, "MACRO - Confirm flash operation, ERROR");
            return false;
        }
        //sts=status_register[0];
        sts = cx.ctrl[r_spirr];

        //count--;
        //fif->log(0, "MACRO - Confirm flash operation read %d times, read 0x%02X", count, sts);
        fif->log(type_log_info, "MACRO - Confirm flash operation read 0x%02X", sts);
        // Repeat until bit #0 (read from FLASH) is != 0    , bit di "Write In Progress"
    }while ((sts & 0x01) != 0);//((count==0) && ((sts & 0x01) != 0));

    //t_stop = fif->timeNowMs();

    /*if (count==0)
    {
        fif->log(0, "TIMEOUT - wait until operation not terminated");
        return false;
    }*/

    //unsigned long t_now=(t_stop-t_start)/1000.0;

    //fif->log(0, "MACRO - Confirm flash operation, in %lusecs", int(t_now));

    return true;
}

//***********************************************************************
// Core reset
// Input parameters:
// Returns:         true = no error
//                  false = error into reset operation
//
// Note 1: set register r_srr with value 0x12ABCDEF, wait 10 us and set
// register r_srr with value 0
//***********************************************************************
bool FlashOperation::reset()
{
    //fif->notify(FpgaFlashInterface::s_reset, 1, "Start software reset.");

    fif->log(type_log_info, "-> MACRO - Resetting FPGA interface");

    memset(&cx, 0, sizeof cx);

    cx.ctrl[r_srr]=SRR_KEYCODE;     //0x12ABCDEF;

    if (!fpgaWrite(r_base, &cx.ctrl, DIM_DATA_BUFFER,"reset"))
    {
        fif->notify(s_reset, -1, "ERROR, reset not completed");
        fif->log(type_log_error, "MACRO - ERROR, reset not completed.");
        return false;
    }

    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION);

    cx.ctrl[r_srr]=0;

    if (!fpgaWrite(r_base, &cx.ctrl, DIM_DATA_BUFFER,"reset"))
    {
        fif->notify(s_reset, -1, "ERROR, reset not completed");
        fif->log(type_log_error, "MACRO - ERROR - reset not completed.");
        return false;
    }

    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION*5);

    fif->notify(s_reset, -1, "Ok, reset comunication completed.");

    fif->log(type_log_success, "MACRO - DONE - reset");
    return true;
}

//***********************************************************************
// Fpga write
// Input parameters:    address = address to write
//                      data = buffer to write
//                      len = buffer's length
//                      mess = message to write into debug log
// Returns:             true, no error
//                      false, generic error into write function
//***********************************************************************
bool FlashOperation::fpgaWrite(uint32_t address, const void* data, unsigned int len, const char* mess)
{

    fif->log(type_log_info, "WRITE: 0x%X*%u CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X CMD=(%s)(%s)",
             address*4,
             len,
             cx.ctrl[r_cmdr],
             cx.ctrl[r_moder],
             cx.ctrl[r_addrr],
             cx.ctrl[r_nbr],
             cx.ctrl[r_spicr],
             cx.ctrl[r_srr],
             retCommand(cx.ctrl[r_cmdr]),
             mess);

    bool ok=fif->rtgWrite(address*4, data, len);

    return ok;
}

//***********************************************************************
// Fpga Read
// Input parameters:    address = address to read
//                      data = ouput buffer
//                      len = buffer's length
//                      mess = message to write into debug log
// Returns:             true, no error
//                      false, generic error into read function
//***********************************************************************
bool FlashOperation::fpgaRead(uint32_t address, void* data, unsigned int len, const char* mess)
{
    //fif->rtgWaitEvent(WAIT_TIME_MS);
    fif->log(type_log_info, "READ request: 0x%X*%u CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)(%s)",
             address*4,
             len,
             cx.ctrl[r_cmdr],
             cx.ctrl[r_moder],
             cx.ctrl[r_addrr],
             cx.ctrl[r_nbr],
             cx.ctrl[r_spicr],
             cx.ctrl[r_srr],
             cx.ctrl[r_spisr],
             retCommand(cx.ctrl[r_cmdr]),
             mess);

    bool ok=fif->rtgRead(address*4, data, len);

    fif->log(type_log_info, "READ response: 0x%X*%u CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)(%s)",
             address*4,
             len,
             cx.ctrl[r_cmdr],
             cx.ctrl[r_moder],
             cx.ctrl[r_addrr],
             cx.ctrl[r_nbr],
             cx.ctrl[r_spicr],
             cx.ctrl[r_srr],
             cx.ctrl[r_spisr],
             retCommand(cx.ctrl[r_cmdr]),
             mess);

   return ok;
}

//***********************************************************************
// Fpga Read
// Input parameters:    address = address to read
//                      data = ouput buffer
//                      len = buffer's length
//                      mess = message to write into debug log
// Returns:             true, no error
//                      false, generic error into read function
//***********************************************************************
bool FlashOperation::fpgaReadFPGAFWIBC(uint32_t address, void* data, unsigned int len, const char* mess)
{
    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION);
    bool ok=fif->rtgReadDirect(address, data, len);


    fif->log(type_log_info, "READ: 0x%X*%u CMD=%s",
             address,
             len,
             mess);

    return ok;
}

//***********************************************************************
// Send command
// Input parameters:    cmd = address to send command
//                      data = buffer that contains register value
//                      len = buffer's length
//                      type_cmd = nc/write/read
// Returns:             false = error in sendCommand function
//                      true = No error
//***********************************************************************
bool FlashOperation::sendCommand(unsigned int cmd, void *data, unsigned int len,  type_cmd_t type_cmd)
{
    bool res = false;

    //1. Set the MODER register.
    //2. Set the CMDR register.
    //3. Set the ADDRR register (this register setting depends by the command set into CMDR. Refer to Table 25 to know if this setting is requested or not).
    //4. Set the NBR register (this register setting depends by the command set into CMDR. Refer to Table 25 to know if this setting is requested or not)
    cx.ctrl[r_cmdr]=cmd;

    //5. Set the "SPI request" bit into the SPICR register
    prepareTransaction();
    res = fpgaWrite(r_base, cx.ctrl, len,"prepare transaction");

    if (!res)
    {
        fif->log(type_log_error, "ERROR  - fpgaWrite - prepare transaction");
        return false;
    }

    //6. Wait until "SPI core Acknowledge" bit into the SPISR is raised by the core.
    //unsigned int spisr = 0;
    uint32_t spisr = 0;

    unsigned long t_start=fif->timeNowMs();
    unsigned long t_stop=fif->timeNowMs();

    bool timeout = false;

    bool is_spi_core_ack = false;
    bool is_spi_core_error = false;
    counterRepeat = 0;

    do
    {
        res = fpgaRead(r_spi_status_base, &cx.ctrl[r_spi_status_base], len,"wait until Ack SPI is set, first");

        if (!res)
        {
            fif->log(type_log_error, "ERROR  - fpgaRead - wait until Ack SPI is set, first");
            return false;
        }

        spisr = cx.ctrl[r_spisr];

        //QString tst = QString("fpgaRead SPISR=%1 - wait until Ack SPI is set, first").arg(spisr, 0, 16);
        //fif->log(type_log_debug, tst.toStdString().c_str());

        t_stop = fif->timeNowMs();
        timeout = !(t_stop-t_start<TIMEOUT_SPI);

        is_spi_core_ack = decodeSPISR(spisr, spi_core_ack);
        is_spi_core_error = decodeSPISR(spisr, spi_core_error);
        if(!is_spi_core_ack && !is_spi_core_error)
        {
            incrCounterRepeat();

            QString msg = QString("REPEAT %1  - fpgaRead - wait until Ack SPI is set, first").arg(counterRepeat);
            fif->log(type_log_error, msg.toStdString().c_str());
            if (WAIT_TIME_BEFORE_NEW_RD>0)
                fif->rtgWaitEvent(WAIT_TIME_BEFORE_NEW_RD);
        }
#ifdef DISABLE_TIMEOUT
    } while (!is_spi_core_ack && !is_spi_core_error);
    timeout = false;
#else
    } while (!is_spi_core_ack && !is_spi_core_error &&  (!timeout));
#endif

    //7. Check "SPI Core Error" bit is not raised (core error can be raised if currently requested command is not supported).
    if (decodeSPISR(spisr, spi_core_error))
    {
        //error in spi transaction
        fif->log(type_log_error, "ERROR: SPI_CORE  CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)",
                 cx.ctrl[r_cmdr],
                 cx.ctrl[r_moder],
                 cx.ctrl[r_addrr],
                 cx.ctrl[r_nbr],
                 cx.ctrl[r_spicr],
                 cx.ctrl[r_srr],
                 cx.ctrl[r_spisr],
                 retCommand(cx.ctrl[r_cmdr]));

        decodeSPIRError(spisr);
        return false;
    }

    if (timeout)
    {
        fif->log(type_log_error, "TIMEOUT wait until Ack SPI is set, first ");
        return false;
    }



    //8. In case currently requested command requires to fill the TX FIFO (refers to Table 25), fill the trasmission buffer by writing data @ the SPIDTR register
    if(type_cmd==cmd_write)
    {
        //riempio il buffer da scrivere
        res = fpgaWrite(adr_fifo, data, len, "write buffer into fifo");
        if (!res)
        {
            fif->log(type_log_error, "ERROR  - fpgaWrite - write buffer into fifo");
            return false;
        }
    }

    //9. Set the "SPI start" bit into the SPICR register (maintaining the "SPI request" bit set)
    startTransaction();
    res = fpgaWrite(r_base, cx.ctrl, len, "set SPI Start, start transaction ");

    if (!res)
    {
        fif->log(type_log_error, "ERROR  - fpgaWrite - set SPI Start, start transaction");
        return false;
    }

    //10. Wait until "SPI busy" into the SPISR is set
    t_start=fif->timeNowMs();
    timeout = false;

    counterRepeat = 0;

    bool is_spi_core_busy = false;
    do
    {
        res = fpgaRead(r_spi_status_base, &cx.ctrl[r_spi_status_base], len, "wait until busy SPI is set, first");

        if (!res)
        {
            fif->log(type_log_error, "ERROR  - fpgaRead - wait until busy SPI is set, first");
            return false;
        }

        spisr = cx.ctrl[r_spisr];
        t_stop=fif->timeNowMs();
        timeout = !(t_stop-t_start<TIMEOUT_SPI);

        //QString tst = QString("fpgaRead SPISR=%1 - wait until busy SPI is set, first").arg(spisr, 0, 16);
        //fif->log(type_log_debug, tst.toStdString().c_str());

        is_spi_core_busy = decodeSPISR(spisr, spi_core_busy);
        is_spi_core_error = decodeSPISR(spisr, spi_core_error);
        if(is_spi_core_busy && !is_spi_core_error)
        {
            incrCounterRepeat();

            QString msg = QString("REPEAT %1  - fpgaRead - wait until busy SPI is set, first").arg(counterRepeat);
            fif->log(type_log_error, msg.toStdString().c_str());
            if (WAIT_TIME_BEFORE_NEW_RD>0)
                fif->rtgWaitEvent(WAIT_TIME_BEFORE_NEW_RD);
        }

#ifdef DISABLE_TIMEOUT
    } while (is_spi_core_busy && !is_spi_core_error);
    timeout = false;
#else
    } while (is_spi_core_busy && !is_spi_core_error &&  (!timeout));
#endif

    if (decodeSPISR(spisr, spi_core_error))
    {
        //error in spi transaction
        fif->log(type_log_error, "ERROR: SPI_CORE  CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)",
                 cx.ctrl[r_cmdr],
                 cx.ctrl[r_moder],
                 cx.ctrl[r_addrr],
                 cx.ctrl[r_nbr],
                 cx.ctrl[r_spicr],
                 cx.ctrl[r_srr],
                 cx.ctrl[r_spisr],
                 retCommand(cx.ctrl[r_cmdr]));

        decodeSPIRError(spisr);

        return false;
    }

    if (timeout)
    {
        fif->log(type_log_error, "TIMEOUT wait until busy SPI is set, first ");
        return false;
    }

    //11. Reset "SPI start" bit
    endTransaction();
    res = fpgaWrite(r_base, cx.ctrl, len, "reset SPI Start");
    if (!res)
    {
        fif->log(type_log_error, "ERROR  - fpgaWrite - reset SPI Start");
        return false;
    }

    //12. Wait until "SPI busy" into the SPISR is reset
    t_start=fif->timeNowMs();
    timeout = false;
    is_spi_core_busy = false;
    counterRepeat = 0;
    do
    {
        res = fpgaRead(r_spi_status_base, &cx.ctrl[r_spi_status_base], len, "wait until busy SPI is reset");
        if (!res)
        {
            fif->log(type_log_error, "ERROR  - fpgaRead - wait until busy SPI is reset");
            return false;
        }
        spisr = cx.ctrl[r_spisr];

        //QString tst = QString("fpgaRead SPISR=%1 - wait until busy SPI is reset").arg(spisr, 0, 16);
        //fif->log(type_log_debug, tst.toStdString().c_str());

        t_stop=fif->timeNowMs();
        timeout = !(t_stop-t_start<TIMEOUT_SPI);

        is_spi_core_busy = decodeSPISR(spisr, spi_core_busy);
        is_spi_core_error = decodeSPISR(spisr, spi_core_error);
        if(is_spi_core_busy && !is_spi_core_error)
        {
            incrCounterRepeat();

            QString msg = QString("REPEAT %1  - fpgaRead - wait until busy SPI is reset").arg(counterRepeat);
            fif->log(type_log_error, msg.toStdString().c_str());
            if (WAIT_TIME_BEFORE_NEW_RD>0)
                    fif->rtgWaitEvent(WAIT_TIME_BEFORE_NEW_RD);
        }

#ifdef DISABLE_TIMEOUT
    } while (is_spi_core_busy && !is_spi_core_error);
    timeout = false;
#else
    } while (is_spi_core_busy && !is_spi_core_error &&  (!timeout));
#endif

    //13. Check "SPI Core Error" bit is not raised (core error can be raised if currently requested command generates an underflow or overflow error).
    if (decodeSPISR(spisr, spi_core_error))
    {
        //error in spi transaction
        //error in spi transaction
        fif->log(type_log_error, "ERROR: SPI_CORE  CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)",
                 cx.ctrl[r_cmdr],
                 cx.ctrl[r_moder],
                 cx.ctrl[r_addrr],
                 cx.ctrl[r_nbr],
                 cx.ctrl[r_spicr],
                 cx.ctrl[r_srr],
                 cx.ctrl[r_spisr],
                 retCommand(cx.ctrl[r_cmdr]));

        decodeSPIRError(spisr);

        return false;
    }

    if (timeout)
    {
        fif->log(type_log_error, "TIMEOUT wait until busy SPI is reset");
        return false;
    }

    //14. In case a read command has been requested, readout read data @ SPIDRR
    //READDATA IF NECESSARY
    bool is_rx_fifo_valid= false;
    if(type_cmd==cmd_read)
    {
        //controllo se ho un dato valido nella fifo
        if (decodeSPISR(spisr, rx_fifo_valid))
        {
            counterRepeat = 0;

            do
            {
                //leggo il buffer che mi è stato spedito
                res = fpgaRead(r_base+r_spirr, data, len, "read data from FIFO");
                if (!res)
                {
                    fif->log(type_log_error, "ERROR  - fpgaRead - read data from FIFO");
                    return false;
                }

                if (WAIT_TIME_BETWEEN_2_OP>0)
                    fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_OP);

                //leggo lo stato del registro spi per capire se ho ancora dati validi da leggere
                res = fpgaRead(r_spi_status_base, &cx.ctrl[r_spi_status_base], len, "check if rx is still valid");
                if (!res)
                {
                    fif->log(type_log_error, "ERROR  - fpgaRead - check if rx is still valid");
                    return false;
                }
                spisr = cx.ctrl[r_spisr];

                //QString tst = QString("fpgaRead SPISR=%1 - check if rx is still valid").arg(spisr, 0, 16);
                //fif->log(type_log_debug, tst.toStdString().c_str());

                is_rx_fifo_valid = decodeSPISR(spisr, rx_fifo_valid);
                is_spi_core_error = decodeSPISR(spisr, spi_core_error);
                if (is_rx_fifo_valid && !is_spi_core_error)
                {
                    incrCounterRepeat();
                    QString msg = QString("REPEAT %1 - fpgaRead - check if rx is still valid").arg(counterRepeat);
                    fif->log(type_log_error, msg.toStdString().c_str());
                    if (WAIT_TIME_BEFORE_NEW_RD>0)
                        fif->rtgWaitEvent(WAIT_TIME_BEFORE_NEW_RD);
                }
            } while (is_rx_fifo_valid && !is_spi_core_error);  //ciclo per controllare che ilbit rx valid sia a 1

            if (decodeSPISR(spisr, spi_core_error))
            {
                //error in spi transaction
                //error in spi transaction
                fif->log(type_log_error, "ERROR: SPI_CORE  CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)",
                         cx.ctrl[r_cmdr],
                         cx.ctrl[r_moder],
                         cx.ctrl[r_addrr],
                         cx.ctrl[r_nbr],
                         cx.ctrl[r_spicr],
                         cx.ctrl[r_srr],
                         cx.ctrl[r_spisr],
                         retCommand(cx.ctrl[r_cmdr]));

                decodeSPIRError(spisr);

                return false;
            }

        }
    }

    //15. De-assert "SPI request" bit.
    resetTransaction();
    res = fpgaWrite(r_base, cx.ctrl, len, "reset SPI request");
    if (!res)
    {
        fif->log(type_log_error, "ERROR  - fpgaWrite - reset SPI request");
        return false;
    }

    //16. Wait until "SPI core Acknowledge" bit into the SPISR is de-asserted by the core
    t_start=fif->timeNowMs();
    timeout = false;
    is_spi_core_ack = false;
    counterRepeat = 0;
    do
    {
        res = fpgaRead(r_spi_status_base, &cx.ctrl[r_spi_status_base], len, "wait if spi core ack de-asserted, second");
        if (!res)
        {
            fif->log(type_log_error, "ERROR  - fpgaRead - wait if spi core ack de-asserted, second");
            return false;
        }
        spisr = cx.ctrl[r_spisr];
        t_stop=fif->timeNowMs();
        timeout = !(t_stop-t_start<TIMEOUT_SPI);

        //QString tst = QString("fpgaRead SPISR=%1 - wait if spi core ack de-asserted, second").arg(spisr, 0, 16);
        //fif->log(type_log_debug, tst.toStdString().c_str());

        is_spi_core_ack = decodeSPISR(spisr, spi_core_ack);
        is_spi_core_error = decodeSPISR(spisr, spi_core_error);
        if (!is_spi_core_ack && !is_spi_core_error)
        {
            incrCounterRepeat();
            QString msg = QString("REPEAT %1 - fpgaRead - wait if spi core ack de-asserted, second").arg(counterRepeat);
            fif->log(type_log_error, msg.toStdString().c_str());
            if (WAIT_TIME_BEFORE_NEW_RD>0)
                fif->rtgWaitEvent(WAIT_TIME_BEFORE_NEW_RD);
        }
#ifdef DISABLE_TIMEOUT
    } while (!is_spi_core_ack && !is_spi_core_error);
    timeout = false;
#else
    } while (!is_spi_core_ack && !is_spi_core_error &&  (!timeout));
#endif

    if (decodeSPISR(spisr, spi_core_error))
    {
        //error in spi transaction
        fif->log(type_log_error, "ERROR: SPI_CORE  CMD=0x%02X MOD=0x%02X ADR=0x%02X LEN=0x%02X SPICR=0x%02X SRR=0x%02X SPISR=0x%02X CMD=(%s)",
                 cx.ctrl[r_cmdr],
                 cx.ctrl[r_moder],
                 cx.ctrl[r_addrr],
                 cx.ctrl[r_nbr],
                 cx.ctrl[r_spicr],
                 cx.ctrl[r_srr],
                 cx.ctrl[r_spisr],
                 retCommand(cx.ctrl[r_cmdr]));

        decodeSPIRError(spisr);

        return false;
    }

    if (timeout)
    {
        fif->log(type_log_error, "TIMEOUT wait if spi core ack");
        return false;
    }

    fif->log(type_log_success, "MACRO FINISHED");

    return true;
}

//***********************************************************************
// Read Flash ID
// Input parameters:
// Returns:             "" if no able to read flash ID
//                      "<flashid string" contains the flash id
// Note 1: ATTENTION! this fuction is required to Unlock FPGA interface
// Note 2: r_moder = 0x08 if secondary FLASH, 0x00 if primary FLASH
//***********************************************************************
bool FlashOperation::readFlashID(reg_moder_qspiport_t spi_port)
{
    fif->log(type_log_info, "-> MACRO - Unlocking FPGA interface, read FLASH ID, first step");

    char flash_id[32];      //flash id
    char fw_id[32];         //fpga firmware IBC

    memset(flash_id, 0x0, 32);
    memset(fw_id, 0x0 , 32);
    memset(FLASH_ID, 0x0, FLASH_ID_SIZE);

    init(moder_spimode_x1mode, moder_byteaddress/*bytes3address*/, spi_port);

    cx.ctrl[r_nbr] = FLASH_ID_NUMBER_OF_BYTES;  //dimension of flash id in bytes

    if (!sendCommand(cmdr_read_id, &FLASH_ID[0],DIM_DATA_BUFFER,cmd_read))//cmd_read_flashID))                                          //Command Register: "x9E"
    {
        fif->notify(s_read_id, -1, "ERROR, read Flash ID, first step, NOT completed");
        return false;
    }

    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION);

    fif->log(type_log_info, "MACRO - DONE - read FLASH ID, first step");

    //(NOTA: a causa di un funzionamento anomalo della primitiva STARTUPE dichiarata dalla xilinx stessa,
    //la prima operazione è da considerare dummy e va ripetuta una seconda volta per essere eseguita).

    fif->log(type_log_info, " ->MACRO - Unlocking FPGA interface, read FLASH ID, second step");

    if (!sendCommand(cmdr_read_id, &FLASH_ID[0],DIM_DATA_BUFFER,cmd_read))      //Command Register: "x9E"
    {
        fif->notify(s_read_id, -1, "ERROR, read Flash ID, second step, NOT completed");
        return false;
    }

    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION);

    sprintf (flash_id, "0x%8.8lX", FLASH_ID[0]);
    fif->log(type_log_info, "FPGA flash ID = %s", flash_id);

    memset(FLASH_ID, 0, FLASH_ID_SIZE);

    if (!readFPGAFWIBC(&FLASH_ID[0], DIM_DATA_BUFFER))
    {
        return false;
    }

    sprintf (fw_id, "%.4s%.4s%.4s", (char *)&FLASH_ID[0], (char *)&FLASH_ID[1], (char *)&FLASH_ID[2]);
    fif->log(type_log_info, "FPGA fw IBC = %s", fw_id);

    fif->notify(s_end, -1, "Ok, read FlashID %s, FPGA fw IBC %s completed", flash_id, fw_id);

    fif->log(type_log_success, "MACRO - DONE - read FLASH ID, second step");

    //check if read flash id is valid   18 for 128 mbit, 19 for 256 mbit
    QString identificator = QString("%1").arg(flash_id);
    if ((!identificator.contains("18", Qt::CaseInsensitive)) && (!identificator.contains("19", Qt::CaseInsensitive)))
    {
        //fif->log(type_log_error, "ERROR, Flash ID is not valid!");
        fif->notify(s_error, -1, "ERROR, Flash ID is not valid!");
        return false;
    }

    return true;
}

//***********************************************************************
// Sets the FPGA FLASH addressing mode
// Input parameters:    spi_port = primary/secondary spi port
//                      byteaddress = 3/4 bytes address
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::setByteAddress(reg_moder_qspiport_t spi_port,reg_moder_byteaddress_t byteaddress)
{
    fif->log(type_log_info, "-> MACRO - Sets the FPGA FLASH addressing mode");

    bool res = false;
    //abilito la scrittura
    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

    //setEnableMsgBlk(true);

    init(moder_spimode_x1mode, byteaddress, spi_port);

    unsigned int cmd = 0;

    if (byteaddress==bytes4address)
    {
        //enter 4b address mode
        cmd = cmdr_4b_add_enter;
    }
    else
    {
        //exit 4b address mode
        cmd = cmdr_4b_add_exit;
    }

    cx.ctrl[r_nbr] = 0;

    if (!sendCommand(cmd, 0, DIM_DATA_BUFFER, cmd_nc))
    {
        fif->log(type_log_error, "MACRO - ERROR - Sets the FPGA FLASH addressing mode");
        return false;
    }

    fif->rtgWaitEvent(WAIT_TIME_INIT_OPERATION);

    fif->log(type_log_success, "MACRO - DONE - Sets the FPGA FLASH addressing mode");
    return true;
}

//***********************************************************************
// Erase sector
// Input parameters:    sector = n. sector to erase
//
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::eraseSector(unsigned long sector_address)
{
    fif->log(type_log_info, "-> MACRO - Sector address to erase : @0x%lX", sector_address);

    bool res = false;
    //abilito la scrittura
    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

    cx.ctrl[r_addrr] = sector_address;
    cx.ctrl[r_nbr]   = 0;

    //sendCommand(0xD8);

    res=sendCommand(cmdr_erase_sector,0,DIM_DATA_BUFFER,cmd_nc);     //0xd8, sector erase
    if (!res)
    {
        fif->log(type_log_error, "MACRO - ERROR - Erase sector");
        return false;
    }
    fif->log(type_log_success, "MACRO - DONE - Erase sector");

    //fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);

    return true;
}

//***********************************************************************
// Write enable
// Input parameters:    enable = if true enable write, otherwise disable write
//
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::writeEnable(bool enable)
{
    bool res = false;
    cx.ctrl[r_addrr]=0;
    cx.ctrl[r_nbr]=0;

    if (enable)
        fif->log(type_log_info, "-> SUB - Write Enable");
    else
        fif->log(type_log_info, "-> SUB - Write Disable");

    res = sendCommand(enable ? cmdr_write_enable : cmdr_write_disable, 0, DIM_DATA_BUFFER,cmd_nc);
    if (!res)
    {
        if (enable)
            fif->log(type_log_error, "ERROR - Write Enable");
        else
            fif->log(type_log_error, "ERROR - Write Disable");
        return false;
    }

    if (enable)
        fif->log(type_log_success, "SUB - Write Enable, DONE");
    else
        fif->log(type_log_success, "SUB - Write Disable, DONE");

    if (WAIT_TIME_BETWEEN_2_MACRO>0)
        fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);

    return true;
}


//***********************************************************************
// Write block
// Input parameters:    address = address where buffer is must be write down
//                      data = buffer to write
//                      len = lenght of the buffer
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::writeBlock(unsigned int address, const char* data, unsigned int len)
{
    bool res = false;
    //abilito la scrittura
    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

    unsigned int w_len=len/4;
    unsigned int w_len_buffer = DIM_DATA_BUFFER/4;

    const uint32_t* p=reinterpret_cast<const uint32_t*>(data);

    if (len<DIM_DATA_BUFFER)
    {
        for(unsigned int i=0; i<w_len; ++i)
            cx.buffer[i]=p[i];
        for (unsigned j =w_len;j<w_len_buffer; ++j )
            cx.buffer[j]=0xFFFFFFFF;
    }
    else
    {
        for(unsigned int i=0; i<w_len; ++i)
        {
            cx.buffer[i]=p[i];
        }
    }

    {//Write block
        cx.ctrl[r_addrr]=address;           //5.5.1.3 Address Register (ADDRR), Flash Address on which core will access.
        cx.ctrl[r_nbr]=DIM_DATA_BUFFER;  //len               //5.5.1.4 Number of Bytes Register (NBR),Number of bytes on which core shall transact.
        //cx.ctrl[r_spicr]=1;                 //5.5.1.6 SPI Control Register (SPICR), It starts a previously required transaction
        //cx.ctrl[r_spidtr]=cx.buffer[0];     //5.5.1.7 SPI Transmit FIFO Register (SPIDTR), Data To Be transmitted through the SPI Core.
        res = sendCommand(cmdr_prog_quad_inp, &cx.buffer[0], /*w_len*/(DIM_DATA_BUFFER/4)*sizeof(p[0]), cmd_write);    //0x32,      quad input fast program
    }

    if (!res)
    {
        fif->notify(s_end, -1, "Fail write Block");
        return false;
    }

    if (WAIT_TIME_BETWEEN_2_MACRO>0)
        fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);

    if (!confirmFlashOperation())
    {
        fif->notify(s_end, -1, "Fail confirm write Block");
        return false;
    }
    else
    {
        //fif->notify(FpgaFlashInterface::s_end, -1, "Ok, write Block");
        return true;
    }
}

//***********************************************************************
// Set/unset QuadSPI communication between FPGA and Flash
// Input parameters:    _flag = true is set quadSPI
//                                false is set normalSPI
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::setQuadSpiMode(bool _flag)
{
    bool res = false;

    if (_flag)
    {
        fif->log(type_log_info, "-> MACRO - Enable QuadSPI");
    }
    else
    {
        fif->log(type_log_info, "-> MACRO - Enable NormalSPI");
    }

    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

     //setEnableMsgBlk(true);
     MyDebug.setLevel(log_level_max);

    if (_flag)
    {
        //Set FLASH Volatile Config. Register (modalità SPI 4x)->
        char data[DIM_DATA_BUFFER] ;
        data[0]=0x6F;                //Data Transmit Reg: 0x"6F"
        cx.ctrl[r_nbr]=1;

        res = sendCommand(cmdr_write_enhan,&data[0],DIM_DATA_BUFFER,cmd_write);              //0x61, write enhanced volatile configuration register

        if (!res)
        {
            fif->log(type_log_error, " MACRO - ERROR - set QuadSPI not completed");
            return false;
        }

        //configure mode register
        cx.ctrl[r_moder]=
                    moder_spimode_x4mode<<0     //quad spi
                    |(moder_byteaddress<<2)
                    |(moder_qspiport<<3);

    }
    else
    {
        //Set FLASH Volatile Config. Register (modalità SPI 1x)->

        char data[DIM_DATA_BUFFER] ;
        data[0]=0xFF;//Data Transmit Reg: 0x"FF"
        cx.ctrl[r_nbr]=1;

        res = sendCommand(cmdr_write_enhan,&data[0],DIM_DATA_BUFFER,cmd_write);              //=0x61,      write enhanced volatile configuration register);

        if (!res)
        {
            fif->log(type_log_error, " MACRO - ERROR - set NormalSPI not completed");
            return false;
        }

        //configure mode register
        cx.ctrl[r_moder]=
                    moder_spimode_x1mode<<0     //normal spi
                    |(moder_byteaddress<<2)
                    |(moder_qspiport<<3);


    }

    /*res = writeEnable(false);
    if (!res)
    {
        return false;
    }*/

    if (WAIT_TIME_BETWEEN_2_MACRO>0)
        fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);


    if (_flag)
    {
        fif->log(type_log_success, "MACRO - DONE - Enable QuadSPI");
    }
    else
    {
        fif->log(type_log_success, "MACRO - DONE - Enable NormalSPI");
    }


    return true;
}



//2.4.1.4 Read (singolo data chunk di 256 byte)
void FlashOperation::readDataChunk(int size, void* data)
{
    init(moder_spimode, moder_byteaddress, moder_qspiport);   //Mode Register: "x06"
    sendCommand(cmdr_mem_fast_read);                                //0x0b, fast read memory, Command Register: "x0B"

    do
    {
        sendCommand(cmdr_read_st_reg);  //READ REGISTER OPERATION, READ STATUS REGISTER
    } while (getSpiCoreBusy());         //"spi busy" bit del registro SPISR = 0,

    //unsigned int nElements = cx.ctrl[r_rxfifoocy];
    data = &cx.ctrl[r_spirr];

    fif->notify(s_end, -1, "Ok, read DataChunk completed");
}



char* FlashOperation::retCommand(unsigned int cmd)
{
    const char* res = "";
    switch(cmd)
    {
    //software reset operations,
        case cmdr_reset_enable:         //=0x66,      //reset enable
            res = "Reset enable";
        break;
        case cmdr_reset_mem:            //=0x99,      //reset mem
            res = "Reset mem";
        break;

        //read id operations,
        case cmdr_read_id:              //=0x9e,
            res = "read id";
        break;
        case cmdr_multi_io_read_id:     //=0xaf,
        res = "multiple I/O read id";
        break;
        case cmdr_read_serial:          //=0x05a
                res = "read serial flash discovery parameter";
        break;

        //read memory operations,
        case cmdr_mem_read:             //=0x03,
                res = "read memory";
        break;
        case cmdr_mem_fast_read:        //=0x0b,
                res = "fast read memory";
        break;
        case cmdr_mem_dual_read:        //=0x3b,
                res = "dual output fast read";
        break;
        case cmdr_mem_quad_read:        //=0x6b,
                res = "quad output fast read";
        break;

        //write operations,
        case cmdr_write_enable:         //=0x06,
                res = "write enable";
        break;
        case cmdr_write_disable:        //=0x04,
                res = "write disable";
        break;

        //read register operation,
        case cmdr_read_st_reg:          //=0x05,
                res = "read status register";
        break;
        case cmdr_read_flag_st:         //=0x70,
                res = "read flag status register";
        break;
        case cmdr_read_non_vol:         //=0xb5,
                res = "read non-volatile config register";
        break;
        case cmdr_read_vol:             //=0x85,
                res = "read volatile config register";
        break;
        case cmdr_read_enhan:           //=0x65,
                res = "read enhanced volatile config register";
        break;
        case cmdr_read_gen_purp:        //=0x96,
                res = "read general purpose read register";
        break;

        //write register operation,
        case cmdr_write_st_reg:         //=0x01,
                res = "write status register";
        break;
        case cmdr_write_non_vol:        //=0xb1,
                res = "write non-volatile configuration register";
        break;
        case cmdr_write_vol:            //=0x81,
                res = "write volatile configuration register";
        break;
        case cmdr_write_enhan:          //=0x61,
                res = "write enhanced volatile configuration register";
        break;
        case cmdr_write_ext:            //=0xc5,
                res = "write extended address register";
        break;
        case cmdr_clear_st_reg:         //=0x50,
                res = "clear flag status register";
        break;

        //program operation,
        case cmdr_prog_page:            //=0x02,
                res = "page program";
        break;
        case cmdr_prog_dual_inp:        //=0xa2,
                res = "dual input fast program";
        break;
        case cmdr_prog_ext_dual:        //=0xd2,
                res = "extended dual input fast program";
        break;
        case cmdr_prog_quad_inp:        //=0x32,
                res = "quad input fast program";
        break;
        case cmdr_prog_ext_quad:        //=0x38,
                res = "extended quad input fast program";
        break;

        //erase operation,
        case cmdr_erase_32kb:           //=0x52,
                res = "32 kb subsector erase";
        break;
        case cmdr_erase_4kb:            //=0x20,
                res = "4kb subsector erase";
        break;
        case cmdr_erase_sector:         //=0xd8,
                res = "sector erase";
        break;
        case cmdr_erase_bulk:           //=0xc7,
                res = "bulk erase";
        break;

        //4-byte address mode operations
        case cmdr_4b_add_enter:         //=0xb7,
                res = "enter 4b address mode";
        break;
        case cmdr_4b_add_exit:          //=0xe9,
                res = "exit 4b address mode";
        break;

        //quad protocol operations
        case cmdr_quad_io_enter:        //=0x35,
                res = "enter quad io mode";
        break;
        case cmdr_quad_io_exit:         //=0xf5,
                res = "exit quad io mode";
        break;

        //power down
        case cmdr_pwr_enter:            //=0xb9,
                res = "enter deep power down";
        break;
        case cmdr_pwr_release:          //=0xab,
                res = "release deep power down";
        break;

        //advanced sector protection
        case cmdr_sect_read:            //=0x2d,
                res = "read sector protection";
        break;
        case cmdr_sect_progr:           //=0x2c,
                res = "program sector protection";
        break;
        case cmdr_sect_read_vol:        //=0xe8,
                res = "read volatile lock bits";
        break;
        case cmdr_sect_write_vol:       //=0xe5,
                res = "write volatile lock bits";
        break;
        case cmdr_sect_read_non_vol:    //=0xe2,
                res = "read nonvolatile lock bits";
        break;
        case cmdr_sect_write_non_vol:   //=0xe3,
                res = "write nonvolatile lock bits";
        break;
        case cmdr_sect_erase_non_vol:   //=0xe4,
                res = "erase nonvolatile lock bits";
        break;
        case cmdr_sect_read_freez:      //=0xa7,
                res = "read global freeze bit";
        break;
        case cmdr_sect_write_freez:     //=0xa6,
                res = "write global freeze bit";
        break;
        case cmdr_sect_red_pwd:         //=0x27,
                res = "read password";
        break;
        case cmdr_sect_write_pwd:       //=0x28,
                res = "write password";
        break;
        case cmdr_sect_unlock_pwd:      //=0x29,
                res = "unlock password";
        break;

        //advanced function interface
        case cmdr_int_activation:       //=0x9b
                res = "interface activation";
        break;
    }

    return (char*)res;
}

/*2.4.1.2 Erase
    For (i=0; i++; i==(Flash Sectors)/2)) Loop
            FLASH Write Enable (modalità SPI 4X, 4 Byte Addressing)->
                Command Register: "x06"     //WRITE OPERATIONS, WRITE ENABLE
                Mode Register: "x06"        //110,  4 bytes address, SPI in x4 mode
            Erase Sector (modalità SPI 4X, 4 Byte Addressing) ->
                Command Register: "xD8"      //ERASE OPERATION, SECTOR ERASE
                Mode Register: "x06"         //110,  4 bytes address, SPI in x4 mode
            Address Register: (Flash Half Address + i * Sector Size) "
            Questo comando effettua un erase del settore appartenente all'indirizzo specificato.
            Loop
                READ Flash Status Register (modalità SPI 4X, 4 Byte Addressing) ->
                Command Register: "x05"      //READ REGISTER OPERATION, READ STATUS REGISTER
                Mode Register: "x06"         //110,  4 bytes address, SPI in x4 mode
                Questo comando legge lo stato del registro di stato della Flash al fine di aspettare l'avvenuto erase del settore tramite il bit di "Write In Progress"
           Until (SPIDRR & "x01") == 0
    End Loop*/


// Erase from profile.BaseOffset for size
bool FlashOperation::eraseForSize(unsigned int size)
{
    //unsigned long t_erase=fif->timeNowMs();

    const unsigned int block_bytes=profile.SectorSize_KBytes*1024;
    const unsigned long boffset=profile.BaseOffset;
    unsigned int blocks=(size+(block_bytes-1))/block_bytes;
    unsigned int block_start=boffset/block_bytes;

    fif->log(type_log_info, "Blocks to Erase: %u+%u", block_start, blocks);

    //for all sector
    for(unsigned int i=0; i<blocks; ++i)
    {
        //FLASH Write Enable (modalità SPI 4X, 4 Byte Addressing)
        int progress=100*(float(i+1)/blocks);
        unsigned long baddress=boffset+i*block_bytes;
        fif->notify(s_erase, progress, "Erasing %u @0x%lX", i+block_start, baddress);
        init(moder_spimode, moder_byteaddress, moder_qspiport);   //Mode Register: "x06"
        if (eraseSector(i+block_start))                                 //ERASE OPERATION, SECTOR ERASE
        {
            do
            {
                sendCommand(cmdr_read_st_reg);                          //READ REGISTER OPERATION, READ STATUS REGISTER
            } while ((cx.ctrl[r_spisr] & 0x01) == 0);                   //erase is ok  -> info is RX_FIFO_VALID bit 0 register SPISR
        }
        else
        {
            writeEnable(false);
            fif->notify(s_erase, -1, "Fail %u", i);
            return false;
        }
    }

    //float t_now=(fif->timeNowMs()-t_erase)/1000;
    fif->notify(s_erase, -1, "Ok, erased completed");

    writeEnable(false);
    return true;
}


//***********************************************************************
// readStatusRegisterSPISR
// Read status register SPISR and put info into cmdr_read_st_reg struct"
// Input parameters:
//
// Returns:
//***********************************************************************
bool FlashOperation::readStatusRegisterSPISR()
{
    //reset status register struct
    memset(&status_register_spisr, 0, sizeof status_register_spisr);
    //reset command struct

    init(moder_spimode, moder_byteaddress,moder_qspiport);
    sendCommand(cmdr_read_st_reg); //READ REGISTER OPERATION, READ STATUS REGISTER

    unsigned int spisr = cx.ctrl[r_spisr];

    status_register_spisr.rx_fifo_valid         = decodeSPISR(spisr, rx_fifo_valid);        //bit 0, This bit flags that data present into SPIRR is valid
    status_register_spisr.rx_fifo_overrun       = decodeSPISR(spisr, rx_fifo_overrun);      //bit 1, This bit flags that an overrun error occurred during data reception into the RX FIFO.
    status_register_spisr.tx_fifo_almost_full   = decodeSPISR(spisr, tx_fifo_almost_full);  //bit 4, This bit flags that an almost full condition occurred loading the TX FIFO. In particular this condition is activated when the number of bytes into the FIFO differs from respect the FIFO size.
    status_register_spisr.tx_fifo_overrun       = decodeSPISR(spisr, tx_fifo_overrun);      //bit 5, This bit flags that an overrun error occurred loading data into the TX FIFO.
    status_register_spisr.tx_fifo_underrun      = decodeSPISR(spisr, tx_fifo_underrun);     //bit 6, This bit flags that an underrun error occurred into the TX FIFO during data transmission.
    status_register_spisr.spi_core_error        = decodeSPISR(spisr, spi_core_error);       //bit 10, The SPI Core error flags an error condition during transaction. This condition can be activated: - when an overrun or underrun is detected (on both "TX FIFO" or "RX FIFO") - when the required command is not implemented
    status_register_spisr.spi_core_ack          = decodeSPISR(spisr, spi_core_ack);         //bit 11, This bit is activated when a previously required transaction required with the "SPI request" on the SPICR register has been accepted
    status_register_spisr.spi_core_busy         = decodeSPISR(spisr, spi_core_busy);        //bit 12, This bit is activated when the core is executing the current transaction after that it has been run with the "SPI start" bit on the SPICR register.

    fif->notify(s_status, -1, "Ok, read StatusRegister SPISR completed");

    return true;
}


bool FlashOperation::getRxFifoValid()
{
    readStatusRegisterSPISR();
    return status_register_spisr.rx_fifo_valid;
}

bool FlashOperation::getRxFifoOverrun()
{
    readStatusRegisterSPISR();
    return status_register_spisr.rx_fifo_overrun;
}

bool FlashOperation::getTxFifoFull()
{
    readStatusRegisterSPISR();
    return status_register_spisr.tx_fifo_almost_full;
}

bool FlashOperation::getTxFifoOverrun()
{
    readStatusRegisterSPISR();
    return status_register_spisr.tx_fifo_overrun;
}

bool FlashOperation::getTxFifoUnderrun()
{
    readStatusRegisterSPISR();
    return status_register_spisr.tx_fifo_underrun;
}

bool FlashOperation::getSpiCoreBusy()
{
    readStatusRegisterSPISR();
    return status_register_spisr.spi_core_busy;
}

bool FlashOperation::getSpiCoreError()
{
    readStatusRegisterSPISR();
    return status_register_spisr.spi_core_error;
}

bool FlashOperation::getSpiCoreAck()
{
    readStatusRegisterSPISR();
    return status_register_spisr.spi_core_ack;
}

//***********************************************************************
// getWriteInProgress
// Questo comando legge lo stato del registro di stato della Flash al fine di aspettare
// l'avvenuta scrittura del pacchetto dati tramite il bit di "Write In Progress"
// Input parameters:
//
// Returns:
//***********************************************************************
bool FlashOperation::getWriteInProgress()
{
    //(SPIDRR & "x01") == 0, che corrisponde al bit 0 del registro SPISR a 1
    return getRxFifoValid();
}

//***********************************************************************
// get enable message confirm in tftp write operation
// Input parameters:
//
// Returns:         true = if is enable
//                  false = if is disable
//***********************************************************************
bool FlashOperation::getEnableMsgBlk()
{
    return enabledMsgBlk;
}

//***********************************************************************
// set enable message confirm in tftp write operation
// Input parameters:    _enable = true if display confirm message
//                              = false if not display confirm message
//
// Returns:
//***********************************************************************
void FlashOperation::setEnableMsgBlk(bool _enabled)
{
    enabledMsgBlk = _enabled;
}

//***********************************************************************
// Read Fpga FW IBC
// Input parameters:
//
// Returns:         true = success, into data buffer there is the correct value
//                  false = error in read FW IBC
//***********************************************************************
bool FlashOperation::readFPGAFWIBC(void* data, const int _len)
{
    return fpgaReadFPGAFWIBC(FPGA_C_MEM_RD_ADD, data, _len,"Read Flash Fw IBC");
}

//***********************************************************************
// Decode SPIR and diplay all bit status with info
// Input parameters: _spir = return from SPI status
//
// Returns:
//
//***********************************************************************
void FlashOperation::decodeSPIRError(unsigned int _spisr)
{
    QString msg = "";
    if  ((_spisr & rx_fifo_valid)!=0)
        msg.append( "RX fifo valid - ");
    else
        msg.append( "RX fifo NOT valid - ");

    if  ((_spisr & rx_fifo_overrun)!=0)
        msg.append( "overrun - ");
    else
        msg.append( "NOT overrun - ");

    if  ((_spisr & tx_fifo_almost_full)!=0)
        msg.append( "TX fifo almost full - ");
    else
        msg.append( "TX fifo not almost full - ");

    if  ((_spisr & tx_fifo_overrun)!=0)
        msg.append( "overrun - ");
    else
        msg.append( "NOT overrun - ");

    if  ((_spisr & tx_fifo_underrun)!=0)
        msg.append( "underrun - ");
    else
        msg.append( "NOT underrun - ");

    if  ((_spisr & spi_core_error)!=0)
        msg.append( "SPI core error - ");
    else
        msg.append( "SPI core NOT error - ");

    if  ((_spisr & spi_core_ack)!=0)
        msg.append( "ack - ");
    else
        msg.append( "NOT ack - ");

    if  ((_spisr & spi_core_busy)!=0)
        msg.append( "busy.");
    else
        msg.append( "NOT busy.");

    fif->log(type_log_error, "SPISR=0x%02X -> %s", _spisr ,msg.toStdString().c_str());
}

//***********************************************************************
// increment counter repeater
// Input parameters:
// Returns:
//***********************************************************************
void FlashOperation::incrCounterRepeat()
{
    if (counterRepeat>MAX_COUNTER_REPEAT)
    {
        counterRepeat = 0;
        return;
    }
    counterRepeat++;
}

//***********************************************************************
// Set protection sector for golden area
// Input parameters:    testLock, test if command is ok
// Returns:             true if success
//                      false if error
//***********************************************************************
bool FlashOperation::setProtectionSector(bool testLock)
{
    bool res = false;

    fif->log(type_log_info, "-> MACRO - Enable setProtectionSectorRegister");

    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

    //SECTOR PROTECTION REGISTER (2Ch)          bit1=0              ->         1 byte command 2C +  2 byte dataSECTOR PROTECTION REGISTER
    char data[DIM_DATA_BUFFER] ;
    memset(data, 0xFF,DIM_DATA_BUFFER);

    //data[0] &= ~(1UL << 1);

    data[1] = 0xFF;         //0b11111111
    data[0] = 0xFD;         //0b11111101

    //fif->notify(FpgaFlashInterface::s_init, -1, "MACRO - setProtectionSectorRegister with this value 0x%01X 0x%01X", data[0], data[1]);

    cx.ctrl[r_nbr]   = 0x02;
    cx.ctrl[r_addrr] = 0x0;

    res = sendCommand(cmdr_sect_progr,&data[0],DIM_DATA_BUFFER,cmd_write);              //0x2c, program sector protection

    if (!res)
    {
        //fif->log(type_log_error, " MACRO - ERROR - setProtectionSectorRegister");
        fif->notify(s_init, -1, "MACRO - ERROR - setProtectionSectorRegister");
        return false;
    }



    if (testLock)
    {
        //COMAND 2D -> READ SECTOR PROTECTION
        fif->log(type_log_info, "Read sector protection");

        uint16_t sts = 0;

        do
        {
            cx.ctrl[r_addrr]=0x0;
            cx.ctrl[r_nbr]=0x02;
            res=sendCommand(cmdr_sect_read ,                        //0x2d, read sector protection
                                &cx.ctrl[r_spirr],
                                DIM_DATA_BUFFER,                                //Number of elements into the "TX FIFO"
                                cmd_read);
            if (!res)
            {
                //fif->log(type_log_error, "MACRO - Read sector protection register , ERROR");
                fif->notify(s_init, -1, "MACRO - Read sector protection, ERROR");
                return false;
            }
            sts = cx.ctrl[r_spirr];

            fif->log(type_log_info, "MACRO - Confirm read sector protection register, read 0x%02X", sts);
        }while ((sts & 0x02) != 0);
    }
    if (WAIT_TIME_BETWEEN_2_MACRO>0)
        fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);

    fif->log(type_log_success, "MACRO - DONE - set sector protection register.");
    fif->notify(s_init, -1, "Ok, sector protection register for golden area completed.");



    return true;
}


//***********************************************************************
// Set Volatile Lock Bits for every sector for golden area
// Input parameters:    testLock, test if command is ok
// Returns:             true if success
//                      false if error
//***********************************************************************
bool FlashOperation::setVolatileLockBits(bool testLock)
{
    bool res = false;

    fif->log(type_log_info, "-> MACRO - set write volatile lock bits");

    res = writeEnable(true);
    if (!res)
    {
        return false;
    }

    //dimensione del singolo blocco da bloccare
    const unsigned int block_bytes=profile.SectorSize_KBytes*1024;                  //usually 64kbytes

    //indirizzo del primo blocco da cancellare, base degli indirizzi
    const unsigned long boffset = profile.golden_address_start_area;

    //numero di blocchi di dimensione SectorSize_KBytes in totale nella memoria
    unsigned int blocks_to_lock = (profile.golden_address_stop_area / block_bytes);            //per una memoria da 128 MBit sono 128 blocchi da bloccare
                                                                                               //per una memoria da 256 MBit sono 256 blocchi da bloccare

    //faccio l'erase dei blocchi coninvolti nella programmazione
    fif->log(type_log_info, "-> MACRO - Blocks to Erase: start block %u, at address @0x%lX + next %u block(s)" , 0, boffset , blocks_to_lock);

    unsigned long t_start=fif->timeNowMs();
    float t_now = 0;
    unsigned long baddress = 0;
    int progress = 0;

    fif->notify(s_init, -1, "Locking %u sectors of golden area.", blocks_to_lock+1);

    QString msg = "";

    for(unsigned int i=0; i<blocks_to_lock; ++i)
    {
        progress=100*(float(i+1)/blocks_to_lock);
        baddress=boffset+i*block_bytes;
        fif->notify(s_erase, progress, "Lock sector %u/%u at address @0x%lX", i+1, blocks_to_lock+1 , baddress);

        //WRITE VOLATILE LOCK BITS (E5h)                 bit0=1              ->         1 byte command E5 +  3/4 byte address sector + 1 byte data
        char data[DIM_DATA_BUFFER] ;
        memset(data, 0xFF,DIM_DATA_BUFFER);

        data[0] =0x01;

        cx.ctrl[r_nbr]   = 1;
        cx.ctrl[r_addrr] = baddress;

        res = sendCommand(cmdr_sect_write_vol,&data[0],DIM_DATA_BUFFER,cmd_write);
        if (!res)
        {
            msg = QString("MACRO - ERROR - set write volatile lock bits at address %1").arg(baddress,0,16);
            fif->log(type_log_error, msg.toStdString().c_str());
            return false;
        }


        if (testLock)
        {
            //COMANDO E8 -> READ VOLATILE LOCK BITS
            fif->log(type_log_info, "Read volatile lock bits");

            uint32_t sts = 0;

            do
            {
                cx.ctrl[r_addrr]=baddress;//0x0;
                cx.ctrl[r_nbr]=0x01;
                res=sendCommand(cmdr_sect_read ,                     //0x2d, read sector protection
                                    &cx.ctrl[r_spirr],
                                    DIM_DATA_BUFFER,                               //Number of elements into the "TX FIFO"
                                    cmd_read);
                if (!res)
                {
                    fif->log(type_log_error, "MACRO - Read volatile lock bits, ERROR");
                    return false;
                }
                sts = cx.ctrl[r_spirr];

                fif->log(type_log_info, "MACRO - Confirm volatile lock bits, read 0x%02X", sts);
            }while ((sts & 0x01) == 0);
        }
    }

    t_now=(fif->timeNowMs()-t_start);

    QString t1 = QTime::fromMSecsSinceStartOfDay(t_now).toString("hh:mm:ss");
    fif->notify(s_init, 0, "Ok, locked %u sector(s) of golden area (%s)", blocks_to_lock+1, t1.toStdString().c_str());
    fif->notify(s_erase, 0, "");

    if (WAIT_TIME_BETWEEN_2_MACRO>0)
        fif->rtgWaitEvent(WAIT_TIME_BETWEEN_2_MACRO);

    fif->log(type_log_success, "MACRO - DONE - set write volatile lock bits");

    return true;
}

//***********************************************************************
// read status flag register
// Input parameters:
// Returns: true = no errors, ready for another command
//          false = error, there is a problem or not responds.
//***********************************************************************
bool FlashOperation::readStatusFlagRegister()
{

#define FLAG_PROTECTION     2     //bit 1 Protection 0 = Clear 1 = Failure or protection error

#define FLAG_PROGRAM        16    //bit 4 Program    0 = Clear 1 = Failure or protection error

#define FLAG_ERASE          32    //bit 5 Erase      0 = Clear 1 = Failure or protection error


    // Set the timeout value [system ticks]
    // max sector erase time from data sheet is 3[s]

    bool res = false;

    fif->log(type_log_info, "-> MACRO - readStatusFlagRegister");

    uint8_t sts = 0;

    cx.ctrl[r_addrr]=0;
    cx.ctrl[r_nbr]=0x01;
    res=sendCommand(cmdr_read_flag_st,              //0x70, read flag status register
                            &cx.ctrl[r_spirr],
                            DIM_DATA_BUFFER ,                           //Number of elements into the "TX FIFO"
                            cmd_read);
    if (!res)
    {
        fif->log(type_log_error, "MACRO - readStatusFlagRegister, ERROR");
        return false;
    }
    sts = cx.ctrl[r_spirr];

    fif->log(type_log_info, "MACRO - readStatusFlagRegister read 0x%02X", sts);

    if ((sts & FLAG_PROTECTION)!=0)
    {
        if ((sts & FLAG_PROGRAM)!=0)
        {
            fif->notify(s_init, 0, "PROGRAM - Failure or protection error");
            return false;
        }
        if ((sts & FLAG_ERASE)!=0)
        {
            fif->notify(s_init, 0, "ERASE - Failure or protection error");
            return false;
        }

        fif->notify(s_init, 0, "PROTECTION - Failure or protection error");
        return false;
    }

    return true;
}

//***********************************************************************
// Program FLASH with data buffer
// Input parameters:    data = buffer to write on flash
//                      size = dimension of data buffer
// Returns:             true if ok
//                      false if error
//***********************************************************************
bool FlashOperation::program(const void* data, unsigned int size)
{
    //reset counterRepeat
    counterRepeat = 0;

    //reg_moder_qspiport_t spi_port = primarySpiPort;
    //reg_moder_byteaddress_t byteaddress = bytes4address;

    //moder_spimode = a_moder_spimode;
    //moder_byteaddress = a_moder_byteaddress;
    moder_qspiport = profile.spiport;
    moder_byteaddress = profile.byteddress;

    //disable confirm message in write tftp operation
    setEnableMsgBlk(false);

    //controllo se si tratta della scrittura del fimware dell'RFIF
    /*if (profile.isFpgaRFIF)
    {
        //se si tratta FW RFIF, il download è diverso rispetto ad altri download
        return programRFIF(data, size);
    }*/
    //Resetting FPGA interface...


    if (!reset())
    {
        return false;
    }



    if (!setByteAddress(moder_qspiport, moder_byteaddress))
    {
        return false;
    }



    //Unlocking FPGA interface...
    // r_moder = 0x08 if secondary FLASH, 0x00 if primary FLASH

    if (!readFlashID(moder_qspiport))
    {
        return false;
    }

    //ATTIVARE LA PROTEZIONE SULLA PARTE GOLDEN
    if (!setProtectionSector(false))
    {
        return false;
    }

    //SETTARE IL LOGCK BITS PER OGNI SETTORE DELLA GOLDEN
    if (!setVolatileLockBits(true))
    {
        return false;
    }



    //set volatile configuration register
    if (!setQuadSpiMode(true))
    {
        return false;
    }

    fif->log(type_log_info  , "MACRO - prima di confirm");

    if (!confirmFlashOperation())
    {
        return false;
    }

    fif->log(type_log_info, "MACRO - before erase");

   // setEnableMsgBlk(true);

    //********************************************
    //START of erase function

    //dimensione del singolo blocco da cancellare
    const unsigned int block_bytes=profile.SectorSize_KBytes*1024;                  //usually 64kbytes

    //indirizzo del primo blocco da cancellare, base degli indirizzi
    const unsigned long boffset = (profile.BaseOffset);
    //const unsigned long boffset = (profile.golden_address_start_area);

    unsigned int blocks = 0;

    if (size!=1)
    {
        //si tratta di una programmazione non simulata,
        //quindi il size rappresenta effettivamente la dimensione del buffer da scrivere nella flash
        //numero di blocchi di dimensione SectorSize_KBytes in totale nella memoria
        blocks = profile.user_address_stop_area / block_bytes;
        //blocks = profile.golden_address_stop_area / block_bytes;        //per una memoria da 128 MBit sono 256 blocchi totali
                                                                      //per una memoria da 256 MBit sono 512 blocchi totali
    }
    else
    {
        //si tratta della simulazione
        //numero di blocchi di dimensione SectorSize_KBytes in totale nella memoria
        blocks=(size+(block_bytes-1))/block_bytes;
    }

    //blocco di partenza
    unsigned int block_start=(boffset/block_bytes)+1;                               //da blocco 129 per una memoria da 128MBit
                                                                                    //da blocco 257 per una memoria da 256MBit

    //blocchi che mancano alla fine dello spazio "user" della flash
    unsigned int blocks_to_delete = blocks - block_start + 1;                       //128 blocchi da cancellare per una memoria da 128MBit
                                                                                    //256 blocchi da cancellare per una memoria da 256M

    //faccio l'erase dei blocchi coninvolti nella programmazione
    fif->log(type_log_info, "-> MACRO - Blocks to Erase: start block %u, at address @0x%lX + next %u block(s)" , block_start, boffset , blocks_to_delete);

    unsigned long t_start=fif->timeNowMs();
    unsigned long t_erase=fif->timeNowMs();
    float t_now = 0;

    bool ok = false;
    unsigned long baddress = 0;
    int progress = 0;

#ifndef NO_WRITE_ERASE

#ifdef NUM_BLOCKS_ERASE
    blocks_to_delete = NUM_BLOCKS_ERASE;
#else

#endif

    fif->notify(s_init, -1, "Erasing %u sectors of user area.", blocks_to_delete+1);

    for(unsigned int i=0; i<blocks_to_delete; ++i)
    {
        progress=100*(float(i+1)/blocks_to_delete);
        baddress=boffset+i*block_bytes;
        fif->notify(s_erase, progress, "Erasing sector %u/%u at address @0x%lX", i+block_start,blocks+1 , baddress);
        ok=eraseSector(baddress);
        if (!ok)
        {
            fif->notify(s_erase, -1, "Fail %u", i);
            return false;
        }

        ok = readStatusFlagRegister();
        if (!ok)
        {
            return false;
        }

        //return false;       //LV PER FERMARE L'ESECUZIONE

        ok = confirmFlashOperation();
        if (!ok)
        {
            return false;
        }


    }

    //t_now=(fif->timeNowMs()-t_erase)/1000;
    t_now=(fif->timeNowMs()-t_erase);

    //fif->notify(FpgaFlashInterface::s_erase, 100, "Ok, erased %u sector(s) (%lusecs)", blocks_to_delete, int(t_now));
    QString t1 = QTime::fromMSecsSinceStartOfDay(t_now).toString("hh:mm:ss");
    fif->notify(s_init, 100, "Ok, erased %u sector(s) of user area (%s)", blocks_to_delete+1, t1.toStdString().c_str());

    //return false;

#endif

    //return true;

    //setEnableMsgBlk(true);

    //********************************************
    //END of erase function

    //********************************************
    //START of program function

    //calcolo i blocchi da scrivere in base alle dimensioni da programmare e la dimensione del singolo blocco
    const unsigned int frag_size=DIM_DATA_BUFFER;
    blocks=(size+(block_bytes-1))/block_bytes;
    unsigned int psize=((size+(frag_size-1))/frag_size)*frag_size;
    unsigned int frags=psize/frag_size;

    fif->notify(s_init, -1, "Programming  %.2fKiB, %u blocks of user area, %u frags", float(size)/1024, blocks, frags);

    //TODO: multi bank support
    fif->log(type_log_info, "-> MACRO - Writing %u frags...", frags);
    const char* p=reinterpret_cast<const char*>(data);

    t_start=fif->timeNowMs();

    unsigned int progr_frags=0;
    progress = 0;

    int num_block = psize / frag_size;

    unsigned int sec_less;
    QString time = "";
    QString time1 = "";

    unsigned int dim_data_write = frag_size;

    for(unsigned int i=0; i<psize; i+=frag_size, ++progr_frags)
    {
        unsigned long baddress=boffset+i;
        if (i>0)
        {
            progress=100*(float(i)/psize);
            t_now=(fif->timeNowMs()-t_start)/1000;
            float kbytes=i/1024.0;
            float prate=float(kbytes)/t_now;


            /*fif->notify(FpgaFlashInterface::s_write, progress, "Writing %u/%u @0x%lX (%usec, %f KiBps)",
                            progr_frags, num_block, baddress,
                            int(t_now), prate);*/

            sec_less = ((num_block - progr_frags)/ 4) / prate;

            time = QTime::fromMSecsSinceStartOfDay(t_now*1000).toString("hh:mm:ss");
            time1 = QTime::fromMSecsSinceStartOfDay(sec_less*1000).toString("hh:mm:ss");
            fif->notify(s_write, progress, "Writing %u/%u @0x%lX (%s/%s, %f KiBps)",
                        progr_frags,
                        num_block,
                        baddress,
                        time.toStdString().c_str(),
                        time1.toStdString().c_str(),
                        prate);
        }
        else
        {
            fif->notify(s_write, progress, "Writing %u @0x%lX", progr_frags, baddress);
        }
        if (i+frag_size>size)
           dim_data_write = i+frag_size-size;
        bool ok=writeBlock(baddress, &p[i], dim_data_write);
        if (!ok)
        {
            fif->notify(s_write, -1, "Fail %u @0x%lX", progr_frags, baddress);
            return false;
        }
    }




    //t_now=(fif->timeNowMs()-t_erase)/1000;
    t_now=(fif->timeNowMs()-t_erase);

    QString t2 = QTime::fromMSecsSinceStartOfDay(t_now).toString("hh:mm:ss");

    fif->notify(s_init, 100, "Ok, written %u frags = %fMiB on user area (%s)",
                frags,
                float(psize)/(1024*1024),
                t2.toStdString().c_str());


    //********************************************
    //END of program function

    //riconfiguro l'indirizzamento a 3 byte per tutte le flash anche se quelle da 256 mbit.
    if (!setByteAddress(moder_qspiport, bytes3address))
    {
        //errori nella configurazione dell'indirizzamento a 3 byte
        return false;
    }

    /*if (!writeEnable(false))
    {
        //errori nella chiusura delle operazioni (disable write)
        return false;
    }*/

#ifndef NO_WRITE_ERASE
    //float kbytes=psize/1024.0;
    //float prate=kbytes/t_now;
    //fif->notify(FpgaFlashInterface::s_end, -1, "Completed. %fKiBps", prate);
#endif

    //set volatile configuration register
    if (!setQuadSpiMode(false))
    {
        return false;
    }

    //fif->notify(FpgaFlashInterface::s_end, -1, "Ok, write finished");

    //QCoreApplication::postEvent(this, new MyEvent(evn_timeout), Qt::HighEventPriority);

    return true;
}
