Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Cog Problem — Parallax Forums

Propeller Cog Problem

My application uses Cogs for independent operation but one Cog is not operating.
Cog 0 should be the main program
Cog 1 should be rational number calculation
Cog 2 should be the Z axis control
Cog 3 should be the X axis control
Cog 4 should be the Y axis control

But Cog 3 will not operate. Cog 3 and Cog 4 are the same identical code. The only difference is Cog 3 operates on a global file of numbers contained in X[500] while Cog 4 operates on a global file of numbers contained in Y[500].

If I send the X[500] file to Cog 4 it works fine. If I try to set a test global variable in Cog 3...the main program does not get the value. The same test global variable put into Cog 4 is read correctly by the main program. It appears that Cog 3 is not operating.

The global variables are all static volatile types.
static volatile int X[500], Y[500];
void Xaxis(void* par);
void Yaxis(void* par);
void Zaxis(void* par);

unsigned int stack[400+25];

cogstart(&Xaxis, NULL, stack, sizeof(stack);
cogstart(&Yaxis, NULL, stack, sizeof(stack);
cogstart(&Zaxis, NULL, stack, sizeof(stack);

Any ideas?

Sincerely,

Discovry

Comments

  • JasonDorieJasonDorie Posts: 1,930
    edited 2017-01-09 22:06
    You can't use the same stack for 3 different cogs - That's almost certainly the issue based on the code shown here. That's also a pretty large stack - I think the minimum for C is 40 longs (it's documented somewhere), so unless your code makes heavy use of stack space what you've shown here would be overkill.
  • I was using a stack size of 40+25 but changed it to 400+25 as a test.

    So, I need three different stack declarations.

    Thank you.

    Discovery
  • Yes, each cog has local state, and as functions call other functions, state gets pushed onto the stack. Three cogs running code in parallel will have their own stack needs to track their individual state. If you give them all the same stack Bad Things(tm) will happen. :)
  • How do you suggest that I declare the three different stacks?

    Discovery
  • The simplest solution would be:
    unsigned int Xstack[40+25];
    unsigned int Ystack[40+25];
    unsigned int Zstack[40+25];
    
    cogstart(&Xaxis, NULL, Xstack, sizeof(Xstack);
    cogstart(&Yaxis, NULL, Ystack, sizeof(Ystack);
    cogstart(&Zaxis, NULL, Zstack, sizeof(Zstack);
    

  • Okay...very good.

    Thank you.

    Discovery
  • Hi,
    I used the code supplied by Jason Dorie above in my upgraded plotting machine and the machine went crazzy.

    So, I made a COG tester program to investigate the COGs. The attached code runs three COGs (X,Y,Z). The results of calculations should produce TestX = 45, TestY = 55, and TestZ = 66. Only COGs Y and Z compute values. Both COGs Y and Z compute correctly. COG X does not compute at all.

    Code is attached. I cannot find anything wrong. What is the problem?

    DiscoveryUntitled%201.pdf
  • Hi again,
    The previously attached C file is not the correct file. The attached C file below is correct. XCog does not compute. The output should be TestX = 45, TestY = 55, and TestZ = 66.

    Discovery
  • Uh, I would think if you wanted the cog associated with Xaxis to start, that you would need to start that cog. Your code only starts Yaxis and Zaxis.


    cogstart(&Yaxis, NULL, Ystack, sizeof(Ystack));
    cogstart(&Zaxis, NULL, Zstack, sizeof(Zstack));
  • You're also not waiting for them to complete before you print the results. Once you start a cog it runs in parallel with your main code, so your printing function may be called while the results are only partially finished, depending on how long the routines take. In this case they're very simple so you're probably getting lucky, but if they get any more complicated you'lol have to wait somehow.

    If your eventual goal is to run a motor on each cog, you'all need to synchronize them somehow.
  • Discovery wrote: »
    Hi again,
    The previously attached C file is not the correct file. The attached C file below is correct. XCog does not compute. The output should be TestX = 45, TestY = 55, and TestZ = 66.

    Your code works fine for me, however you need to wait for the cogs to complete before working on the data they modify. Add a delay of 1 second (just to be sure) before printing the values. Actual code needs to account for the time needed for a cog to compute the values before working on them, use a flag variable to indicate when the data are ready. Something like this:
    /*
      COG Tester.side
    */
    #include"simpletools.h"
    #include"propeller.h"
    #include"stdio.h"
    #include"math.h"
    #include"stdlib.h"
    #include "simpletools.h"
    
    int j;
    
    static volatile int TestX;
    static volatile int TestY;
    static volatile int TestZ;
    
    // Flags
    static volatile int Xready;
    static volatile int Yready;
    static volatile int Zready;
    
    void Xaxis(void* par);
    void Yaxis(void* par);
    void Zaxis(void* par);
    
    unsigned int Xstack[40+25];
    unsigned int Ystack[40+25];
    unsigned int Zstack[40+25];
    
    int main(void)     // main function
    {
      cogstart(&Xaxis, NULL, Xstack, sizeof(Xstack));
      cogstart(&Yaxis, NULL, Ystack, sizeof(Ystack));
      cogstart(&Zaxis, NULL, Zstack, sizeof(Zstack));
    
      // Wait for all values to be ready
      while(!Xready || !Yready || !Zready)
        ;
    
      print("TestX = %d\n",TestX);
      print("TestY = %d\n",TestY);
      print("TestZ = %d\n",TestZ);
    }
    
    void Xaxis(void* par)
    {
      for(j=1;j<10;j++)
      {
        TestX = TestX + j;
      }
      Xready = 1; // Signal value ready
    }
    
    void Yaxis(void* par)
    {
      for(j=1;j<11;j++)
      {
        TestY = TestY + j;
      }
      Yready = 1; // Signal value ready
    }
    
    void Zaxis(void* par)
    {
      for(j=1;j<12;j++)
      {
        TestZ = TestZ + j;
      }
      Zready = 1; // Signal value ready
    }
    


  • Shall do.

    Thanks you.

    Discovery
  • Oh, I just noticed that you are using a global variable for the count loop! That's not correct! Probably the compiler optimizes the loop with a register variable so it works in that case, but if you do something like that in a real code you'll have troubles. You need to use instance variables for each running cog. Remove that j global variable and change the cog code like this:
    void Xaxis(void* par)
    {
      int j;
      for(j=1;j<10;j++)
      {
        TestX = TestX + j;
      }
      Xready = 1; // Signal value ready
    }
    
  • Oh...

    Thank you.

    Discovery
  • A sanity check.

    I thought that global variables were defined by static variable statements prior to the main program. Variables used in Cogs were local variables. What am I missing?

    Discovery
  • At the top of your code file you define variable 'j'. That makes it global. If you define the variable *inside* a function, then it's a local variable. The "running in a cog" part doesn't matter to C/C++.
    int i;  // this is a global variable
    
    void MyFunction(void)
    {
      int x;  // this is a local variable, scoped inside MyFunction()
    }
    
  • "static" has nothing to do with whether a variable is global or local. For global variables, it makes the symbol inaccessible from other .c files, i.e. it makes the variable only visible from inside that .c file. For local variables, it guarantees that every invocation to the function gets the "same" variable, meaning that it will have the same value across function calls or in different cogs. In other words, a static local is really a global variable that is only visible from the {} scope it is declared in.
  • Hi ,

    In other words, a static local is really a global variable that is only visible from the {} scope it is declared in.

    Sorry, but I am confused.

    In my application code, not my test code, I use a variable Ops in two Cogs to wait for the Ops to go to "1" then the Cogs set digital outputs to control two stepper motors (X and Y) in the plotter machine..

    In the X Cog...
    while(Ops==0)
    {
    }

    {
    for j=1;j<w+1;J++)
    {
    X=x[J];
    stepperX();
    }

    and in the Y Cog...
    while(Ops==0))
    {
    }

    {
    for(j=1;j<z+1;j++)
    {
    Y= y[j];
    stepperY();
    }
    So, the j variable should be declared int j in each Cog and the Ops, X[500], and Y[500] variables should be declared just
    int in the main program code. Is that correct?

    Once the Ops variable in the main program goes to 1, both Cogs call the stepperX() and StepperY() subroutines to clock the two steppers very rapidly emptying the contents of the values in the X and Y arrays and moving the plotting head.

    I there anything wrong here?

    Sincerely,

    Discovery
  • Discovery wrote: »
    In other words, a static local is really a global variable that is only visible from the {} scope it is declared in.

    Sorry, but I am confused.

    If you want a suggestion to better understand the variables visibility, separate the different "tasks" your code does into different source files. One source would be the "main" source with the entry point and all global variables and functions, then one source for each task running in a separate cog. I hope that the IDE you are using (if any) doesn't merge the code to simplify things.

    When you do that, you'll find that variables and functions defined in a source are not immediately visible from other sources, you need to declare them as extern in each source where you are using them. If you don't declare them extern then the linker will throw an error (duplicated symbol). Variables defined as static are visible only from the source where are declared, even if you declare them extern in another source, the linker will throw a symbol not found error. I assume that you already know that variables declared in a function are visible by that function only, even if static.

    Aside from learning, separating the source code is a good pratice and helps maintaining the code clear.

    About your code, it should work as described. I'm not sure when the Ops variable is cleared. As described both cogs should start but you don't know when they finish, you need something else to signal that the cogs have finished the work otherwise resetting the Ops variable may cause troubles (one code may not see the new state and believe that it should start the stepper fuction again, maybe with incorrect data.

    Also, defining volatile the variables used by concurrent code is a good practice because tells the compiler to not optimize them and always use the correct value.

    Hope this helps.
  • Thank you for the information. I would bet that there is a lot more to learn about passing the two stepper control signals (two per stepper) through the hub in such a way that they do not get combined in the IO register.

    Discovery
  • Not sure what you mean.
    There is a single DIRA/OUTA register for each cog and in C you can modify the single bits using the bitwise or, and, xor operators:
    OUTA |= (1 << 15); // Set PIN15=1
    OUTA &= ~(1 << 15); // Set PIN15=0 (note the tilde operator)
    OUTA ^= (1 << 15); // Toggles PIN15
    

    I think there are helper functions in simple libraries to make it work more like Spin, but in the end the above is what they do.

    See the Propeller manual about how the registers are then combined to produce the actual pin states.
  • The OUTA and DIRA registers are hardware-level shadow registers that are special, and exist independently inside each cog, so you aren't setting a global value. The Propeller chip itself combines the different values of OUTA and DIRA in each cog to control the hardware - different cogs may have different OUTA/DIRA settings, so you don't need to take any care to combine them properly with other cogs.

    The one thing I think you will have a problem with is that two independent cogs controlling steppers is not a simple thing. Picture a line with a 30 degree slope. For each two steps you take in X, you take one step in Y. How do you make sure that the two motors advance at the right time so your line stays straight? It's actually easier to do this in a single cog with one loop controlling both motors. Look up DDA (digital differential analyzer) and Bresenham algorithms. Stepper motors can be treated very much like pixels on a graphics display, so graphics rasterization algorithms can be used with very few changes.
  • I see...
    My current controller uses a single Cog to operate the Z axis stepper to keep the plasma cutter head at a distance of 1.6 mm from the base metal throughout the cut. The main program Cog does the calculating for X axis steps and Y axis steps and works well but it must be faster. That brought up the thought to put the X axis and Y axis control in each of two Cogs. The other option that I found is to replace the slow steppers with high speed high power units and keep the control as is. The decision is to order replacement Steppers.

    Sincerely,

    Discovery
Sign In or Register to comment.