STM32G0 dma can't read const data from flash in boot memory space - Page 1
Forum section: electronics / general

author: Dan N
author_rank: Contributor
timestamp: September 20, 2024, 07:19:33 pm
content: While testing some bare-metal SPI-Tx using DMA on a STM32G030, I ran into a dma transfer error for which I can't find definitive documentation on adress spaces allowed for dma read.With the default bootloader and linker setup that remaps flash to adress 00000000, const data like below will be stored in boot memory space after the code (00000244 for my example). Apparently that address range isn't allowed for dma read and will shutdown with a transfer error (no spi activity):Code:const uint16_t spidat[] = {0x1234, 0x5678, 0x9abc, 0xdef0};....DMA1_Channel1->CMAR = ( uint32_t )&spidat ;....I tried enabling SYSCFG clocks and making sure SYSCFG->CFGR1 MEM_MODE[1:0] is *0 (flash is remapped) and dma still shutsdown with a transfer error.This hack works (CMAR = 08000244), but is easily broken if the data is changed to not be "const":Code:DMA1_Channel1->CMAR = FLASH_BASE + ( uint32_t )&spidat ;

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 21, 2024, 03:17:21 am
content: Why are you remapping the flash instead leaving the default 0x8000000?

author: Siwastaja
author_rank: Super Contributor
timestamp: September 21, 2024, 10:31:10 am
content: IIRC for DMA you can't use the remapped address but need to use the actual adress. So just try orring your remapped 0-based address with 0x08000000 when writing to DMA memory address register.

author: voltsandjolts
author_rank: Unknown
timestamp: September 21, 2024, 11:04:01 am
content: Quote from: Dan N on September 20, 2024, 07:19:33 pmYou shouldn't use linker settings to remap flash, that's the job of hardware in the mcu. Linker should use the default 0x8000000, as David says.That way&spidatwill give real hardware addresses required by the dma unit, whetherspidatis in flash or ram.Also note thatkeyword 'const' by itself is not a guarantee that a variable is only stored in flash.

author: Dan N
author_rank: Contributor
timestamp: September 21, 2024, 11:52:55 am
content: The default hardware config remaps flash to 00000000 after reset (BOOT0 pin has internal pulldown active after reset). From the RM:Boot from main Flash memory: the main Flash memory is aliased in the boot memory space (0x0000 0000), but still accessible from its original memory space (0x0800 0000). In other words, the Flash memory contents can be accessed starting from address 0x0000 0000 or 0x0800 0000.I'm using this linker script:https://github.com/ataradov/mcu-starter-projects/blob/master/stm32g031/linker/stm32g031x6.ldCode:MEMORY{flash (rx) : ORIGIN = 0x00000000, LENGTH = 32Kram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K}I'll try changing the linker map.I still haven't found docs that explicitly say dma can't read from boot memory space. Is it the address or the 16-bit read that triggers the transfer error?

author: voltsandjolts
author_rank: Unknown
timestamp: September 21, 2024, 12:32:57 pm
content: DMA issues are a PITA to debug. Maybe try a more standard config and transfer first, just to get something working and go from there. You also have the dma mux to contend with.Certainly in at least some other STM32 mcu's thedma needs physical addresses.

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 21, 2024, 01:15:09 pm
content: So did you try setting the Linker Script to 0x8000000?

