How do I define a lookup table in PROGMEM?

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

I am trying to define a lookup table in PROGMEM for an experimental Lisp interpreter on an ATmega328. It needs to consist of a series of strings and function addresses. I've tried this:

const char String1[] PROGMEM = "car";
const object* (*Function1)(object*, object*) PROGMEM = &fn_car;
const char String2[] PROGMEM = "cdr";
const object* (*Function2)(object*, object*) PROGMEM = &fn_cdr;

and so on. The idea is that if the parser encounters "car" it should execute the function fn_car, etc.

 

However I get the error "variable 'Function1' must be const in order to be put into read-only section". Any suggestions how I should do this?

Last Edited: Mon. Apr 11, 2016 - 06:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff addressed that in this earlier thread

https://www.avrfreaks.net/comment...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Thank you - that looks useful.

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

I'm sure you can find other similar discussions with a determined search.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Shame you hadn't shown us some more context. Anyway this compiles with no warnings/errors:

$ cat avr.c
#include <avr/io.h>
#include <avr/pgmspace.h>

typedef char object;

const object * fn_car(object * a, object * b);
const object * fn_cdr(object * a, object * b);

const char String1[] PROGMEM = "car";
const object* (*Function1)(object*, object*) PROGMEM = fn_car;
const char String2[] PROGMEM = "cdr";
const object* (*Function2)(object*, object*) PROGMEM = fn_cdr;

const object * fn_car(object * a, object * b) {
}

const object * fn_cdr(object * a, object * b) {
}

int main(void) {
}

I can't help wondering if you meant the function pointer to be "const" not the object* that the functions return? Don't quote me on this but I think this would be the correct placement of the "const" if that's what was intended:

#include <avr/io.h>
#include <avr/pgmspace.h>

typedef char object;

object * fn_car(object * a, object * b);
object * fn_cdr(object * a, object * b);

const char String1[] PROGMEM = "car";
object*  (* const Function1)(object*, object*) PROGMEM = &fn_car;
const char String2[] PROGMEM = "cdr";
object* (* const Function2)(object*, object*) PROGMEM = &fn_cdr;

object * fn_car(object * a, object * b) {
}

object * fn_cdr(object * a, object * b) {
}

int main(void) {
}

(again that compilers without warning/error - though just compiling does not prove much!).

 

Oh and is this C or C++? If it is C then you may want to explore __flash as an alternative to PROGMEM as long as your compiler is beyond 4.6

 

EDIT: forgot to ask but as the string and the fn-ptr are "joined" wouldn't you consider making them two members of a struct{} instead then just put that whole thing in PROGMEM ?

 

EDIT2: just realised I was using 4.5.3 where the requirement for PROGMEM data to be "const" was not implemented. So I'm pretty sure that for 4.6+ you must use my 2nd example there. Only you then know if the return of the functions is supposed to be a pointer to const object or perhaps even a const pointer to object?

 

EDIT3: yup, just checked with 4.9.2 - the first code here has the same error as you encountered, the second one doesn't.

Last Edited: Tue. Apr 12, 2016 - 10:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Thank you for the replies. With your help I've got something working, but it's a bit ugly; here's an abbreviated version:

const char String0[] PROGMEM = "car";
const char String1[] PROGMEM = "cdr";
// ... etc

const char* const StringTable[] PROGMEM = {
  String0, String1 // ... etc
};

typedef void (*f_ptr_type)(void); 

const f_ptr_type FunctionTable[] PROGMEM = {
  (f_ptr_type) &fn_car,
  (f_ptr_type) &fn_cdr
// ... etc
};

object* mylookup(char* n){
  int entry = 0;
  while (entry <= 13) {
    if(strcmp_P(n, (char*)pgm_read_word(&StringTable[entry])) == 0)
      return func((object* (*)(object*, object*))pgm_read_word(&FunctionTable[entry]));
    entry++;
  }
  return NULL;
}

Here "mylookup" is called with a string, such as "car", and it returns a cell constructed by the call to "func" containing the function address.

 

Any suggestions of how to make it a bit more elegant would be appreciated.

Last Edited: Wed. Apr 13, 2016 - 09:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

johnsondavies wrote:
Any suggestions of how to make it a bit more elegant would be appreciated.

As I say, as the string and the function pointer are "joined" I would make that link and actually join them (a struct). So something like this where I've also wrapped enough around it to make it compilable:

#include <avr/io.h>
#include <avr/pgmspace.h>

typedef char object;

typedef object *(*f_ptr_type)(object *, object *); 

typedef struct {
    const char * string;
    f_ptr_type fptr;
} tbl_entry_t;

object * fn_car(object * a, object * b) {
}

object * fn_cdr(object * a, object * b) {
}

object * fn_add(object * a, object * b) {
}

const char String0[] PROGMEM = "car";
const char String1[] PROGMEM = "cdr";
// ... etc

const tbl_entry_t lookup[] PROGMEM = {
    { String0, fn_car },
    { String1, fn_cdr }
    // etc.
};

object * func(f_ptr_type fp) {
}

object* mylookup(char* n){
  int entry = 0;
  while (entry <= 13) {
    if(strcmp_P(n, (char*)pgm_read_word(&lookup[entry].string)) == 0)
      return func((f_ptr_type)pgm_read_word(&lookup[entry].fptr));
    entry++;
  }
  return func(fn_add);
}

int main(void) {
}

That compiles without warning/error. No guarantees it actually does what you intended though! The key thing though is this:

const tbl_entry_t lookup[] PROGMEM = {
    { String0, fn_car },
    { String1, fn_cdr }
    // etc.
};

which combines string+function pointer into single entities.

 

BTW in doing this I noticed you had:

typedef void (*f_ptr_type)(void); 

that is too simplistic. No wonder you needed convoluted typecasts. It needs to be:

typedef object *(*f_ptr_type)(object *, object *); 

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

That's much better than my version, thank you.

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

I guess the proof of the pudding is whether it actually does what it is supposed to.

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

It works!

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

Next (as this is "new" code you appear to be authoring) you should look at ditching PROGMEM in favour of the recently added __flash so you won't need the pgm_read_*() dereferences. Untried but something like the following...

 

Again this compiles cleanly but I don't know if it actually works (and you need a "late" compiler like a 4.8.1 or 4.9.2 or even later):

#include <avr/io.h>
#include <avr/pgmspace.h>

typedef char object;

typedef object *(*f_ptr_type)(object *, object *); 

typedef struct {
    const __flash char * string;
    f_ptr_type fptr;
} tbl_entry_t;

object * fn_car(object * a, object * b) {
}

object * fn_cdr(object * a, object * b) {
}

object * fn_add(object * a, object * b) {
}

const __flash char String0[] = "car";
const __flash char String1[] = "cdr";
// ... etc

const __flash tbl_entry_t lookup[] = {
    { String0, fn_car },
    { String1, fn_cdr }
    // etc.
};

object * func(f_ptr_type fp) {
}

object* mylookup(char* n){
  int entry = 0;
  while (entry <= 13) {
    if(strcmp_P(n, (char*)lookup[entry].string) == 0)
      return func(lookup[entry].fptr);
    entry++;
  }
  return func(fn_add);
}

int main(void) {
}

While the pgm_read_*() stuff is removed (makes things look much cleaner!) you still need <avr/pgmspace.h> for the prototype to strcmp_P().

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

Thank you - that's definitely tidier. I may try that when everything else is working.

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

Thanks for all your help; here's what I needed it for:

 

www.ulisp.com