Implementing (far) Function Tables in C

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

Preface: although this question is about bootloaders, I believe it also applies to anyone trying to implement function tables in C for devices with >64K.

I am working on separating my bootloader into two parts. One is fixed and the other is updateable. Because functions can move around in the updateable portion, I have implemented a FSM in the fixed portion which uses a function table in the updateable portion. Here is the function table definition in C:

typedef uint8_t (*fn_ptr_t)(void);

#define JUMPTABLE_SECTION __attribute__ ((section (".jumptable")))
#define NUMBER_OF_STATES	3
const fn_ptr_t FunctionTable[NUMBER_OF_STATES] JUMPTABLE_SECTION =  { 
	Start, Update, Exit };

and here is the FSM in main:

register uint8_t state asm("r4");

int main(void) {
	state = 0;
	do {
		#if defined(RAMPZ)
		((fn_ptr_t)pgm_read_word_far(0x10000 + (uint16_t)&FunctionTable[state]))();
		#else
		((fn_ptr_t)pgm_read_word_near(&FunctionTable[state]))();
		#endif	
		state++;		
	} while (state != NUMBER_OF_STATES);
	while (TRUE);
}

I have been able to just squeeze the fixed portion (including flash writing) in 256 bytes (a mega128 page). Part of the trick here was to use the version of GCC that generates the smallest code (3.4.5). However the generated code for deferencing the far pointer and calling the function is larger than I would have wanted (18 words in the icall).

The bootloader with updating feature all works correctly. However I wondering if there is a way to do what I want in C that uses less code? I think a table (function, jump or otherwise) is needed. I have seen assembler RJMP tables but I don't know how in C to make a generic function call which in turn then jumps to the word in the RJMP table.

--Mike

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

mikeperks wrote:
Because functions can move around in the updateable portion, I have implemented a FSM in the fixed portion which uses a function table in the updateable portion.
I have been able to save 2 words by removing the FSM and adding a non-inline function that calls the appropriate function pointer for the two places it is needed. Here is the code with some details skipped:
const fn_ptr_t FunctionTable[] JUMPTABLE_SECTION = { Start, Exit };

#define StartFN 0
#define ExitFN  1

int main(void) {
    if (CallSubFunction(StartFN))
        Update();
    CallSubFunction(ExitFN);
    while (TRUE);
}

static uint8_t CallSubFunction(uint8_t index)
{
    return ((fn_ptr_t)pgm_read_word_far(0x10000 + (uint16_t)&FunctionTable
))(); }

--Mike

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

To stimulate discussion of this interesting topic, instead of a jump table, maybe it would not be too wasteful of Flash to define several sections of flash within the boot region to store certain functions at absolute addresses. This way, you will always know where those particular functions are to be found.

Maybe there is some other way to get the compiler/linker to place certain functions at absolute addresses....

-Tony

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

But the "padding" you had to get them to known boundaries would "cost more" than a function table? Unless you want to take on the job of the linker and hand locate them to adjacent boundaries but then how do you ensure that the exact locations used in the app match the ones used in this version of the bootloader? Again a function table (that also contains a bootloader version number in case the quantity/APIs change) seems like the answer.

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

This should be in the GCC section (I believe it would be more helpful there), as the solution is going to be compiler specific. I imagine it might get more responses there as well.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Apart from Tony's mention of named sections (which just happens to be the GCC way to absolutely position code) what's GCC specific about this "concept" otherwise? Presumably all the C compilers have some way of absolutely locating code routines and function tables (in fact for the very solution of passing a table from boot to app, amongst other things).

As for locating such tables in code flash and not RAM, the OP shows a GCC way to do it but presumably the other compilers have something similar?

I think the general question (from Tony resurrecting this thread) was really "is there a better way?"

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

Well IIRC, GCC has some issues at the 64K barrier, and you need to do some trickery to get it to work. In addition, you need additional functions to read the pointer value from flash.

For example, in IAR you would treat it just the same as if it was a near function table.

#pragma location="jumptable"
fn_ptr_t FunctionTable[] = {Start, End};
.
.
.
// call the desired function
res = FunctionTable[idx]();

For devices with more that 128K (64K words) you would have to declare the functions, and pointers, as __farfunc, the calling would be the same.

As you can see, this does not differ all that much for standard C (for IAR, results with other compilers may vary), where as the GCC way you need access functions to make it work. This is why I say that the solution is GCC specific, and the discussion really belongs in the GCC section.

Whenever we start playing with the different memory spaces on the AVR, the solution undoubtedly becomes compiler specific.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

glitch wrote:
This is why I say that the solution is GCC specific, and the discussion really belongs in the GCC section.
I have no problem in moving it and it did cross my mind that it should be. I haven't really got a satisfactory answer to my original question.

In the meantime however I rearranged the functions and now use a jumptable. The GCC syntax is as follows:

void int_vect_jump(void) __attribute__((naked)) __attribute__((section(".jumptable")));
void int_vect_jump(void)
{
   __asm__ __volatile__ (         \
      "rjmp main  \n\t"           \
      "rjmp ExitBootloader  \n\t" \
      ::                          \
   ); 
}

and then to RJMP into the table you can write code such as:

#define jmpExitBootloader 2

asm("rjmp int_vect_jump + %0" :: "i" (jmpExitBootloader));

This is what I was missing.

--Mike

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

Quote:
I have no problem in moving it

Ta da!