Shop OBEX P1 Docs P2 Docs Learn Events
How do I get timed interrupt-like behavior? — Parallax Forums

How do I get timed interrupt-like behavior?

Bill BairdBill Baird Posts: 23
edited 2006-07-21 08:49 in Propeller 1
I thought I could use waitcnt for my timing needs, but believe I am now discovering that the waitcnt instruction does exactly the reverse of what I am looking for. I am used to the counters with interrupt on the SX chips so I have misunderstood the propellor no interrupt system.

I really need an independent hardware counter to accumulate in the background like the SX counter while assembly code executes in the foreground until a fixed interval is reached. Then I execute a short piece of code (in the interrupt routine in the SX) and resume the count in the background while the foreground code continues.

Waitcnt instead stops all execution for a defined interval. Then code execution begins again without any timing if I understand the instruction right.

Is there a way to use the A and B counters as background timers that can interrupt ongoing foreground execution to inject a few lines of code at predefined intervals? It seems unlikely given my understanding of their single pin transition counting and output functions.

Or can I use a whole cog just for waitcnt timing and get it to fetch a value from main memory to change the outa pin values in a brief interval (like an interupt routine) before the next delay count begins? Then I run the desired foreground code on another cog completely independently to set up the series of values in main memory that the waitcnt cog needs. Am I getting it?

But then damn!....the Hub delay uncertainty for fetching from main memory messes up the timing in the interval between the waitcnt periods. I need a precisely timed overall cycle of timed subintervals. Even if I load all the output values at once between the restarts of the main cycle of subintervals, I have then an uncertain period that is part of the overall repeating cycle, so the total timing cycle is uncertain.

Is there a way out of this? I love the 32 bit path and the multiprocessing capability of the propellor, but do I have to go back to the SX to get this simple timed interupt function?

Comments

  • T&E EngineerT&E Engineer Posts: 1,396
    edited 2006-07-20 12:16
    You may be able to interface the SX-28 to the Propellor. This way you will have some interupt capability (in an indirect way).
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-20 15:00
    I'm not completely clear about what you're trying to do. You can certainly use the A and B counters to count external events continuously. You can use WAITCNT to wait for precise marked times (defined intervals) and do whatever you want in the interval between the marked times. Please note that WAITCNT doesn't stop execution for a defined interval. It stops execution until a precise point in time (while the counters continue their counting). It's used for synchronization, not quite like a PAUSE. Since most instructions are deterministic, WAITCNT gives you a mark in time and your code (if you want to count instruction times) can keep precise time from those marks. The HUB instructions throw that off, but you can resynchronize afterwards with a WAITCNT.
  • Charlie JohnsonCharlie Johnson Posts: 147
    edited 2006-07-20 15:15
    "I really need an independent hardware counter to accumulate in the background like the SX counter while assembly code executes in the foreground until a fixed interval is reached. Then I execute a short piece of code (in the interrupt routine in the SX) and resume the count in the background while the foreground code continues."

    Put this "short piece of code" in a seperate COG that does the WAITCNT in a REPEAT loop.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-20 17:10
    Bill,

    dy8coke is right. Interrupt-driven systems like the SX are multi-tasking: one processor doing many tasks. The Propeller is a multiprocessing controller: many processors, each doing one task. You can think of the SX's interrupt system as a separate hardware processor that justs waits for specific events to happen. The Propeller is no different in that regard, except that you get to design as many interrupt processors ("event processors" being the better term) as you need in software and service the events without interrupting another process. So an event processor in the Propeller would be a simple loop, like this:

    ··1. Wait for the event to occur.
    ··2. Handle the event.
    ··3. Go to 1.

    The Propeller provides four wait instructions for use in step one: waitcnt, waitvid, waitpeq, and waitpne. The first two wait for events from internal perpherals; the last two, from external pins. These and the eight cogs are all you need to make the translation from multi-tasking interrupt handling to multi-processing event handling.

    -Phil
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-20 18:15
    Phil,
    There are still circumstances where you want to wait for one of two events to occur and really have to minimize the delay from the first event to your action. If you assign each event test to a separate cog and use a 3rd cog for the action, you have only two ways to communicate among the cogs, through hub memory or through I/O pins. Hub memory has a difficult to predict delay associated with it while I/O pins are a very limited resource. If I had two I/O pins to spare, I'd use them. Assign one to a cog acting as a clock, just doing (I've left out the initialization):
    waitLoop    waitcnt  timeCounter,#ticksToNext
                     or          outa,#%01
                     jmp       waitLoop
    
    


    The second cog would sit in a loop checking CTRA and CTRB for specific values and doing an "or outa,%10" when appropriate. The 3rd cog would start these first two cogs and wait in a loop using a WAITPNE as in:
    waitForIt  waitpne  stateZero,#%11
                   mov        holdState,outa
                   andn       outa,#%11
                    ...          ' do something based on bits 1 and 0 of holdState
                    jmp       #waitForIt
    stateZero  long        0
    
    


    The only problem with this is that you might miss one of the two events that occurred between mov and the andn. Unfortunately, I don't think there is any way to do a LOCK-like operation that doesn't involve the HUB and its delays. There are no instructions other than the LOCK ones that can first copy a bit to one of the flags (c or z), then set or clear just that bit.
    A lot depends on the precise timing you want to do relative to either your time intervals or the specific counts you're collecting. If you can tolerate some variability from the event to the action done, you can make this work easily and reliably on the Propellor. You won't miss counts and the time "ticks" will still be precisely at the interval you've specified.
  • Jeff MartinJeff Martin Posts: 755
    edited 2006-07-20 18:29
    Bill,

    You're close to the right solution but have missed one important concept. Here's the solution:

    1) Use one cog for your timed event
    2) Use another cog (or more) for any other foreground tasks

    The timed-event cog should first read the System Counter value (CNT), add the required interval to that value (save for later) and then enter a loop. The loop should first read then next outa value from main memory, then execute the WAITCNT instruction (with that previously saved CNT+Interval value) to wait for the right time, add the interval to that previous saved CNT+Interval value (save for later), then it should write the outa value and loop back around.

    Here's the important concept: The System Counter never stops (except during a power-down/reset condition, of course) and is never reset or cleared in any way. The beauty of the WAITCNT command is that it can be used for both PAUSE-like functions as well as synchronized functions. If you use WAITCNT correctly, the effect is that it automatically compensates for the indeterminent amount of time it took to read from main memory before the last delay. This works as long as the interval time is not less than the longest loop time. Look in the Propeller Manual under "Synchronized Delay" to see examples of how this works.

    Looks like you're familiar with the SX, remember the RETIW command? The WAITCNT command, when used for synchronized delays, functions the similar to using the RTCC (set to increment on instruction cycles) to cause timed interrupts and using the RETIW command to set the next interrupt point, automatically compensating for the execution time of the interrupt routine.

    I've used WAITCNT a number of times for this very reason... it's a joy to use and exciting the see the rock-solid timing that results from it.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Jeff Martin

    · Sr. Software Engineer
    · Parallax, Inc.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-20 19:15
    Bill,

    To add to Jeff's comment: If you're in a jam, timing-wise, where the next interval data won't quite be ready before waitcnt for the prior one is executed, you can do something like this:

    loop   [b]waitcnt[/b]      time, #32                'Wait for the last interval, less 32. Add 32 to time.
           [b]rdlong[/b]       interval, interval_addr  'Read the next interval value.
           [b]sub[/b]          interval, #32            'Subtract 32 from it.           
           [b]waitcnt[/b]      time, interval           'Wait the remaining 32 clocks from the last interval. Add next interval, less 32, to time.
           (do your I/O)                         'Write the port pins, or whatever.
           [b]jmp[/b]          #loop                    'Loop back.
    
    
    


    This will allow all but 32 clocks of the last interval to pass before you read the next interval. Because the rdlong is sandwiched between two waitcnts, any uncertainties in hub timing will be eliminated. Each interval will have to be larger than 32 (plus I/O and loop overhead) for this to work.

    -Phil
  • acantostegaacantostega Posts: 105
    edited 2006-07-20 21:19
    The very reason I'm playing with the Propeller for robotics is that it I don't to have mess around with interrupts to make some sort of pseudo RTOS for multitasking. Since I was using a subsumption approach, I had several Augmented Finite State Machines running in parallel along with other I/O code for stuff like serial comms, handling sensors (managing sonar sensors, resampling ADCs, counting encoder pulses, IR sensor messages) controlling PWMs for motor control and servos for a grasper ... yuck. Debugging sucked of course. Now I can just run one cog per AFSM, and a couple of cogs for I/O, gleefully wasting cycles with busy waits [noparse]:)[/noparse]
  • Bill BairdBill Baird Posts: 23
    edited 2006-07-20 23:37
    Bingo ! I think I get it now. You guys are right that I have failed to understand the synchronization mode of use for waitcnt and how it can give me the exact timed intervals I need and compensate for the uncertain timing of hub operations.

    What I have missed as Jeff says (if I get it right) is that the system clock does run in the background as an absolute time reference during any data fetching from main memory with Hub timing uncertainty. I need to think in terms of using specified markers or timing target points on this absolute time scale as Mike and Phil point out.

    Therefore, in my timing and output dedicated cog, if I can pick an interval - since the end of the last waitcnt - that I know is large enough (with worst case Hub delay) to do my output and fetch my data array, then I can add that interval to the next waitcnt target and I have an exact time interval covering the uncertain fetch time.

    If I get this synchronization mode right, it means I can shorten the pause/delay function of waitcnt so that there is lots of computation space for fetches etc. between waitcnt's, but the interval between the waitcnt target time points is exact. I can roughly figure the amount of time for the desired in between computation and bring on the waitcnt somewhere before the next target timepoint is to arrive for the precise timing onset of the code that follows. As Phil shows I can sandwich hub operations between such brief waitcnts to contain the operations within a precise time interval.

    Thank you all for the volume and detail of response here. Going beyond interrupt thinking to multiprocessing "cog think" is discussed in the articles about the propellor on the website so I guess it is no surprise that I am somewhat confused. Getting used to this new way of thinking will take some time for me and I am still taking in the detail of all the information you have provided.

    My output timing problem is not as difficult as what some of you are addressing here I believe. I only need to do output at prespecified intervals in a cycle. So I can load these intervals into the timing cog memory at start up, and then I have only to pull in the 16 longs of 24 bit output patterns per cycle that I need to output at 16 specific times within the overall cycle. The large cycle is 1 ms in duration so repeats at 1k Hz, but the smallest interval is one instruction or 4 clocks or 50 ns at 80 M Hz system clock.

    These intervals come in increasing powers of two out to 2^16 clocks of total cycle time, so the later intervals are full of computation space. A given output pin is on for certain combinations of these intervals given by the loaded longs that specify an overall duty cycle to control the squared intensity of a component of a tricolor LED. The propellor lets us drive 8 independent banks of tricolor LEDs for visual stimulation in evoked potential EEG studies at the 1K Hz cycle time or "frame rate", and the control of squared intensity compensates for the eye's logarithmic brightness sensitivity. These particular features are beyond any specs for off the shelf LED drivers which usually refresh at 60/sec video rates but can drive much larger pixel arrays of independent LEDs. We need the fast intensity control cycle to be absolutely sure we are outside the passband of neural response and the brain is not getting driven by the duty cycle itself.

    We have 50 KBs of input to do also on another cog with the remaining 8 portA pins, and plenty of math processing to do on other cogs as well to realize CIE color space transforms, but these can be done in Spin we believe. The propellor is an awesome device with potential far beyond what we demand and we thank you for the brilliant engineering development involved and the ready forum help that gives us access to it capabilities.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-21 04:36
    You're basically right. Generally, I do all the prep / calculations / moving of information / setup prior to doing a WAITCNT waiting for a precomputed time point. The number of instructions / clocks until output bits are set / input bits are read, etc. is fixed and knowable in relation to the just executed WAITCNT. That WAITCNT has also added a "clock ticks to the next time point" to the destination operand of the WAITCNT which gets it ready for the next "wait until the next time point". After the I/O comes the code to set up for the next time point, and so on.
  • Bill BairdBill Baird Posts: 23
    edited 2006-07-21 08:28
    Yess, thanks for the verification. I am putting this to work and coding up my timing cycle right now. No heat and no distractions at this time of night.......................
  • Graham StablerGraham Stabler Posts: 2,507
    edited 2006-07-21 08:49
    This is cool, so if I want to for example toggle a pin every second but do some processing that takes less than a second between the toggles I can just make a note of count do my processing and THEN wait for the cnt using WAITCNT!

    The fact that data output via the TV etc is so easy will make working out the timing a matter of just displaying CNT after the processing I guess, also cool.

    There is a theme here, cool!

    Graham
Sign In or Register to comment.