Shop OBEX P1 Docs P2 Docs Learn Events
Multitasking musings - Page 2 — Parallax Forums

Multitasking musings

2»

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2015-04-07 15:51
    The RESULT variable is located at DBASE. The stack frame consist of the 4 words (2 longs) just before the RESULT variable. You do not need to initialize it if your program doesn't do a return from the first method. However it is needed if your do a return. The value $FFF9FFFF will cause it to return to address $FFF9 in the ROM, which performs a cogstop(cogid).
  • tonyp12tonyp12 Posts: 1,951
    edited 2015-04-07 16:28
    Looks too simple, why would starting a cog that points to a function invoke a new copy of the Spin interpreter?
    I guess Spin knows that is what you want, but I would had guess you would have to so something extra first.
    PUB Main                                   'starts in cog 0
      cognew(flash1,@stack1)            'start next cog which s/be #1
      
    PRI flash1
      dira[1] := 1
      repeat
    
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-07 16:30
    Is this correct???

    DBASE points to the STACK (if you specify one), else to the "end of the variables, etc" for the "boot" case.
    Now, the first long is actually reserved for the RESULT, and we should set it to FFF9FFFF just in case???
    The next long (DBASE+4) is the first long on the stack, and should also be set to FFF9FFFF.


    DBASE points to the STACK.
    The first long on the stack is actually reserved for the RESULT. Should we set it to 0 or FFF9FFFF ???
    So on spin program start DCURR = DBASE + 4.

    There are 2 longs of FFF9FFFF following the VAR, and before the STACK ???
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2015-04-07 17:58
    tonyp12 said
    Looks too simple, why would starting a cog that points to a function invoke a new copy of the Spin interpreter?

    A good question.... which has sent me back to the instruction manual (yes, I know, RTFM!)

    There is a really good description here https://lamestation.atlassian.net/wiki/display/SPIN/COGINIT

    The subtle part is at the beginning - coginint, then some parameters, then a line, then coginit and some parameters. I read that the first time and didn't get it, but reading it again, it becomes clearer - coginit is one instruction but it can be used in two completely different ways.

    I've only ever coded it the second way - using it to launch some pasm code. But read that first method again " COGINIT (CogID, SpinMethod < (ParameterList) >, StackPointer )"

    This is loading and launching the spin interpreter, and then running a particular PUB or PRI.

    Ok, maybe that will do the trick. Maybe it need not be so complicated after all.

    I guess it could get complicated if that PUB then called another PUB which was shared between two cogs. But maybe not - each would be running in its own stack space and each would be creating its own local variables so they would stay separate.

    I need to think about this some more. First thought - declare some stack space and keep them all separate. Not sure how much stack space is needed - I guess it is one long for each subroutine call so those won't be much as most code is not that nested, but the local variables are presumably going to need stack space, so tally them up and should give a ballpark figure.

    But I guess there is still the issue being discussed above about precompiled code that is brought in from say an SD card and loaded into a particular location in memory. Can that location change from the location where it was first compiled? From what dave is saying, yes it can, if a few parameters are adjusted.

    So to keep things simple, I compile my led flasher program and it produces a few bytes of code, and I store that on an SD card. I then read it back to, say, location 5000H in hub ram, and I tell it to use a stack at, say, location 6000H. Then I do a coginit and pass the stack location and the cog number and the parameterlist and the location of the code.

    Why go to this bother? Well the idea I have is that you have SD driver code which currently takes half the hub ram or more. Move that driver code into external ram. Have a routine to transfer between hub and external ram and this is just a few pasm instructions. Backup the current spin program to external ram and pause it, move the SD card code into hub, grab blocks of data and move to external ram, restore the original program, and maybe it is then possible to have SD code that effectively takes no memory. That frees up lots more space for the actual program you might write.

    I think I can see how the bits might start to fit together.
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-07 19:17
    tonyp12 wrote: »
    Looks too simple, why would starting a cog that points to a function invoke a new copy of the Spin interpreter?
    I guess Spin knows that is what you want, but I would had guess you would have to so something extra first.
    PUB Main                                   'starts in cog 0
      cognew(flash1,@stack1)            'start next cog which s/be #1
      
    PRI flash1
      dira[1] := 1
      repeat
    
    Because the spin interpreter is a PASM program that needs to be run in the cog (and it consumes the whole cog). So, coginit/cognew copies the spin interpreter from $F004 (hub ROM) to the cog RAM and begins execution of the spin interpreter (a pasm program).
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-07 19:32
    Dr_Acula wrote: »
    .....
    I guess it could get complicated if that PUB then called another PUB which was shared between two cogs. But maybe not - each would be running in its own stack space and each would be creating its own local variables so they would stay separate.
    As long as they are compiled together and loaded together (relocated), then no problems.

    I need to think about this some more. First thought - declare some stack space and keep them all separate. Not sure how much stack space is needed - I guess it is one long for each subroutine call so those won't be much as most code is not that nested, but the local variables are presumably going to need stack space, so tally them up and should give a ballpark figure.

    But I guess there is still the issue being discussed above about precompiled code that is brought in from say an SD card and loaded into a particular location in memory. Can that location change from the location where it was first compiled? From what dave is saying, yes it can, if a few parameters are adjusted.
    Bring it in from SD or RAM or FLASH, etc.

    The main program just needs to reserve sufficient space in hub for the relocatable objects to reside, including their variables and stack space.

    If you want to share variables between the main program and the relocatable object modules, then you will need to have a CON section (use an include style object) that equates to where the variables live in the main program.

    So to keep things simple, I compile my led flasher program and it produces a few bytes of code, and I store that on an SD card. I then read it back to, say, location 5000H in hub ram, and I tell it to use a stack at, say, location 6000H. Then I do a coginit and pass the stack location and the cog number and the parameterlist and the location of the code.
    No. Everything is correct up to telling it where the stack is. The relocatable spin loader (part of the main program) will put the stack at the end of the variables which come after the object. The variables and stack do not get loaded. The variables just get cleared in hub after the object is loaded, and the top of stack gets initialised with fff9ffff fff9ffff.

    Why go to this bother? Well the idea I have is that you have SD driver code which currently takes half the hub ram or more. Move that driver code into external ram. Have a routine to transfer between hub and external ram and this is just a few pasm instructions. Backup the current spin program to external ram and pause it, move the SD card code into hub, grab blocks of data and move to external ram, restore the original program, and maybe it is then possible to have SD code that effectively takes no memory. That frees up lots more space for the actual program you might write.
    It is going to take a little more work to be able to "pause" the cog and "unload the hub space - code/variables/stack", load in a new "code/variables/stack", "re-commence" the cog, etc.
    But I can see that it is doable.
    There would be no need to save out the "code" section to external memory (RAM/SD/etc) as long as it is there to be reloaded.

    I think I can see how the bits might start to fit together.
    Me too :)
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-04-07 19:44
    Cluso99 wrote: »
    Is this correct???

    DBASE points to the STACK (if you specify one), else to the "end of the variables, etc" for the "boot" case.
    Now, the first long is actually reserved for the RESULT, and we should set it to FFF9FFFF just in case???
    The next long (DBASE+4) is the first long on the stack, and should also be set to FFF9FFFF.


    DBASE points to the STACK.
    The first long on the stack is actually reserved for the RESULT. Should we set it to 0 or FFF9FFFF ???
    So on spin program start DCURR = DBASE + 4.

    There are 2 longs of FFF9FFFF following the VAR, and before the STACK ???
    DBASE points to the RESULT variable, and not the beginning of the stack. The stack actually begins 8 bytes before DBASE. The first 8 bytes contain the initial stack frame, which is used to jump to $FFF9 if the first method returns.

    DCURR will equal DBASE + 4 only if the starting method has no parameters, and has no local stack variables. In general, DCURR = DBASE + 4 + N*4 + M*4, where N is the number of calling parameters and M is the number of local stack variables.

    Normally the VAR data space immediately follows the code in memory, which is normally followed by the stack. However, this isn't strictly required. The values of VBASE, DBASE and DCURR can be modified to point to any block of memory in hub RAM.
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-07 19:56
    Dave Hein wrote: »
    DBASE points to the RESULT variable, and not the beginning of the stack. The stack actually begins 8 bytes before DBASE. The first 8 bytes contain the initial stack frame, which is used to jump to $FFF9 if the first method returns.

    DCURR will equal DBASE + 4 only if the starting method has no parameters, and has no local stack variables. In general, DCURR = DBASE + 4 + N*4 + M*4, where N is the number of calling parameters and M is the number of local stack variables.

    Normally the VAR data space immediately follows the code in memory, which is normally followed by the stack. However, this isn't strictly required. The values of VBASE, DBASE and DCURR can be modified to point to any block of memory in hub RAM.
    Got it. My sample listing was not complete enough to show all possibilities.

    There doesn't seem to be any way to determine the exact length of VAR if the stack does not follow the variables ???
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-07 20:04
    Drac,
    Been thinking (yes, dangerous I know ;) )...

    We could now separate the FAT file handling into spin object(s) from the raw SD sector access. This would reduce the FAT driver footprint, especially if we broke it up into smaller objects.

    BTW Here is a listing of a single object "spin_flash1"
    0000: 00 1b b7 00 ' Frequency: 12000000 Hz
    0004: 00          ' XTAL mode
    0005: 6b          ' Checksum
    0006: 10 00       ' Base of program
    0008: 2c 00       ' Base of variables
    000a: 74 00       ' Base of stack
    000c: 18 00       ' Initial program counter
    000e: 78 00       ' Initial stack pointer
    
    '******************************************************************************
    '                              spin_flash1.spin                                
    '******************************************************************************
    
    '=================================== CONs =====================================
    _PIN = 1
    '=============================== Object Header ================================
    0010: 1c 00 02 00 ' 28 bytes, 2-1 methods, 0 object pointers
    0014: 08 00 00 00 ' ptr #1 to $0018: PUB start (locals size: 0)
    '=========================== Method #1: PUB start =============================
    'PUB start
    '------------------------------------------------------------------------------
      dira[_PIN]~~                  ' make output
    '------------------------------------------------------------------------------
    0018: 36             PUSH#1	
    0019: 3d d6 1c       REGUSING	DIRA<>, POSTSET
    '------------------------------------------------------------------------------
      repeat
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
        !outa[_PIN]                 ' invert
    '------------------------------------------------------------------------------
    001c: 36             PUSH#1	
    001d: 3d d4 47       REGUSING	OUTA<>, BIT_NOT
    '------------------------------------------------------------------------------
        waitcnt(clkfreq + cnt)      ' delay 1s
    '------------------------------------------------------------------------------
    0020: 35             PUSH#0	
    0021: c0             PUSH.L	Mem[]
    0022: 3f 91          REGPUSH	CNT
    0024: ec             ADD  	
    0025: 23             WAITCNT	
    0026: 04 74          GOTO 	.-12 (dest:$001c)
    0028: 32             RETURN	
    0029: 00 00 00    
    '================================ VAR Section =================================
    002c: 00 00 00 00 00 00 00 00 ' LONG vars(16)
    0034: 00 00 00 00 00 00 00 00 ' 
    003c: 00 00 00 00 00 00 00 00 ' 
    0044: 00 00 00 00 00 00 00 00 ' 
    004c: 00 00 00 00 00 00 00 00 ' 
    0054: 00 00 00 00 00 00 00 00 ' 
    005c: 00 00 00 00 00 00 00 00 ' 
    0064: 00 00 00 00 00 00 00 00 ' 
    006c: ff ff f9 ff ff ff f9 ff 
    
    Note in the binary file, the VAR Section onwards is not included, and has to be fabricated by the spin object loader (see Dave's example a few posts ago).
  • David BetzDavid Betz Posts: 14,516
    edited 2015-04-08 18:42
    Dr_Acula wrote: »
    Thanks cluso for doing these experiments. I think the propeller has a few tricks yet that we haven't tried out. I see there is a long running language thread of C vs Basic running on the forum - how crazy/nifty/impossible would it to be to neatly sidestep the argument by running multiple languages on the propeller... all at the same time? Allocate separate hub memory for each one, a cog per language, and set them all going.
    Back in the olden days, compilers had the .org instruction - compile starting at this memory location. Is there an equivalent in spin?
    You can sort of do this already with PropGCC and Spin. I wrote a program a while back called spinwrap that lets you call Spin methods from within a C program. Actually, it works best with C++ since Spin objects can be mapped to C++ objects so calling methods looks natural. The spinwrap code is in a repository in the Parallax github account if you're interested in looking at it.

    https://github.com/parallaxinc/spinwrap
  • jmgjmg Posts: 15,173
    edited 2015-04-09 15:21
    Cluso99 wrote: »

    Bring it in from SD or RAM or FLASH, etc.

    The main program just needs to reserve sufficient space in hub for the relocatable objects to reside, including their variables and stack space.

    I see Cypress 1MBit FRAM FM24V10 supports 2-wire and 3.4MHz speeds, so while it is not especially cheap, it could be a good device to prove larger scale swap behaviour. FRAM can also preserve variables, not just code.
Sign In or Register to comment.