Arduino: Support %S to printf PROGMEM char array

Created on 23 Jan 2018  路  12Comments  路  Source: esp8266/Arduino

Hello,

I noticed that %S to printf PROGMEM char array was not supported on the ESP8266 Arduino.

It simply print "%S" instead.

Is this volunteer ? If yes, are there existing usage to implement it simply ?

Thank you

core enhancement

Most helpful comment

The problem is that ESP8266 memory architecture does not support mapping flash into dport (data memory space). It can be mapped only into instruction memory space, and for instruction memory only 4-byte-aligned access is allowed. So while we can put a const char* string into flash, it will not be possible to address it bytewise, which is what most of the existing code does. Therefore we have to place it into RAM.

edit: some projects built around the esp8266 (like esp-open-rtos) feature an "unaligned access exception handler", which is a software workaround for this limitation, at the cost of performance. The handler gets invoked whenever code tries to access instruction memory as if it was byte addressable. Then it does the equivalent of what pgm_read_byte does, and returns from the exception. From application perspective, the byte gets read successfully, but this costs more CPU cycles.

All 12 comments

@earlephilhower Great improvement! Did I get it right that after igrr/newlib-xtensa#5 settles, there would be no need to use F() helpers and special str_P funcions anymore? const char * will go directly to flash and str functions will manage them automatically?

char * will not go directly to flash, but there functions like printf will support PROGMEM strings as format arguments and string arguments.

@igrr I've always wondered: what's the reason of keeping const char * into RAM? it is not intended to change anyway. For me, everything flash strings brings in, can be achieved by const char * on other platforms.

The problem is that ESP8266 memory architecture does not support mapping flash into dport (data memory space). It can be mapped only into instruction memory space, and for instruction memory only 4-byte-aligned access is allowed. So while we can put a const char* string into flash, it will not be possible to address it bytewise, which is what most of the existing code does. Therefore we have to place it into RAM.

edit: some projects built around the esp8266 (like esp-open-rtos) feature an "unaligned access exception handler", which is a software workaround for this limitation, at the cost of performance. The handler gets invoked whenever code tries to access instruction memory as if it was byte addressable. Then it does the equivalent of what pgm_read_byte does, and returns from the exception. From application perspective, the byte gets read successfully, but this costs more CPU cycles.

@igrr I completely understand this architectural factors, but can it be done transparently in libc, like igrr/newlib-xtensa#5 did with strcpy?

The problem is that igrr/newlib-xtensa#5 is only transparent as long as the code uses printf and does not read the string byte by byte.

A fully transparent workaround is to force the compiler to use 32-bit loads only, even when accessing 1-byte quantities. This was implemented in https://github.com/jcmvbkbc/gcc-xtensa/commit/6b0c9f92fb8e11c6be098febb4f502f6af37cd35, but was never included into the mainline version.

For the discussion of different approaches to this problem (unaligned exception handler, mforce-l32), please have a look at https://github.com/SuperHouse/esp-open-rtos/issues/11.

Thank you for the links. So, how do you think why none of this got mainlined?

My $0.02...

Reading bytes by exception handler isn't just slow, it's glacially slow. One "ld" turns into an exception trap, stack push, context switch, ~60 instructions of fixup code to figure out what insn failed and fudge the stored registers to make it look like it passed, context switch and complete stack pop. Repeat that for every single byte of every single string. You can look at the assembly code in the Linux kernel for the LXR architecture that does this, it's amazing...

Using only 32-bit accesses even for bytes adds (IIRC) 3 insns to every single non-32bit memory read to shift and mask. Not just PROGMEM accesses, everything has to be done this way. That's pretty painful, too, for the case where most people won't ever benefit from it. You'd also need the OS routines burned in ROM and in the SDKs to be rebuilt with this, as OTW you'd just fault in them when they tried to access PROGMEM stored parameters.

4160 will fix the original issue here(%S format), but the latest SDK as a silent update added a misaligned access exception handler that makes byte-wise PROGMEM accesses work (albeit kind of slowly).

Closed via #5376

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Marcelphilippeandrade picture Marcelphilippeandrade  路  3Comments

hoacvxd picture hoacvxd  路  3Comments

hulkco picture hulkco  路  3Comments

SmartSouth picture SmartSouth  路  3Comments

tiestvangool picture tiestvangool  路  3Comments