avr-gcc-list
[Top][All Lists]
Advanced

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

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


From: Stu Bell
Subject: RE: [avr-gcc-list] Allocating variables to a fixed address
Date: Mon, 11 May 2009 08:53:07 -0600

> 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
> vectors:
> 
> 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 = {
     test_lcd_2,
     test_lcd_string,
  ...
  };

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
   #define TESTPROC_LCD_STRING 1
   . . .

   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.

Best regards, 

Stu Bell 
DataPlay (DPHI, Inc.) 








reply via email to

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