I am unable to perform a full reprogram of the external flash. How do you reprogram the portion of code between indices 0x0000 and 0x1000?
I have functions that perform the erase and write operation that I save into RAM before the programming starts. And I am able to successfully perform erase and write operations as expected with indices starting at 0x1000. These functions do not have any form of FTFE error when they are called and used when I attempt to reprogram starting at 0x0000.
The problem is even though the function notifies a successful write occurred, when I read the firmware image back using JFLASH the program flash contains all FFs.
I also noticed that if I attempt to only perform a sector erase on sector 0 (0x0000 idx) the entire flash gets cleared anyway.
Are there some special rules I need to follow in order to program that portion of flash? Obviously erasing and reprogramming it has to somehow be possible since the MCUExpresso editor can erase and program that portion.
I could really use the help as I need to be able to perform firmware updates on the product I am developing!
I am going through the example code suggested and it appears to use a hardcoded value that is saved in an array for the full program image before writing. Are there examples somewhere of using this but having the image come over SPI?
My implementation uses an external Flash chip connected over SPI bus that holds the new image information.
Hi,
I have tried your code .When I erase 0x0-0x1000,the same issue occur.
We can refer flashloader_loader in sdk package.It includes flash_erase_all_unsecure .It copy code ram ,then it can write and erase flash successfully.
Jianyu:
Have a great day,
TIC
-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!
- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------
Hi,
Thank you for the reply. I cannot seem to find the sdk you mention anywhere in the available SDK components or sample projects portion of MCUExpresso. I also do not see the term flashloader when I do a project search.
Where do I find the sdk that contains flashloader_loader?
The SDK Version I am using is SDK2.x_FRDM-K64F 2.7.0(303 2019-12-19) manifest version 3.6.0.
--------------------- UPDATE ----------------------
I went and downloaded the newest version of bootloader and made sure to require the bootloader examples to be included. I now can see the example. If I have anymore questions I'll be sure to update you.
Hi @sierra18,
Here is relevant code about spi&&flash . I hope it will help you.
#include "w25q.h"
void SPI_Config(void)
{
dspi_master_config_t masterConfig;
gpio_pin_config_t cs_config = {kGPIO_DigitalOutput, 1,};
CLOCK_EnableClock ( kCLOCK_PortD);
PORT_SetPinMux(PORTD, PIN0_IDX, kPORT_MuxAsGpio); /* PORTD0 (pin 93) CS0 */
PORT_SetPinMux(PORTD, PIN1_IDX, kPORT_MuxAlt2); /* PORTD1 (pin 94) is configured as SPI0_SCK */
PORT_SetPinMux(PORTD, PIN2_IDX, kPORT_MuxAlt2); /* PORTD2 (pin 95) is configured as SPI0_SOUT */
PORT_SetPinMux(PORTD, PIN3_IDX, kPORT_MuxAlt2); /* PORTD3 (pin 96) is configured as SPI0_SIN */
GPIO_PinInit (GPIOD,PIN0_IDX,&cs_config);
/* Master config */
masterConfig.whichCtar = kDSPI_Ctar0;
masterConfig.ctarConfig.baudRate = TRANSFER_BAUDRATE;
masterConfig.ctarConfig.bitsPerFrame = 8U;
masterConfig.ctarConfig.cpol = kDSPI_ClockPolarityActiveHigh;
masterConfig.ctarConfig.cpha = kDSPI_ClockPhaseFirstEdge;
masterConfig.ctarConfig.direction = kDSPI_MsbFirst;
masterConfig.ctarConfig.pcsToSckDelayInNanoSec = 1000000000U / TRANSFER_BAUDRATE;
masterConfig.ctarConfig.lastSckToPcsDelayInNanoSec = 1000000000U / TRANSFER_BAUDRATE;
masterConfig.ctarConfig.betweenTransferDelayInNanoSec = 1000000000U / TRANSFER_BAUDRATE;
masterConfig.whichPcs = EXAMPLE_DSPI_MASTER_PCS_FOR_INIT;
masterConfig.pcsActiveHighOrLow = kDSPI_PcsActiveLow;
masterConfig.enableContinuousSCK = false;
masterConfig.enableRxFifoOverWrite = false;
masterConfig.enableModifiedTimingFormat = false;
masterConfig.samplePoint = kDSPI_SckToSin0Clock;
DSPI_MasterInit(EXAMPLE_DSPI_MASTER_BASEADDR, &masterConfig, DSPI_MASTER_CLK_FREQ);
}
void SPI_Transfer(uint8_t *txData,uint8_t *rxData,uint8_t size)
{
dspi_transfer_t masterXfer;
masterXfer.txData = txData;
masterXfer.rxData = rxData;
masterXfer.dataSize = size;
masterXfer.configFlags = kDSPI_MasterPcsContinuous;//kDSPI_MasterCtar0 | kDSPI_MasterPcs0 |
DSPI_MasterTransferBlocking(EXAMPLE_DSPI_MASTER_BASEADDR, &masterXfer);
}
uint8_t SPI_ReadWriteByte(uint8_t inData)
{
uint8_t a = inData,b=0x00;
SPI_Transfer(&a,&b,1);
return b;
}
void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR()&0x01)==0x01);
}
uint16_t W25QXX_ReadID(void)
{
uint16_t Temp = 0;
SPI_ReadWriteByte (0x00);//
W25QXX_CS_Low ;
SPI_ReadWriteByte (0x90);
SPI_ReadWriteByte (0x00);
SPI_ReadWriteByte (0x00);
SPI_ReadWriteByte (0x00);
Temp|=SPI_ReadWriteByte(0xFF)<<8;
Temp|=SPI_ReadWriteByte(0xFF);
W25QXX_CS_High;
return Temp;
}
uint8_t W25QXX_ReadSR(void)
{
uint8_t byte=0;
SPI_ReadWriteByte (0x00);//
W25QXX_CS_Low; //
SPI_ReadWriteByte(W25X_ReadStatusReg); //
byte=SPI_ReadWriteByte(0Xff); //
W25QXX_CS_High; //
return byte;
}
void W25QXX_Write_SR(uint8_t sr)
{
SPI_ReadWriteByte (0x00); //
W25QXX_CS_Low; //
SPI_ReadWriteByte(W25X_WriteStatusReg);//
SPI_ReadWriteByte(sr); //
W25QXX_CS_High; //
}
void W25QXX_Write_Enable(void)
{
SPI_ReadWriteByte (0x00); //
W25QXX_CS_Low; //
SPI_ReadWriteByte(W25X_WriteEnable); //
W25QXX_CS_High; //
}
void W25QXX_Write_Disable(void)
{
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_WriteDisable);
W25QXX_CS_High;
}
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i;
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_ReadData);
SPI_ReadWriteByte((uint8_t)((ReadAddr)>>16));
SPI_ReadWriteByte((uint8_t)((ReadAddr)>>8));
SPI_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
*pBuffer++ = SPI_ReadWriteByte(0XFF);
}
W25QXX_CS_High;
}
void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t i;
W25QXX_Write_Enable();
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_PageProgram);
SPI_ReadWriteByte((uint8_t)((WriteAddr)>>16));
SPI_ReadWriteByte((uint8_t)((WriteAddr)>>8));
SPI_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++)
{
SPI_ReadWriteByte(*pBuffer++);
}
W25QXX_CS_High;
W25QXX_Wait_Busy();
}
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; //
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//
while(1)
{
W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;//
else //NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain; //
if(NumByteToWrite>256)
{
pageremain=256; //
}
else
{
pageremain=NumByteToWrite; //
}
}
}
}
uint8_t W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;
secoff=WriteAddr%4096;
secremain=4096-secoff;
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;
while(1)
{
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);
for(i=0;i<secremain;i++)
{
if(W25QXX_BUF[secoff+i]!=0XFF) break;
}
if(i<secremain)
{
W25QXX_Erase_Sector(secpos);
for(i=0;i<secremain;i++)
{
W25QXX_BUF[i+secoff] = *pBuffer++;
}
W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);
}
else
{
W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);
}
if(NumByteToWrite==secremain)
{
break;
}
else
{
secpos++;
secoff=0;
pBuffer += secremain;
WriteAddr += secremain;
NumByteToWrite -= secremain;
if(NumByteToWrite>4096)secremain=4096;
else
{
secremain=NumByteToWrite;
}
}
}
}
void W25QXX_Erase_Chip(void)
{
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_ChipErase);
W25QXX_CS_High;
W25QXX_Wait_Busy();
}
void W25QXX_Erase_Sector(uint32_t Dst_Addr)
{
Dst_Addr*=4096;
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_SectorErase);
SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>16));
SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
SPI_ReadWriteByte((uint8_t)Dst_Addr);
W25QXX_CS_High;
W25QXX_Wait_Busy();
}
void W25QXX_PowerDown(void)
{
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_PowerDown);
W25QXX_CS_High;
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
}
void W25QXX_WAKEUP(void)
{
SPI_ReadWriteByte (0x00);
W25QXX_CS_Low;
SPI_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
W25QXX_CS_High;
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
__asm("NOP");
}
@nxf58904 Is this SPI communication something that I need to use to modify bootloader code generated in the frdm64f_flashloader example? If so I'm having troubles understanding where to start looking to try and make these modifications. Is there some form of resource available that walks through what items are modifiable?
As I am new to the world of writing code that interacts with bootloaders, the reliance on terminology makes the example project very hard to understand.
Hi
There is no difference in the first Flash sector in terms of erase and write behavior.
However one has to be very careful since the flash configuration (from 0x400) will also be erased and also - if interrupts are operating directly from the reset vector area - these will not be operational during and after an erasure until new ones are installed.
I have used a solution for K64s to upgrade loaders in the field (requiring also erasure and reprogramming of the first sector) and can confirm that it is possible, however it is a delicate task due to the fact that if there were a reset/power loss during the operation the chip could be left in an un-programmed state (neither reset vector nor flash configuration) and need reprogramming (with Jtag) to recover. Therefore it was only intended for critical loader updates (controlled by a special application) and not used generally.
If you require general field updates you should never reprogram the area occupied by the loader itself, which always includes sector 0. There are mature out-of-the-box K64 solutions at
- https://www.utasker.com/kinetis/FRDM-K64F.html
- https://www.utasker.com/kinetis/TWR-K64F120M.html
- https://www.utasker.com/kinetis/TEENSY_3.5.html
if you don't have a full solution yet.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or product development requirements
Can you post your Flash write code?
Here is mine - I call the "FlashSectorEraseWrite()" method with the sector to be updated along with a data buffer (the size of the sector containing the sector information after the write):
uint32_t flashSectorEraseWrite(uint32_t sector
, uint32_t* source) {
uint32_t returnValue = FLASH_RESPONSE_NOERROR;
status_t status;
__disable_irq();
if (kStatus_FLASH_Success != (status = FLASH_Erase(&s_flashDriver
, sector
, FSL_FEATURE_FLASH_PFLASH_BLOCK_SECTOR_SIZE
// , kFLASH_ApiEraseKey))) {
, kFTFx_ApiEraseKey))) {
#if (2 != SDK_DEBUGCONSOLE)
PRINTF("\nFLASH_Erase returned %i"
, status);
#endif
returnValue = FLASH_RESPONSE_WRITEFAILURE;
}
__enable_irq();
if (FLASH_RESPONSE_NOERROR == returnValue) {
__disable_irq();
if (kStatus_FLASH_Success != (status = FLASH_Program(&s_flashDriver
, sector
, (uint8_t*)source
, FSL_FEATURE_FLASH_PFLASH_BLOCK_SECTOR_SIZE))) {
#if (2 != SDK_DEBUGCONSOLE)
PRINTF("\nFLASH_Program returned %i"
, status);
#endif
returnValue = FLASH_RESPONSE_WRITEFAILURE;
}
__enable_irq();
}
return returnValue;
}
Sure, here are the functions I use for erasing a sector and writing a section, I pass in the starting address and the size written. I notice in your code you disable and enable interrupts. I do a single disabling of interrupts before I begin the rewriting process via a call to __asm("cpsid i");
/*!
* Programs a section of Kinetis FLASH from Programming Acceleration RAM
* @param address Address in Kinetis FLASH to program.
* @param size Number of phrases to program (phrases are 8 bytes long)
*
*/
uint8_t __relocate_section__ _FtflProgramSection(uint32_t address, uint16_t size)
{
uint8_t status;
// uint16_t index;
// uint64_t *ProgramAccelMem = (uint64_t*)PROGRAMMING_RAM_ACCELERATION_START_ADDR;
DGBFWPG_PUTC('+');
// We will directly program an entire section of memory only
while(((status = FTFE->FSTAT) & FTFE_FSTAT_CCIF_MASK) == 0);
if( (status & FTFE_FSTAT_ACCERR_MASK) || (status & FTFE_FSTAT_FPVIOL_MASK) )
FTFE->FSTAT = 0x30;
FTFE->FCCOB0 = FtflCmdProgramSection; //the enum value is 11
FTFE->FCCOB1 = (uint8_t) (0xFF&(address>>16));//FCCOB0FTFL_FCCOB1
FTFE->FCCOB2 = (uint8_t) (0xFF&(address>>8)); //FTFL_FCCOB2
FTFE->FCCOB3 = (uint8_t) (0xFF&address); //FTFL_FCCOB3
FTFE->FCCOB4 = (uint8_t) (0xFF&(size>>8)); //FTFL_FCCOB4
FTFE->FCCOB5 = (uint8_t) (0xFF&size); //FTFL_FCCOB5
// Trigger Flash operation
FTFE->FSTAT = 0x80;
do
{
status = FTFE->FSTAT;
}while(status == 0);
if(status & FTFE_FSTAT_ACCERR_MASK)
{
DGBFWPG_PUTC('#');
}
if(status & FTFE_FSTAT_RDCOLERR_MASK)
{
DGBFWPG_PUTC('%');
}
if(status & 0x70)
{
// error flag is set
DGBFWPG_PUTC('$');
}
return status;
}
uint8_t __relocate_section__ _FtflEraseSector(uint32_t address, uint16_t size)
{
uint8_t status;
// uint16_t index;
// uint64_t *ProgramAccelMem = (uint64_t*)PROGRAMMING_RAM_ACCELERATION_START_ADDR;
do
{
status = FTFE->FSTAT;// FTFL_FSTAT;
}while((status & FTFE_FSTAT_CCIF_MASK) == 0);
if( (status & FTFE_FSTAT_ACCERR_MASK) || (status & FTFE_FSTAT_FPVIOL_MASK) )
FTFE->FSTAT = 0x30;
FTFE->FCCOB0 = FtflCmdEraseFlashSector;
FTFE->FCCOB1 = (uint8_t) (0xFF&(address>>16));//FCCOB0FTFL_FCCOB1
FTFE->FCCOB2 = (uint8_t) (0xFF&(address>>8)); //FTFL_FCCOB2
FTFE->FCCOB3 = (uint8_t) (0xFF&address); //FTFL_FCCOB3
// FTFE->FCCOB4 = (uint8_t) (0xFF&(size>>8)); //FTFL_FCCOB4
// FTFE->FCCOB5 = (uint8_t) (0xFF&size); //FTFL_FCCOB5
// Trigger Flash operation
FTFE->FSTAT = 0x80;
do
{
status = FTFE->FSTAT;
}while(status == 0);
if(status & FTFE_FSTAT_ACCERR_MASK)
{
DGBFWPG_PUTC('#');
}
if(status & FTFE_FSTAT_RDCOLERR_MASK)
{
DGBFWPG_PUTC('%');
}
if(status & 0x70)
{
// error flag is set
DGBFWPG_PUTC('$');
}
return status;
}