How do you allocate pgm sections above text?

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just another one of these stupid questions of mine.

On a >64kB ATMega I need to use a lot of data, for which I don't want to waste the precious <64kB/<128kB space. Of course I will use use pgm_read_far_whatever().

My idea is to place the data above text, more precisely, above .fini9 . I know that as long as I don't return from main - and I don't see a single reason why would I - I could use any of the .finiX sections, and that's exactly what I am going to do right now.

But, anyway, what would be the *proper* way of doing this, without messing around with linker sections? Wouldn't it perhaps be worth to suggest to the project maintainers to modify the default linker scripts (relevant to the "big" Mega-s) to add such a section?

Thanks,

Jan Waclawek

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After a bit of experimenting I moved the data to .fini8 - placing data to .fini9 causes the linker to complain about internal error - some of the magic inside obviously doesn't like the _exit symbol to be moved around.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek,

I don't quite understand. The 64k or 128k parts only has that much code space. It does not matter where you place your fixed data. Having said that, you can save RAM space buy keeping data in the code space and only reading it when you need it.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Exactly. By moving your FLASH data to the end of memory you effectively prohibit optimization of data access – you need to use ELPM instruction which is longer and take longer to execute. And there are no benefits of such an approach as you code uses short/relative calls. Notice that avr-gcc till now cannot use extended jumps instructions so to reach far memory locations (in ATMega256) it uses trampoline. So as far as your code takes one consistent block of memory the actual location of the code itself is not important. So better leave everything as it is, I think that smarter guys choose the location for some reasons :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let's put it this way: assume, I have a big chunk of data >64kB. I need to access it through ELPM/pgm_read_xxx_far() *anyway*; but if I put it into the PROGMEM area, it might push up my other PROGMEM data above the 64kB boundary, which means, that I will need to access also ALL PROGMEM data through ELPM/pgm_read_xxx_far() (as I cannot be sure about the order how the linker decides to place them).

On a 'M256x, such a chunk of data might also push up the functions above the 64kW/128kB boundary, which might bring all sorts of unnecessary complications.

In short, the <64kB area is a precious one, which deserves to be protected from huge chunks of data; and I am seeking for a standard way to do that.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek,

Someone please correct me if I am wrong but doesn't the real boundary issue occur at 128K. One of my apps uses about 50k of data and pointers combined with about 25K of code. It crosses the 64K boundary without issue. Now I am not an expert by any means but as far as I can tell the compiler/linker does not require special instructions below 128K.

Stu_san provided a great explanation of this for me some time ago.

https://www.avrfreaks.net/index.p...

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For code, yes the limit is 128K. For PROGMEM, the limit is 64K. That's because the standard pgm_* routines use byte addressing in the code space (yeah, that happens - just when you thought you had this figured out! :roll: ).

Jan, you can always create your own section which will be placed after all the .text section. Since you need to use pgm*far() routines anyway, it won't matter where they end up, however you can actually tell the linker where to put it. For example, I place all strings for my bootloader at a specific location so the app has access to the boot loader version.

Here's how I do it. In the code, do:

#define BOOTSTRING_SECTION   __attribute__ ((section (".bootstring")))

const char boot_Version   [] BOOTSTRING_SECTION = "0.9";
const char boot_Monitor   [] BOOTSTRING_SECTION = "DPHIMON ";

All strings and constants are placed in the section ".bootstring".

In your makefile, do:

LDFLAGS += -Wl,--section-start=.bootstring=0x3FA00

The above address is in bytes not words.

To access the strings, I use pgm_*_far() like you do, along with GET_FAR_ADDRESS:

void sendCRLF()
{
	sendstring_P( (uint32_t) GET_FAR_ADDRESS( boot_CRLF ) );
}
. . .
void sendstring_P(uint32_t str_P)
{
	char c;

	do
	{
		c = pgm_read_byte_far( str_P );
		if ( c )
		{
			sendchar( c );
			str_P++;
		}
	} while ( c );
}