author: Dan N
author_rank: Contributor
timestamp: September 21, 2024, 04:15:41 pm
content: Thanks everyone. It works with the linker map using flash physical address:Code:flash (rx) : ORIGIN = 0x08000000, LENGTH = 32KDMA was working with the hack in the first post, but it's good to get rid of that.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 21, 2024, 07:23:36 pm
content: Linker scripts in STM example projects for the STM32G0 do map the flash at 0x08000000. It's a reasonable approach to avoid the issues you saw with the DMA.Also, depending on the programming tool you use, having the flash section mapped at address 0 in the linker script may cause issues as you can't flash at address 0. (If you provide a .bin to the programming tool (from STM), then it will normally flash it at 0x08000000 by default anyway. But if you provide a .elf instead (or even a .hex), it may cause issues for flashing, as addresses in this case are usually taken from the file itself.This is what I have for a STM32G0B1 (that I've used):Code:MEMORY{RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 144KFLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K}

author: hans
author_rank: Super Contributor
timestamp: September 22, 2024, 08:24:08 am
content: The values in the linker script are used to link symbols together in the code space. It doesn't change anything on the hardware.A lot of memory map configuration registers are only applicable for CPU access, sort of like virtual memory. For DMA you need to use the physical addresses.E.g. this was one of the major downsides of bitbanding on Cortex-m3: although it would allow for atomic read-modify-write operations with an unique memory address for a particular peripheral bit, it was only a virtual memory mapping only available to the CPU and hence inaccessible from DMA. They got rid of bitbanding on newer cores, because I don't think it was that often used, and if it was on e.g. GPIO, most GPIO peripherals have set-clear registers now.

author: peter-h
author_rank: Super Contributor
timestamp: September 22, 2024, 08:30:28 am
content: Why use the boot loader at all?On my 32F4 project I have BOOT0=BOOT1=GND and never bothered with any of it.

author: voltsandjolts
author_rank: Unknown
timestamp: September 22, 2024, 09:25:19 am
content: Quote from: peter-h on September 22, 2024, 08:30:28 amIIUC when the OP mentions "default bootloader" what they mean is "default boot mode" i.e. flash remapped to 0x00000000 and executed.There is nothing here about a bootloader.

author: peter-h
author_rank: Super Contributor
timestamp: September 22, 2024, 11:13:19 am
content: OK I sawQuotebut that isn't right because the default boot mode puts the code base at 0x08000000. I have no idea how you could get the code base to 0x00000000 or indeed why you would want that. The linkfile alone, as already said, won't do it.

author: voltsandjolts
author_rank: Unknown
timestamp: September 22, 2024, 11:36:39 am
content: Quote from: peter-h on September 22, 2024, 11:13:19 amIt really is a testiment to the ease of use of cortex mcus that some folks get as far as they do with minimal reading and learning effort. It certainly helped me in the beginning.From the RM of STM32G030, section 2.5 Boot Configuration:Quote

author: Dan N
author_rank: Contributor
timestamp: September 22, 2024, 11:51:47 am
content: Quote from: peter-h on September 22, 2024, 11:13:19 amOn a STM32G0xx the vector table must be at 00000000. That's why hardware remaps (or alias) flash/sram/system-memory to 00000000.Quote from: voltsandjolts on September 22, 2024, 09:25:19 amI really did mean just the factory bootloader that is mask programmed into system memory space. In other words not trying to do anything "clever" with custom boot/programming.

author: peter-h
author_rank: Super Contributor
timestamp: September 22, 2024, 01:17:33 pm
content: QuoteThe Q I would ask is why is this feature needed?I come from decades of doing assembler (Z80 onwards) and one just treats things literally.But ifQuotethen I can see the need for this, but why put the vector table at 0 in the first place?

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 22, 2024, 02:33:49 pm
content: This thread is pretty much solved and doesn't need further nosense discussion, it's all been said already.Just leave the linker to 0x8000000, the vector table doesn't care, 0x00000000 access the same memory as 0x8000000.Quote from: voltsandjolts on September 22, 2024, 11:36:39 amQuote from: hans on September 22, 2024, 08:24:08 am

author: langwadt
author_rank: Super Contributor
timestamp: September 22, 2024, 02:47:05 pm
content: Quote from: peter-h on September 22, 2024, 01:17:33 pmbecause that's a cortex-m expects it to be and coming out of reset starts execution from the address stored at 0x4 (0x0 is the stack pointer address)how is that different from z80 et.al. ?

author: Siwastaja
author_rank: Super Contributor
timestamp: September 22, 2024, 03:59:00 pm
content: Quote from: peter-h on September 22, 2024, 01:17:33 pmBecause ST wants to offer factory bootloader which can be swapped in by simple BOOT0 pin strapping. Aliasing different parts of actual memory into the expected boot address of 0, based on the voltage on the BOOT0 pin, is the simplest and most obvious way to achieve this feature.

author: Siwastaja
author_rank: Super Contributor
timestamp: September 22, 2024, 04:03:56 pm
content: Quote from: DavidAlfa on September 22, 2024, 02:33:49 pmAgain, this is not question-answer site like stackoverflow. This is a discussion forum.This is an example of nonsense: banana qwerty mushroom yes snake.The discussion in this thread isn't. Just because the discussion goes beyond your skill level does not make it nonsense.QuoteYeah. But I have a suggestion: write that as 0x08000000. As you write it with 28 bits shown, it is way too easy for a human eye to mis-parse, unless you always use copy-paste. Especially because there is a long tradition of highest-bit-set-prefixed address spaces to signify... something, depending on architecture and product.

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 22, 2024, 05:02:02 pm
content: It's not that.All programming threads end in the same way, writing the entire history back to the ENIAC times and Alan Turing's hair color on Sunday mornings when taking a coffee in his kitchen in spring, where the sun angle was slightly tilted torwards the 3rd window near the freezer causing a reflection in his face.

author: bson
author_rank: Unknown
timestamp: September 23, 2024, 12:39:49 am
content: The following example would output text and rodata to 0x08000000, but relocate it for execution at 0x00000000.Code:MEMORY {flash (rx) : ORIGIN = 0x08000000, LENGTH = 32Kram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K}SECTIONS {.text : AT(0) {// ... usual stuffs ...ALIGN(4); // so .rodata starts at a 32-bit boundary for simplified copying}.rodata : AT(ADDR(.text)+SIZEOF(.text)) {// ... usual stuffs ...ALIGN(4); // make this a multiple of 32-bit words}// .data etc}This technique of separating the load address from the relocation address is especially useful when one or more segments of code is intended to be copied into CC RAM or SRAM to be executed there. In that case where it's loaded into the ROM is different from the location where it's to be run. Avoids having to fiddle with generating PIC which is often inefficient.

author: peter-h
author_rank: Super Contributor
timestamp: September 23, 2024, 03:55:57 pm
content: There seems to be an alternative syntax for generating code which executes from an address different from where it lives in FLASH e.g.Code:.loader :{. = ALIGN(4);_loader_ram_start = .;*loader.o (.text .text* .rodata .rodata* .data .data*). = ALIGN(4);_loader_ram_end = .;} >RAM AT>FLASH_BOOT_loader_flash_start = LOADADDR(.loader);and then it gets copied to RAM withCode:extern char _loader_ram_start;extern char _loader_ram_end;extern char _loader_flash_start;// Copy loader code and its init data to RAM.B_memcpy(&_loader_ram_start, &_loader_flash_start, &_loader_ram_end - &_loader_ram_start);For more fun, I don't have memcpy() available to me at that point (it is boot block code) so I haveCode:__attribute__((optimize("O0")))// prevent replacement with memcpy()static void B_memcpy (void *dest, const void *src, size_t len){char *d = dest;const char *s = src;while (len--)*d++ = *s++;}and the optimise 0 attrib is there to prevent the compiler detecting that the loop can be replaced with memcpy()You have the "AT" at the start while I have it at the end. No idea how this works

author: voltsandjolts
author_rank: Unknown
timestamp: September 23, 2024, 04:12:47 pm
content: https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Common-Function-Attributes.html#index-optimize-function-attributeQuote

author: wek
author_rank: Frequent Contributor
timestamp: September 23, 2024, 05:34:47 pm
content: Quote from: voltsandjolts on September 23, 2024, 04:12:47 pmInteresting, but then we could also argue, whether any of the gazillion options and features of gcc in itself are suitable in production code. There are undoubtedly many of whose are poorly written, understood, maintained, ported to various platforms and targets; and worse of all, they interact.The sentiment the above warning indicates isbasedprobably on interaction of "local optimization suppression" with "global optimization enforcement" features like inlining and LTO. gccbugzilla searchappears to confirm at least partially.JW

author: cv007
author_rank: Frequent Contributor
timestamp: September 23, 2024, 11:20:26 pm
content: QuoteThe AT takes an expression, typically in the form of a symbol previously created from a location counter, the AT> uses a memory region. I guess the former is dependent on the sequence of the linker script where the latter resolves the symbol value independent of the script sequence and probably ends up with less location counter symbol clutter (assuming there is a need for a number of these kind of sections).QuoteNot quite sure how a load address for a .text section would ever be useful, unless a programmer needs to have an elf or hex file that is 0 based for some reason. The section is already loadable, and no one but a programmer would even see the LMA address. The VMA address remains the same in any case, and in the example since the flash region is not being used (>flash) the VMA address becomes 0 based and you end up with the original problem of this thread.For most mcu scenarios, probably the only section that sees the need for AT is the .data section (and things like .ramfunc file sections usually live inside so startup code takes care of the runtime transfer). For something like a newer series avr, the .rodata section does require a VMA address for memory mapping (for direct access of flash data) and an LMA address for storage, in addition to the normal .data section use of AT.

author: peter-h
author_rank: Super Contributor
timestamp: September 24, 2024, 07:36:25 am
content: QuoteWow what a daft commentThe compiler attribute either works or it does not. Currently it does (I would soon find out if it didn't in this case) and this is yet another reason tofreeze the toolsand stop chasing for ever moving targets like Cube and GCC, for the purist reasons that if a new compiler breaks your product then your code is crap, while introducing crazy levels of business risk.I also have a command line thingy (-fno-tree-loop-distribute-patterns) which is supposed to stop identification of loop structures but I found (way back, maybe GCC 8 ) that it didn't work reliably. Now it googles to nothing... don't recall where I found it. But the attribute approach definitely works.QuoteTo me, the AT at the end of a section reads more naturally, FWIW.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: September 24, 2024, 08:03:16 am
content: Sure, freezing the foundation is essential in card house building 😁

author: Jeroen3
author_rank: Super Contributor
timestamp: September 24, 2024, 09:04:09 am
content: Quote from: peter-h on September 24, 2024, 07:36:25 amThe comment is not that daft. Sure you can use it now and freeze tools. However using unstable, deprecated or experimental features is a potential headache for future you.Oh and, if you google something starting with - it willnotsearch those keyword.https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-ftree-loop-distribute-patterns--The remap function is only in the chip because of limitation in the Cortex m0. Larger cores support vector table offset.The remap allows you to substitute the code it will run in hardware. Since basically the first it will do is read where the reset handler is it doesn't really matter for the code itself, as long as the M0 is fooled.Position independent linking (or offset) for bare metal has drawbacks as experienced.You'd need to do tricks to obtain the actual physical address to give the DMA. IIt can be done! With exporting the linker symbols for example, butheadaches guaranteed.

author: peter-h
author_rank: Super Contributor
timestamp: September 24, 2024, 09:11:34 am
content: QuoteI knew that reallyQuoteWhere would you draw the line? Change from GCC 11 to GCC 20 and you get a pile of warnings, andon this working productyou spend days chasing them down, perhaps introducing problems.

author: Jeroen3
author_rank: Super Contributor
timestamp: September 24, 2024, 09:27:05 am
content: I typically avoid linker statements at all in the C code. They are not guaranteed to work and don't throw errors when they don't.My limit is at __attribute__ ((aligned (n))). It specifies intent that is valid on all architectures, though not being perfectly portable.

author: peter-h
author_rank: Super Contributor
timestamp: September 24, 2024, 09:34:30 am
content: Which linker statements are you thinking of?

author: Siwastaja
author_rank: Super Contributor
timestamp: September 24, 2024, 02:16:29 pm
content: Quote from: peter-h on September 24, 2024, 07:36:25 amYeah. I have been using per-function optimize attribute in production code for years, without noticing this warning in documentation. If they were serious about this, why not emit a compile-time message so that users could actually see it, and maybe silence manually it with something like -Wno-optimize-attribute-warning.In any case, never noticed any problems whatsoever. I tend to use it when the module is -Os for obvious reasons but some particular function, usually an interrupt handler or a smaller function the ISR calls needs to be -O2 or even -O3, again for other obvious reasons.Separating it to a different compilation unit is of course an option but when they clearly are part of the same module and share same static variables etc. anyway, such extra separation makes the project more messy, while the optimize attribute is neat and tidy.But then again, in situations where it matters you have to verify the compiler output (execution time, code size) manuallyanywayand I fully expect to do that again every time compiler versions change,even ifI used file-scope optimization settings. Luckily only very small % of actual projects are timing-critical code like that, so I'm not too afraid of changing versions - just that you shouldn't do it like every day for no reason whatsoever.

author: bson
author_rank: Unknown
timestamp: September 25, 2024, 12:54:26 am
content: Quote from: peter-h on September 23, 2024, 03:55:57 pmIf you know your memory block is 32-bit aligned at both ends you could make it:Code:__attribute__((optimize("no-tree-loop-distribute-patterns"))) // Do not substitute function callsstatic inline void B_memcpy32 (void *to, const void *from, size_t len) {len /= 4;uint32_t *dst = (uint32_t*)to;for (const uint32_t *src = (const uint32_t*)from; len > 0; len--)*dst++ = *src++;}'no-tree-loop-distribute-patterns' only blocks substituting code patterns with function calls to builtins.Or IMO, even better would be C++ style, but that's just me:Code:#include <stdint.h>#include <stddef.h>// Compile with: g++ -c -S foo.cc -O2 -Wall// Strongly suggest inlining to gcc on the premise that this is only// used either in rare instances, or where performance really matters.template <typename T1, typename T2>[[gnu::optimize("no-tree-loop-distribute-patterns"), gnu::always_inline]]static inline void B_memcpy_aligned (T1 *to, const T2 *from, size_t len) {static_assert(sizeof(T1) == sizeof(T2));len /= sizeof (T1);const T1* f = (const T1*)from;while (len-- > 0)*to++ = *f++;}uint8_t buf1, buf2;volatile uint8_t buf3, buf4;int main(int argc, char **argv) {B_memcpy_aligned((uint32_t*)buf2, (const uint32_t*)buf1, sizeof buf1);B_memcpy_aligned((volatile uint32_t*)buf4, (const volatile uint32_t*)buf3, sizeof buf3);B_memcpy_aligned((uint32_t*)buf2, (const volatile uint32_t*)buf3, sizeof buf3);B_memcpy_aligned((volatile uint32_t*)buf4, (const uint32_t*)buf1, sizeof buf1);return 0;}This also covers volatiles, although the copy does drop the volatileness of the second parameter. If both parameters were the same type T it would only work if they were both volatile or non-volatile. Still saves from having to write code using ' specialization by copy and paste.' But I don't think it's possible to convert the 'from' argument into T1 while preserving its volatileness.BTW, if compiled with -O2 or -O3, only the copies with volatile destinations will be kept, and with -O3 both of those are totally unrolled! (gcc 14)Oh, and the 'optimize' attribute ONLY reliably reworks when reducing the optimization level. You typically can't tell it to optimize only one function above the global level, although in this case it might work.

author: peter-h
author_rank: Super Contributor
timestamp: September 25, 2024, 06:58:15 am
content: QuoteI have such a function too for other usesQuoteInteresting, but actually that is how I use it.If I wanted a specially fast individual function I would code it in asm.