Difference between revisions of "Starting Cortex-M3 Development Using the GNU Tool Chain - Part 2"
From EdWiki
m (→Hello ARM) |
m (→Linker script for applications using startup.c) |
||
Line 204: | Line 204: | ||
<span style=""><font size="+0.5"> | <span style=""><font size="+0.5"> | ||
− | <syntaxhighlight lang="" line start="1"> | + | <syntaxhighlight lang="C" line start="1"> |
/****************************************************************************** | /****************************************************************************** | ||
* | * |
Latest revision as of 10:08, 6 March 2022
Introduction
In the previous part, we learnt how to write Cortex-M3 Assembly language program and execute it in Qemu. In this part, we will write a simple C-language program and execute it in Qemu.
Setting up the ARM Lab
Please setup an Embedded ARM Lab as described here.
Hello ARM
In this section, we will learn to compile a simple ARM Cortex-M3 program, and test it using Qemu.
int main()
{
int i1,i2,i3;
i1 = 0x12345678;
i2 = 0x87654321;
i3 = i1 + i2;
i3 = i2/i1;
while(1)
;
return 0;
}
Startup Code for use with GNU Tool Chain
//*****************************************************************************
//
// startup.c - Startup code for use with GNU tools.
//
//*****************************************************************************
//*****************************************************************************
//
// Forward declaration of the default fault handlers.
//
//*****************************************************************************
void ResetISR(void);
static void NmiSR(void);
static void FaultISR(void);
static void IntDefaultHandler(void);
//*****************************************************************************
//
// The entry point for the application.
//
//*****************************************************************************
extern int main(void);
//*****************************************************************************
//
// Reserve space for the system stack.
//
//*****************************************************************************
static unsigned long pulStack[64];
//*****************************************************************************
//
// The vector table. Note that the proper constructs must be placed on this to
// ensure that it ends up at physical address 0x0000.0000.
//
//*****************************************************************************
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
// The initial stack pointer
ResetISR, // The reset handler
NmiSR, // The NMI handler
FaultISR, // The hard fault handler
IntDefaultHandler, // The MPU fault handler
IntDefaultHandler, // The bus fault handler
IntDefaultHandler, // The usage fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
IntDefaultHandler, // SVCall handler
IntDefaultHandler, // Debug monitor handler
0, // Reserved
IntDefaultHandler, // The PendSV handler
IntDefaultHandler // The SysTick handler
};
//*****************************************************************************
//
// The following are constructs created by the linker, indicating where the
// the "data" and "bss" segments reside in memory. The initializers for the
// "data" segment resides immediately following the "text" segment.
//
//*****************************************************************************
extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
//*****************************************************************************
//
// This is the code that gets called when the processor first starts execution
// following a reset event. Only the absolutely necessary set is performed,
// after which the application supplied entry() routine is called.
//
//*****************************************************************************
void ResetISR(void)
{
unsigned long *pulSrc, *pulDest;
//
// Copy the data segment initializers from flash to SRAM.
//
pulSrc = &_etext;
pulDest = &_data;
while(pulDest < &_edata )
{
*pulDest++ = *pulSrc++;
}
//
// Zero fill the bss segment.
//
__asm(" ldr r0, =_bss\n"
" ldr r1, =_ebss\n"
" mov r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
" cmp r0, r1\n"
" it lt\n"
" strlt r2, [r0], #4\n"
" blt zero_loop");
//
// Call the application's entry point.
//
main();
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives a NMI. This
// simply enters an infinite loop, preserving the system state for examination
// by a debugger.
//
//*****************************************************************************
static void NmiSR(void)
{
//
// Enter an infinite loop.
//
while(1) {
;
}
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives a fault
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
//
//*****************************************************************************
static void FaultISR(void)
{
//
// Enter an infinite loop.
//
while(1) {
;
}
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives an unexpected
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
//
//*****************************************************************************
static void IntDefaultHandler(void)
{
//
// Go into an infinite loop.
//
while(1) {
;
}
}
Linker script for applications using startup.c
/******************************************************************************
*
* standalone.ld - Linker script for applications using startup.c
*
*****************************************************************************/
ENTRY(ResetISR)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text :
{
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : AT (ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM
}
Building Binaries
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g -c test.c -o test.o arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g -c startup.c -o startup.o arm-none-eabi-ld -g -T standalone.ld startup.o test.o -o test.elf arm-none-eabi-objcopy -O binary test.elf test.bin
Executing in Qemu
In order to use GDB, launch Qemu with the -s option. It will wait for a GDB connection.
qemu-system-arm -M lm3s6965evb --kernel test.bin --serial null -nographic -S -s
In another terminal, launch GDB:
arm-none-eabi-gdb test.elf
In GDB, connect to Qemu:
(gdb) target remote localhost:1234
Then you can use gdb normally. For example, type C to launch the kernel.
(gdb) c
Here are some useful tips in order to use gdb on system code:
use info reg to display all the CPU registers use x/10i $ip to display the code at the PC position