Bare metal

From Openmoko

(Difference between revisions)
Jump to: navigation, search
(C code (app.c))
m (Category: -Software (non-openmoko), +System Developers)
 
(2 intermediate revisions by 2 users not shown)
Line 52: Line 52:
 
=== Makefile ===
 
=== Makefile ===
  
Change the CROSS variable to point at the location of your cross-compiler.  The example assumes a build environment was setup with the [[MokoMakefile]] in <tt>/mnt/oe/openmoko</tt>.  We first compile, asssemble and link our code into an ELF binary.  It seems like u-boot has some support for using ELF files directly, but we'll instead extract the raw machine code from the ELF using objcopy and load it manually.
+
Change the CROSS variable to point at the location of your cross-compiler.  The example assumes a build environment was setup with the [[MokoMakefile]] in <tt>/mnt/oe/openmoko</tt>.  We first compile, assemble and link our code into an ELF binary.  It seems like u-boot has some support for using ELF files directly, but we'll instead extract the raw machine code from the ELF using objcopy and load it manually.
  
 
<pre>
 
<pre>
Line 170: Line 170:
 
</pre>
 
</pre>
  
[[Category:Technical]]
+
[[Category:System Developers]]
[[Category:Software (non openmoko)]]
+
[[Category:Documentation]]
+

Latest revision as of 20:59, 14 September 2008

Here is an example of how to run your own C code on the bare metal, without any operating system. Since I don't yet have a physical device, this has only been tested using Qemu.

Contents

[edit] Example program

The example program does nothing but blink the LCD backlight on and off. A simple first exercise might be to change it to blink out "hello world" in morse code.

Download: bare-metal-moko.tar.gz (4Kb)

[edit] C code (app.c)

The GPIO Port B data register (GPBDAT) of the S3C2410 is located at physical address 0x56000014. The LCD backlight is controlled by GPB0, so we enter an infinite loop toggling bit 0 of GPBDAT on and off. The for loop is to just sleep for a while so that it flashes on and off slow enough that a human can see it.

Note that without extra work you won't be able to use global variables or string constants. You might be able to fix this by experimenting with U-boot's ELF support and linker scripts. Of course you will also not be able to use any system calls or the C library as you are running without an OS.

#define GPBDAT *((volatile unsigned long*)0x56000014)

void cstart(void)
{
	int i;
	while (1) {
		GPBDAT ^= 1; /* toggle the backlight */
		for (i = 0; i < 50000000; i++); /* delay a little bit */
	}
}

[edit] Assembler head (app.S)

This is a tiny piece of ARM assembler that will be the first instructions in our program. The bootloader will execute this after loading our program into memory. We simply set the stack pointer register to somewhere in RAM and jump to our C code.

.text
.globl _start
_start:
	mov	sp, #0x30000000    @ start of RAM
	add	sp, sp, #0xa00000  @ 10mb in, should be a safe place to put our stack
	b	cstart             @ call C code

[edit] Linker script (app.lds)

You can setup specific instructions for the linker here to tell it how things should be layed out in memory. You might be able to get global variables working by fiddling with this, for example. For now we just say our program will be running at 0x35000000.

SECTIONS
{
  . = 0x35000000;
}

[edit] Makefile

Change the CROSS variable to point at the location of your cross-compiler. The example assumes a build environment was setup with the MokoMakefile in /mnt/oe/openmoko. We first compile, assemble and link our code into an ELF binary. It seems like u-boot has some support for using ELF files directly, but we'll instead extract the raw machine code from the ELF using objcopy and load it manually.

CROSS=/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/
CC=${CROSS}gcc
OBJCOPY=${CROSS}objcopy

app.bin: app.elf
	${OBJCOPY} -O binary $^ $@ 

app.elf: app.S app.c
	${CC} -Wall -nostdlib $^ -o $@ -T app.lds

[edit] Kermit script (download.kermit)

We use C-Kermit to download our binary code into the phone's memory. We do this by issuing loadb 0x350000000 to u-boot to tell it to receive a file into address 0x35000000. We then send the file using the kermit protocol. Finally we tell u-boot to jump to address 0x35000000 and start executing our program.

