printf problems in second cog
TomUdale
Posts: 75
Greetings,
I am having some trouble getting printf to work in a second cog:
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
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
Comments
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
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'.
It is up to the user to manage resources using mutex locking for this purpose. See propeller.h documentation for lock functions.
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
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.
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
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
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
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
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
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
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.
Sure. It just can't be the default, that's 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:
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
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
Doh! I am sure that is it. How silly. I will check tomorrow.
Thanks for the help.
Best regards,
Tom
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
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.
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:
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
Why do you need to use a clock set methodology other than what the tools provide?
Ahh. So that explains that.
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
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
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.
I am confused by this. I have this in my cog.h:
and this in propeller.h:
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