Shop OBEX P1 Docs P2 Docs Learn Events
cognew Spin vs C — Parallax Forums

cognew Spin vs C

I have been playing around with the P1 for a few years now, programming it exclusively in C. I decided to finally take a look at Spin, mostly on a whim. The first thing that stood out was that when you called cognew to start a process in a separate cog, you are able to have arguments. When programming in C, using SimpleIDE and the SimpleTools.h library, the cognew does not allow your functions to take any arguments.

One of the first bits of example code in either language is a multicore version of the blinking light "Hello World" program. The C version has two separate blink functions with the delay times hard coded. The Spin version has the blink method take several arguments, including pin number, and the counter ticks for the delay. So the same method can be used for both cogs. There seems to be no way to do this in C or C++.

I never understood why the C version of cognew does not allow your functions to take arguments. I thought it must be using function pointers, which allow you to pass arguments to the function being pointed to. Seeing the cognew in Spin makes me even more confused as to why the C version is that way.

Is there something preventing the C cognew function from allowing the called function to take arguments?

Thanks in advance for any information.

Comments

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-04-06 18:08

    In Spin, cognew() function only ever takes two arguments -- but they'll change based on the code you're wanting to run. In most cases that will be a PASM block and will look like this

      cog = cognew(@entry, @start_of_vars) + 1
    

    The first parameter is the address of the code to execute (that's in a DAT block), and the second is the address of the hub variable that serves as the "mailbox" (this is called par in the PASM code). We can access other hub variables offset from par.

    The other mechanism with cognew looks like this:

       cog = cognew(blink(pin, onticks, offticks), @stack) + 1
    

    There are still just two arguments: the first one is the method to run (along with its arguments, if any), and a pointer to stack space. In this second case we're actually starting another Spin interpreter cog and we have to point at the starting method (which can have parameters). For a manually-launched Spin interpreter, we have to provide a stack (array of longs).

    I don't know how the C version of cognew works, but I hope this helps you understand what's going on in Spin. Again, the second version is launching another Spin cog, which would not work in C.

    From the P1 manual:

  • @KevinW said:
    I never understood why the C version of cognew does not allow your functions to take arguments. I thought it must be using function pointers, which allow you to pass arguments to the function being pointed to. Seeing the cognew in Spin makes me even more confused as to why the C version is that way.

    You need to use cogstart, instead. I believe cognew resembles the Spin counterpart to start a cog code. The Spin compiler can understand if you are specifiying a Spin method or Cog code and act accordingly, in C is not possible (unless I'm missing something) because it can't know if the argument is a function or a cog code (it is always an address...).

    In C it is also a bit more complicated than Spin, because the argument is a pointer (to void), you can't specify the parameters list, so you need to be aware of this and pass the variables accordingly.

    The following example shows how to use a structure to pass the pin number and blink delay.

    struct _param {
        int pin;
        int delay;
    };
    
    uint32_t blink1_stack[128];
    uint32_t blink2_stack[128];
    struct _param blink1, blink2;
    
    int main(int argc, char * argv[])
    {
        blink1.pin = 0;
        blink1.delay = _CLKFREQ / 1000;
        blink2.pin = 1;
        blink2.delay = _CLKFREQ / 2000;
    
        cogstart(blink, &blink1, blink1_stack, sizeof(blink1_stack));
        cogstart(blink, &blink2, blink2_stack, sizeof(blink2_stack));
    
        while(1);
    
        return 0;
    }
    
    void blink(void * arg)
    {
        uint32_t t = CNT;
        struct _param * param = (struct _param *)arg;
    
        pinMode(param->pin, OUTPUT);
    
        while(1) {
            digitalWrite(param->pin, HIGH);
            waitcnt(t += param->delay);
            digitalWrite(param->pin, LOW);
            waitcnt(t += param->delay);
        }
    }
    

    You can of course pass a single variable as a pointer, like &pin.
    I don't remember if the stack can be allocated automatically with only the size.

    Hope this helps.

  • @macca Thanks for the help! Your answer is more along this line of what I was thinking. It looks like cog_run is the function I was thinking of in C. So in Spin it is cognew and in C it is cog_run(). Sorry for the confusion. The description of cog_run() is below.

    The example you shared uses arduino IDE names such as pinMode and digitalWrite. I am using SimpleIDE with the simpletools.h header. The C reference does not seem to include cogstart, but I looked at the source code for the cog_run() function and it calls cogstart. I changed the functions in your example to match the names in simpletools.h

    /*
    Blank Simple Project.c
    http://learn.parallax.com/propeller-c-tutorials
    */

    include "simpletools.h" // Include simple tools

    void blink(void * arg);

    struct _param {
    int pin;
    int delay;
    };

    uint32_t blink1_stack[128];
    uint32_t blink2_stack[128];
    struct _param blink1, blink2;

    int main()
    {
    blink1.pin = 0;
    blink1.delay = 1000;
    blink2.pin = 1;
    blink2.delay = 250;

    cogstart(blink, &blink1, blink1_stack, sizeof(blink1_stack));
    cogstart(blink, &blink2, blink2_stack, sizeof(blink2_stack));
    
    while(1);
    
    return 0;
    

    }

    void blink(void * arg)
    {
    uint32_t t = CNT;
    struct _param * param = (struct _param *)arg;

    set_direction(param->pin,1);
    
    while(1) {
        high(param->pin);
        pause(param->delay);
        low(param->pin);
        pause(param->delay);
    }
    

    }

    It worked as intended. So thanks again for the help!

    I looked up the source code for cog_run() and it calls the function cogstart(). Unfortunately, only cog_run() is documented in the simpletools.h doxygen HTML page included in the SimpleIDE Learn folder. I will have to dig a little bit see if I can find more information.

  • @KevinW said:
    The example you shared uses arduino IDE names such as pinMode and digitalWrite. I am using SimpleIDE with the simpletools.h header. The C reference does not seem to include cogstart, but I looked at the source code for the cog_run() function and it calls cogstart. I changed the functions in your example to match the names in simpletools.h

    Ah yes, I'm using my own library that replicates some of the Arduino functions.

    I looked up the source code for cog_run() and it calls the function cogstart(). Unfortunately, only cog_run() is documented in the simpletools.h doxygen HTML page included in the SimpleIDE Learn folder. I will have to dig a little bit see if I can find more information.

    The propeller.h file is your friend:

    /**
     * @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);
    
  • @macca Thanks again! that should be what I need.

  • @KevinW,

    I didn't like the simplicity of Simple very much and made my own C++ HAL for the Propeller many years ago. You may find PropWare::Runnable interesting for your use case: https://david.zemon.name/PropWare/api-develop/classPropWare_1_1Runnable.xhtml

    It may be that installing all of PropWare on your system isn't worth the hassle if you're happy with your current toolset, but you could certainly pull specific pieces out of PropWare that interest you.

  • jaspastjaspast Posts: 12
    edited 2023-11-15 05:04

    @macca said:

    Ah yes, I'm using my own library that replicates some of the Arduino functions.

    @macca is your arduino-ish library available anywhere?

    I want to use P2 as a spacecraft simulator student project for an engineering class. However, I don't have time to teach spin2. The point of the class is the spacecraft systems, not the programming required to tie it all together.

    Edit: I've looked into propware and libarduino, but I couldn't get them to work with C in flexprop/P2. At the moment I'm really just stuck trying to get I2C working with some arduino libraries (INA219, for example).

  • @jaspast

    I'm attaching the library here, I don't know if it works with flexprop, it was part of a tool I think I never released that used the propeller GCC port (P1 only), so it may well not work at all.

  • Thanks @macca. I look forward to trying it.

Sign In or Register to comment.