Beginning with Bare Metal


53 posts   Page 1 of 3   1, 2, 3
by emcsquirreled » Thu Aug 23, 2012 4:42 am
Today, I began trying to set up my first piece of bare metal code. I followed the "getting started" links from this forum, http://www.raspberrypi.org/phpBB3/viewtopic.php?f=72&t=10850,and read their contents. I also looked up the more active forum members on Github, and read through the code posted there, (especially dwelch67's bootloaders). Unfortunately, I was unable to find anything I could understand enough to get started with. I have a moderate background in C and C++, but bare metal code is run at a much lower level than I have ever been before.

What do I need to do in order to compile, download, and run bare metal code on my Raspberry Pi? If anyone has a good tutorial or article, that would be great. Code would be helpful, too, but I do not want to just copy/paste something and fail to understand what it is doing, and how to rewrite or modify it.

I am running a Model B Raspberry Pi with a 2GB SD card, if that makes a difference.

Thanks in advance for any advice, pointers, or code snippets.
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by hermanhermitage » Thu Aug 23, 2012 6:40 am
emcsquirreled wrote:What do I need to do in order to compile, download, and run bare metal code on my Raspberry Pi? If anyone has a good tutorial or article, that would be great. Code would be helpful, too, but I do not want to just copy/paste something and fail to understand what it is doing, and how to rewrite or modify it.

I am running a Model B Raspberry Pi with a 2GB SD card, if that makes a difference.

Thanks in advance for any advice, pointers, or code snippets.

I'd recommend the following steps:

1. Its easiest if you have a second Pi or desktop/laptop computer running your preferred OS. The reason for this is that until you have enough code running the Pi serial port is the easiest input output mechanism to view what your bare metal program is doing - and you'll need a second computer to connect to it.

2. Get yourself a working 115200 baud serial connection between your Pi and another computer (or Pi). Follow dwelch's instructions for this. I list a few ways here: https://github.com/hermanhermitage/videocoreiv/wiki/Getting-Started. I'd say http://www.ftdichip.com/Products/Cables/RPi.htm looks pretty good.

3. If you dont have the serial convertor handy, then order it and whilst you wait grab Dex's basic and play with that. Don't be put off by the "basic" or the asm part, follow his tutorials and just have some fun familiarizing yourself with a baremetal environment. You will get to the C/C++ part later.

4. Once you have a working serial connection, when you boot up your RPi with Linux you should see output appear on a terminal program on your other machine. This means your serial connection is good to go.

5. To compile dwelch's samples you'll need a compiler. If you have a linux like raspbian already installed on your Pi then you have one good to go. If you have a non ARM computer as your dev machine, then you'll need a version of tools that compiles to ARM (eg on windows or linux try grabbing https://launchpad.net/gcc-arm-embedded/ ... -q2-update).

Now, I'm going to be lazy here and assume you are using a second pi (or your only pi for building the baremetal samples).

6. Clone dwelch's repo: Using a second pi, I do open a terminal (or ssh in) and do the following:
Code: Select all
pi@raspberrypi:~$ mkdir dwelch
pi@raspberrypi:~$ cd dwelch
pi@raspberrypi:~/dwelch$ git clone https://github.com/dwelch67/raspberrypi.git
Cloning into raspberrypi...
remote: Counting objects: 586, done.
remote: Compressing objects: 100% (295/295), done.
remote: Total 586 (delta 310), reused 564 (delta 288)
Receiving objects: 100% (586/586), 815.69 KiB | 327 KiB/s, done.
Resolving deltas: 100% (310/310), done.

7. Build the uart01 sample:
Code: Select all
pi@raspberrypi:~$ cd raspberrypi/uart01
pi@raspberrypi:~/dwelch/raspberrypi/uart01$ export ARMGNU=
pi@raspberrypi:~/dwelch/raspberrypi/uart01$ make clean
rm -f *.o
rm -f *.bin
rm -f *.hex
rm -f *.elf
rm -f *.list
rm -f *.img
rm -f *.bc
rm -f *.clang.opt.s
pi@raspberrypi:~/dwelch/raspberrypi/uart01$ make
as vectors.s -o vectors.o
gcc -Wall  -O2 -nostdlib -nostartfiles -ffreestanding -c uart01.c -o uart01.o
ld vectors.o uart01.o -T memmap -o uart01.elf
objdump -D uart01.elf > uart01.list
objcopy uart01.elf -O ihex uart01.hex
objcopy uart01.elf -O binary uart01.bin

