128 lines
5.6 KiB
Plaintext
128 lines
5.6 KiB
Plaintext
|
|
See the top level README for information on where to find documentation
|
|
for the raspberry pi and the ARM processor inside. Also find information
|
|
on how to load and run these programs.
|
|
|
|
This example is for the older pi1 boards, see other directories for
|
|
other flavors of raspberry pi.
|
|
|
|
The pi1 has one led that is tied to gpio 16.
|
|
|
|
Being the first example I will spend a little more time describing it.
|
|
I have some other ramblings on baremetal and the gnu tools so I will
|
|
try not to duplicate that.
|
|
|
|
The primary use case for the raspberry pi is to run some version of
|
|
linux. The three main files to do that are bootloader.bin, start.elf
|
|
and kernel.img. The first two being GPU programs the last being ARM.
|
|
If you have no other files (dont have a config.txt) they (starg.elf
|
|
GPU code) copy the kernel.img file to 0x8000 in RAM, place some
|
|
code at address 0x0000 that is intended to prepare the ARM for booting
|
|
linux, then branch to 0x8000. We simply replace the kernel.img file
|
|
with our own program. I prefer to not mess with config.txt stuff
|
|
because it is not the primary use case, most pis out there do not use
|
|
this, so without is significantly better tested. Also over time the
|
|
config.txt things you can play with come and go and change name, some
|
|
of the popular ones are undocumented and so on. So I really dont want
|
|
to rely on that. Simply replace the kernel.img and otherwise be stock.
|
|
|
|
vectors.s is the entry point for this program, even an application on
|
|
an operating system like linux has some assembly up front before
|
|
calling the main function. For this processor the minimum is to to
|
|
set up the stack pointer and call the main function. Because some
|
|
compilers add extra stuff if they see a main() funtion I use some
|
|
function name other than main() typically for embedded systems like this.
|
|
I have adopted the habit of using notmain() both to not be named main()
|
|
and to emphasize this is bare metal and not your average application.
|
|
|
|
See my ramblings on .data and .bss, I dont need/use them so the
|
|
bootstrap (little bit of assembly before calling the first C function)
|
|
does not have to prepare those segments. I only need to setup the
|
|
stack and call the first C function in the project.
|
|
|
|
I normally would set the stack pointer at the top of ram...Well that is
|
|
a lie, normaly one would do such a thing, but with code like this that
|
|
mostly ports across a number of boards, it becomes a pain keeping track
|
|
of who has how much ram. Instead for simple examples I set the stack
|
|
somewhere where it doesnt collide with the code, but also I dont have to
|
|
change every board. Because I am using the 0x8000 entry point I can
|
|
set the stack at 0x8000 and it will grow down toward 0x0000, and that
|
|
is more than enough for these projects. The way the ARM works it
|
|
subtracts then writes stuff so the first thing on the stack will really
|
|
be at 0x7FFC, you dont have to set it to 0x7FFC to avoid the code at
|
|
0x8000.
|
|
|
|
The gpio pin is setup as an output to drive the LED. The blink rate
|
|
appears to be around a couple-three times a second, but may vary based
|
|
on your compiler and settings. This program does not attempt to use
|
|
any other peripherals (a timer) it relies on simply wasting time in a
|
|
loop then changing the state of the LED. If you were to use this line
|
|
of code:
|
|
|
|
for(ra=0;ra<0x1000;ra++) continue;
|
|
|
|
The optimizer will replace that with this one assignment:
|
|
|
|
ra = 0x1000;
|
|
|
|
And actually since that value isnt used again, it is dead code the
|
|
optimizer will likely remove that as well.
|
|
|
|
One way to get around this is to make the loop variable volatile, this
|
|
tells the compiler every time you use it grab it from and save it back
|
|
to its home in ram. I prefer a different approach. I have a simple
|
|
dummy function in assembly language, it simply returns.
|
|
|
|
.globl dummy
|
|
dummy:
|
|
bx lr
|
|
|
|
The assembly is outside the visibility of the optimizer as would
|
|
anything basically not in the same file (llvm is a little different it
|
|
might "see" those other objects and optimize across them, gnu wont).
|
|
|
|
So by having that external function and by passing the loop variable
|
|
to it.
|
|
|
|
for(ra=0;ra<0x1000;ra++) dummy(ra);
|
|
|
|
We force the compiler to actually implement this code and run that loop
|
|
that many times. Dont need to declare the variable volatile. If
|
|
uncomfortable with assembly langauge you could create a dummy function
|
|
in a separately compiled file
|
|
|
|
void dummy ( void )
|
|
{
|
|
}
|
|
|
|
Which produces the same code.
|
|
|
|
00000000 <dummy>:
|
|
0: e12fff1e bx lr
|
|
|
|
Some toolchains have the ability to see across objects when they
|
|
optimize so you still have to be careful. I prefer the assembly approach
|
|
to defeating the optimizer.
|
|
|
|
So this program sets up the gpio to drive the LED. Uses the loop to kill
|
|
some time, changes state of the LED, repeat forever. The blink rate
|
|
will be the same for the same program. But compiler differences and
|
|
options can cause one build to be different from another in the blink
|
|
rate. It is not really deterministic, thus the desire to use timers in
|
|
the examples that follow. If you change the number the loop counts to
|
|
and re-build with the same tools, you should see a change in the
|
|
blink rate.
|
|
|
|
Note the Broadcom documentation uses addresses 0x7Exxxxxx for the
|
|
peripherals. That is we assume the GPU's address space to access those
|
|
things. The ARM window into that is to date either at 0x20xxxxxx or
|
|
0x3Fxxxxxx depending on the specific broadcom chip for that board type
|
|
the pi1 uses 0x20xxxxxx so when you se 0x7Exxxxxx replace that
|
|
0x7E with 0x20.
|
|
|
|
I normally dont leave the compiled output in the repository, but you
|
|
may need it to compare with your results to see why for example mine
|
|
works and yours doesnt, so I will leave these here for this example.
|
|
|
|
|