Shop OBEX P1 Docs P2 Docs Learn Events
Prop Cog, Object, Method hierarchy — Parallax Forums

Prop Cog, Object, Method hierarchy

Don PomplunDon Pomplun Posts: 116
edited 2012-11-06 17:12 in Propeller 1
I've been battling strange happenings in my prop spin code for my house control computer. It uses all 8 cogs in half a dozen objects, most with plenty of PUB & PRI methods. As I look for my problems, some hierarchy questions arise. e.g., an object cognews a method located "halfway" down the code. There are PUB & PRI methods textually located both before and after. I'm wondering if the "physical" location of these has a bearing on which cog they run in, etc.
For instance, object 1 initially cognews method D. Methods A, B & C precede D in the listing, and E, F & G follow it. If something in object 2 calls F, is there a connection to D's cog? Or doesn't it make any difference?
Maybe all my PUB methods in object 1 should be located before D, which runs in its own cog(?)
TIA
Don

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2012-10-26 17:16
    Maybe you need to toss it all out and re-do it in a much simpler fashion. That may sound extreme, but it's actually very useful. You've already learned a lot in implementing what you have, but you probably kept adding pieces without necessarily paying attention to how they fit together. You're probably using way more cogs than you need and possibly more objects than you need. You may get it all to work eventually, but it will be terrible to maintain or modify.

    Start with your overall description ... what is the hardware you have? how is it connected to the Prop? what is the house computer supposed to do? how is it supposed to behave?
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-10-26 17:27
    Strange things usually happen if code A accidently changes memory that's been used by code B. One example is that stacks are not dimensioned properly. Then COG 1 could overwrite the stack of COG 2.
    Or maybe arrays are not dimensioned big enough.
    Or a more complex data-structure is updated by COG 1 and COG 2 reads the date without being synchronized to COG 1.
    ...

    From a SPIN point of view all 8 COGs are equal! Which COG is used for which method depends on which COG is the next free one when doing the COGNEW. No dependency on where the code is located in the source-file!
    Is there a connection? Well ... if F and D use the same global variables, then this is a connection. If F and D only use local variables (which are placed in the individual stack) or distinct global variables then there is no connection.

    The location of the code in the source file is not relevant.

    It would be better to post your code, then we can work on that and point you to the right direction.
  • Mike GreenMike Green Posts: 23,101
    edited 2012-10-26 17:33
    Objects are commonly used to implement abstractions. FullDuplexSerial and Simple_Serial implement essentially a conventional UART (Universal Asynchronous Receiver Transmitter). FloatMath implements a floating point processor. Servo32 implements a multiple servo controller. Some objects manage the interface to an assembly language program running in a cog and some do their work all in Spin. It depends on what they have to do.
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-10-26 18:48
    Yes, Mike, you're probably right. The system used to be one big object (except for special stuff like FullDuplexSerial). As that got awkward, good advice, probably from this forum, got me to re-do it as multiple objects. Maybe time for moderation, moderation, moderation!
    Have to weigh the time-dependent stuff for cog usage. The system spends 99.99% just incrementing the time and checking to see if anything is scheduled to be done. Keypads & motion detectors need to be serviced as events occur, but on the whole, it's almost no activity over 24 hours (86400 seconds).
  • Mike GreenMike Green Posts: 23,101
    edited 2012-10-26 21:03
    Sounds like way less than 8 separate threads (cogs). For timekeeping, you'd just use the system clock. It runs all the time. The resolution is 12.5ns at 80MHz clock speed. You can easily implement carries into another 32 bit long and you can put off handling the carry for up to about 50 seconds before you lose track of it. If your scheduled actions take less than 50 seconds to run, you can just call them and they all run in the same cog as the one doing the timekeeping. Maybe you'd just have the timekeeping cog schedule the time-dependent events and generate events from the things that need polling like the motion detectors and keypads. These events would be stored as entries in a list implemented in a couple of arrays. As events get activated, their entries are moved to an active list where a 2nd cog picks them off in order (returning them to an inactive list) and calls the method corresponding to some numeric code in the event entry. You could theoretically use a single cog for both the timing / polling and the event execution, but you'd have to be more careful with worst case execution time of the event methods than you need to do with the 2 cog scheme. Any other cogs would be used for PASM programs like FullDuplexSerial or part of an SD card library.
  • JonnyMacJonnyMac Posts: 9,108
    edited 2012-10-27 08:55
    I have -- when the main program loop runs fairly quickly -- used a fix loop time to synthesize a real-time clock or timer. For example, if your main loop processing can be done under 100ms, you might do something like this:
    var
    
      byte  rtcticks
      byte  rtc[3]
    
    
    pub main | t
    
      ' start required objects
    
      t := cnt
      repeat { main loop }
    
        ' loop code
    
        waitcnt(t += (clkfreq / 10))                        ' let 0.1s expire
        update_rtc
    
      
    pub update_rtc 
    
    '' Update RTC registers
    
      if (++rtcticks == 10)
        rtcticks := 0
        if (++rtc[0] == 60)
          rtc[0] := 0
          if (++rtc[1] == 60)
            rtc[1] := 0
            if (++rtc[2] == 24)
              rtc[2] := 0
    

    Of course, it's easy to adjust if your main loop requires other than 100ms to do its work. Ideally, it should be less than one second and an even multiple to simplify the update_rtc method.
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-02 10:49
    OK, I spent most of a day combining my multitude of objects into one (plus fullDuplexSerial). It certainly makes searching and coordination easier! It's now running in 5 cogs (plus the serial object).
    It appears that my current remaining problem is associated with the serial handler. One cog continually updates a serial LCD display. Another polls for incoming serial characters from an X10 radio receiver that gets signals from wireless motion detectors & keypads. Conflicts seem to occur that hang up one and/or the other of these processes.
    I see one *solution* as combining these processes in one cog so that screen updates are postponed whenever a poll sees an incoming character, and that process is carried thru to completion. That wouldn't create any visible impact since incoming strings are only a half dozen bytes at 9600.
    But I see a problem with my next upgrade which will add a bunch of button pads which will be hard-wired to a serial input. This necessitates two serial input pins. I'm not sure how I implement another fullDuplexSerial. Any help here appreciated!

    Don
  • kwinnkwinn Posts: 8,697
    edited 2012-11-02 18:25
    For implementing the second serial port I would suggest dropping fullDuplexSerial and using the four port serial object for both inputs and outputs. Using the one object for all your serial input and output may resolve some of your problems. The input and output pins of a port can go to two separate devices as long as they have the same baud rate. For instance if your LCD display the X10 both ran at 9600 baud the LCD could be on the output of port 0 and the X10 on the input. The serial object would take care of buffering the data for you so timing is not an issue.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-03 10:32
    kwinn wrote: »
    For implementing the second serial port I would suggest dropping fullDuplexSerial and using the four port serial object for both inputs and outputs.

    Since I have the link handy, I thought I'd mention Tracy Allen's four port object FullDuplexSerial4portPlus_0v3. It watches for framing errors, has adjustable rx and tx buffers and fixes the flow control bugs in Tim Moore's original code. It's the best version of Tim Moore's four port object I'm aware of.
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-03 12:27
    Thanx for the 4-port info & the link. Will give that a try!
    Don
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-03 16:14
    Converted to FDS4portPlus. NOTHING worked. Below is my FDS4PP tester. Should send a string of A's every second, and blink the test LED. If I comment out the ser.TX line, LED blinks OK. With ser.TX line there, code stops dead, and, of course, no serial data. (probably something obvious!)
    CON
            _clkmode = xtal1 + pll16x                                               'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
    OBJ
      ser : "FullDuplexSerial4portPlus"
      
    PUB public_method_name
      dira[16]~~ '  set test LED to output
      dira[4]~~  '  RS422 direction port
      outa[4]~~  '  Direction is send
      dira[3]~~  '  RS422 TX port
    
    '' mode bit 0 = invert rx                               XXX#INVERTRX
    '' mode bit 1 = invert tx                               XXX#INVERTTX
    '' mode bit 2 = open-drain/source tx                    XXX#OCTX
    '' mode bit 3 = ignore tx echo on rx                    XXX#NOECHO
    '' mode bit 4 = invert cts                              XXX#INVERTCTS
    '' mode bit 5 = invert rts                              XXX#INVERTRTS
    
      outa[16]~~
      
      ser.init
      
    '  ser.AddPort(port,rxpin,txpin,ctspin,rtspin,rtsthreshold,mode,baudrate)
      ser.AddPort( 1, -1, 3, -1, -1, 0, %001010, 9600 )  ' LCD panel xmit port
    
      if ser.start == -1  '  starts serial object cog 
        abort
      waitcnt(8_000_000 + cnt)
    
      repeat
    '    ser.tx(1,65)
        waitcnt(80_000_000 + cnt)
        !outa[16]  '  blinks an LED on pin 16
    
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-03 16:31
    Don,

    A couple of things stick out to me.

    If you're using a pin as a serial tx or rx line don't set the "dira" or "outa" for it. The serial object will take care of that for you. It looks like you're setting the direction of pin 3 at the beginning or your code and also using pin 3 for your LCD tx pin.

    Does you LCD really need an inverted signal? Whenever I've used a serial LCD, I'm pretty sure I just had the "mode" set to zero.

    And finally, you need to adjust the rx and tx buffers within the serial object to the size you want. I'd suggest something around 64 (it doesn't need to be a power of 2).

    Here's the section with the buffer sizes:
      TX_SIZE0                      = 16  ' enter in the needed size in bytes for each rx and tx buffer
      TX_SIZE1                      = 1   ' these values are arbitrary, just to show that they can be different
      TX_SIZE2                      = 16  ' not necessarily binary or less than any particular limit.
      TX_SIZE3                      = 64   '
      RX_SIZE0                      = 8
      RX_SIZE1                      = 1
      RX_SIZE2                      = 600
      RX_SIZE3                      = 64
     
    

    Since you're using port #1 change the values of "TX_SIZE1" and "RX_SIZE" to the values you want. You might want to reduce the size of the other buffers you're not using while you're there.

    Also the "Start" method returns zero if no cog is available, not -1, since one is added to the returned value of the newcog call.

    FYI, as you can see, the port numbering starts with zero (as so many computer things). (Do computer programmers say "Hi this is child number zero" when introducing their oldest child?)

    Edit: I just realized I was looking at Tracy Allen's "FullDuplexSerial4portPlus_0v3 ". I'm not sure if it has the same code as "FullDuplexSerial4portPlus".
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-03 16:42
    Thanx Duane,
    My *real* code uses both ports 0 & 1. Just used #1 in the test because that's where the already working LCD panel is connected. It uses *inverted* because it goes thru an RS422 converter which also does an inversion. But that hardware works fine with my old code. Took out the DIR's and bumped up the buffer sizes, but all still the same.
    Don
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-03 16:48
    I can't quote a previous post right now for some reason.

    You mention you should see a string of "A"s but it looks like the command to send an "A" has been commented out?
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-03 17:01
    Right . . . with it commented out, the 1 second loop runs, flashing the test LED. If I uncomment it, it just hangs there - no flashes or characters.
  • kuronekokuroneko Posts: 3,623
    edited 2012-11-03 17:34
    First, get rid of the NOECHO bit (%000010). You dont receive anything so the tx method will eventually hang while receiving (and discarding) the echo. Second, I'm using version 0v3 of the serial object which unfortunately has port 1's transmit buffer set to 1. Which means
    repeat until (tx_tail[port] <> (tx_head[port] + 1) // txsize[port])
    
    can never leave. Both, tail and head are initially 0 but a size of 1 will pull (head + 1) always back to 0. Assuming you suffer from the same effect, just increase the buffer sizes for port 1.
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-03 18:22
    BINGO! I already had set the buffers to at least 16, but clearing the echo bit did the trick. Thanx, now back to testing.
    Don
  • Don PomplunDon Pomplun Posts: 116
    edited 2012-11-06 17:12
    Thanx for the heads up on the 4 port serial. Looks like my code is now back to working. Additionally had to bump up the cog's method's stack space to eliminate those Mystery Crashes.
    Now on to *enhancements*
    Don
Sign In or Register to comment.