Hacker News new | past | comments | ask | show | jobs | submit login
Bare Metal Programming on Raspberry Pi 3 (github.com/bztsrc)
142 points by oumua_don17 25 days ago | hide | past | favorite | 29 comments

> Rust is a much higher level language than preferable for bare metal, something like with C++. But if you provide the required runtime libraries, you can do it.

What does he mean by this? Rust has no-std mode, you can run it on an ARM M0 if you so desire.

Yes, this is not a good reason. The Pi 3 is significantly larger than the chips I'm writing bare-metal Rust on daily.

That said, nobody is obligated to use Rust. He should write his tutorial in whatever he wants. This isn't an actual blocker, though.

Just want to say that I enjoy your blog and would like to hear more about the bare-metal Rust you're writing.

Thank you! It's part of my job at Oxide Computer; it will all be open source eventually, but isn't quite ready for it yet.

Some people have romantic notions about how bare-metal programming should be difficult, or done in some traditional, artisanal fashion.

Most of these people have never built a modern bare-metal product; they're either stuck in the 90s, or they're dilettantes.

I have a lot of sympathy for that if you're doing it for fun.

It's like buying a romantic cabin in the woods to be cut off from society to write a novel.

But then you learn it gets mobile reception these days and your friend asks you why you haven't installed a Powerwall and solar yet?

If you're attempting to do it commercially, that's a different thing.

I think that there has been a sea-change in computer education over the years. It has become more and more high-level built on abstraction.

Additionally, computers have become both locked down and "civilized".

Say you had an apple computer in the woz era - there's a good chance you had to deal with signals and hardware along with having a bootrom instead of an operating system.

Nowadays, apart from something like the raspberry pi, there's a good chance people have never done assembly language programming, and have never been able to interface with the machine or external hardware.

too bad, actually.

> there's a good chance people have ... never been able to interface with the machine or external hardware.

I agree that the trend is to go up and not down in what abstraction regards, but I also think that embedded programming was never so popular like it is today. Like... you can do it with a budget under 20 USD and a couple of tutorials.

You don't necessarily need to do assembler to interact with hardware. You can do it with C/C++/Rust/etc., and it's as easy as (for example) writing into some microcontroller register.

Unless you are talking about something else...

> You don't necessarily need to do assembler to interact with hardware.

It's too bad because people don't know how things really work underneath.

The second computer course I took was assembly language, and it was a formative experience. I have carried that mental model around with me for all my life.

The first time I touched hardware was in an EE course where unfortunately I didn't really delve into hardware like I should. I remember my partner was an EE and I was computer science and we each did what we did best. Looking back I we should have switched - he should have done the software and I should have done the hardware. I think I would have had better mental models from the course. And until I did stuff like a pi all these years later I treated hardware kind of like mysterious magic in my head.

Seriously, all this should be treated as learning to swim. I've known people who didn't know how to swim and for them there's an insurmountable wall erected in boats and at the beach that could be fixed with a day or two of lessons.

What would be some good examples of modern bare-metal products?

Not quite a consumer product but this was recently on HN: https://www.bunniestudios.com/blog/?p=5921

I worked on some echosounders a few years ago that were bare metal, and before that some power management equipment.

Though if I was doing it again I'd probably have a bare-metal chip running the timing critical, and throw a cheap arm core on there running linux just to make life easier.

And I'm pretty sure I could probably do away with the bare-metal and just pin some userspace code to a core and it'd work just as well.

Edit: *'days' -> 'years'

Frankly I'd like examples of actually-documented bare metal that you can actually buy.

Not sure exactly what he means by that but he does link to another effort that implements everything using Rust: https://github.com/rust-embedded/rust-raspberrypi-OS-tutoria...

It's pretty easy to understand, Rust is huge. You can't build it on a Pi 3, it'll run out of RAM if you try to compile it.

If you're running bare metal you aren't going to be running your compiler (let alone building it) on the target regardless of the language.

Your compiler runs on a separate development machine. Looking at the repo, he's already building an LLVM-based cross-compiler for C anyway.

Forth, Lisp, Smalltalk would beg to differ.

The project creator makes the following statements regarding 64-bit capabilities:

"Second, it's a 64 bit machine. I gave up programming for 32 bit long long time ago. The 64 bit is so much more interesting, as it's address space is increadibly huge, bigger than the storage capacity which allows us to use some interesting new solutions."

I find it odd to make this argument in the context of Raspberry Pi 3 since it only has 1GB RAM onboard. This is only 1/4 of the total space that can addressed by 32-bit addresses.

Physical RAM is completely different from the address space.

For a simple example, the control register for the UART serial port peripheral allows you to 'flip an off switch' by writing a 0 to the whole register, or to turn the UART transmit and receive functions by writing a one to the ninth and tenth bits, and to turn on the FIFO buffer by writing a 1 to the first bits.

In this code, that control register has an address defined:

    #define UART0_CR ((volatile unsigned int*)(0x3F000000+0x00201030))
And the code can interact with the peripheral as described by writing `UART0_CR = 0;` or `UART0_CR = 0x301;`.

There is no RAM storage at 4,236,263,616 bytes from the start of memory, but this peripheral exists at and handles writes to that address space. Take a look at https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-..., page 5. It's part of the address space, but not part of RAM.

Relatedly, one useful feature of some ARM processors is the bitwise section of the address space. You want to set the FIFO bit above, but you don't know if the Tx, Rx, or both is on? You need to do the equivalent of:

    temp = UART0_CR; // Either 0x100, 0x200 0x300, or 0x000
    // UART0_CR = 0x001; <- Won't work, you've deleted the Tx and Rx
    temp = temp | 0x001; // Bitwise OR sets last bit only
    UART0_CR = temp;
which takes 3 cycles and a scratch register. Instead, on at least some Cortex M microcontrollers, there's a copy of UART0_CR (and all the other peripheral registers) expanded in a part of the address space where each word is just one bit of the nominal register, so you can write in a single cycle without a scratch register.

Re. bit-banding (the "expanded" form of a register) -- I don't think that's available outside Cortex-M3/M4. It isn't even available on Cortex-M7, as it interacts poorly with caching.

Cool! Thanks for the detailed explanation and example.

Probably more thinking about mapping large files to virtual address spaces and treating them like memory. Try to map a 50GB file into memory on a 32 bit system and you run into complications that you don't have on a 64 bit system.

And the system that allows that, is the system that allows for paging of unused memory in/out. Once you have virtual memory, you can do some really interesting things. But 32bit of VM is hugely constrained for a lot of things. Then again, having 2x the size of a pointer is a cost that must be accounted for too.

It's about the address space size, not the available memory. For example you can't really have large stacks with 32-bit because if you spin up a few thousand threads you run out of address space, even if you don't run out of physical memory. Not a problem on 64 bit.

It's not really bare-metal, given that a ThreadX OS controls booting of the raspberry pi and continues to run afterwards


Side note: Microsoft bought ThreadX in 2019.

Interestingly, it seems to have been renamed to "Azure RTOS ThreadX". The source is on GitHub:


Oh, turns out I can't write bare metal software in C++, I should stop doing this probably.

Thank you.

UART using polling[0] instead of interrupts, i.e. sending a character and waiting in a busy loop until it gets sent, isn't it quite wasteful?

[0] https://github.com/bztsrc/raspi3-tutorial/blob/master/03_uar...

Depends on your goal. A lot of the time you want your printfs to be blocking on embedded systems, even if literally the rest of the system is asynchronous so that you don't miss the interesting prints right before the crash because they were queued and not sent yet.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact