Shop Learn
An inquiry into the state of C dev tools for the P1 — Parallax Forums

An inquiry into the state of C dev tools for the P1

I've been working with the Prop 1 for about 6 months now for work, and have had rather inexplainable bugs or odd behavior at times (eg, the first line of code in main() being a print statement that doesn't run), or other hard-to-track-down issues that are hard to reliably reproduce. While I am not an expert in C programming, I have done a fair amount of it already, and am perplexed by issues like these. Is propgcc to blame? Or is CMM Compact with -Os Size optimization furthering complications?

My experience before the Prop in C is Arduino/Nios II platforms, and in the case of the latter there's a wonderful debugger interface where I can go line-by-line, see what ASM the C code compiled to, peek/poke registers and memory values, etc.

The Prop 1 has been not nearly as forgiving for the applications I'm using it for (commercial environments where reliability is important (thus, I unfortunately can't post my code)). The code isn't particularly complex usually, making use of Parallax's libraries and a small handful of debugged and tested custom libraries (though I never do things like malloc, preferring to keep all memory uses and buffer sizes predetermined) and larger than necessary.

While I absolutely love the hardware design of the Prop 1, I feel it's become a significant chore to develop for in a professional environment (though I can definitely see the appeal for hobbyist/educational use). I know there's the ViewPort tool, which looks like it would be useful, but the registration page seems to be down. To tl;dr everything, I'm looking to figure out the following:

-is propgcc (C in particular) completely refined and robust (to a point where it's ready for industry use?)
-are there tools to look at what my C code has actually been turned into (asm-wise), preferrably where I can see it in a form of 'this line of C code -> these line(s) of assembly code'
-any other advice for Propeller C development and debugging weird or hard-to-reproduce issues

Thanks!

Comments

  • I have been writing C code on the P1 for several years now and have not run into your issues.

    What tools are you using for development?

    I have been using SimpleIDE and try to use LMM as it generates pure assembly code that has good timing specs although it uses a lot of memory.

    There is an emulator that could be used for debugging but I find just using LED's or print statements to be sufficient.

    Mike

  • I've been using SimpleIDE and CMM Compact, but have found that switching to LMM Compact doesn't seem to resolve my problems. LED's and prints are what I've been using so far, but there's still... very odd issues I've run into.

    For example, I do a normal function call to something I wrote. I have print statements in said function to verify that it is working correctly, including a print statement at the very end (just before the closing curly bracket (I've also verified that it's not a bracket mismatch issue)) of the function. Right after I come back from that function (it's a void that populates a char* buffer I provide (I've already checked to verify that there's no overflow issues)) I have a print statement that never executes, and an LED state change (switching it from high to low) that never executes either.

  • Not to derail my own thread, but here's the relevant code (with non-relevant parts redacted, as this is for work). This wouldn't be the first time I've encountered an issue where, logically speaking, a line of code should execute, but doesn't.
    in the function being called, I read a sensor that gives me 2 bytes back (with the upper byte having only one nibble in use). This function gets that data, and then turns it into ASCII hex for sending to a console. I've already debugged the function.

    The function is part of a software library I wrote. Before it was all just one giant .c file, before being heavily optimized and put into isolated source files when going over it again.

    What I don't understand is why "finished" prints (from the function), but then the "read [redacted] line never runs. It's as if after it finishes parsing the data into the buffer (which is more than large enough, and I've verified does not overflow), it never returns to the calling function.


    While advice on tracking this issue down would be great, it's just one example of how having an in-circuit debugger where I can do single-stepping, see C vs the assembly it compiles into, and watch code execution in real time would be invaluable.

  • That almost sounds like running out of stack space, but if anything still works in LMM mode, that's certainly not the problem.

    If you could post a minimal sample that exhibits the behaviour that would be most useful.

    Or, to answer your questions:

    -is propgcc (C in particular) completely refined and robust (to a point where it's ready for industry use?)

    It hasn't been updated in forever, but it is stable and fully working.

    -are there tools to look at what my C code has actually been turned into (asm-wise), preferrably where I can see it in a form of 'this line of C code -> these line(s) of assembly code'

    Just like any other GCC, you can do something to this extent on the command line and get a nice listing.

    propeller-elf-gcc hello.c -mcmm -Os -g3 -Wa,-adslhn > hello.lst
    

    This will compile hello.c with -mcmm and -Os and dump a listing to hello.lst

  • I see you are using I2C. Are you using the Simplei2c.h library or did you roll your own.

    I2C can hang if clock stretching is used.

    Mike

  • maccamacca Posts: 274
    edited 2021-02-12 06:55

    @z80jon said:
    While advice on tracking this issue down would be great, it's just one example of how having an in-circuit debugger where I can do single-stepping, see C vs the assembly it compiles into, and watch code execution in real time would be invaluable.

    These are most likely stack-overflow issues, you are allocating 255+ bytes in main, plus another 96+ in the redacted function, print uses another 256+ bytes of stack for its own purposes, and you only know how much stack space is used by other functions. How big is your compiled code ?

    Try to move buffer from main to a global variable, this should free some space to allow the program to work, if it is a stack issue.

    It is also possible that your code overflows the variable space corrupting the stack, but with the partial code you posted is hard to tell.

    Also, keep in mind that gcc doesn't remove unused functions, so make sure that you don't have unused code that takes space for nothing.

    Hope this helps.

  • @iseries said:
    I see you are using I2C. Are you using the Simplei2c.h library or did you roll your own.

    I2C can hang if clock stretching is used.

    Mike

    Stock I2C libs.

    @macca said:

    @z80jon said:
    While advice on tracking this issue down would be great, it's just one example of how having an in-circuit debugger where I can do single-stepping, see C vs the assembly it compiles into, and watch code execution in real time would be invaluable.

    These are most likely stack-overflow issues, you are allocating 255+ bytes in main, plus another 96+ in the redacted function, print uses another 256+ bytes of stack for its own purposes, and you only know how much stack space is used by other functions. How big is your compiled code ?

    Try to move buffer from main to a global variable, this should free some space to allow the program to work, if it is a stack issue.

    It is also possible that your code overflows the variable space corrupting the stack, but with the partial code you posted is hard to tell.

    Also, keep in mind that gcc doesn't remove unused functions, so make sure that you don't have unused code that takes space for nothing.

    Hope this helps.

    The unused code part shouldn't be an issue; the compile size is less than 10k. In fact, unused code was why I moved to making a library (following the Parallax tutorial on doing so) in the first place. I'd previously been using several command line arguments to make gcc treat every function/variable as its own source file, but moved away from that to this.

    I tried moving byte buffers over to global and got an even more unexplainable issue...

    Got me the following output from the while loop: "abcReading!dab" ("Reading!" being printed by the redacted function). Why it would hang on the pause(500) is... perplexing to me. In the functions called in this ~12k program, there's no massive buffers (not in the global scope as I've updated it now), no malloc's or significant variables, and the call stack only ever goes up to 3 deep (excluding Parallax-written library calls). Unless sequential calls to i2c_in (my code does that several dozen times when reading in data from the device) cause an issue that isn't always noticed until later, I'm not sure what to make of this.

    Removing the pause(500) statement got me this: "abcReading!dabcReading!" (and then it hangs). Any theories as to why a pause(500) would cause the program to get stuck, or any other ideas?

  • maccamacca Posts: 274

    @z80jon said:
    Got me the following output from the while loop: "abcReading!dab" ("Reading!" being printed by the redacted function). Why it would hang on the pause(500) is... perplexing to me. In the functions called in this ~12k program, there's no massive buffers (not in the global scope as I've updated it now), no malloc's or significant variables, and the call stack only ever goes up to 3 deep (excluding Parallax-written library calls). Unless sequential calls to i2c_in (my code does that several dozen times when reading in data from the device) cause an issue that isn't always noticed until later, I'm not sure what to make of this.

    Removing the pause(500) statement got me this: "abcReading!dabcReading!" (and then it hangs). Any theories as to why a pause(500) would cause the program to get stuck, or any other ideas?

    When moving things around causes different results, it is 100% a stack corruption issue!
    Your code may overflow buffers, write to pointers with the wrong data size, and a million other things. For example, in your redacted function you are using ASCIBuffer[pointer] are you sure that the buffer is sized corectly and pointer doesn't overflow ?
    Unfortunately only you can see what you code is doing.
    And about the stack deep... it is not only the number of nested calls, it is also how much space is taken by local variables which are allocated on the stack. Given the small code size I tend to exclude this, but a double check won't hurt.

  • @macca said:

    @z80jon said:
    Got me the following output from the while loop: "abcReading!dab" ("Reading!" being printed by the redacted function). Why it would hang on the pause(500) is... perplexing to me. In the functions called in this ~12k program, there's no massive buffers (not in the global scope as I've updated it now), no malloc's or significant variables, and the call stack only ever goes up to 3 deep (excluding Parallax-written library calls). Unless sequential calls to i2c_in (my code does that several dozen times when reading in data from the device) cause an issue that isn't always noticed until later, I'm not sure what to make of this.

    Removing the pause(500) statement got me this: "abcReading!dabcReading!" (and then it hangs). Any theories as to why a pause(500) would cause the program to get stuck, or any other ideas?

    When moving things around causes different results, it is 100% a stack corruption issue!
    Your code may overflow buffers, write to pointers with the wrong data size, and a million other things. For example, in your redacted function you are using ASCIBuffer[pointer] are you sure that the buffer is sized corectly and pointer doesn't overflow ?
    Unfortunately only you can see what you code is doing.
    And about the stack deep... it is not only the number of nested calls, it is also how much space is taken by local variables which are allocated on the stack. Given the small code size I tend to exclude this, but a double check won't hurt.

    Ironically, I'm really glad to hear that! I did find that pulling most of the functions out and into one giant piece of code did seem to work, which seems to further agree with your conclusion.

    Do you have any recommendations for how I should go about monitoring the stack size / testing for / isolating corruption issues?

  • maccamacca Posts: 274

    @z80jon said:
    Do you have any recommendations for how I should go about monitoring the stack size / testing for / isolating corruption issues?

    No, not on the Propeller, I don't think there are tools to debug these issues.
    Usually a good review of all buffer usage is enough to find the issue. Maybe a source code analyzer like lint or cppcheck may detect the issue, don't know if these tools will work with microcontroller's code.
    When I can't understand what's wrong, sometimes I start over from the beginning, adding one function at a time to see when the issue appears again.

  • @macca said:
    When moving things around causes different results, it is 100% a stack corruption issue!
    Your code may overflow buffers, write to pointers with the wrong data size, and a million other things. For example, in your redacted function you are using ASCIBuffer[pointer] are you sure that the buffer is sized corectly and pointer doesn't overflow

    Did a bit more review of my code and.... you got me; it was a buffer overflow. I'd forgotten that while the final size of the data is 96 bytes, it takes up 128 when read in from the device (thus, rawBuffer was to blame). Oddly enough this code sometimes worked when it was structured relative to the rest of my code in certain ways, which is why I had such a hard time tracking it down.

    I also decided to ditch rawBuffer altogether, using the upper region of ASCIIBuffer to serve as the rawBuffer (doing this: char *rawBuffer = &ASCIIBuffer[63];).

  • maccamacca Posts: 274

    @z80jon said:
    Did a bit more review of my code and.... you got me; it was a buffer overflow. I'd forgotten that while the final size of the data is 96 bytes, it takes up 128 when read in from the device (thus, rawBuffer was to blame).

    Good, I'm glad you found the cause.

    Oddly enough this code sometimes worked when it was structured relative to the rest of my code in certain ways, which is why I had such a hard time tracking it down.

    Yes, because depends on what memory locations the overflow writes, if for example writes to other variables that are later reinitialized (another temporary buffer, a for-loop counter variable, etc.) you may never know it happens. When it corrupts the return address of a function or a variable that must have certain values you start seeing things like you described.

    I also decided to ditch rawBuffer altogether, using the upper region of ASCIIBuffer to serve as the rawBuffer (doing this: char *rawBuffer = &ASCIIBuffer[63];).

    Well... hope the upper region is large enough... :smiley:

  • One of the things I do is I first write my code in C# so that I can debug it. Then I translate it over to the Propeller.

    Mike

Sign In or Register to comment.