Of course, the pgm_*_far stuff is from the pgmspace.h file, but GET_FAR_ADDRESS is from Carlos Lamas' morepgmspace.h which I have attached below.

All of this should give you enough information for what you want to do.

Stu

Attachment(s): 

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For program there is 128k boundary, but for accessing data stored in FLASH memory you must deal with 64k boundary (16 bit pointers to bytes not words), beyond this you must use ELPM instruction.
To wek: if you have >64kb data chunk you must use ELPM, that’s obvious, but you can access data below 64k normally, you have some control about data placement in FLASH by use of struct. C guarantees that struct fields will be placed in particular user-defined order. So you can put smaller data chunks at the beginning of FLASH memory. But if it still doesn’t satisfies you, you can always create named section which address you will pass into the linker. Anyway data placement doesn’t create significant code issues till the code fits in one <128k block. If the code is bigger you will end up with trampoline anyway, no matter where you place your code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TFrancuz,

You did not mean the struct idea seriously, did you? I now have around a hundred PROGMEM variables across several dozen source files, collectively generating code for 7 different AVR devices; to put these into a single struct is simply unthinkable.

Stu San,

You might have forgotten that I already am aware of the origin of GET_FAR_ADDRESS() macro. I know how to create absolutely addressed sections; but while it is perfectly fit for the purpose you mentioned (sharing data among two different programs) and maybe a couple of other reasons (e.g. for an optimised algorithm requiring particular alignment - albeit this one would be solved better using the .align linker directive), it is inferior to my solution (to put it into existing .finiX section) when it comes to general-purpose data. It is the linker's task to take care of data placement after all, isn't it?

Using the .finiX section works perfectly, except it is not a politically correct solution, plus it may have some side-effects I am not able to foresee now. This is why I started this thread.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok., but explain please why can’t you just leave everything as it is? As I said for your program actual location doesn’t matter, the generated code will be almost the same no matter if it will be in lower 128k or upper 128k of ATMega256 device. If you refer to your data by *_FAR macros the exact location of the data doesn’t matter too. There is no something like ‘precious lower 64 or 128k’ for your program, it’s only precious lower 64k for your data, but if total size of FLASH-stored data exceeds 64k you must use far pointers anyway. If you have some special constants which must be in lower 64k of FLASH to speed up access time and reduce pointers size just create another fixed segment and put your data in that segment.
So can you specify what is the problem?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TFrancuz wrote:
Ok., but explain please why can’t you just leave everything as it is?

Hummm... Isn't this what differentiates humans from the rest of nature? ;-)

TFrancuz wrote:
As I said for your program actual location doesn’t matter, the generated code will be almost the same no matter if it will be in lower 128k or upper 128k of ATMega256 device.

For me, it *does* matter if it is "almost the same" or "the same". Just as an example, the latency of interrupts in some of the devices in the mentioned project is very, very tight.

TFrancuz wrote:
If you refer to your data by *_FAR macros the exact location of the data doesn’t matter too.

If I assign manually certain data for the _far_ access, only those have to be accessed using _far_; and as long as the rest fits below 64k, I can avoid using _far_ for these.

TFrancuz wrote:
There is no something like ‘precious lower 64 or 128k’ for your program,

If you prefer convenience (uniform approach) to code size or execution time, well, then maybe there isn't. I chose the other way round.

TFrancuz wrote:
it’s only precious lower 64k for your data, but if total size of FLASH-stored data exceeds 64k you must use far pointers anyway.

As long as I have a lot of relatively small constant variables, accessed relatively often, it's worth to keep them in the lowest 64kB. I as the programmer of the application am able to recognise, which variables may go to _far_ and which benefit from being _non_far_ (shall I write, _near_ ? :-) )

