How to send data through UART of LM3S811

1

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
}
assembly
arm
microcontroller
microprocessors
asked on Stack Overflow Jul 16, 2020 by Paulson Raja L

1 Answer

2

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...

  1. your .data init appears broken. 2) it wasn't defined as volatile 3) you do not need to have a read/write (.data) global variable to point at a register, you can save on ram and code by not making it a .data global variable and instead just #define it.

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.

answered on Stack Overflow Jul 16, 2020 by old_timer • edited Jul 24, 2020 by halfer

User contributions licensed under CC BY-SA 3.0