Shop OBEX P1 Docs P2 Docs Learn Events
printf problems in second cog — Parallax Forums

printf problems in second cog

TomUdaleTomUdale Posts: 75
edited 2013-11-14 14:07 in Propeller 1
Greetings,

I am having some trouble getting printf to work in a second cog:
// main.c

#include <propeller.h> 
#include <stdio.h>
#include "databus.h"
#include "woggle.h" 


int main()
{

    WoggleEnv_t     woggleEnv=MkWoggleEnv(100); // woggle will blink 100 times
    int             count=0;
    int             startRes;

 

     // Now switch to the fast clock.
     clkset(CPX_PROP_CLK, CPX_PROP_FRQ);

     // blink 30 times from main with output
     for(count=0;count<30;count++)
     {
         WriteLEDRed(3);
         printf("Hello %d\n",count);
         waitcnt(CNT + CLKFREQ/10);
         WriteLEDOff(3);
         waitcnt(CNT + CLKFREQ/10);
     }

     // start woggle to continue blinking and printf'ing
     startRes=StartWoggleCog(woggleEnv); 
     if(startRes<0)
         printf("WoggleStart Choked\n");
     else
         printf("WoggleStart Happy\n");
 }


//woggle.h

#ifndef INC_WOGGLE_INC 

     #define WOGGLE_VARS         12
     
     
     typedef struct tag_WoggleData
     {
         unsigned    blinkCount;
     }WoggleData;
     
     typedef struct tag_WoggleEnv
     {
         WoggleData      data;
         unsigned long   stack[(160+WOGGLE_VARS*4)/4];
     }WoggleEnv_t;
  
     void                  Woggle(void*);
     WoggleEnv_t    MkWoggleEnv(unsigned blinkCount);
     #define             StartWoggleCog(env) cogstart(&Woggle,&env.data,env.stack,sizeof(env.stack))
  

     #define INC_WOGGLE_INC
 #endif //INC_WOGGLE_INC


// woggle.c
#include <propeller.h> 
#include <stdio.h>
#include "woggle.h"
#include "databus.h"

 
 WoggleEnv_t     MkWoggleEnv(unsigned blinkCount)
 {
     WoggleEnv_t     env={0};
  
     env.data.blinkCount=blinkCount;
     return env;
 }

 
  
 void DoWoggle(WoggleData* data)
 {
     unsigned count;
     unsigned num=data->blinkCount;
  
     for(count=0;count<num;count++)
     {
         waitcnt(CNT + CLKFREQ/20);
         WriteLEDRed(3);
         waitcnt(CNT + CLKFREQ/20);
         WriteLEDOff(3);
         printf("Hello From Woggle%u\n",count);
     }
 }
 
  
 void Woggle(void* data)
 {
     DoWoggle((WoggleData*)data);
 }
 


The program is supposed to blink the LED in the main program, along with associated output, then launch the woggle cog to output a programmable number of blinks (set via MkWoggleEnv(100) ), also with associated output. Everything works except the woggle output. The LED blinks as expected, but no printf.

This code is compilled LMM with TinyLib.

Any thoughts?

Best regards,

Tom
«1

Comments

  • TomUdaleTomUdale Posts: 75
    edited 2013-11-08 12:26
    Hmmm, I just figured it out, kind of. If I comment out the printf's in the first cog, the ones in the second cog start working.

    This makes me think that the pin dir/state of one cog is interfering with that of the second - probably the Tx pin has been left with dir[]=1 and out[]=1. Does that sound plausible, and if so, what do I do about it? Seems like something that should be handled in the library code.

    It does not seem that unreasonable to want to send debug messages from multiple cogs.

    Tom
  • photomankcphotomankc Posts: 943
    edited 2013-11-08 12:27
    I'm not at a place where I can double check this but.... I don't believe you can printf from other cogs. printf() sends text to the 'console' and the console only lives on the the kernel cog. The only way to do this that I know of is to have shared memory between the cogs where cog #2 can stuff a value that lets you know what it is up to, but the main kernel cog is going to have to check that value and do the printf'ing of what that value means. SimpleText also allows you to start up another serial port on the other cog and use that for debug but it would have to be on different pins.


    ETA: Your test confirms that partly. Although I thought only the main cog had a 'console', but it appears that whichever cog talks first has the 'console'.
  • jazzedjazzed Posts: 11,803
    edited 2013-11-08 12:35
    There is no provision for printing simultaneously from two cogs in Propeller-GCC without using locks.

    It is up to the user to manage resources using mutex locking for this purpose. See propeller.h documentation for lock functions.
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-08 12:54
    Hi Jazzed,

    While I agree that locks would be needed if I were simultaneously printf'ing from two cogs, I constructed that test to prevent that. the woggle cog only prints after all the delays assuring that cog0 is finished before it goes. Indeed, if I comment out the error reporting at the bottom of cog0, I get the same problem even though the two cogs are clearly accessing the Tx/Rx pins serially.

    What I just discovered moments ago is that if I follow my cog0 printf code by

    DIRA&=~CPX_DEBUG_TX; DIRA&=~CPX_DEBUG_RX;


    the woggle code starts working. That is, if I explicitly set my debug pins to inputs, everything is happy. So it seems as if the lowest level driver need only to enable/disable the transmit pin around the innards of putc() for this to start behaving.

    Best regards,

    Tom
  • jazzedjazzed Posts: 11,803
    edited 2013-11-08 13:34
    I see. Sorry I didn't read the posts in detail.

    What you are seeing however is the effect of trying to deal with an unruly board which has a large user base of 5000+ sold. If the pins are set to inputs with that board (original Quickstart), you will get garbage on the terminal (unless a pull-up resistor is added to the TX pin). Rather than constantly irritating Parallax about this, we did the best we could given the circumstances.
  • ersmithersmith Posts: 6,054
    edited 2013-11-09 03:53
    The way to printf from two cogs simultaneously is to use the FullDuplexSerial driver, by adding code like:
    #include <driver.h>
    
    extern _Driver _FullDuplexSerialDriver;
    _Driver *_driverlist[] = { _FullDuplexSerialDriver, NULL };
    
    to your main program.

    The default SimpleSerial driver will not work, because as you observed the TX pin has to be left high when idle. That's a requirement of the protocol, unfortunately, and as jazzed points out some Parallax boards do not have a pull up resistor on TX and so we have to explicitly drive the line high. Thus, two COGs can't talk to the UART at the same time.

    FullDuplexSerial will work because it launches a separate COG to talk to the serial port. The other COGs use a shared buffer to communicate with the serial COG. The library already has appropriate locks in place to ensure this will work properly. See for example the pthreads demos in the PropGCC distribution (the pthreads library automatically links in the FullDuplexSerial driver), which have up to 7 cogs printing simultaneously.

    Eric
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-09 04:47
    Hi Jazzed,

    I realized the same thing on the drive home. Indeed I am surprised that my test worked as cleanly as it did. I don't remember that we put a pull-up on that pin but yet the output was totally clean. So I will have to look into why it worked.

    But all is clear otherwise.

    What exactly would I need to do to make my own stdio driver with the appropriate DIR toggles? Can I "derive from" (copying function pointers for most calls) the existing Simple Serial driver and wrap just the putByte call? It seems like that should work. Or am I missing something and need to create an entirely new driver?


    Cheers,

    Tom
  • David BetzDavid Betz Posts: 14,516
    edited 2013-11-09 04:48
    ersmith wrote: »
    TThe default SimpleSerial driver will not work, because as you observed the TX pin has to be left high when idle. That's a requirement of the protocol, unfortunately, and as jazzed points out some Parallax boards do not have a pull up resistor on TX and so we have to explicitly drive the line high.
    I wonder if we should use a variable that can be patched by the loader to control whether we hold the TX pin high between calls to putchar? That way you could add an entry to the board configuration file to control that behavior.
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-09 04:53
    Eric,

    Thanks for the info. I replied to Jazzed before I updated my browser to see your reply. I will look into the FullDuplex driver. I had seen that but had not given it a try yet. I will mess around with all this next week.

    I suppose there are actually two options if you have an appropriately designed board, the FullDuplex or a wrapped SimpleSerial, the choice depending on whether you have the cog to spare and the energy to write the wrapper.

    Thanks again and
    best regards,

    Tom
  • ersmithersmith Posts: 6,054
    edited 2013-11-09 08:03
    David Betz wrote: »
    I wonder if we should use a variable that can be patched by the loader to control whether we hold the TX pin high between calls to putchar? That way you could add an entry to the board configuration file to control that behavior.

    Hmmm. That's a pretty good idea; it would make using SimpleSerial with multiple cogs feasible. On the down side it would mean programs which work on some boards would fail to work on others (mainly the Quickstart, I guess). I'm not sure if that's worth the trade-off or not.

    Eric
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-09 08:13
    Hi David,
    David Betz wrote: »
    I wonder if we should use a variable that can be patched by the loader to control whether we hold the TX pin high between calls to putchar? That way you could add an entry to the board configuration file to control that behavior.

    That would be very handy. A further enhancement might be to have something like locked_stdio=TRUE/FALSE (default FALSE) to enable/disable locking of the Tx/Rx pins during putChar/getChar so as to permit true simultaneous access from multiple cogs. That might completely obviate the need for FullDuplexSerial (at least for this task).

    In any event, you can probably set up the defaults for both settings so that nothing changes from the current behavior unless the user explicitly makes a config change.

    Best,

    Tom
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-09 09:18
    Hi Eric,
    ersmith wrote: »
    On the down side it would mean programs which work on some boards would fail to work on others (mainly the Quickstart, I guess). I'm not sure if that's worth the trade-off or not.

    If you made this setting, call it TX_PULLED_UP (or I guess RX_PULLED_UP depending on one's view), FALSE by default, then it would not interfere with the existing behavior. Only if the user had a board with the pullup would they enable the feature. So it should not cause any support issue unless the user went in mucking about with their config file without knowing what was going on.

    I think if you were writing a general purpose program that you expected to be applied to unknown HW, you would stick with the defaults.

    Best regards,

    Tom
  • David BetzDavid Betz Posts: 14,516
    edited 2013-11-09 09:43
    ersmith wrote: »
    Hmmm. That's a pretty good idea; it would make using SimpleSerial with multiple cogs feasible. On the down side it would mean programs which work on some boards would fail to work on others (mainly the Quickstart, I guess). I'm not sure if that's worth the trade-off or not.

    Eric
    True. I guess the program would have to come with release notes that mention that it doesn't work with QuickStart.
  • jazzedjazzed Posts: 11,803
    edited 2013-11-09 15:19
    Hi. Just want to clarify one thing.

    All Propeller boards from Parallax have a pull-up resistor for P30 on them by default except the Quickstart (either FTDI internal pull-up or PropBOE buffer circuit + external pull-up). The Quickstart has the buffer circuit without an external pull-up. The Quickstart has this floating P30 (which tends to oscillate horribly) because no devices drive it when Propeller sets it as an input (the default for Propeller on reset).

    Some folks in Parallax seemed to think it was a good idea to have the Quickstart P30 pin unloaded so that it could be used for any purpose. So, like it or not, that's where we stand, and we have to support it. That's the way it is until someone at Parallax says otherwise.

    Thanks for understanding.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-11-09 15:23
    jazzed wrote: »
    Hi. Just want to clarify one thing.

    All Propeller boards from Parallax have a pull-up resistor for P30 on them by default except the Quickstart (either FTDI internal pull-up or PropBOE buffer circuit + external pull-up). The Quickstart has the buffer circuit without an external pull-up. The Quickstart has this floating P30 (which tends to oscillate horribly) because no devices drive it when Propeller sets it as an input (the default for Propeller on reset).

    Some folks in Parallax seemed to think it was a good idea to have the Quickstart P30 pin unloaded so that it could be used for any purpose. So, like it or not, that's where we stand, and we have to support it. That's the way it is until someone at Parallax says otherwise.

    Thanks for understanding.
    Yes but we could still offer an option for turning off the "quickstart fix" feature for boards that don't require it.
  • jazzedjazzed Posts: 11,803
    edited 2013-11-09 15:51
    David Betz wrote: »
    Yes but we could still offer an option for turning off the "quickstart fix" feature for boards that don't require it.

    Sure. It just can't be the default, that's all.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-11-09 20:49
    jazzed wrote: »
    Sure. It just can't be the default, that's all.
    Agreed.
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-13 07:03
    Greetings All,

    Just to follow up, I have discovered that our board does not have a pull-up on the transmit pin, so I guess I am relying on the FTDI pull-ups on the Propeller Plug to be able to disable the TX pin and switch cogs using the simple serial driver.

    Anyway, I forged ahead trying to write my own driver. I am not really having much luck. My intention to get started was to simply wrap the SimpleSerial driver:
    const char _HVxBSerialPrefix[] = "HVxBSER:"; 
    
     extern _Driver _SimpleSerialDriver;
     
    
     
    
     static int HVxB_fopen(FILE *fp, const char *name, const char *mode)
    
     {
    
         return _SimpleSerialDriver.fopen(fp,name,mode);
    
     }
    
     
    /// ..other formulaic wraps... 
    
    
    static int HVxB_putbyte(int c, FILE *fp) {
    
         return _SimpleSerialDriver.putbyte(c,fp);
    
     }
    
     
    
    
    
    static _Driver _HVxBSerialDriver =   {
    
         _HVxBSerialPrefix,
    
         HVxB_fopen,
         NULL,       /* fclose hook, not needed */
    
         HVxB_read,
    
         HVxB_write,
    
         NULL,       /* seek, not needed */
    
         NULL,       /* remove, not needed */
    
         HVxB_getbyte,
    
         HVxB_putbyte
    
     };
    
     
    _Driver *_driverlist[] = 
    
    {
    &_HVxBSerialDriver,
    
         NULL
    
     };
    
     
    
    int main() 
    
    {
       //etc
    
     }
    
    
    
    
    

    My assumption was that this would replace the default _driverList with a new list that contained my HVxBSerialDriver. This does not seem to work.

    Firstly if I comment out the guts of HVxB_putbyte, simply returning the passed in character, I still got output from cog0. So clearly I was not replacing the driver list at all and thus my cog0 output was still coming from SimpleSerial.

    Secondly, this breaks the ability of my second cog to printf. Indeed many things break that. For example, removing the static from the wrapper functions has this same effect. In fact, just defining one wrapper function (no driver, no driver list) that calls any SimpleSerial driver function without static breaks the second cog. Very confusing.

    I messed around with INCLUDE_DRIVER(_HVxBSerialDriver) but that did not help me either.

    So the general question now is how do you replace the default driver list (even just to switch to FullDuplex, for example)?


    Best regards,

    Tom
  • ersmithersmith Posts: 6,054
    edited 2013-11-13 14:20
    I'm not sure why you're having problems, Tom. Your basic approach is correct -- declaring a new _driverList[] certainly should override the normal SimpleSerial, and it does for me. Do all the COGs share the same source code (in particular are you running LMM C threads on all of them)? If some COGs are running different, COGC code, then they won't be affected by stdio changes (and changes you make in them won't affect the main COG).

    I will also add the caveat that TinyLib uses a completely different stdio setup than the normal libs. Are you still trying to use TinyLib? If so, you need to replace the drivers in a different way, but I'm not quite sure what that is.

    Eric
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-13 14:57
    Hi Eric,

    ersmith wrote: »
    I will also add the caveat that TinyLib uses a completely different stdio setup than the normal libs. Are you still trying to use TinyLib? If so, you need to replace the drivers in a different way, but I'm not quite sure what that is.

    Doh! I am sure that is it. How silly. I will check tomorrow.

    Thanks for the help.

    Best regards,

    Tom
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-13 15:16
    Hi Jazzed,

    Thanks for that example. That answers some other questions I was having - it was not clear how to use the fopen routines - although seeing it, it could not be simplier. Can I assume therefore that you can close and reopen the default stdin/stdout/stderr handles in a similar fashion?

    All the best,

    Tom
  • jazzedjazzed Posts: 11,803
    edited 2013-11-13 15:33
    Tom,

    The example I posted uses the Parallax Learn Libraries. It is different from using the standard C methodology, and produces much smaller code as long as you don't use standard library calls which use printf and friends. All Learn library functions are documented in the SimpleIDE installations.

    They are also documented in at learn.parallax.com and in the workspace repository by library name. https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries Index.html

    Using fopen is documented in the PropellerGCC Library file: http://propgcc.googlecode.com/hg/doc/Library.html
    If you want to reuse stdout with standard C libraries, you will need to do freopen.

    Lots of other documentation can be found here https://sites.google.com/site/propellergcc/ and here https://code.google.com/p/propgcc/w/list
    We are looking at ways to consolidate the wiki and google site :)

    I've used this as an on-line C reference: http://publications.gbdirect.co.uk/c_book/
    There are tons of others like http://www.cplusplus.com/ which also has C references.
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-14 07:52
    Hi Eric and Jazzed,

    So I tried turning off TinyLib this morning and...nothing works. I remember this now - it is why I was using TinyLib. I have explored this some more, and I am thinking that there is something wrong with my CLKFREQ. Here is the code:
    #include <propeller.h>
    #include <stdio.h>
    #include "cpx_prop_databus.h"
     
    int main()
     {
         int             count;
    
         // Now switch to the 80MHz fast external clock.
         clkset(CPX_PROP_CLK, CPX_PROP_FRQ);
    
         // Try some fast blinking (use o'scope).
         AcquireBusForWrite();
         for(count=0;count<10;count++)
         {
             WriteLEDRed(2);
             WriteLEDOff(2);
         }
         ReleaseBus();
     
         // Wait a tad so the terminal can get it together.
         waitcnt(CNT + CLKFREQ/2);
    
         // Try to say something.
         for(count=0;count<10;count++)
         {
             printf("Hello %u\n",count);
             waitcnt(CNT + CLKFREQ/10);
         }
    
        // Try some slow blinking.     
         AcquireBusForWrite();
         for(count=0;count<10;count++)
         {
             WriteLEDRed(2);
             waitcnt(CNT + CLKFREQ/10);
             WriteLEDOff(2);
             waitcnt(CNT + CLKFREQ/10);
         }
         ReleaseBus();
    }
    

    Recall that my board setting is RCFAST. I chose this so that the boot loader will set up the propeller to run on the internal osc until I get to the clkset(CPX_PROP_CLK, CPX_PROP_FRQ) line. In theory, if I comment out the clkset line, I will be running always on the internal fast osc (which indeed seems to be true).

    So I ran a series of tests with two variables:

    printf= Tiny or the CStdIO
    clock=RCFAST or 80Mhz (by commenting out clkset).

    Here is the result matrix:

    Conditions...........Fast LEDs?......Printf Works..........Slow LEDs?
    Tiny/80MHz.........yes..................yes.......................yes
    cstdIio/80MHz.....yes..................no.........................yes
    Tiny/RCFAST.......yes..................no.........................no (?!?)
    cstdio/RCFAST....yes..................no.........................yes

    My interpretation of this is that somehow the CLKFREQ is getting screwed up (i.e. not set appropriately) and is throwing the putc routines off in a way that disables the serial. The result of no slow LEDs after the printf in the Tiny/RCFAST case makes me think that either the printf or the waitcnt(CNT + CLKFREQ/10) calls in my code are blocking for a very long time.

    I kind of figure there are assumptions I am making and/or the various Tiny/cstdio libs are making regarding the clock frequency that are incompatible. I am a bit baffled that neither of the RCFAST printf cases work. That is about as "Hello world" as it gets.

    Any thoughts?

    All the best,

    Tom
  • jazzedjazzed Posts: 11,803
    edited 2013-11-14 08:20
    RCFAST is not recommended for things like printing, etc....

    Why do you need to use a clock set methodology other than what the tools provide?
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-14 08:39
    Hi Jazzed,
    jazzed wrote: »
    RCFAST is not recommended for things like printing, etc....

    Ahh. So that explains that.
    jazzed wrote: »
    Why do you need to use a clock set methodology other than what the tools provide?

    That is how our board is set up: 80MHz clock from a CPLD. I could possibly lower the rate (assuming enough CPLD for a bigger divider) and use a PLL if that would simplify my life.

    Best regards,

    Tom
  • jazzedjazzed Posts: 11,803
    edited 2013-11-14 08:55
    Use a .cfg file that matches your board.
    # Tom's Board
        clkfreq: 80000000
        clkmode: XINPUT
    #  Optional
    #    baudrate: 115200
    #    rxpin: 31
    #    txpin: 30
    
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-14 09:34
    Hi Jazzed,

    That did it. Indeed my custom driver playing is now working also.

    So the lessons here are:

    1) printf and friends are not compatible with RCFAST (and probably RCSLOW) - at least at 115200 baud rate.

    2) printf and friends do not look for possible CLKFREQ changes caused by clkset. They only look at the boot loader settings.

    3) Tiny lib does not use the same Driver installation process as documented for the stdlib.

    Thanks and all the best,

    Tom
  • jazzedjazzed Posts: 11,803
    edited 2013-11-14 09:46
    To clarify:

    Point 1: RCFAST has a fairly precise clock for each P8x32a chip, but not an accurate one (different frequencies per chip).
    Point 2: The clkset macro does not change HUB address 0 which is the basis for all timing calculations. --edit-- Oops, this is wrong. Sorry.
    Point 3: Tinylib uses methodologies that make it small and are not at all standard C compliant.

    As to point 3: That is why we don't use the name printf or other stdio names in the simpletext library. Redefining printf and friends in multiple ways has caused endless grief, and I'm certain the grief will continue.
  • TomUdaleTomUdale Posts: 75
    edited 2013-11-14 10:40
    Hi Jazzed,
    jazzed wrote: »
    Point 2: The clkset macro does not change HUB address 0 which is the basis for all timing calculations.

    I am confused by this. I have this in my cog.h:
    [COLOR=#000000][FONT=&amp] 
    [/FONT][/COLOR]/** 32 bit system startup clock frequency variable */
    extern unsigned int _clkfreq; /* in the spin boot code */
    
    /** @brief This is an alias for the 32 bit clock frequency which is kept in address 0. */
    #define _CLKFREQ _clkfreq
    

    and this in propeller.h:
    #define clkset(mode, frequency) \
    do { \
      _CLKFREQ = (frequency); \
      _CLKMODE = (mode); \
      __builtin_propeller_clkset(mode); \
    } while(0)
    

    That looks to me like CLKFREQ/_CLKFREQ/_clkfreq is both pointing to address 0 and updated on clkset (unless I am misinterpreting the term "alias").

    But indeed the evidence points to the fact that hub address 0 is _not_ getting updated which certainly implies that &_clkfreq !=0. So my questions then become, why are there two places where the frequency is stored and why does clkset not update both of them?

    Best regards,

    Tom
Sign In or Register to comment.