mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
443 lines
12 KiB
C
443 lines
12 KiB
C
/*
|
|
* Flash.c
|
|
*
|
|
* Created on: 20.03.2013
|
|
* Author: skuser
|
|
*/
|
|
|
|
#include "Memory.h"
|
|
#include "Configuration.h"
|
|
#include "Common.h"
|
|
#include "Settings.h"
|
|
#include "LEDHook.h"
|
|
|
|
#define USE_DMA
|
|
#define RECV_DMA DMA.CH0
|
|
#define SEND_DMA DMA.CH1
|
|
|
|
/* Convert defines from Makefile */
|
|
#define FLASH_DATA_START FLASH_DATA_ADDR
|
|
#define FLASH_DATA_END (FLASH_DATA_ADDR + FLASH_DATA_SIZE - 1)
|
|
|
|
/* Definitions for FRAM */
|
|
#define FRAM_USART USARTD0
|
|
#define FRAM_PORT PORTD
|
|
#define FRAM_CS PIN4_bm
|
|
#define FRAM_MOSI PIN3_bm
|
|
#define FRAM_MISO PIN2_bm
|
|
#define FRAM_SCK PIN1_bm
|
|
|
|
/* Declarations from assembler file */
|
|
uint16_t FlashReadWord(uint32_t Address);
|
|
void FlashEraseApplicationPage(uint32_t Address);
|
|
void FlashLoadFlashWord(uint16_t Address, uint16_t Data);
|
|
void FlashEraseWriteApplicationPage(uint32_t Address);
|
|
void FlashEraseFlashBuffer(void);
|
|
void FlashWaitForSPM(void);
|
|
|
|
static uint8_t ScrapBuffer[] = {0};
|
|
|
|
INLINE uint8_t SPITransferByte(uint8_t Data)
|
|
{
|
|
FRAM_USART.DATA = Data;
|
|
|
|
while (!(FRAM_USART.STATUS & USART_RXCIF_bm));
|
|
|
|
return FRAM_USART.DATA;
|
|
}
|
|
|
|
#ifdef USE_DMA
|
|
INLINE void SPIReadBlock(void* Buffer, uint16_t ByteCount)
|
|
{
|
|
/* Set up read and write transfers */
|
|
RECV_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_INC_gc;
|
|
RECV_DMA.DESTADDR0 = ((uintptr_t) Buffer >> 0) & 0xFF;
|
|
RECV_DMA.DESTADDR1 = ((uintptr_t) Buffer >> 8) & 0xFF;
|
|
RECV_DMA.TRFCNT = ByteCount;
|
|
SEND_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc;
|
|
SEND_DMA.SRCADDR0 = ((uintptr_t) ScrapBuffer >> 0) & 0xFF;
|
|
SEND_DMA.SRCADDR1 = ((uintptr_t) ScrapBuffer >> 8) & 0xFF;
|
|
SEND_DMA.TRFCNT = ByteCount;
|
|
|
|
|
|
/* Enable read and write transfers */
|
|
RECV_DMA.CTRLA |= DMA_CH_ENABLE_bm;
|
|
SEND_DMA.CTRLA |= DMA_CH_ENABLE_bm;
|
|
|
|
/* Wait for DMA to finish */
|
|
while( RECV_DMA.CTRLA & DMA_CH_ENABLE_bm )
|
|
;
|
|
|
|
/* Clear Interrupt flag */
|
|
RECV_DMA.CTRLB = DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm;
|
|
SEND_DMA.CTRLB = DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm;
|
|
}
|
|
#else
|
|
INLINE void SPIReadBlock(void* Buffer, uint16_t ByteCount)
|
|
{
|
|
uint8_t* ByteBuffer = (uint8_t*) Buffer;
|
|
|
|
while(ByteCount-- > 0) {
|
|
FRAM_USART.DATA = 0;
|
|
while (!(FRAM_USART.STATUS & USART_RXCIF_bm));
|
|
|
|
*ByteBuffer++ = FRAM_USART.DATA;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_DMA
|
|
INLINE void SPIWriteBlock(const void* Buffer, uint16_t ByteCount)
|
|
{
|
|
/* Set up read and write transfers */
|
|
RECV_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc;
|
|
RECV_DMA.DESTADDR0 = ((uintptr_t) ScrapBuffer >> 0) & 0xFF;
|
|
RECV_DMA.DESTADDR1 = ((uintptr_t) ScrapBuffer >> 8) & 0xFF;
|
|
RECV_DMA.TRFCNT = ByteCount;
|
|
SEND_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc;
|
|
SEND_DMA.SRCADDR0 = ((uintptr_t) Buffer >> 0) & 0xFF;
|
|
SEND_DMA.SRCADDR1 = ((uintptr_t) Buffer >> 8) & 0xFF;
|
|
SEND_DMA.TRFCNT = ByteCount;
|
|
|
|
/* Enable read and write transfers */
|
|
RECV_DMA.CTRLA |= DMA_CH_ENABLE_bm;
|
|
SEND_DMA.CTRLA |= DMA_CH_ENABLE_bm;
|
|
|
|
/* Wait for DMA to finish */
|
|
while( RECV_DMA.CTRLA & DMA_CH_ENABLE_bm )
|
|
;
|
|
|
|
/* Clear Interrupt flag */
|
|
RECV_DMA.CTRLB = DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm;
|
|
SEND_DMA.CTRLB = DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm;
|
|
}
|
|
#else
|
|
INLINE void SPIWriteBlock(const void* Buffer, uint16_t ByteCount)
|
|
{
|
|
uint8_t* ByteBuffer = (uint8_t*) Buffer;
|
|
|
|
while(ByteCount-- > 0) {
|
|
FRAM_USART.DATA = *ByteBuffer++;
|
|
while (!(FRAM_USART.STATUS & USART_RXCIF_bm));
|
|
|
|
FRAM_USART.DATA; /* Flush Buffer */
|
|
}
|
|
}
|
|
#endif
|
|
|
|
INLINE void FRAMRead(void* Buffer, uint16_t Address, uint16_t ByteCount)
|
|
{
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
|
|
SPITransferByte(0x03); /* Read command */
|
|
SPITransferByte( (Address >> 8) & 0xFF ); /* Address hi and lo byte */
|
|
SPITransferByte( (Address >> 0) & 0xFF );
|
|
|
|
SPIReadBlock(Buffer, ByteCount);
|
|
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
}
|
|
|
|
INLINE void FRAMWrite(const void* Buffer, uint16_t Address, uint16_t ByteCount)
|
|
{
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
SPITransferByte(0x06); /* Write Enable */
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
|
|
asm volatile ("nop");
|
|
asm volatile ("nop");
|
|
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
|
|
SPITransferByte(0x02); /* Write command */
|
|
SPITransferByte( (Address >> 8) & 0xFF ); /* Address hi and lo byte */
|
|
SPITransferByte( (Address >> 0) & 0xFF );
|
|
|
|
SPIWriteBlock(Buffer, ByteCount);
|
|
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
}
|
|
|
|
INLINE void FlashRead(void* Buffer, uint32_t Address, uint16_t ByteCount)
|
|
{
|
|
uint8_t* BufPtr = (uint8_t*) Buffer;
|
|
|
|
/* We assume that ByteCount is a multiple of 2 */
|
|
uint32_t PhysicalAddress = Address + FLASH_DATA_ADDR;
|
|
|
|
if ( (PhysicalAddress >= FLASH_DATA_START) && (PhysicalAddress <= FLASH_DATA_END) ) {
|
|
/* Sanity check to limit access to the allocated area */
|
|
while(ByteCount > 1) {
|
|
uint16_t Word = FlashReadWord(PhysicalAddress);
|
|
|
|
*BufPtr++ = (Word >> 0) & 0xFF;
|
|
*BufPtr++ = (Word >> 8) & 0xFF;
|
|
|
|
PhysicalAddress += 2;
|
|
ByteCount -= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
INLINE void FlashWrite(const void* Buffer, uint32_t Address, uint16_t ByteCount)
|
|
{
|
|
const uint8_t* BufPtr = (uint8_t*) Buffer;
|
|
|
|
/* We assume that FlashWrite is always called for write actions that are
|
|
* aligned to APP_SECTION_PAGE_SIZE and a multiple of APP_SECTION_PAGE_SIZE.
|
|
* Thus only full pages are written into the flash. */
|
|
uint16_t PageCount = ByteCount / APP_SECTION_PAGE_SIZE;
|
|
uint32_t PhysicalAddress = Address + FLASH_DATA_ADDR;
|
|
|
|
if ( (PhysicalAddress >= FLASH_DATA_START) && (PhysicalAddress <= FLASH_DATA_END) ) {
|
|
/* Sanity check to limit access to the allocated area */
|
|
|
|
while(PageCount-- > 0) {
|
|
/* For each page to program, wait for NVM to get ready,
|
|
* erase the flash page buffer, program all data to the
|
|
* flash page buffer and write buffer to flash using
|
|
* the atomic erase and write operation. */
|
|
|
|
FlashWaitForSPM();
|
|
|
|
FlashEraseFlashBuffer();
|
|
FlashWaitForSPM();
|
|
|
|
for (uint16_t i=0; i<APP_SECTION_PAGE_SIZE; i += 2) {
|
|
uint16_t Word = 0;
|
|
|
|
Word |= ( (uint16_t) *BufPtr++ << 0);
|
|
Word |= ( (uint16_t) *BufPtr++ << 8);
|
|
|
|
FlashLoadFlashWord(i, Word);
|
|
FlashWaitForSPM();
|
|
}
|
|
|
|
FlashEraseWriteApplicationPage(PhysicalAddress);
|
|
FlashWaitForSPM();
|
|
|
|
PhysicalAddress += APP_SECTION_PAGE_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
INLINE void FlashErase(uint32_t Address, uint16_t ByteCount)
|
|
{
|
|
uint16_t PageCount = ByteCount / APP_SECTION_PAGE_SIZE;
|
|
uint32_t PhysicalAddress = Address + FLASH_DATA_ADDR;
|
|
|
|
if ( (PhysicalAddress >= FLASH_DATA_START) && (PhysicalAddress <= FLASH_DATA_END) ) {
|
|
/* Sanity check to limit access to the allocated area */
|
|
while(PageCount-- > 0) {
|
|
FlashWaitForSPM();
|
|
|
|
FlashEraseApplicationPage(PhysicalAddress);
|
|
FlashWaitForSPM();
|
|
|
|
PhysicalAddress += APP_SECTION_PAGE_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
INLINE void FlashToFRAM(uint32_t Address, uint16_t ByteCount)
|
|
{
|
|
/* We assume that ByteCount is a multiple of 2 */
|
|
uint32_t PhysicalAddress = Address + FLASH_DATA_ADDR;
|
|
|
|
if ( (PhysicalAddress >= FLASH_DATA_START) && (PhysicalAddress <= FLASH_DATA_END) ) {
|
|
/* Sanity check to limit access to the allocated area.
|
|
* Set up FRAM memory for writing. */
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
SPITransferByte(0x06); /* Write Enable */
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
|
|
asm volatile ("nop");
|
|
asm volatile ("nop");
|
|
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
|
|
SPITransferByte(0x02); /* Write command */
|
|
SPITransferByte(0); /* Address hi and lo byte */
|
|
SPITransferByte(0);
|
|
|
|
/* Loop through bytes, read words from flash and write
|
|
* double byte into FRAM. */
|
|
while(ByteCount > 1) {
|
|
uint16_t Word = FlashReadWord(PhysicalAddress);
|
|
|
|
SPITransferByte( (Word >> 0) & 0xFF );
|
|
SPITransferByte( (Word >> 8) & 0xFF );
|
|
|
|
PhysicalAddress += 2;
|
|
ByteCount -= 2;
|
|
}
|
|
|
|
/* End write procedure of FRAM */
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
}
|
|
}
|
|
|
|
INLINE void FRAMToFlash(uint32_t Address, uint16_t ByteCount)
|
|
{
|
|
/* We assume that FlashWrite is always called for write actions that are
|
|
* aligned to APP_SECTION_PAGE_SIZE and a multiple of APP_SECTION_PAGE_SIZE.
|
|
* Thus only full pages are written into the flash. */
|
|
uint16_t PageCount = ByteCount / APP_SECTION_PAGE_SIZE;
|
|
uint32_t PhysicalAddress = Address + FLASH_DATA_ADDR;
|
|
|
|
if ( (PhysicalAddress >= FLASH_DATA_START) && (PhysicalAddress <= FLASH_DATA_END) ) {
|
|
/* Sanity check to limit access to the allocated area and setup FRAM
|
|
* read. */
|
|
FRAM_PORT.OUTCLR = FRAM_CS;
|
|
|
|
SPITransferByte(0x03); /* Read command */
|
|
SPITransferByte(0); /* Address hi and lo byte */
|
|
SPITransferByte(0);
|
|
|
|
while(PageCount-- > 0) {
|
|
/* For each page to program, wait for NVM to get ready,
|
|
* erase the flash page buffer, program all data to the
|
|
* flash page buffer and write buffer to flash using
|
|
* the atomic erase and write operation. */
|
|
|
|
FlashWaitForSPM();
|
|
|
|
FlashEraseFlashBuffer();
|
|
FlashWaitForSPM();
|
|
|
|
/* Write one page worth of data into flash buffer */
|
|
for (uint16_t i=0; i<APP_SECTION_PAGE_SIZE; i += 2) {
|
|
uint16_t Word = 0;
|
|
|
|
Word |= ( (uint16_t) SPITransferByte(0) << 0);
|
|
Word |= ( (uint16_t) SPITransferByte(0) << 8);
|
|
|
|
FlashLoadFlashWord(i, Word);
|
|
FlashWaitForSPM();
|
|
}
|
|
|
|
/* Program flash buffer into flash */
|
|
FlashEraseWriteApplicationPage(PhysicalAddress);
|
|
FlashWaitForSPM();
|
|
|
|
PhysicalAddress += APP_SECTION_PAGE_SIZE;
|
|
}
|
|
|
|
/* End read procedure of FRAM */
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
}
|
|
}
|
|
|
|
void MemoryInit(void)
|
|
{
|
|
/* Configure FRAM_USART for SPI master mode 0 with maximum clock frequency */
|
|
FRAM_PORT.OUTSET = FRAM_CS;
|
|
|
|
FRAM_PORT.OUTCLR = FRAM_SCK;
|
|
FRAM_PORT.OUTSET = FRAM_MOSI;
|
|
|
|
FRAM_PORT.DIRSET = FRAM_SCK | FRAM_MOSI | FRAM_CS;
|
|
|
|
FRAM_USART.BAUDCTRLA = 0;
|
|
FRAM_USART.BAUDCTRLB = 0;
|
|
FRAM_USART.CTRLC = USART_CMODE_MSPI_gc;
|
|
FRAM_USART.CTRLB = USART_RXEN_bm | USART_TXEN_bm;
|
|
|
|
/* Init DMAs for reading and writing */
|
|
RECV_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc;
|
|
RECV_DMA.TRIGSRC = DMA_CH_TRIGSRC_USARTD0_RXC_gc;
|
|
RECV_DMA.TRFCNT = 0;
|
|
RECV_DMA.SRCADDR0 = ((uintptr_t) &FRAM_USART.DATA >> 0) & 0xFF;
|
|
RECV_DMA.SRCADDR1 = ((uintptr_t) &FRAM_USART.DATA >> 8) & 0xFF;
|
|
RECV_DMA.SRCADDR2 = 0;
|
|
RECV_DMA.DESTADDR0 = 0;
|
|
RECV_DMA.DESTADDR1 = 0;
|
|
RECV_DMA.DESTADDR2 = 0;
|
|
RECV_DMA.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_1BYTE_gc;
|
|
|
|
SEND_DMA.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc | DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_FIXED_gc;
|
|
SEND_DMA.TRIGSRC = DMA_CH_TRIGSRC_USARTD0_DRE_gc;
|
|
SEND_DMA.TRFCNT = 0;
|
|
SEND_DMA.SRCADDR0 = 0;
|
|
SEND_DMA.SRCADDR1 = 0;
|
|
SEND_DMA.SRCADDR2 = 0;
|
|
SEND_DMA.DESTADDR0 = ((uintptr_t) &FRAM_USART.DATA >> 0) & 0xFF;
|
|
SEND_DMA.DESTADDR1 = ((uintptr_t) &FRAM_USART.DATA >> 8) & 0xFF;
|
|
SEND_DMA.DESTADDR2 = 0;
|
|
SEND_DMA.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_1BYTE_gc;
|
|
}
|
|
|
|
void MemoryReadBlock(void* Buffer, uint16_t Address, uint16_t ByteCount)
|
|
{
|
|
if (ByteCount == 0)
|
|
return;
|
|
FRAMRead(Buffer, Address, ByteCount);
|
|
}
|
|
|
|
void MemoryWriteBlock(const void* Buffer, uint16_t Address, uint16_t ByteCount)
|
|
{
|
|
if (ByteCount == 0)
|
|
return;
|
|
FRAMWrite(Buffer, Address, ByteCount);
|
|
|
|
LEDHook(LED_MEMORY_CHANGED, LED_ON);
|
|
}
|
|
|
|
void MemoryClear(void)
|
|
{
|
|
FlashErase((uint32_t) GlobalSettings.ActiveSettingIdx * MEMORY_SIZE_PER_SETTING, MEMORY_SIZE_PER_SETTING);
|
|
|
|
MemoryRecall();
|
|
}
|
|
|
|
void MemoryRecall(void)
|
|
{
|
|
/* Recall memory from permanent flash */
|
|
FlashToFRAM((uint32_t) GlobalSettings.ActiveSettingIdx * MEMORY_SIZE_PER_SETTING, MEMORY_SIZE_PER_SETTING);
|
|
}
|
|
|
|
void MemoryStore(void)
|
|
{
|
|
/* Store current memory into permanent flash */
|
|
FRAMToFlash((uint32_t) GlobalSettings.ActiveSettingIdx * MEMORY_SIZE_PER_SETTING, MEMORY_SIZE_PER_SETTING);
|
|
|
|
LEDHook(LED_MEMORY_CHANGED, LED_OFF);
|
|
LEDHook(LED_MEMORY_STORED, LED_PULSE);
|
|
}
|
|
|
|
bool MemoryUploadBlock(void* Buffer, uint32_t BlockAddress, uint16_t ByteCount)
|
|
{
|
|
if (BlockAddress >= MEMORY_SIZE_PER_SETTING) {
|
|
/* Prevent writing out of bounds by silently ignoring it */
|
|
return true;
|
|
} else {
|
|
/* Calculate bytes left in memory and start writing */
|
|
uint32_t BytesLeft = MEMORY_SIZE_PER_SETTING - BlockAddress;
|
|
ByteCount = MIN(ByteCount, BytesLeft);
|
|
|
|
/* Store to local memory */
|
|
FRAMWrite(Buffer, BlockAddress, ByteCount);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool MemoryDownloadBlock(void* Buffer, uint32_t BlockAddress, uint16_t ByteCount)
|
|
{
|
|
if (BlockAddress >= MEMORY_SIZE_PER_SETTING) {
|
|
/* There are bytes out of bounds to be read. Notify that we are done. */
|
|
return false;
|
|
} else {
|
|
/* Calculate bytes left in memory and issue reading */
|
|
uint32_t BytesLeft = MEMORY_SIZE_PER_SETTING - BlockAddress;
|
|
ByteCount = MIN(ByteCount, BytesLeft);
|
|
|
|
/* Output local memory contents */
|
|
FRAMRead(Buffer, BlockAddress, ByteCount);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|