Bare metal from scratch, help? :)

I need a professional help… um, with bare metal Pokitto programming from scratch. Cause you know, one day I’d like to make my relaxed one file library without any proprietary code. But I’m a noob. I’ll dump my questions here and if someone knows answers, just randomly drop them here maybe :slight_smile:

So I have this:

  • main.c: Program I’d like to compile that would init the HW and just e.g. draw a pixel on the screen. Once I have the option of outputting stuff, I think I can work it out from there.
  • link.ln: Linker script cause memory needs to be like LPC wants it.
  • a make script which really just does this:

(project now lives here)

arm-none-eabi-gcc -std=gnu99 -c -Wall -Wextra -fno-builtin -funsigned-char -fdata-sections -MMD -mcpu=cortex-m0plus -mthumb -o main.o main.c
arm-none-eabi-ld -Tlink.ld -o main.elf main.o
arm-none-eabi-objcopy -O binary main.elf main.bin

This works! Well at least the compilation. For reference I have:

Question: can I achieve what I want without any assembly? I’d like it to be in just one language: C. Looks like I can.

At the beginning of the memory is mapped the flash, mkay, I suppose the bin will be copied here AS IS, somewhere near the end of this section I am expecting to come over the loader.

I can see there is “main” RAM from 0x10000000 to 0x10008000… OK… but there is yet ANOTHER RAM at 0x20004000 to 0x20004800. Now I suppose this is the RAM we use for the stack? And the other one is for global variables etc.? I need to enable the clock for the “other RAM” so that it works? :neutral_face:

Question: what bare minimum steps exactly do I need to do to init our HW? My ideas is I am supposed to create the interrupt vector at the beginning of the flash, put the initial SP at the first address and then a pointer to the reset interrupt to my init routine (can it all work with just this one interrupt?), in which I need to do something like copy memory segments and maybe enable clock for RAM etc., enable GPIO and send some bits to init the display and then I can call the main function and write to display?

So now I need to set up this boi: link.ln. I’ve never done it. Deep buried in Pokittolib there is this proprietary boi:

PokittoLib/blob/master/Pokitto/mbed-pokitto/targets/cmsis/TARGET_NXP/TARGET_LPC11U6X/TOOLCHAIN_GCC_ARM/TARGET_LPC11U68/LPC11U68.ld

Hey, it’s pretty complex there, half of the stuff I can’t understand. Will it compute with something very simple like:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x40000
  RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 0x8000
  RAM2 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x0800
}

ENTRY(start)

SECTIONS
{
  . = 0x00000000;

  .text :
  {
    *(vectorTable)
    *(.text)
  }

  ROM_START = .;

  .rodata :
  {
    *(.rodata)
  }

  ROM_END = .;

  . = 0x10000000;

  RAM_START = .;

  .data :
  {
    *(.data)
  }

  RAM_END = .;

  BSS_START = .;

  .bss :
  {
    *(.bss)
  }

  BSS_END = .;
}

Then I have my program main.c. The vector I create like this?

uint32_t *vector[2] __attribute__((section("vectorTable")))=
{
 (uint32_t *) 0x20000000, // <-- initial stack pointer value? Should it point to "RAM 2"?
 (uint32_t *) start // <-- reset interrupt handler?
};

Can this work? Then I’ll have my main function and the start function, in which I init everything and call main. Now in Pokittolib this is a separate assembly file, but in the web example I’ve seen it just in C.

Now I’m seeing they’re setting some bits of LPC_SYSAHBCLKCTRL to enable clock for various modules. Can I do this in C e.g. like this?

*((uint32_t *) 0x40048080) |= (1 << 1) | (1 << 2) | (1 << 6) | (1 << 16) | (1 << 26);

Then I’ve seen I should fill the .bss segment with 0s and copy the .data segment (initialized vars) to RAM.

Ok this is about as far as I’ve made it so far, I’ll be updating this post if I am making any progress, maybe one day it can help someone or something. Thanks for any help with this xD

5 Likes

Sure you can, but C will produce a larger binary than the code in minilib.

