//=====================================================================
//TITLE:
// NativeSample调试过程记录
//AUTHOR:
// norains
//DATE:
// Tuesday 28-September-2010
//Environment:
// KEIL MDK 4.0
// .NET Micro Framework
//=====================================================================
有不少朋友来信问我,程序应该如何调试。其实,程序调试无非就是猜测+验证的过程。恰好最近在调试NativeSample,也做了相应的记录,以以此作为本篇的主题。虽然最后并没有真正解决问题,但里面的思路,还是有一定的借鉴价值的。
如果我们编译NativeSample是使用如下命令:
Msbuild ./Solutions/$PlatformDir$/NativeSample /t:build /p:flavor=debug;memory=ram
那么在调试时,可能会遇到不少莫名其妙的错误。而这些错误,却都和scatterfile_tools_mdk.xml有关,现在我们就来仔细探究一下。
如果是按照《详解STM32F10x在.Net Micro Framework的Processor Properties的取值》(http://blog.csdn.net/norains/archive/2010/09/20/5896807.aspx)这篇文章所述说的来进行取值设置的话,那么生成的scatterfile_tools_mdk.xml文件,将有如下内容:
<Set Name="Heap_Begin" Value="0x08100000"/>
<Set Name="Heap_End" Value="0x084FFFF8"/>
<Set Name="Stack_Bottom" Value="+0"/>
<Set Name="Stack_Top" Value="0x08502000"/>
调试时,汇编"ldr r0, =StackTop"执行完毕之后,R0为0x08502000,如下图:
也就是说StatckTop为0x08502000,和scatterfile_tools_mdk.xml的Stack_Top数值一致。
但跑到TinyHal.cpp的BootEntry函数就会出错,如下图:
这时候bus fault会源源不断地输出,即使你点击MDK的停止按钮也无济于事,因为你会发现MDK软件已经崩溃了。
我们回过头来看看STM32F103ZE的内存设置,如图:
可以知道0x0800 0000 ~ 0x0808 0000是flash地址,而现在居然stack和heap的地址设置于flash范围之内,是不是因为这个原因导致了崩溃?
我们换个思路来思考一下,因为红牛开发板有两片SRAM,根据叶帆的这篇文章《【.Net Micro Framework PortingKit - 02】STM3210E平台构建》(http://blog.csdn.net/yefanqiu/archive/2010/01/02/5119171.aspx),我们将stack和heap的地址放到外部RAM中,故scatterfile_tools_mdk.xml的数值我们修改如下:
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6800FFFC"/>
<Set Name="Stack_Bottom" Value="+0"/>
<Set Name="Stack_Top" Value="0x6801FFFC"/>
重新编译,生成新的NativeSample.axf文件。当然,还不要忘记载入相应的ini脚本文件,因为这时候的代码没有对外部SDRAM进行任何初始化,如果想让外部SDRAM能够正常工作,只能借助于脚本文件了。一切准备就绪,这时候进行调试,可以发现已经可以进入到BootEntry函数了,如图:
不过执行到LOAD_IMAGE_Length = (UINT32)&Image$$ER_RAM$$Length;这个语句,又会出现错误,如图:
虽然错误信息不一样,但此时MDK的表现形式也一样,无法停止调试,MDK完全崩溃,只能选择强制关闭。
我们还是回过头看看scatterfile_tools_mdk.xml文件的<If Name="TARGETLOCATION" Value="RAM">这个字段:
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x08020000"/>
<Set Name="Code_Size" Value="0x00100000"/>
<Set Name="Valid" Value="true"/>
</If>
而回头看看开始实行局部变量赋值时,此时的R0的地址刚好为0x0802 0000,如图:
这个数值和scatterfile_tools_mdk.xml文件中的Code_BaseAddress相一致,是不是问题就出现在这里呢?因为这个地址也是flash的地址!所以,根据叶帆的文章,我们将这几个数值更改如下:
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x20001000"/>
<Set Name="Code_Size" Value="0x0000F000"/>
<Set Name="Valid" Value="true"/>
</If>
重新编译NativeSample,然后再调试。不过,这次在下载的时候就出错了,提示地址不对,无法下载,如图所示:
看来这次不能依样画瓢,叶帆文章的数值并不适合。当然,这并不是说叶帆的数值不对,因为很可能是我们的设定和他的不同,所以才造成无法下载。不过,这里先将这问题搁下,我们现在换个角度,看看scatterfile_tools_mdk.xml文件的Code_BaseAddress字段是否对代码中的&Load$$ER_RAM$$Base数值有影响。所以,再次对scatterfile_tools_mdk.xml文件的数值进行修改,如下所示:
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x08030000"/>
<Set Name="Code_Size" Value="0x00008000"/>
<Set Name="Valid" Value="true"/>
</If>
当然,更改了这个之后,也需要对配置文件configure.ini的PC指针进行更改,否则无法正常运行。其更改如下:
PC = 0x08030000;
调试运行,我们发现,&Load$$ER_RAM$$Base确实已经变成0x08030000了,如图:
可见scatterfile_tools_mdk.xml文件的Code_BaseAddress字段确实是对&Load$$ER_RAM$$Base有影响。
然后回头想想,执行LOAD_IMAGE_Start = (UINT32)&Load$$ER_RAM$$Base;语句时没有任何问题;但到了LOAD_IMAGE_Length = (UINT32)&Image$$ER_RAM$$Length就完蛋了。再看看微软默认生成的scatterfile_tools_mdk.xml文件,Code_BaseAddress为0x0802 0000,是flash的地址,但Code_Size却是0x00100000!这很明显是错误的,因为STM32F103ZE的Flash的大小只有0x0008 0000,而这还是从0x0800 0000开始算起的!如果是从0x08020000开始,那么大小应该是:0x0800 0000 + 0x0008 0000 - 0x0802 0000 = 0x0006 0000!为了验证我们的猜测是否正确,将scatterfile_tools_mdk.xml中的数值更改如下:
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x08020000"/>
<Set Name="Code_Size" Value="0x00060000"/>
<Set Name="Valid" Value="true"/>
</If>
再次重新编译,然后进行调试。当然,这时候的脚本文件的PC指针也要记得进行修改。不过,调试之后,发现错误还是依旧,如图:
看起来,scatterfile_tools_mdk.xml的Code_Size数值有误还并不是程序崩溃的元凶。
问题会出在哪里呢?现在暂时还不知道,不过还是暂且搁下。之前我们编译的不是以RAM的方式编译么,那么如果以FLASH的方式会如何呢?用不同的编译方式,重新编译一下NativeSample看看:
Msbuild ./Solutions/$PlatformDir$/NativeSample /t:build /p:flavor=debug;memory=flash
开始调试,看看什么情况。不过还是很遗憾,还是会出错,如图:
会不会是之前的文件是采用RAM编译,即使后期变更为FLASH也没有更新的缘故?不试试怎么知道?索性,将BuildOutput文件夹删掉,然后重新以如下命令编译NativeSample:
Msbuild ./Solutions/$PlatformDir$/NativeSample /t:build /p:flavor=debug;memory=flash
编译完毕之后,再调试看看。不过,这次在下载的阶段就出错了,如图:
有了之前的经验,可以知道下载出错是因为scatterfile_tools_mdk.xml中的FLASH相关地址数据不正确。那么应该怎么更改呢?嗯,最开始,还是拿来主义吧,就将叶帆的这篇《【.Net Micro Framework PortingKit - 14】TinyCLR编译与测试 》(http://blog.csdn.net/yefanqiu/archive/2010/02/18/5310634.aspx)文章的数值原封不动拿过来试试,故scatterfile_tools_mdk.xml的flash段数值更改如下:
<If Name="TARGETLOCATION" Value="FLASH">
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6801DFFC"/>
<!-- 8k -->
<Set Name="Custom_Heap_Begin" Value="0x6801E000"/>
<Set Name="Custom_Heap_End" Value="0x6801FFFC"/>
<!-- 32k -->
<Set Name="Stack_Bottom" Value="0x20008000"/>
<Set Name="Stack_Top" Value="0x2000FFFC"/>
<Set Name="Code_BaseAddress" Value="0x08000000"/>
<Set Name="Code_Size" Value="0x00080000"/>
<Set Name="Config_BaseAddress" Value="0x00000000"/>
<Set Name="Config_Size" Value="0x00020000"/>
<Set Name="Deploy_BaseAddress" Value="0x00020000"/>
<Set Name="Valid" Value="true"/>
<IfDefined Name="ALTERNATEFLASHLOCATION">
<Set Name="Code_BaseAddress" Value="%Code_BaseAddress + Code_Size%"/>
</IfDefined>
</If>
不过,直接结果就是,编译出错,如图:
可能直接复制的时候,和原来的有误。那么根据叶帆的文章,我们稍微修正一下试试,如下:
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6801DFFC"/>
<Set Name="Stack_Bottom" Value="0x20008000"/>
<Set Name="Stack_Top" Value="0x2000FFFC"/>
<If Name="TARGETLOCATION" Value="FLASH">
<!-- STM32F103ZE_RedCow has 8MB of 32-bit FLASH at 0x10000000-->
<Set Name="Config_BaseAddress" Value="0x20001000"/>
<Set Name="Config_Size" Value="0x0000F000"/>
<Set Name="Code_BaseAddress" Value="0x08000000"/>
<Set Name="Code_Size" Value="0x00080000"/>
<Set Name="Valid" Value="true"/>
<!-- -->
<IfDefined Name="ALTERNATEFLASHLOCATION">
<Set Name="Code_BaseAddress" Value="%Code_BaseAddress + Code_Size%"/>
</IfDefined>
</If>
这时候,终于能够正常编译通过了。看看能不能正确下载,点击Download,很遗憾,依然无法下载:
这种情形比使用RAM的时候更为糟糕,因为我们连下载这一步还是无法通过。没办法,虽然有点无奈,但我们还是转到RAM方式,至少我们还能下载成功。将scatterfile_tools_mdk.xml文件恢复为如下数值:
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6800FFFC"/>
<Set Name="Stack_Bottom" Value="+0"/>
<Set Name="Stack_Top" Value="0x6801FFFC"/>
<If Name="TARGETLOCATION" Value="FLASH">
<!-- STM32F103ZE_RedCow has 8MB of 32-bit FLASH at 0x10000000-->
<Set Name="Config_BaseAddress" Value="0x10020000"/>
<Set Name="Config_Size" Value="0x00020000"/>
<Set Name="Code_BaseAddress" Value="0x10300000"/>
<Set Name="Code_Size" Value="0x00100000"/>
<Set Name="Valid" Value="true"/>
<!-- -->
<IfDefined Name="ALTERNATEFLASHLOCATION">
<Set Name="Code_BaseAddress" Value="%Code_BaseAddress + Code_Size%"/>
</IfDefined>
</If>
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x08020000"/>
<Set Name="Code_Size" Value="0x00100000"/>
<Set Name="Valid" Value="true"/>
</If>
接着删掉BuildOutput文件夹,重新输入如下命令进行编译:
Msbuild ./Solutions/$PlatformDir$/NativeSample /t:build /p:flavor=debug;memory=ram
先试试看,确认编译的文件在LOAD_IMAGE_Length = (UINT32)&Image$$ER_RAM$$Length;的时候是否还会崩溃。如果崩溃,那么意味着我们又开始回到起点,可以继续下一步研究了。
回头再看看scatterfile_tools_mdk.xml文件,发现Stack_Bottom 被设置为+0,错误会不会和这有关呢:
<Set Name="Stack_Bottom" Value="+0"/>
先试试看,我们将这行更改为
<Set Name="Stack_Bottom" Value="0x68010000"/>
编译,然后debug看看。结果还是和之前的一样,没有任何进展。好吧,我们再回头。这个heap不是设置在外部的ram么?那么我设置在CPU内部的RAM会怎么样?所以,将这些数值更改为:
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6800FFFC"/>
<Set Name="Stack_Bottom" Value="0x20001000"/>
<Set Name="Stack_Top" Value="0x20000200"/>
编译完毕,再次debug。结果,还是一样,看来崩溃的问题并不在这里。
或许,我们应该将环境设置得和叶帆的一样。因为,这个编译,本来就是针对于在RAM的,虽然我们强制更改了地址,让程序下载到flash后还能跑一小节,但这都是只读的代码。如果涉及到更改内存的内容,就会崩溃。这会不会就是崩溃的源头?因为现在程序是默认下载到flash中的,我们设置一下,让MDK将程序下载到RAM试试。
这时候的scatterfile_tools_mdk.xml文件设置如下:
<Set Name="Heap_Begin" Value="0x68000000"/>
<Set Name="Heap_End" Value="0x6800FFFC"/>
<Set Name="Stack_Bottom" Value="+0"/>
<Set Name="Stack_Top" Value="0x6801FFFC"/>
<If Name="TARGETLOCATION" Value="RAM">
<Set Name="Code_BaseAddress" Value="0x20001000"/>
<Set Name="Code_Size" Value="0x0000F000"/>
<Set Name="Valid" Value="true"/>
</If>
先编译,成功之后,当然不能直接下载到flash,否则会和之前的状况一样,提示下载出错。我们换个角度想,之所以无法成功下载,不就是scatterfile_tools_mdk.xml文件的地址和MDK的下载地址不对应么?我们直接更改MDK的地址不就可以了?说改就改,依次点击Option For Target->Utilities->Setting,然后将默认的FLASH地址更改为和scatterfile_tools_mdk.xml文件一致的数值,如图:
设置完毕之后,这时候再点击Download,就能够顺利地将程序下载到RAM中了。不过结果还是令人沮丧的,因为到了LOAD_IMAGE_Length = (UINT32)&Image$$ER_RAM$$Length语句,程序还是依然崩溃。
因为现在已经能下载到RAM调试了,大环境变了,所以有些旧账可以翻出来试试。一直觉得scatterfile_tools_mdk.xml的Stack_Bottom不顺眼,将它改成如下数值试试:
<Set Name="Stack_Bottom" Value="0x68010000"/>
重新编译,开始调试。只可惜,结果还是让人郁闷的,依然在老地方崩溃了。
那如果我不用外部的RAM,而是使用内部的RAM,又会有啥结果?继续捣鼓scatterfile_tools_mdk.xml文件,更改如下:
<Set Name="Stack_Bottom" Value="0x2000FFFC"/>
<Set Name="Stack_Top" Value="0x20000000"/>
当然,这个只是用来测试而已,看看问题出在哪里,所以对于Heap_Begin和Heap_End并没有再进行设置。
编译,开始调试,看看结果如何。不过结果更糟糕,在进入BootEntry就出错了,如图:
会不会是Stack_Top的数值搞颠倒了?我们将scatterfile_tools_mdk.xml的statck交换一下:
<Set Name="Stack_Bottom" Value="0x20000000"/>
<Set Name="Stack_Top" Value="0x2000FFFC"/>
编译,再开始调试。虽然这次能进入了BootEntry,但却没有跑到LOAD_IMAGE_Start = (UINT32)&Load$$ER_RAM$$Base,而是在循环中就死掉了。