Author Topic: avr-gcc read flash across 64KB boundary  (Read 2793 times)

0 Members and 1 Guest are viewing this topic.

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
avr-gcc read flash across 64KB boundary
« on: September 21, 2024, 10:18:56 am »
I am working on an AVR program that has a lot of const data placed in flash (think big tables). For now I am using "const __flash", and all is well. However, I foresee that one of these tables some day will cross the 64 KB boundary (I am using an AVR with 128 KB flash), or even worse: One of the tables will be bigger than 64 KB in itself.
I know __flash pointers are only 16-bit and won't work above 64 KB. I have just read up on __memx and have started to experiment with it a little. __memx pointers are 24-bit wide, but the msb signals if it address flash or ram. This seems to give some overhead when using them, especially when traversing lots of data in big tables.

Is there something I have missed? Is there a way, in avr-gcc, to have a pointer to flash only, that is wider than 16 bit?
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22435
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: avr-gcc read flash across 64KB boundary
« Reply #1 on: September 21, 2024, 01:53:23 pm »
You may find this relevant:
https://gcc.gnu.org/wiki/avr-gcc#Address_Spaces
When overflow is anticipated, you could plan on putting all such objects into the __flashNs, and make note to only access them with specific RAMPZ value, or other means (e.g. NVMCTRL when applicable).

GCC may handle it automatically, but do check the output to see what it's doing.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #2 on: September 21, 2024, 03:46:56 pm »
I did have a look at __flashN, but they are unfortunately also 16-bit, so no good across a 64KB boundary.
Perhaps I should mention that I am not in control of these tables or their sizes. They are created by the linker from data in (potentially many) source files. I prefer to have a solution where I don't need to manually manage the placement of the tables into segments in flash.

I have tested some more with __memx, and it does what it's supposed to. I guess I'll start with that, and then I can try to optimize the flash access with inline assembler when I get too annoyed about the overhead.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4413
  • Country: us
