10

I'm trying to get a 'hello world' type program running on my Beagleboard-xm rev. C, by calling a C puts function from assembly.

So far I've been using this as a reference: http://wiki.osdev.org/ARM_Beagleboard

Here's what I have so far, but there's no output.

hello.c

volatile unsigned int * const UART3DR = (unsigned int *)0x49020000;

void puts(const char *s) {
  while(*s != '\0') { 
    *UART3DR = (unsigned int)(*s); 
    s++; 
  }
}

void hello() {
  puts("Hello, Beagleboard!\n");
}

boot.asm

.global start
start:
   ldr sp, =stack_bottom
   bl hello
   b .

linker.ld

ENTRY(start)

MEMORY
{
    ram : ORIGIN = 0x80200000, LENGTH = 0x10000
}

SECTIONS
{
    .hello : { hello.o(.text) } > ram
    .text : { *(.text) } > ram
    .data : { *(.data) } > ram
    .bss : { *(.bss) } > ram
     . = . + 0x5000; /* 4kB of stack memory */
    stack_bottom = .;

}

Makefile

ARMGNU = arm-linux-gnueabi

AOPS = --warn --fatal-warnings
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding

boot.bin: boot.asm
   $(ARMGNU)-as boot.asm -o boot.o
   $(ARMGNU)-gcc-4.6 -c $(COPS) hello.c -o hello.o
   $(ARMGNU)-ld -T linker.ld hello.o boot.o -o boot.elf
   $(ARMGNU)-objdump -D boot.elf > boot.list
   $(ARMGNU)-objcopy boot.elf -O srec boot.srec
   $(ARMGNU)-objcopy boot.elf -O binary boot.bin

Using just the asm file like this works.

.equ UART3.BASE,        0x49020000
start:
   ldr r0,=UART3.BASE
   mov r1,#'c'

Here are some Beagleboard/minicom related info: http://paste.ubuntu.com/829072/

Any pointers? :)


I also tried

void hello() {
  *UART3DR = 'c';
}

I'm using minicom and send the file via ymodem, then I try to run it with:

go 0x80200000

Hardware and software control flow in minicom are off.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
farnsworth
  • 349
  • 3
  • 13

3 Answers3

3

that should have worked for you. Here is some code I dug up from way back when, did not try it on a beagleboard tonight just made sure it compiled, it had worked at one time...

startup.s:

    .code 32

.globl _start
_start:

    bl main
hang: b hang

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

hello.c :

extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
void uart_send ( unsigned char x )
{
    while((GET32(0x49020014)&0x20)==0x00) continue;
    PUT32(0x49020000,x);
}
void hexstring ( unsigned int d )
{
    //unsigned int ra;
    unsigned int rb;
    unsigned int rc;

    rb=32;
    while(1)
    {
        rb-=4;
        rc=(d>>rb)&0xF;
        if(rc>9) rc+=0x37; else rc+=0x30;
        uart_send(rc);
        if(rb==0) break;
    }
    uart_send(0x0D);
    uart_send(0x0A);
}
int main ( void )
{
    hexstring(0x12345678);
    return(0);
}

memmap (linker script):

MEMORY
{
    ram : ORIGIN = 0x82000000, LENGTH = 256K
}

SECTIONS
{
    ROM : { startup.o } > ram
}

Makefile :

CROSS_COMPILE = arm-none-eabi

AOPS = --warn --fatal-warnings 
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding 

all : hello.bin

hello.bin : startup.o hello.o memmap
    $(CROSS_COMPILE)-ld startup.o hello.o -T memmap -o hello.elf  
    $(CROSS_COMPILE)-objdump -D hello.elf > hello.list
    $(CROSS_COMPILE)-objcopy hello.elf -O binary hello.bin

startup.o : startup.s
    $(CROSS_COMPILE)-as $(AOPS) startup.s -o startup.o

hello.o : hello.c 
    $(CROSS_COMPILE)-gcc -c $(COPS) hello.c -o hello.o

clean :
    rm -f *.o
    rm -f *.elf
    rm -f *.bin
    rm -f *.list

Looks like I just left the stack pointer wherever the bootloader had it. Likewise, as you, assumed the bootloader had initialized the serial port.

I assume you have serial port access working, you see uboot and you are able to type commands in order to download this program (xmodem, or whatever) into the boards ram? If you cant do that then it may be you are not connected to the serial port right. the beagleboards serial port is screwy, might need to make your own cable.

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • It works, thanks. Now I need to figure out what I'm doing wrong. It's the first piece of code != ASM that actually prints something. – farnsworth Feb 05 '12 at 11:26
  • For further reference if someone is having trouble with this. I modified the Makefile CROSS_COMPILE = arm-none-linux-gnueabi as I'm using the codesourcery toolchain for linux. Loaded the file with loady via ymodem, and while de memmap has it's origin at 0x82000000 I went agead and ran it with go 0x80200000 even though you set the origin at 0x82000000. – farnsworth Feb 05 '12 at 11:28
  • both arm-none-linux-gnueabi and arm-none-eabi are from codesourcery, if you dont make any system calls then either will work. Supposedly the non-linux version is better if you use gcclib calls (use divide or modulo or things like that). – old_timer Feb 05 '12 at 11:52
  • not to imply arm-none-linux-gnueabi, can only be from code sourcery (now mentor graphics). you can certainly build either of the mentioned targets from gnu sources yourself and the above code will work. – old_timer Feb 05 '12 at 11:54
  • interesting I have the wrong address? what if you change the linker script to use the same address as the one you are actually using? My guess is the code is accidentally position independent, being such a small simple program that may very well be the case. – old_timer Feb 05 '12 at 11:56
  • works either way. Now the weird thing is that I can print individual characters with with uart_send('c') for example, but cannot print strings print_string(char *str){ while (*str != '\0') uart_send (*str++); } print_string("Test"); . Any thoughts on this? – farnsworth Feb 06 '12 at 05:22
  • disassemble and check to see where it put the string, .data or .text. If .data then you need to use a linker script that puts it in .text using a .data address and then boot code that copies it from .text to .data. or declare the string const to move it to .text and not mess with .data. – old_timer Feb 06 '12 at 06:11
  • I've moved rodata to text and used const char *s="Test"; print_string(s); Same result, nothing. – farnsworth Feb 06 '12 at 14:24
  • unless you force it in in the linker script you have to put the files on the command line in the desired order. by putting hello.o first you set that at 0x8020000 you should have boot.o then hello.o. Look at the disassembly. – old_timer Feb 06 '12 at 14:30
  • when I built it I got start at 0x80200050, if you examine you list file you can try starting the program at 0x80200xxx instead of 0x80200000 – old_timer Feb 06 '12 at 14:35
  • you also need to poll the transmitter full/empty status, you cant just jam bytes in to the transmitter register (in general) you might get away with it if there is a fifo, but dont rely on that. – old_timer Feb 06 '12 at 14:37
  • I messed up the starting address, sorry for the trouble, and **thank you** for your time. – farnsworth Feb 06 '12 at 15:05
  • 2
    Not a problem, bare metal, low level stuff, is a lost art and I am interested in keeping it alive. thank you for not giving up on this, and I hope you continue to understand and learn things at this level. I am learning every day myself... – old_timer Feb 06 '12 at 15:13
2

You can't just blindly write a string of characters to a UART - you need to check status on each character - it works in the single character example because the UART is always going to be ready for the first character, but for the second and subsequent characters you need to poll (or better yet use an ISR, but let's walk before we run).

There's some good example code here: http://hardwarefreak.wordpress.com/2011/08/30/some-experience-with-the-beagleboard-xm-part-2/

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • Check out the LE. I also tried printing only one character and I didn't get any output, nice article will check if it solves the issue, though I would really like to know what I do wrong and where. – farnsworth Feb 04 '12 at 18:23
  • Maybe try running the code form the link I gave above and see if that works ? If it does work then you can compare with your code and see what's different ? – Paul R Feb 04 '12 at 18:25
  • Make sure you turn off hardware flow control – Paul R Feb 04 '12 at 19:15
  • it's off I said it in the later edit, last line. I've rerun the assembly example and it works, when calling C it doesn't. – farnsworth Feb 04 '12 at 19:16
  • Is there any way you can tell whether your C code is actually *running*, e.g. turn an LED on or off or something like that ? – Paul R Feb 04 '12 at 19:22
2

I've not enough repetation to comment.. But my answere to

Works either way. Now the weird thing is that I can print individual characters with with uart_send('c') for example, but cannot print strings print_string(char *str){ while (*str != '\0') uart_send (*str++); } print_string("Test"); . Any thoughts on this?

is:

You write faster in the output buffer, as UART is able to send.. So you've to check, if the output buffer is empty, before you send a new character.

I've done this in the code on my blog (http://hardwarefreak.wordpress.com/2011/08/30/some-experience-with-the-beagleboard-xm-part-2/)

Philipp
  • 51
  • 4