TFrancuz wrote:
If you have some special constants which must be in lower 64k of FLASH to speed up access time and reduce pointers size just create another fixed segment and put your data in that segment.

Why fixed? It's not justified here. The linker is perfectly capable of place the data just above text.

TFrancuz wrote:
So can you specify what is the problem?

Let me answer with a question: you are suggesting me solutions inferior in all respect to my solution. Why should I do things the wrong way, if there is a better way?

Do you know a *specific* reason why I should NOT place my constant data accessed as _far_ into .finiX?

Thanks,

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know one reason not to do that – it will not give you any benefits.
If you have time issues with some interrupt vectors – place them in dedicated memory area at the beginning of memory. But if the interrupt evoking time is so critical you should rather write it in assembly language – if 2 cycles are so critical you can't assume that if you change gcc version the generated assembly code will not change too, and ruin your perfectly calculated execution time.
I can't agree that as a programmer you knows better to which data you can use 'near' pointer and to which 'far' pointer because you don't know where linker will put your data, and even if you know as you said you have a lot of source files which consists of as you said thousands of const definitions, so you never know if you didn't exceed 64k limit. So this feature is useless.
But if you still want to do that, the only way is to put your data in different segment and pass its location into the linker.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TFrancuz wrote:
But if the interrupt evoking time is so critical you should rather write it in assembly language

Of course they all are in asm...

TFrancuz wrote:
I can't agree that as a programmer you knows better to which data you can use 'near' pointer and to which 'far' pointer because you don't know where linker will put your data,

No, no, you misunderstood me. As I know which data are appropriate to go to upper memory, I assign them to the "upper memory section" (.fini8 in my case) manually; the rest is assigned PROGMEM.

TFrancuz wrote:
But if you still want to do that, the only way is to put your data in different segment and pass its location into the linker.

Of course it's not the only way. I am currently putting the _far_ data into .fini8 and it works as supposed.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
TFrancuz wrote:
I can't agree that as a programmer you knows better to which data you can use 'near' pointer and to which 'far' pointer because you don't know where linker will put your data,

No, no, you misunderstood me. As I know which data are appropriate to go to upper memory, I assign them to the "upper memory section" (.fini8 in my case) manually; the rest is assigned PROGMEM.


But because as you said you have a lot of source files with a lot of const definitions you never know if you will not exceed 64k limit. So to me this solution sucks.

wek wrote:
TFrancuz wrote:
But if you still want to do that, the only way is to put your data in different segment and pass its location into the linker.

Of course it's not the only way. I am currently putting the _far_ data into .fini8 and it works as supposed.
JW

More or less its exactly the same solution I proposed. The only difference is that you use predefined memory segment. The problem is that nobody guarantee the particular position of fini subsections. Only execution order of machine code of this sections is guaranteed, but location I think could be random. So it doesn't seem to be universal solution. Or am I wrong?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TFrancuz wrote:
I wrote:
No, no, you misunderstood me. As I know which data are appropriate to go to upper memory, I assign them to the "upper memory section" (.fini8 in my case) manually; the rest is assigned PROGMEM.

But because as you said you have a lot of source files with a lot of const definitions you never know if you will not exceed 64k limit. So to me this solution sucks.

Yes, it's not perfect: there is no warning when the 64k limit of PROGMEM+jumptables+.data initialisers+whatever_else_goes_there is crossed.

In my case, what goes to _far_ is a few relatively big chunks of data - it's not >64k at the moment, but can grow there any time later. As it happens, this big data is used only in one of the multiple devices, but that's not that important here.

TFrancuz wrote:
More or less its exactly the same solution I proposed. The only difference is that you use predefined memory segment. The problem is that nobody guarantee the particular position of fini subsections. Only execution order of machine code of this sections is guaranteed, but location I think could be random. So it doesn't seem to be universal solution. Or am I wrong?

As long as the default linker scripts are used, position of .finiX is given by those scripts as the last of what goes to FLASH.

JW