web-dev-qa-db-fra.com

Comment exécuter un programme C sans OS sur le Raspberry Pi?

J'aimerais expérimenter l'utilisation du Raspberry Pi pour différentes applications intégrées de bas niveau. Le seul problème est que, contrairement aux cartes microcontrôleurs AVR et PIC disponibles, Raspberry Pi exécute généralement un système d'exploitation (comme Raspbian) qui répartit le temps CPU sur tous les programmes en cours d'exécution et le rend peu pratique pour certaines applications en temps réel.

J'ai récemment appris que, en supposant que vous ayez un chargeur de démarrage comme GRUB installé, exécuter un programme C sur x86 (sous la forme d'un noyau) prend très peu de temps réel configuration, juste un programme d'assemblage pour appeler la fonction principale et le code C.

Existe-t-il un moyen d'y parvenir avec un Raspberry Pi? Ce serait un excellent moyen d'en apprendre davantage sur la programmation de bas niveau ARM, et il a déjà quelques périphériques complexes avec lesquels s'amuser (USB, Ethernet, etc.)

36
watswat5

Bien que le métal nu soit possible sur le Pi, je l'éviterais car Linux devient si léger et gère tout un tas de choses pour vous.

Voici un tutoriel pour vous lancer si vous voulez toujours apprendre des trucs de bare metal: http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1 /

Cela dit, je chargerais simplement votre distribution Linux embarquée préférée (RT patché pourrait être préféré en fonction de vos besoins) et je l'appellerais bien.

15
It'sPete

Exemple de clignotant minimal entièrement nu entièrement automatisé

Testé sur Ubuntu 16.04 Host, Raspberry Pi 2.

celui de dwelch est l'exemple le plus complet , mais c'est un monde bonjour minimal facile à configurer.

Usage:

  1. Insérez la carte SD sur l'hôte

  2. Faites l'image:

    ./make.sh /dev/mmblck0 p1
    

    Où:

    • /dev/mmblck0 est le périphérique de la carte SD
    • p1 est la première partition de l'appareil (/dev/mmblck0p1)
  3. Carte SD insérée sur PI

  4. Éteignez puis rallumez

enter image description here

GitHub en amont: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker/tree/d20f0337189641824b3ad5e4a688aa91e13fd764

départs

.global _start
_start:
    mov sp, #0x8000
    bl main
hang:
    b hang

principal c

#include <stdint.h>

/* This is bad. Anything remotely serious should use timers
 * provided by the board. But this makes the code simpler. */
#define BUSY_WAIT __asm__ __volatile__("")
#define BUSY_WAIT_N 0x100000

int main( void ) {
    uint32_t i;
    /* At the low level, everything is done by writing to magic memory addresses.
    The device tree files (dtb / dts), which are provided by hardware vendors,
    tell the Linux kernel about those magic values. */
    volatile uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;
    volatile uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;
    volatile uint32_t * const GPSET1  = (uint32_t *)0x3F200020;
    volatile uint32_t * const GPCLR1  = (uint32_t *)0x3F20002C;

    *GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) | (1 << 21);
    *GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) | (1 << 15);
    while (1) {
        *GPSET1 = 1 << (47 - 32);
        *GPCLR1 = 1 << (35 - 32);
        for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
        *GPCLR1 = 1 << (47 - 32);
        *GPSET1 = 1 << (35 - 32);
        for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
    }
}

ldscript

MEMORY
{
    ram : Origin = 0x8000, LENGTH = 0x10000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}

make.sh

#!/usr/bin/env bash

set -e

dev="${1:-/dev/mmcblk0}"
part="${2:-p1}"
part_dev="${dev}${part}"
mnt='/mnt/rpi'

Sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi

# Generate kernel7.img
arm-none-eabi-as start.S -o start.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld start.o main.o -T ldscript -o main.elf
# Get the raw Assembly out of the generated elf file.
arm-none-eabi-objcopy main.elf -O binary kernel7.img

# Get the firmware. Those are just magic blobs, likely compiled
# from some Broadcom proprietary C code which we cannot access.
wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=true
wget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true

# Prepare the filesystem.
Sudo umount "$part_dev"
echo 'start=2048, type=c' | Sudo sfdisk "$dev"
Sudo mkfs.vfat "$part_dev"
Sudo mkdir -p "$mnt"
Sudo mount "${part_dev}" "$mnt"
Sudo cp kernel7.img bootcode.bin start.elf "$mnt"

# Cleanup.
sync
Sudo umount "$mnt"

Exemples de métal nu compatibles avec QEMU

Le problème avec le clignotant est qu'il est difficile d'observer les LED dans QEMU: https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of- the-leds-and-gpios-in-a-qemu-emulation-like-t

Ici, je décris quelques configurations de QEMU bare metal qui peuvent être intéressantes: Comment faire des programmes bare metal ARM et les exécuter sur QEMU? Écriture dans le UART est le moyen le plus simple de sortir la sortie de QEMU.

Bonus

Voici un exemple x86 pour les curieux: Comment exécuter un programme sans système d'exploitation?

https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ est un excellent tutoriel, et comme ils vous diront la meilleure façon rapide et sale d'exécuter du code sur le métal nu consiste à détourner une distribution linux, pour ce faire, il suffit de compiler dans kernel.img (avec les options d'architecture appropriées) et de l'utiliser pour remplacer celui existant dans la distribution linux pour cette section du didacticiel à laquelle vous pouvez accéder : https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html#pitime

3
joshbooks

Le Pi peut être un peu sous-optimal pour ce que vous voulez faire, car la conception du SoC est telle que le ARM CPU est un citoyen de seconde classe - ce qui signifie qu'il y a des cerceaux à parcourir) obtenir un programme nu sur lui.

Cependant, vous pouvez tricher un peu et utiliser -Boot API pour vous donner accès à certaines des fonctionnalités fournies par U-Boot mais être en mesure d'ajouter vos propres fonctionnalités sur le côté.

2
unixsmurf