/****************************************************************************/
/* Copyright 2015 MBARI                                                     */
/****************************************************************************/
/* Summary  : Definitions for Flash memory programming for PIC24FJ128GA406  */
/* Filename : flash_mem.h                                                   */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 12/10/2015                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 12dec2015  rah created                                                   */
/****************************************************************************/

#include "flash_mem.h"
#include <string.h>

/* for debugging with sprintf and serPutString */
#include <stdio.h>
#include "serial.h"

int __attribute__((space(prog), section(".params"), address(PARAM_PAGE))) paramBuff[512];


/************************************************************************/
/* Function    : memRead                                                */
/* Purpose     : Read param area of flash memory                        */
/* Inputs      : Ptr to buff to put data, size of buff                  */
/* Outputs     : 0 for OK                                               */
/************************************************************************/
int memRead(char* buff, int size)
{
    int i, bytes_read;
    DWORD_VAL data;
#if FLASH_MEM_DEBUG 
    char debug[64];
#endif
    
    /* if size is too big or too small bailout with error */
    if ( (size < 1) || (size > MAX_PAGE_BYTES) )
        return -1;

    /* clear bytes_read variable */
    bytes_read = 0;
        
    for (i = 0; i < size; i += 2)
    {
        /* read the word */
	data.word.LW = __builtin_tblrdl(__builtin_tbloffset(paramBuff) + i);
	data.word.HW = __builtin_tblrdh(__builtin_tbloffset(paramBuff) + i);

#if FLASH_MEM_DEBUG 
        sprintf(debug, "address 0x%04X = 0x%04X 0x%04X\r\n", 
                PARAM_PAGE + i, data.word.HW, data.word.LW);
        serPutString(0, debug);
#endif

        /* assign the results to the data buffer */
        *(buff++) = data.v[0];
        if ( ++bytes_read == size )
            return 0;

        *(buff++) = data.v[1];
        if ( ++bytes_read == size )
            return 0;
    }

    return(0);
}


/************************************************************************/
/* Function    : memWrite                                               */
/* Purpose     : Write param area of flash memory                       */
/* Inputs      : Ptr to buff, size to write                             */
/* Outputs     : 0 for OK                                               */
/************************************************************************/
int memWrite(char* buff, int size)
{
    int i, j, bytes_written, rows, temp;
    DWORD_VAL data;

#if FLASH_MEM_DEBUG 
char debug[64];
#endif
    
    /* if size is too big or too small bailout with error */
    if ( (size < 2) || (size > MAX_PAGE_BYTES) )
        return(-1);
    
    rows = (size + (2*WORDS_PER_ROW - 1)) / (2*WORDS_PER_ROW);

    bytes_written = 0;                      /* clear bytes_written variable */

    NVMADRL = __builtin_tbloffset(paramBuff);
    NVMADRH = __builtin_tblpage(paramBuff);
    NVMCON = PM_ROW_WRITE;
    temp = TBLPAG;
    TBLPAG = 0xFA;

    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < WORDS_PER_ROW; j++)
        {
            if ( ++bytes_written < (size + 1) )   /* assign the results to the data buffer */
                data.v[0] = *(buff++);
            else
                data.v[0] = 0xFF;
        
            if ( ++bytes_written < (size + 1) )
                data.v[1] = *(buff++);
            else
                data.v[1] = 0xFF;

            data.v[2] = 0xFF;        /* set the upper byte and phantom byte to FF       */
            data.v[3] = 0xFF;
            
#if FLASH_MEM_DEBUG 
            sprintf(debug, "address 0x%04X = 0x%04X 0x%04X\r\n", 
                    PARAM_PAGE + i*(2*WORDS_PER_ROW) + 2*j, data.word.HW, data.word.LW);
            serPutString(0, debug);
#endif
            __builtin_tblwtl(2*j, data.word.LW);
            __builtin_tblwth(2*j+1, data.word.HW);
        }

        /* write the latch contents to flash memory */
        __builtin_disi(0x3fff);
        __builtin_write_NVM();

        while (NVMCONbits.WR == 1)
            ; /* wait for bit change */

        __builtin_disi(0);
    }
    
    TBLPAG = temp;
    return 0;
}


/************************************************************************/
/* Function    : memErase                                               */
/* Purpose     : Erase param area of flash memory                       */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void memErase()
{
    __builtin_disi(0x3fff);

    NVMADRL = __builtin_tbloffset(paramBuff);
    NVMADRH = __builtin_tblpage(paramBuff);

    NVMCON = PM_PAGE_ERASE;
    
    __builtin_tblwtl(0, __builtin_tbloffset(paramBuff));
    __builtin_write_NVM();
    
    while (NVMCONbits.WR == 1)
        ; /* wait for bit change */
    
    __builtin_disi(0);
}