set carrier-watch off
set handshake none
set flow-control none
robust
set file type bin
set file name lit
set rec pack 1000
set send pack 1000
set window 5

# This is for qemu, with serial on a TCP socket
set host localhost 1200 /raw-socket

# Try something like this for USB/serial on a real physical device.
#set line /dev/ttyUSB0
#set speed 115200

# This will upload app.bin to address 0x35000000 and execute it.

connect /trigger:# /quietly
< echo loadb 35000000
connect /trigger:{Ready for binary} /quietly
send app.bin
connect /trigger:# /quietly
< echo go 35000000
echo
connect /quietly
quit 0


[edit] Using with qemu

Terminal 1:

~/tmp % wget http://meshy.org/~ato/upload/bare-metal-moko.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3322  100  3322    0     0   4156      0 --:--:-- --:--:-- --:--:--  8384
~/tmp % tar -zxf bare-metal-moko.tar.gz 
~/tmp % cd bare-metal-moko 
~/tmp/bare-metal-moko % make
/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/gcc -Wall -nostdlib app.S app.c -o app.elf -T app.lds
/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/objcopy -O binary app.elf app.bin 

Terminal 2:

/mnt/oe/openmoko/build/qemu % cp ~/tmp/bare-metal-moko/qemu-telnet.sh openmoko
/mnt/oe/openmoko/build/qemu % ./openmoko/qemu-telnet.sh 
QEMU waiting for connection on: localhost:1200,server

Terminal 1:

~/tmp/bare-metal-moko % kermit -y download.kerm
 DNS Lookup...  Trying 127.0.0.1...  Reverse DNS Lookup... (OK)
 localhost.localdomain connected on port 1200
ÿûÿûÿûÿý
        
U-Boot 1.3.2-rc2 (May 22 2008 - 01:06:44)
                                         
I2C:   ready
DRAM:  128 MB
NAND:  64 MiB
Found Environment offset in OOB..
Video: 640x480x8 31kHz 59Hz      
                           
NAND read: device 0 offset 0x244000, size 0x5000
                                                
Reading data from 0x248e00 -- 100% complete.
 20480 bytes read: OK                       
USB:   S3C2410 USB Deviced
In:    serial             
Out:   serial
Err:   serial
GTA01Bv4 # loadb 35000000
## Ready for binary (kermit) download to 0x35000000 at 115200 bps...
#                                                

# Total Size      = 0x00000064 = 100 Bytes
## Start Addr      = 0x35000000           
GTA01Bv4 # go 35000000         
## Starting application at 0x35000000 ...

Terminal 2:

...
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
...
Personal tools

Here is an example of how to run your own C code on the bare metal, without any operating system. Since I don't yet have a physical device, this has only been tested using Qemu.

Example program

The example program does nothing but blink the LCD backlight on and off. A simple first exercise might be to change it to blink out "hello world" in morse code.

Download: bare-metal-moko.tar.gz (4Kb)

C code (app.c)

The GPIO Port B data register (GPBDAT) of the S3C2410 is located at physical address 0x56000014. The LCD backlight is controlled by GPB0, so we enter an infinite loop toggling bit 0 of GPBDAT on and off. The for loop is to just sleep for a while so that it flashes on and off slow enough that a human can see it.

Note that without extra work you won't be able to use global variables or string constants. You might be able to fix this by experimenting with U-boot's ELF support and linker scripts. Of course you will also not be able to use any system calls or the C library as you are running without an OS.

#define GPBDAT *((volatile unsigned long*)0x56000014)

void cstart(void)
{
	int i;
	while (1) {
		GPBDAT ^= 1; /* toggle the backlight */
		for (i = 0; i < 50000000; i++); /* delay a little bit */
	}
}

Assembler head (app.S)

This is a tiny piece of ARM assembler that will be the first instructions in our program. The bootloader will execute this after loading our program into memory. We simply set the stack pointer register to somewhere in RAM and jump to our C code.

