1. jflash ダウンロード アルゴリズムの概要
jflash は segger によって開発されたソフトウェアであり、jlink と一緒に使用する必要があります。jlink を使用したことがある人なら、プロジェクトの開発段階やデバッグ段階で jlink が非常に役立つことを知っています。
jflash ダウンロード アルゴリズムは、jflash にチップがない場合、または jflash を使用してプログラムを外部フラッシュにダウンロードする場合に、jflash がフラッシュ ダウンロード アルゴリズム プログラムを呼び出して、消去、読み取り、書き込み、検証の操作を完了することを実現します。フラッシュの。jflash のインターフェースは次のとおりです。
2. jflash ダウンロードアルゴリズムの作成プロセス
jflash ダウンロード アルゴリズムの作成のために、対応するテンプレート プロジェクトが mdk で提供されています。パスは にありますKeil_v5\ARM\Flash\_Template
。テンプレート プロジェクトを直接使用して、独自のフラッシュ ドライバーをそれに追加できます。
この記事ではプロセス全体を詳しく紹介するわけではありません。具体的な手順については、「
STM32H7 の MDK SPI FLASH ダウンロード アルゴリズムを最初から書く」を参照してください。
または、csdn がそれを再現し、
STM32H7 の MDK SPI FLASH ダウンロード アルゴリズムを最初から作成します。
この記事では、発生した問題に焦点を当て、解決策を紹介します。
3. sfud の移植プロセス中に、コンパイル エラー L6248E が発生する
この問題については、私の他のブログ「外部フラッシュ ダウンロード アルゴリズムの mdk 作成、コンパイル エラー L6248E」を参照してください。
主な理由は、sfud では、char *name
フラッシュ モデルの名前を格納するポインターが複数の場所で定義されているためです。ただし、フラッシュ ダウンロード アルゴリズムでは、コンパイルされたコードがアドレスに依存しないコードである必要があるため、ここではポインターを使用できず、アドレスname
空間が明確である必要があります。配列のアドレス空間はコンパイル後に決定されるため、ここでは配列へのポインターを変更する必要があります。変更は次のとおりです。
すべての*name
ポインターを配列に変更します。
typedef struct __sfud_spi {
/* SPI device name */
char name[32];
/* 以下有省略 */
} sfud_spi, *sfud_spi_t;
4. コード実行時のスタック オーバーフローの問題を解決する
フラッシュ ダウンロード アルゴリズムにはスタートアップ ファイルがないため、プログラム実行時のスタック サイズを設定できませんが、デフォルトのスタックは非常に小さいため、ネストされた関数が多すぎるか、ローカル変数が多すぎると、スタック オーバーフローが発生します。変数、この問題は見つけるのが難しく、テスト中にこの問題に遭遇しました。
- 4.1 問題の分析
マップファイルを通じて分析してみましょう。
まず、テンプレート プロジェクトによって提供されるリンク スクリプト ファイルを見てください。
; 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
指定されたメモリ領域は、フラッシュ ダウンロード アルゴリズムが配置されている領域です。DSCR
指定された領域は主にFlashDevice
この構造体を格納するために使用され、jflash はこの領域の構造体を取得することでフラッシュの基本的な情報を知ることができます。
このリンク スクリプトによって生成されるマップ ファイル内の PRG 領域と DSCR 領域は次のとおりです。
上の図からわかるように.bss
、セグメントによって占有されている領域は PrgData には含まれておらず、アドレス 0x0000373c は、まさに で定義されている 4096 バイトの配列です。コードは次のとおりです。
ソースコードは次のとおりです。
#define BUF_SIZE 4096
uint8_t aux_buf[BUF_SIZE];
マップ ファイルから、sfud.o の変数も .bss セクションの 0x0000473c: の位置にあることがわかりますが、開始位置は 0x0000373c であり、
.bssDSCR
セクションがここにスペースを割り当てていないことがわかります。これは、.bss セクションが生成されたプログラムのサイズを占有せず、起動時にスタートアップ ファイルによって sram の別の部分にロードされ、その後 0 に初期化されて、.bss セクションとスタック領域が配置されることも示しています。一緒にスペースを割り当てます。
通常のプログラムであれば、もちろん問題ありません。しかし、フラッシュ ダウンロード アルゴリズム プログラムにはスタートアップ ファイルがなく、フラッシュ アルゴリズムの実行中に .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 を引いて 408 バイトになります。つまり、スタックのサイズは 408 バイト以内でなければなりません。それを超えると、.bss の変数が上書きされます。
- 4.2 プログラムを改善する
上記の問題は、変数が .bss セクションにあるために発生しますが、変数が .bss セクションにない場合はどうすればよいでしょうか。
コードの次の部分を変更します。
#define BUF_SIZE 4096
uint8_t aux_buf[BUF_SIZE] = {
1};
上記のコードは、aux_buf[] 配列に初期値を与えるため、コンパイルされたマップ ファイルは次のようになります。
aux_buf が .data セクションにある、つまりプログラムの一部になっていることがわかります。スタックMSP値も変更されました。スタック オーバーフローは aux_buf には影響しません。ただし、ここの .bss セクションには、フラッシュの読み取りと書き込みに使用される sfud.o 内の初期化されていない変数が依然として保存されています。sfud_write には次のコードがあります。
cmd_data は .bss セクションにあり、これはマップ ファイルに対応する 261byte=0x105 です。
これを見ると、スタックがオーバーフローすると cmd_data が上書きされ、フラッシュの書き込みデータに影響を与えることがわかります。これは実際のテストでも同様の現象で、スタックが下に伸びてcmd_data[252]を上書きしてしまうため、0xfcに間違ったフラッシュアドレスを書き込んでしまうことがよくあります。
- 4.3. 解決策
上記の分析から、このプログラムには 2 つの問題があることがわかります: ① 生成されたプログラムの .bss セクションの変数には、スペースを割り当てるためのスタートアップ ファイルがありません; ② デフォルトのスタックが多すぎる小さいとスタックがオーバーフローします。
この2か所から始めましょう。- 1. .bss セクション用に別の領域を作成します。
.bss セクションの変数アドレスには領域が割り当てられておらず、そのアドレスはコンパイル後に決定され、.bss セクションは領域の最後にあるため、最初に .bss セクションを分離し、リンク スクリプトを次のように変更できますPRG
。以下に続きます:
- 1. .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
}
}
生成されたマップ ファイル:
上の図は、リンク時に .bss に別のスペースがあることを示していますが、プログラムの実行中にスペースを割り当てる起動ファイルはなく、スタック位置は、ファイルの最後に開始オフセットとして指定されます。プログラムは 0x00003694. バイト (オフセットがどのくらいかを知る方法はありませんが、約 400 バイトになるように出力しました)、オーバーフロー カバレッジの問題がまだあるため、スタックの開始位置を調整する必要があります。
コンパイルされたプログラムが占有するアドレス空間を拡張し、スタックとして間接的に使用するために使用される大きな配列を作成できます。変更されたリンク スクリプトは次のとおりです。
; 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
}
}
ZI はコンパイルされたプログラム内のスペースを占有しないため、つまりプログラムは PrgData の後に終了しているため、上記の間隔 0x2000 は PrgData の終了位置を基準としています。0x2000 の目的は、プログラムの最後に .bss 用の 8K 領域 (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オフセットされている。
マップ ファイルは次のとおりです。
変更されたスペース割り当ては次のとおりです。
この時点で、フラッシュ書き込みエラーの問題は解決されました。
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 によって生成されたフラッシュ ダウンロード アルゴリズムがあります。
- 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>