WestfW
Posts: 119
Joined: Tue Nov 01, 2011 9:56 pm

ROM functions multi-processor aware?

Thu Sep 16, 2021 11:46 pm

Are the ROM function (floating point, etc) all fully reentrant and multi-processor aware?

I just noticed that the Arduino MBed core doesn't seem to be using the ROM functions for floating point, and I don't know whether that's a bug, an oversight, or intentional...

https://github.com/arduino/ArduinoCore-mbed/issues/325

User avatar
triss64738
Posts: 63
Joined: Wed Jun 16, 2021 5:13 pm
Location: masto/fedi: sys64738@hellsite.site
Contact: Website

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 12:14 am

How do you mean "reentrant"? They're called using SVC instructions, commonly called software interrupts, though they don't really behave like hardware interrupts, as they cannot be interrupted themselves from an outside source, only by another SVC instruction. And even then they act mostly like regular function calls instead of interrupts, except with a magic piece of code with a lookup table deciding what the actual called code is, instead of a branch target address or so. When these ROM functions are finished, they will return to the code of the correct core.

EDIT: also, the Pico SDK does provide the __aeabi_* symbols, so you'd have to look at the linker map output to see where they're actually coming from.

EDIT2: example from my project:

nm output:

Code: Select all

2000c760 t __aeabi_bits_init
2000cd2a t __aeabi_dfcmple_guts
2000ce70 T __aeabi_double_init
2000d630 T __aeabi_float_init
2000d588 t __aeabi_i2f_main
20010d48 W __aeabi_idiv0
20010d48 W __aeabi_ldiv0
2000d6d8 T __aeabi_mem_init
Linker map:

Code: Select all

/usr/lib/gcc/arm-none-eabi/9.3.0/thumb/v6-m/nofp/libgcc.a(_dvmd_tls.o)
                              CMakeFiles/project.dir/opt/pico-sdk/pico-sdk/src/rp2_common/pico_divider/divider.S.obj (__aeabi_idiv0)
[...]
 .text          0x0000000020010d48        0x4 /usr/lib/gcc/arm-none-eabi/9.3.0/thumb/v6-m/nofp/libgcc.a(_dvmd_tls.o)
                0x0000000020010d48                __aeabi_ldiv0
                0x0000000020010d48                __aeabi_idiv0
It is actually forwarding integer division to the Pico SDK code that uses the hardware divider.

EDIT3: I've also added the cross-reference table (as it doesn't output it by default):

Code: Select all

__aeabi_idiv0                                     /usr/lib/gcc/arm-none-eabi/9.3.0/thumb/v6-m/nofp/libgcc.a(_dvmd_tls.o)
                                                  CMakeFiles/project.dir/opt/pico-sdk/pico-sdk/src/rp2_common/pico_divider/divider.S.obj

[...]

__wrap___aeabi_idiv                               CMakeFiles/project.dir/opt/pico-sdk/pico-sdk/src/rp2_common/pico_divider/divider.S.obj
                                                  CMakeFiles/project.dir/opt/pico-sdk/pico-sdk/src/rp2_common/pico_printf/printf.c.obj
                                                  CMakeFiles/project.dir/CMSIS-DAP/Firmware/Source/DAP.c.obj
                                                  [ and so on ]

WestfW
Posts: 119
Joined: Tue Nov 01, 2011 9:56 pm

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 7:56 am

They're called using SVC instructions
huh? The SDK-based compiles "wrap" the float functions in __WRAP____dadd() type names that use a lookup table derived from the ROM somehow, and the Arduino MBED code seems to use ____aeabi_dadd_veneer() names that end up calling functions that have been moved to RAM (somehow.)

I don't see any SVC calls at all, and the vector table contains a "BKPT" for the SVC vector (at least, in the .elf)
Using an uninteruptable SVC for float functions would be a bad thing.

nm output:

2000c760 t __aeabi_bits_init
2000cd2a t __aeabi_dfcmple_guts
your float functions are also in RAM.

If I compile one of the sdk examples (say adc_console), I see:

Code: Select all

arm-none-eabi-nm adc_console.elf|grep dadd
10003908 T __wrap___aeabi_dadd
10003b9c T dadd_shim

WestfW
Posts: 119
Joined: Tue Nov 01, 2011 9:56 pm

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 8:12 am

Is there a description somewhere of just (or approximately) how the SDK implements using the ROM functions instead of standard gcc functions for floating point and etc?

I think it goes something like:
  1. during linking, references to the replaceable functions are replaced with calls to __WRAP__functionName.
  2. at startup, code reads tables of function pointers into RAM for stuff like floating point.
  3. The __WRAP__functionName functions either call the ROM via the pre-loaded function pointers (for floating point?) or use a separate single-function lookup mechanism to find the address and call it.
