[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-gcc-list] Re: Allocating variables to a fixed address

From: David Brown
Subject: [avr-gcc-list] Re: Allocating variables to a fixed address
Date: Mon, 11 May 2009 21:46:06 +0200
User-agent: Thunderbird (Windows/20090302)

Stu Bell wrote:
I have defined all cross-module routines and give them 'psuedo-calls' in a vector table so:

* Function entry points for independent test routines

// LCD Display
FUNCTION_SECTION NOINLINE void test_lcd_2(PGM_P string) { prog_lcd_string2(string); }

FUNCTION_SECTION NOINLINE void test_lcd_string(PGM_P string) { prog_lcd_string(string); }

FUNCTION_SECTION NOINLINE void test_clearlcd2(void) { clearlcd2(); }

. . .
Where FUNCTION_SECTION is located at 0xff00

Now I see that the compiler is no longer preserving the order of these

avr-nm -n main.elf gives:

0000ff00 T test_read_mSecDownCnt
0000ff10 T test_write_mSecDownCnt
0000ff1e T test_geta2d
0000ff3c T test_nSec400Wait
0000ff42 T test_lcd_hex4out
0000ff48 T test_lcd_hex8out
. . .
Is there a way to achieve this? The project is much too big for assembler (not enough time & maintenance). I can force variable access through functions, of couse. But this doesn't help when I can't trust my vector table to stay constant.

If I had your problem, this is how I would solve it: Define a real table
of function pointers instead of trying to get the linker to create one
for you.

  typedef void (*TestProc) (void*);

  NOINLINE void test_lcd_2(void* pStr) {PGM_P string = (PGM_P) pStr;
prog_lcd_string2(string); }

  NOINLINE void test_lcd_string(void* pStr) { PGM_P string = (PGM_P)
pStr; prog_lcd_string(string); }

  NOINLINE void test_clearlcd2(void* ign) { clearlcd2(); }

  NOINLINE void test_lcd_hex4out(void* pByte) { uint8_t byte = *
(uint8_t*) pByte; return lcd_hex4out(byte); }


  const TestProc* TestTable[TEST_VECTORS] FUNCTION_SECTION = {

You will need to modify your calls to either send the pointer to the
argument (this forces all calls to have a single parameter of,
essentially, a word) or to always send a word.  In either case, the
receiving routine will then cast the argument to the proper value before
passing it on.
Do not place the routines or other constants in the same linker section
as the function table.  If you leave the table in a section of its own,
it will always be placed at the head of the section without interference
from anything else.

Calling the function requires you to pull the function out of the table
before calling it:

   #define TESTPROC_LCD_2      0
   . . .

   uint16_t test_proc;

   test_proc = pgm_read_word( (uint16_t) &TestTable[ TESTPROC_LCD_2 ] );
   *(TestProc*)test_proc( myarg );

   . . .

You could probably enclose the above stuff in a macro to make your code
far more readable:

#define CallTestProc(proc,arg) \ do { \
           uint16_t test_proc;    \
           test_proc = pgm_read_word( (uint16_t) &TestTable[ (proc) ] );
           *(TestProc*)test_proc( (arg) ); \
      } while (0);
. . .

    CallTestProc( TESTPROC_LCD_2, myarg );

That's my inspiration for the day.  It's probably wrong somewhere, but
it's how I would approach the problem.

<AOL>Me too!</AOL>

I've done something similar on another platform. Using a table of function pointers makes it *much* easier to be sure that your addresses are all where you think they are, and that you don't have to worry about inlining or other optimisations.

Do a similar thing with shared variables - put them all in a single structure, and make the address of that structure available (for example, via an accessor function - it need only be called once at startup). If you can keep the size of the struct under 64 bytes these shared globals can be accessed reasonably efficiently.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]