In addition to its somewhat unique architecture, the Propeller was designed to be practical – because it was designed by a person (Parallax founder Chip Gracey) who started as a microcontroller enthusiast and, like you and me, had to work very hard given the restrictions of traditional microcontrollers. Precision timing is an important aspect of many microcontroller applications, and the Propeller was designed to make this task simpler.
In the Hub there is a special RAM register called CNT: the system counter. This register may be read (it is read-only) by any cog (processor) at any time. Internally, the CNT register connected directly to the system clock and is incremented (one is added to it) on every clock cycle. For most systems (including Blockly projects), this means the CNT register is being updated 80 million times per second!
To maintain system integrity, mutually-exclusive resources must not be accessed by more than one cog at a time. The Hub controls access to mutually-exclusive resources by giving each cog a turn in a “round robin” fashion from Cog 0 through Cog 7 and back to Cog 0 again. Want to learn more about the Propeller microcontroller? Check out the Propeller P8X32A datasheet.
We can use the CNT register for timing by reading it at the start and end of an event; the difference between these two values is duration of the event, expressed at a resolution of 1/80,000,000th of a second. This is excellent for timing high-speed events like an object moving through a speed trap that has sensors on either end.
There is a more popular use of the CNT register that involves precision of timing: that is in the control of code loops. Have a look at this simple set of blocks:
We're sending a message to the terminal window every second. Right? Well... yes and no. In human terms, absolutely. In machine terms, the time between messages is actually a little more than one second You see, it takes time to transfer the message to the terminal cog, and to print the new line that follows. What we don't see is that the program must jump back to the top after it completes the pause block; even this jump takes a little bit of time.
The pause block is perfect for when we want to insert a specific delay into the program, regardless of anything else that is happening.
Let's look at another kind of block – that looks a little complicated at first – but does something really special that is at the heart of many time-critical applications:
To control this type of loop – which we call a synchronized loop – we need a variable and the use of the wait until block (in the SYSTEM category). The critical part here is that we capture the CNT register into the variable called tSync immediately before entering the loop; this is our synchronization point. After the message is sent, we're going to add the system clock frequency (80,000,000) into tSync; what this does is set a target that is exactly one second past the original synchronization point. The key is in the wait until block. This block will wait until the value of the CNT register matches what we have in tSync. This is different from the pause block because the start of the initial delay was outside the loop, before we printed anything to the terminal. Since the is an infinite loop, the message will be printed to the terminal exactly every second while the program runs. This is happening, because we're always adding the system ticks in one second to the last target (tSync).
Let's make it a little more interesting. Have a look at these blocks:
We've added another line, plus another new line to the display.
Pop quiz: Will this take longer to run than the first version of this kind of loop?
The answer is no, and that's what makes the wait until command different than pause. The great thing is that we can put as much code into the loop as will run in the amount of time we have (in this case, one second). The use of synchronized loops is the basis of many robotics and industrial applications.