Jimmy Page
Jimmy Page

Reputation: 79

How does Delay work in PIC ASM? I'm truly baffled

I really don't understand it at all. All the code I see online somehow in the comments has random numbers of why it's doing that many cycles, but there is literally no explanation as to why what does what or where what does who. I really have no idea.

For example:

cblock  
Delay1  
Delay2  
Delay3  
endc  

Start:  

.  
.  
.  

Delay  
movlw .2  
movwf Delay1  ;&&&&
movlw .3  
movwf Delay2  
movlw .4       <------ Important note here. For some reason it loops back to the
movwf Delay3   <------ "&&&&" mark when it reads this piece of code. NO IDEA why

DelayLoop  
decfsz Delay1,f    <----- Explain what this does. Where does it get the time from?
goto   DelayLoop   <----- Thanks
decfsz Delay2,f    <-----
goto   DelayLoop  

end

Any help would be fantastic.

Upvotes: 1

Views: 7681

Answers (3)

Jon
Jon

Reputation: 11

The frequency of the built in RC oscillator determines the clock cycle, which in turn determines the speed.

Upvotes: 1

old_timer
old_timer

Reputation: 71506

"I really don't understand it at all. All the code I see online somehow in the comments has random numbers of why it's doing that many cycles, but there is literally no explanation as to why what does what or where what does who. I really have no idea."

"I understand that, but I like, don't know where you get EXACT timing from. How do you calculate it? Where do these numbers come from. If I set VAR1 to 15, what happens, what changes. If I set the three delay variables to 4, 16, 12, where does the the timing come from? What happens to those numbers to cause the loop to be a certain time? Thanks – Jimmy Page"

If I understand your questions then:

The 16F690 data sheet says:

One instruction cycle consists of four oscillator periods for an oscillator frequency of 4 MHz, this gives a normal instruction execution time of 1 μs. All instructions are executed within a single instruction cycle, unless a conditional test is true, or the program counter is changed as a result of an instruction. When this occurs, the execution takes two instruction cycles, with the second cycle executed as a NOP.

So lets say Delay1 has the value 3 and we have this loop:

Top
decfsz Delay1,f
goto Top

Each time we execute decfsz we get the stock one cycle. IF f is zero and we have to skip then it becomes a two cycle instruction. Each time we execute the goto the pc changes so it is a 2 cycle instruction. So if we walk the loop and I show this using the format

Delay1 value before instruction, instruction, cycles used to execute, total cycles

3,decfsz,1,1
2,goto,2,3
2,decfsz,1,4
1,goto,2,6
1,decfsz,2,8

So that loop by itself with Delay1 starting at 3 took 8 cycles or 8us if we were running at 4mhz. It is important to compute cycles first, then adjust for the clock rate of the part, that same code could be 16us when running at 2mhz or 32us when running at 1mhz.

So by inspection we can see that for f values that are not one the decfsz+goto pair are 2+1=3 cycles. The one time we hit decfsz with a value of 1 it will be 2 cycles. So starting with Delay1 of 3 there will be 2 non-one entry values (3,2). And add 2 cycles for the last time we hit decfsz and skip, total cycles ((3-1)*3)+2=8.

If we had entered this loop with Delay1 set to 11, it would be ((11-1)*3)+2=32 cycles, a 7 would be 20 cycles.

If you go further and wrap one decfsz loop around another you continue to multiply the number of executed cycles. If Delay1 is a 3 going in and Delay2 is a 2

Top
decfsz Delay1,f
goto Top
decfsz Delay2,f
goto Top

Then the first time through the Delay1 decfsz,goto loop we know is 8 cycles. The first decfsz Delay2,f because Delay2 is not a 1 is 1 cycle, we are up to 9 total. the goto is 2 more, 11 total. Assuming the f memory is 8 bit, then the second time we hit the Delay1 loop we enter with a 0, think of it as a 0x100 or 256 for the math giving us ((256-1)3)+2=767 more cycles, 778 total so far. Delay2 is now a 1, so this is the final decfsz Delay2,f so that costs us 2, a total of 780 cycles. And we could come up with an algorithm for computing the cycles for that Something close to ((Delay2-1)(((256-1)*3)+2))+(((Delay1)-1)*3)+2)+((Delay2-1)*3)+2 cycles.

And although my loop is smaller by one decfsz loop than yours, if you make mine resemble yours in that it starts with some other instructions:

Entry
movlw .3
movwf Delay1
movl2 .2
movwf Delay2

Top
decfsz Delay1,f
goto Top
decfsz Delay2,f
goto Top

We need to add 4 more cycles for the two movlw's and two movwf's to the overall equation for number of cycles to execute.

So, literally, there is an explanation why it is executing so many cycles. And that explanation was in the datasheet for the device.

Lets take this further. Lets get the code generator that patrickmdnet linked to to generate 780 cycles:


; Delay = 780 instruction cycles
; Clock frequency = 4 MHz

; Actual delay = 0.00078 seconds = 780 cycles
; Error = 0 %

    cblock
    d1
    d2
    endc

            ;778 cycles
    movlw   0x9B
    movwf   d1
    movlw   0x01
    movwf   d2
Delay_0
    decfsz  d1, f
    goto    $+2
    decfsz  d2, f
    goto    Delay_0

            ;2 cycles
    goto    $+1

This loop is architected a bit different.

we start with 4 cycles of loading the f registers before we get to Delay_0

The inner decfsz loops goto does not branch up to Delay_0 directly as the code in your question and my explanation above, this one skips over decfsz d2,f. So for the non-one passes through that loop there is 1 cycle for the decfsz, 2 cycles for the goto $+2 and 2 cycles for the goto Delay_0, total 5 cycles for each non-one d1. And add two more for the time that d1 is a 1. this gives us ((0x9B-1)*5)+2 = 772 cycles add the 4 cycles before this we are up to 776 cycles.

Interestingly that last decfsz d1,f hits a decfsz d2,f with d2 set to 0x01 which means it is guaranteed to skip. it is 2 cycles, a goto $+2 with another goto $+1 instead of the movlw/movwf to load d2 would have done the same thing. Anyway this single instruction of 2 cycles brings us up to a total of 778 cycles

We need two more cycles to get to 780 and that is done here with a goto $+1, gotos modify the pc so they are always 2 cycles. We have the 780 cycles that I asked the program to generate.

Upvotes: 4

patrickmdnet
patrickmdnet

Reputation: 3392

The idea behind delay loops is to burn a certain number of cycles that correlate to a certain amount of time. The amount of time per cycle depends on the clock rate of your PIC CPU.

What you posted is a three-stage delay loop similar to the ones generated here: http://www.piclist.com/techref/piclist/codegen/delay.htm

The first block of instructions loads three memory locations with counters. Use the program above to find the optimal values to produce the delay you want.

The second set of instructions is the actual delay. "decfsz VAR,f" will decrement VAR by 1; if VAR is then zero, it skips the next instruction. So in the above case, decfsz will run until Delay1 is zero, then jump over the "goto DelayLoop" and start "decfsz Delay2,f".

I suggest reading this page: http://www.mstracey.btinternet.co.uk/pictutorial/progtut4.htm

and also this page: http://www.piclist.com/techref/microchip/PIC16DelayTutorial.htm

for more help on how decfsz and goto works.

Upvotes: 3

Related Questions