制作jflash下载算法,并解决堆栈溢出导致的写数据错误问题

2023-11-02

1、jflash下载算法介绍

jflash是segger开发的一款软件,需要配合jlink一起使用。使用过jlink的人都知道,在项目开发调试阶段非常有帮助。
jflash下载算法就是实现当jflash中没有自己使用的芯片,或者想使用jflash将程序下载到外部flash中时,通过jflash调用flash下载算法程序完成对flash的擦除、读、写、校验操作。jflash的界面如下所示:
在这里插入图片描述

2、jflash下载算法的制作过程

jflash下载算法的制作,在mdk中提供了对应的模板工程,路径在Keil_v5\ARM\Flash\_Template中,可以直接使用模板工程,在其中添加自己的flash驱动即可。

本文就不过多介绍整个过程了,具体的步骤,请参考:
从零编写STM32H7的MDK SPI FLASH下载算法
或csdn对其的转载
从零编写STM32H7的MDK SPI FLASH下载算法

本文重点讲述遇到的问题,并介绍解决方法。

3、移植sfud过程中,编译错误 L6248E

此问题可以参考我的另一篇博客: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;

4、解决代码执行过程中的栈溢出问题

flash下载算法没有启动文件,因此我们无法设置程序运行的堆栈的大小,但是默认的堆栈又很小,当函数嵌套过多或局部变量定义太多时就会出现栈溢出,导致覆盖正常程序使用的变量,此问题很难定位,我在测试中就遇到了这个问题。

  • 4.1问题分析
    下面我们通过map文件来分析下。
    首先看下模板工程提供的链接脚本文件:
; 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处的变量造成覆盖。

  • 4.2 改善一下程序
    上述问题的产生是由于变量在.bss段里所致,那如果变量不在.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]进行了覆盖。

  • 4.3、解决方法
    经过上面分析可以看出,这个程序存在两个问题,①生成的程序中.bss段里的变量没有启动文件为其分配空间;②默认的栈太小,栈会溢出。
    就从这两个地方入手。
    • 1、给.bss段单独创建区域。
      既然.bss段的变量地址没有进行空间分配,而它的地址编译后又是确定的,并且.bss段在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写错误的问题得到解决。

5、完整的代码

  • 1、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
  }
}
  • 2、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);
}

6、在jflash中添加新算法

  • 1、创建文件夹
    C:\Users\'你电脑的用户名'\AppData\Roaming\SEGGER\下创建JLinkDevices文件夹。
  • 2、JLinkDevices文件结构需要按类似以下布局:
JLinkDevices
└── W25Q128
    └── W25Q128JM
        ├── W25Q128_SPI_FLASH.FLM
        └── W25Q128.xml

其中 W25Q128_SPI_FLASH.FLM就是mdk生成的flash下载算法。

  • 3、W25Q128.xml内容
<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>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

制作jflash下载算法,并解决堆栈溢出导致的写数据错误问题 的相关文章

  • 2.23怎么在OrCAD原理图中显示与隐藏元器件的Value值?【OrCAD原理图封装库50问解析】

    笔者电子信息专业硕士毕业 获得过多次电子设计大赛 大学生智能车 数学建模国奖 现就职于南京某半导体芯片公司 从事硬件研发 电路设计研究 对于学电子的小伙伴 深知入门的不易 特开次博客交流分享经验 共同互勉 全套资料领取扫描文末二维码 温馨提
  • 单片机FLASH操作

    FLASH 操作 查看程序已经占用的FLASH的扇区 剩余的扇区就是可以操作而不会使程序发生错乱的区域 找到listing文件夹下面的 map文件 搜索Memory Map of the image 查看占用的内存 起始地址是 0x8000
  • 【电路设计】220V AC转低压DC电路概述

    前言 最近因项目需要 电路板上要加上一个交流220V转低压直流 比如12V或者5V这种 一般来说 比较常见也比较简单的做法是使用一个变压器将220V AC进行降压 比如降到22V AC 但是很遗憾的是 支持220V的变压器一般体积很大 而板
  • 异步Buck和同步Buck的特点

    1 介绍 随着时代的发展 工业 车载 通信 消费类等产品都提出了小型化 智能化的需求 相应的 对于这些系统中的电源模块提出了小型化的要求 目前 市场上依然存在很多异步Buck电源管理芯片使用的场景 针对这些应用 采用同步Buck电源管理芯片
  • (esp-idf)一文看懂u8g2库点亮OLED

    github仓库地址 HawkJ02 esp32 oled github com 首先丢一个u8g2库的地址 olikraus u8g2 U8glib library for monochrome displays version 2 gi
  • 2022年 大学生工程训练比赛[物料搬运]

    本人和团结参加了2022年大学生工程训练 简称工训赛 校赛选拔 准备了几个月的时间和花费了较多的资金 由于疫情等多种情况 很遗憾未能参加湖南省省赛 过了这么久还是写个博客记录参赛准备和调试过程 目录 一 比赛要求 二 整体思路 三 硬件模块
  • 计算机组成与设计:硬件/软件接口,第三章详细梳理,附思维导图

    文章目录 三 计算机的运算 章节导图 一 整数的表示 无符号整数 原码 反码 原码是带符号整数的表示方法
  • 548、基于51单片机的比赛计分仿真设计(简易,LCD1602,独立按键)

    毕设帮助 开题指导 技术解答 有偿 见文末 目录 一 设计功能 二 proteus仿真 三 原理图 四 程序源码 五 资料包括 一 设计功能 二 proteus仿真 三 原理图 四 程序源码 五 资料包括
  • Stm32最小系统板电路设计

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • 【Proteus仿真】【Arduino单片机】定时智能插座开关

    文章目录 一 功能简介 二 软件设计 三 实验现象 联系作者 一 功能简介 本项目使用Proteus8仿真Arduino单片机控制器 使LCD1602液晶 DS18B20温度传感器 按键 蜂鸣器 继电器开关 HC05蓝牙模块等 主要功能 系
  • 硬件基础-电容

    电容 本质 电容两端电压不能激变 所以可以起到稳定电压作用 充放电 电容量的大小 想使电容容量大 使用介电常数高的介质 增大极板间的面积 减小极板间的距离 品牌 国外 村田 muRata 松下 PANASONIC 三星 SAMSUNG 太诱
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 串口流控(CTS/RTS)使用详解

    1 流控概念 在两个设备正常通信时 由于处理速度不同 就存在这样一个问题 有的快 有的慢 在某些情况下 就可能导致丢失数据的情况 如台式机与单片机之间的通讯 接收端数据缓冲区已满 则此时继续发送来的数据就会丢失 流控制能解决这个问题 当接收
  • PD SINK协议芯片系列产品介绍对比-ECP5701、FS312A、CH221K、HUSB238、AS225KL

    目录 一 ECP5701 二 FS312A 三 CH221K 四 HUSB238 五 AS225KL 在如今快节奏生活不断蔓延的背景下 人们对各种事情的处理也渐渐地开始要求在保证质量的情况下 不断加快 手机快充就是一个典型的例子 从开始的1
  • 学习STM32正点原子好吗?

    今日话题 学习STM32正点原子好吗 正点原子的教程内容简单明了 代码也清晰直接 使初学者能够轻松理解其功能和使用方法 尤其对于需要快速完成大学作业等任务的大学生来说 可以直接借鉴并稍作修改 便可满足需求 正点原子提供的资料通俗易懂 适合用
  • 会stm32有机会进大公司吗?

    今日话题 会stm32有机会进大公司吗 我本科期间参与了飞思卡尔和电赛等比赛 使用过多种单片机 但渐渐发现单片机只是其中的一小部分 不要过分迷恋所谓的单片机基础和技巧 更值得深入研究的是C语言 如果你对此感兴趣 我愿意无偿分享一个资源包 其
  • STM32H5 Nucleo-144 board开箱

    文章目录 开发板资料下载 目标 点亮LD1 绿 LD2 黄 和LD3 红 三个LED灯 开箱过程 博主使用的是STM32CubeMX配置生成代码 具体操作如下 打开STM32CubeMX File gt New project 选择开发板型
  • 【学习分享】全志平台TWI子系统源码分析(1)从设备树到寄存器

    全志平台TWI子系统源码分析 1 从设备树到寄存器 前言 一 名词解释 二 从设备树入手看源码 1 TWI设备树 2 TWI源码位置 3 TWI总线相关寄存器 总结 前言 这次开坑主要是想把全志平台TWI子系统在源

随机推荐