8. Copy the uart01.bin into place on the SD card, quoting from dwelch:
"From there you take .bin files from my examples and place them on the sd card
with the name kernel.img."
9. Boot up a RPi with the SD card installed, the sample should run.
Posts: 65
Joined: Sat Jul 07, 2012 11:21 pm
Location: Zero Page
by kemra102 » Wed Aug 29, 2012 7:20 pm
Our LUG has been looking at Raspberry Pi and some Bare Metal development in particular (G7TNZ is one of my fellow members and does most of the bare metal work here).

If you go here on our website we have instructions for setting up a cross compilation environment in Ubuntu 12.04 (will probably work in other versions or in other Linux's even) that lets you compile to bare metal or even Raspbian. There's also a link to a script that does all the set up for you.

Hope that goes some way to helping.
Posts: 1
Joined: Wed Aug 29, 2012 7:14 pm
by g7tnz » Wed Aug 29, 2012 7:34 pm
You may not have seen https://github.com/G7TNZ/RPi-kernel - It may give you some ideas. Contained in the readme is a link to where we write up how to set up a development environment along with a script to set up the environment for you.
Posts: 9
Joined: Thu Dec 01, 2011 5:33 pm
by emcsquirreled » Fri Aug 31, 2012 2:22 am
Thanks everyone for the input. I have been busy and have not done any of this yet, but I phrased the original question improperly. The instructions were clear, but I don't understand exactly what everything does. I was more wondering exactly what was happening, instead of how to make it go. Thanks again.
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by dwelch67 » Fri Aug 31, 2012 4:08 am
Please elaborate on what it is you dont understand. You dont understand how hardware works and how to program it? You dont understand how to write programs with no operating system? You dont understand how to run the programs? Even if "everything" is your answer, let us start with "something", specify one thing you want to start understanding and we can go from there. Or maybe specify the one thing you do understand and we wont go there...

David
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by emcsquirreled » Mon Sep 03, 2012 3:30 pm
First off, sorry for being so slow in responding. My Pi has fallen to a lower priority level than I would like recently, but I am now ready to get my project up and running again.

dwelch67 wrote:Please elaborate on what it is you dont understand. You dont understand how hardware works and how to program it? You dont understand how to write programs with no operating system? You dont understand how to run the programs? Even if "everything" is your answer, let us start with "something", specify one thing you want to start understanding and we can go from there. Or maybe specify the one thing you do understand and we wont go there...


I want to know what happens during the boot, and what the various files load / initialize. I read through the code in your repo, but I do not (yet ;)) know ASM, and the comments where all referring to lower-level activities than I know about. I do not mind using someone else's ASM code, but I would like to know a little more about it than "this loads everything and starts your main() C function".
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by hermanhermitage » Mon Sep 03, 2012 9:56 pm
emcsquirreled wrote:I want to know what happens during the boot, and what the various files load / initialize.

Much of the initial system setup is done by the VideoCore processor before releasing the ARM from reset. This includes:
- Begin executing from internal bootrom.
- Determine secure code execution level (plain, signed or signed+encrypted code).
- Load bootcode.bin from a FAT filesystem on the SD card into cache memory. Jump (non returning function call) into entry point of bootcode.bin.
- Configure memory & controller (SDRAM).
- Load loader.bin from SD card into memory. Jump (non returning function call) into entry point of loader.bin
- Load start.elf from SD into the higher part of memory.
- Launch ThreadX multitasking kernel and VideoCore services.
...
Now comes the ARM part, the last bit of VideoCore before kicking the ARM off is:
- Load kernel.img from SD into the lower part of memory (depends on various flags but generally 0x8000).
- Initialize 0x0000 to 0x8000 with vectors, arguments for kernel etc.
- Release ARM from reset. Reset vector transfers control to 0x8000.
- ARM is now executing code at 0x8000

Of course you can probably ignore all the proceeding parts unless you are going deep - most of what people are calling baremetal is more a quasi-baremetal - just focusing on the ARM.

You can just focus on ARM execution from 0x8000.

In Dave's code you will see he has a file vectors.s that sets up the stack pointer (where C overflows parameters and stores local variables) and jumps into notmain(). The reason this is in assembler is simply that (1) the stack pointer -may- need to be initialized before calling a C function (depending on how the compiler handles C functions), and (2) there is no portable way of setting the stack pointer in C.

