My AVR printf seems to be broken - Page 1
Forum section: electronics / general

author: nali
author_rank: Frequent Contributor
timestamp: February 15, 2024, 11:23:09 am
content: I'm having a look at the AVR64EA48 Curiosity Nano board with a fresh install of Microchip Studio (really fresh, it's a new PC). Used MCC to set up the usart & LED gpio and enable printf redirection.LED blinky works fine, but Hello World crashes. Basically if I printf a single character it's OK but 2 or more characters ends up in a loop transmitting 0x00 over the usart.I've checked that the MCC generated files include code for redirection which seems to be OK.Code:int USART1_printCHAR(char character, FILE *stream){while(!(USART1_IsTxReady()));USART1_Write(character);return 0;}FILE USART1_stream = FDEV_SETUP_STREAM(USART1_printCHAR, NULL, _FDEV_SETUP_WRITE);Debugging doesn't help as it just jumps from source code to directly to some random assembly code. Now I'm out of ideas. Can anyone give a clue?Code:int main(void){SYSTEM_Initialize();sei();while(1){//These print OKprintf("a");printf("b");//The following line crashesprintf("cd");}}

author: tellurium
author_rank: Frequent Contributor
timestamp: February 15, 2024, 12:47:43 pm
content: printf() call, depending on the argument, can be compiled into different things. Depends, so please check.If given a one-char string, it can inline the call to a single char output routine. This will not trigger variable access.If format specifiers are used, then a real printf() would be called.If no format specifiers are used, like in printf("cd"), then instead of printf, something like puts("cd") can be called, and that generates variable access.So, I would:1. check the generated assembly for printf("a") and printf("cd") calls.2. Create a simple USART1_printString(const char *s) and call it, USART1_printString("cd"). If that crashes too, then the issue is with the variable access - did the startup code execute OK and initialized the data section?

author: nali
author_rank: Frequent Contributor
timestamp: February 15, 2024, 01:02:36 pm
content: That would make sense why the difference between the lines of code. I'll see what I can tell from the ASM, but I can say that using format specifiers fails e.gCode:printf("b %d",n++);as that's what I was using, I just simplified the code as much as possible to narrow it down.

author: cv007
author_rank: Frequent Contributor
timestamp: February 15, 2024, 01:43:58 pm
content: Here is a generic, stand-alone program to use the uart with your board (any uart)-https://godbolt.org/z/YnM6b3xG3A little more complex than a simple example, but still simple enough with the added benefit it can be easily used for any uart. I created this in the online compiler, but did also just test it on a tiny3217 with changes to the led pin and usart/tx pin to match my board. Another advantage of the example is all the code is right in front of you.I would also generate an lss listing and search for vfprintf, it should be there if doing more than a simple string.

author: nali
author_rank: Frequent Contributor
timestamp: February 15, 2024, 02:14:00 pm
content: So the assembly listing doesn't really help too much. The main() listing is clear enough, but the jump to the offending printf() just goes to a lump of code with no debug symbols, it just says "no source file" which to me suggests something is broken - the auto generated usart1.c routines do appear elsewhere in the listing so I'm not sure what is going onIf I can't get this running today I may shelve the usart stuff for now. This is for a proof of concept for which the usart was to implement a simple CLI just for setting a couple of parameters which I can hard-code to get something done for now.

author: cv007
author_rank: Frequent Contributor
timestamp: February 15, 2024, 04:58:56 pm
content: QuoteI use MPLABX on a linux pc, and there is a project option to enable generation of an 'lss' file, which I always enable to get a nice asm listing- which comes from avr-objdump. Not sure what Microchip Studio has for options, but if using a gcc toolchain it should be there.If you are using XC8, then you probably do not have that option available (then requires some work do do on your own). Also, if using XC8 my example will not work as they felt the need to mess with the FILE struct in their version, so they renamed some things and also removed udata which the example uses to pass info via the FILE*.If you do not have a gcc toolchain, you can add one-https://www.microchip.com/en-us/tools-resources/develop/microchip-studio/gcc-compilerswhich will allow more control/options in the ide project options, and is a 'normal' version of gcc rather than a heavily modified version (xc8). Maybe MCC requires XC8, not sure as I do not use MCC.Attached is an lss listing of my previous example (added .txt on my own to make forum upload happy)Also note gcc is byte address oriented, and your asm listings from the ide are word oriented (and are crap).edit- There could be code you are not showing and assume MCC would deal with this, but as given/shown the USART1_stream is not being used, and would need to use fprintf-fprintf( &USART1_stream, "SREG = 0x%02X\n", SREG );or at least I would try it and see what difference it makes. If MCC was taking care of this, I would think this stream setup would be hidden away.

author: nctnico
author_rank: Super Contributor
timestamp: February 15, 2024, 05:25:27 pm
content: A printf which prints a constant string, is typically replaced by a call to puts(). It could be worthwhile to try and print the string using puts() as a test to see if you get the same behaviour.

author: nali
author_rank: Frequent Contributor
timestamp: February 15, 2024, 05:52:19 pm
content: Ther's something janky going on here, and it seems to be the xc8 compiler doing funny things with string constants and arrays rather than printf. Leaving aside the printf command for the moment, if I declare & initialise an array it doesn't work if I initialise it like so:Code:char str1 = {'a','b','c','d'};char str2 = {'0','1','2','3','4'}; //this doesn't populateIf you look at the screenshot str1 initialises OK but not str2 (unless I remove the last element) as you can see in the debug. With a correctly populated array I've successfully created/used a sendstring() and also used printf().I write code infrequently so I have to stop & think every time I have to do it (and having a headcold doesn't help), but that just seems plain wrong to me. I might try gcc as@cv007suggests - or I might just decide to say FU Microchip and choose another platform (unless of course it's me being stupid not them...).

author: cv007
author_rank: Frequent Contributor
timestamp: February 15, 2024, 06:20:59 pm
content: QuoteI would not use XC8 for different reasons. First they decided to go their own direction so you now have a different flavor of gcc and once you are into 'their version' you will probably find it hard to get back to a 'standard' version. You will also have to put up with limited optimization options, or remove the limitations (its gcc, you can).They also seem to be pretty slow in updating their source code archives, and in today's example I can download the source archive for xc8 v2.45, but the libc included does not match what they are actually using (they give you the 'normal' libc source, not their version). Want to look at these stdio functions? You will have no source code to look at.Bypass all that trouble and just use a normal version of gcc, whether downloaded from them, or sourced from an arduino setup (extract from ide download), or whatever. You will be producing code like all the other normal avr citizens and when help is needed you will not have to preface your question with- 'oh, by the way I'm using the XC8 compiler'.

author: nctnico
author_rank: Super Contributor
timestamp: February 15, 2024, 07:15:52 pm
content: I second the suggestion to use gcc. I'm not familiar enoug with the AVR platform but if this platform has different memory areas (seperate code and RAM memories which are addresses in a non-uniform way), then it doesn't really have a stack so declaring initialised (constant) variables in functions is really iffy as the compiler will have to convert this into a global variable. C doesn't really scale well on such platforms (like 8031/8051 and 8 bit PICs) as these don't really support using pointers and in some cases don't even have a stack.

author: eutectique
author_rank: Frequent Contributor
timestamp: February 15, 2024, 10:56:08 pm
content: str2is not a C string, it does not terminate with zero.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 15, 2024, 11:14:19 pm
content: Quote from: nali on February 15, 2024, 05:52:19 pmThose are not string literals, but just array initializers. You need to brush up on your C, first thing, before blaming the tools.str1 will contain the equivalent of "abcd", because you declare a 5-item char array and since you omit the 5th initializer, the compiler initializes this 5th item to zero, as per the standard. So you end up with something that looks like a C string.str2 initializes all 5 items of the array, so there is no terminal zero, this doesn't form a zero-terminated string. If you call any function expecting a zero-terminated string, passing str2, anything can happen, including a "crash".The reason the debugger shows 0xFF for all items of str2 here may be misleading. If you step one more statement in the debugger past SYSTEM_Initialize(), what does it show for str2?So it's probably just a misleading information, making you make the wrong conclusions.A zero-terminated string of 5 characters must contain 6 bytes, with the last being zero. And btw, you can use string literals to initialize char arrays in C - no need to list the characters individually.The correct (and easier to type and read) version would be:char str2 = "01234";you can alternatively omit the array size here in this declaration, removing the possibility of messing it up:char str2[] = "01234";

author: cv007
author_rank: Frequent Contributor
timestamp: February 16, 2024, 12:43:05 am
content: QuoteThe avr has always had a stack, and in gcc the Y pointer (R28,29) is used as the stack frame pointer. You can create non-static local what-ever-you-want all day long, and the compiler will adjust the frame pointer as needed and put it on the stack. These newer avr's also deal with const data in a way that need no special handling, so you eliminate all these data in flash problems of previous avr.QuoteThese newer avr mcu's are alright for 8bit, simple to use and quite a bit nicer than their older versions, and if you say thank for the ide but I'll provide the compiler, then things will be fine. You certainly can choose something else, but it probably doesn't get any simpler and in any case you have to deal with the same c/c++ language. I would also not put much faith in the simulator, although if debugging on actual hardware you will be ok (you do have a board with built-in debugger).If you want to explore c++, I think these avr are a good option since the c++ standard library is not available so you will not get into any trouble and will be forced to learn c++ the language and not c++ the library. Same as previous example, but taken in the c++ direction-https://godbolt.org/z/WeqWWjPzewhich is a very nice way to go, but requires time/learning.

author: westfw
author_rank: Super Contributor
timestamp: February 16, 2024, 07:42:54 am
content: QuoteTurn on some optimization. Currently the code is obscured by its verbosity (-O0 produces really sucky code with all versions of gcc, AFAIK.) -Og is probably best for debugging, but I don't know if XC8 "free version" allows it.I'm not sure about mixing MCC and avr-libc things like FDEV_SETUP_STREAM :-(Oh: also add "-g" to your compile, do get additional debug info in the diasassembly, perhaps (depending on how the disassembly is being generated.)

author: nali
author_rank: Frequent Contributor
timestamp: February 16, 2024, 09:33:22 am
content: Quote from: SiliconWizard on February 15, 2024, 11:14:19 pmI'm aware of that thanks. Although I code infrequently I do understand the concept of arrays & strings. I'd initially started this simple(!) journey with something like:Code:uint8_t count = 0;while (1){printf("Counter value is: %d\r\n", count++);_delay_ms(500);}but ended up manually plugging values into arrays to try to work out what was going on. I printed out everything OK using my own version of writeString() which took the array and a count rather than piss about with null terminated strings.QuoteI looked at it again today as I wondered if some stuff was being optimised out. If I populate an array fully (including null if defining as a string) it doesn't workQuoteSee aboveQuoteLook at the code below. Am I doing anything wrong? (apart from not fully populating str1, str3)Code:int main(void){char str1 = {'a','b','c','d'};char str2 = {'0','1','2','3','4'};char str3 = "ffs";char str4 = "zxcv";SYSTEM_Initialize();sei();while(1){_delay_ms(100);printf(str1);printf(str2);printf(str3);printf(str4);}}This is the debug at the breakpoint after SYSTEM_Initialize() As you can see str1 and str3 are as expected, str2 & str4 remain at 0xff.

author: nali
author_rank: Frequent Contributor
timestamp: February 16, 2024, 10:39:35 am
content: UPDATEStarted a new GCC project, works fine!Took me about 3 minutes to make the project, configure MCC & run to print a rolling count(as it should)Thanks to all who took the time to reply