C3 and threads(cognew)
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
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
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; }pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, stack_size);https://sites.google.com/site/propellergcc/documentation/libraries/propeller-h-library#TOC-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; }The general rule is that we use waitcnt with cogstart and usleep with pthreads. Try changing that.
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.
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); } }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?
Also, you don't need to use malloc for the stack, simple arrays will work.
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.
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.
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; }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.
Ray
Have you tried the CMM mode? That will allow you to run more code versus the LMM mode.
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
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.
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 */
Ray