How do I get timed interrupt-like behavior?
Bill Baird
Posts: 23
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?
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
Put this "short piece of code" in a seperate COG that does the WAITCNT in a REPEAT loop.
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
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):
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:
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.
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.
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:
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
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.
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