Shop OBEX P1 Docs P2 Docs Learn Events
Variable Magic, episode 9 — Parallax Forums

Variable Magic, episode 9

K2K2 Posts: 693
edited 2010-10-16 09:19 in Propeller 1
On page 8 of the one-and-only AppNote on the Propeller, in the SPIN portion of the code, there is a line like this:
    repeat x from 0 to period

Later, near the bottom of the DAT section, there is this line:
period   long 100
How does the SPIN interpreter running in one cog have direct access to the RAM of a second cog?

Comments

  • JonnyMacJonnyMac Posts: 9,235
    edited 2010-10-14 15:04
    It all starts out in the hub ram, so the Spin cog can access that version of it.
  • K2K2 Posts: 693
    edited 2010-10-14 15:51
    So SPIN gets the initialized value but not the current value? Normally I would rant about such a crazy scheme, but I'm all ranted out. Now I'm just content to know the answer. Thank you for it, JonnyMac.

    Years of conventional programming has succeeded in programming me.
  • CastIronyCastIrony Posts: 2
    edited 2010-10-14 16:07
    If a cog is executing SPIN code, that cog's RAM is already filled with the SPIN interpreter and cannot store any variables.

    All SPIN variables sit in Hub RAM and are shared among all cogs.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-14 16:16
    K2 wrote:
    So SPIN gets the initialized value but not the current value?
    It gets the current hub value. If the value is changed within another cog's local memory, that cog would have to write the new value back to the hub for other cogs to have access to it. There is no direct access from one cog to another cog's local memory. All such communication has to go through either hub RAM or the via external pins.

    -Phil
  • K2K2 Posts: 693
    edited 2010-10-14 18:46
    It gets the current hub value.
    -Phil

    That's exactly my point about this being a crazy scheme. Where else in all of computerdom does a single variable become two variables in the same listing without the slightest name change? For experienced proppers, this likely seems normal. For me, the quintessential prop outsider, I look at such a piece of code and shake my head.

    If I end up using this sample code from AN001 in my application, changing that 'feature' will be the very first thing I do. For my own sanity if nothing else, I would NEVER write a piece of code with two variables parading as one. It's a recipe for debugging hell.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-14 19:02
    K2 wrote:
    For experienced proppers, this likely seems normal. For me, the quintessential prop outsider, I look at such a piece of code and shake my head.
    It' is perfectly normal and completely logical. There's nothing preventing you from becoming a "Prop insider"! It's just a frame of mind; nothing more is required or expected. :)

    -Phil
  • kwinnkwinn Posts: 8,697
    edited 2010-10-14 19:40
    K2, it really is quite logical in the "propeller way". If a "cognew" instruction is executed the instructions and data in the "dat" section get loaded in a new cog and the PASM program in that cog will access the "period" memory location in that cog. The spin program accesses the "period" location in hub memory if it continues to run. Think of them as separate instances of the same variable. With the prop you could have as many as 8 instances of the same variable.

    Hmm. Is it possible to get 8 instances of the same PASM program running in all 8 cogs?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-14 19:46
    kwinn wrote:
    Hmm. Is it possible to get 8 instances of the same PASM program running in all 8 cogs?
    Sure. The initial Spin cog starts the first one and exits. Each PASM cog starts the next one until it can't anymore.

    -Phil
  • kwinnkwinn Posts: 8,697
    edited 2010-10-14 20:21
    For the sake of argument lets say the spin interpeter starts in cog 0 and it executes the following. In the end all cogs are running the same pasm program. Correct?

    cognew (...) ' start program in next cog (cog 1)
    cognew (...) ' start program in next cog (cog 2)
    cognew (...) ' start program in next cog (cog 3)
    cognew (...) ' start program in next cog (cog 4)
    cognew (...) ' start program in next cog (cog 5)
    cognew (...) ' start program in next cog (cog 6)
    cognew (...) ' start program in next cog (cog 7)
    cognew (...) ' starts program in cog 0, overwriting the spin interpeter
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-14 20:25
    The last cognew(...) should be a coginit(cogid, ...). Otherwise it just fails (cog 0 is still in use).
  • kwinnkwinn Posts: 8,697
    edited 2010-10-14 20:41
    Thanks kuroneko. Good to know.
  • Heater.Heater. Posts: 21,230
    edited 2010-10-14 23:46
    K2:
    That's exactly my point about this being a crazy scheme. Where else in all of computerdom does a single variable become two variables in the same listing without the slightest name change?

    Crazy, no. A little confusing, perhaps. A variable has been able to become two or more variables, without a name change, ever since the beginning of time. Well, the invention of UNIX anyway.

    UNIX creates processes with the fork() function. Basically if your code executes fork() an entire second copy of your program starts running as a separate process in a separate memory space. That includes the duplication of all variables. From fork() onwards the duplicate variables, with the same names, are updated by each process totally independently.

    Here is a simple example:
    /* Orion Lawlor's Short UNIX Examples, olawlor@acm.org 2004/9/5
    Shows how to use fork() in a UNIX program.
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>           /* for pid_t */
    #include <sys/wait.h>            /* for wait */
    
    
    
    int a=1;
    int b=1;
    
    void *doWork(const char *where, int loops)
    {
        int i;
        printf("Entered work function (%s)\n",where);
        fflush(stdout);
        for (i=0; i<loops; i++)
        {
            b=a+b;
        }
        printf("Leaving work function (%s)\n",where);
        return (void *)b;
    }
    
    
    int main()
    {
        /*Spawn a new process to run alongside us.*/
        pid_t pid=fork();
        if (pid==0)                  /* child process */
        {
            doWork("child", 1000000);
            printf ("Child: a=%d, b=%d\n", a, b);
            exit(0);
        }
        else                         /* pid!=0; parent process */
        {
            doWork("parent", 1000);
            printf ("Parent: a=%d, b=%d\n", a, b);
            waitpid(pid,0,0);        /* wait for child to exit */
        }
        return 0;
    }
    
    
    /*
    ******** Program output: ********
    Entered work function (child)
    Leaving work function (child)
    Child: a=1, b=1000001
    Entered work function (parent)
    Leaving work function (parent)
    Parent: a=1, b=1001
    */
    

    If you run this you will see the variable "b" is either 1000001 or 1001 depending on which copy we are looking at.

    As for the Prop, starting a new PASM COG does a similar thing. All of a sudden all that stuff in a DAT section is now duplicated into a separate memory space, in the COG in this case.

    If you look a little at the Prop architecture and what COGINIT COGNEW do then there is no surprise in any of this variable duplication. It's a natural, logical, consequence of the architecture.

    P.S. By the way all local variables to functions, procedures, methods in many different languages become new copies of themselves with different values every time the function is called. So the same variable name is a different thing on each invocation.
  • Heater.Heater. Posts: 21,230
    edited 2010-10-14 23:54
    K2,
    If I end up using this sample code from AN001 in my application, changing that 'feature' will be the very first thing I do. For my own sanity if nothing else, I would NEVER write a piece of code with two variables parading as one.

    Actually I'm inclined to agree with you. But perhaps for different reasons.

    If a PASM code gets all of it's knowledge of the outside world through PAR parameters. And then communicates with the world via a memory area, "mailbox", whose address was given in a PAR block. Then that PASM code can be used by languages other than Spin. For example Prop BASIC, or C with Catalina or Zog.

    If that PASM block has named variables in DAT being set up by Spin code prior to starting it then it is harder to use from non-spin languages as they don't have the "linkage" with it's name in Spin.

    I wish all objects, in OBEX say, were written with out the need for this Spin linkage. Sadly they are not and using them with Zog is a pain or other system is a pain.
  • K2K2 Posts: 693
    edited 2010-10-15 07:21
    Heater and kwinn, with their logical arguments, have succeeded in convincing me it's not such a crazy thing. To quote Monty Python, it's "inherent in the system." I just have to look for it, provide for it, and not get messed up by it.

    Everytime I get something useful built with another processor, my next question is how to do the same thing with the Prop. It has been a fascinating, and sometimes frustrating, endeavor. The Prop has such unique solutions, and yet there always seems to be a way to get from here to there. Meanwhile, the counter hardware is no small portion of the power of the Prop, and there are eight complete copies of it! That's pretty impressive.
  • kwinnkwinn Posts: 8,697
    edited 2010-10-15 10:26
    Heater. wrote: »

    If a PASM code gets all of it's knowledge of the outside world through PAR parameters. And then communicates with the world via a memory area, "mailbox", whose address was given in a PAR block. Then that PASM code can be used by languages other than Spin. For example Prop BASIC, or C with Catalina or Zog.

    If that PASM block has named variables in DAT being set up by Spin code prior to starting it then it is harder to use from non-spin languages as they don't have the "linkage" with it's name in Spin.

    I wish all objects, in OBEX say, were written with out the need for this Spin linkage. Sadly they are not and using them with Zog is a pain or other system is a pain.

    This is a good point. Are there guidelines on how to write pasm programs that are easy to use with other languages and do those languages have a consistent interface or is each one unique?
  • Heater.Heater. Posts: 21,230
    edited 2010-10-15 11:15
    It's a good point that Bill Henning, myself and others have been discussing on and off for a long time. So far with out any consensus arising.

    As such there are no guidelines other than the general idea that I outlined above. So let's put it like this, a PASM object that is to be easily usable with any language and/or operating system should:

    1) Take all of it's required initial configuration parameters via data passed to it via a PAR pointer.

    2) Should interact with software external to it only via a shared memory area.

    3) The address of that shared memory should be passed to the PASM object as one (or more) parameters passed in via PAR at start up (COGNEW, COGINIT)

    It should not:

    1) Rely on some Spin code setting up parameters within its DAT area prior to being loaded.

    The point is that whilst we can expect any language to support COGINIT/COGNEW and PAR, in some way, we cannot expect them to be able "poke" values into DAT blocks prior to starting the PASM in a COG. Reason being that they have no "linkage" to the locations of those values.

    This has repercussions even when working in Spin. If all PASM code was free of any linkage with any Spin object then it could be compiled separately from the Spin. Then it could be included into the complete program at any point in the code via a "file" statement. Imagine, all the PASM you need living in the same known memory area, which could be reused after the COGs have started for, say, video buffer space.

    BSTC, and I believe HomeSpun, can already compile Spin objects and extract just the assembled PASM part out of them. So we are part of the way there already. I have made use of this with Zog but most objects seem to need some modification to remove that Spin "linkage". Catalina has been through all this already.




    There are perhaps other features of reusable PASM that I have missed, and I know Bill Henning has a even more strict ideas in mind for his Largos operating system.
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2010-10-15 16:00
    kwinn wrote: »
    Think of them as separate instances of the same variable. With the prop you could have as many as 8 instances of the same variable.
    It's like parallel universes, unless enacted upon, they are all identical. But if one cog changes their instance of the variable, it does not affect the other instances.

    But you really have to think of the propeller as 8 completely separate microcontrollers that can talk and share things. You really just have to get used to all the nuances that the propeller has in it's parallel processing capabilities. In this example, all SPIN cogs see the DAT value as as single value, but PASM would view it separately in each individual cog.

    So maybe, I should change what I said a bit. In PASM it is 8 completely separate parallel microcontrollers, but in SPIN they are 8 linked semi-parallel microcontrollers. But the main difference is only how the two languages reference memory and execute code.
  • kwinnkwinn Posts: 8,697
    edited 2010-10-15 16:36
    @Heater, I can see how setting up this passing of multiple parameters and data between PASM and multiple languages can be tricky.
    Not so much because it is difficult to do, but rather because there are so many ways it could be done, and most of those ways not optimum for many languages.
  • JonnyMacJonnyMac Posts: 9,235
    edited 2010-10-16 08:11
    ...we cannot expect them to be able "poke" values into DAT blocks prior to starting the PASM in a COG.

    A lot of my objects violate this suggestion. For example, I do this a lot:
    US_001 := clkfreq / 1_000_000
    

    ...where US_001 is a value for the cog I'm about to launch. Do you suggest I load that value into a hub variable and then transfer that via the PAR linkage? While it seems redundant, I do see the value in allowing the object to run under other languages.
  • kwinnkwinn Posts: 8,697
    edited 2010-10-16 08:44
    This is a lot more complex than a first glance suggests. Another can of worms opened!

    1 - Passing initial parameters to pasm programs.
    2 - Passing data and parameter changes to pasm programs.
    3 - Passing data from pasm programs to "whatever language" program in hub.
    4 - Where to put parameters in hub memory (top, bottom, scattered in program, etc.)

    There are repercussions for these decisions to both pasm programs and the language compiler.
  • Heater.Heater. Posts: 21,230
    edited 2010-10-16 09:19
    JonnyMac,
    Do you suggest I load that value into a hub variable and then transfer that via the PAR linkage? While it seems redundant, I do see the value in allowing the object to run under other languages.
    That's exactly the suggestion. The point is to assume that the PASM/DAT a "black box", a binary blob that you load into a COG, that you don't know the location of any such variables/parameters with it.

    This seems like it wastes LONGS in the PASM to get those parameters from PAR at start up. But I have found that such intitialization/setup code space can be reused as variable space when the actual guts of the PASM is running. Zog makes good use of this recycling idea.


    kwin:
    1 - Passing initial parameters to pasm programs.
    Easy enough, just pass params in via a PAR block as is commonly done anyway.
    2 - Passing data and parameter changes to pasm programs.
    Data is easy. Just do it through the shared memory area the address of which was passed in through the PAR block. This is commonly done already as far as I can tell.

    Parameter changes is a bit more trick. Normally PASM does not support this (does it) for example to change the baud rate in FullDulexSerial you have to stop it and start it agin with a new baud rate.

    If parameter changes on the fly are really needed they can be done through a COMMAND interface in the shared memory area.
    3 - Passing data from pasm programs to "whatever language" program in hub.
    Just do it through the shared memory area as normal. For example in Zog the C program knows where the FullDuplexSerial buffers are in memory and their head and tail pointers so it can just add and remove bytes in the same way the FDX Spin code does. The PASM need not know or care what language "out there" is doing that.
    4 - Where to put parameters in hub memory (top, bottom, scattered in program, etc.)
    In normal Spin/PASM objects so far, the shared memory area is defined in the Spin as a VAR or DAT area and is therfore at some random location in HUB. Better that the "start" method of such objects is give the shared memory area location as parameter by the client object. The start method then passes that on to the PASM through PAR.

    Bill Hennings VMCOG shows a good example of doing this.

    This way the client code, in what ever language, can decide for itself where the shared memory area is.

    P.S. There should probably be a "zeroth rule" for all of this:

    0) The PASM code will not read/write any HUB locations, except those whos addresses are passed in through PAR.
Sign In or Register to comment.