I am learning bare-metal programming, I tried to send data out through the UART of LM3S811 in Qemu. But it did not print any characters in the terminal. I have provided the .c file and the linker script. It would help if there are any links to learn bare-metal Programming from scratch. Thanks.
The .c file
char stack[1024];
extern char etext;
extern char sdata;
extern char edata;
extern char sbss;
extern char ebss;
static char *bssp = (char *)0xDEADBEEF;
unsigned int *uart = (unsigned int *)0x4000C000;
void start()
{
char *from, *to;
from = &etext;
to = &sdata;
while (to != &edata) {
*to++ = *from++;
}
bssp = &sbss;
while (bssp != &ebss) {
*bssp++ = 0;
}
main();
while(1);
}
__attribute__((section(".vectors")))
void *vectors[] = {
stack + sizeof(stack),
start,
};
int main()
{
char *ptr = "Have a great day";
while (*ptr != '\0') {
*uart = *ptr;
ptr++;
}
return 0;
}
The Linker Script:
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00010000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000
}
SECTIONS {
.text : {
*(.vectors);
*(.text);
etext = .;
} > FLASH
.data : {
sdata = .;
*(.data);
edata = .;
} > SRAM AT >FLASH
.bss : {
sbss = .;
*(.bss);
ebss = .;
} > SRAM
.rodata : {
*(.rodata);
} > FLASH
}
So starting with this, and then going to change it
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
notmain.c
void PUT32 ( unsigned int, unsigned int );
#define UART_DR 0x4000C000
int notmain ( void )
{
char *ptr = "Have a great day";
while (*ptr != '\0') {
PUT32(UART_DR,*ptr);
ptr++;
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
}
build
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy -O binary notmain.elf notmain.bin
examine
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000009 andeq r0, r0, r9
00000008 <reset>:
8: f000 f804 bl 14 <notmain>
c: e7ff b.n e <hang>
so vector table looks good
18: 4c04 ldr r4, [pc, #16] ; (2c <notmain+0x18>)
1a: 4805 ldr r0, [pc, #20] ; (30 <notmain+0x1c>)
1c: 3401 adds r4, #1
1e: f7ff fff7 bl 10 <PUT32>
22: 7821 ldrb r1, [r4, #0]
24: 2900 cmp r1, #0
...
2c: 00000034 andeq r0, r0, r4, lsr r0
...
Disassembly of section .rodata:
00000034 <.rodata>:
34: 65766148 ldrbvs r6, [r6, #-328]! ; 0xfffffeb8
38: 67206120 strvs r6, [r0, -r0, lsr #2]!
3c: 74616572 strbtvc r6, [r1], #-1394 ; 0xfffffa8e
40: 79616420 stmdbvc r1!, {r5, r10, sp, lr}^
...
the string appears to be in rodata, so that looks good too.
qemu-system-arm -M lm3s811evb -m 1M -nographic -kernel notmain.bin
Have a great day
ctrl-a then x to exit qemu
flash.s
.thumb
.global _start
_start:
.word 0x20001000
.word notmain
notmain.c
#define UART_DR (*((volatile unsigned int *)0x4000C000))
void notmain ( void )
{
char *ptr = "Have a great day";
while (*ptr != '\0') {
UART_DR=*ptr;
ptr++;
}
while(1) continue;
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
}
build
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy -O binary notmain.elf notmain.bin
examine
Disassembly of section .text:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000009 andeq r0, r0, r9
00000008 <notmain>:
vector table is good
00000008 <notmain>:
8: 2348 movs r3, #72 ; 0x48
a: 4a04 ldr r2, [pc, #16] ; (1c <notmain+0x14>)
c: 4904 ldr r1, [pc, #16] ; (20 <notmain+0x18>)
e: 600b str r3, [r1, #0]
10: f812 3f01 ldrb.w r3, [r2, #1]!
14: 2b00 cmp r3, #0
16: d1fa bne.n e <notmain+0x6>
18: e7fe b.n 18 <notmain+0x10>
1a: bf00 nop
1c: 00000024 andeq r0, r0, r4, lsr #32
20: 4000c000 andmi r12, r0, r0
Disassembly of section .rodata:
00000024 <.rodata>:
24: 65766148 ldrbvs r6, [r6, #-328]! ; 0xfffffeb8
28: 67206120 strvs r6, [r0, -r0, lsr #2]!
2c: 74616572 strbtvc r6, [r1], #-1394 ; 0xfffffa8e
30: 79616420 stmdbvc r1!, {r5, r10, sp, lr}^
...
That is an interesting solution but looks fine, string is in flash, etc.
qemu-system-arm -M lm3s811evb -m 1M -nographic -kernel notmain.bin
Have a great day
You will want to examine the output of your build.
If I build it
00000000 <vectors>:
0: 20000408 andcs r0, r0, r8, lsl #8
4: 00000009 andeq r0, r0, r9
00000008 <start>:
very strange sp init value but whatever, it should work yes?
Disassembly of section .text:
00000000 <vectors>:
0: 20000408 andcs r0, r0, r8, lsl #8
4: 00000009 andeq r0, r0, r9
00000008 <start>:
8: b508 push {r3, lr}
a: 480d ldr r0, [pc, #52] ; (40 <start+0x38>) 20000008
c: 4b0d ldr r3, [pc, #52] ; (44 <start+0x3c>) 20000000
e: 4283 cmp r3, r0
10: d006 beq.n 20 <start+0x18>
12: 4a0d ldr r2, [pc, #52] ; (48 <start+0x40>) 00000058
14: f812 1b01 ldrb.w r1, [r2], #1
18: f803 1b01 strb.w r1, [r3], #1
1c: 4298 cmp r0, r3
1e: d1f9 bne.n 14 <start+0xc>
copy 8 bytes from 0x0x0058 to 0x20000000
20: 4b0a ldr r3, [pc, #40] ; (4c <start+0x44>) 20000008
22: 4a0b ldr r2, [pc, #44] ; (50 <start+0x48>) 20000408
24: 480b ldr r0, [pc, #44] ; (54 <start+0x4c>) 20000000
26: 4293 cmp r3, r2
28: bf18 it ne
2a: 2100 movne r1, #0
2c: 6043 str r3, [r0, #4]
2e: d004 beq.n 3a <start+0x32>
30: f803 1b01 strb.w r1, [r3], #1
34: 4293 cmp r3, r2
36: d1fb bne.n 30 <start+0x28>
38: 6043 str r3, [r0, #4]
zero from 0x20000008 to 0x20000408 wiping out the stack, good thing the local variables are not on the stack and that you are not returning from this function.
3a: f000 f80d bl 58 <etext>
3e: e7fe b.n 3e <start+0x36>
40: 20000008 andcs r0, r0, r8
44: 20000000 andcs r0, r0, r0
48: 00000058 andeq r0, r0, r8, asr r0
4c: 20000008 andcs r0, r0, r8
50: 20000408 andcs r0, r0, r8, lsl #8
54: 20000000 andcs r0, r0, r0
Disassembly of section .data:
20000000 <uart>:
20000000: 4000c000 andmi r12, r0, r0
20000004 <bssp>:
20000004: deadbeef cdple 14, 10, cr11, cr13, cr15, {7}
Disassembly of section .bss:
20000008 <stack>:
...
hexdump -C notmain.bin
00000000 08 04 00 20 09 00 00 00 08 b5 0d 48 0d 4b 83 42 |... .......H.K.B|
00000010 06 d0 0d 4a 12 f8 01 1b 03 f8 01 1b 98 42 f9 d1 |...J.........B..|
00000020 0a 4b 0b 4a 0b 48 93 42 18 bf 00 21 43 60 04 d0 |.K.J.H.B...!C`..|
00000030 03 f8 01 1b 93 42 fb d1 43 60 00 f0 0d f8 fe e7 |.....B..C`......|
00000040 08 00 00 20 00 00 00 20 58 00 00 00 08 00 00 20 |... ... X...... |
00000050 08 04 00 20 00 00 00 20 61 20 05 4a 05 4b 11 68 |... ... a .J.K.h|
00000060 02 46 13 f8 01 0f 00 28 fa d1 0a 60 70 47 00 bf |.F.....(...`pG..|
00000070 00 00 00 20 81 00 00 00 00 c0 00 40 ef be ad de |... .......@....|
00000080 48 61 76 65 20 61 20 67 72 65 61 74 20 64 61 79 |Have a great day|
00000090 00 |.|
00000091
.data is certainly not at 0x58 in the flash though, so good thing you didn't use .data
By design maybe you hoped that .data would be right after .text but it is better to just ask the linker for exactly where things are. Also how things are aligned may also have made the etext not the start of the next section.
If we try this
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 0x1000
SRAM : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > FLASH
.rodata : { *(.rodata*) } > FLASH
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > SRAM AT > FLASH
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
.bss : {
__bss_start__ = .;
*(.bss*)
} > SRAM
__bss_end__ = .;
__bss_size__ = __bss_end__ - __bss_start__;
}
.thumb
.global _start
_start:
.word 0x20001000
.word main
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__
.word __bss_start__
.word __bss_end__
.word __bss_size__
remove the start function
Disassembly of section .text:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000025 andeq r0, r0, r5, lsr #32
8: 00000055 andeq r0, r0, r5, asr r0
c: 20000000 andcs r0, r0, r0
10: 20000004 andcs r0, r0, r4
14: 00000004 andeq r0, r0, r4
18: 20000004 andcs r0, r0, r4
1c: 20000404 andcs r0, r0, r4, lsl #8
20: 00000400 andeq r0, r0, r0, lsl #8
Disassembly of section .text:
00000000 <_start>:
0: 20001000
4: 00000025
8: 00000055 __data_rom_start__
c: 20000000 __data_start__
10: 20000004 __data_end__
14: 00000004 __data_size__
18: 20000004 __bss_start__
1c: 20000404 __bss_end__
20: 00000400 __bss_size__
Disassembly of section .data:
20000000 <uart>:
20000000: 4000c000 andmi r12, r0, r0
hexdump -C notmain.bin
00000000 00 10 00 20 25 00 00 00 55 00 00 00 00 00 00 20 |... %...U...... |
00000010 04 00 00 20 04 00 00 00 04 00 00 20 04 04 00 20 |... ....... ... |
00000020 00 04 00 00 61 20 05 4a 05 4b 11 68 02 46 13 f8 |....a .J.K.h.F..|
00000030 01 0f 00 28 fa d1 0a 60 70 47 00 bf 00 00 00 20 |...(...`pG..... |
00000040 45 00 00 00 48 61 76 65 20 61 20 67 72 65 61 74 |E...Have a great|
00000050 20 64 61 79 00 00 c0 00 40 | day....@|
00000059
so .data is at 0x55
Now:
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 0x1000
SRAM : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > FLASH
.rodata : { *(.rodata*) } > FLASH
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > SRAM AT > FLASH
__data_end__ = .;
}
.thumb
.global _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
ldr r0,=__data_rom_start__
ldr r1,=__data_start__
ldr r2,=__data_end__
data_loop:
ldrb r3,[r0]
strb r3,[r1]
add r0,r0,#1
add r1,r1,#1
cmp r1,r2
bne data_loop
bl main
b .
unsigned int *uart = (unsigned int *)0x4000C000;
int main()
{
char *ptr = "Have a great day";
while (*ptr != '\0') {
*uart = *ptr;
ptr++;
}
return 0;
}
Disassembly of section .text:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000009 andeq r0, r0, r9
00000008 <reset>:
8: 4805 ldr r0, [pc, #20] ; (20 <data_loop+0x12>)
a: 4906 ldr r1, [pc, #24] ; (24 <data_loop+0x16>)
c: 4a06 ldr r2, [pc, #24] ; (28 <data_loop+0x1a>)
0000000e <data_loop>:
e: 7803 ldrb r3, [r0, #0]
10: 700b strb r3, [r1, #0]
12: 3001 adds r0, #1
14: 3101 adds r1, #1
16: 4291 cmp r1, r2
18: d1f9 bne.n e <data_loop>
1a: f000 f807 bl 2c <main>
1e: e7fe b.n 1e <data_loop+0x10>
20: 0000005d andeq r0, r0, sp, asr r0
24: 20000000 andcs r0, r0, r0
28: 20000004 andcs r0, r0, r4
0000002c <main>:
2c: 2061 movs r0, #97 ; 0x61
2e: 4a05 ldr r2, [pc, #20] ; (44 <main+0x18>)
30: 4b05 ldr r3, [pc, #20] ; (48 <main+0x1c>)
32: 6811 ldr r1, [r2, #0]
34: 4602 mov r2, r0
36: f813 0f01 ldrb.w r0, [r3, #1]!
3a: 2800 cmp r0, #0
3c: d1fa bne.n 34 <main+0x8>
3e: 600a str r2, [r1, #0]
40: 4770 bx lr
42: bf00 nop
44: 20000000 andcs r0, r0, r0
48: 0000004d andeq r0, r0, sp, asr #32
Disassembly of section .rodata:
0000004c <__data_rom_start__-0x11>:
4c: 65766148 ldrbvs r6, [r6, #-328]! ; 0xfffffeb8
50: 67206120 strvs r6, [r0, -r0, lsr #2]!
54: 74616572 strbtvc r6, [r1], #-1394 ; 0xfffffa8e
58: 79616420 stmdbvc r1!, {r5, r10, sp, lr}^
...
Disassembly of section .data:
20000000 <uart>:
20000000: 4000c000 andmi r12, r0, r0
hexdump -C notmain.bin
00000000 00 10 00 20 09 00 00 00 05 48 06 49 06 4a 03 78 |... .....H.I.J.x|
00000010 0b 70 01 30 01 31 91 42 f9 d1 00 f0 07 f8 fe e7 |.p.0.1.B........|
00000020 5d 00 00 00 00 00 00 20 04 00 00 20 61 20 05 4a |]...... ... a .J|
00000030 05 4b 11 68 02 46 13 f8 01 0f 00 28 fa d1 0a 60 |.K.h.F.....(...`|
00000040 70 47 00 bf 00 00 00 20 4d 00 00 00 48 61 76 65 |pG..... M...Have|
00000050 20 61 20 67 72 65 61 74 20 64 61 79 00 00 c0 00 | a great day....|
00000060 40 |@|
00000061
qemu-system-arm -M lm3s811evb -m 1M -nographic -kernel notmain.bin
y
Because uart is not volatile then if you look at the compiled output it only writes the last item y to the register, the rest of the loop is looking for the end of the string. But at least now the .data global variable has been initialized with the right value so it works.
change to this
volatile unsigned int *uart = (unsigned int *)0x4000C000;
int main()
{
char *ptr = "Have a great day";
while (*ptr != '\0') {
*uart = *ptr;
ptr++;
}
return 0;
}
qemu-system-arm -M lm3s811evb -m 1M -nographic -kernel notmain.bin
Have a great day
I intentionally don't use/support .data nor .bss in part because of the non-portable linker scripts and bootstraps, etc. So burn a little extra flash at times to avoid it. Making the bootstrap and linker script simple. But I would spend more time on the linker script so that the bootstrap looks more like this
ldr r0,=__data_rom_start__
ldr r1,=__data_start__
ldr r2,=__data_end__
data_loop:
ldm r0!,{r4,r5}
stm r1!,{r4,r5}
cmp r1,r2
bne data_loop
by aligning the .data segment on both ends to a 64 bit aligned. Would also do that for .bss as needed, but if I were to assume .bss is zero, then I would be more likely to zero all of sram not just bss, and that would be the first thing I do before copying .data over.
ldr r0,=0x20000000
ldr r1,=0x20001000 <-- adjust for sram available/used in this part/app
mov r4,#0
mov r5,#0
mov r6,#0
mov r7,#0
sram_init:
stm r0!,{r4,r5,r6,r7}
cmp r1,r2
bne sram_init
I don't have a used for an initialized stack nor bss so as shown my bootstrap looks like this
reset:
bl notmain
b .
since I do want to return from the C entry point (various reasons). Sometimes this:
reset:
bl notmain
wfi_loop:
wfi
b wfi_loop
wfi, wfe, whatever. Some of these cores implement the wait fors as nops as they don't support them.
So...
I didn't proofread this much, so hopefully all of my cutting and pasting worked. Whether I pasted it right it did run correctly on my machine, and the binary looked functional.
User contributions licensed under CC BY-SA 3.0