Raspberry Pi: Difference between revisions

no edit summary
[unchecked revision][unchecked revision]
No edit summary
No edit summary
 
(26 intermediate revisions by 11 users not shown)
Line 1:
{{In Progress}}
{{FirstPerson}}
 
{{You}}
{{Sole Editor}}
This is a tutorial on bare-metal [OS] development on the Raspberry Pi. This tutorial is written specifically for the Raspberry Pi Model B Rev 2 because the author has no other hardware to test on. But so far the models are basically identical for the purpose of this tutorial (Rev 1 has 256MB ram, Model A has no ethernet).
 
Line 11 ⟶ 13:
You will need a:
* '''Raspberry Pi''', RPi in short.
* [[SD Card]] to boot from.
* A SD Card reader so you can write to the SD Card from your developement system.
* A serial adaptor for the RPi.
* Power from an external powerPower supplySupply, usbUSB or the serialSerial adaptorAdaptor.
 
=== Serial adaptor ===
 
The RPi has 2 serialsserial ports ([[UART|UARTs]]). This tutorial only concerns itself with UART0, called simply UART or serial port. UART1 is ignored from now on. The basic UART onboard uses a 3.3V TTL and is connected to some of the GPIO pins labeled "P1" on the board. x86 PCs and MACs do use 5V TTL so you need some adaptor to convert the TTL. I recommend aA '''USB to TTL Serial Cable - Debug / Console Cable for Raspberry Pi''' with separate connectors per lead, like [http://www.adafruit.com/products/954 commercial RPi serial adaptor], is recommended.. Which is then connected to the RPi [[Media:ARM_RaspberryPi_serial.jpg|like this]]. SlightlyThe slightly cheaper [http://www.ebay.com/itm/USB-To-RS232-TTL-PL2303HX-Auto-Converter-Module-Converter-Adapter-5V-3-3V-Output-/350568364250 PL2303HX adapter] was found usable, but seems to be unreliable if connected to an USB port with an extension cable (a USB 2.0 hub might remedy this).
 
Note: The serial adaptor I use provides both a 0V and 5V lead (black and red) which provide power to the RPi. No extra power supply is needed besides this.
 
Alternatively, you can use the FTDI chip on an Arduino (or clone thereof). Connect RX on the Arduino to RX on the Pi, TX to TX and GND to GND, then connect the Arduino's reset pin to ground to prevent code in flash from interfering. Due to reset being held low, the Arduino itself is entirely bypassed. If you have a clone that supports 3.3V operation (such as a Seeeduino) then you can enable it to be safe, but this approach works fine with 5V.
===Testing your hardware/serial port===
 
=== Testing your hardware/serial port ===
 
First things first, you're going to want to make sure all your hardware works. Connect your serial adaptor to the RPi and boot up the official Raspian image. The boot process will output to both the serial and the HDMI and will start a getty on the serial. Set up your serial port, however yours works, and open up minicom. Make sure you have flow control turned off.
Line 28 ⟶ 32:
 
If you get 'Permission Denied' '''do NOT become root!''' This is unnecessary. Instead do:
<sourcesyntaxhighlight lang="bash">
sudo adduser <user> dialout
</syntaxhighlight>
</source>
This will let your user use serial ports without needing root.
 
Line 37 ⟶ 41:
If you started minicom only after the RPi has booted then simply press '''return''' in minicom so the getty will output a fresh login prompt. Otherwise wait for the boot messages to appear. If you don't get any output then connect the RPi to a monitor to check that it actually boots, check your connections and minicom settings.
 
=== Building a cross compiler ===
 
Like me you are probably using a x86 PC as main machine and want to edit and compile the source on that and the RPi is an ARM cpuCPU so you absoluetlyabsolutely need a cross compiler. But even if you are developing on an ARM system it is still a good idea to build a cross compiler to avoid accidentally mixing stuff from your developement system with your own kernel. Follow the steps from [[GCC_CrossGCC Cross-Compiler]] to build your own cross compiler but use:
 
<sourcesyntaxhighlight lang="bash">
export TARGET=arm-none-eabi
</syntaxhighlight>
</source>
 
Now you are ready to start.
 
== Tutorials and examples ==
# [httphttps://www.cl.cam.ac.uk/freshersprojects/raspberrypi/tutorials/os/ Tutorial in assembler (University of Cambridge)]
# [[ARM_RaspberryPi_Tutorial_CRaspberry Pi Bare Bones|Tutorial in C]]
# [[Raspberry Pi Bare Bones Rust|Tutorial in Rust]]
# [https://github.com/dwelch67/raspberrypi Collection of examples and bootloader by dwelch67]
# [https://github.com/bztsrc/raspi3-tutorial Tutorials for AArch64 in C by bzt]
# [https://github.com/s-matyukevich/raspberry-pi-os OS tutorial for the Raspberry Pi by s-matyukevich]
# [[Detecting Raspberry Pi Board]]
 
== Booting the kernel ==
 
Do you still have the SD card with the original raspianRaspian image on it from when you where testing the hardware above? Great. So you already have a SD card with a boot partition and the required files. If not then download one of the original raspberryRaspberry boot images and copy them to the SD card.
 
Now mount the first partition from the SD card and look at it:
 
<sourcesyntaxhighlight lang="text">
bootcode.bin fixup.dat kernel.img start.elf
cmdline.txt fixup_cd.dat kernel_cutdown.img start_cd.elf
config.txt issue.txt kernel_emergency.img
</syntaxhighlight>
</source>
 
When the RPi powers up the ARM cpuCPU is halted and the GPU runs. The GPU loads the bootloader from rom and executes it. That then finds the SD card and loads the bootcode.bin. The bootcode handles the config.txt and cmdline.txt (or does start.elf read that?) and then runs start.elf. start.elf loads the kernel.img at 0x00008000, puts a few opcodes at 0x00000000 and the ATAGS at 0x00000100 and at last the ARM cpuCPU is started. The cpuCPU starts executing at 0x00000000, where it will initialize r0, r1 and r2 and jump to 0x00008000 where the kernel image starts.
 
So to boot your own kernel simply replace kernel.img with our own, umount, sync, stick the SD card into RPi and turn the power on.
Line 70 ⟶ 78:
Note: The GPU also initialized the video ouput, detecting the right resolution from the monitor (if hdmi) or from the config.txt and creates a 2x2 pixel framebuffer (red, yellow, blue and cyan pixels) that the hardware scales to fullscreen with color interpolation. So you get rectangle with a nice color fading.
 
== Boot- from- serial kernel==
 
The RPi boots the kernel directly from an SD card and only from an SD card. There is no other option. While developing this becomes tiresome since one has to constantly swap the SD card from the RPi to a SD card reader and back. Writing the kernel to the SD card over and over also wears out the card. Plus the SD card slot is somewhat fragile; several people have reported that they broke it accidentally. Overall not an ideal solution. So what can we do about that?
Line 78 ⟶ 86:
Raspbootin is completely transparent for your kernel. It preserves the r0, r1 and r2 registers and ATAGs placed into memory by the GPU for your kernel. So whether you boot your kernel directly from an SD card or with Raspbootin via serial port makes no difference to your code.
 
=== Raspbootin serial protocol ===
 
You don't have to care about this unless you want to write your own boot server.
Line 84 ⟶ 92:
The boot protocol for Raspbootin is rather simple. Raspbootin first sends 3 breaks (<code>\x03</code>) over the serial line to signal that it is ready to receive a kernel. It then expects the size of the kernel as <code>uint32_t</code> in little endian byte order. After the size it replies with "<code>OK</code>" if the size is acceptable or "<code>SE</code>" if it is too large for it to handle. After "<code>OK</code>" it expects <code>size</code> many bytes representing the kernel. That's it.
 
== Parsing ATAGs ==
 
A good documentation for ATAGs can be found [http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html#appendix_tag_reference here].
Line 90 ⟶ 98:
[http://web.archive.org/web/20120605001004/http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html cached version from the Wayback Machine]
 
Note that later Raspberry Pis will pass you a device tree blob instead in R2. ATAGs still can be found at 0x100, if you disable device tree (r2 contains 0x0 in this case) - identifiable because they always start with an ATAG_CORE (0x54410001). In comparison, Device Tree is probably far more useful, but is more complex. A device tree starts with the uint32_t 0xd00dfeed '''(big-endian).''' Note the big endian - this applies to all values. ARM defaults to little endian, so you'll probably want to write some endian routines early!
==Framebuffer support==
[https://www.devicetree.org/ Device Tree Specification]
 
== Framebuffer support ==
 
On boot the RPi configures a display with a virtual resolution of 2x2 pixel scaled to full screen. Each pixel has a different color and the hardware scaling interpolates the colors to show a nice color fade. So before you do anything first connect a monitor and see to it that you get some output.
 
For a framebuffer, you haveneed to learn how to
[https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes access mailboxes]. And then you have to send the GPU some [https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface mail]. Or, you could read about the Framebufferframebuffer [http://elinux.org/RPi_Framebuffer here],
 
Start with a single simple querry at first and then build up more complex mails. If you get it working then I recommend just altering the virtual size and color depth and leaving the physical resolution as is. The RPi seems to do a fine job of detecting the monitor and the user can also configure a resolution in the boot config files on the SD card. Best to honor his wishes.
 
== Interrupts and exceptions ==
 
By default the exception vector table on ARM starts at 0x0. You can use that but there are better ways. You can set a flag to use a high vector at 0xffff0000 or set the exception vector base address register to point to your own table anywhere (32byte32 byte aligned) you like.
 
Note: Interrupts are level triggered so you have to clear the source of an interrupt or mask it before returning from interrupt. The ARM cpuCPU in the RPi also supports some extra instructions for storing registers on the stack of a different mode, switching modes and returning from interrupt. Those extensions are nicely described in the ARM arm.
 
Note: The return address in LR during an interrupt will be 0-8 byte, depending on the type of exception, offset to what it should be and needs to be adjusted before returning. Again look into the ARM arm for which offset applies to which exception.
Line 109 ⟶ 120:
Implementing and testing the software interrupt first is a good idea since you can trigger it in a controlled way.
 
When configuring some peripheral to send an interrupt it is a usefulluseful thing to have interrupts disabled in the CPSR, enable the interrupt you are interested in (or all) in the 3 interrupt enable registers and then poll the 3 pending registers in a tight loop and output changes. This allows you to see if the peripheral raises an interrupt and (if in doubt) which one. After that the real interrupt handler can be configured and tested. Gives you a nice half way point to test what you have so far.
 
== Floating point support - VFP==
 
To be able to use any floating point operations, such as storing or loading floating point numbers, you need to enable the FPU before using it. To do this, you have to enable access to the coprocessor to whoever should be able to use it, and you have to enable the FPU itself.
 
<syntaxhighlight lang="asm">
# enable FPU in coprocessor enable register - this gives everybody access to both locations of coprocessor.
ldr r0, =(0xF << 20)
mcr p15, 0,ldr r0, c1,=(0xF c0,<< 220)
mcr p15, 0, r0, c1, c0, 2
</syntaxhighlight>
 
And then enable the FPU itself:
 
<syntaxhighlight lang="asm">
# enable FPU in FP exception register
MOV r3, #0x40000000
# VMSR FPEXC, r3 # assembler bug
.long 0xeee83a10
</syntaxhighlight>
 
The third line is the actual instruction that you'd want to use, but due to a bug in Binutils 2.23 it does not assemble. The line below it is what it should assemble to, and replaces the opcode. After doing these two, it's possible to use the FPU.
 
== USB ==
 
A standalone BSD-licenced USB driver with support for keyboard and mouse is available here: https://github.com/Chadderz121/csud . This driver can be kept stand-alone, by editing the <code>/source/platform.c</code> file to interface the driver with your implementation of <code>malloc()</code> and similar functions, or you can integrate the driver more closely with your operating system.
 
== External references ==
# [https://www.scss.tcd.ie/~waldroj/3d1/arm_arm.pdf arm_arm.pdf] - generalGeneral ARM Architecture Reference Manual v6
# [http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf DDI0301H_arm1176jzfs_r0p7_trm.pdf] - More specific ARM for the RPi
# [https://github.com/dwelch67/raspberrypi dwelch67 examples] - basicBasic toolchain + UART stuff
# [http://elinux.org/RPi_Hardware RPi_Hardware] - listList of datasheets (and one manual about peripherals on the broadcomBroadcom chip)
# [https://github.com/raspberrypi/firmware/wiki GitHub Raspberry Pi firmware wiki] - forFor mailboxes and video stuff
# [http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf BCM2835-ARM-Peripherals.pdf] - Datasheet for RPi peripherals
# [http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html Booting ARM Linux] - Describes the generic bootloader interface to the ARM port of linuxLinux which the RPi bootloader emulates
# [http://sourceforge.net/projects/rpiqemuwindows/ RPi Emulator] - A preconfigured QemuQEMU RPi emulation environment for Windows.
 
[[Category:ARM]]
[[Category:ARM_RaspberryPiRaspberry Pi]]