Shop OBEX P1 Docs P2 Docs Learn Events
Shared variables and communication between cogs — Parallax Forums

Shared variables and communication between cogs

ThePenguinMasterThePenguinMaster Posts: 89
edited 2009-07-02 22:04 in Propeller 1
Hey, first off, thanks for taking the time to read this thread. So far i have spent a lot of time playing with the propeller, but only single cogs and pre-made objects that utilize other cogs. I'm getting to the point where i need to better understand how memory is shared between cogs. i want to be able to start a routine in a cog, and have a shared buffer between the cogs where status flags can be set and maybe a chunk of space for data to be transferred. Also, what if both cogs need to read and write to the location in memory? are there any objects in the object exchange you might be able to recommend for a reference?

Comments

  • KyeKye Posts: 2,200
    edited 2009-07-01 01:30
    Hehe, well, its so simple its doesn't need to be explained.

    Use pointers (pointer := @varibale) - pointer should be word sized or larger. If you need to share variables from different objects.

    Otherwise, all cogs started up in a single object file have acess to all variables and data sections very easily. The same as if you were using just one cog.

    As for reading and writing to the same location, you need to use status flags and a data register. Its different depending on what application you want.

    For example: You want to have a flag variable which controls the synchronization between the processors and a data register or set or registers that hold the information you wish to share between the cogs.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • ThePenguinMasterThePenguinMaster Posts: 89
    edited 2009-07-01 02:12
    Thanks, I think I will keep things in the same object for now. I did a test and it is as surprisingly simple as you said!
  • matbmatb Posts: 39
    edited 2009-07-01 08:56
    I started to write some stuff up on the wiki the other day when I was experimenting with different ways of sharing variables between objects.

    Basically it seems there is no really pretty way to do it, compared to C.

    The way used by Parallax, for example in TV.spin is probably the best option.

    From a 'master' object, pass in a pointer to an array to the 'slave' object.
    The Master can then use DAT, VAR, stack variables, or even a manually allocated pointer.
    The slave object stores this pointer.

    The consequence is that the 2 (or more) objects need to agree on the layout of the buffer, which means manually maintaining tight code coupling. In the slave objects, you have to use the typecast syntax.
    The master could use VARS being the most maintainable.

    The most intersting thing I found was that how the pointers were stored had a big influence on code size, and had some unexpected impacts. Access using a pointer in the STACK was best, closely followed by a pointer in a VAR. The worst was using a CONstant to hold a address.!

    Not pretty but its here http://propeller.wikispaces.com/Referencing+Globals. Hack away to make it useful. I'd like it to end up more a set of design patterns really. (Even better would be tweaking spin to have a #include equivalent or some other mechanism! The current way is a maintenance nightmare)
  • Nick MuellerNick Mueller Posts: 815
    edited 2009-07-01 09:23
    A basic principle that can be applied to SPIN, ASM and C is the following:
    Have a controll block (a struct in C, an array in SPIN) that keeps all the necessary information in a single place.
    That information could be flags like "I'm ready", size of something, enum for what to do, etc. Further, that controll block might have an pointer to some array that needs processing and an information about the array's size.
    In C, that might look like this:
    struct {
      int state;  // signals what to do, or not to do
      int size;   // size of array ptr is pointing to
      void* ptr;
    };
    
    



    If you pass a pointer to that controll block in the cognew, all is tidy, flexible, controllable and clear.

    HTH,
    Nick

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Never use force, just go for a bigger hammer!

    The DIY Digital-Readout for mills, lathes etc.:
    YADRO
  • KyeKye Posts: 2,200
    edited 2009-07-01 12:45
    You could also use locks, but I feel that they have the same problem as using the command coginit. So that said, they are dangerous.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • matbmatb Posts: 39
    edited 2009-07-01 13:22
    Whats a good pattern/example for sensor values? Particularly a bunch of basically unrelated values (e.g. guages)

    I find that problematic, as something, usually a few Objects and/or COGs measuring some raw values.

    I then need to use it for a few things, in a number of places:

    - in display logic to show on screen (and detect change of value)
    - in monitoring logic to decide if needs to alert or do something else
    - in logging logic to record historical stats

    I originally had each sensor type object managing its own data, with a spin method to retrieve each value.
    I then found I needed to use a master object to poll ALL the objects to copy the values somewhere else,
    then they were calling other objects with lots of parameters, for example to display a screenfull of different measurements.

    I was thinking either:
    • a obj full of CONstants that are pointers to 'free' space for each value
    • dito but with constants for array offsets
    • a wrapper object with global values all in DAT, with spin access methods

    A wrapper would be nice, but lots of overhead involved.
  • ThePenguinMasterThePenguinMaster Posts: 89
    edited 2009-07-01 14:23
    Ok so pointers are simple enough to use. Also I started a few test programs using variables between cogs and it looks simple enough. The lack of structure is difficult to deal with but ill adapt to it, it’s a luxury that I guess I don’t really need. If I ever want to get my feet wet in ASM then I will need to learn to work with out structures and such. I did a test where I declared a simple variable as a long, and then stored a number into it. From there I started a new cog with a function that starts up the terminal and then displays that value. Everything worked great! Now I’m trying to adapt that to work with another concept, the pseudo code for what I want to accomplish is as follows:
    ·
    VAR
    ··········· Long theList···· ‘this is intended to be an array of stored strings
    ··········· Long Stack[noparse][[/noparse]10]
    PUB start | i
    ··········· i:=0
    ··········· Cognew(GetList(@thelist, stack[noparse][[/noparse]0])
    ··········· ‘There is some code to wait for the other cog here
    ··········· Repeat while theList[noparse][[/noparse]i] > 0
    ······················· Terminal.str(theList[noparse][[/noparse]i])
    ······················· i++
    ·
    PUB GetList(list)
    ··········· Repeat until AFunctionThatReturnsAString returns nothing
    ··········· ··········· Long[noparse][[/noparse]list] := AFunctionThatReturnsAString
    ··········· ··········· List++
    ·
    It is what happens in GetList that I am unsure about, now what I’m thinking is that you want to create a long containing the string that ‘AFunctionThatReturnsAString’ returns and increment the address to the long and do it again to create an array. Now I know that the repeat statement will not work as typed, because there is no returns nothing statement, but the idea is that I don’t know how many strings the function is going to return, and I need to store them into an array that both cogs can access. So using the pointers is a huge help. Now when I read the values they are not what I expect. Any suggestions? I know this may be a simple problem, but I’m new to this and some of the simple concepts are proving to be difficult. It’s a little different than C++, but once I get the basic concepts down, I should be good to go. If necessary, I can post the exact code I wrote last night after I get home.
  • matbmatb Posts: 39
    edited 2009-07-01 22:29
    Problem one...
    Pointers are not typed like in c. Incrementing the pointer only incremented one byte.
    Better to use the array indexing syntax (generated code is smaller in spin, not sure if its faster though)
    PUB GetList(list) | i
                Repeat until AFunctionThatReturnsAString returns nothing
                            Long[noparse][[/noparse] list ][noparse][[/noparse] i ] := AFunctionThatReturnsAString
                            i++
    
    



    As for waiting for the 2nd cog... as the example stands, not much point using 2nd cog at all.

    Edit: Edit: Grrr how do you post unmolested code????

    Post Edited (matb) : 7/1/2009 10:43:55 PM GMT
  • Nick MuellerNick Mueller Posts: 815
    edited 2009-07-01 22:37
    > If I ever want to get my feet wet in ASM then I will need to learn to work with out structures and such.

    It isn't that hard. It is an ever repeating pattern: Pass a pointer to a controll-block.
    In the ASM-code, I first write the sequence and size of the parameters as a comment. Read the parameters with rdlong/rdword/rdbyte, store them in local vars and then you go ..

    In the meantime, I like that pattern a lot. I'd even use it when only passing one parameter.

    Nick

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Never use force, just go for a bigger hammer!

    The DIY Digital-Readout for mills, lathes etc.:
    YADRO
  • ThePenguinMasterThePenguinMaster Posts: 89
    edited 2009-07-02 02:19
    OK so this is what i have so far..
    CON
            _clkmode        = xtal1 + pll16x
            _xinfreq        = 5_000_000
            tvPin           = 24
            cardPin         = 20
    obj
       term: "tv_text"
       fStore: "fsrw"
         
    var
       byte tbuf[noparse][[/noparse]20]
       long fileList[noparse][[/noparse]20]
       
    pub start | r, sta, bytes, i
       term.start(tvPin)       
       GetFileList
    
          i:=0
       repeat while not fileList[i] == 0 
          term.str(fileList[i])
          term.out($0D) 
          i++ 
    
    pub GetFileList|i
    
       fStore.mount(cardPin)    
       fStore.opendir
       i:=0
       repeat while 0 ==  fStore.nextfile(@tbuf)
           fileList[i] := @tbuf
           i++ 
    [/i][/i][/i]
    



    what i am trying to do is make a function that will grab a file list and place it into an array, this array needs to be accessible from any cog, so i have that part down, but the storing of a register location into a list is what is not working now. @tbuf is a pointer to a location in mem that holds the file name, so what happens is that when i print the elements of the array, i get the last value that was stored in tbuf as many times as files are on my SD card. I need to store the value into the array and not the pointer. This is simple im sure, but once i get these concepts i will be in the clear.
  • matbmatb Posts: 39
    edited 2009-07-02 14:24
    You had the assignment into the array almost right the first time.
    fileList := @tbuf
    Should be
    LONG[noparse][[/noparse] fileList ][noparse][[/noparse] i ] := @tbuf


    Secondly, you are over-writing the string in tbuf each time. You need more than one buffer, or to manage it some other way.

    Thirdly, which is not a bug, the pointers need not be longs, a word is a big enough.
  • ThePenguinMasterThePenguinMaster Posts: 89
    edited 2009-07-02 22:04
    How can I move the data contained in @tbuf to another buffer that i can keep from being overwritten? i think I'm going to open this in a new post because I'm starting to leave the original topic. I will try your suggestions.
Sign In or Register to comment.