jflash是segger开发的一款软件,需要配合jlink一起使用。使用过jlink的人都知道,在项目开发调试阶段非常有帮助。
jflash下载算法就是实现当jflash中没有自己使用的芯片,或者想使用jflash将程序下载到外部flash中时,通过jflash调用flash下载算法程序完成对flash的擦除、读、写、校验操作。jflash的界面如下所示:
jflash下载算法的制作,在mdk中提供了对应的模板工程,路径在Keil_v5\ARM\Flash\_Template
中,可以直接使用模板工程,在其中添加自己的flash驱动即可。
本文就不过多介绍整个过程了,具体的步骤,请参考:
从零编写STM32H7的MDK SPI FLASH下载算法。
或csdn对其的转载
从零编写STM32H7的MDK SPI FLASH下载算法。
本文重点讲述遇到的问题,并介绍解决方法。
此问题可以参考我的另一篇博客:mdk制作外部flash下载算法, 编译错误 L6248E。
主要是因为在sfud里面,多个地方定义了char *name
指针用于存储flash型号的名字。但是因为flash下载算法要求编译出的代码必须是地址无关的代码,因此这里不能用指针,需要为name
明确地址空间。这里需要将指针改为数组,因为数组的地址空间编译后是确定的,改动点如下:
将所以*name
指针,改为数组:
typedef struct __sfud_spi {
/* SPI device name */
char name[32];
/* 以下有省略 */
} sfud_spi, *sfud_spi_t;
flash下载算法没有启动文件,因此我们无法设置程序运行的堆栈的大小,但是默认的堆栈又很小,当函数嵌套过多或局部变量定义太多时就会出现栈溢出,导致覆盖正常程序使用的变量,此问题很难定位,我在测试中就遇到了这个问题。
; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW,+ZI)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
上述脚本中,PRG
所指定的内存区域是flash下载算法所在的区域。DSCR
所指定的区域主要用于存放FlashDevice
这个结构体,jflash通过获取此区域的这个结构体可以知道flash的一些基本信息。
按这个链接脚本生成的map文件中PRG和DSCR区域部分如下:
从上图可以看出.bss
段所占的空间并没有算在PrgData中,而0x0000373c这个地址正好是代码中定义的4096byte的数组如下:
源码如下:
#define BUF_SIZE 4096
uint8_t aux_buf[BUF_SIZE];
从map文件还能看出,sfud.o中的变量也在.bss段中,是在0x0000473c这个位置:
但是DSCR
的起始位置是0x0000373c,说明了.bss段在这里并没有分配空间。这也就说明了.bss段并不占用生成的程序的大小,而是在启动时由启动文件把它加载到sram的另一块地方,然后初始化为0,并且.bss段和栈空间会放在一起进行空间分配。
如果是一个正常程序,这样当然没问题。但是我们的flash下载算法程序就不能这样操作了,因为它没有启动文件,flash算法运行时.bss段处的数据也没有加载到sram这个操作,因此。bss段中的变量仍旧是在0x0000373c开始处的空间。
在Init
函数中,添加打印堆栈的代码,如下:
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
int result = 0;
UART0_Init(115200, USART_WordLength_8b, USART_StopBits_1b, USART_Parity_No);
SFUD_INFO("MSP: 0x%x\n", __get_MSP());
SFUD_INFO("Init: adr=0x%x, clk=0x%x, fnc=0x%x\n", adr, clk, fnc);
g_size = 0;
result = sfud_init();
if(result != SFUD_SUCCESS) {
return result;
}
return (0); // Finished without Errors
}
上述代码打印出的MSP的值,我测试如下:
MSP: 0x200038d4
因为我的代码运行在0x20000000处,所以,MSP的偏移位置也就是0x38d4,减去0x0000373c得到408byte,也就是说栈的大小必须在408byte以内,超过就会对.bss处的变量造成覆盖。
#define BUF_SIZE 4096
uint8_t aux_buf[BUF_SIZE] = {1};
上述代码对aux_buf[]数组给了初值,这样编译出的map文件如下:
可以看出aux_buf到.data段里了,也就是变成了程序的一部分,同时打印出的堆栈MSP的值也变大了。堆栈溢出不会对aux_buf造成影响了。但是这里的.bss段仍存放的是sfud.o里的未初始化的变量,这可是读写flash会使用到的。而sfud_write中有这样的代码:
这里cmd_data就是在.bss段里,正好是261byte=0x105,和map文件对应上。
看到这里就明白了如果栈溢出,就会对cmd_data进行覆盖,对flash的写数据造成影响。这和我实际测试测出来的现象一样,经常在0xfc这个flash地址写错,就是因为栈向下生长对cmd_data[252]进行了覆盖。
PRG
区域的最后边,可以先将.bss段独立出来,修改链接脚本如下:; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW)
}
PrgZI +0 ; ZI
{
* (+ZI)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
生成的map文件:
上图虽说在链接时给.bss单独了空间,但是程序运行时并没有启动文件为其分配空间,而栈的位置又是以0x00003694这个程序结束的的位置为开始偏移指定字节(偏移多少我们无从得知,我打印出来大概就是400多byte),依然存在溢出覆盖的问题,因此需要调整栈的起始位置。
我们可以创建个大数组,这个数组用来扩展编译出的程序所占的地址空间,来间接作为栈来使用。修改的链接脚本如下:
; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW)
}
PrgZI +0 ; ZI
{
* (+ZI)
}
}
EXTSPACE +0x2000 ; 和PRG区域间隔8192byte
{
EXSTACKAPACE +0
{
* (.exstackspace)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
上述间隔0x2000是相对于PrgData的结束位置来说的,因为ZI在编译出的程序中并不占空间,也就是程序在PrgData后就已经结束了。0x2000的作用就是用于在程序结尾处预留8K区域给.bss,也就是ZI。
到此处为止,.bss的空间就解决了,因为预留的足够大,即使栈溢出也关系不大。但是为了更加严谨,接下来还是需要预留栈空间的位置。
在FlashPrg.c
中定义数组,并将其分配在EXTSPACE
中:
#define EXT_STACK_SIZE (2048)
#define EXTSTACK_AREA_ATTRIBUTES __attribute__ ((section(".exstackspace"))) __attribute__((aligned(4)))
volatile uint8_t ext_stack_space[EXT_STACK_SIZE] EXTSTACK_AREA_ATTRIBUTES;
上述2048代表在EXTSPACE
空间中占用2048字节作为栈的扩展区域,起始位置和PrgData
区域偏了0x2000。
map文件如下:
修改后的空间分配如下:
至此,flash写错误的问题得到解决。
Target.lin
文件如下:; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW)
}
PrgZI +0 ; ZI
{
* (+ZI)
}
}
EXTSPACE +0x2000
{
EXSTACKAPACE +0
{
* (.exstackspace)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
FlashPrg.c
文件如下:/**************************************************************************//**
* @file FlashPrg.c
* @brief Flash Programming Functions adapted for New Device Flash
* @version V1.0.0
* @date 10. January 2018
******************************************************************************/
/*
* Copyright (c) 2010-2018 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FlashOS.H" // FlashOS Structures
#include "uart.h"
#include "sfud.h"
#define EXT_STACK_SIZE (2048)
#define EXTSTACK_AREA_ATTRIBUTES __attribute__ ((section(".exstackspace"))) __attribute__((aligned(4)))
volatile uint8_t ext_stack_space[EXT_STACK_SIZE] EXTSTACK_AREA_ATTRIBUTES;
#define BUF_SIZE 4096
uint8_t aux_buf[BUF_SIZE];
/*
Mandatory Flash Programming Functions (Called by FlashOS):
int Init (unsigned long adr, // Initialize Flash
unsigned long clk,
unsigned long fnc);
int UnInit (unsigned long fnc); // De-initialize Flash
int EraseSector (unsigned long adr); // Erase Sector Function
int ProgramPage (unsigned long adr, // Program Page Function
unsigned long sz,
unsigned char *buf);
Optional Flash Programming Functions (Called by FlashOS):
int BlankCheck (unsigned long adr, // Blank Check
unsigned long sz,
unsigned char pat);
int EraseChip (void); // Erase complete Device
unsigned long Verify (unsigned long adr, // Verify Function
unsigned long sz,
unsigned char *buf);
- BlanckCheck is necessary if Flash space is not mapped into CPU memory space
- Verify is necessary if Flash space is not mapped into CPU memory space
- if EraseChip is not provided than EraseSector for all sectors is called
*/
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
int result = 0;
__disable_irq();
UART0_Init(115200, USART_WordLength_8b, USART_StopBits_1b, USART_Parity_No);
SFUD_INFO("MSP: 0x%x\n", __get_MSP());
SFUD_INFO("Init: adr=0x%x, clk=0x%x, fnc=0x%x\n", adr, clk, fnc);
g_size = 0;
result = sfud_init();
if(result != SFUD_SUCCESS) {
return result;
}
return (0); // Finished without Errors
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc) {
/* Add your Code */
SFUD_INFO("UnInit: fnc=0x%x\n", fnc);
return (0); // Finished without Errors
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void) {
/* Add your Code */
int result = 0;
const sfud_flash *flash = sfud_get_device(SFUD_DEVICE_INDEX);
result = sfud_erase (flash, 0, flash->chip.capacity);
if (result != SFUD_SUCCESS) {
return result;
}
SFUD_INFO("EraseChip ok.\n");
return (0); // Finished without Errors
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr) {
/* Add your Code */
int result = 0;
uint32_t block_start;
const sfud_flash *flash = sfud_get_device(SFUD_DEVICE_INDEX);
block_start = adr;
result = sfud_erase (flash, block_start, 4096);
if (result != SFUD_SUCCESS) {
return result;
}
SFUD_INFO("EraseSector ok: adr=0x%x\n", adr);
return (0); // Finished without Errors
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
/* Add your Code */
const sfud_flash *flash = sfud_get_device(SFUD_DEVICE_INDEX);
uint32_t start_addr = adr;
SFUD_INFO("ProgramPage, addr=0x%x, size=%d\n", adr, sz);
if(sfud_write(flash, start_addr, sz, buf) != SFUD_SUCCESS) {
SFUD_INFO("ProgramPage err!\n");
return -1;
}
if(start_addr < 0x20000) {
g_size += sz;
}
return (0); // Finished without Errors
}
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
{
const sfud_flash *flash = sfud_get_device(SFUD_DEVICE_INDEX);
uint32_t start_addr = adr;
uint32_t len;
uint32_t n = 0;
uint32_t size = sz;
// SFUD_INFO("Verify: addr=0x%x, size=0x%x, buf=0x%p\n", adr, sz, buf);
while (size)
{
if (size < BUF_SIZE) {
len = size;
} else {
len = BUF_SIZE;
}
sfud_read(flash, start_addr, len, aux_buf);
for (int i = 0; i < len; i++) {
if (aux_buf[i] != buf[n + i]) {
SFUD_INFO("Verify err, addr=0x%x, aux_buf[%d]=0x%x\n", start_addr + i, i, aux_buf[i]);
return (start_addr + i); // Verification Failed (return address)
}
}
size -= len;
start_addr += len;
n += len;
}
SFUD_INFO("Verify ok: 0x%x\n", adr + sz);
return (adr + sz); // Done successfully
}
int BlankCheck (unsigned long adr,unsigned long sz,unsigned char pat)
{
const sfud_flash *flash = sfud_get_device(SFUD_DEVICE_INDEX);
uint32_t start_addr = adr;
uint32_t len;
SFUD_INFO("BlankCheck: addr=0x%x, size=0x%x, pat=0x%x\n", adr, sz, pat);
while (sz)
{
if (sz < BUF_SIZE) {
len = sz;
} else {
len = BUF_SIZE;
}
sfud_read(flash, start_addr, len, aux_buf);
for (int i = 0; i < len; i++) {
if (aux_buf[i] != pat) {
SFUD_INFO("BlankCheck err, addr=0x%x\n", start_addr + i);
return (start_addr + i + 1); // Verification Failed (return address)
}
}
sz -= len;
start_addr += len;
}
SFUD_INFO("BlankCheck ok: 0x%x\n", adr + sz);
return (0);
}
C:\Users\'你电脑的用户名'\AppData\Roaming\SEGGER\
下创建JLinkDevices
文件夹。JLinkDevices
└── W25Q128
└── W25Q128JM
├── W25Q128_SPI_FLASH.FLM
└── W25Q128.xml
其中 W25Q128_SPI_FLASH.FLM
就是mdk生成的flash下载算法。
<Database>
<Device>
<ChipInfo Vendor="WINBIND" Name="Cortex-M7" WorkRAMAddr="0x20000000" WorkRAMSize="0x20000"Core="JLINK_CORE_CORTEX_M7" />
<FlashBankInfo Name="QSPI Flash" BaseAddr="0x00000000" MaxSize="0x01000000" Loader="W25Q128_SPI_FLASH.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" />
Device>
Database>