[Top][All Lists]

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

Re: [avr-libc-dev] New pgm_read_ptr() macro?

From: David Brown
Subject: Re: [avr-libc-dev] New pgm_read_ptr() macro?
Date: Thu, 03 Jun 2010 13:04:18 +0200
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/20100317 Lightning/1.0b1 Thunderbird/3.0.4

On 03/06/2010 10:09, Weddington, Eric wrote:
Hi Jan,

Be sure to keep posts on-list.

I would be ok with adding this and adding Dean's new macro too.
Removing functionality from avr-libc gets into
backwards-compatibility issues.

However, I want to make sure that this has been tested out, and that
documentation is included. In both patches.

I've used it myself in applications, but it would be good for others to test it. In particular, it should be tested with different levels of optimisation (in case the __builtin functions don't disappear properly), and it should be tested with different language flags (in case type __builtin functions or typeof conflict with pendantic flags).

There is also, as you say, some documentation needed. The limitation of the macro is that it only works for sizes 1, 2 or 4. If you try to use it with a different size, you get a compile-time error, which is nice. Unfortunately, the error message is a bit obscure, and needs documentation. If anyone has a better way to produce an error message with risk of generating code, I'd love to know!

As another point, should we improve the consistency between eeprom.h and pgmspace.h by adding a "pgm_read_block" function/macro? This could, I think, be as simple as:

#define pgm_read_block(dst, src, n) (void) memcpy_P(dst, src, n)

Or it could be optimised as inline assembly.

Should the pgm_read macro then call this for other sizes, or should it give a compile-time error?

And if a general pgm_read macro is a good idea, should we also have an eeprom_read macro and an eeprom_write macro?



Eric Weddington

-----Original Message----- From: Jan Waclawek
[mailto:address@hidden Sent: Thursday, June 03, 2010 10:04 AM
To: Weddington, Eric Subject: RE: [avr-libc-dev] New pgm_read_ptr()


this macro is in fact only a wrapper over the existing pgm_read_xxx
macros/functions, alleviating the user from chosing the "right
one". All its magic happens BEFORE any code is generated, so
there's no reason why would this be more or less effective
size-wise (nor speed-wise).

The real benefit is in reduced source size (less typing),
especially if the "argument" is NOT a pointer but the object itself
(see end remark of original David's post).

The real drawback is that the user needs to understand its
limitations (=>  need for proper documentation).

Jan Waclawek

----- Original Message ---------------

I think it's not so much a philosophical change that
concerns me. I'm interested in how resulting code size would be
affected, as suggested by someone else on this list (sorry, I've
forgotten who it was).

Have you done some testing about resulting code size using
this macro compared to the more specific macros?

Code size is probably the *most* important factor that I
hear users focus on.

Eric Weddington

-----Original Message----- From:
org] On Behalf Of David Brown Sent: Thursday, June 03, 2010
9:03 AM To: address@hidden Subject: Re: [avr-libc-dev]
New pgm_read_ptr() macro?


Are there any opinions about whether a macro such as the
one I gave
below should be part of avrlibc?  It's not as clear-cut as
Dean's pgm_read_ptr() macro, which follows the existing
patterns in the headers.  Macros like mine are more like
template programming than the fixed-size macros, and thus are a
slight change in philosophy.

If the idea of a generic pgm_read(&x) macro does appeal, then
the same tricks can be used for other accesses such as wrapping
access in
"volatile", reading and writing eeprom, and atomically
accessing data.



How about a more general solution based on typeof and

#define pgm_read(v) ((typeof(*v)) \
(__builtin_choose_expr(sizeof(*v) == 1, pgm_read_byte(v), \
__builtin_choose_expr(sizeof(*v) == 2, pgm_read_word(v), \
__builtin_choose_expr(sizeof(*v) == 4, pgm_read_dword(v), \
({ char error[((sizeof(*v) == 1) || (sizeof(*v) == 2) || \
(sizeof(*v) == 4)) ? 1 : -1]; error[0]; }) \ )))))

The macro above looks a bit ugly, but it's not too hard
to follow.
Basically, we are using sizeof(*v) to figure out the size
of the data
you are wanting, and using it to pick the correct
pgm_read_xxx function
for the data size. We use __builtin_choose_expr instead of
?: to avoid
any unwanted expression evaluations or side effects, as
well as to allow
different sized return values. The "char error[...]" part
is a way to
force a compile-time error if the macro is used with
something whose
size is not 1, 2 or 4 bytes.

The result is that you can write:

char x; char* PROGMEM FlashPointer =&x; char* FlashPtr =

You can equally write:

static const PROGMEM uint8_t x8 = 8; static const PROGMEM
uint16_t x16 = 16; static const PROGMEM uint32_t x32 = 32;

void test(void) { volatile uint8_t y8 = pgm_read(&x8);
volatile uint16_t y16 = pgm_read(&x16); volatile uint32_t y32
= pgm_read(&x32); }

Now your pgm_read's are independent of the type, assuming
they have a
compatible size.

Personally, my preference would be to change the semantics
to remove the
"&", so that you would write:

char* FlashPtr = pgm_read(FlashPointer);

This is just a small change to the macro, but I think it's
neater - you
are reading an object from flash, rather than reading
data from an
address in flash. But such a change would require a name
change to avoid



_______________________________________________ AVR-libc-dev mailing
list address@hidden

reply via email to

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