Shop OBEX P1 Docs P2 Docs Learn Events
Spin Refresher: read and store variable values between methods and procedures? — Parallax Forums

Spin Refresher: read and store variable values between methods and procedures?

courtenscourtens Posts: 101
edited 2021-09-23 14:02 in Propeller 1

This code illustrates what I am trying to do in a rudimentary simplistic way; but I forgot how to properly address variables. I intentionally left out variable declarations as to not confuse things ... global, and local; as I forgot how to syntax these.

CON
  _clkmode      = xtal1 + pll16x
  _xinfreq      = 5_000_000 
  High = 1
  Low  = 0
  P2 = 2

VAR
  Long stack_0[20]  'Sets up a stack space for a Cog(processor)
  Long start_temp 
  Long start_A             
  Long start_B     
  Long end_temp                
  Long end_A             
  Long end_B 

PUB Main
  CogNew(TimerCog, @stack_0)    'Starts a New Cog  
  repeat
    ' do other stuff

PUB TimerCog | stA, stB, stTemp, enA, enB, enTemp, duration
  set_pin_to_input(P2)
  repeat 
    phsa := 0
    repeat while phsa < 5_000_000
        LogSignal
    stA :=  start_A   ' do something with values stored in start_A, start_B ...
        enA :=  end_A
        duration = enA - stA
    ResetLogs

PRI LogSignal
    if ina[P2] == Low and start_temp == 0   ' just dropped low
        start_temp := phsa
        if start_A == 0
            start_A := start_temp
        elseif start_B == 0                     
            start_B := start_temp   
    if ina[P2] == High and start_temp <> 0  ' just went high
        end_temp := end := phsa
        start_temp := 0     ' reset
        if end_A == 0
            end_A := end_temp
        elseif end_B == 0                       
            end_B := end_temp

PRI ResetLogs
    start_A := start_B := end_A := end_B := end_temp := start_temp := 0

PUB set_pin_to_input(pin)  'set pin to input                    
  dira[pin]~
  outa[pin]~

How do I access (read) and save (write) variable values most effectively; locally or globally; and between routines, methods, and objects ... functions?