The stack grows downward, below the program code at 0x8000. Think of items being saved to the stack with *--sp = data, and restored with data = *sp++; Think of blocks of stack space allocated with: sp -= BLOCK_SIZE; block = sp; and freed with sp += BLOCK_SIZE;. Any function using a reasonable number of local variables be generally allocate a scratch block on entry, and free the block on exit. This and register allocation are the main things hidden from the C programmer by the C compiler.

Code: Select all
0x8000:
_start:
    mov sp,#0x8000   ; this sets the SP to 0x8000.  C will push (&pull) parameters and local variables onto the stack
    bl notmain            ; start the first C function (bl = branch & link = goto this function but keep a note of where to return to in the link register)

hang:                         ; for(;;);
    b hang                   ; (b = branch)


The assembler code has done its job of transferring into the C functions. Now this is a minimal environment not a "hosted" version of C. So no stdio, no malloc/free, no C library at all (unless you link in a version and implement the non portable parts). Generally in this baremetal environment all IO will be done by memory mapped access to hardware devices, or in the case of the RaspberryPi - sending messages to the VideoCore host processor via a mailbox. The VideoCore takes responsibility and is master of many devices (multimedia, video in/out, 3d, etc), but the simpler ("open") ones like GPIO, SD, USB, ARM MMU... are left to the ARM to manage.

For any serious baremetal work you'll end up implementing C lib functionality (memory allocation, IO) and device drivers to attach it to. A common starting point for C runtime is a library called newlib (http://sourceware.org/newlib/). At some point after you have written enough device libraries etc, and you add task switching you end up with a mini OS.

In Dave's code below you see he is doing direct memory mapped access to the GPIO registers. The documentation for some of the peripherals is in a manual - but its heavily redacted and not really of production quality - (ie expect to burn hours here and there with the odd erroneous bit definitions). See: http://elinux.org/RPi_Low-level_peripherals and http://www.raspberrypi.org/wp-content/u ... herals.pdf

