Shop OBEX P1 Docs P2 Docs Learn Events
C3 and threads(cognew) — Parallax Forums

C3 and threads(cognew)

RsadeikaRsadeika Posts: 3,837
edited 2012-09-26 06:39 in Propeller 1
Below is a rough interpretation of a spin2cpp --ccode conversion of a Spin program that basically starts a couple of cognew methods with the associated stack. The program below compiles, but I do not see printf("This is job_2\n") from job_2(). Anybody have any ideas as to why that is happening?

What I am trying to do is have a program that has a separate cognew for an LED1, LED2, and IR detector. Plus have access to writing to the SD card, and access to the softRTC program. Essentially, it would work like this, using an IR remote I can light up either LED1, or LED2; once it gets lit, I would like to have it time stamped, and recorded on the SD card. I might call this an intermediate level C programming, which should provide information as to how to start a separate and new cog, plus do some data logging. I guess the question is, am I on the right track? Any suggestions would be appreciated.

Ray
/**
 * @file MasterN.c
 * This is the main MasterN program start point.
 */
#include <propeller.h>
#include <unistd.h>

#include <stdint.h>

typedef struct MasterN {
  int32_t Job_1stack[40];
  int32_t Job_2stack[40];
  char dummy__;
} MasterN;

static MasterN thisobj;

int MasterN_Job_1(void)
{
    printf("This is job_1\n");
    while(1)
    {
        usleep(50000);    
    }
}

int MasterN_Job_2(void)
{
    printf("This is job_2\n");
    while(1)
    {
        usleep(50000);
    }
}


/**
 * Main program function.
 */
