Beaglebone Black Baremetal

I am trying to run baremetal code on the beaglebone black. I am currently compiling baremetal code using the arm-none-eabi-gcc compiler and I want to know if this is even possible. I plan to create my own MLO file on the memory card which will get called by the BOOTRom code. I am currently unsure how to create my own MLO and how I will observe output. Thanks to the chip shortage, I am unable to get a jtag debugger either. I am currently planning to make my own baremetal blink code but I am unsure on how to do it. ANY THOUGHTS ARE APPRECIATED!

Here’s a tiny baremetal program for the BBB:

It’s completely written in assembly, which is of course completely unnecessary but I don’t have a C/C++ example readily available.

Some bits from my own baremetal C++ that may perhaps be useful:

Linker script (similar to the one in bbb-asm-demo but with some stuff related to C/C++):
demo.ld (1.7 KB)

Assembly file dealing with early cpu initialization and cpu exceptions:
start.S (4.0 KB)
and the asm/defs.h it includes:
defs.h (2.1 KB)

Since the start.S doesn’t zeroize bss nor invoke initializers, those things need to be done first in (adapted from my own codebase, hopefully without introducing mistakes):

#include <stdint.h>
#include <string.h>

extern "C" {

extern uint8_t __bss_start[];
extern uint8_t __bss_end[];

extern void (*__init_array_start[])();
extern void (*__init_array_end[])();


void main()
	// bootROM may leave ethernet DMA to OCMC enabled.  disable it before
	// zeroizing our bss in case they overlap.
	if( ( *(uint32_t volatile *)0x44e'00'014 & 0x3'00'00 ) == 0 )
		*(uint32_t volatile *)0x4a1'00'81c = 1;

	// note: I've put stack in an uninitialized section outside bss,
	// which is why this should be safe to do here in C.
	memset( __bss_start, 0, __bss_end - __bss_start );
	asm( "" ::: "memory" );

	// run initializers
	for( auto i = __init_array_start; i != __init_array_end; ++i )

	// when main returns, it enters an infinite wfi loop in start.S hence
	// the app becomes purely interrupt-driven.

I don’t even remember when I wrote that bit about Ethernet DMA, but it sounds like something I found out the hard way while using TFTP boot :wink:

Do be aware this codebase doesn’t use toolchain-provided startfiles nor dynamic memory allocation or really any library code other than basic utility functions like memset. I’m sure it’s possible to use more of the standard C library (newlib) but I don’t really know much about how exactly that would integrate or what steps are needed.

Here’s also some code for MMU/cache setup, which is generally one of the first things you’ll want to do: (5.0 KB)

Also, beware that TI’s “StarterWare” sdk for baremetal AM335x development contains some truly terrible code, e.g. last time I looked at it its uart driver caused random data corruption and the uart example compiled to a deadloop if any optimization was enabled. I would not have a great deal of confidence in the rest of that codebase, it appears to have been written by people who lack the knowledge and experience necessary to write reliable baremetal code. Perhaps some stuff in it is still useful, just… be warned.


I have been trying to make this work with arm-none-eabi-gcc but, I am unable to see any leds blinking on the board if I copy the MLO file into the SDCard.

I tried using your code to format the SDCard but it seems that I am missing something. Could you please help me with this?


Well my code doesn’t blink any leds, it toggles some leds whenever you press the S2 button (the one closest to the card slot).

Also, beware that the AM335x bootrom on the BBB by default prefers to boot from eMMC over booting from SD card. You can force it to ignore eMMC and boot from SD card by holding down the S2 button while connecting power to the board (it’s only sampled at power-on, the setting persists until next power-cycle).

Wiping eMMC (or at least wiping the bootloader from eMMC) will likewise cause bootrom to load the MLO from SD card instead.