#include "chipflash.h"
#include "string.h"
#include <stdalign.h> 

//H523 扇区大小为8K Byte

// 算出目标地址在哪个扇区
static uint32_t bsp_get_sector(uint32_t Addr)
{
    uint32_t sector = 0;
    sector = (Addr - CPU_FLASH_BASE_ADDR) / SECTOR_SIZE;
    return sector;
}

// 读取目标地址范围FLASH内容
static uint8_t bsp_read_cpu_flash(uint32_t ulFlashAddr, uint8_t *ucpDst, uint32_t ulSize)
{
    uint32_t i;
    if (ulFlashAddr + ulSize > CPU_FLASH_BASE_ADDR + CPU_FLASH_SIZE)
    {
        return 1;
    }
    if (ulSize == 0) // 长度为0时返回，否则起始地址为奇数地址会出错
    {
        return 1;
    }
    for (i = 0; i < ulSize; i++)
    {
        *ucpDst++ = *(uint8_t *)ulFlashAddr++;
    }
    return 0;
}

// 比较FLASH 指定地址的数据是否一致
static uint8_t bsp_cmp_cpu_flash(uint32_t ulFlashAddr, uint8_t *ucpBuf, uint32_t ulSize)
{
    uint32_t i;
    uint8_t ucIsEqu; // 相等标志位
    uint8_t ucByte;
    // 如果偏移地址超过芯片容量，则不改写输出缓冲区
    if (ulFlashAddr + ulSize > CPU_FLASH_BASE_ADDR + CPU_FLASH_SIZE)
    {
        return FLASH_PARAM_ERR;
    }

    if (ulSize == 0)
    {
        return FLASH_IS_EQU; // FLASH内容与待写入数据相等
    }

    ucIsEqu = 1; // 假设所有字节和待写入的数据相等，如果遇到任何一个不相等，则设置为0
    for (i = 0; i < ulSize; i++)
    {
        ucByte = *(uint8_t *)ulFlashAddr; // 取1字节出来作比较
        if (ucByte != *ucpBuf)            // 不一致
        {
            if (ucByte != 0xFF) // 且不等于默认
            {
                return FLASH_REQ_ERASE;
            }
            else
            {
                ucIsEqu = 0; // 标志位置0，不一致，需要写
            }
        }

        ulFlashAddr++;
        ucpBuf++;
    }

    if (ucIsEqu == 1)
    {
        return FLASH_IS_EQU; // 一致
    }
    else
    {
        return FLASH_REQ_WRITE; // 直接写
    }
}

// 16字节整倍数写，不支持跨扇区，扇区8KB，长度不是8字节整数倍，最后几个字节末尾补0写入
static uint8_t bsp_write_cpu_flash(uint32_t ulFlashAddr, uint8_t *ucpSrc, uint32_t ulSize)
{
    uint32_t i;
    uint8_t ucRet;

    if (ulFlashAddr + ulSize > CPU_FLASH_BASE_ADDR + CPU_FLASH_SIZE || ulFlashAddr < CPU_FLASH_BASE_ADDR)
    {
        return 1;
    }
    if (ulSize == 0)
    {
        return 0;
    }
	//地址必须16字节对齐
	if (ulFlashAddr & 15 )
		return 1;

    ucRet = bsp_cmp_cpu_flash(ulFlashAddr, ucpSrc, ulSize);

    if (ucRet == FLASH_IS_EQU)
    {
        return 0;
    }

    __set_PRIMASK(1);

    HAL_FLASH_Unlock();

    for (i = 0; i < ulSize / 16; i++)
    {
        __ALIGNED(4) uint8_t FlashQuadWord[16] = {0};
        memcpy( FlashQuadWord, ucpSrc, 16);
        ucpSrc += 16;

        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, ulFlashAddr, (uint32_t)&FlashQuadWord[0]) == HAL_OK) // 双字64bit
        {
            ulFlashAddr = ulFlashAddr + 16; // 递增128bit
        }
        else
        {
            goto err;
        }
    }

    if (ulSize & 15) // 剩余的部分，如果不是16字节整数倍，剩余部分都是0
    {
        __ALIGNED(4) uint8_t FlashWord[16] = {0};
        memcpy(FlashWord, ucpSrc, ulSize & 15);
		uint32_t ptr = (uint32_t)(&FlashWord[0]);  
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, ulFlashAddr, (uint32_t)(&FlashWord[0])) != HAL_OK)
        {
            goto err;
        }
    }
   if( HAL_FLASH_Lock() != HAL_OK)
   {
	   goto err;
   }
    __set_PRIMASK(0);
    return 0;

err:
    HAL_FLASH_Lock();
    __set_PRIMASK(0);
    return 1;
}

static uint8_t bsp_erase_cpu_flash(uint32_t ulFlashAddr, uint32_t u2FlashAddr)
{
    uint32_t FirstSector = 0, NumofSectors = 0, FinishSector = 0;
    FLASH_EraseInitTypeDef EraseInitStruct;
    uint32_t SECTOR_ERR = 0;
    uint8_t ret;

    __set_PRIMASK(1);
    HAL_FLASH_Unlock();

    // 获取此地址所在的扇区
    FirstSector = bsp_get_sector(ulFlashAddr);
    FinishSector = bsp_get_sector(u2FlashAddr);

    // 固定1个扇区
	NumofSectors =
		(FinishSector - FirstSector > 0) ?  (FinishSector - FirstSector) : 1; 

    // 擦除扇区配置
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
    EraseInitStruct.Banks = FLASH_BANK_1;
    EraseInitStruct.Sector = FirstSector;
    EraseInitStruct.NbSectors = NumofSectors;

    // 扇区擦除
    ret = HAL_FLASHEx_Erase(&EraseInitStruct, &SECTOR_ERR);

    HAL_FLASH_Lock();

    __set_PRIMASK(0);

    return ret;
}

// _CHIP_INFO chip_info;

// static void get_chip_info(void)
// {
//     chip_info.ChipUniqueID[0] = *(__IO uint32_t*)(ChipUniqueID_HIGH);
//     chip_info.ChipUniqueID[1] = *(__IO uint32_t*)(ChipUniqueID_MEDIUM);
//     chip_info.ChipUniqueID[2] = *(__IO uint32_t*)(ChipUniqueID_LOW);
//     chip_info.ChipFlashSize = *(__IO uint16_t*)(CHIPFLASHSIZE);
//     chip_info.ChipMCUID = *(__IO uint32_t*)(CHIPMCUID);
// }

__FLASH_OPS _flash_ops = {
    .Read_Flash = bsp_read_cpu_flash,
    .Write_Flash = bsp_write_cpu_flash,
    .Erase_Flash = bsp_erase_cpu_flash,
};

__FLASH_OPS *flash_ops = &_flash_ops;