Shop OBEX P1 Docs P2 Docs Learn Events
Information exchange between different cogs trough variables (different threads — Parallax Forums

Information exchange between different cogs trough variables (different threads

chbrandlchbrandl Posts: 13
edited 2007-08-17 18:05 in Propeller 1
Hi,
I wanted·to calculate different threads of a whole problem, but how does a variable exchange works. I test it with a small programm but the cogs doesn´t exchange the information.
The programm should simply read a variable from a the terminalprogram, calculate three different results on three cogs and display the whole result on the terminal. How does it work correct. I alter an example program from parallax.

CON
··
· _clkmode = xtal1 + pll16x
· _xinfreq = 5_000_000
·
VAR long x
··· long in
··· long in1
··· long in2
··· long stack1[noparse][[/noparse]10]
··· long stack2[noparse][[/noparse]10]
··· long stack3[noparse][[/noparse]10]
OBJ
··
· Debug: "FullDuplexSerialPlus"
··
··
PUB TwoWayCom | value
· ''Test HyperTerminal number entry and display.

· Debug.start(31, 30, 0, 57600)
· repeat
···· Debug.Str(String("Enter a decimal value: "))
···· value := Debug.getDec
···· Debug.Str(String(10, 10, 13, "You Entered", 10, 13, "
"))
···· debug.dec(value)
···· Debug.Str(String(10, 13, "Decimal: ",10,13))
···· cognew (multi(value+1,IN),@stack1)
···· cognew (multi(value+2,IN1),@stack1)
···· cognew (multi(value+3,IN2),@stack2)
···· debug.dec(IN)
···· debug.dec(in1)
···· debug.dec(in2)
···· value:=IN+IN1+IN2
···· Debug.Dec(value)
···· debug.str(string(10,13," ",10,13))
···· repeat 2
······· Debug.Str(String(10, 13))
············
PUB MULTI(A,B)
···· B:=A*5

Thanks
Christopher···
·

Comments

  • BergamotBergamot Posts: 185
    edited 2007-08-16 13:46
    All Spin variables live on the hub, so there really is no need to transfer them from cog to cog. If two objects have access to a variable, they can just use it.

    I think your problem is that you're passing the values of the b variables into the function, when you should be passing the address to the variable itself.

    Post Edited (Bergamot) : 8/16/2007 1:51:51 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-16 15:12
    Bergamamot pointed out problem 1: This is a basic misunderstanding of parameter passing by reference, that has nothing to do with parallelism at all.
    Problem 2 is a typo (stack1 is use twice)
    Problem 3 is that is makes no sense to START a COG for an operationen taking less that 10 SPIN operations; you should learn how to use a COG as a "server", waiting for requests.

    Do do not run into problem 4 as long as you perform "debug" operations after loading the COGS, you will however when you remove them and try to add the three results that have presumably not yet been computed...

    Post Edited (deSilva) : 8/16/2007 3:19:06 PM GMT
  • chbrandlchbrandl Posts: 13
    edited 2007-08-16 18:33
    Okay but how it works, how can two cogs interact with variables together ?
    The use of the stack1 twice is·an error,·it should be stack1, stack2, stack3, like declared above.
    I test all variantes, but the variables are always 0, and the result is 0.
    One is right a new cog is only·used if there are more than 10 operations.
    Please an example in spin.

    Best regards
    Christopher


    Post Edited (chbrandl) : 8/16/2007 6:38:11 PM GMT
  • BergamotBergamot Posts: 185
    edited 2007-08-16 18:46
    EDIT: I'm having trouble with code formatting, and other people have answered it better below

    Post Edited (Bergamot) : 8/16/2007 6:56:35 PM GMT
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-16 18:47
    May be you have really run into "Problem 4" with the first debug.dec, because IN is mostly likely not yet ready...

    But in general your program should work when doing the "call by references" correctly as explained by Bergamot.

    Example... Bergamot also explained that: You can access any "global" variable you like! There are no such restrictions in SPIN as you keep reading from the Assembly gurus all the time smile.gif

    Edit: This forum software swallowed his "[noparse][[/noparse] ", it should read:
    long [noparse][[/noparse] B ] := A * 5
    

    Post Edited (deSilva) : 8/16/2007 6:54:12 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-16 18:52
    You write:
    PUB MULTI(A,B)
         B:=A*5
    
    


    This can not do what you want. B is a local variable that is allocated in the stack that this cog is using. Spin passes parameters "by value". It copies the value supplied. You want to pass that parameter "by reference" which is done by passing an address "by value". In other words, you have:
    PUB MULTI(A,B)
       LONG[noparse][[/noparse] B ] := A * 5
    
    


    When you start this cog, you have to pass the address of the variable as the 2nd parameter like this:
       COGNEW(MULTI(value+1,@in),@stack1)
    
    
  • chbrandlchbrandl Posts: 13
    edited 2007-08-17 11:35
    Hi, thank you for your replys, but now I have an other problem, the cogs only do parts of the program. The program should add the sum of the numbers.

    Best whishes
    Christopher

    CON
    · _clkmode = xtal1 + pll16x
    · _xinfreq = 5_000_000
    ·
    VAR long x
    ··· long in
    ··· long in1
    ··· long in2
    ··· long stack1[noparse][[/noparse]20]
    ··· long stack2[noparse][[/noparse]20]
    ··· long stack3[noparse][[/noparse]10]
    ··· byte cog
    ··· byte cog1
    ··· long out
    OBJ
    ··
    · Debug: "FullDuplexSerialPlus"
    ··
    ··
    PUB TwoWayCom | value
    · ''Test HyperTerminal number entry and display.

    · Debug.start(31, 30, 0, 57600)
    · repeat
    ···· Debug.Str(String("Enter a decimal value: "))
    ···· value := Debug.getDec
    ···· Debug.Str(String(10, 10, 13, "You Entered", 10, 13, "
    "))
    ···· debug.dec(value)
    ···· Debug.Str(String(10, 13, "Decimal: ",10,13))
    ···· cog:=cognew(MULTI(Value,@IN),@stack1)
    ···· 'cog1:=cognew(MULTI(Value+1,@IN1),@stack2)
    ···· out:=IN+IN1+IN2
    ···· debug.str(String(10,13,"IN",10,13))
    ···· debug.dec(IN)
    ···· debug.str(String(10,13,"IN1",10,13))
    ···· debug.dec(IN1)
    ···· debug.str(String(10,13,"OUT",10,13))
    ···· debug.dec(out)
    ···· debug.str(string(10,13,"fertig",10,13))
    ···· repeat 2
    ······ Debug.Str(String(10, 13))
    ···· IN:=0
    ···· IN1:=0
    ···· IN2:=0
    ···· OUT:=0·
    ···· cogstop(cog)
    ···· 'cogstop(cog1)·
    ············
    PUB MULTI (A,B)
    ···· long [noparse][[/noparse]B]:=0
    ···· long [noparse][[/noparse]X]:=1
    ···· repeat A
    ····· long [noparse][[/noparse]B]:=long [noparse][[/noparse]B]+long [noparse][[/noparse]X]
    ····· long [noparse][[/noparse]X]:=long [noparse][[/noparse]X]+1
    ···· return B
  • mparkmpark Posts: 1,305
    edited 2007-08-17 13:18
    1. Shouldn't you also pass @X to MULTI?

    2. "Problem 4" that daSilva is obliquely referring to is the fact that it takes some time to start a cog. After you do
    cog:=cognew(MULTI(Value,@IN),@stack1)
    you'll need to add a little delay before looking at IN. The call to debug.str might add enough delay that IN is valid, but
    out:=IN+IN1+IN2
    immediately after the cognew will read IN before the cog has had a chance to update it. Either add a waitcnt before computing out or move the computation down, maybe to just in front of
    debug.str(String(10,13,"OUT",10,13))

    3. Where can I find FullDuplexSerialPlus?



    Michael
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-17 13:42
    PUB MULTI (A,B)
         long [noparse][[/noparse] B ]:=0
         long [noparse][[/noparse] X ]:=1
         repeat A
          long [noparse][[/noparse] B ]:=long [noparse][[/noparse] B ]+long [noparse][[/noparse] X ]
          long [noparse][[/noparse] X ]:=long [noparse][[/noparse] X ]+1
         return B
    



    There are still some misunderstandings, Chbrandl, wrt to basic SPIN features:

    (5) There is no return value from a routine started through COGNEW.
    (6) long[noparse][[/noparse] address ] "dereferences" address, i.e. address has to be a pointer (as with @B); you most likely wanted to use X directly (and NOT long [noparse][[/noparse] X ] )
    (7) it is extremely tricky to modify the same variable from different processes (consult information about locks in this forum)
    (8) It is not only good practice to use "something OP= other" rather than "something := something OP other", but also considerably faster.

    Post Edited (deSilva) : 8/17/2007 1:48:14 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-17 13:48
    Please note that "long[noparse][[/noparse] X ]" references the variable whose address is in "X".

    Now that you've added X which is a global variable shared by all the cogs, you have a standard multiprocessor programming problem ... All of the cogs can attempt to change X at the same time. When you do "X := X + 1", you're really getting a copy of X, adding 1 to it, then copying the result back to X. Since several cogs can be running at the same time, another one can be attempting the same thing at the same time. Which X value are you working with? The one before the other cog increments it or the one after the other cog increments it? When you store what you think should be the value of X back into X, are you destroying the value that some other cog just stored there?

    Have a look at the discussion in the Propeller Manual on the LOCKxxx statements. These are the standard way to share resources among multiple processors. You are fiddling with the subject of several lectures in a course on multiprocessing.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-17 13:56
  • chbrandlchbrandl Posts: 13
    edited 2007-08-17 16:53
    Thanks for the answers.
    No X should be·a local·counter variable. B is the result which should be transfered to the first (I think controlling calling cog 0) cog.
    I hope I see it right, that the variables X, B and A will be used only by the cog and they are on the local·RAM and not on the main RAM. I s it right that the first cog starts with the command cognew a new process on an other cog and uses local variables in it 512 byte local ram ?

    And this is only an example to test my ideas not a really project.
    I wanted to start 2 or more cogs with a similar mathematical problem, which should be solved parallel and all part solves should be·processed finally on the calling cog.

    start Calling (0)cog
    ·start mathproblem on first cog
    ·start mathproblem on second cog
    ·start mathproblem on thrid cog
    process result from first cog with result from second cog and result from third cog.
    Print it on the display.

    Best regards
    Christopher

    PS: The module FullDuplexSerialPlus is found on the object exchange or on the education board on the examples.



    ·
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-17 17:02
    chbrandl said...
    I hope I see it right, that the variables X, B and A will be used only by the cog and they are on the local RAM and not on the main RAM.

    No, No, No! your concepts are still twisted. There is no such thing as "local RAM" when using SPIN!
    chbrandl said...
    ...uses local variables in it 512 byte local ram ?
    No!
  • BergamotBergamot Posts: 185
    edited 2007-08-17 17:05
    chbrandl said...
    I hope I see it right, that the variables X, B and A will be used only by the cog and they are on the local RAM and not on the main RAM. I s it right that the first cog starts with the command cognew a new process on an other cog and uses local variables in it 512 byte local ram ?
    No, all SPIN variables are located in the main hub RAM. The local cog memory is filled with the SPIN interpreter. Only by writing assembly code can you get at the local cog memory.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-17 17:26
    Bergamot and deSilva are technically right, but when you talk about local variables, you're referring to those declared locally in the method and allocated in the stack used by the cog that is executing the method. As far as your program is concerned, they are in memory associated with the cog even though they're stored in part of the main memory. There's no penalty for two cogs to access main memory simultaneously since each cog is assigned a time slot for main memory access.

    A COGNEW or COGINIT does one of two things:

    1) It loads an assembly (machine instruction) program into a cog's local memory either from a user provided 2K byte area (512 long word) in main memory or the Spin interpreter from ROM.

    2) The assembly program is started at location zero after loading is complete, roughly 100us later (after copying 512 long words from main memory to cog memory). If the program is the Spin interpreter, it will begin interpreting the method call that was set up in the COGNEW / COGINIT statement using the stack and the parameters to the method call that were set up on the stack by the COGNEW / COGINIT statement.

    3) If the program started in the cog is a Spin method, it has access to all of the variables (VAR) declared in its object and all of the data (DAT) declared in its object. These are global to the object and this access has nothing to do with the fact that the program is using more than one cog. It also has access to its local variables which are private to the method and unique to the cog executing the method (since there should be a unique stack for each cog running Spin).
  • Mike GreenMike Green Posts: 23,101
    edited 2007-08-17 17:39
    I think it will be easier for you to write your algorithm as an object with most of the variables declared globally (in a VAR section). One variable will be a stack area and the object will have a start method (and a stop method). Your main program will have an instance declared of the object for each cog that you want to be running it.

    The start method will be given the parameters that are different from one instance to the next and it will store them locally for further reference. The main program can call methods in the object to: 1) check on the progress of the computation; 2) retrieve the results when the computation is done; 3) supply information to the computation that may change during the computation; 4) retrieve intermediate results.

    Each instance of the object will have its own set of variables (VAR), but there will be only one copy of the code (PUB/PRI) and only one copy of the data areas (DAT). If your computation requires shared variables, these can be put in the DAT areas and you can use LOCKxxx (see the description in the manual) to regulate access to them.

    Have a look at any I/O driver that uses a separate cog for the I/O. It will have this sort of structure. There will be a start and stop method and several interface methods that are called from whatever program is using this object. These take care of interfacing between the main program and the program running in the dedicated cog. You'll find I/O drivers like this in the Propeller Tool's library and in the Propeller Object Exchange website.
  • chbrandlchbrandl Posts: 13
    edited 2007-08-17 17:54
    Thanks,
    I adapted the program, now it runs. Is there a methode or a command to see if a cog is ready with its calculations ? At the moment I use a waitcnt command.

    I had the same idea to write the methode as an object, but i don´t like objects and object oriented programming languages. In my opinion they are very hard to read.

    Thanks a lot
    Best regards from Austria
    Christopher
    ·
  • deSilvadeSilva Posts: 2,967
    edited 2007-08-17 18:05
    (1) There is a German speaking forum, that will also support you
    (2) Don't be afraid: SPIN "objects" are just a trade name smile.gif
    (3) Your (sub-)program can simply set a "global" variable, just before it quits!

    Post Edited (deSilva) : 8/17/2007 6:12:54 PM GMT
Sign In or Register to comment.