But there's a lot of guessing there, and the source code seems pretty obscured by asm and c macros and such...

There was an example in the "double palindrome" discussion:

Code: Select all

typedef uint32_t (*uu_func)(uint32_t);
uu_func faster_rev32, faster_clz32;
    :
  // get function pointers to ROM rev32 and clz32 functions
  faster_rev32 = (uu_func)rom_func_lookup(rom_table_code('R', '3'));
    :
  unsigned int r = faster_rev32(n);

User avatar
triss64738
Posts: 63
Joined: Wed Jun 16, 2021 5:13 pm
Location: masto/fedi: sys64738@hellsite.site
Contact: Website

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 12:05 pm

WestfW wrote:
Fri Sep 17, 2021 7:56 am
They're called using SVC instructions
huh? The SDK-based compiles "wrap" the float functions in __WRAP____dadd() type names that use a lookup table derived from the ROM somehow, and the Arduino MBED code seems to use ____aeabi_dadd_veneer() names that end up calling functions that have been moved to RAM (somehow.)

I don't see any SVC calls at all, and the vector table contains a "BKPT" for the SVC vector (at least, in the .elf)
Using an uninteruptable SVC for float functions would be a bad thing.
You're right, I was mixing up things with boot ROM functions from another device. Though, I'm not really sure what "reentrant" is supposed to mean in this context if interrupts aren't at play at all.

But, let's have a look inside a compiled binary:

The operator '%' gets replaced with a call to __aeabi_uidivmod in an object file, which gets wrapped at link time:

object file:

Code: Select all

mov r0, r4
mov r1, r5
bl __aeabi_uidivmod
final output executable:

Code: Select all

mov r0, r4
mov r1, r5
bl __wrap___aeabi_uidiv
In this binary, the __wrap___aeabi_uidiv symbol has the same address as 3 other symbols: __wrap___aeabi_uidivmod, div_u32u32, and divmod_u32u32. The latter two are SDK functions: source.

But how do the aeabi functions get redirected to their wrappers? Well, here's the definition of wrapper_func, which simply ends up defining a thumb function with a prefix in the name. So there must be something else going on.

pico_dividers also uses some interesting-looking CMake functions. It is defined here, and simply passes a "--wrap" argument to the linker. The manpage of 'ld' on my system says the following about this option:
Use a wrapper function for symbol. Any undefined reference to
symbol will be resolved to "__wrap_symbol". Any undefined
reference to "__real_symbol" will be resolved to symbol.

This can be used to provide a wrapper for a system function. The
wrapper function should be called "__wrap_symbol". If it wishes to
call the system function, it should call "__real_symbol".
This is how wrapper functions work in general (including divisions and memcpy), but not all of them touch the boot ROM, including the example I gave here.

At startup, functions like this one get executed, that, if available, load a number of functions from a ROM subroutine table. These are then used by the functions here. The latter functions are in turn used by the code here, which provides a number of these __wrap___aeabi_* functions, and calls the functions calling into the boot ROM, if those functions are available.

EDIT: if the Arduino MBed linker flags do not end up having these --wrap=funcname options, then they will use the standard soft-float implementations from libgcc.

P.S.:
your float functions are also in RAM.
I was compiling with -DPICO_NO_FLASH=On, so that's to be expected.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 29644
Joined: Sat Jul 30, 2011 7:41 pm

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 12:37 pm

WestfW wrote:
Thu Sep 16, 2021 11:46 pm
Are the ROM function (floating point, etc) all fully reentrant and multi-processor aware?

I just noticed that the Arduino MBed core doesn't seem to be using the ROM functions for floating point, and I don't know whether that's a bug, an oversight, or intentional...

https://github.com/arduino/ArduinoCore-mbed/issues/325
If you think about it, the ROM function are simply code, but in ROM, so unless you are using them from interrupts, each core will simply run the code on its own set of registers. So I would assume they are safe to be used from both cores at the same time, as long as they are not talking to the same memory, where you would, I hope, be using mutexes anyway.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Working in the Applications Team.

kilograham
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 965
Joined: Fri Apr 12, 2019 11:00 am
Location: austin tx

Re: ROM functions multi-processor aware?

Fri Sep 17, 2021 1:37 pm

WestfW wrote:
Thu Sep 16, 2021 11:46 pm
Are the ROM function (floating point, etc) all fully reentrant and multi-processor aware?

I just noticed that the Arduino MBed core doesn't seem to be using the ROM functions for floating point, and I don't know whether that's a bug, an oversight, or intentional...

https://github.com/arduino/ArduinoCore-mbed/issues/325
All of the ROM functions are reentraant / multicore safe (not particularly aware).

I'm guessing that this is an oversight in the Mbed case, They are probably missing out both on the floatiing point/double stuff as well as much faster integer division.

Return to “SDK”