Comments

  • Do you want to write the values to the eeprom or a SD card?

  • You did forget "Long end" in your VAR

  • courtenscourtens Posts: 101
    edited 2021-09-23 04:36

    @DigitalBob said:
    Do you want to write the values to the eeprom or a SD card?

    I would like to use the values to do some math in PUB Main, or any other procedure.

    @DigitalBob said:
    You did forget "Long end" in your VAR

    Nice catch!

    I do think I just found the section in the PDF manual for that, under "Parameters and Local Variables" page 184. This the section:

    If a variable must be altered by a routine, the caller must pass the variable by reference; meaning it must pass the address of the variable instead of the value of the variable, and the
    routine must treat that parameter as the address of a memory location in which to operate on. The address of a variable, or other register-based symbol, can be retrieved by using the
    Symbol Address operator, ‘@’. For example,
    Pos := 250 MoveMotor(@Pos, 100)
    The caller passed the address of Pos for the first parameter to MoveMotor. What MoveMotor receives in its Position parameter is the address of the caller’s Pos variable. The address is
    just a number, like any other, so the MoveMotor method must be designed to treat it as an address, rather than a value. The MoveMotor method then must use something like:
    PosIndex := LONG[Position]
    ...to retrieve the value of the caller’s Pos variable, and something like:
    LONG[Position] := <some expression>
    ...to modify the caller’s Pos variable, if necessary.

    So it sounds like I could store variable values as a local variable, or a global variable. What is faster? Is the global using the hub to read and write values to the address? Or can the cog running that routine do it directly without waiting for it's turn?

  • msrobotsmsrobots Posts: 3,709
    edited 2021-09-23 08:34

    I think you have a major miss understanding here.

    Inside a procedure (PRI or PUB does not change anything) local Variables and Parameters are just saved on the stack, so are just valid as long you are inside the procedure.

    If you define a variable in a DAT or VAR section it is per definition global, so accessible from any COG and any Procedure.

    If you call a Procedure with parameters, they are copied onto the stack, any modification will not get pushed back (called by Value not by reference)

    If you just access a global variable directly (not using as parameter) you modify the variable in HUB.

    The Spin Stack is also in HUB so there is NO timing difference between local or global access. All of them are in HUB memory.

    Avoiding the HUB access latency just works in PASM, there you can access the COG memory and avoid the HUB latency.

    You CAN give the address of a variable as parameter to a procedure, but you very seldom need to, basically just if you have some PASM running in a COG, if working with Spin Cogs it is not needed at all.

    start_B := 123

    is the same as

    LONG[@start_B] :=123

    both modify the same location in HUB memory.

    Since Spin is a Interpreter, every instruction needs a (lot of) couple of instructions so the 12 clock latency between HUB access of one COG does not really matter.

    I hope this will help your understanding.

    Mike

  • @msrobots said:
    I think you have a major miss understanding here.

    I was just rewording my first post when I saw your post pop in. Mike, thank you for your most helpful input!

    This was the key:

    The Spin Stack is also in HUB so there is NO timing difference between local or global access. All of them are in HUB memory.

    I have been using local variables heavily (thinking apparently wrongly) that that would somehow avoid the HUB latency access timing issue.

    So instead, I should better declare, read and write ALL variables globally. I think you just made my day!

  • courtenscourtens Posts: 101
    edited 2021-09-30 13:39

    @msrobots said:
    Avoiding the HUB access latency just works in PASM, there you can access the COG memory and avoid the HUB latency.

    Mike, is this really true? I am getting some terrible latency issues.

    For instance, if I access a bunch of global variables held in the HUB, such as in this line of code using a different cog, it looks like I am bumping in some variable access timing issues. Note: all variables in this line of code are global variables:

    if laser_state == 1 and hole_start_temp == 0 and hole_end + pop_delay < phsa and hole_end > 0 and hole_duration > hole_duration_last - movement

    I think I do need to first pull all the values from the HUB and re-declare them locally to speed things up. Is this right?

  • no

    if you read a variable directly from HUB (declared in VBAR or DAT) you need one HUB access.

    if you pass that value into a procedure as parameter or move it to a local variable you have one HUB access for reading the variable another HUB access to write it into the parameter (on stack) or local variable (on stack) and then a third HUB access to finally read the local value from stack.

    it would be slower.

    One thing you need to keep in mind is that the COGs are real parallel working Cores. So if one Core reads the variable modifies it and writes it back and another Core is writing the same variable in between this change will be overwritten by the first Core and lost.

    This is not a Propeller specific problem, all multi core systems have that issue with shared resources like RAM or PINs.

    There are different ways to get around this problem, one (mostly used in Spin/Pasm) is to have some Protocol for access, say one Cog is writing the value, the other one is reading it - fine.

    Often used with Spin to Cog parameters is on Cog writes a command >0 into the 'mailbox' in HUB variable, the other does what it needs to do and clears the value to 0 so the first Cog knows the transfer was successful before overwriting with a new command.

    Another common way are Semaphores also called locks. The P1 has 8 of them. These are seldom used since they produce even more overhead and are not needed for the above methods of access.

    If your program is not to large you could try to use FlexProp from @ersmith to compile your Spin Code to Pasm and speed everything up by at least factor 4.

    Enjoy!

    Mike

  • In FlexProp local variables are kept in COG memory (if possible... if you take the address of a variable with @ then it has to go in HUB). Also, even in the "official" Spin2 compiler I think access to the first 8 locals is slightly faster than access to general global variables (because the offset is encoded in the opcode). So in general @courtens original idea of keeping frequently used data in local variables is a good one. To copy between COGs though you will need to use globals.

  • @ersmith said:
    In FlexProp local variables are kept in COG memory (if possible... if you take the address of a variable with @ then it has to go in HUB). Also, even in the "official" Spin2 compiler I think access to the first 8 locals is slightly faster than access to general global variables (because the offset is encoded in the opcode).

    The question is regarding Spin1, where the first 8 instance variables are just as fast as the first 8 locals.

Sign In or Register to comment.