In the previous post I made reference to 'isochronous programming'.
This is just a few idle thoughts on this subject (I hope you won't
begrudge me the bandwidth)...
'Isochronous' derives from the Greek meaning 'same [amount of] time'.
This is useful for implementing loops and the like that execute at
a constant, jitter-free, time period in the absence of interrupts.
The PIC has a wonderful architecture for doing this. For a start, all
instructions execute in exactly 4 clock cycles (1I) unless a branch takes
place, in which case they take 8 cycles (2I). This makes it really easy to
count cycles without referring to the manual. Secondly, there are a
lot of simple sequences which are naturally isochronous. For example,
incrementing a 16-bit counter:
incfsz low ; K
goto $+2 ; G
incf high ; J
always takes 3 instructions no matter whether the carry is required or
not. Why is this so? Well, all the conditional instructions 'K' (those
ending with sz, ss or sc) are isochronous in conjunction with the following
instruction 'G' if G executes in 1I. If G happens to be 'goto $+2' then
the sequence KGJ is also isochronous if J executes in 1I.
One could work out a 'PIC algebra' for determining whether a sequence is
isochronous. Let the meta-instructions
K be a conditional instruction
K(n) n (>=0) K instructions in sequence
J be a 1I instruction (i.e. do something useful)
G any goto (2I)
G(n) be a goto $+n instruction (2I) where n > 0.
S(n) n (>=0) instructions in isochronous sequence
G(0) is not allowed since it is an infinite loop. G(-ve) would imply
some sort of looping which we can't accomodate in this algebra.
Here are some isochronous sequences (S). Implied is the trivial fact that
two such sequences S1 and S2 may be concatenated to form a new iso seq.
Sequence I cyc. Comment
----------------------- ------ ----------------------------
KG(2)J 3 Logical inversion of KJ
KG(n+2)S(n)G(n+2)S(n+1) n+4 'If then else' construct
S(n+1) n+6 1-in-3 case
The last 2 sequences are the most interesting. These arise in the situation
where the innards of the loop require one of (say) three different things
to be performed each time. When programming this, it may turn out that
the various cases require 22, 11 and 4 instructions respectively. Since
we are constrained by isochronism, the worst case (22) governs the loop
timing. Expanding the 1-in-3 sequence, things only fit perfectly when the
cases take n, n+1 and n+1 instructions to execute, so we have a problem
The simple-minded solution (which may be practical if the differences are
small) is to 'pad out' the best cases with NOPs (or other time wasters)
so that they take the proper amount of time. E.g. the 22 sequence would
be put in one of the 'n+1' slots. The 11 sequence would be padded up to
22 cycles (n+1) and the 4 sequence padded up to 21 (n).
In practise, the padding can be accomplished in smarter fashion e.g.:
K1 ; Conditional instruction
goto foo ; case 1
K2 ; next conditional
goto bar ; case 2
; 4 instructions of case 3 go here.
; Then waste a total of 21-4 = 17 instructions. However, the 'endfoo'
; sequence has accounted for 11 of these, leaving 6 NOPs required.
endfoo equ $
; Total of 22-11-2 = 9 NOP instructions for padding (the -2 is for the goto).
foo equ $
; 11 instructions of case 2 go here.
goto endfoo ; above
bar equ $
; 22 instructions of case 1 go here then we continue straight on.
endiso equ $
; End of isochronous sequence
All padding is implemented in a single sequence, minimising the use of code
It's really neat that the PIC's architecture lends itself so nicely to
isochronous code, though I often find it useful to refer to a certain
subset of such: code in which if the distance between two instructions
is D, the time between them is also D. Cycle-counting within such code
is especially easy, since you can merely subtract addresses.
One potential caveat, though [not that this is ever ACTUALLY a problem] is
that conditional and goto instructions will behave slightly differently
with respect to return-from-interrupt timings than other instructions.
If an interrupt is triggered during a skip or goto, the skipped instruction
will not take any extra time before the interrupt starts, but the PC will
point past it when the interrupt returns. Thus, "return from interrupt"
will appear to execute in 1 cycle if the interrupt was triggered immediate-
ly before an instruction would be skipped.
More... (looser matching)
- Last day of these posts
- In 1996
, 1997 only
- New search...