table of string in flash on larger than 64K AVRs?

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

I am writing something that can parse the genre code from a MP3 file and fetch a string. ( more info http://www.digitaldeliftp.com/Re... )

 

I want the code to be compatible with AVR, ARM, and x86 (I would make it work on Xmega but I don't really own Xmega).

 

I am wondering... does my code look like it will work for extra large AVR devices, even ones that need far pointers instead of near pointers? I don't really own a big enough AVR to test it on.

 

#if defined(__AVR__)
#include <avr/pgmspace.h>
#if FLASHEND <= 0xFFFF
#define mgstr_t uint16_t
#else
#define mgstr_t uint32_t
#endif
#else // ARM or x86
#define PROGMEM 
#define mgstr_t char*
#endif

static const char genrestr_blues[] PROGMEM = "Blues";
static const char genrestr_classic_rock[] PROGMEM = "Classic_Rock";
static const char genrestr_country[] PROGMEM = "Country";
static const char genrestr_dance[] PROGMEM = "Dance";
static const char genrestr_disco[] PROGMEM = "Disco";
static const char genrestr_funk[] PROGMEM = "Funk";
static const char genrestr_grunge[] PROGMEM = "Grunge";

const mgstr_t music_genres_tbl[] PROGMEM = {
 (mgstr_t)genrestr_blues,
 (mgstr_t)genrestr_classic_rock,
 (mgstr_t)genrestr_country,
 (mgstr_t)genrestr_dance,
 (mgstr_t)genrestr_disco,
 (mgstr_t)genrestr_funk,
 (mgstr_t)genrestr_grunge,
};

 

#ifdef __AVR__
#if FLASHEND > 0x10000
strcpy_PF((char*)tmp, (uint_farptr_t)pgm_read_dword_far(&(music_genres_tbl[genre_code])));
#else
strcpy_P((char*)tmp, (const char*)pgm_read_word(&(music_genres_tbl[genre_code])));
#endif
#else // ARM or x86
strcpy((char*)tmp, (const char*)(music_genres_tbl[genre_code]));
#endif

 

Thanks

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

The .progmem.data section in the linker script is placed immediately after .vectors so until you get close to 64K of PROGMEM there shouldn't be a problem as the linker will be stuffing as much as it can in the first 64K. I sort of assume you won't be putting the MP3 data itself in AVR flash so I doubt you really have >64K of PROGMEM data do you?

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

well it's about learning the right way of doing things, and covering all possibilities

 

I am pretty sure I'm actually doing this project on something with SDIO, so a pretty beefy Cortex chip

 

Does the code look right? It should be pretty straightforward to see. What I am afraid of is the strings being stored as 0x3456 when in reality it should have been 0x00123456, and then the table stores 0x00003456. I do not know if PROGMEM pointers are automatically promoted to 32 bit instead of 16.

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

No pointers are 16 bit.

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

clawson wrote:

No pointers are 16 bit.

 

Was that a "no, pointers are always 16 bit"? Or was it "pointers are not 16 bit"?

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

Pointers are 16 bit.

 

The linker will put the PROGMEM or __flash text in the bottom 64k so the 16 bit pointers can reach it. This works as long as you have less than 64K of PROGMEM data. (The vectors have to fit in this part too.)

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

Pointers are 16 bit.

Unless they are what AVR Libc refers to as "far" pointers or Atmel's ASF refers to as "hugemem" pointers, in which case they are 32 bits, although at most only 24 bits are valid.  ASF uses these strictly for external RAM, so they only apply to the A1 and A1U, which support XRAM via the External Bus Interface.  Libc has the *_PF functions you used above, so the intention is clearly to use them with FLASH.

 

I played around with these a few years ago on the XMEGA A1 Xplained.  I don't recall ever using far or hugemem pointers for FLASH, but your code looks like it ought to work.  You will probably need to create a (FLASH) memory section in order to put these above the 16-bit space.  I've never done that with FLASH either, and don't know if it is doable.

 

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

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

avr/pgmspace.h has both near and far macros for reading FLASH:

#define 	PROGMEM   __ATTR_PROGMEM__
#define 	PGM_P   const char *
#define 	PGM_VOID_P   const void *
#define 	PSTR(s)   ((const PROGMEM char *)(s))
#define 	pgm_read_byte_near(address_short)   __LPM((uint16_t)(address_short))
#define 	pgm_read_word_near(address_short)   __LPM_word((uint16_t)(address_short))
#define 	pgm_read_dword_near(address_short)   __LPM_dword((uint16_t)(address_short))
#define 	pgm_read_float_near(address_short)   __LPM_float((uint16_t)(address_short))
#define 	pgm_read_ptr_near(address_short)   (void*)__LPM_word((uint16_t)(address_short))
#define 	pgm_read_byte_far(address_long)   __ELPM((uint32_t)(address_long))
#define 	pgm_read_word_far(address_long)   __ELPM_word((uint32_t)(address_long))
#define 	pgm_read_dword_far(address_long)   __ELPM_dword((uint32_t)(address_long))
#define 	pgm_read_float_far(address_long)   __ELPM_float((uint32_t)(address_long))
#define 	pgm_read_ptr_far(address_long)   (void*)__ELPM_word((uint32_t)(address_long))

 

EDIT: which you are already using, doh...  As for pointers getting promoted to 32 bits, I don't know how that could happen.  You'll have to conditionally define macros as either the near version or the far version, based on FLASH size.  Or do what you are doing in the second chunk of code in your original post.

 

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

Last Edited: Tue. Apr 5, 2016 - 02:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sooooo from what I'm gathering... I'm screwed no matter what, if I have too much flash data, because the pointers are 16 bit, even if I use _far or _PF, I still don't know what bits 16:31 are. Correct?

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

The program counter (CPU instruction pointer) is 16 bits but it is always a word address (128KB). The mega256 and up have the EIND method to cross pages with jumps and calls.

When you get to this mess of paging, it's time to switch to an ARM Cortex M0 or M3 or M4. Low cost. Arduino-alike libraries from Teensy 3 (PJRC.com) and others.

 

For data...

.18.5.2 Handling of the RAMPD, RAMPX, RAMPY and RAMPZ Special Function Registers

Some AVR devices support memories larger than the 64 KiB range that can be accessed with 16-bit pointers. To access memory locations outside this 64 KiB range, the contentent of a RAMP register is used as high part of the address: The X, Y, Z address register is concatenated with the RAMPX, RAMPY, RAMPZ special function register, respectively, to get a wide address. Similarly, RAMPD is used together with direct addressing.

  • The startup code initializes the RAMP special function registers with zero.
  • If a named address space other than generic or __flash is used, then RAMPZ is set as needed before the operation.
  • If the device supports RAM larger than 64 KiB and the compiler needs to change RAMPZ to accomplish an operation, RAMPZ is reset to zero after the operation.
  • If the device comes with a specific RAMP register, the ISR prologue/epilogue saves/restores that SFR and initializes it with zero in case the ISR code might (implicitly) use it.
  • RAM larger than 64 KiB is not supported by GCC for AVR targets. If you use inline assembler to read from locations outside the 16-bit address range and change one of the RAMP registers, you must reset it to zero after the access.
Last Edited: Tue. Apr 5, 2016 - 04:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could give in and read the documentation I guess:

 

http://www.nongnu.org/avr-libc/u...

 

The fact is that pointers in avr-gcc are all (apart from the recently added __memx) 16 bit so you can only, in theory address 64K with such a pointer. However if you read through avr/pgmspace.h you will hit this piece of magic:

 

http://www.nongnu.org/avr-libc/u...

 

and that's the secret for getting beyond 64K. The post from the author of pgm_get_far_address, when it was initially called GET_FAR_ADDRESS is here:

 

https://lists.gnu.org/archive/ht...

 

PS apologies for the brevity of my post #4 - I find this site close to impossible to use from a mobile phone!

Last Edited: Tue. Apr 5, 2016 - 09:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

 

PS apologies for the brevity of my post #4 - I find this site close to impossible to use from a mobile phone!

 

Wow. That's dedication. I don't even try.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

PS apologies for the brevity of my post #4 - I find this site close to impossible to use from a mobile phone!

My mobile's browser doesn't support posting HTML content, so If I want more than a single monolithic line, I must resort to hand-coded HTML.  This kind of rubbish:

<p>This will appear on the first line</p><p>&nbsp;</p><p>... and this will appear on the third line, with a blank line in between.</p>

I rarely bother, except when the program on TV is exceptionally boring ;-)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

sooooo from what I'm gathering... I'm screwed no matter what, if I have too much flash data, because the pointers are 16 bit, even if I use _far or _PF, I still don't know what bits 16:31 are. Correct?

No.  The _far or _PF functions take a 32-bit "address" and put bits 23:16 into one of the RAMP register and bits 15:0 into the corresponding index register giving you 24 bits of "pointer."  The upper byte (bits 31:24) is ignored.

 

For reading flash, RAMPZ:Z must be used as the ELPM instruction inherently uses those registers.  I don't recall whether the ASF "hugemem" functions use X or Z.

 

 

edit: typos and last two sentences.

 

edit2: The RAMPX, RAMPY, and RAMPD registers only appear in the XMEGAs.  The MEGAs (with > 64kB of flash) have RAMPZ and EIND only.

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

Last Edited: Fri. Apr 8, 2016 - 02:59 AM