-
-
Notifications
You must be signed in to change notification settings - Fork 64
F() Flash Macro No Longer Recognized #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
F() is useless in megaAVR architecture (it only increases flash occupation). Which code are you trying to compile? |
But for compatibility with existing sketches, all other cores have a working |
Also this core has it, this is why I'm asking for the code 🙂 |
The code is the Adafruit_IO_Arduino library. |
I just compiled one random example from that library and I didn't get any error. Could you provide more info? |
Here is an example sketch that uses the library. |
Got it. This patch makes the project compile again diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h
index 1f02a58..3891105 100644
--- a/cores/arduino/Arduino.h
+++ b/cores/arduino/Arduino.h
@@ -28,6 +28,7 @@
#undef F
#define F(str) (str)
+#define __FlashStringHelper char
#ifdef __cplusplus
extern "C"{ But I'm not 100% sure it is the right approach. |
Confirmed. |
@facchinm even though it would fix this exact issue, it's not a very elegant solution. Will this mess up other libraries that use |
I think it will, since libraries might overload a method for both How does SAM and SAMD solve this? I suspect like above? |
@facchinm, could you please explain a little further why the F() macro is useless for the megaAVR architecture? I did not understand what you meant by "only increases flash occupation". Both the Uno R3 and the WiFi R2 have flash memory; are they used differently? |
Classic AVR architecture is referred as modified Harward, because data and memory are in different address spaces (so address 0 could refer to ram or flash, and there are different functions to access them). |
Great, thanks! |
Anyway, |
Does that mean that the ATmega328P in the Uno R3 also does not need to use the F() macro? |
Does this also mean that instead of using
or
Sorry for all of the questions. |
What makes you ask that? The 328P uses the "original" AVR architecture, which does need the
It would certainly have been better if there was a proper public type for this with a better name, but some libraries will need to use this wrapper type to properly handle both PROGMEM and regular strings. And, as you say, it's already used in the wild, so we need to keep it working if possible. As for the proper way to do this, I think we can just implement Looking at SAMD, it does this by overrinding the and defining I think the same approach would work for megaAVR. You should probably include all of |
No, that has completely different behaviour (it allocates a dynamic string on the heap).
This (or just The point of this issue is to ensure that code that is written for AVR and uses |
I asked that because both the ATmega328P (Uno R3) and the ATmega4809 (Uno WiFi R2) have mega in their name. I had assumed the 328P was Classic AVR and the 4809 was the new architecture, but I then realized both had mega in their name and thought I should clarify. |
That is correct.
And that is, indeed, confusing :-) |
Hi guys, I'm running in the same issue as mentioned above. @matthijskooijman write:
I agree that using the explicit cast also fixes the issue: currently I need to either use the older 1.8.4 of megaAVR or fix it locally to get my project working. |
SAMD, SAM, ESP32, STM32 define it this way:
Teensy uses essentially the same define, but with C style syntax:
ESP8266 uses this:
|
Short feedback here:
I've tried this approach. It didn't work! I use the MFRC522 library. When adding
it worked here at least... |
The reason #define F(string_literal) ((const __FlashStringHelper *)(string_literal)) does not work is that those libraries see that and use pgm_read_byte() to read it instead of just reading the variable. But variables declared PROGMEM in megaavr-land have the address offset, so it's just a pointer to the location in flash, not the location in RAM where the flash is mapped to. and pgm_read_byte() (effectively) does the corresponding adjustment (actually, it uses lpm, like the classic AVRs, but if you think of it as making the corresponding adjustment, that's conceptually equivalent) - so we're taking a pointer that points to the location in the memory address space, but then using a function to read from it that expects an address in the flash... I think #define __FlashStringHelper char is a much better solution here... I don't see any solutions that maintain perfect compatibility but don't kneecap the advantages that the megaavr architecture has... I mean, I guess you could just go back to making it put the strings into PROGMEM and returning a __FlashStringHelper - but then we're back to wasting flash - and I think pgm_read_byte() is probably significantly slower too.... To what extent are libraries that break with that fix around in the wild AND NO LONGER MAINTAINED? Because if they're still being actively maintained, this should be fixed by the library authors IMO - should be a dead simple fix to just check for a register that only exists on megaavr (how about VPORTA? That will work for all the megaavr parts, including megaAVR 0-series, tinyAVR, and the new DA-series parts) with a #ifdef and not include the problematic functions if it's there - would probably be trivial to just PR problematic libraries. Like, in the time people have been discussing it in this thread, we could have probably found and PR'ed all the libraries people are using that the naive compatibility layer breaks... |
Oh, so there are still two separate flash and data memory spaces on megaavr, it's just that flash is mapped into data with an offset. That's rather unfortunate, but I can imagine this has compatibility advantages at some level.
As I said in #62 (comment), this would break for some libraries:
An example of this is the Print class, but there are likely also libraries in the wild that doe this. So making The SAM/SAMD solution is: keep The SAM/SAMD solution will not work for megaavr, because megaavr (as @SpenceKonde pointed out) already defines Another way to look at it, is that the "Arduino API" currently implies that:
This is not documented API, so we could maybe change this, but preserving some compatibility with this behaviour is important IMHO. Given that
That does not sound like a good approach, we should rather define a clear API (adding some define that describes the semantics of this core) rather than relying on an arch-specific exception. However, writing this, I think there would be an alternative fix:
This means that:
This does break point 1. above, since
The above does currently work on AVR/SAM/SAMD and is used in practice (I've been using it in a few projects). So maybe this approach is not quite perfect either... One other approach, which I think is essentially what @SpenceKonde proposes, would be to:
This allows libraries and sketches to, omit overloads for Hm, but this would then break libraries that do:
Since that ends up with a pointer into flash, casted to normal I have also used code like this in practice, since One final approach would be to:
However, that would then break applications that actually want to read from hardcoded flash offsets (e.g. to read a bootloader version from flash) or want to overwrite PROGMEM variables with Hm. I'm not so sure what the best way forward would be here. What a mess :-S Maybe we should byte the bullet and implement some better alternative for |
Another problem with changing the return type of void someFunction(const __FlashStringHelper* msg);
// void someFunction(const char* msg); // this does not exist I have at least 24 of those in one of my libraries. I agree that redefining It seems like we need a layer indirection, a set of On all architectures, if someone creates a I agree with @matthijskooijman. This is a bit of a mess. |
Worth noting that with current implementation (I think everywhere) if they cast it to a __FlashStringHelper* and put it into PROGMEM explicitly, that will work (and it won't work if treated like a const char* - but it will if you offset it by 0x4000 (megaAVR 0-series) or 0x8000 (tinyAVR 0/1-series) |
Re-reading the above, I think I can see two ways to fix this:
I would tend to prefer option 2., since the resulting binary code is more efficient and clean, but there might still be corner cases. Option 1. is probably safer, since it just makes things work just like on regular AVR. Any thoughts on these options? Any additional breaking cornercases I forgot? |
I like option 2 as well.... I'll give that a spin on megaTinyCore.... |
Cool! Re option 1, the performance overhead might actually be even smaller than I thought. I was thinking As for the cycle time, the AVR Instruction Set Manual says:
So that makes at least 3 cycles for |
Giving this a bit more thought, and seeing the PRs that try to implement both approaches, I suspect that maybe option 1. is the better one after all. There is some overhead, but it should really work with all code out there (AFAICS) and does not complicate the include path even more, which is worth a few cycles IMHO. |
I've been going through all those issues (#53, #62), PRs (#54, #82, #85, #87) and commits (a0f6beb) again and frankly, my head hurts 🤕. All in all it seems to me that by @facchinm 's commit a0f6beb we've implemented (restored actually) option 1 as described by @matthijskooijman here. @matthijskooijman can you confirm that I copy you correctly? Also @giulcioffi's PRs are outdated a bit by #62 (comment) (this muddled the water when investigating this issue) - but no fault of her own, rather result of the long-standing PR we failed to integrate. |
Yes, I agree. Now, -API includes the main avr-libc There were some alternative PRs to compare the impact of this approach (but of those, I think only #85, implementing option 2, is relevant, since #87 has major compatibility problems, and #82 essentially just does what current master does with a bunch of duplicated code). So if we still want to consider option 2, we might want to look at the result of #85, but to really compare, that should be rebased on current master so it really compares option 1 with option 2 (it now compares the older, broken no-op |
Thank you very much for your feedback @matthijskooijman. Let's go with option #1 for now and schedule another look for this issue at a later point in time 🙏 . |
When is the next version expected to be released that will include this fix? |
@facchinm What do you reckon? |
@aentinger version 1.8.7 was released with the fix just before the end of the year 🙂 |
The F() flash macro used for storing strings in flash memory no longer appears to be working as of the 1.8.5 version. The following error is reported during compile for each use.
error: cannot convert 'const char*' to 'const __FlashStringHelper*' in return
#define F(str) (str)
The text was updated successfully, but these errors were encountered: