#ifndef FPGAFLASHOPERATION_H
#define FPGAFLASHOPERATION_H

#include <string.h>
#include <map>

#include "fpgaflashengine.h"
#include "fpgaflashconfig.h"

/*
 *
Resetting the core:
1. Reset the core issuing the keycode @ the the SRR address
2. Wait @ least 10 usec
Using the core:
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 6-2 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 6-2 to know if this setting is requested or not).
5. Set the "SPI request" bit into the SPICR register
6. Wait until "SPI core Acknowledge" bit into the SPISR is raised by the core.
7. Check "SPI Core Error" bit is not raised (core error can be raised if currently requested command is not supported).
8. In case currently requested command requires to fill the TX FIFO (refers to Table 6-2), fill the trasmission buffer by writing data @ the SPIDTR register
9. Set the "SPI start" bit into the SPICR register (maintaining the "SPI request" bit set)
10. Wait until "SPI busy" into the SPISR is set
11. Reset "SPI start" bit
12. Wait until "SPI busy" into the SPISR is reset
13. Check "SPI Core Error" bit is not raised (core error can be raised if currently requested command generates an underflow or overflow error).
14. In case a read command has been requested, readout read data @ SPIDRR
15. De-assert "SPI request" bit.
16. Wait until "SPI core Acknowledge" bit into the SPISR is de-asserted by the core
 */

/*0x02C MODER RW 0x0 Mode Register
0x030 CMDR RW 0x0 Command Register
0x034 ADDRR RW 0x0 Address Register
0x038 NBR RW 0x0 Number of bytes Register
0x040 SRR RW 0x0 Software Reset Register
0x060 SPICR RW 0x0 SPI Control Register
0x400 SPIDTR RW 0x0 SPI Transmit FIFO Register
0x864 SPISR RO 0x0 SPI Status Register
0x874 TXFIFOOCY RO 0x0 TX FIFO Occupancy Register
0x878 RXFIFOOCY RO 0x0 RX FIFO Occupancy Register
0xC00 SPIRR RO 0x0 SPI Receive FIFO Register Interrupt Control Grouping
0x01C GIER RW 0x0 Global Interrupt Enable Register
0x020 ICSR RW 0x0 Interrupt Clear Status Register
0x028 IER RW 0x0 Interrupt Enable Register 0x820 ISR
*/

#define FLASH_ID_SIZE               4
#define FLASH_ID_NUMBER_OF_BYTES    20


// 5.5 Register Space, AXI_QSPIFlash_IF_PG Rev. A, pag 16
#define R32_(x_)    ((x_)/4)
enum reg_offset_t
{
    //RW -> read write
    //RO -> read only

    r_base              =R32_(0),
    r_moder             =R32_(0x02C),    //RW  Mode Register
    r_cmdr              =R32_(0x030),    //RW  Command Register
    r_addrr             =R32_(0x034),    //RW  Address Register
    r_nbr               =R32_(0x038),    //RW  Number of bytes Register
    r_srr               =R32_(0x040),    //RW  Software Reset Register
    r_spicr             =R32_(0x060),    //RW  SPI Control Register
    r_spidtr            =R32_(0x400),    //RW  SPI Transmit FIFO Register
    r_spi_status_base   =R32_(0x800),    //RO  SPI Status base
    r_spisr             =R32_(0x864),    //RO  SPI Status Register
    r_txfifoocy         =R32_(0x874),    //RO  TX FIFO Occupancy Register
    r_rxfifoocy         =R32_(0x878),    //RO  RX FIFO Occupancy Register
    r_spirr             =R32_(0xC00),    //RO  SPI Receive FIFO Register

    r_gier              =R32_(0x01C),    //RW  Global Interrupt Enable Register
    r_icsr              =R32_(0x020),    //RW  Interrupt Clear Status Register
    r_ier               =R32_(0x028),    //RW  Interrupt Enable Register
    r_isr               =R32_(0x820),    //RO  Interrupt Status Register

    adr_fifo=r_base+r_spidtr
};

// 5.5.1.1 Mode Register (MODER)
enum reg_moder_spimode_t            //SPI Mode
{
    moder_spimode_x1mode = 0,        //normal SPI, x1 mode
    moder_spimode_x2mode = 1,        //x2 mode
    moder_spimode_x4mode = 2         //x4 mode
};

//5.5.1.2 Command Register (CMDR)
//APPENDIX 1. MICRON FLASH SUPPORTED INSTRUCTIONS
enum reg_cmdr_command_t
{
    //software reset operations,
    cmdr_reset_enable   =0x66,      //reset enable
    cmdr_reset_mem      =0x99,      //reset mem

    //read id operations,
    cmdr_read_id        =0x9e,      //read id
    cmdr_multi_io_read_id=0xaf,     //multiple I/O read id
    cmdr_read_serial    =0x05a,     //read serial flash discovery parameter

    //read memory operations,
    cmdr_mem_read       =0x03,      //read memory
    cmdr_mem_fast_read  =0x0b,      //fast read memory
    cmdr_mem_dual_read  =0x3b,      //dual output fast read
    cmdr_mem_quad_read  =0x6b,      //quad output fast read

    //write operations,
    cmdr_write_enable   =0x06,      //write enable
    cmdr_write_disable  =0x04,      //write disable

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

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

    //program operation,
    cmdr_prog_page      =0x02,      //page program
    cmdr_prog_dual_inp  =0xa2,      //dual input fast program
    cmdr_prog_ext_dual  =0xd2,      //extended dual input fast program
    cmdr_prog_quad_inp  =0x32,      //quad input fast program
    cmdr_prog_ext_quad  =0x38,      //extended quad input fast program

    //erase operation,
    cmdr_erase_32kb     =0x52,      //32 kb subsector erase
    cmdr_erase_4kb      =0x20,      //4kb subsector erase
    cmdr_erase_sector   =0xd8,      //sector erase
    cmdr_erase_bulk     =0xc7,      //bulk erase

    //4-byte address mode operations
    cmdr_4b_add_enter   =0xb7,      //enter 4b address mode
    cmdr_4b_add_exit    =0xe9,      //exit 4b address mode

    //quad protocol operations
    cmdr_quad_io_enter  =0x35,      //enter quad io mode
    cmdr_quad_io_exit   =0xf5,      //exit quad io mode

    //power down
    cmdr_pwr_enter      =0xb9,      //enter deep power down
    cmdr_pwr_release    =0xab,      //release deep power down

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

    //advanced function interface
    cmdr_int_activation =0x9b       //interface activation
};

enum type_cmd_t
{
    cmd_nc,             //cmd without read and write
    cmd_read,           //cmd with read
    //cmd_read_flashID,   //cmd with read
    cmd_write,          //cmd with write
};

//5.5.1.6 SPI Control Register (SPICR)
enum reg_spicr_req_t                   //SPI request
{
    spicr_req_no           =0,         //no request begin transaction
    spicr_req_begin_trans  =1          //It requires to the IP to begin with a transaction
};
enum reg_spicr_start_t                 //SPI start
{
    spicr_start_no         =0,         //no start previous transaction
    spicr_start_prev_trans =1          //It starts a previously required transaction
};



class FlashOperation
{
public:

    reg_moder_spimode_t moder_spimode;
    reg_moder_byteaddress_t moder_byteaddress;
    reg_moder_qspiport_t moder_qspiport;

    //struct contain data buffer and control command
    struct cx_t
    {
        uint32_t buffer[DIM_DATA_BUFFER/4];       //data buffer (256byte)
        //uint32_t ctrl[DIM_DATA_BUFFER/4];         //control command
        uint32_t ctrl[DIM_DATA_BUFFER/4];            //R32_(0xC00)];     //control command, max register address/4
    };
    cx_t cx;

