26. February 2023 · Categories: Embedded

The nRF52840 from Nordic Semi has an issue with SPIM3, in that using it with a shared memory buffer could lead to data corruption (Erratum 198). The description describes a simultaneous access from CPU and DMA on the same RAM block as the cause, but after some testing I believe the explanation to be insufficient. The problem seems to only occur with the first byte transmitted, and then it is not corrupted, it is not loaded at all and the SPIM uses the last byte from the last transaction instead. My theory is that the DMA needs to feed two bytes at the start, and that it does not wait for the first transaction to finish before starting the DMA clock. So the first load has no tolerance to retrieve its data before the read for the second byte is issued, which also aborts the first read. On the other hand SPIM0-2 seem to wait: only after the first byte has been read will the module start with transmission, and the DMA start issuing reads for the following bytes. For all following bytes I assume that the read has, on all SPIM, at least half the byte time to get filled.

The best support for this thesis is that when we arrange for SPIM0 to SPIM2 to start at the same time, we see that with contention the times differ, while without they line up nicely.

It looks like it is only the CPU that can lead to corruption, at least a test with the 3 other SPIM could not cause an issue. This is surprising given that all other AHB Masters have higher priority than SPIM3, and so could cause a critical delay. Peripherals however work at 1/4th the speed of the CPU and RAM, and so I guess it needs more than four masters to saturate the bus.

09. May 2022 · Categories: Embedded

The gcc linker sensibly checks that all files use the same calling convention to prevent problems, but it provides an awful error message that left me confused. When I updated a Cortex‑M project to use floating point math (-float-abi=hard), the linker gave the following error

mylib.o uses VFP register arguments, build/program.elf does not

It sounds like I have to update some setting to tell the linker which calling convention to use for the output, but what it actually means is slightly different: When the first object file is added, its calling convention becomes the calling convention of the output, and when any following files do not match, it is an error, and you see that message.

The most likely cause is that your build system did not rebuild everything, and a full rebuild will fix it. If not, you will need to check your libraries, and then the build options for your object files to see why they were built with a different calling convention. Make sure you put an object file with the correct convention at the start.

08. November 2021 · Categories: Embedded

When using maths on a Cortex-M project, I ran into the problem that the linker would not find the pow function, which should be available. It turned out that the make files I was using were not correctly configured, and I got a basic lesson in diagnosing linker problems.

First is to get some diagnostics to figure out the problem. These are basically the --verbose option to get info about the libraries being loaded, and then --trace-symbol=mysym asks the linker to tell you when a symbol is introduced. Then you can use nm -gC lib.a to figure out whether the symbol is actually defined. If you use gcc to invoke the linker, remember that you need to forward linker options, e.g., -Wl,--verbose.

Now if the symbol is defined, but still not found, the culprit could be the link order. The linker keeps a table of defined elements, and required references. When an object file is found, all exported elements are added to its defined list. But when a library is found, only elements that are already a required reference are added, which is then repeated within the same library to resolve dependencies. This means that libraries must be always behind the object files, and if they have dependencies, must be ordered highest abstraction level first. You can use --start-group archives --end-group to group multiple archives so that dependencies between them are recursively resolved, but it costs performance. With --undefined= and --require-defined=, you can force including symbols from libraries. It would help with resolving dependencies in one pass, but it is brittle and should be avoided.