If all you want to do is blink an LED, you really don’t need much code at all. For anything non-trivial, you’ll need pretty much everything that is in the minilib. Might as well just get that and translate it to C, if having pure C is really that important to you.

No, the RAM at 0x10000000 is the stack and global variables, as per the default linker script. The chip doesn’t like having the stack in RAM2 or USBRAM, presumably because they’re disabled on boot-up.

You’ll need a full table, but you can have a single handler for multiple interrupts. Also, the chip won’t boot if the checksum isn’t right.

Pretty much, yes.

3 Likes

Thank you very much @FManga :slight_smile:

Partly this and I also want to learn and understand what it actually does, not just blindly reformat assembly instructions.

Aaah I see, it’s right there in the manual on the page 428. I’d have never figured this out myself, thanks :slight_smile:

2 Likes

I’d also be interested in this since one of the biggest things preventing me contributing to improving the Pokitto Lib or writing an alternative to some of the Pokitto Lib’s facilities is lack of knowledge about the underlying system.

(Especially the screen, boot procedures, and the memory map.)


The thing about the checksum reminds me of how the Game Boy Advance wouldn’t boot if the cartridge didn’t have the correct Nintendo logo in place.

1 Like

Great, we can learn together @Pharap, post any relevant stuff here. I’ll try to just blink the LED as @FManga pointed out.

Would it be possible to program Arduino devices without the Arduino compiler this way? I mean it’s an open platform, there should be nothing preventing it, right? I’d be mainly interested in it because I hate the infamous PROGMEM.

I’ll probably mostly lurk unless I have extra time to divert.
Mainly I wanted to make it known that this would be useful to other people too.

By ‘the Arduino compiler’ do you mean the Arduino IDE or GCC?

The Arduino IDE actually defers to AVR GCC under the hood.

There is also Arduino CLI which probably isn’t as bare-bones as you want but at least lets you compile Arduino stuff from the command line instead of using the ghastly ‘IDE’.

Assuming you’re definitely refering to Arduino and AVR (which seems to be a bit of a non-sequitur considering the Pokitto doesn’t have to deal with a PROGMEM macro)…

PROGMEM is defined as __ATTR_PROGMEM__, and __ATTR_PROGMEM__ is defined as __attribute__((__progmem__)) and hence is actually a GCC attribute.

That part is actually from avr-libc, an implementation of the standard C library for AVR, which uses a BSD-3 licence and can be found here:
https://www.nongnu.org/avr-libc/user-manual/index.html

Do you know why PROGMEM is needed in the first place, or should I explain that while I’m at it?


Just because this is bothering me:
Even in C you’d be better off making a named variable instead of resorting to unnamed magic numbers.

uint32_t * const explainWhatThisAddressActuallyMeans = (uint32_t *)0x40048080;

*explainWhatThisAddressActuallyMeans |= (1 << 1) | (1 << 2) | (1 << 6) | (1 << 16) | (1 << 26);

(And while you’re at it you could make an enum for all the flags so nobody is left guessing what they mean.)

1 Like

Hmmm indeed, it seems it’s an AVR issue (yes, it’s an issue). From what I’m reading their CPUs are Harvard architecture, so that’s pretty bad. Never do Harvard architecture kids.

You know what I think about enums. :roll_eyes: But comments there will be.

It might make the code ugly but it’s easy enough to work around by just defining the PROGMEM macro to be empty and defining pgm_read_byte to be a normal unsigned char pointer dereference.

Technically ‘modified’ Harvard because ‘progmem’ can store data as well as instructions.

For those playing along at home:
An AVR chip has a different instruction for reading from ‘progmem’ because it doesn’t have a unified address space.

The Pokitto doesn’t have this problem because it’s memory mapped such that its flash memory and RAM can be accessed via the same address bus.

No, I don’t. That’s genuinely a new one on me.

Best not go into detail though, things will get further offtopic.

1 Like

It’s typical of 8-bit CPUs to have weird workarounds for the limited address space and, compared to switching out memory banks, AVR’s progmem is practically a feature.

2 Likes

@drummyfish I think ancient AVR 8-bit architecture (PROGMEM) is not worth targeting. Newer Arduinos are also ARM Cortex which makes your job much simpler and easier

1 Like