    //struct contain status register information read from spi
    struct status_register_spisr_t
    {
        bool rx_fifo_valid;                 //bit 0, This bit flags that data present into SPIRR is valid
        bool rx_fifo_overrun;               //bit 1, This bit flags that an overrun error occurred during data reception into the RX FIFO.
        bool 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.
        bool tx_fifo_overrun;               //bit 5, This bit flags that an overrun error occurred loading data into the TX FIFO.
        bool tx_fifo_underrun;              //bit 6, This bit flags that an underrun error occurred into the TX FIFO during data transmission.
        bool 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
        bool 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
        bool 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.
    };
    status_register_spisr_t status_register_spisr;

    FpgaFlashProfile profile;

    FpgaFlashInterface* fif;

    FpgaFlashEngine* engine;

    bool enabledMsgBlk;                     //enable message block

    bool getEnableMsgBlk();

    void setEnableMsgBlk(bool _enable);

    uint32_t FLASH_ID[FLASH_ID_SIZE];

    // INITIALIZE operations, 2.4.1.1 Inizializzazione
    // Inizialize mode register, set engine
    void init(FpgaFlashEngine* engine_,
                        reg_moder_spimode_t a_moder_spimode,
                        reg_moder_byteaddress_t a_moder_byteaddress,
                        reg_moder_qspiport_t a_moder_qspiport);

    // Initialize connection without set engine
    void init(reg_moder_spimode_t a_moder_spimode,
                    reg_moder_byteaddress_t a_moder_byteaddress,
                    reg_moder_qspiport_t a_moder_qspiport);

    // Core reset
    bool reset();

    // Read FLASH ID
    bool readFlashID(reg_moder_qspiport_t spi_port);

    // Read FPGA FW IBC
    bool readFPGAFWIBC(void* data, const int _len);

    //Sets the FPGA FLASH addressing mode
    bool setByteAddress(reg_moder_qspiport_t spi_port,reg_moder_byteaddress_t byteaddress);

    //2.4.1.4 Read (singolo data chunk di 256 byte)
    void readDataChunk(int size,void* data);

    bool program(const void* data, unsigned int size);

    //SPISR - SPI Status register info
    bool getRxFifoValid();
    bool getRxFifoOverrun();
    bool getTxFifoFull();
    bool getTxFifoOverrun();
    bool getTxFifoUnderrun();
    bool getSpiCoreBusy();
    bool getSpiCoreError();
    bool getSpiCoreAck();

    //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"
    bool getWriteInProgress();

    // Erase from profile.BaseOffset for size
    bool eraseForSize(unsigned int size);

    // Read status register SPISR and put info into cmdr_read_st_reg struct
    bool readStatusRegisterSPISR();

    //terminazione
    bool terminateDigitalCtrl();
    bool terminateOtherFPGA();

private:

    bool setQuadSpiMode(bool _flag);

    void decodeSPIRError(unsigned int _spisr);

    bool confirmFlashOperation();

    void printDebugFlashConfig();

    bool sendCommand(unsigned int cmd, void *data=0, unsigned int len=DIM_DATA_BUFFER, type_cmd_t type_cmd=cmd_nc);

    // WRITE OPERATIONS, enable/disable
    bool writeEnable(bool enable);

    bool fpgaWrite(uint32_t address, const void* data, unsigned int len=DIM_DATA_BUFFER, const char* mess="");

    bool fpgaRead(uint32_t address, void* data, unsigned int len=DIM_DATA_BUFFER, const char* mess="");

    bool fpgaReadFPGAFWIBC(uint32_t address, void* data, unsigned int len=DIM_DATA_BUFFER, const char* mess="");

    //2.4.1.3 Write (singolo data chunk di 256 byte)
    bool writeBlock(unsigned int address, const char* data, unsigned int len);

    //*****************************************
    // operation with the transaction
    void resetTransaction();
    void prepareTransaction();
    void startTransaction();
    void endTransaction();

    //ERASE OPERATION, erase sector
    bool eraseSector(unsigned long sector_address);

    //return stirng description for cmd
    char* retCommand(unsigned int cmd);

    unsigned int counterRepeat;

    void incrCounterRepeat();

    bool setProtectionSector(bool testLock=false);

    bool setVolatileLockBits(bool testLock=false);

    bool readStatusFlagRegister();


};

#endif // FPGAFLASHOPERATION_H
