Zig on small SoC (256KB)?

I think I made a mistake. I wanted LoRa support for my Zig based PinePhone bare-metal OS project, but they have discontinued the LoRa back-plate :frowning: So I’ve ordered a nRF52840 based gadget, but just realised it only has 256KB RAM, gulp!

Has anyone targeted Zig on such small SoCs here? any advice/tips please?

I’m still on ‘skinny Zig’ aka 0.15.2, and it looks like an Arm M4 32bit SoC, but I also saw something about thumb. So it would be especially good to know what my .target might look like please. I see both M4 and thumb listed in Zig targets, but there seem to be a few options there.

I can strip fonts and framebuffers from my OS (already removed std lib), but MMU is 64KB before I start. 256KB feels more like a serial-buffer compared to the 2GB RAM I have now :slight_smile:

I also currently use .bin via objcpy as by image to boot, but from what I can see this chip is using uf2, is that something I can target directly from Zig?

3 Likes

There is the awesome microzig project by the ZigEmbeddedGroup, which should support many targets (as far as I know, it also supports the Arm Cortex M4).

And then, the rule of thumb (pun intended) is to ALWAYS build in ReleaseSmall modes. \

I never did things on a chip with 256KB of RAM, but I did do things on the Raspberry Pi Pico 2 (which has 520KB of ram, “only” a little bit more than double that).
So it is possible.

6 Likes

I have a really simple application written in Zig running on an MSP432P401R (Cortex-M4 with 64KB RAM, 256KB Flash). If you’re building in Debug mode, your program probably won’t fit in flash. Thanks to others on this forum, I’ve found a decent starting set of options to better control the output file size.

Some module options to enable to help reduce the size of your program:

  • .strip = true
  • .sanitize_c = .trap (can also disable completely with .off)
  • .valgrind = false

Some linker options to help reduce size:

  • link_gc_sections = true
  • link_function_sections = true
  • link_data_sections = true
  • lto = .full

With these, I was able to get the size down enough to run in Debug mode.

I’m not sure if you’re running an RTOS or Linux. An RTOS would run on these little MCUs, but getting Linux to run on them might be difficult. I’ve never seen it done, personally.

For my project, I use openocd to write an ELF, but have also used a BIN from objcopy. Never used uf2 though, it doesn’t look like the build system supports that currently.

1 Like

did you mean true? I would have expected it to be bigger without stripping symbols.
Or does it make some later linker steps work better?

Oops, yes. Thanks for catching that. I Updated my comment.

I second the recommendation for MicroZig. I was playing with two small boards, micro:bit and Raspberry Pico W, and in both cases the integration was excellent. No messing around with the build system.

1 Like

Great, that gives me hope! I see mention of the SoC and the UF2 format there, I shall investigate more. Thanks!

Thanks, that looks like a really useful list. It’s my own OS I’ve been working on, mainly just a hardware extraction layer and the start of an app/UI layer, so I should hopefully be able to strip out quite a lot.

edit: Oh and I hadnt even considered that the code could run from flash, so that gives quite some breathing space too :slight_smile:

Thanks, I knew Zig was targeting the C world, but had no idea it was already capable of targeting such small SoCs, nice!

After the encouragement yesterday (thanks all!), I set about with a chainsaw to my OS today. The good news is that I´m down to 85KB for .elf, and the .bin is now only 20KB!! Probably a year since my OS was that small, quite amazed, and a very positive first step.

I realised I didnt need MMU, and pacman had to go :frowning: but left serial, timers, gic, gpio, ccu and a few global assembly utilities. And of course flashing LED routines :wink:

I´m also down to a single std import, just for the panic handler. Is there an alternative signature I can use that still allows Zig to find it, but doesnt require std import please?

pub fn panic(dontPanic: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {

1 Like

The import isn’t just getting a function signature. It’s importing the implementation of the function.

That’s just how you use code that’s written in a different file.

Yes, the std import is required. But Zig is clever and compiles only the parts you need in the std lib (which is only that std.builtin.StackTrace struct).

So you NEED to use it, but it isn’t as bad as you think.

EDIT: shouldn’t have been a reply to WeeBull, but to @whitehexagon.

1 Like

No. If I’m correct, the OP made a custom panic handler, but for the function signature of his pub fn panic he needs std.builtin.StackTrace, that was what he was asking for.

Of course, that makes sense. I just took out my custom panic override & the last std import, and strangely it didnt change the output binary size. So that one can stay :slight_smile:

That’s down to the way Zig lazily includes code in your binary. If you don’t have a reference to a function in the live code-path, it’s not included.

Imports don’t change file size. Calling the function does.

1 Like

So I never call my custom panic handler, it was only there for Zig to call (so that I hopefully got a serial console message).

But since I removed the rest of std lib, I guess that too is now redundant, and hence was probably being dropped anyway from my build. Makes sense. Thanks for making me think a bit more about that. So my OS is now officially std free! as long as you only want to flash an LED or two :wink:

Yep, and you can @import "std.kitchensink" and that would also have no effect unless you made reference to it.

The zig panic handler gets also called on invalid operation, division by zero, etc.

So zig will include one anyway, might as well be yours with additional debugging capabilities.

2 Likes

So I learned about @setRuntimeSafety(true); today, since I wanted to try that, but I only ever build releaseSmall and the panic handler was not triggering. Thank-you.

Please note that stripping debug sections will reduce the size of your binary file, but not what’s flashed to your microcontroller (unless you add it via a linkerscript, but I’ve never seen a reason to, debuggers read the information from the file, not your hardware).

1 Like