.text
.globl _start
_start:
	mov	sp, #0x30000000    @ start of RAM
	add	sp, sp, #0xa00000  @ 10mb in, should be a safe place to put our stack
	b	cstart             @ call C code

Linker script (app.lds)

You can setup specific instructions for the linker here to tell it how things should be layed out in memory. You might be able to get global variables working by fiddling with this, for example. For now we just say our program will be running at 0x35000000.

SECTIONS
{
  . = 0x35000000;
}

Makefile

Change the CROSS variable to point at the location of your cross-compiler. The example assumes a build environment was setup with the MokoMakefile in /mnt/oe/openmoko. We first compile, asssemble and link our code into an ELF binary. It seems like u-boot has some support for using ELF files directly, but we'll instead extract the raw machine code from the ELF using objcopy and load it manually.

CROSS=/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/
CC=${CROSS}gcc
OBJCOPY=${CROSS}objcopy

app.bin: app.elf
	${OBJCOPY} -O binary $^ $@ 

app.elf: app.S app.c
	${CC} -Wall -nostdlib $^ -o $@ -T app.lds

Kermit script (download.kermit)

We use C-Kermit to download our binary code into the phone's memory. We do this by issuing loadb 0x350000000 to u-boot to tell it to receive a file into address 0x35000000. We then send the file using the kermit protocol. Finally we tell u-boot to jump to address 0x35000000 and start executing our program.

set carrier-watch off
set handshake none
set flow-control none
robust
set file type bin
set file name lit
set rec pack 1000
set send pack 1000
set window 5

# This is for qemu, with serial on a TCP socket
set host localhost 1200 /raw-socket

# Try something like this for USB/serial on a real physical device.
#set line /dev/ttyUSB0
#set speed 115200

# This will upload app.bin to address 0x35000000 and execute it.

connect /trigger:# /quietly
< echo loadb 35000000
connect /trigger:{Ready for binary} /quietly
send app.bin
connect /trigger:# /quietly
< echo go 35000000
echo
connect /quietly
quit 0


Using with qemu

Terminal 1:

~/tmp % wget http://meshy.org/~ato/upload/bare-metal-moko.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3322  100  3322    0     0   4156      0 --:--:-- --:--:-- --:--:--  8384
~/tmp % tar -zxf bare-metal-moko.tar.gz 
~/tmp % cd bare-metal-moko 
~/tmp/bare-metal-moko % make
/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/gcc -Wall -nostdlib app.S app.c -o app.elf -T app.lds
/mnt/oe/openmoko/build/tmp/cross/arm-angstrom-linux-gnueabi/bin/objcopy -O binary app.elf app.bin 

Terminal 2:

/mnt/oe/openmoko/build/qemu % cp ~/tmp/bare-metal-moko/qemu-telnet.sh openmoko
/mnt/oe/openmoko/build/qemu % ./openmoko/qemu-telnet.sh 
QEMU waiting for connection on: localhost:1200,server

Terminal 1:

~/tmp/bare-metal-moko % kermit -y download.kerm
 DNS Lookup...  Trying 127.0.0.1...  Reverse DNS Lookup... (OK)
 localhost.localdomain connected on port 1200
ÿûÿûÿûÿý
        
U-Boot 1.3.2-rc2 (May 22 2008 - 01:06:44)
                                         
I2C:   ready
DRAM:  128 MB
NAND:  64 MiB
Found Environment offset in OOB..
Video: 640x480x8 31kHz 59Hz      
                           
NAND read: device 0 offset 0x244000, size 0x5000
                                                
Reading data from 0x248e00 -- 100% complete.
 20480 bytes read: OK                       
USB:   S3C2410 USB Deviced
In:    serial             
Out:   serial
Err:   serial
GTA01Bv4 # loadb 35000000
## Ready for binary (kermit) download to 0x35000000 at 115200 bps...
#                                                

# Total Size      = 0x00000064 = 100 Bytes
## Start Addr      = 0x35000000           
GTA01Bv4 # go 35000000         
## Starting application at 0x35000000 ...

Terminal 2:

...
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
neo_bl_switch: LCD Backlight now off.
neo_bl_switch: LCD Backlight now on.
...