关于DSPF28335报错" a data verification error occurred,file load failed"
背景
一般来说,在第一次尝试烧录的时候这种情况比较常见,这个错误有种表现形式,其中常见的就是如下图所示。
并且一般在debug console一栏中有这样的报错信息:
Cortex_M3_0: Loader: One or more sections of your program falls into a memory region that is not writable. These regions will not actually be written to the target. Check your linker configuration and/or memory map.
Cortex_M3_0: File Loader: Verification failed: Values at address 0x00000020 do not match Please verify target memory and memory map.
Cortex_M3_0: GEL: File: C:\ti\workspaces\910\m3x\Debug\m3x.out: a data verification error occurred, file load failed.
数据验证错误的实际含义:当CCS尝试使用要加载到target内存的可执行文件来验证target内存内容时,发现不匹配。最常见的原因就是CCS无法成功的将可执行文件加载到target。更具体的说,它无法将可执行文件一部分特定符号信息的二进制文件写入到内存地址中。
一个由actual target code和一些debug标识符的可执行文件可以通过CCS烧录。*.out是由CCS工程编译可执行文件的默认扩展名。由于可执行文件没有成功加载(loaded),导致验证失败。
原因
CCS不能把可执行二进制文件写入到特定内存地址的原因可能存在很大差异,但是从本质上讲,这意味着在以下方面存在不匹配:
- 代码生成工具认为在你构建的应用程序的目标上有可用(写入)内存;
- CCS debugger认为target中有可用(写入)内存;
- 在程序尝试load的时候,target中存在可写入内存。
要理解三种环境并确保他们正确对其,以避免此类错误。
代码生成工具(Code Generation Tools)
这里默认使用TI Code Generation Tools
名称 | 已初始化 | 说明 |
---|---|---|
.text | 是 | 可执行代码 |
.bss | 否 | 全局变量 |
.cinit | 是 | 用于初始化全局变量的表 |
.data (EABI) | 是和否 | 从汇编器出来时已初始化;由链接器更改为未初始化 |
.data (COFF ABI) | 是 | 已初始化的数据 |
.stack | 否 | 系统堆栈 |
.heap 或 .sysmem | 否 | malloc 堆 |
.const | 是 | 已初始化的全局变量 |
.switch | 是 | 某些 switch 语句的跳转表 |
.init_array 或 .pinit | 是 | 启动时调用的 C++ 构造函数的表 |
.cio | 否 | stdio 函数的缓冲区 |
在应用程序生成的链接阶段,链接器组合目标文件并将节分配到目标系统的已配置内存中。链接器命令语言控制内存配置,output section定义和address绑定。你可以通过定义和创建自己设计的内存模型来配置系统内存。通过MEMORY,SECTIONS这两个链接指令来将section分配到特定内存中。这些指令一般存在于cmd文件中,该文件被传递给链接器。关于cmd文件,在TI官网有详细资料。在一般的硬件中,默认属性的cmd文件常常不可用,故此,需要为target创建合适的链接文件。
下面是一个C6748 EVM的例子,RAM的起始位在0x8000000。https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_Linker-Command-File-Primer.html
/*****************************************************************************
* linker command file for C6748 test code.
*
* Copyright 2009, Logic Product Development Company. All Rights Reserved.
******************************************************************************/
-l rts67plus.lib
-stack 0x00000800
-heap 0x00000800
MEMORY
{
dsp_l2_ram: ORIGIN = 0x11800000 LENGTH = 0x00040000
shared_ram: ORIGIN = 0x80000000 LENGTH = 0x00020000
external_ram: ORIGIN = 0xC0000000 LENGTH = 0x08000000
arm_local_ram: ORIGIN = 0xFFFF0000 LENGTH = 0x00002000
}
SECTIONS
{
.text > shared_ram
.const > shared_ram
.bss > shared_ram
.far > shared_ram
.switch > shared_ram
.stack > shared_ram
.data > shared_ram
.cinit > shared_ram
.sysmem > shared_ram
.cio > shared_ram
}
当使用BIOS时,自动生成链接器命令,target内存被定义在BIOS的tcf/cfg文件中。
调试内存映射(Debugger Memory Map)
调试内存map为CCS调试器指定target内存配置。当没有正确设置调试内存map时,调试器无法得知target内存配置情况。默认情况下,调试器内存map是被禁止的。在这种情况下,调试器假定所有内存范围都是可访问的,它无法自动尝试阻止调试器访问内存。为了调试稳定性,还是需要设置调试内存map,让调试器知道哪部分内存是可用的。关于Memory Map文档。不正确的内存map会导致很多问题,主要问题之一是阻止调试器写入目标上实际可用的内存,因为错误的内存映射(不正确地)告诉调试器该内存区域不可写。如果在程序load期间出现上述情况,也就是错误的调试内存map就会导致“data verification error”。链接指令文件的信息与调试内存map不匹配导致许多中数据验证错误。重要的是要检查调试器内存map中定义可用的内存范围,同时该内存在链接指令文件中定义可写。
下面是C6748 EVM的调试器内存映射设置的屏幕截图。用红色圈出的内存范围表示地址0x8000000 - 0x8001FFFF中有可用内存(读/写)。这与链接器命令文件中定义的"shared_ram"内存区域一致,其中所有节都被分配:
调试器内存映射通常使用GEL文件来设置。在大多数情况下,这是使用目标的启动GEL文件完成的。
以下是C6748启动凝胶文件的一部分示例,该文件使用GEL调用打开调试器内存映射并定义可用内存区域。请注意,此定义比链接器命令文件定义的范围更完整。这很常见,因为链接器命令文件只需要知道它可以将节分配到的内存范围,而尽可能多地了解有关内存布局的详细信息对调试器是有益的。要检查的关键是链接器命令文件中定义的范围是否也存在于内存映射中。
hotmenu Setup_Memory_Map()
{
GEL_MapOn( );
GEL_MapReset( );
/* DSP */
GEL_MapAddStr( 0x00700000, 0, 0x00100000, "R|W|AS4", 0 ); // DSP L2 ROM
GEL_MapAddStr( 0x00800000, 0, 0x00040000, "R|W|AS4", 0 ); // DSP l2 RAM
GEL_MapAddStr( 0x00E00000, 0, 0x00008000, "R|W|AS4", 0 ); // DSP L1P RAM
GEL_MapAddStr( 0x00F00000, 0, 0x00008000, "R|W|AS4", 0 ); // DSP L1D RAM
GEL_MapAddStr( 0x01800000, 0, 0x00010000, "R|W|AS4", 0 ); // DSP Interrupt Controller
GEL_MapAddStr( 0x01810000, 0, 0x00001000, "R|W|AS4", 0 ); // DSP Powerdown Controller
GEL_MapAddStr( 0x01811000, 0, 0x00001000, "R|W|AS4", 0 ); // DSP Security ID
GEL_MapAddStr( 0x01812000, 0, 0x00008000, "R|W|AS4", 0 ); // DSP Revision ID
GEL_MapAddStr( 0x01820000, 0, 0x00010000, "R|W|AS4", 0 ); // DSP EMC
GEL_MapAddStr( 0x01830000, 0, 0x00010000, "R|W|AS4", 0 ); // DSP Internal Reserved
GEL_MapAddStr( 0x01840000, 0, 0x00010000, "R|W|AS4", 0 ); // DSP Memory System
GEL_MapAddStr( 0x11700000, 0, 0x00100000, "R|W|AS4", 0 ); // DSP L2 ROM (mirror)
GEL_MapAddStr( 0x11800000, 0, 0x00040000, "R|W|AS4", 0 ); // DSP l2 RAM (mirror)
GEL_MapAddStr( 0x11E00000, 0, 0x00008000, "R|W|AS4", 0 ); // DSP L1P RAM (mirror)
GEL_MapAddStr( 0x11F00000, 0, 0x00008000, "R|W|AS4", 0 ); // DSP L1D RAM (mirror)
/* Shared RAM */
GEL_MapAddStr( 0x80000000, 0, 0x00020000, "R|W|AS4", 0 ); // Shared RAM
/* EMIFA */
GEL_MapAddStr( 0x40000000, 0, 0x20000000, "R|W|AS4", 0 ); // EMIFA SDRAM Data
GEL_MapAddStr( 0x60000000, 0, 0x02000000, "R|W|AS4", 0 ); // AEMIF CS2
GEL_MapAddStr( 0x62000000, 0, 0x02000000, "R|W|AS4", 0 ); // AEMIF CS3
GEL_MapAddStr( 0x64000000, 0, 0x02000000, "R|W|AS4", 0 ); // AEMIF CS4
GEL_MapAddStr( 0x66000000, 0, 0x02000000, "R|W|AS4", 0 ); // AEMIF CS5
GEL_MapAddStr( 0x68000000, 0, 0x00008000, "R|W|AS4", 0 ); // EMIFA Control
/* DDR */
GEL_MapAddStr( 0xB0000000, 0, 0x00008000, "R|W|AS4", 0 ); // DDR Control
GEL_MapAddStr( 0xC0000000, 0, 0x20000000, "R|W|AS4", 0 ); // DDR Data
...
特殊情况(Special Cases)
某些内核具有内部外设,可修改调试器存储器映射,从而改变调试会话中看到的内容:
-
Cortex M和R微控制器具有存储器保护单元(MPU),可防止读取存储器区域。使用DAP读取内存可以解决此问题。
-
应用处理器和选定DSP具有存储器管理单元(MMU-ARM9、Cortex A、C66 x),可将整个存储器区域转换为其他地址。CCS底部状态栏指示MMU状态(打开或关闭):
-
在这些情况下,您可以使用DAP或从Memory Browser视图中选择Physical Memory选项来直接访问内存。
可用Target内存
最后,链接器命令文件(和调试器内存映射)中定义的内存位置需要实际存在于目标上。如果允许调试器写入目标上实际不可用的内存位置,则写入将失败,并且将发生数据验证错误。在这种情况下,您必须将链接器命令文件(或RTSC平台包)与现有硬件匹配。
此外,一种非常常见的情况是,这不是存储器范围根本不存在的问题,而是存储器尚未初始化、闪存编程器配置错误或存储器被锁定的问题。如果要将应用程序加载到外部存储器或闪存,但存储器尚未初始化(时钟未设置等)或闪存已锁定,则加载将失败,并显示数据验证错误。
验证内存的一个有用方法是手动启动目标,打开内存浏览器视图(菜单视图→内存浏览器)并指向有问题的地址。此时,诊断在RAM和闪存之间切换:
诊断RAM问题
-
如果内存不存在,则显示零。如果存储器配置错误,则会显示零或数据。
-
要确认数据是否有效(表示内存配置正确),请单击内存浏览器的刷新按钮:如果数字随机变化,则数据可能无效。
-
如果数据稳定且位于RAM中,则尝试修改其中一个地址。如果数据没有变化或随机变化或在其它地址变化,则存储器可能配置错误。
-
仔细检查目标配置文件,查看是否正确配置了板的GEL。否则,可以添加GEL文件。
-
如果您使用的是开发板,请为开发板而不是器件创建目标配置文件,因为它已经为您预填充了正确的GEL文件。例如,如果您有BeagleBone板,则在创建目标配置文件时选择此板,而不是“纯”AM3359器件。
-
另一个大的“陷阱”是没有意识到电路板上的各种跳线和开关实际上可以改变其内存布局。一些常见的例子是,通过改变跳线,可以交换地址位置,使RAM或闪存位于该地址。在这种情况下,请查阅电路板文档。
-
如果正确配置了GEL文件,请转到“脚本”菜单并检查是否有配置内存的菜单项。根据设备的不同,通常会有多个条目用于不同的DDR/SDRAM内存频率。
-
如果使用具有外部存储器接口的C2000设备,请转到“脚本”菜单并选择启用XINTF的选项。
诊断FLASH问题
If the Flash is blank, 0xFFFF or 0xFFFFFFFF would be shown (a blank flash device has all bits 1).
If the memory has pre-existing code, zeros or data would be shown. In this case, it is possible the memory is protected. Check the ‘Special Cases’ section above.
To solve this a few things can be tried:
- Check if the clock configuration matches the hardware. Go to menu Tools → On-chip Flash and check the clock settings - it can be named Crystal Frequency, CLKOUT, etc.
- Try to erase the Flash memory manually. Go to menu Tools → On-chip Flash and select the option Erase Flash.
- For C2000 devices, each page can be individually selected - this is useful to track any pages that may be faulty.
- For Tiva and Hercules devices, an address range can be selected.
- For MSP devices, only the entire memory ranges (BSL, main, information) can be selected.
- After erasing, try to perform a Blank check. This may error that happen only during a memory read.
- Sometimes it is possible the device is locked or protected. In this case, from the same option above (menu Tools → On-chip Flash) you have access to unlock functions.
人们第一次遇到数据验证错误的常见场景是从模拟器上的调试转移到实际硬件上的调试。这是因为许多模拟器都有一个平面内存系统,其中所有内存都可用(可写),因此可以在没有链接器命令文件(或使用不正确的文件)的情况下开发应用程序,并且仍然可以加载程序,而不会在模拟器上出现任何问题。然后,当尝试在实际硬件上进行调试时,加载几乎肯定会失败,因为不是所有内存都可用。
模拟器稳定性也可能导致数据验证错误。虽然通信可能足够稳定,可以成功连接到CCS的目标,但它可能不够稳定,无法准确写入内存。已知必须使用较低的仿真器TCLK频率设置才能将程序准确加载到目标存储器的问题。