Sure, but that will make it stored in RAM, sometimes I do need it in flash to save RAM. I had this issue when porting Anarch to Gamebuino – levels and images need to be stored in flash as they’re big. But I want to be able to access these data via normal pointers as other data, because otherwise I have to create pretty ugly macros and when you’re e.g. storing a pointer in progmem that points to ram which them points back to progmem, it gets really disgusting. And then always when you’re accessing some general data you need to know in which memory it is stored, which is not always easy because you may e.g. change your data structures in the middle of the project and some is suddenly moved to progmem while your whole code is treating it as if it’s in RAM, so you have to manually go over the code again and fix all this access. It’s really really bad.

Hmm it actually seems you’re correct as I am reading on Wikipedia :open_mouth: They say most Arduinos use 8bit AVRs, but 32 bits seems to be ARM based, even though Atmel creates 32 AVRs too.

So this means I do not have to use PROGMEM on 32 bit Arduinos and static const will make sure the stuff ends up in flash? Please say yes.

Yes. PROGMEM is a vestige of times when microcontrollers lived in trees.

You should not use any “Arduino” to target the Arduino Zero / Leonardo whatever. They are just normal ARM Cortex processors. The port addresses are a bit different between different makes (Atmel / NXP / STM32) but the cores are very similar. Use pure C and an abstraction layer to define the relevant addresses / relevant peripherals for different processors. That is what mbed/Arduino actually do under the hood.

1 Like

I meant on non-AVR devices.

I was under the impression that making something const or constexpr is enough for it to be stored in flash on ARM.

Or does it not quite work like that?

If you were in C++-land I’d have a very nice solution for you involving a datatype that exactly matches pointer syntax by overloading various operators.

With it you could do:

auto pointer = make_progmem_pointer(&data);

And then *pointer would read the data structure out of progmem and ++pointer, --pointer and pointer[index] would work exactly as expected.

In C-land, no such luck - you have to resort to big ugly macros and functions.

The crux of the issue is that because reading from a RAM pointer and reading from a progmem pointer require a different instruction on AVR, the compiler would need some way to be able to differentiate between the two in order to know which instruction to generate.

The best way to do that without it being built into the language is to introduce a new datatype for progmem pointers, but unfortunately C doesn’t offer the required level of features for that, and in fact only a few languages do.

At a minimum you need generics to be able to offer a progmem pointer for all types, and the ability to generate the necessary assembly code. If you want the pointer syntax then you also need operator overloading (of the correct operators).


Leonardo uses AVR (ATmega32u4), not ARM.

Things marked const go into the rodata section, which the linker script says should go into the flash, so it does work like that by default.

1 Like

Macros are not an issue, I love them, but there is still the problem I have to know from which kind of memory I am reading.

Like I said, “Leonardo whatever”, that is the extent of my interest in them :wink:

That’s exactly how I thought it worked. Thanks for confirming.

Which means that conditionally defining PROGMEM to nothing and pgm_read_byte(x) to *((const unsigned char *)x) should be enough.

The author could forget to make their ‘progmem’ items const on an ARM system, but since anything going into progmem on AVR has to be const it’s only a one-way issue, and hopefully it’s not a common one either.

You could inspect the address value at runtime and identify which part of the memory map it lies in.

That aside there’s no way to do it in a generic way at compile time without generics.

You could write some macros to generate boilerplate that creates progmem_pointer_to_x types and read_progmem_pointer_to_x functions, but actually using those would get kind of clunky.

If you limited yourself to just the built-in types you could leverage C11’s _Generic to make life a little bit easier.
(You’d have to #ifdef for C++ though because that’s outside ‘the common subset’.)

That’s why I say don’t do Harvard architecture kids. Harvard architecture: not even once.

Or, y’know, just use a language that can deal with it. :P
(E.g. Ada, C++, D, Nim, Rust, Zig, (Free) Pascal, et cetera)

I am now confused by the “boot ROM” section. They say there is a bootloader (plus some API), so there’s a code from the manufacturer in there which handles the vector table checksum verification etc.? I read that the API is e.g. for accessing EEPROM, but can I access it even without it (writing my own “driver”)?