CodeSourcery provides toolchains for many architectures, and among them there are “bare metal” toolchains that produce programs that can be run natively on the processor.
In the past I already explored some possibilities about developing bare metal ARM programs:
- Simplest bare metal program for ARM
- Hello world for bare metal ARM using QEMU
- Using Ubuntu ARM cross-compiler for bare metal programming
- Using Newlib in ARM bare metal programs
In my opinion the most interesting purpose of the bare metal toolchains is development on small microcontrollers, such as the ones based on Cortex-M processors.
My beloved QEMU is able to make me try a system without buying the hardware first; in particular, mainline QEMU can emulate the Texas Instruments Stellaris lm3s6965 microcontroller, which is a Cortex-M3 based chip that has 256KiB of Flash, 64KiB of RAM, some serial ports, ADConverters, timers and many other peripherals.
I wanted to be able to write a small “Hello World” program that prints on the serial port with as little effort as possible (in terms of code to write), and emulate it on QEMU. Basically I want to be able to compile and run this “main.c
“:
#include <stdio.h> int main() { printf("Hello World!\n"); return 0; }
For this purpose, I installed the “qemu-system
” package on my Debian machine, and installed the “Sourcery G++ Lite 2011.03-42 for ARM EABI” toolchain.
The “arm-none-eabi
” toolchain includes Newlib as an implementation of the C library, but it also contains a particular library that is linked by default in compiled programs, which is called CS3™: the CodeSourcery Common Startup Code Sequence. This library is associated with linker scripts for generic and specific platforms. All this specific low-level code is present in many version inside the installation package, because there are many ARM architectures. In my case, since I wanted to develop for Cortex-M3, I need to use the “thumb2
” versions of the library, as stated in the “Getting Started” guide of the toolchain. To do so, I must supply some flags to the compiler: “-mthumb -march=armv7 -mfix-cortex-m3-ldrd
“.
A correct linker script must be supplied, to indicate the memory map and where to put the code and data. The arm-none-eabi toolchain package contains a generic linker script in the path “.../Sourcery_G++_Lite/arm-none-eabi/lib/thumb2/generic-m.ld
” that I modified in the following way into a file called “lm3s6965.ld
“:
/* Linker script for lm3s6965 ARM Cortex-M3 microcontroller * * Modified from generic-m.ld file shipped in: * Sourcery G++ Lite 2011.03-42 * * Version: Sourcery G++ Lite 2011.03-42 * Support: https://support.codesourcery.com/GNUToolchain/ * * Copyright (c) 2007, 2008, 2009, 2010 CodeSourcery, Inc. * * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this * notice is included verbatim in any distributions. No written agreement, * license, or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their authors * and need not follow the licensing terms described here, provided that * the new terms are clearly indicated on the first page of each file where * they apply. */ OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") ENTRY(__cs3_reset) SEARCH_DIR(.) GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3micro) MEMORY { flash (rx) : ORIGIN = 0x00000000, LENGTH = 256K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K } /* These force the linker to search for particular symbols from * the start of the link process and thus ensure the user's * overrides are picked up */ EXTERN(__cs3_reset __cs3_reset_generic_m) EXTERN(__cs3_start_asm __cs3_start_asm_sim) /* Bring in the interrupt routines & vector */ INCLUDE micro-names.inc EXTERN(__cs3_interrupt_vector_micro) EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end) /* Provide fall-back values */ PROVIDE(__cs3_heap_start = _end); PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram); PROVIDE(__cs3_region_num = (__cs3_regions_end - __cs3_regions) / 20); PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram); SECTIONS { .text : { CREATE_OBJECT_SYMBOLS __cs3_region_start_flash = .; _ftext = .; *(.cs3.region-head.flash) ASSERT (. == __cs3_region_start_flash, ".cs3.region-head.flash not permitted"); __cs3_interrupt_vector = __cs3_interrupt_vector_micro; *(.cs3.interrupt_vector) /* Make sure we pulled in an interrupt vector. */ ASSERT (. != __cs3_interrupt_vector_micro, "No interrupt vector"); PROVIDE(__cs3_reset = __cs3_reset_generic_m); *(.cs3.reset) __cs3_start_asm_sim = DEFINED(__cs3_start_asm) ? __cs3_start_asm : __cs3_start_asm_sim; *(.text.cs3.init) *(.text .text.* .gnu.linkonce.t.*) *(.plt) *(.gnu.warning) *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.ARM.extab* .gnu.linkonce.armextab.*) *(.gcc_except_table) } >flash .eh_frame_hdr : ALIGN (4) { KEEP (*(.eh_frame_hdr)) } >flash .eh_frame : ALIGN (4) { KEEP (*(.eh_frame)) } >flash /* .ARM.exidx is sorted, so has to go in its own output section. */ PROVIDE_HIDDEN (__exidx_start = .); .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >flash PROVIDE_HIDDEN (__exidx_end = .); .rodata : ALIGN (4) { *(.rodata .rodata.* .gnu.linkonce.r.*) . = ALIGN(4); KEEP(*(.init)) . = ALIGN(4); __preinit_array_start = .; KEEP (*(.preinit_array)) __preinit_array_end = .; . = ALIGN(4); __init_array_start = .; KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) __init_array_end = .; . = ALIGN(4); KEEP(*(.fini)) . = ALIGN(4); __fini_array_start = .; KEEP (*(.fini_array)) KEEP (*(SORT(.fini_array.*))) __fini_array_end = .; . = ALIGN(0x4); KEEP (*crtbegin.o(.ctors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*crtend.o(.ctors)) . = ALIGN(0x4); KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*crtend.o(.dtors)) . = ALIGN(4); __cs3_regions = .; LONG (0) LONG (__cs3_region_init_ram) LONG (__cs3_region_start_ram) LONG (__cs3_region_init_size_ram) LONG (__cs3_region_zero_size_ram) __cs3_regions_end = .; . = ALIGN (8); _etext = .; } >flash ASSERT (!(__cs3_region_init_ram & 7), "__cs3_region_init_ram not aligned") ASSERT (!(__cs3_region_start_ram & 7), "__cs3_region_start_ram not aligned") ASSERT (!(__cs3_region_init_size_ram & 7), "__cs3_region_init_size_ram not aligned") ASSERT (!(__cs3_region_zero_size_ram & 7), "__cs3_region_zero_size_ram not aligned") .data : ALIGN (8) { __cs3_region_start_ram = .; KEEP(*(.jcr)) *(.got.plt) *(.got) *(.shdata) *(.data .data.* .gnu.linkonce.d.*) . = ALIGN (8); *(.ram) . = ALIGN (8); _edata = .; } >ram AT>flash .bss : ALIGN (8) { *(.shbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) . = ALIGN (8); *(.ram.b .bss.ram) . = ALIGN (8); _end = .; __end = .; } >ram __cs3_region_init_ram = LOADADDR(.data); __cs3_region_init_size_ram = _edata - __cs3_region_start_ram; __cs3_region_zero_size_ram = _end - _edata; __cs3_region_size_ram = LENGTH(ram); .stab 0 (NOLOAD) : { *(.stab) } .stabstr 0 (NOLOAD) : { *(.stabstr) } /* DWARF debug sections. * Symbols in the DWARF debugging sections are relative to * the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* DWARF 2.1 */ .debug_ranges 0 : { *(.debug_ranges) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } /DISCARD/ : { *(.note.GNU-stack) } }
The lm3s6965 manual indicates that the 256KiB Flash is placed at address 0x00000000
, while the 64KiB RAM starts at address 0x20000000
, and it is reflected in the “MEMORY
” part of the linker script. One other important modifications with respect to the original linker script is putting the “.data
” and “.bss
” section into RAM, because it is a read-write memory, while the Flash memory is read-only in normal situations. The Flash memory will be filled by QEMU with a binary file that we provide, and the CS3 code will take care of initializing the RAM at boot time.
We need to override the reset code of the CS3 library because the default behavior is implemented to be run inside a generic simulator. Instead we can simply create an assembly file that implements the very first code to be executed and then calls the CS3 functions (“__cs3_start_c
” to be precise). I created the following “reset.S
” assembly file:
.text .thumb .global __cs3_reset .thumb_func __cs3_reset: # add peripherals and memory initialization here LDR r0, =__cs3_start_asm BX r0 .thumb_func __cs3_start_asm: # add assembly initializations here LDR r0, =__cs3_start_c BX r0 .end
The last thing needed to compile is low-level input/output functions that connect the “stdio
” functions to the microcontroller’s UART. From the microcontroller’s manual it seems that the UART in question is the ARM PrimeCell PL011 that I already explored in a previous post; also, the start address of the UART registers is 0x4000C000
. I can use the following “syscalls.c
” file that I already created for Newlib tests, and that I took mainly from this tutorial:
#include <sys/stat.h> enum { UART_FR_RXFE = 0x10, UART_FR_TXFF = 0x20, UART0_ADDR = 0x4000C000, }; #define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) #define UART_FR(baseaddr) (*(((unsigned int *)(baseaddr))+6)) int _close(int file) { return 0; } int _fstat(int file, struct stat *st) { st->st_mode = S_IFCHR; return 0; } int _isatty(int file) { return 1; } int _lseek(int file, int ptr, int dir) { return 0; } int _open(const char *name, int flags, int mode) { return -1; } int _read(int file, char *ptr, int len) { int todo; if(len == 0) return 0; while(UART_FR(UART0_ADDR) & UART_FR_RXFE); *ptr++ = UART_DR(UART0_ADDR); for(todo = 1; todo < len; todo++) { if(UART_FR(UART0_ADDR) & UART_FR_RXFE) { break; } *ptr++ = UART_DR(UART0_ADDR); } return todo; } static char *heap_end = 0; caddr_t _sbrk(int incr) { extern char __cs3_heap_start; /* Defined by the linker */ extern char __cs3_heap_end; /* Defined by the linker */ char *prev_heap_end; if (heap_end == 0) { heap_end = &__cs3_heap_start; } prev_heap_end = heap_end; if (heap_end + incr > &__cs3_heap_end) { /* Heap and stack collision */ return (caddr_t)0; } heap_end += incr; return (caddr_t) prev_heap_end; } int _write(int file, char *ptr, int len) { int todo; for (todo = 0; todo < len; todo++) { UART_DR(UART0_ADDR) = *ptr++; } return len; }
Now it’s time to compile the program, create the binary code that must be written in the Flash, and call QEMU to emulate it:
$ arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -T lm3s6965.ld main.c reset.S syscalls.c -o main $ arm-none-eabi-objcopy -O binary main main.bin $ qemu-system-arm -M lm3s6965evb --kernel main.bin --serial stdio Hello World!
From this point, it is possible to create a more complex application using the C standard library as needed. This application should work also on real hardware with some modifications on the UART code, that currently does not check if the write buffer is full and does not initialize the port speed, for example. Other than that, the embedded Flash must be written with the binary file using the tools that the microcontroller vendor provides.
Jerry Jacobs
2011/09/21
This is a great tutorial !
John Pitt
2011/09/28
qemu: hardware error: Unknown device ‘ssd0323’ for bus ‘SSI’
CPU #0:
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=00000000
PSR=40000153 -Z– A svc32
Aborted
John Pitt
2011/09/28
i am getting this above error,
Please send me what i have to do so that my code works well and i get correct output
Thanx in advance…… 🙂
Balau
2011/10/02
This seems like a problem in QEMU: the R15 register (which is the program counter) is still at 0, so the code has yet to run.
If you are not using the latest QEMU version, you can download the source code and compile it.
You can find an example on how to compile QEMU from this post, in the QEMU section:
Debugging ARM programs inside QEMU
Adrian Arce
2011/10/12
You are a genius my friend!!! Thanks for all your tutorials, I have learned a lot with it. Go ahead with your work, we need more tutos like yours..
Vencax
2012/01/26
Hi, VERY useful tutorial. Could you yet provide a snippet (or description) how to simulate interrputs?
Balau
2012/01/26
I think it’s not trivial to manage interrupts, I am planning to write a post about that in the future.
Meanwhile, take a look at some ARM official documentation:
Click to access DUI0552A_cortex_m3_dgug.pdf
Click to access DUI0471F_developing_for_arm_processors.pdf
Also, you can check the CMSIS library:
http://www.onarm.com/cmsis/download/
QEMU should be able to emulate the interrupts quite correctly, since it can run Linux.
Iggo
2012/02/29
Hi! Sorry to bother you… Maybe I ll be off topic… but.. I would like to ask u some advices…
I m very a noob, I m trying to debug x-loader via eclipse gdb connected to linaro qemu-Beagleboard xm. I loaded the sources, and I m using a precompiled version of x-loader (MLO) on qemu with -s -S to stop execution for debugging. Obviously (not so much for me…) I cannot “link” the source code listed, to the execution. I m able to execute step by step, but not to set breakpoints or to change register values on the emulated machine… Some ideas? Maybe that behaviour is correct… but I don’t know :-)….
Thank u very much..
Igor.
Balau
2012/03/01
What is eclipse using as a debugger? It should be something like arm-none-eabi-gdb.
Have you tried using arm-none-eabi-gdb from the command line (outside eclipse) and see if something changes?
Iggo
2012/03/01
Sorry.. I meant: I m using eclipse (but actually eclipse uses gdb, I think) and I recompiled gdb for arm architecture… even if, I compiled not as arm-none-eabi-gdb but arm-linux-eabi-gdb. What I don’t really know is: if I use a debugger with qemu, but x-loader is not compiled with -g flag, should I be able to set breackpoints, set register values and so on?
Thank u very much, sorry for not being clear.
Balau
2012/03/01
No problem, we’re here to learn.
I think you should be able to set breakpoints, but not with symbol names such as “break main”, you must specify an address.
I think you should also be able to change registers anyway, but you must use ARM version of gdb.
I’m not sure of what you said: you say you recompiled gdb for ARM architecture, but in my opinion it is better to use a GDB that is already prepared for you, such as the one shipped in CodeSourcery toolchain, or the EmDebian one (for Debian) or the Linaro one (for Ubuntu).
Igor
2012/03/04
Sorry… here again… 🙂
I m on linux (crunchbang deb based) what I ve done:
– installed codesourcery arm-none-eabi
– installed eclipse with CDT ARMtools an GIT
– compiled linaro qemu with BB-xm support.
– downloaded linaro SD card image for beagleboard-xm (obviously there is MLO in the image)
– got x-loader (MLO) from git with eclipse
Now:
– I managed to compile qemu and it works.
– I cross compiled x-loader (after changing the name of the compiler in the Makefile to arm-none-eabi..)
– I launched Qemu with the -s -S and the linaro sd image.
– I connect with Eclipse to 127.0.0.1:1234 (using the codesourcery arm-none-gdb).
Everything OK.
BUT (there’s always a ‘BUT’) in the debug perspective I get: “No source available for “0x4…..” which could be ok ( I think) cause the MLO running in QEMU is not compiled with “-g” flag.
I can see the registers and go on step by step… but even if I set breakpoints… the execution doesn’t stop after I click Resume(F8).
I m just asking (cause u r obviously more experienced than I am) am I missing something?? Am I making a noob mistake???
I m still abit confused about arm-none-eabi and arm-linux-eabi, but in this case, building bare metal sw… should be arm-NONE… I hope..
Thank u VERY VERY much.
Bye Igor.
Balau
2012/03/05
It seems to me that you are doing the correct things. I confirm you that “No source available” is expected if you don’t have the executable compiled with debug information (-g flag). If you are setting the breakpoints using the address (for example “break *0x40001000”) then they should work. If they do not work, maybe there’s something wrong in QEMU, more specifically in the way that breakpoints (hardware or software) are implemented.
Try to see if something change by using hardware breakpoints or software breakpoints explicitly. By default, when you run “break”, GDB decides whether to use a software breakpoint or an hardware breakpoint. To explicitly set a hardware breakpoint, there’s the “hbreak” commands that can be used like “break”. To explicitly set a software breakpoint, you need to execute “set breakpoint auto-hw on”, and after that the “break” command is always a software breakpoint. See also this: http://sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html
After this check, I think you should ask the Linaro QEMU mailing list if they have some suggestions or if they have known issues on breakpoints.
Stan
2012/04/01
Hi, Balau
First of all I would like to thank you for the useful tutorials.
This is off topic but I will try to explain what I’m trying to achieve. I want to run a linux port for ARM cortex M3 with qemu. I’ve prepared some basic board support for it. The port is almost working with one exception, the init (pid 1) process. I’ve compiled a simple application which doing while(1) and compiled it with the following command
arm-uclinixeabi-gcc -static -Wl,-elf2flt init.c -o init
next. I’ve created a initramfs.cpio file and pointed it to the linix configuration.
When I run the linux produced Image I see this log:
io scheduler noop registered (default)
mice: PS/2 mouse device common for all mice
Freeing init memory: 48K
p=81deaffc
start_thread(regs=0x81c21f80, entry=0x81de8045, start_stack=0x81deafa8)
### filename: /init – we were successful
### arm_syscall: (no: 3581de80, __ARM_NR_BASE: f0000)
### bad_syscall:
[1] init: obsolete system call 3581de80.
Code: 4d2d 04f8 df0d 14f8 (4dc0)
### do_notify_resume(regs=81c21fa8)
syscall: 0, flags: 1
### do_signal(regs=81c21fa8)
init/1: potentially unexpected fatal signal 4.
Pid: 1, comm: init
CPU: 0 Not tainted (2.6.33-arm1 #396)
PC is at 0x81de805d
LR is at 0x0
pc : [] lr : [] psr: 81000000
sp : 81deaf88 ip : 00000000 fp : 00000000
r10: 81de9b78 r9 : 00000000 r8 : 00000000
r7 : 3581de80 r6 : 000000d0 r5 : 1a3201d1 r4 : 00000000
r3 : 00000000 r2 : 81deafbc r1 : 81deafb4 r0 : f4000000
Flags: Nzcv IRQs on FIQs on Mode USER_26 ISA (null) Segment kernel
Kernel panic – not syncing: Attempted to kill init!
[] (unwind_backtrace+0x1/0x80) from [] (panic+0x33/0xc4)
[] (panic+0x33/0xc4) from [] (do_exit+0x47/0x406)
[] (do_exit+0x47/0x406) from [] (do_group_exit+0x4d/0x6c)
[] (do_group_exit+0x4d/0x6c) from [] (get_signal_to_deliver+0x1c1/0x1e8)
[] (get_signal_to_deliver+0x1c1/0x1e8) from [] (do_signal+0x41/0x3ae)
[] (do_signal+0x41/0x3ae) from [] (do_notify_resume+0x2b/0x5c)
[] (do_notify_resume+0x2b/0x5c) from [] (work_pending+0x15/0x16)
After some debugging I found that the dump is produced from vector_swi() exeption handler and it seems that syscall number should be written in r7 register but the register value is wrong (3581de80). So my conclusion is that the init process doesn’t fill correctly the syscall number in r7 (according to EABI).
Could you give some pointers on which direction should I dig in.
Thanks in advance !
—
best regards,
Stan
Balau
2012/04/01
The toolchain that you are using ships some libraries that implements the syscalls, and they are linked with any program that you build.
In my opinion you can:
– use a newer ucLinux toolchain that works with the kernel version you have.
– use an older kernel that works with the toolchain version you have.
– rebuild your own toolchain (hard work)
– override the system calls. If you link a function that has the same name as a system call (write? _write? depends on what the toolchain does) then the library function will not be used. I think you can find them by disassembling init (with arm-uclinuxeabi-objdump -dxS for example) and finding the “svc” (or “swi”, I don’t remember right now) instruction. The disassembler should probably dump also the beginning of the functions; if it does not, build init in ELF mode (without elf2flt) and disassemble from that file.
sasa
2012/07/21
Hi there,
I’m using the same toolchain for cortex-M3 and as long as I don’t link the standard library, everything is fine. But if I do so, I can’t understand why it links it the symbols of the ARMv5TE architecture instead of those for v7. Beside the explicit -L flag, is there a way to fix this in codesourcery? How did you to this?
Balau
2012/07/21
What is the command line of your compilation? Have you tried the “
-mcpu=cortex-m3
“? Are you giving the same architecture options also when linking?I’m doing what I wrote on the blog post and I see from the map file (adding “
-Xlinker -Map=main.map
” to the compiler options) that the “thumb2
” libraries are being linked.The version I am using is “arm-none-eabi-gcc (Sourcery G++ Lite 2011.03-42) 4.5.2
“.I suggest you try this GCC toolchain from ARM, which is targeted at Cortex-M and -R processors, thus more suitable to your needs.
shwetavn
2012/08/27
Hi,
I’m trying to add NAND flash to lm3s811evb in QEMU.
Since I’m new to QEMU I donot know what dependencies exist in adding a new device.
So please let me know how can this be done.
Balau
2012/08/27
I have no experience in modifying QEMU code, I don’t know what needs to be done.
I know this example of adding parallel Flash to an existing machine:
http://thomas.enix.org/Blog-20081002153859-Technologie
I think you could ask for information from qemu-devel mailing list.
shwetavn
2012/09/04
Hi,
To understand adding of NAND flash controller to a machine, I want to understand TOSA code. Can you please tell me how I can compile and execute test programs on TOSA.
Balau
2012/09/04
I know almost nothing about TOSA.
I see from a quick search that it’s a PDA that contains a Intel XScale PXA255, which is similar to ARM926.
I can only suggest to try something like the following:
hw/tosa.c
“-d in_asm,cpu -D ./qemu.log -singlestep
” options, to make it generate a (big) log that can help you trace what happens-s -S
” options and attaching with the GDB of the toolchain you are using, using “target remote localhost:1234
” and debugging with thatI can’t help you more.
Sudheendra
2012/09/24
what do i need to do to build the same program on windows.
when i say qemu-system-arm -help on windows 7 msys shell, nothing is printed. what might be the problem?
Balau
2012/09/24
I never tried to do the same on Windows, I don’t know if it’s easy.
For sure you need both the toolchain and QEMU Windows programs.
For the toolchain, it seems Sourcery CodeBench has a Windows installer, so that should not be much of a problem.
For QEMU, this page seems promising: http://qemu.weilnetz.de/
Depending on your Windows 7 installation, you should check the w64 or w32 folders.
Try to understand the version of QEMU, I can’t tell from that site but once you installed it you should make sure it’s a quite new version. I suppose any version greater than 1.0 should be OK.
I don’t know what QEMU have you installed, try with “
qemu-system-arm /help
“, because Windows programs tend to use the slash for options.Hope this helps, I don’t think I can help you more than this.
Sudheendra
2012/09/25
hi thanks for your help. after copying the required .dll files i am able to run qemu. i also get the print statements in qemu source code. but when i try to run the above application it crashes. please let me know if i have to do anything else.
also is there any way to check the arm registers on windows environment after running assembly program.
Balau
2012/09/25
You can try:
I don’t know if they work on Windows, I never tried, but that’s what I would do on Linux.
minghuasweblog
2012/09/27
Reblogged this on minghuasweblog.
brad
2012/10/17
Nice article but I have a question. I am converting my ARM RVDS4.1 project (using C and C++) over to CodeSourcery and I keep getting this link error.
In function `__libc_init_array’: init.c:(.text+0x40): undefined reference to `_init’
Any idea what I am missing in my makefile?
Balau
2012/10/17
I tried to generate the map file of my example.
“
_init
” is a function in “Sourcery_G++_Lite/lib/gcc/arm-none-eabi/4.5.2/thumb2/crti.o
“.I get the same error as yours if I add “
-nostartfiles
” during link step.brad
2012/10/18
So I removed my -nostartfiles flag but I still have to manually link in
CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_EABI/lib/gcc/arm-none-eabi/4.6.3/crti.o
But you are using a different tool chain than me
manjeet Pawar
2012/10/23
Hi .Balau……I have to run Bare-metal test or system level validation tests for cortex a15.
I need to understand following things:-
MACK (simplified OS for validation)
based stress testing
• MPRIS – pthead based tests for MP
• ‘C’ Stress testing library (including
coherency tests and targeted stress)
Can u explain me what they are..As I am really confused about these…
Thanks
Balau
2012/10/23
I can just imagine, because it’s the first time I see those two acronyms.
I assume you are talking about this PDF because your things seem copy-pasted from page 20.
It seems RIS means “Random Instruction Sequence” and the MP stands for Multi-Processor. I can suppose MPRIS is a pthread (not pthead, it’s probably a typo) program that stimulates many instruction sequences to see if the processors step on each other’s toes and screw the execution.
The C stress testing library is probably a set of functions that you can call with some parameters, and they stress something about the architecture. “coherency” is a concept usually associated with caches, basically it should be the fact that the cache line contains exactly the content of the remote memory.
Both seems to run on some sort of (probably small) operating system. I suppose it’s a POSIX-compatible OS because it has pthreads.
It’s probably a proprietary solution that can’t be found online.
I hope this helps, but I strongly suggest you talk clearly to whoever gave you this task.
Manjeet
2012/10/26
Thanks Balua…
I tried to find info about these things…I thought I could have get some tests to run..But finally I reached to your blog while searching for “Bare-metal” TEST because there are three type of validation for an architecture:- unit level ,top level and system level. And in system level we have Bare-metal,OS based apps and stress tests to run(I get all this info from the same PDF).But I am confused as all such tasks or tests are not available on net to test for a system.F.eg there is a company “Obsidian Software” which used to took care of system verifiaction for ARM architecture.But now ARM has bought it.
I think such things they haven’t made it open source.And for a kernel programmmer its difficult to think for these things,whras people who are working on VLSI may have much ideas about this.
Initially I wanted to try your blog ,but i thought better to ask before trying…
Aditya
2012/12/27
Hi,
I am using the arm-none-eabi-gcc to compile my code.
I want to generate an SIO file for cortex r5:
Here is what I am trying to do-
$(LINK) has lm3s6965.ld, main.c, reset.S files.
$(OBJECTS) has all the .c files in it.
$(files) have the .o files generated.
1)arm-none-eabi-gcc -c -mthumb -march=armv7-r -T $(LINK) $(OBJECTS)
2)arm-none-eabi-gcc -fprofile-arcs -ftest-coverage -mthumb -march=armv7-r -T $(LINK) $(files) -o my_test
3) arm-none-eabi-objcopy -O binary my_test test.axf
4) $(FROMELF) –bin -o final.SIO test.axf
Is this the correct way of doing it?
I am not very good in this and would like if you help me with this.
Thanks!
Balau
2012/12/27
Watch out. In my blog post I used gcc to compile and link everything in a single step.
A more common way is to first compile each C source file into an object file using the “-c” option. Then link all the object files together to create the ELF executable.
If I wanted to do it in the second way, it could have been:
arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -c main.c -o main.o
arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -c reset.S -o reset.o
arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -c syscalls.c -o syscalls.o
arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -T lm3s6965.ld main.o reset.o syscalls.o -o main
Then, your last two commands may be doing something wrong. They both need an ELF file as input, which is “my_test”. If you need a binary image of the program, then this command should be enough:
arm-none-eabi-objcopy -O binary my_test final.SIO
The axf extension usually indicates that the file is an executable (ARM Executable Format) and it usually means an ELF (Executable Linkable Format) executable. For this reason you could have called it “my_test.axf” instead of “my_test”. Try to run these commands and understand the results:
file main.c
file main.o
file my_test
Last thing: be aware that my example is not suited for Cortex-R5 processors. In particular the reset code is very different, the Cortex-R processors have a similar reset procedure as the Cortex-A and the ARM926. Also the memory map of the chip architecture should be reflected in the “.ld” linker script. You could start with a R5 program that works and then try to build your own program.
One thing that could help is seeing the disassembled program, if you execute “arm-none-eabi-objdump -dS my_test > my_test.dis” you can see in my_test.dis the result of your build in text format, and compare it with the expected result.
Aditya
2012/12/27
Hi Balau,
Thanks for the explanation.
1) Where can I find the reset, linker code for cortex r5 so that I can modify the original code to suit my needs.(Memory mapping etc.)
2) So, the general procedure what I have understood is like:
Create object files for the reset ,syscalls and other .c files first. Then, make a main from all the object files and finally, the objcopy to SIO.
I will try this out.
Aditya
2012/12/27
The above thing worked!!
Now I need to do the same thing for cortex r5.
Where can I get the command line syntax for r5 and the reset and linker files (for arm-none-eabi) ?
Thanks
Balau
2012/12/27
1) I suggest to take a look at Developing Software for ARM Processors, in particular the section on Vector table for ARMv6 and earlier, ARMv7-A and ARMv7-R profiles, which is relevant for Cortex-R processors.
There’s also a PDF version.
Be aware that the ARM assembler is different from GNU assembler, so you probably need to modify the reset assembly code to make it do the same things.
2) yes, you are on the right way.
Aditya
2012/12/28
How can I modify the vector table?
This is what I want to do:
DSRAM size is 32k
DSRAM_ENABLE EQU (1 << 2)
ISRAM_ENABLE EQU (1 << 12)
VECTOR_SELECT EQU (1 << 13)
PRESERVE8
AREA IRAM_CODE, CODE, READONLY
CODE32
EXPORT Start
Start
ENTRY
reset
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit ; No interrupts
LDR SP, =SVC_Stack
IMPORT __main
B __main
IMPORT main
IMPORT __rt_lib_init
EXPORT __rt_entry
__rt_entry
bl __rt_lib_init
b main ;
END
This was compiled with armcc.
Where can I get the __rt_entry function?
Balau
2012/12/29
If you want to rewrite the vector table and the reset code with arm-none-eabi-gcc, I suggest following the “Building Bare-Metal ARM Systems with GNU” guide: http://www.embedded.com/design/embedded/4007119/Building-Bare-Metal-ARM-Systems-with-GNU-Part-1–Getting-Started
This is the PDF version: http://www.state-machine.com/arm/Building_bare-metal_ARM_with_GNU.pdf
Be aware that some of it is specific to the AT91SAM7S64. I have no experience with Cortex-R processors so I don’t know what are the changes.
Regarding the __rt_entry function, it’s probably something specific to armcc. I don’t have it installed because it’s expensive. You could ask the people who are paid to support that product.
amal
2014/04/23
Hello
I’m working on this tutorial and when i do” make all” i have this error
/home/tools/install/bin//arm-none-eabi-cpp reset.s | /home/tools/install/bin//arm-none-eabi-as -EL -march=armv7-m -mthumb -o reset.o
/home/tools/install/bin//arm-none-eabi-gcc -g -march=armv7 -mthumb -c -o main.o main.c
/home/tools/install/bin//arm-none-eabi-gcc -nostartfiles -Wl,-Map=main.map,–cref,–gc-sections -mfix-cortex-m3-ldrd -lc -lgcc -T lm3s6965.ld -o main.elf reset.o main.o
/home/tools/install/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld:lm3s6965.ld:39: syntax error
collect2: ld returned 1 exit status
make: *** [main.elf] Erreur 1
Can you help me please
Balau
2014/04/23
Some things come to mind:
* Add
-Wl,--verbose
option to the linking phase to give you more info.* The
-m
options should be used also in the linking phase. Line 39 in my linker script isINCLUDE micro-names.inc
. The linker searches for these things based on-m
options.* Your toolchain seems to be a different version than mine, I don’t know if my linker script is still valid. Try to check the differences of the linker script with the
generic-m.ld
of your toolchain.* If you name an assembly with uppercase
.S
extension and give it as input togcc -c
it will preprocess it and assemble it without callingcpp
andas
directly.Alan Moraes
2014/06/18
Hi, Balau,
Thank you for all your tutorials. They are a invaluable resource for newcomers.
I was able to reproduce your results using Sourcery G++ Lite 2011.03-42 and QEMU 2.0.0 in Debian Testing.
Regards,
Alan
june
2014/07/22
Hi,
You have quite nice tutorials on the blog, Great work
I am currently working on PIC32MZ and struggling to do something similar
“CodeSourcery bare metal toolchain working on PIC32MZ.”
I am looking for example startup script and linker script for PIC32MZ
I am surprised that I couldn’t find anything on Internet, one very old link is here
http://www.nadler.com/embedded/PIC32MX_using_CS.html
Do you know anything which might be useful to me.
Thanks
Balau
2014/07/22
I don’t have experience with MIPS32, which seems to be the baseline architecture of PIC32MZ. I suggest searching something about MIPS32 (not specific to PIC32MZ) and try to adapt it to your case. Also read well the PIC32MZ datasheet that seems very rich of information; for example the the memory layout section and CPU exception section seems to contain useful information about the startup. Another way is to take a look at U-Boot source code, which is a mature bare metal project, and see what they use as startup. If you have an executable compiled for PIC32MZ, and the format of the executable is ELF, you could try to disassemble it with -objdump -d to see if you can make sense of the startup part.
Yoda
2014/09/25
Hey Balau, excellent work. I have one query. You implemented syscalls to force the stdio functions to use the open,read,write functions which connect with UART.
How does this override happen ? Why won’t the linker not accept the standard library provided and instead take the read/write functions from the syscall.c file.
In the linker script line 32, the comment says, this overrides the default values and looks for user generated symbols.
Also how did you come up with names of syscall functions so that it matches the standard library and is able to override it.
Balau
2014/09/27
I am not completely sure about the priority of linking one symbol instead of another, but here’s my understanding of it:
In the command that compiles the program, there is a hidden step: each source file is compiled into a machine code object file (usually with extension
.o
that substitutes.c
). Then the linker receives all the object files compiled from source and receives the linker script and command line options. Usually all the object files are considered first. Then the linker considers all the symbols that are referenced by the program code but are not linked yet. So it searches into system libraries, libraries passed from the command line with-l
options and libraries indicated in the linker script.So if I already provide the
_open
function inside my source file, the linker won’t try to search for_open
somewhere else, and my implementation is the one that is placed in the final build.I took the linker script from the compiler installation so I’m not completely sure about its effect. The manual can help.
About the names, I tried to link everything without providing the functions. This generates many “undefined reference” errors that can be used to understand the missing functions. They have the same prototypes as their system call counterparts without the initial underscore
_
. The<a href="https://sourceware.org/newlib/" rel="nofollow">newlib</a>
source code can be explored to have an idea about them.Hansen Wang
2015/02/25
In your last post, you wrote down qemu start bin code at address 0x10000, but in this post, it seems start at 0x00000000, why?
And I tried the codes above, however, it dump like following.
<>
qemu: fatal: Trying to execute code outside RAM or ROM at 0x20010000
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=20100308 R14=00000000 R15=20010000
PSR=40000173 -Z– T svc32
FPSCR: 00000000
Aborted (core dumped
<>
Any help is appreciate.
Balau
2015/02/25
QEMU emulates different machines differently, so generally on application processors such as ARM926 and Cortex-A it places the kernel at 0x10000, but in microcontrollers such as Cortex-M it places it at 0 where the exception vector table is.
A common advice that I give is here: trace the execution or attach to QEMU with GDB.
In your particular case 0x20010000 is the first address outside the memory. Maybe the code made a bad jump to 0x20010000 or below it, you can quickly discover it by tracing or debugging the execution with the methods above.
maria
2015/04/06
I want porting a poject from codewarrior 5.9 to sourcery code bench for power (GCC4.9) using MPC555x family , any help please
Balau
2015/04/07
I’m not familiar with CodeWarrior or the Sourcery Code Bench IDE, only with its GCC toolchain. I would suggest first gaining familiarity with the two products by creating a simple program in both, and running it on the target processor. The porting could be easy or difficult depending on many things: how complex is the project, how generic is the C that is written, how much assembly code is there, how different are the two toolchains, how different are the two IDEs. One way to do it is trying and retrying.
Ranjith
2015/04/14
Hi Balau,
This is ranjith. I want to use standard c library functions on my c file such as printf, malloc, realloc. I don’t know how to initialize c library on linker, compile c file with standard c libraries.(I am using qemu for emulation of versatilepb platform). Please help me.
Thanks,
Ranjith.
Balau
2015/04/19
Take a look at this post, and this site, hope this helps.
Arun Kuttiyara Varghese
2015/07/19
Hi ,
but in ubuntu 64bit 15.04, I am getting the below error.
$ arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -T lm3s6965.ld main.c reset.S syscalls.c -o main
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot find -lcs3
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot find -lcs3unhosted
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot find -lcs3micro
collect2: error: ld returned 1 exit status
Also,
$arm-none-eabi-gcc –version
arm-none-eabi-gcc (4.8.3-18ubuntu2+12) 4.8.3
Copyright (C) 2013 Free Software Foundation, Inc.
any idea why this issue is coming ?
Thanks,
Arun
Balau
2015/07/20
Yes, you have this issue because my post is about CodeSourcery toolchain, but you are using the toolchain packaged in Ubuntu, which is probably GCC ARM embedded, and they have different options.
Take a look at Flashing the STM32-P152 board with OpenOCD for an example on how to use GCC ARM embedded toolchain, and take a look also at my STM32 on Linux section in general.
Chandrika
2019/02/12
Hi Balau, I’m new to the whole emulation technology. But I’m trying to learn. I want to emulate cortex m4 ARM processor with STM32 controller hardware. Is it possible I can use the same package that is provided by QEMU, that is lm3s6965evb TI stellaris evaluation board. The confusion is , QEMU provides processor support or virtual platforms. Cortex m4 guest CPU should work with any hardware be it , TI or STM. Also I’m going bare metal so should the M profile for TI stellaris evaluation board be a problem for my specs or i can still use this guest platform by QEMU? Thanks. I wouldnt have understood anything if it wasn’t for your blogs 🙂 Thanks again!
Chandrika Joshi
2019/02/12
Hello Balau,
Your blogs are quite interesting and insightful. I’m trying to emulate a STM32 controller with cortex M4 processor on QEMU with bare metal base. I want to implement this using the Cortex M profile supported by QEMU. I understand from their site that they support 2 hardware boards from TI stellaris. But since the same cortext m4 processor is used in both STM32 and TI stellaris, does it create no problem that the QEMU supports TI stellaris in particular? Is my understanding correct or am i missing something? as per my understanding STM32 controller is being emulated so it should not bother that QEMU supports which board as all I care about is cortex M4 support package by QEMU.
Thank you.
Balau
2019/02/13
Hello Chandrika, I think if you only need to emulate M4 execution without peripherals you should be fine, but be aware that STM32 and stellaris have very different peripherals and memory map. So for example the serial port can be accessed on a different address with different registers. Nvic and systick instead should be fine since they are part of the m4 system
Chandrika Joshi
2019/02/13
Hello Balau,
Thank you for your reply. I want to implement the complete STM32 controller. So there is no work around? something like I can implement STM32 controller with qemu support? It is too tedious to use MATLAB and expensive.
Thank you