Using cogstart - demo and questions
mindrobots
Posts: 6,506
I wrote my own little demo to use cogstart (instead of using _start_cog_thread). It works but I have a couple questions:
Here's my code:
First question: How to I properly define my function do_blink and/or reference it in the cogstart to make these warnings disappear?
Second question: In the comments for cogstart from the propeller.h include, it says the stack size must be enough to hold the struct _thread_state_t, the initial stack frame and other stack frames used by called functions. _thread_state_t is 148 bytes (37 longs) so I used 40 (the cogstart.c code adds 3 bytes when checking sizes) and then used an arbitrary 16 for the stack size. Is there a minimum/standard stackframe size that needs to be accommodated? I thought there might be since it is mentioned in the comments.
There are probably other things I should have done better/differently, please comment all you want on anything else.
Thanks!
Here's my code:
/* blinky3.c - blink an LED Board Types: Any with an LED connected to a free pin Memory Models: All that your board supports This builds on previous blinky programs and demonstrates how to start an additional LMM VM kernel in a new Cog using the cogstart function. The comments in this code document the changes for this program only. The program uses a C function running in a second Cog to blink the LED ** * @brief Start a new propeller LMM function/thread in another COG. * * @details This function starts a new LMM VM kernel in a new COG with * func as the start function. The stack size must be big enough to hold * the struct _thread_state_t, the initial stack frame, and other stack * frames used by called functions. * * @details This function can be used instead of _start_cog_thread. * * @param func LMM start function * @param par Value of par parameter usually an address * @param stack Address of user defined stack space. * @param stacksize Size of user defined stack space. * @returns COG ID allocated by the function or -1 on failure. * int cogstart(void (*func)(void *), void *par, void *stack, size_t stacksize); */ #include <stdio.h> #include <propeller.h> #include "pin.h" // propeller-load variable patching based on *.cfg content - see blinky2.c for details int _cfg_led_pin = -1; // The value will default to -1 if not patched by the loader #define STACK_SIZE 16 + 40 // stack needs to accomodate thread control structure (40) plus // room for you to use (16)- set it's size static int cog1_stack[STACK_SIZE]; // allocate space for the stack - you need to set up a // stack for each Cog you plan on starting volatile int wait_time; // a global variable we will change from Cog0 to pass // delay times to cog1 /* the do_blink function will be started in it's own Cog and toggle the LED at whatever rate is set by the starting Cog - this is the main code from blink1 and blink2 moved to it's own function so it can be run in it's own LMM kernel */ void do_blink(void) { pinOutput(_cfg_led_pin); while (1) { pinHigh(_cfg_led_pin); waitcnt(wait_time + CNT); pinLow(_cfg_led_pin); waitcnt(wait_time + CNT); } } /* main runs in Cog0 - it starts a second cog and then enters a loop to change the wait time between the LED toggles. do_toggle picks up this changing value and uses it fo it's waitcnt */ int main (void) { int cog_id; // we get the Cog ID (or error) back from the cogstart // we don't really do anything with it in this program but // good practice to keep it around wait_time = CLKFREQ / 32; // initial wait time between toggles waitcnt((CLKFREQ/2)+CNT); // give the terminal time to start cog_id = cogstart(do_blink, NULL, cog1_stack, STACK_SIZE * 4); // if cog_id is -1, then there was a problem starting the cog // just sit in a loop if that happens - otherwise, cog_id is // the cog number that was started and the program can continue if (cog_id < 0) { printf("Unable to start Cog. Status %d returned.\n", cog_id); while (1) { } } else { printf("Blink Cog %d has started.\n", cog_id); while (1) { printf("Entering Blink Loop.\n"); for (int i=1;i<5;i++){ waitcnt((CLKFREQ * 2)+ CNT); wait_time = wait_time << 1; if (i == 4) { wait_time = CLKFREQ / 32; } } } } } /* Copyright (c) 2012 Rick Post Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */I get these warnings:
blinky3.c: In function 'main': blinky3.c:78:5: warning: passing argument 1 of 'cogstart' from incompatible pointer type [enabled by default] c:\propgcc\bin\../lib/gcc/propeller-elf/4.6.1/../../../../propeller-elf/include/propeller.h:171:5: note: expected 'void (*)(void *)' but argument is of type 'void (*)(void)'
First question: How to I properly define my function do_blink and/or reference it in the cogstart to make these warnings disappear?
Second question: In the comments for cogstart from the propeller.h include, it says the stack size must be enough to hold the struct _thread_state_t, the initial stack frame and other stack frames used by called functions. _thread_state_t is 148 bytes (37 longs) so I used 40 (the cogstart.c code adds 3 bytes when checking sizes) and then used an arbitrary 16 for the stack size. Is there a minimum/standard stackframe size that needs to be accommodated? I thought there might be since it is mentioned in the comments.
There are probably other things I should have done better/differently, please comment all you want on anything else.
Thanks!
Comments
This isn't doing what you think it is doing. You forgot to put parens around your STACK_SIZE macro definition so the C compiler is seeing this as "16 + 40 * 4" which is 176 not "(16 + 40) * 4" which is 224 which is probably what you were expecting. You'd be better off using sizeof(cog1_stack) instead of this calculation.
Function cogstart is looking for a pointer (should be noted in the doc markup), I.e: &do_blink
The struct thread_state_t is 148 bytes.
According to the cogstart function, stack size should be > sizeof(thread_state_t) + 3*sizeof(unsigned int).
So, the minimum stack size would be 148+3*4 = 160 bytes. Stack size should also be evenly divisible by 4.
So, without resorting to dynamic allocation, your cog_stack should be defined as:
unsigned int cog1_stack[160/4];
If you need local variables in your lmm thread, you should add about 4 bytes per integer variable:
unsigned int cog1_stack[(160+VARS*4)/4];
STACK_SIZE should be the actual size of the stack array in bytes, that is sizeof(cog1_stack).
cog_id = cogstart(&do_blink, NULL, cog1_stack, sizeof(cog1_stack));
Thanks,
--Steve
@Steve,
&do_blink did not get rid of the warnings. I needed to define do_blink as follows:
void do_blink(void *dummy)
cogstart.c has:
int cogstart(void (*func)(void *), void *arg, void *stack, size_t stack_size)
the compiler appears to want a parameter in the function definition even though nothing is ever done with it. Since the function parameter isn't or can't really be passed to the new cog via the function call, instead it's via the *arg, can the cogstart actually be defined as:
int cogstart(void (*func), void *arg, void *stack, size_t stack_size)
to remove the requirement of putting a dummy parameter in? (that probably violates something I'm unaware of but it would make more sense! )
For the stack I put this in to try and explain it better.
that actually made me laugh out loud! Somewhere between 30 and 35 bits....whatever it takes!!
Thanks, guy for setting me straight!
LOL. My initial thought was that fewer bytes might be needed for a short than an int.
Better to just say 4 bytes. Maybe 8 bytes are needed for 64bit doubles.
Thanks,
--Steve
Added: Actually, if you have a small number of variables in your do_blink function (without other functions), the compiler will allocate local registers for them. If you have several functions, you'll need an equivalent number of stack frames which i presume is 3*sizeof(int) each and space for variables..
The "arg" parameter is actually passed to the function func when the new thread starts up. It may be that your particular function is ignoring it or doesn't want any parameter, but in general the call results in a sequence like happening (this is done internally to the LMM kernel startup code).
outside of PropGCC, I might do something like this: and then at some point: this would pass the argument by value on the stack.
However, with cogstart defned as:
this isn't intuitively obvious to my small c mind (I'm not real strong on pointers) and anything I've tried doesn't seem to work. It either generated build errors or ends up not passing 15 to the function.
I may have missed it but I haven't found any examples of cogstart passing arguments.
There are cases of _start_cog_thread passing mailbox addresses which is fine if that's the route we need to go. That can be explained and demonstrated.
The discussion above led me to think that you could pass one argument to the function you are starting - if it's truly an argument, then it could be anything int, char, *char, *ptr to struct and can be passed by value or by reference (immutable or mutable), if it can only be a void ptr, them let's call it a mailbox pointer and equate to the PASM PAR register and be done with it. We can then get on about mailboxes and struct and how to build and pass parameter blocks to started Cogs.
Please let me know if I'm missing something major or just being pig headed - either way, it needs to be explained and demonstrated in the tutorials since it is a rather important feature.
Thanks!
Two key items:
2) Calling the function directly won't work. It must be invoked with cogstart.
In the function signature "void function(void *par);" like shown below type void* means that par can be anything. The par value is then recast according to your calling convention:
#2 - I knew (my ramblings may have led you to believe otherwise! )
#1 - ahhhh, the power, abuse and misuse of casting and recasting values in a weakly typed language....in the end, even the wicked are forgiven. I'm at the point now where C usually gives me just enough rope to hang myself! (provided I reference the rope properly!)
Bottom line, you can only pass one parameter so it better be the value you want, a pointer to the value you want or a pointer to a properly defined struct and then you're free to abuse that struct however you need to.
I'll try these.
Also, I doubt this will compile:
That's because you can't dereference a void pointer. I think what Steve meant was this: