A better alternative for pgm_read_word etc...

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

The macros pgm_read_word etc have a few shortcomings:

  • They are different macros for each size.
  • They are not type-safe - casts are needed when storing pointers in flash.
  • They don't allow the compiler to optimise the case when the result is known at compile time.

Here is a C++ function template which solves those problems:

template  inline T __attribute__((artificial, const))
pgm_read(T const * ptr) {
  if (__builtin_constant_p(*ptr)) return *ptr;
  T result;
  switch(sizeof(T)) {
    case 1:
      asm("" : "=r" (result) : "0" pgm_read_byte(ptr));
      break;
    case 2:
      asm("" : "=r" (result) : "0" pgm_read_word(ptr));
      break;
    case 4:
      asm("" : "=r" (result) : "0" pgm_read_dword(ptr));
      break;
    default:
      memcpy_P(&result, ptr, sizeof(T));
      break;
  }
  return result;
}

In C a macro must be used and the type of the local variable can't be computed. Some GCC builtin function come to the rescue:

#define pgm_read(ptr) ( \
  (__typeof__(*(ptr))) \
  __builtin_choose_expr(__builtin_types_compatible_p(float, __typeof__(*(ptr))) \
    || __builtin_types_compatible_p(double, __typeof__(*(ptr))) \
    || __builtin_types_compatible_p(long double, __typeof__(*(ptr))), \
    __builtin_constant_p(*(ptr)) ? *(ptr) : pgm_read_float((ptr)), \
    __builtin_choose_expr(sizeof(*(ptr)) == 1, \
      __builtin_constant_p(*(ptr)) ? *(ptr) : pgm_read_byte((ptr)), \
      __builtin_choose_expr(sizeof(*(ptr)) == 2, \
        __builtin_constant_p(*(ptr)) ? *(ptr) : pgm_read_word((ptr)), \
        __builtin_choose_expr(sizeof(*(ptr)) == 4, \
          __builtin_constant_p(*(ptr)) ? *(ptr) : pgm_read_dword((ptr)), \
          __builtin_constant_p(*(ptr)) ? *(ptr) : ({ \
	    uint64_t result; \
	    memcpy_P(&result, ptr, 8); \
	    result; \
	    }) )))))

With C++ the need to call pgm_read explicitly can be eliminated:

template  struct progmem {
  T value;
  template  operator __attribute__((artificial)) Q () const {
    return pgm_read(&value);
  }
  operator __attribute__((artificial)) T () const {
    return pgm_read(&value);
  }
};

template  inline T pgm_read(progmem const* ptr) {
  return pgm_read(&ptr->value);
}

Which may be used like:

progmem PROGMEM b = { 6 };
progmem PROGMEM e[] = { 4, 5, 6, 7, 8 };
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Here is a C++ function

Hope you have your hard hat on, Timothy! Duck and cover! :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

It's OK Johan, I'm sure you will BOTH find it useful ;-)

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

At the end of the day, both pgm_read_xxx and the loops with C++ are only a poor replacement for true memory classes a.k.a. named memory spaces.

JW

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

Quote:

It's OK Johan

You know I was just having a bit of fun.. :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

With C++0x with existing GCC extensions program memory string literals can be implemented, for example:

typedef progmem const* const flash_string;

template  struct flash_str {
  static progmem const PROGMEM value[sizeof...(args) + 1];
};

template  progmem const PROGMEM flash_str::value[] = {args..., 0};

template constexpr inline flash_string operator "" _flash () {
  return flash_str::value;
}

flash_string hello = "Hello"_flash;

That's only partly tested due to the current lack of a suitable compiler, and definitions should be included in header files. Alas one cannot move vtables to flash without compiler changes.

Another C++ trick is to make erroneous statements like:

PORTA = _BV(SE);

result in compile time errors.

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

TimothyEBaldwin wrote:
The macros pgm_read_word etc have a few shortcomings:
[...]
Here is a C++ function template which solves those problems
Nice. A few weeks ago I did a similar thing for the same reasons, such that an array in progmem could be read with standard (val = arr[idx]) syntax and an attempt to write to such an array (arr[idx] = val) would result in a compile-time error.

However, I only needed a byte array at the time, so that is all I implemented. Your more generic solution is very welcome. Thanks.

CH
==