Code: Select all
int notmain ( void )
{
    unsigned int ra;

    ra=GET32(GPFSEL1);
    ra&=~(7<<18);
    ra|=1<<18;
    PUT32(GPFSEL1,ra);
    ...


A good introduction if you go the ARM route is at http://www.cl.cam.ac.uk/freshers/raspbe ... orials/os/.
Posts: 65
Joined: Sat Jul 07, 2012 11:21 pm
Location: Zero Page
by gin » Mon Sep 03, 2012 10:30 pm
One thing I've noticed missing from a lot of RPi startup code which is pretty important is initializing the .bss section in the startup routine. Without this, anything the compiler decides to put in this section thinking it will be initialized to zero won't necessarily be zero when you run it and you will get weird bugs.

You can fix it with something like (link map):
Code: Select all
   __bss_start__ = .;
   .bss : { *(.bss) } > ram
   __bss_end__ = .;   
   __bss_size__ = __bss_end__ - __bss_start__;

and (startup code):
Code: Select all
init_bss:
    # Initialize .bss
    ldr   r0, = __bss_start__  @ r0 = __bss_start__
    ldr   r1, = __bss_size__   @ r1 = __bss_size__

    # Handle __bss_size__ == 0
    cmp   r1, #0               @ if (r1 == 0)
    beq   call_main            @     goto call_main;

    mov   r2, #0               @ r2 = 0;
zero:
    strb  r2, [r0], #1         @ *(r0++) = r2
    subs  r1, r1,   #1         @ if (--r1 != 0)
    bne   zero                 @     goto zero

call_main:
    bl    main
Posts: 12
Joined: Tue Aug 28, 2012 5:52 am
by dwelch67 » Tue Sep 04, 2012 12:00 am
I wonder if I am in part responsible (for not initializing .bss). I write my embedded (bare metal) code so that there is no need to initialize .bss and no reason to copy .data. I normally state this somewhere.

It is nice to see that gcc is now starting to hand out warnings for code that is written to assume a variable is initialized to zero. Some sort of variable used without being set warning, which it didnt used to do, somewhere in the 4.6 or 4.7 range it started doing this (by default). Point being that should also discourage the behavior. I did recently see someone asking about where their variable went, they had initialized it to 0 in their code, instead of going into .data it was placed in .bss (compiler assuming .bss is zeroed). So if you use .data you should definitely init .bss.

David
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by gin » Tue Sep 04, 2012 2:08 am
Yeah, I know I first started off using your examples so I could see that being the case. :D

Stuff in the .bss section isn't uninitialized, though - it is initialized to zero. GCC won't give you any warning when it puts stuff there legally, it just generates a smaller ELF file since it doesn't have to explicitly initialize the entry to zero (since the .bss section is defined to be initialized to zero by the C runtime). For example:

Code: Select all
static int cur_buffer = 0;
static char buffers[1] = { 0 };
void main(void)
{
   do_stuff(buffers[cur_buffer]);
}


will have errors if you don't initialize the .bss and assume (correctly) that either cur_buffer or buffers[0] is going to be 0.
Posts: 12
Joined: Tue Aug 28, 2012 5:52 am
by dwelch67 » Wed Sep 05, 2012 3:18 am
Okay I guess gcc is not calling these out by default.

Code: Select all
unsigned int fun2 ( unsigned int );
unsigned int x;
unsigned int fun ( unsigned int a )
{
    unsigned int y;
    return(fun2(x+y+a));
}

Code: Select all
.globl _start
_start:
    bl fun
    b .

.globl fun2
fun2:
    bx lr

Code: Select all
arm-none-eabi-as start.s -o start.o
arm-none-eabi-gcc -Wall -O2 -c fun2.c -o fun.o
fun2.c: In function ‘fun’:
fun2.c:11:18: warning: ‘y’ is used uninitialized in this function [-Wuninitialized]
arm-none-eabi-ld -Ttext=0 start.o fun.o -o fun.elf

Code: Select all
arm-none-eabi-objdump -D fun.elf
fun.elf:     file format elf32-littlearm
Disassembly of section .text:

00000000 <_start>:
   0:   eb000001    bl   c <fun>
   4:   eafffffe    b   4 <_start+0x4>

00000008 <fun2>:
   8:   e12fff1e    bx   lr

0000000c <fun>:
   c:   e92d4008    push   {r3, lr}
  10:   e59f3010    ldr   r3, [pc, #16]   ; 28 <fun+0x1c>
  14:   e5933000    ldr   r3, [r3]
  18:   e0830000    add   r0, r3, r0
  1c:   ebfffff9    bl   8 <fun2>
  20:   e8bd4008    pop   {r3, lr}
  24:   e12fff1e    bx   lr
  28:   0000802c    andeq   r8, r0, ip, lsr #32

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0


Note that none of these programs in this post will actually run properly, barely enough code to keep the linker from complaining, that is all.

The linker has placed the x variable in ram, you can see the code is reading that value from ram before calling fun2().

The things I didnt expect/remember was 1) I thought gcc would warn about the uninitialized variable(s) by default, had to add -Wall 2) that it would complain about both x and y. Y definitely makes sense as that is on the stack and who knows what you are going to get.

Just so folks know what we are talking about.

First off the global variable x, because it was not initialized to anything and is global is assumed to be zero by the programmer. But if you do not have so called bootstrap code that preceeds the (first) C function call, then that memory is possibly uninitialized and your assumption will fail, so you wont get the zero you expect. typical startup code will zero that memory out.

Now change the code to this:

Code: Select all
unsigned int fun2 ( unsigned int );
unsigned int x=2;
unsigned int fun ( unsigned int a )
{
    unsigned int y;
    return(fun2(x+y+a));
}


Code: Select all
00000000 <_start>:
   0:   eb000001    bl   c <fun>
   4:   eafffffe    b   4 <_start+0x4>

00000008 <fun2>:
   8:   e12fff1e    bx   lr

0000000c <fun>:
   c:   e92d4008    push   {r3, lr}
  10:   e59f3010    ldr   r3, [pc, #16]   ; 28 <fun+0x1c>
  14:   e5933000    ldr   r3, [r3]
  18:   e0830000    add   r0, r3, r0
  1c:   ebfffff9    bl   8 <fun2>
  20:   e8bd4008    pop   {r3, lr}
  24:   e12fff1e    bx   lr
  28:   0000802c    andeq   r8, r0, ip, lsr #32

Disassembly of section .data:

0000802c <__data_start>:
    802c:   00000002    andeq   r0, r0, r2


Now x moved from .bss to .data and you can see the initialized value of 0x00000002, and the code still reads from what is now .data to fetch the x variable before adding it and calling fun2().

So to go all out:

memmap:
Code: Select all
MEMORY
{
   rom : ORIGIN = 0x00000, LENGTH = 32K
   ram : ORIGIN = 0x40000, LENGTH = 32K
}

SECTIONS
{
   .text : { *(.text*) } > rom
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ram AT > rom
   __data_end__ = .;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ram
   __bss_end__ = .;
}

Code: Select all
.globl _start
_start:
    bl fun
    b .

.word __bss_start__
.word __bss_end__
.word __data_rom_start__
.word __data_start__
.word __data_end__

.globl fun2
fun2:
    bx lr

Code: Select all
unsigned int fun2 ( unsigned int );
unsigned int x=2;
unsigned int z;
unsigned int fun ( unsigned int a )
{
    unsigned int y;
    y=5;
    return(fun2(x+y+z+a));
}


Code: Select all
arm-none-eabi-as start.s -o start.o
arm-none-eabi-ld -T memmap start.o fun.o -o fun.elf
arm-none-eabi-objdump -D fun.elf
Disassembly of section .text:

00000000 <_start>:
   0:   eb000006    bl   20 <fun>
   4:   eafffffe    b   4 <_start+0x4>
   8:   00040004    andeq   r0, r4, r4
   c:   00040008    andeq   r0, r4, r8
  10:   00000054    andeq   r0, r0, r4, asr r0
  14:   00040000    andeq   r0, r4, r0
  18:   00040004    andeq   r0, r4, r4

0000001c <fun2>:
  1c:   e12fff1e    bx   lr

00000020 <fun>:
  20:   e92d4008    push   {r3, lr}
  24:   e59f3020    ldr   r3, [pc, #32]   ; 4c <fun+0x2c>
  28:   e5932000    ldr   r2, [r3]
  2c:   e59f301c    ldr   r3, [pc, #28]   ; 50 <fun+0x30>
  30:   e5933000    ldr   r3, [r3]
  34:   e0823003    add   r3, r2, r3
  38:   e2833005    add   r3, r3, #5
  3c:   e0830000    add   r0, r3, r0
  40:   ebfffff5    bl   1c <fun2>
  44:   e8bd4008    pop   {r3, lr}
  48:   e12fff1e    bx   lr
  4c:   00040000    andeq   r0, r4, r0
  50:   00040004    andeq   r0, r4, r4

Disassembly of section .data:

00040000 <__data_start__>:
   40000:   00000002    andeq   r0, r0, r2

Disassembly of section .bss:

00040004 <__bss_start__>:
   40004:   00000000    andeq   r0, r0, r0



address 0x8 is __bss_start__, 0xC is bss end, 0x10 is data rom start, 0x14 data start, and 0x18 data end.

This is what you would (want to) see if you had a system that boots off of a rom, runs code from rom but .data and .bss need to be in ram. The problem is the .data values need to be in non-volatile memory so the trick with the gnu tools is to do the this AT that thing in the linker script. it needs to be at x when the C code starts to use it, but we are carrying it around at y until then. So just like gin pointed out the .bss needs to be initialized so does .data. You use these variables you place in the linker script and then call out in the code as global variables and the linker fills in the raw values for them. In this case the 4 bytes (one word) starting at address 0x54 in rom and copy it to address 0x40000. Then zero the 4 bytes (one word) starting at address 0x40004.

You can do this in the asm startup code (I did NOT do that in these examples) or you can actually get away with doing them in the C code so long as you do it before you actually use any of the variables in .data or .bss.

Sometimes you will see the memset and memcpy library functions used, and sometimes you will see a small loop in asm used...



The point I was trying to make, and I usually try to mention this in my examples, I personally dont use .data and dont assume .bss is zero, for example:

Code: Select all
unsigned int fun2 ( unsigned int );
unsigned int x;
unsigned int z;
unsigned int fun ( unsigned int a )
{
    unsigned int y;
    x=2;
    z=0;
    y=5;
    return(fun2(x+y+z+a));
}


In this case we would have saved a little, if I had the copy code in the above stuff, and removed it below we would save that amount. The single .data word which consumed a word in .text goes away, instead in this case that is replaced by two instructions the mov and str.

Code: Select all
00000000 <_start>:
   0:   eb000006    bl   20 <fun>
   4:   eafffffe    b   4 <_start+0x4>
   8:   00040000    andeq   r0, r4, r0
   c:   00040000    andeq   r0, r4, r0
  10:   00000054    andeq   r0, r0, r4, asr r0
  14:   00040000    andeq   r0, r4, r0
  18:   00040000    andeq   r0, r4, r0

0000001c <fun2>:
  1c:   e12fff1e    bx   lr

00000020 <fun>:
  20:   e92d4008    push   {r3, lr}
  24:   e59f3020    ldr   r3, [pc, #32]   ; 4c <fun+0x2c>
  28:   e3a02002    mov   r2, #2
  2c:   e5832000    str   r2, [r3]
  30:   e59f3018    ldr   r3, [pc, #24]   ; 50 <fun+0x30>
  34:   e3a02000    mov   r2, #0
  38:   e2800007    add   r0, r0, #7
  3c:   e5832000    str   r2, [r3]
  40:   ebfffff5    bl   1c <fun2>
  44:   e8bd4008    pop   {r3, lr}
  48:   e12fff1e    bx   lr
  4c:   00040000    andeq   r0, r4, r0
  50:   00040004    andeq   r0, r4, r4


In general though my approach will cost you some more .text space. The PUT32 and GET32 functions I use also cost you more than that. I dont do the .data and care about .bss (other than where it is) for one thing because the code to implement becomes toolchain dependent. Gnu linker scripts and startup code vs other compilers. If you move it all to .text that ports everywhere, including other gnu based compilers and linker scripts. I use PUT32 and GET32 because even gnu and other tools I have had not implement a pointer approach, sometimes you can go months or years without problems...and then one day you cant no matter what you do get the compiler to produce the right instruction.

Since I started doing that, with the work I do having an abstraction layer makes this code much more portable, I can very easily go from in chip simulation to outside the chip simulation to bare metal on silicon to on top of an operating system on the chip or on top of an operating system through pcie or whatever else is available (over network interfaces, serial, etc). Without changing this code. Plus many other things I can do with this abstraction. IMO well worth the size and performance hits.

Obviously my readmes and stuff are wordy and long but I dont want to add that much more and go onto a tangent. Maybe on my projects I should make an example dedicated to .bss and .data.

gin I hope I didnt steal your thunder on this...and thanks for calling me out on the gcc warning I has mis-remembered where I was seeing the warning (local variables not globals).

David
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by dwelch67 » Wed Sep 05, 2012 6:48 am
I added a bssdata example to my github raspberrypi repo (http://github.com/dwelch67/raspberrypi) which specifically addresses this .bss topic and I added to that .data and .rodata as well.

David
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by emcsquirreled » Wed Sep 05, 2012 4:17 pm
hermanhermitage wrote:Of course you can probably ignore all the proceeding parts unless you are going deep - most of what people are calling baremetal is more a quasi-baremetal - just focusing on the ARM.

You can just focus on ARM execution from 0x8000.


So, after copying and pasting the ASM initialization files, I can just focus on C (without standard library functions)? How does the initialization need to change if I want to run C++ (again, without libraries)?
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by dwelch67 » Wed Sep 05, 2012 5:40 pm
That is a whole other ball of wax from what I understand. If I were to do such a thing (which I wont, sorry), I would start by using say the codesourcery (now mentor graphics) toolchain and allow it to use a default linker script and default startup code, then examine the stuff it is doing in the startup. I think the ygarto (sp?) or devkitarm folks have support for C++ so you may find their startup code and lift it if you dont want to just let their tool use it as is.. Basically in addition to .bss and .data there are other things between reset and main() that may need to happen.

David
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by everslick » Wed Sep 05, 2012 6:00 pm
yes for example it is valid c++ to initialize a global with the returned value of a function:

static bar = foo();

int foo() {
return (666);
}

other things are calling constructors for global objects.

only putting random comments here, not sure how relevant they are for the init code at question. :-/

(this is not goin to be very useful, sorry)
clemens
User avatar
Posts: 8
Joined: Fri Jun 15, 2012 8:23 am
Location: Austrfia
by emcsquirreled » Wed Sep 05, 2012 6:45 pm
dwelch67 wrote:That is a whole other ball of wax from what I understand. If I were to do such a thing (which I wont, sorry), I would start by using say the codesourcery (now mentor graphics) toolchain and allow it to use a default linker script and default startup code, then examine the stuff it is doing in the startup. I think the ygarto (sp?) or devkitarm folks have support for C++ so you may find their startup code and lift it if you dont want to just let their tool use it as is.. Basically in addition to .bss and .data there are other things between reset and main() that may need to happen.

David


everslick wrote:yes for example it is valid c++ to initialize a global with the returned value of a function:

static bar = foo();

int foo() {
return (666);
}

other things are calling constructors for global objects.

only putting random comments here, not sure how relevant they are for the init code at question. :-/

(this is not goin to be very useful, sorry)
clemens


:shock: Well, it looks like I don't need OOP after all... Thanks for popping my bubble of wishful thinking. I'll rejoin practical reality now!

C should provide all the functionality I really need, and now that I know a little bit about what it is doing, I am going to use dwelch67's boot code instead of re-coding it from scratch. From there, everything should be straight forward (GPIO access, very basic filesystem, and the like). Thanks everyone for helping me out and sending me on to my first OS-less computer project! :D
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by hermanhermitage » Thu Sep 06, 2012 12:51 am
emcsquirreled wrote: :shock: Well, it looks like I don't need OOP after all... Thanks for popping my bubble of wishful thinking. I'll rejoin practical reality now!

C should provide all the functionality I really need, and now that I know a little bit about what it is doing, I am going to use dwelch67's boot code instead of re-coding it from scratch. From there, everything should be straight forward (GPIO access, very basic filesystem, and the like). Thanks everyone for helping me out and sending me on to my first OS-less computer project! :D


Its not so bad hosting minimal C++ but it depends on the compiler. Egcs back in the mid to late 90s was very easy (hours), where as all the commercial EDG based compilers have often been more painful (days).

But starting from dwelch67's stuff is an excellent way to get yourself up and running fast. The other way would be DexBasic.
Posts: 65
Joined: Sat Jul 07, 2012 11:21 pm
Location: Zero Page
by dwelch67 » Thu Sep 06, 2012 2:46 am
I dont want to discourage your choice of C++, it is a case of I dont know how to do it, but I know of projects that do. This comes up periodically and I know from the GBA homebrew days that it came up for that platform and the popular toolchains and libraries had C++ working. I only built my cross compilers just enough to do C without any libraries (no gcc library unless it happened to work and no C libraries). Seriously the formerly codesourcery now mentor graphics toolchain does C++ and has a free lite version. The devkitarm and perhaps other GBA/NDS homebrew toolchains do as well, and you can use those tools on the raspberry pi so long as you change the linker script and perhaps the
startup code depending on how gba/lds specific it is if at all..

Plain old C and assembly work well and are easier to get up and running. Not sure which forum this happened on but I added a bccdata example in my raspberry pi examples so that folks can understand a little bit more about using or not using or making assumptions or not about initializing global variables, esp if the try to do that with my examples as a baseline.
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by emcsquirreled » Thu Sep 06, 2012 3:23 am
hermanhermitage wrote:But starting from dwelch67's stuff is an excellent way to get yourself up and running fast. The other way would be DexBasic.


Well, I hate to post another really dumb question, but here goes. I was going through dwelch67's repos trying to find the proper files to download. Which project do I want to cannibalize? My first attempt was to pull down the fourth bootloader (as it seemed the most complete), but it seemed to be relying on a serial connection which I do not have and do not really want to set up. My next target was the blinker01 project, but there seemed to be a mix of pre-compiled .bins and source code. Also, the readme implied that the ASM initialization code from blinker01 was not enough to host a full program.

I seem to have all the pieces, but I just cannot figure out how they all go together. If someone could tell me which files I need in which versions to fully initialize my system and run any C code in notmain() (minus libraries, obviously), that would be really great.

hermanhermitage wrote:Its not so bad hosting minimal C++ but it depends on the compiler. Egcs back in the mid to late 90s was very easy (hours), where as all the commercial EDG based compilers have often been more painful (days).


dwelch67 wrote:I dont want to discourage your choice of C++, it is a case of I dont know how to do it, but I know of projects that do. This comes up periodically and I know from the GBA homebrew days that it came up for that platform and the popular toolchains and libraries had C++ working. I only built my cross compilers just enough to do C without any libraries (no gcc library unless it happened to work and no C libraries). Seriously the formerly codesourcery now mentor graphics toolchain does C++ and has a free lite version. The devkitarm and perhaps other GBA/NDS homebrew toolchains do as well, and you can use those tools on the raspberry pi so long as you change the linker script and perhaps the
startup code depending on how gba/lds specific it is if at all.


Sweet! Once my Pi boots into some pseudo bare metal code (e.g. notmain() from dwelch67's code), I'll look into this. Gotta crawl before you walk, though, so booting is taking center stage for now.
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA
by gin » Thu Sep 06, 2012 3:47 am
emcsquirreled wrote:My first attempt was to pull down the fourth bootloader (as it seemed the most complete), but it seemed to be relying on a serial connection which I do not have and do not really want to set up.

blinker01 looks complete (I think it says it just has a small stack). The binary stuff looks like it was just stuff left over form building - you can do a "make clean" and it will remove it all. I'm pretty sure that was the project I used to test my toolchain install. You can look at some of the other projects for more info about larger stacks and modifying the link maps (his new bss one looks like it has a lot of info).

Also, if you decide you want to seriously program bare metal on this thing, buy a cheap UART to USB dongle. If it had not been for dwelch67's recommendation to get one of those I would have spent _so_ many hours pulling that SD card out to reprogram (and TBH probably would have stopped out of inconvenience. :P)
Posts: 12
Joined: Tue Aug 28, 2012 5:52 am
by dwelch67 » Thu Sep 06, 2012 4:33 am
I use micro sd cards with an full sd adapter and for some reason I have broken two or three of the adapters in a raspberry pi, fortunately only the adapters are broken and not the raspi. I think Dex and some others have had many more sd card cycles than I have but it has taken hundreds of cycles of what I call the "sd card dance" to get my bootloaders and other items working. Put a fair amount of wear and tear on my computers front usb port as well unplugging and replugging to power cycle the raspi. The serial bootloader and jtag bootloader have saved me many more hundreds of sd card insertions. $15-$20 plus a few bucks in shipping will save a lot of pain, also being able to print stuff out the uart greatly helps debugging.

I think I tend to use bootloader03 if I use the serial ones, bootloader04 if I remember right requires a config.txt file which I prefer to not use if I dont have to.

I changed a few of the projects to include all of the output/build files. Folks were asking to have at least the binary file to copy to the sd card, and then they would try to come up with a toolchain and learn to use the toolchain to re-build the projects, etc. So a few blinkers and bootloaders I put the binaries at a minimum in the repo. The .list files are the most important output files for understanding the build process (IMO), if I was smart I included those as well. I dont normally put binaries out there, it made sense in this case.

We (bare metal programmers on the raspberry pi) have gotten some public notice and a nice paragraph on the front page of the raspberry pi site about this forum. I am feeling a bit inspired again I may grab the work by Dex and the Baking Pi and play with video finally. What might be more interesting is if this bare metal usb code posted today is enough to talk to the ethernet chip on board, I might do a crude udp stack and loader, for loading stuff to the arm over ethernet...

Welcome to bare metal, enjoy...What you will find here is that there are no rules, you can program however you want, no apis you have to conform to, no operating systems to get in the way, etc. The freedom is addictive...
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by dwelch67 » Thu Sep 06, 2012 4:36 am
Did everyone see the front page of the raspi site today? Looks like they are going to add/move the one arm jtag signal not on the main connector, to the main connector. You wont have to do any soldering to use arm jtag...Nice...
Posts: 370
Joined: Sat May 26, 2012 5:32 pm
by hermanhermitage » Thu Sep 06, 2012 11:13 am
dwelch67 wrote:Did everyone see the front page of the raspi site today? Looks like they are going to add/move the one arm jtag signal not on the main connector, to the main connector. You wont have to do any soldering to use arm jtag...Nice...

Yes this is excellent news having jtag should attract even more bare metallers! :)
Posts: 65
Joined: Sat Jul 07, 2012 11:21 pm
Location: Zero Page
by emcsquirreled » Thu Sep 06, 2012 4:04 pm
gin wrote:blinker01 looks complete (I think it says it just has a small stack). The binary stuff looks like it was just stuff left over form building - you can do a "make clean" and it will remove it all. I'm pretty sure that was the project I used to test my toolchain install. You can look at some of the other projects for more info about larger stacks and modifying the link maps (his new bss one looks like it has a lot of info).


Will using a "small stack" come back to bite me? Or will it run most everything within reason?
Linux -- The operating system with a CLUE (Command Line User Environment)
--
FTC Team 4508
E=MC Squirreled
emcsquirreled.github.com
Posts: 91
Joined: Sat Aug 18, 2012 1:26 am
Location: Portland, OR USA