How to link write_volatile core library function to ram

In low level boot loader development, its normal to keep functions at load address from FLASH memory & with vMA at RAM & write init functions that would copy the functions into RAM and execute.

In one such function (one example here) below, though the function is linked to RAM & loaded at run time from flash, the embeddes sytems crashes. While debugging find that write_volatile internal calls (into core library) are mapped to flash sections.

Is there a way to map all the internal calls (into core library too) to .ramfunc ? t trying to explore any rust specific flags that can do this. I can't map the entire core library into RAM section.

The other option for me is to re-write this function without using any core library functions.

#[link_section = ".ramfunc"]
    #[inline(never)]
    pub fn en_flash_controller (&self)
    {
        unsafe {
            //en flash controller
            let qspi0_fctrol  = (self.base as *mut u32).byte_add(REG_FCTRL);
            write_volatile(qspi0_fctrol, 1);
        }
    }

can you show your linker script?

correct me if I understand it wrong, but I think your approach is the opposite to how this stuff is commonly done: you kept regular code at rom address, and a special section .ramfunc is used to put certain code at ram address?

it is usually the other way around: you put all "normal" runtime code at it's final target address, while make the initialization code a special case, i.e. all .text.* goes to the ram address, while a dedicated .init section goes to the boot rom address.

these functions perform flash operations like sector erase, flash erase and can't be run from flash (within itself).

oh, I misunderstood the situation then.

I thought your main application code were running in the ram, only the init section were running directly from flash and it simply relocated the application to the ram, sort of like a bootloader.

but in reality, the majority of the code executes in-place from flash, only a small subset of functions (which I would refer to as the flash "driver" module) needs to be run in ram.

this is indeed a tricker situation, and my previous reply doesn't apply.

your application is essentially splitted into isolated execution domains, and the current toolchain doesn't directly support this use case. it's kind of like your application has two "address spaces", but not really.

I've no personal experience for such scenarios, but I would layout the approach how I would tackle the problem personally. just a disclaimer, this is 100% hypothetical, I don't know the details, or if it will work at all. you need to figure the details out yourself, for example, by asking a chatbot.

first, I would build a separate module for the flash driver that must runs in ram, this binary should be completely self-contained without external dependencies, and it uses a different linker script from the main application, so the symbols are placed at the ram address.

then for the main application, I would include the flash driver as a binary blob, and memcpy it to the correct ram address when initialization. I would probably strip the flash driver binary to reduce its size before embedding it into the main application.

alternatively, I can extract the loadable sections from the (fully linked, and self-contained) flash driver module, and include the sections instead of the the whole binary.

now here's the trick that makes this all work, i.e. how the symbols exposed by the flash driver are resolved? answer is to use the symbol table of the linked driver binary!

$ ld $(objects) --just-symbols=my_flash_dirver.elf -T my_app.ld -o my_app.elf

the --just-symbols command line option is available in the GNU linker (thus also the LLVM linker, because it is compatible with the GNU linker).


I would NOT use the #[link_section] approach like what you are doing currently. the problem is, there's no reliable way to guarantee your function is "self-contained" in the same section, i.e. it does not call external functions such as those from core, since core contains methods for many "built-in" types, and many lang items, which you cannot get rid of, otherwise the language could not work.

as an example, I would assume the write_volatile() function, as you mentioned, should normally be lowered into a single LLVM store primitive, but in debug builds, the "shim" function in core may not be inlined, and the ub_check assertions may not be elided, so you end up calling an external function instead, and it's completely out of your control.

1 Like

Seems like a clean solution; though never used linker flag --just-symbols.

Trying to add more details: How the init function know correct where-to--copy from flash address & where-to-copy-into ram address?

the seperate linker used for flash driver executable would have LMA as fixed flash address & VMA as fixed ram address as continuous memory chunk which would be used to copy at init function. Right?

the module is embedded as a binary blob, so it is just data to the main application, and the from address is just the pointer to the data.

the to address however, is a little trickier. both the linker script of the driver module and the main application should agree on the address of the code section of the driver module.

I believe it could be somewhat flexible if the driver module is compiled as position independent code (the pic model, which should be default for most target), and I think it might also be necessary for it to only conain a code section (i.e. no reference to static data or bss section).

since a bare metal target doesn't have a an ELF loader (unless you are willing to include one in your application), and you includes the blob as data, LMA doesn't matter, only VMA is relevant. and the VMA of the section is the to address where you should copy the module.

you also need to make sure the address is not occupied by "normal" data/bss/stack sections in the main app's linker script.