一、概述
MDK开发ARM裸机程序时,在调试阶段通常是先让程序在SDRAM中执行,以加快调试速度,也避免频繁烧写Nor Flash,因此需要知道如何指定程序在哪个位置执行。本文以realarm 2410开发板为例,进行描述。该开发板使用S3C2410A做为CPU,有2MB的 Nor Flash,以及64MB的SDRAM,4KB的SRAM。程序可直接在Nor Flash中调试和运行,如果代码小于4KB,也可以直接在SRAM中运行,然而在SDRAM 中运行,就需要事先用脚本初始化SDRAM,才能把程序加载到SDRAM中执行。
realarm2410使用Nor Flash时的内存映射,如下图:
图1 |
下面详细说明各种情况。
二、程序在Nor Flash中调试
由图1可见,CPU复位后PC指针为0,正好代码在Flash中执行,因此,无需修改PC指针,仅需在程序的初始化部分初始化SDRAM,以及分配好Stack以及Heap则可,要使用分散加载文件*.sct指定程序链接加载的位置,分散加载文件如下:
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_ROM1 0x0 0x00200000 { ; load region size_region ER_ROM1 0x0 0x00200000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_STACK 0x33ff3000 { ; RW data *(STACK) } RW_HEAP 0x33ef3000 { ; RW data *(HEAP) } RW_RAM1 0x30000000 0x04000000 { ; RW data .ANY (+RW +ZI) } RW_IRAM1 0x40000000 0x00001000 { .ANY (+RW +ZI) } } |
分散加载文件LoadToFlash.sct |
该分散加载文件把RO指定到0x0开始大小为0x200000的位置,即2MB的Flash中,RW指定在起始地址为0x30000000大小为0x4000000的64MB的SDRAM中,0x40000000开始的大小为0x1000的4KB的SRAM也作为RW区使用,而Stack则在SDRAM的0x33ff3000开始,Heap则在0x33ef3000开始。大小在初始化程序中指定,如下:
UND_Stack_Size EQU 0x00000400
SVC_Stack_Size EQU 0x00001000
ABT_Stack_Size EQU 0x00000400
FIQ_Stack_Size EQU 0x00001000
IRQ_Stack_Size EQU 0x00001000
USR_Stack_Size EQU 0x00001800
Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size +
FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
Stack_Top EQU Stack_Mem + Stack_Size
Heap_Size EQU 0x00100000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem SPACE Heap_Size
那么,SDRAM除了20KB的Stack和1MB的 Heap外,都当成了RW。SDRAM分配图如下:
|
图2 |
总而言之,要实现Flash调试代码,需实现以下步骤:
- 通过设计分散加载文件,指定RO在Flash,同时分配好其它的RW以及Stack和Heap的地址,并配置工程使用分散加载文件来链接;
- S3C2410A.s要初始化SDRAM,并分配好Stack和Heap;
- 工程中设置使用J-LINK为Flash编程,并设置好Flash的编程算法,勾选调试前需更新代码;
- 在工程设置中设置为调试前更新代码;具体需特别说明的工程设置图如下:
图3:指定Link按照LoadToFlash.sct规则执行 |
图4:指定使用J-LINK/J-TRACK ARM来调试程序 |
图5:一定要勾选Update Target before Debugging |
图5按下settings按键,设置Flash下载设置和编程算法,realarm2410使用的SST39VF1601,MDK有现成的编程算法,直接按下ADD按键,选择芯片型号则可,如图6。
图6:Flash编程设置 |
如此,在MDK中执行start debug则可进入Flash中调试代码了。
提供跑马灯程序的MDK工程,注意选择好Target为DebugInFlash。
跑马灯的MDK工程下载 |
三、程序在SRAM中调试
有了前面在Flash中调试所述的原理基础,设置为SRAM中调试就好办了,按以下步骤:
- 要保证程序小于4KB,RO放到SRAM中;
- 如果使用的RW段、Stack和Heap还是放到SDRAM中,那么依然要在S3C2410A.s中初始化;
- Debug前要执行一个初始化脚本,因为SRAM 地址从0x40000000开始,因此需要在初始化脚本中把PC指针设置到0x40000000中;
- 一定不可勾选调试前更新代码这个选项,否则start debug时会提示错误,实际上SRAM调试的代码不可烧写到Flash中执行。
附上分散加载文件和工程配置图如下:
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_ROM1 0x40000000 0x00001000 { ; load region size_region ER_ROM1 0x40000000 0x00001000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_STACK 0x33ff3000 { ; RW data *(STACK) } RW_HEAP 0x33ef3000 { ; RW data *(HEAP) } RW_RAM1 0x33000000 0x00e00000 { ; RW data .ANY (+RW +ZI) } } |
图7:设置Debug的初始化文件为init_sram_RunFromSram.ini |
init_sram_RunFromSram.ini文件如下:
FUNC void SetupForStart (void) {
// <o> Program Entry Point
PC = 0x40000000;
}
SetupForStart(); // Setup for Running
|
图8:不可勾选Update target before Debugging,可设为使用外部工具 |
提供跑马灯程序的MDK工程下载,注意选择好Target为DebugInSram。
跑马灯的MDK工程下载 |
四、程序在SDRAM中调试
有了前面所述的原理基础,设置为SDRAM中调试基本和SRAM的相似,但是,有以下两个不同点:
- SDRAM不受4KB大小限制;
- SDRAM需要初始化才能使用,因此在debug初始化文件中需要增加初始化SDRAM,并且把要调试程序的按照axf格式加载到SDRAM中;
- 初始化程序S3C2410A.s中不可再初始化,因为debug初始化脚本已经初始化过了,而且加载了程序,如果再初始化那会把程序给破坏点了。
还是用分散加载文件指定RO、RW、Stack以及Heap的位置,把RO分配到SDRAM的起始的2MB中,其它和前面一样,文件如下:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_ROM1 0x30000000 0x00200000 { ; load region size_region
ER_ROM1 0x30000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_STACK 0x33ff3000 { ; RW data
*(STACK)
}
RW_HEAP 0x33ef3000 { ; RW data
*(HEAP)
}
RW_RAM1 0x31000000 0x03000000 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM1 0x40000000 0x00001000 {
.ANY (+RW +ZI)
}
}
Debug初始化文件如下:
FUNC void SetupForStart (void) {
// <o> Program Entry Point
PC = 0x30000000;
}
FUNC void Init (void) {
_WDWORD(0x4A000008, 0xFFFFFFFF); // Disable All Interrupts
_WDWORD(0x53000000, 0x00000000); // Disable Watchdog Timer
// Clock Setup
// FCLK = 150 MHz, HCLK = FCLK/2 MHz, PCLK = HCLK/2 MHz
_WDWORD(0x4C000000, 0x0FFF0FFF); // LOCKTIME
_WDWORD(0x4C000014, 0x0000000F); // CLKDIVN
_WDWORD(0x4C000004, 0x00043011); // MPLLCON
_WDWORD(0x4C000008, 0x00038021); // UPLLCON
_WDWORD(0x4C00000C, 0x001FFFF0); // CLKCON
// Memory Controller Setup for SDRAM
_WDWORD(0x48000000, 0x22000000); // BWSCON
_WDWORD(0x4800001C, 0x00018005); // BANKCON6
_WDWORD(0x48000020, 0x00018005); // BANKCON7
_WDWORD(0x48000024, 0x008404F3); // REFRESH
_WDWORD(0x48000028, 0x00000032); // BANKSIZE
_WDWORD(0x4800002C, 0x00000020); // MRSRB6
_WDWORD(0x48000030, 0x00000020); // MRSRB7
_WDWORD(0x56000000, 0x000003FF); // GPACON: Enable Address lines for SDRAM
}
// Reset chip with watchdog, because nRST line is routed on hardware in a way
// that it can not be pulled low with ULINK
_WDWORD(0x40000000, 0xEAFFFFFE); // Load RAM addr 0 with branch to itself
CPSR = 0x000000D3; // Disable interrupts
PC = 0x40000000; // Position PC to start of RAM
_WDWORD(0x53000000, 0x00000021); // Enable Watchdog
g, 0 // Wait for Watchdog to reset chip
Init(); // Initialize memory
LOAD .\led_InSdram.axf INCREMENTAL // Download program
SetupForStart(); // Setup for Running
脚本中的Init函数对寄存器的操作含义可以查阅S3C2410的寄存器手册,实现的就是关中断,看门狗,设置时钟,初始化内存控制器。LOAD .\led_InSdram.axf INCREMENTAL作用就是把led_InSdram.axf文件加载进去,axf为elf格式的文件,本身带有加载的地址,因此只要分散加载文件按这只正确了,编译链接后生成的axf文件就有了正确的加载地址;最后把PC设置到RO的基址0x30000000执行代码。
S3C2410A.s需关闭内存控制器的初始化功能,找到这一行代码:MC_SETUP EQU 1;改为MC_SETUP EQU 0。
提供跑马灯程序的MDK工程下载,注意选择好Target为DebugInSdrSram。并记得修改MC_SETUP EQU 0。
跑马灯的MDK工程下载 |