Tuesday, February 14, 2012

Propeller Lessons Learned

The last few Propeller programming days have be frustrating, I could not figure out how to properly pass parameters by reference. Several debugging tactics only compounded the problem and lead to even more frustrations.  After several hour, and talking with Jeff - KO7M, we were able to sort everything out.

The lessons learned are documented here to maybe help other Propeller users:

Lessons Learned

0. Debugging of other COG methods can not used LCD, TERM or TV output, those types of Objects are not multi-COG aware. Use global variable to provide debug information back to the main program where the LCD, TERM or TV Object initialized those objects and "print" can be used.

1. Parameters passed by reference can be used with any method and the cognew statement

2. The calling statement should appear as:
  • some_method ( @some_variable )
3. All use of the reference parameter within sub methods should appear as follow:
  • long [ pArg ]  :=  some_value
  • or
  • set_some_variable  :=  long [ pArg ]
4. A passed by reference of an Array looks the same in the calling statement, as:
  • some_method ( @some_array )
5. Use of the reference array within a sub method should appear as follows:
  • long [ pArg ] [ some_array_index ]  :=  some_value
  • some_variable  :=  long [ pArg ] [ some_array_index ]
  • but
  • long [ pArg [ some_array_index ] ]  := some_value  -  IS WRONG
6. The newly created COG should be given enough Stack space, if in doubt, make it larger, strange or bad things happen if the stack is too small.

Conclusion

These ideas appear simple now, but they were learned (very much) the hard way, where the manual and simple examples were not much help.

This following is a contrived test program to simulate a long running background task, where control and progress can be monitored from the calling method.


CON
    WMin = 381

    _CLKMODE = XTAL1 + PLL16X
    _XINFREQ = 5_000_000

    CLK_FREQ = ((_clkmode-xtal1)>>6)*_xinfreq
    MS_001 = CLK_FREQ / 1_000

    ' INFO for LCD
    #16, LCD_RS, LCD_RW, LCD_E, LCD_BL
    #12, LCD_DB4, LCD_DB5, LCD_DB6, LCD_DB7
    #16, COLS
    #2,  ROWS

    ' Arg Array Element Names, iSIZE must be last
    #0, iCTL, iSTAT, iPROGRESS, iSIZE

    ' Process Control States
    #0, cRUN, cABORT, cREPORT
           
    ' Returned Status States
    #0, sNOTSTARTED, sINIT, sRUNNING, sFINISHED, sABORTED  

OBJ

    LCD   : "jm_lcd4_ez"   ' Rev 1.4

VAR

PUB Demo |  subCogId, okay, Args[iSIZE], Stack[32] ' Args and Stack could be Globals
      Args[iCTL] := cRUN
      Args[iSTAT] := sNOTSTARTED
      Args[iPROGRESS] := 99


      LCD.Startx(LCD_BL, LCD_E, LCD_RW, LCD_RS, LCD_DB4, COLS, ROWS)

      LCD.cmd(LCD#CLS)   ' Clear Screen
      LCD.blon
      LCD.str(string("Init:"))
      LCD.moveto(1,2)
      LCD.str(string("Stat: "))
      LCD.moveto(8,2)
      LCD.str(string(",Prg:"))
      pauseSec(2)

      Args[iSTAT] := sINIT ' Assume RUNNING State

      okay := subCogID := cognew(mySUB2(@Args), @Stack)

      LCD.dec(okay)


      ' Report the New COG's Progress
      repeat while Args[iSTAT] < sFINISHED

        LCD.moveto(6,2)
        LCD.rjdec(Args[iSTAT], 2, " ")

        LCD.moveto(13,2)
        LCD.rjdec(Args[iPROGRESS], 3, " ")
        LCD.out("%")

        if Args[iPROGRESS] =< 75  ' To simulate an ABORT required condition
          Args[iCTL] := cABORT

        pause(200)


      ' Report Final Results
      LCD.moveto(6,2)
      LCD.rjdec(Args[iSTAT], 2, " ")

      LCD.moveto(13,2)
      LCD.rjdec(Args[iPROGRESS], 3, " ")

      pauseSec(1)

      LCD.moveto(1,1)
      LCD.str(string("Finished: "))

      repeat             ' Keep alive
        pauseSec(1)


PUB mySUB2(pArgs)        ' A simple cog, that calls yet another Method

    mySUB1(pArgs)


PUB mySUB1(pArgs)

    pauseSec(2)          ' Simulate Initization time

    long[pArgs][iSTAT] := sRUNNING

    repeat while long[pArgs][iPROGRESS] > 0
      if long[pArgs][iCTL] == cABORT
         long[pArgs][iSTAT] := sABORTED
         QUIT
      long[pArgs][iPROGRESS]--

      pause(200)  ' This is the dummy program process activity

    long[pArgs][iSTAT] := sFINISHED


DAT
PRI pauseSec(sec)
      pause(1000 * sec)

PUB pause(ms)
      waitcnt(((clkfreq / 1_000 * ms - 3932) #> WMin) + cnt)

DAT


This program does not represent good Propeller programming practices, it was contrived just to understand parameter passing by reference. A typical or more correct strategy would use methods to control and report status of the sub COG or other methods.

Thanks Jeff, for the help

It is all fun with the Prop.


UPDATE
Lesson Learned Number "6."; has bitten me twice since this was first posted: Give the COG More Stack Space !  For now I am starting with 64 Longs, and will trim only if necessary. Previously I was using 16 or maybe 32 Longs for "cognew" stacks.

--

No comments: