¿Cómo ejecutar un programa C sin sistema operativo en la Raspberry Pi?


Me gustaría experimentar usando Raspberry Pi para algunas aplicaciones embebidas de bajo nivel diferentes. El único problema es que, a diferencia de las placas de microcontroladores AVR y PIC disponibles, Raspberry Pi normalmente ejecuta un sistema operativo (como Raspbian) que distribuye el tiempo de CPU a través de todos los programas en ejecución y lo hace poco práctico para ciertas aplicaciones en tiempo real.

He aprendido recientemente que, suponiendo que tiene un gestor de arranque como GRUB instalado, ejecutando un programa C en x86 (en forma de un kernel) toma muy poca configuración real, solo un programa ensamblador para llamar a la función principal y el código C real.

¿Hay alguna manera de lograr esto con una Raspberry Pi? Sería una gran manera de aprender sobre la programación ARM de bajo nivel, y ya tiene algunos periféricos complejos para jugar (USB, Ethernet,etc.)

5 answers

Mientras que bare metal es posible en la Pi, yo lo evitaría ya que Linux se está volviendo muy ligero y maneja un montón de cosas para usted.

Aquí hay un tutorial para comenzar si aún quieres aprender cosas de metal desnudo: http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/

Con todo lo dicho, simplemente cargaría su distro de Linux incrustado favorito (RT parcheado podría ser preferido en función de sus requisitos) y lo llamaría Bien.

 13
Author: It'sPete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-24 03:00:36

En realidad, la Raspberry Pi es uno de los brazos más fáciles de programar bare metal (sin sistema operativo) Tengo muchos ejemplos en github para comenzar

Https://github.com/dwelch67/raspberrypi

Una cosa buena sobre raspberry pi es que no necesita un gestor de arranque como uboot, hay una gpu que realmente trae el chip primero y luego carga el núcleo (o la aplicación bare metal, lo que sea) en ram y se ramifica de la misma manera que uboot tendría. Otra cosa agradable es que usted no puede ladrillo como usted puede con tantos otros tableros en esta clase, si se equivoca usted tira de la tarjeta sd, inténtelo de nuevo, si se da por vencido a continuación, poner una tarjeta sd con Linux en él de nuevo y ejecutar linux...

 33
Author: old_timer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-14 15:05:16

Ejemplo de intermitente de metal desnudo mínimo totalmente automatizado

Probado en Ubuntu 16.04 host, Raspberry Pi 2.

Dwelch es el ejemplo más completo, pero este es un mínimo fácil de configurar hello world.

Uso:

  1. Inserte la tarjeta SD en el host

  2. Haz la imagen:

    ./make.sh /dev/mmblck0 p1
    

    Donde:

    • /dev/mmblck0 es el dispositivo de la tarjeta SD
    • p1 es la primera partición del dispositivo (/dev/mmblck0p1)
  3. Insertar tarjeta SD en PI

  4. Apagar y encender

introduzca la descripción de la imagen aquí

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

Start.S

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

Main.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"

Ejemplos de QEMU friendly bare metal

El problema con el intermitente es que es difícil observar ledes en 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

Aquí describo algunas configuraciones de QEMU de metal desnudo que pueden ser de interés: ¿Cómo hacer programas de brazo de metal desnudo y ejecutarlos en QEMU? Escribir en el UART es la forma más fácil de obtener la salida de QEMU.

Bonus

Aquí hay un ejemplo de x86 para los curiosos: Cómo ejecutar un programa sin un sistema operativo?

 11
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-22 06:08:12

Https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os / es un gran tutorial, y como te dirán la mejor manera rápida y sucia de ejecutar código en bare metal es secuestrar una distribución de linux, para hacerlo, solo compila al núcleo.img (con las opciones de arquitectura apropiadas) y utilícela para reemplazar la existente en la distribución de linux solo para esta sección del tutorial puede ir a: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html#pitime

 3
Author: joshbooks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-24 03:17:09

El Pi puede ser un poco subóptimo para lo que desea hacer, ya que el diseño SoC es tal que la CPU ARM es un ciudadano de segunda clase, lo que significa que hay algunos aros para saltar para obtener un programa de metal desnudo ejecutándose en él.

Sin embargo, podría hacer trampa un poco y usar la API U-Boot para darle acceso a algunas de las características que U-Boot proporciona, pero ser capaz de agregar sus propias características en el lateral.

 2
Author: unixsmurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-24 10:27:00