int main(void)
{
    waitcnt(CLKFREQ + CNT);
    printf("This is main\n"); 
    
    cognew(MasterN_Job_1(), (&thisobj.Job_1stack));
    cognew(MasterN_Job_2(), (&thisobj.Job_2stack));
   
    while(1)
    {
        usleep(50000);
    }    
    return 0;
}

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 15:27
    Ray,

    You can't run a C function with a cognew. You have to use a pthread instead. I haven't used pthreads myself, but I looked at your "What is a pthread?" thread, and I was able to get your program running with the code shown below. I'm not sure how the stack is handle. It's either part of the pthread_t pointer that is passed to the pthread_create or it is malloc'ed when the thread starts up. There probably a way to control the size of the stack, but I don't know how to do that offhand. I would suggest finding out more about pthreads.
    /**
     * @file MasterN.c
     * This is the main MasterN program start point.
     */
    #include <stdio.h>
    #include <propeller.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <stdint.h>
    
    void *MasterN_Job_1(void *argv)
    {
        pthread_set_affinity_thiscog_np(); 
        printf("This is job_1 on cog %d\n", cogid());
        while(1)
        {
            usleep(50000);    
        }
    }
    
    void *MasterN_Job_2(void *argv)
    {
        pthread_set_affinity_thiscog_np(); 
        printf("This is job_2 on cog %d\n", cogid());
        while(1)
        {
            usleep(50000);
        }
    }
    
    
    /**
     * Main program function.
     */
    int main(void)
    {
        pthread_t thr1;
        pthread_t thr2;
    
        waitcnt(CLKFREQ + CNT);
        printf("This is main on cog %d\n", cogid()); 
        
        pthread_create(&thr1, NULL, MasterN_Job_1, NULL);
        pthread_create(&thr2, NULL, MasterN_Job_2, NULL);
       
        while(1)
        {
            usleep(50000);
        }    
        return 0;
    }
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 15:49
    OK, I found out how to specify the stack size. It done by passing a pointer to a pthread attribute struct as the second paramater to the pthread_create. The code for setting up the stack size looks like this:
        pthread_attr_t attr;
    
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, stack_size);
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 17:36
    I wasn't aware of cogstart. I guess this requires less library support than pthreads, so it would take less memory. I modified Ray's program to use either pthreads or cogstart depending on whether USE_PTHREADS is defined. It's not working with cogstart for some reason. Maybe stdio requires something that's not provided by cogstart.
    /**
     * @file MasterN.c
     * This is the main MasterN program start point.
     */
    #include <stdio.h>
    #include <propeller.h>
    
    #undef USE_PTHREADS
    
    #ifdef USE_PTHREADS
    #include <pthread.h>
    #endif
    
    void *MasterN_Job_1(void *argv)
    {
    #ifdef USE_PTHREADS
        pthread_set_affinity_thiscog_np(); 
    #endif
        printf("This is job_1 on cog %d\n", cogid());
        while(1)
        {
            usleep(50000);    
        }
    }
    
    void *MasterN_Job_2(void *argv)
    {
    #ifdef USE_PTHREADS
        pthread_set_affinity_thiscog_np(); 
    #endif
        printf("This is job_2 on cog %d\n", cogid());
        while(1)
        {
            usleep(50000);
        }
    }
    
    
    /**
     * Main program function.
     */
    int main(void)
    {
    #ifdef USE_PTHREADS
        pthread_t thr1;
        pthread_t thr2;
    #else
        int stacksize = sizeof(_thread_state_t)+sizeof(int)*100;
        int *stack1 = (int*) malloc(stacksize);
        int *stack2 = (int*) malloc(stacksize);
    #endif
    
        waitcnt(CLKFREQ + CNT);
        printf("This is main on cog %d\n", cogid()); 
    
    #ifdef USE_PTHREADS
        pthread_create(&thr1, NULL, MasterN_Job_1, NULL);
        pthread_create(&thr2, NULL, MasterN_Job_2, NULL);
    #else
        cogstart(MasterN_Job_1, NULL, stack1, stacksize);
        cogstart(MasterN_Job_2, NULL, stack2, stacksize);
    #endif
       
        while(1)
        {
            usleep(50000);
        }    
        return 0;
    }
    
  • ersmithersmith Posts: 6,105
    edited 2012-09-23 18:21
    Rsadeika wrote: »
    Below is a rough interpretation of a spin2cpp --ccode conversion of a Spin program that basically starts a couple of cognew methods with the associated stack. The program below compiles, but I do not see printf("This is job_2\n") from job_2(). Anybody have any ideas as to why that is happening?
    spin2cpp does not handle spin methods as arguments of cognew. It only works for cognew on PASM code.
  • jazzedjazzed Posts: 11,803
    edited 2012-09-23 19:11
    Dave Hein wrote: »
    I wasn't aware of cogstart. I guess this requires less library support than pthreads, so it would take less memory. I modified Ray's program to use either pthreads or cogstart depending on whether USE_PTHREADS is defined. It's not working with cogstart for some reason.

    The general rule is that we use waitcnt with cogstart and usleep with pthreads. Try changing that.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 19:50
    I tried a few things, and it looks like the SimpleSerial driver doesn't work with multiple cogs. The cogstart version works if I use the FullDuplexSerial driver. I noticed that when I used pthreads it was loading the 2 threads in cogs 2 and 3 instead of 1 and 2. I suspect that the FullDuplexSerial driver is automatically being loaded when using pthreads. If I explicitly load the SimpleSerial driver, then pthreads doesn't print either.
  • jazzedjazzed Posts: 11,803
    edited 2012-09-23 20:13
    Dave Hein wrote: »
    I tried a few things, and it looks like the SimpleSerial driver doesn't work with multiple cogs. The cogstart version works if I use the FullDuplexSerial driver. I noticed that when I used pthreads it was loading the 2 threads in cogs 2 and 3 instead of 1 and 2. I suspect that the FullDuplexSerial driver is automatically being loaded when using pthreads. If I explicitly load the SimpleSerial driver, then pthreads doesn't print either.

    Thanks for the update. These various print things having caveats is a pain. I've found in many cases (especially with files and floating point) that the standard printf is often smaller than __simple_printf. Maybe we should retire that option since we have the tiny library in the release. Of course the tiny library has it's own limits.
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-24 03:28
    The program below is using cogstart, which I can get job_1 to start and work, but job_2 does not start up. I thought when you use cogstart it starts the next available cog? I am assuming, in the program below, their should be at least one more cog available for use. I also put in a gets() in the main, so far it has no affect on job_1. In one of my other attempts, using pthreads, as soon as I put in a functional gets(), the other pthreads lock up. So at least with this version the new started cog is not locking up with a gets(), not yet at anyway.

    The latest tinkering has me thinking, the great thing about the Propeller are the cogs, and when using Spin, you can get a cog(s) to function like its own little processor without being affected by the other running cog(s). So, in C what is the code to achieve the equivalent result? I know this question has been asked before, lets try an apples to apples answer, if possible.

    Ray
    #include <stdio.h>
    #include <stdlib.h>
    #include <propeller.h>
    
    volatile unsigned int wait_time;
    //volatile unsigned int wait_time1;
    
    /*
     * toggle thread function gets started in an LMM COG.
     * param arg = pin number to toggle
     */
    void job_1(void *arg)
    {
        int pin0 = (int) arg;
        unsigned int nextcnt;
        DIRA |= (1 << pin0);         // set pin to output
        nextcnt = wait_time + CNT;
        for(;;) {
            OUTA |=  (1 << pin0);    // set pin high
            nextcnt = waitcnt2(nextcnt, wait_time);
            OUTA &= ~(1 << pin0);    // set pin low
            nextcnt = waitcnt2(nextcnt, wait_time);
        }
    }
    
    void job_2(void *arg)
    {
        int pin1 = (int) arg;
        unsigned int nextcnt;
        DIRA |= (1 << pin1);         // set pin to output
        nextcnt = wait_time + CNT;
        for(;;) {
            OUTA |=  (1 << pin1);    // set pin high
            nextcnt = waitcnt2(nextcnt, wait_time);
            OUTA &= ~(1 << pin1);    // set pin low
            nextcnt = waitcnt2(nextcnt, wait_time);
        }
    }
    
    void main (void)
    {
        int pin0 = 0;
        int pin1 = 0;
        
        int stacksize = sizeof(_thread_state_t)+sizeof(int)*3;
        int *stack = (int*) malloc(stacksize);
        int cog;
        // initialize wait time to 50ms
        wait_time = CLKFREQ/20;
        // start the cog
        cog = cogstart(job_1, (void*) pin0, stack, stacksize);
        waitcnt(CLKFREQ + CNT);
        printf("do_toggle started on COG %d\n", cog);
            
    //    wait_time = CLKFREQ/20;
    //    int stacksize1 = sizeof(_thread_state_t)+sizeof(int)*3;
    //    int *stack = (int*) malloc(stacksize);
        int cog1;
    //    wait_time = CLKFREQ/20;
        cog1 = cogstart(job_2, (void*) pin1, stack, stacksize);
        
    //    waitcnt(CLKFREQ + CNT);
        printf("do_toggle started on COG1 %d\n", cog1);
        int8_t buffer[80];    
        while(1)
        {
            printf("> ");
            gets(buffer);
        }
    }
    
  • ersmithersmith Posts: 6,105
    edited 2012-09-24 05:04
    Dave Hein wrote: »
    I tried a few things, and it looks like the SimpleSerial driver doesn't work with multiple cogs.

    Yes. The default state of a serial port output is high. If two COGs are using the same serial port then neither one can ever output, because the other will leave the pin high and so any attempt to drive it low is fruitless.

    This could be worked around by having SimpleSerial switch the pin to input in COGs that aren't using it at the moment, except that on the QuickStart board this results in garbage as the pin floats around.

    I didn't see any good solution to this, but perhaps someone else will have an idea?
  • jazzedjazzed Posts: 11,803
    edited 2012-09-24 09:56
    Ray, you need a stack per function started in each cog. Spin needs a stack per function as well.
    Also, you don't need to use malloc for the stack, simple arrays will work.
    Rsadeika wrote: »
    The latest tinkering has me thinking, the great thing about the Propeller are the cogs, and when using Spin, you can get a cog(s) to function like its own little processor without being affected by the other running cog(s). So, in C what is the code to achieve the equivalent result?

    What you are attempting with cogstart will produce the equivalent result. It should be practically the same as cognew in spin except that the arguments are different.
  • jazzedjazzed Posts: 11,803
    edited 2012-09-24 09:58
    ersmith wrote: »
    Yes. The default state of a serial port output is high. If two COGs are using the same serial port then neither one can ever output, because the other will leave the pin high and so any attempt to drive it low is fruitless.

    This is why I wanted to drive it low at the end of the function. If the stupid Quickstart board had a pull up, this wouldn't be a problem because we could let it float.
  • ersmithersmith Posts: 6,105
    edited 2012-09-24 11:13
    jazzed wrote: »
    This is why I wanted to drive it low at the end of the function. If the stupid Quickstart board had a pull up, this wouldn't be a problem because we could let it float.

    Leaving it low results in a break condition. I don't know how terminal programs will respond to that -- some of them will probably be OK, but I don't know in general.
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-24 13:24
    I guess I am sort of on the right track, it works as expected. Maybe somebody can give it a quick look over, and make some suggestions as to where it can be improved. I will be adding the softRTC next, and see if it still works as expected. I am going to add the IR detector as a last item, I think that will be a lot more difficult than I expect.

    Ray
    #include <stdio.h>
    #include <stdlib.h>
    #include <propeller.h>
    #include <unistd.h>
    
    volatile unsigned int wait_time;
    volatile unsigned int cid0;
    volatile unsigned int cid1;
    volatile unsigned int lednum;
    
    /*
     * toggle thread function gets started in an LMM COG.
     * param arg = pin number to toggle
     */
    void do_LED0(void *arg)
    {
        cid0 = cogid();
    
    
        while(1)
        {
            if (lednum == 0)
            {
                high(lednum);
                sleep(1);
                low(lednum);
                lednum = 100;
            }
        }
    }
    
    void do_LED1(void *arg)
    {
        cid1 = cogid();
       
        while(1)
        {
            if (lednum == 1)
            {
                high(lednum);
                usleep(1000000);
                low(lednum);
                lednum = 99;
            }
        }
    }
    
    
    void high(int WCpin)
    {
        unsigned int bits = 1 << WCpin;
        DIRA |= bits;
        OUTA |= bits;
    }
    
    void low(int WCpin)
    {
        unsigned int mask = 1 << WCpin;
        DIRA |= mask;
        OUTA &= ~mask;
    }
    
    void main (void)
    {
        waitcnt(CLKFREQ + CNT);
        
        int stacksize = sizeof(_thread_state_t)+sizeof(int)*3;
        int *stack = (int*) malloc(stacksize);
        int cog;
        
        cog = cogstart(do_LED0, NULL, stack, stacksize);
        printf("do_LED0 started on COG %d\n", cog);
        cog = cogstart(do_LED1, NULL, stack, stacksize);
        printf("do_LED1 started on COG %d\n", cog);
        
        uint8_t buffer[80];
            
        while(1)
        {
            printf("> ");
            gets(buffer);
            if (!strcmp(buffer, "quit")) break;
            else if (!strcmp(buffer, "led1"))
            {
                lednum = 1;
            }
            else if (!strcmp(buffer, "led0"))
            {
                lednum = 0;
            }
            else
            {
                printf("Invalid command\n");
            }
        
        }
        cogstop(cid0);
        cogstop(cid1);
        printf("System stopped\n");
        return 0;
    }
    
  • jazzedjazzed Posts: 11,803
    edited 2012-09-24 14:27
    Rsadeika wrote: »
    I guess I am sort of on the right track, it works as expected. Maybe somebody can give it a quick look over, and make some suggestions as to where it can be improved. I will be adding the softRTC next, and see if it still works as expected. I am going to add the IR detector as a last item, I think that will be a lot more difficult than I expect.

    Please use a separate stack for each cog function as previously suggested. It's the right thing to do. What you have now is working "as expected" by accident rather than by safe design. The functions are running at the same time, but they are mutually exclusive because one does work with lednum == 0 and the other with lednum == 1.
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-25 05:32
    Using the program, in the previous post, I started to add some code, and it started to get to big, so I switched it to XMMC mode at which point the program no longer works as expected. Since the program is based on the example in cogstart, and it has this quote:
    Start a new propeller LMM function/thread in another COG.
    Does this mean it will not work in XMMC mode? If so, is there an example as to how I can get a cogstart to work in XMMC mode?

    Ray
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-25 06:07
    It seems like there would be a problem with running additional C cogs in the XMMC mode. I believe you can only have one XMMC cog running at a time because of limitations on the external memory cache driver. You can't start an LMM C cog from XMMC because the LMM interpreter isn't part of the executable, and the code is compiled for the XMMC model. You could include COG C code in your program, and start them using cognew.

    Have you tried the CMM mode? That will allow you to run more code versus the LMM mode.
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-25 08:16
    Yes, I tried CMM, runs fine, but I anticipate the program size to exceed what is allowed in CMM mode. Before I grow my program, using cogstart, I need to know if their will be a problem down the road with XMMC, and the use of cogstart.
    You could include COG C code in your program, and start them using cognew.
    At this point, I do not fully understand how to use it. I was playing around a bit with cognew, did not make much headway in programming success.

    So, if their are limitations with using cogstart and XMMC, then I guess I have to concentrate on the cognew thing. Some examples would be nice, maybe something that starts a couple of threads, blinking some LEDs, and some I/O in main().

    Ray
  • jazzedjazzed Posts: 11,803
    edited 2012-09-25 09:24
    Rsadeika wrote: »
    So, if their are limitations with using cogstart and XMMC, then I guess I have to concentrate on the cognew thing. Some examples would be nice, maybe something that starts a couple of threads, blinking some LEDs, and some I/O in main().

    It is correct that XMMC can not run multiple COG functions.

    There are many examples where PASM and COGC programs are used to good effect.

    I'm attaching a very simple example and nicely structured example of using a COGC program for you. It is a button demo. That example uses C++, but can easily be converted to C. The _C_LOCK statement is a work-around for a bug in an old propeller-gcc version, it can be ignored/removed now.

    There is an 8 COG COGC LED demo in the repository and propeller-gcc-demos.zip.
    There is also a COGC toggle demo in the repository and propeller-gcc-demos.zip.
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-25 09:53
    Thanks jazzed.

    I was just looking at the cog_c example that is in your toggle series. On the C3 board, I loaded up the program, and ran it in LMM, it works as advertised. When I switched it to run in XMMC, it ran, but not as expected, the LEDs flashed, at the same rate, forever. Not sure where the problem is.

    Boy, this learning curve for C on the Propeller, is very steep, and I can not see the plateau, if there is one.

    Ray
  • jazzedjazzed Posts: 11,803
    edited 2012-09-25 10:50
    Rsadeika wrote: »
    Thanks jazzed.

    I was just looking at the cog_c example that is in your toggle series. On the C3 board, I loaded up the program, and ran it in LMM, it works as advertised. When I switched it to run in XMMC, it ran, but not as expected, the LEDs flashed, at the same rate, forever. Not sure where the problem is.

    Boy, this learning curve for C on the Propeller, is very steep, and I can not see the plateau, if there is one.

    Ray

    The demo tries to be generic, but in this case it fails because the pins is interfering with the C3 xmmc hardware.

    Please change toggle_fw.cogc:
    static _COGMEM unsigned int pins = 0xfffffff; /* all pins except Serial IO and I2C */
    to
    static _COGMEM unsigned int pins = 0x8000; /* all pins except Serial IO and I2C */
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-09-26 06:39
    I cleaned up the program a little, now it works correctly, or maybe I am just getting very lucky. While it is being compiled in CMM mode, the program still has about 4KB of space still left. Response time for a CMM mode is still very good, I do not see any lag time anywhere. Once spin2cpp gets updated, then I will add the IR_Remote file, and see if that still fits in the existing space. If it does not then I do not know what the next step will be. I am using a 4GB uSD card, and there does not seem to be any problems, I will have to look around for a larger uSD card to see if there is a limit.

    Ray
Sign In or Register to comment.