This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/MSP430-GCC-OPENSOURCE: printf() implementation in GNU gcc (Mitto Systems Limited)

Part Number: MSP430-GCC-OPENSOURCE
Other Parts Discussed in Thread: MSP430F5529

Tool/software: TI C/C++ Compiler

For debugging purposes I would like to have printf available. However, printf didn't do anything when I added it into some example code pulled from resource explorer in CCS.

I've been unable to find details about how printf is implemented and where it prints by default. I tried to find it in the compiler source code but grepping around for printf returns too many results to parse efficiently. Any insight would be appreciated.

  • Hi,

    Could you please provide some further information:

    • What device are you using?
    • What is the version of MSP430-GCC are you using?
    • What OS are you developing on?
    • What CCS version are you using?

    You should be able to see the printf output in the "CIO" console. This console view should automatically open when something is printed using printf.

    Is your program running correctly apart from the missing printf output? i.e. are you able to observe execution reaching the printf and continuing after it?

    In CCS 9.1.0 there is a "Hello World" example available when you create a new CCS project. Can you confirm whether or not that works for your device?

    Regards,

  • Mike,

    Could be lots of things, but could be stack size.

    This sounds like a problem I ran into. Printf and its variants use a huge amount of stack, about 1K on the TI DSPs we use, not sure how much on an MSP430. Until we realized this and increased the stack size a lot, the symptom was that printf just didn't do anything. We used the memory viewer to watch the stack area, and ended up filling it with a known pattern then watching how much of it got overwritten.

    Lloyd

  • If the problem is caused by a limitation of stack size I would recommend trying the "-mtiny-printf" option which is available since MSP430-GCC 8.2.0.52. It is documented in the MSP430-GCC User Guide (slau646).

    In addition to ROM savings achieved by removing support for reentrancy from printf(), this option also reduces RAM usage since buffering of the output string that is generically required by the printf() family of functions has also been removed. The string being output still gets buffered before being output (if necessary), but it happens at a lower level, in the write() syscall, instead of in printf().

    Furthermore, the call graph from printf isn't as deep with "-mtiny-printf", as some supporting functions have been removed, so stack is less likely to overflow.

    As a quick test on the MSP430F5529, I observed that you get an extra ~1000 bytes of usable RAM by using printf() with "-mtiny-printf" compared to the default.

  • Thanks for the tip, I suspect my application will be dealing with stack constraints a lot so this will definitely be helpful.

    In this particular case the printf was implemented inside an ISR which is not recommended. I put a simple printf inside of an empty project and now have no issues getting it to read out in the CCS console. 

  • I'm currently using GNU v7.3.2.154 which means that I don't have "-mtiny-printf" available unfortunately. As I mentioned in my other reply, I created a new empty CCS project and then added a simple printf in main and I have no issues printing to the console.

    Now my question is how does that work under the hood? If I wanted to run my project outside CCS, how would I get printf to print over a serial connection via minicom for instance? I couldn't find detailed information on how the CIO console implements the serial connection so I'm still unsure how printf is able to write to it with no additional config.

  • Mike,

    I don't think you can use CIO unless a debug probe is installed since it goes over JTAG. I handled this by writing a routine that uses sprintf to build the string and then kicking off an interrupt-driven uart channel to send the result out.

    Lloyd

  • EmbeddedMike said:
    I'm currently using GNU v7.3.2.154 which means that I don't have "-mtiny-printf" available unfortunately.

    If at all possible I would recommend upgrading to MSP430-GCC 8.2.0.52 since you'll benefit from other features/bug fixes as well. If you got to "Help>Check For Updates" in CCS it should offer to update MSP430-GCC.

    Alternatively, you can download the toolchain as an archive from here: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/latest/index_FDS.html

    EmbeddedMike said:
    Now my question is how does that work under the hood? If I wanted to run my project outside CCS, how would I get printf to print over a serial connection via minicom for instance? I couldn't find detailed information on how the CIO console implements the serial connection so I'm still unsure how printf is able to write to it with no additional config.

    One way is to override the "write" function with your own implementation which sends the string over UART or wherever you want it to go. If you have the MSP430-GCC sources, you can see the default definition of write(), which uses CIO in "newlib/libgloss/msp430/write.c".

    The prototype for write() is:

    int write (int fd, const char *buf, int len);

    CIO only works with a debug client which understands what the CIO commands mean. The client does the heavy lifting by reading the string to be printed (in the case of "write") from a specific memory location.

  • Thank you for the detailed information, that resolves my primary issue. However, as a follow up I have two questions:

    1. How does "stdout" work in the context of the MSP430? In the printf documentation (found in sprintf.c) it states the following:

      DESCRIPTION
      <<printf>> accepts a series of arguments, applies to each a
      format specifier from <<*<[format]>>>, and writes the
      formatted data to <<stdout>>, without a terminating NUL
      character. The behavior of <<printf>> is undefined if there
      are not enough arguments for the format. <<printf>> returns
      when it reaches the end of the format string. If there are
      more arguments than the format requires, excess arguments are
      ignored.

      I didn't see any mention of write() in the source code for printf() and it doesn't seem like MSP430 has a linux-like stdout stream. I'm missing the connection between printf and write.

    2. What happens if my released code makes a call to printf and it hasn't been overridden? Would it just do nothing and move on or would the default implementation of printf hang the code?

  • Thanks for the info on the CIO console, that makes sense. Is the uart implementation with sprintf publicly available? If it's something you'd rather keep internal to your work, that's totally understandable.

  • EmbeddedMike said:
    How does "stdout" work in the context of the MSP430? In the printf documentation (found in sprintf.c) it states the following:

    DESCRIPTION
    <<printf>> accepts a series of arguments, applies to each a
    format specifier from <<*<[format]>>>, and writes the
    formatted data to <<stdout>>, without a terminating NUL
    character. The behavior of <<printf>> is undefined if there
    are not enough arguments for the format. <<printf>> returns
    when it reaches the end of the format string. If there are
    more arguments than the format requires, excess arguments are
    ignored.

    I didn't see any mention of write() in the source code for printf() and it doesn't seem like MSP430 has a linux-like stdout stream. I'm missing the connection between printf and write.

    puts() is the missing connection. The string you pass to printf() will go from printf() to puts() to write(). This is a simplification since there are a number of *printf*, *puts* and *write* functions in between which handle reentrancy (or further formatting in the case of *printf*), but these are the main functions.

    The purpose of printf() is just to format the given string by inserting the given arguments into the format specifiers in the string. Once the string is formatted it is passed to puts().

    You can observe that if you pass a constant string with no format specifiers to printf(), the compiler will optimize it directly to puts(), because the formatting functionality of printf() is not required.

    puts() doesn't need to do much in single-threaded applications, it can just pass the formatted string to write(). write() is considered a low-level system call, and it assumes that it is safe to write the string to the specified stream once it is called. In multi-threaded applications, puts() will acquire locks to ensure write() isn't called for the same stream at the same time.

    "stdout" doesn't really have a specific meaning for MSP430, since there is no OS running you cannot guarantee where a string sent to "stdout" will go. It depends on how write() is implemented and the debug server/client implementation.

    It looks like the CCS CIO mechanism understands the difference between stdout and stderr though. The following prints "Hello World" in a red font in the CIO console (to indicate stderr):

    fprintf(stderr, "Hello World\n");

    When using "stdout" instead, the text appears in regular black font.

    EmbeddedMike said:
    What happens if my released code makes a call to printf and it hasn't been overridden? Would it just do nothing and move on or would the default implementation of printf hang the code?

    Again, it depends on how write() is implemented. The default implementation won't hang since the CIO mechanism just requires the output string to be copied to a specific memory address, which the CCS debug client will then read and interpret. So when there is no debugger connected, the code still writes the printf() string to the memory address, but it just continues afterwards and doesn't wait.

    A quick test by putting code to blink and LED, after a printf() call confirms this is how it works.

    If you override write() to send characters over UART and that code loops whilst it waits for some packet to be received, it could hang.

  • You've been extremely helpful, I really appreciate the thorough responses!

  • Mike,

    Sorry I can't release the code because it was paid for by a client. But it's very simple, a function called something like MyPrintf using va_args, pass the args to sprintf, and send the resulting string to your own uart printing handler.

    Lloyd

  • lslonim said:

    Sorry I can't release the code because it was paid for by a client.

    No worries!

    lslonim said:

    But it's very simple, a function called something like MyPrintf using va_args, pass the args to sprintf, and send the resulting string to your own uart printing handler.

    Thanks for the info, that makes perfect sense.