Mike - K8LH
Well-Known Member
Just wanted to pass along a new and improved K8LH PIC fixed delay subsystem for those PIC assembly language programmers who might be interested.
For those of you not familiar with my subsystem it was designed to be a general purpose replacement for the code generated by the PICLIST delay code generator, capable of generating precise delays in cycles, microseconds, or milliseconds using almost any clock (4, 8, 12, 16, or 20 MHz). It also allows you to subtract N number of cycles from the delay for isochronous code timing.
Basically I replaced the 4-cycle inner/outer timing loop in my old timing subroutine with Mike Bond's 4-word 5-cycle 16-bit timing loop and shaved three words off of the subroutine in the process. This also reduced timing subroutine "overhead" and associated minimum delay from 16 cycles to 11 cycles.
Then while studying the subsystem code I wondered if I could include some of the 'nop' instructions in the timing loop to increase the upper delay limit without affecting timing loop "overhead".
It turns out that each 'nop' included in the timing loop will increase the upper delay limit by 65536 cycles but you need one additional 'nop' at the top of the timing subroutine to handle the additional delay%loop subroutine entry point. Still, one additional word for each additional 65536 cycles isn't bad.
Finally I modified the DelayCy() macro to handle larger delays by allowing it to call the timing subroutine multiple times. This version is listed in the next post.
Regards, Mike
For those of you not familiar with my subsystem it was designed to be a general purpose replacement for the code generated by the PICLIST delay code generator, capable of generating precise delays in cycles, microseconds, or milliseconds using almost any clock (4, 8, 12, 16, or 20 MHz). It also allows you to subtract N number of cycles from the delay for isochronous code timing.
Basically I replaced the 4-cycle inner/outer timing loop in my old timing subroutine with Mike Bond's 4-word 5-cycle 16-bit timing loop and shaved three words off of the subroutine in the process. This also reduced timing subroutine "overhead" and associated minimum delay from 16 cycles to 11 cycles.
Code:
;
; DelayCy() clock equate and macro delay operand multipliers
;
radix dec
clock equ 4 ; 4, 8, 12, 16, 20 MHz
usecs equ clock/4 ; cycles/microsecond multiplier
msecs equ clock/4*1000 ; cycles/millisecond multiplier
;
; DelayCy() macro generates four instructions
;
DelayCy macro delay ; 11..327690 cycle delay range
movlw high((delay-11)/5)+1
movwf DelayHi
movlw low ((delay-11)/5)
call uDelay-((delay-11)%5)
endm
;
; example code for simulation testing
;
SimTest DelayCy(10*msecs) ; <- put simulator PC here
nop ; <- put simulator break point here
;
; uDelay(11..327690) subroutine Mike McLaren, K8LH, Mar-09
;
; 9 words, 1 RAM variable, 14-bit core
;
nop ; entry for (delay-11)%5 == 4 |B0
nop ; entry for (delay-11)%5 == 3 |B0
nop ; entry for (delay-11)%5 == 2 |B0
nop ; entry for (delay-11)%5 == 1 |B0
uDelay addlw -1 ; subtract 5 cycle loop time |B0
skpc ; borrow? no, skip, else |B0
decfsz DelayHi,F ; done? yes, skip, else |B0
goto uDelay ; do another loop |B0
return ; |B0
;
It turns out that each 'nop' included in the timing loop will increase the upper delay limit by 65536 cycles but you need one additional 'nop' at the top of the timing subroutine to handle the additional delay%loop subroutine entry point. Still, one additional word for each additional 65536 cycles isn't bad.
Code:
;
; DelayCy() clock equate and macro delay operand multipliers
;
radix dec
clock equ 8 ; 4, 8, 12, 16, 20 MHz
usecs equ clock/4 ; cycles/microsecond multiplier
msecs equ usecs*1000 ; cycles/millisecond multiplier
;
; 5 cycle loop delay range 11..327690 cycles
; 6 cycle loop delay range 11..393226 cycles
; 7 cycle loop delay range 11..458762 cycles
; 8 cycle loop delay range 11..524298 cycles
;
loop equ 8 ; select 5..8 cycle loop
;
; DelayCy() macro generates four instructions
;
DelayCy macro delay ; minimum 11 cycles
movlw high((delay-11)/loop)+1
movwf DelayHi
movlw low ((delay-11)/loop)
call uDelay-((delay-11)%loop)
endm
;
; example code for simulation testing
;
SimTest DelayCy(8*usecs) ; <- put simulator PC here
nop ; <- put simulator break point here
;
; 16 bit uDelay subroutine with adjustable 5..8 cycle loop time
;
; 12 words, 1 RAM variable, 14 bit core --- Mike, K8LH
;
nop ; entry for (delay-11)%loop == 7 |B0
nop ; entry for (delay-11)%loop == 6 |B0
nop ; entry for (delay-11)%loop == 5 |B0
nop ; entry for (delay-11)%loop == 4 |B0
nop ; entry for (delay-11)%loop == 3 |B0
nop ; entry for (delay-11)%loop == 2 |B0
nop ; entry for (delay-11)%loop == 1 |B0
uDelay addlw -1 ; subtract "loop" cycle time |B0
skpc ; borrow? no, skip, else |B0
decfsz DelayHi,F ; done? yes, skip, else |B0
goto uDelay-loop+5 ; do another loop |B0
return ; |B0
;
Regards, Mike
Last edited: