Shop OBEX P1 Docs P2 Docs Learn Events
Using cogstart - demo and questions — Parallax Forums

Using cogstart - demo and questions

mindrobotsmindrobots Posts: 6,506
edited 2012-06-05 14:05 in Propeller 1
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:
/*  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

  • David BetzDavid Betz Posts: 14,516
    edited 2012-06-01 09:19
    mindrobots wrote: »
    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:
    #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 
    
    ...
    
          cog_id = cogstart(do_blink, NULL, cog1_stack, STACK_SIZE * 4);
    

    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.
  • jazzedjazzed Posts: 11,803
    edited 2012-06-01 09:27
    Hi Rick.

    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
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-06-01 11:49
    @David, I always forget how literal (read STUPID) the #define is in C. I will once more try to beat that into my head when using #define.

    @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! :smile: )

    For the stack I put this in to try and explain it better.
    #define MINIMUM_LMM_STACK 160            //  MINIMUM stack in bytes to accomodate thread control structure (40 LONG) 
    #define COG1_LOCAL_VARS 0                   // number of local variable in LMM thread (4 bytes / Interger)                                
    
    static int cog1_stack[(MINIMUM_LMM_STACK + (COG1_LOCAL_VARS*4))/4];      // allocate space for the stack - you need to set up a
                                                                     // stack for each Cog you plan on starting
    .
    .
    .
                   cog_id = cogstart(do_blink, NULL, cog1_stack, sizeof(cog1_stack));       // use sizeof to pass stack size in BYTES
    

    .....you should add about 4 bytes per integer variable...

    that actually made me laugh out loud! Somewhere between 30 and 35 bits....whatever it takes!! :lol:

    Thanks, guy for setting me straight!
  • jazzedjazzed Posts: 11,803
    edited 2012-06-01 12:39
    mindrobots wrote: »
    that actually made me laugh out loud! Somewhere between 30 and 35 bits....whatever it takes!! :lol:


    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..
  • ersmithersmith Posts: 6,097
    edited 2012-06-02 17:26
    mindrobots wrote: »
    &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)

    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
    cogstart(foo, bar, stack, stack_size);
    
    results in a sequence like
    start new thread;
    foo(bar) in new thread;
    finish new thread
    
    happening (this is done internally to the LMM kernel startup code).
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-06-05 05:58
    OK, building on the cogstart example from above, I want to actually pass a parameter/argument to do_blink - let's say the pin the led is connected to.

    outside of PropGCC, I might do something like this:
    void do_blink(int pin) {
    .
    .
    }
    
    and then at some point:
    do_blink(_cfg_led_pin);
    
    or
    
    do_blink(15);
    
    this would pass the argument by value on the stack.

    However, with cogstart defned as:
    int cogstart(void (*func)(void *), void *arg, void *stack, size_t stack_size)
    

    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!
  • jazzedjazzed Posts: 11,803
    edited 2012-06-05 06:25
    Rick,

    Two key items:
    1) The function must match the signature: void function(void *par);
    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:
    [FONT=courier new]/*
     * call by reference - natural as pointer par is an address
     */
    
    void function(void *par)
    {
        int pin = *(int*) par;
        while(1);
    }
    
    void main(void)
    {
        int pin = 2;
        cogstart(&function, &pin, stack, sizeof(stack));
        while(1);
    }[/FONT]
    
    [FONT=courier new]/*
     * call by value - less natural as pointer par is a value
     */
    [/FONT]
    [FONT=courier new]void function(void *par)
    {
        int pin = (int) par;
        while(1);
    }
    
    void main(void)
    {
        int pin = 2;
        cogstart(&function, pin, stack, sizeof(stack));
        while(1);
    }
    [/FONT]
    
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-06-05 07:45
    Thanks!

    #2 - I knew (my ramblings may have led you to believe otherwise! :lol:)

    #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. :innocent: 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.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-06-05 14:05
    mindrobots wrote: »
    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.
    Essentially, you can think of this parameter as if it were PAR in Spin. Usually it's a pointer to a structure with initialization information in it.

    Also, I doubt this will compile:
        int pin = (int) *par;
    

    That's because you can't dereference a void pointer. I think what Steve meant was this:
        int pin = *(int *)par;
    
Sign In or Register to comment.