Re: avr-gcc read flash across 64KB boundary
« Reply #3 on: September 21, 2024, 10:57:52 pm »
since pointers on AVR are 16 bits, data structure size is limited to 32kbytes.(there may be a separate problem with smaller data crossing the 64k boundaries in flash, but you'll need to reconsider tables that you expect might exceed 64kbytes.)

https://www.avrfreaks.net/s/topic/a5C3l000000UAVLEA4/t054177?comment=P-266422
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #4 on: September 22, 2024, 08:55:55 am »
Thank you for that link. That is very interesting reading.
I knew there was a limit to array sizes, but I didn't know what that limit was.

I've just made a test where I got one of my flash tables to be about 80KB. It worked fine using a __memx pointer.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4413
  • Country: us
Re: avr-gcc read flash across 64KB boundary
« Reply #5 on: September 23, 2024, 02:02:58 am »
Quote
It worked fine using a __memx pointer.
Good to know.
How did you create the table?  Linker magic?
avr-gcc doesn't let you make really large structures...
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #6 on: September 23, 2024, 05:38:11 am »
How did you create the table?  Linker magic?
avr-gcc doesn't let you make really large structures...

Hehehe yes, "linker magic" is the short version  :-DD
The long version is that I have a macro that puts data into a custom section in flash. Well ok. I have multiple macros and corresponding multiple sections. But that doesn't matter. Each invocation of the macro creates one element in the table, and the linker puts them all together (and even sorts them if I need that). I just got linker script augmentation to work the other day, which is something I've tried to do earlier, but failed. It appears that I was using a bad example.

Here is one such macro (the most complex one of course): https://github.com/ejbergdk/loconet-routectrl3/blob/5b79e1fd5d28f4487d9ec86e8af7c40b931f97d3/routectrl3/route.h#L53
And this file demonstrates how the macro is used: https://github.com/ejbergdk/loconet-routectrl3/blob/5b79e1fd5d28f4487d9ec86e8af7c40b931f97d3/routectrl3/ctrl_routetables.c#L45

And in case anyone wonders... This code is used to control my model train layout. A route is the process of a train moving from one point to another on the layout. Requesting routes that would conflict with another (causing trains to collide) are postponed until the conflict is gone.
Now, my layout is nowhere big enough to cause me to hit the 64KB limit, but as I've recently made my train controller project public, someone else might do that. And that is what prompted me to make sure the 64KB won't be a problem.
 
The following users thanked this post: pardo-bsso

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 7541
  • Country: fi
    • My home page and email address
Re: avr-gcc read flash across 64KB boundary
« Reply #7 on: September 23, 2024, 05:39:11 am »
You can find the AVR GCC documentation for _memx address space here.  It requires GCC version 4.7 or later.

To collect elements into a single large _memx array, annotate the variables with __attribute__((section (".progmemx.data"))).  Note that the default alignment is two bytes, so the size of each variable/object should be even, or you'll get surprising padding with default linker scripts.  Also, the order of such variables/objects is semi-random.

(I've had to do post-processing passes to reorder the data in some cases, for example to sort a command array for binary search or hash table access.  It is easy to do using binutils' avr-objdump, avr-objcopy if and only if the data does not contain relocations (pointers to other objects); relocations can be retained using a number of different methods, with fixed structure formatted data being relatively easy.)

To find out what linker script is in use, run
    avr-gcc -O2 -Wl,--verbose -mmcu=atmega32u4
replacing the MCU (atmega32u4) with the specific one you're using.  (This specific one will typically refer to /usr/lib/avr/bin/../lib/ldscripts/avr5.xn = /usr/lib/avr/lib/ldscripts/avr5.xn on non-Windows machines.)
 

Online Doctorandus_P

  • Super Contributor
  • ***
  • Posts: 4109
  • Country: nl
Re: avr-gcc read flash across 64KB boundary
« Reply #8 on: September 23, 2024, 06:08:52 pm »
I'd say you may be going in the wrong direction.
I've had lots of fun with the AVR microcontrollers years ago, and I still use them for simple projects (my programs rarely exeed 8kB, but if they don't fit in the old ATMEGA8 I can easly swap to an ATMEGA328. But the 8-bit architecture simply has it's limits. There was a blue monday that I was interested in the Xmega's, and they have lots of nice and new features. But after realizing how different they were from the normal ATMega's I realized it would be foolish to not look further, so I finally settled on STM32. I don't know how far they go, but I guess that a 1MB lookup table is no problem at all. And maybe STM32 was not a very good choice. I choose it because lots of chinese use it in hobby equipment, so I can reprogram such existing gadgets to do something more useful. Documentation for STM32 is mindboggingly overwhelming, and not well organized. It's not a friendly processor to begin with, but there are plenty of other Cortex-M3 and similar processor families. I've heard rumors that the LPC variant is easier to get started in (also used in Teensy's).

But if you're stuck with the processor you have now. There is probably a way to force it to work, and it's the way of least effort. But a respin of a PCB (for new products) is not such a big deal either.
 
The following users thanked this post: Siwastaja

Offline gjl

  • Newbie
  • Posts: 4
  • Country: de
Re: avr-gcc read flash across 64KB boundary
« Reply #9 on: December 21, 2024, 11:19:16 am »
To date, the __memx named address space is the only one that supports reading across the 64 KiB flash segment boundaries.

In avr-gcc v15, __flashx has been added as 24-bit flash address space.  It supports reading across the flash segment boundaries.  The overhead caused by pointer handling is the same like for __memx, but the overhead to access locations might be less than imposed by __memx.  Just like __memx, __flashx allocates its objects in input section .progmemx.data.

Then there is AVR-LibC v2.2's avr/pgmspace.h which supports PROGMEM_FAR (similar to PROGMEM), PSTR_FAR (similar to PSTR) and lots of macros like pgm_read_int_far (similar to pgm_read_int).  Notice that avr/pgmspace.h implements all these features in that header without library support, so it should be straight forward to use these features with an older toolchain.

Quote
One of the tables will be bigger than 64 KB in itself.

In that case, even __flashx won't work (because offsets are still 16 bits wide) and you'll have to use inline assembly resp. avr/pgmspace.h.
« Last Edit: December 21, 2024, 01:37:25 pm by gjl »
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #10 on: December 21, 2024, 04:14:31 pm »
In avr-gcc v15, __flashx has been added as 24-bit flash address space.
Holy smoke. That's exactly what I needed. Impressive that there's still being developed new features for avr-gcc.
Unfortunately, that means that I now need to look at a new development setup. I am currently still using the aging Atmel/Microchip Studio 7, and I know perfectly well that it won't be upgraded. I don't feel like tormenting myself with MPLAB (I've heard plenty of bad things about it), so I'll probably go full Linux instead.

Meanwhile, I did get __memx to work. It does have a little overhead, but it isn't as bad as I first imagined.
 
The following users thanked this post: Dazed_N_Confused

Offline gjl

  • Newbie
  • Posts: 4
  • Country: de
Re: avr-gcc read flash across 64KB boundary
« Reply #11 on: December 21, 2024, 07:19:17 pm »
that means that I now need to look at a new development setup.

You can still go avr/pgmspace.h without the need for new avr-gcc or AVR-LibC.

https://github.com/avrdudes/avr-libc/blob/main/include/avr/pgmspace.h

Performance-wise, the best might be __flash1, though you need an amendment to the linker description file as outlined in the avr-gcc Wiki:

https://gcc.gnu.org/wiki/avr-gcc#Address_Spaces
« Last Edit: December 21, 2024, 07:24:54 pm by gjl »
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #12 on: December 22, 2024, 12:42:03 pm »
You can still go avr/pgmspace.h without the need for new avr-gcc or AVR-LibC.
I can, but I suspect that it will create more overhead, as RAMPZ is reset after every far read. And the code won't be as "clean" and readable as it is now, using __memx.

Quote
Performance-wise, the best might be __flash1, though you need an amendment to the linker description file as outlined in the avr-gcc Wiki:
That will bring me back to the 64KB segment limit that I wanted to avoid in the first place.

I just might try to make 3 versions (using __memx, avr/pgmspace.h and homemade inline assembler), and measure their performance against each other. That could be a fun little exercise.
 

Offline SvuppeTopic starter

  • Regular Contributor
  • *
  • Posts: 100
  • Country: dk
Re: avr-gcc read flash across 64KB boundary
« Reply #13 on: December 22, 2024, 03:53:44 pm »
I can, but I suspect that it will create more overhead, as RAMPZ is reset after every far read.
I misread that in pgmspace.h. The RAMPZ reset only applies to AVR's with EBI (which I don't have).

Quote
I just might try to make 3 versions (using __memx, avr/pgmspace.h and homemade inline assembler), and measure their performance against each other. That could be a fun little exercise.
I have now done a small test, based on data I already use in a project. This is a table in flash with 74 entries.
Using __memx pointer to traverse the table takes 104.5 us.
Using avr/pgmspace.h and its pgm_read_xxx_far takes 79.8 us.
Using a hand-crafted inline asm loop takes 66.2 us.
 

Offline gjl

  • Newbie
  • Posts: 4
  • Country: de
Re: avr-gcc read flash across 64KB boundary
« Reply #14 on: December 23, 2024, 01:44:07 pm »
When you want a loop, here is also memcpy_PF.  Though that will cost a function call and argument shuffling.

https://avrdudes.github.io/avr-libc/avr-libc-user-manual/group__avr__pgmspace.html#ga13cc6cd0692aeccaac2789350a16412b
https://github.com/avrdudes/avr-libc/blob/main/libc/pmstring/memcpy_PF.S

In many cases, avr-gcc's code generation for address-space access is quite suboptimal, hence better consider the inline asm macros / inline functions provided by AVR-LibC.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf