Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Timing conundrum - PIC Assembly

Status
Not open for further replies.

augustinetez

Active Member
Would someone be kind enough to check my test code below and see if something stands out that I'm not seeing please.

The problem:-

This is the timing loop to be used for frequency measuring in a project I'm messing about with, it calls a subroutine to check for TMR0 overflow and adjusts the various storage registers as needed. I make this subroutine to be 13 cycles long so it should be the same length as 11 NOP's + a RETURN.

But the sim (Oshonsoft) says no - timings shown in the code:

Code:
    list n=0    ; Turn off page breaks in the List file

; *******************************************************************************
; *    Timer Loop test                                *
; *******************************************************************************

    ERRORLEVEL -302        ;Get rid of 'not in bank 0' warning in assembly

    list     p=16f628a
    #include p16f628a.inc
    
;   Set configuration bits
    __CONFIG _HS_OSC & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF
    
;
    cblock 20h
    CounterA
    CounterB
    CounterC
    byte2
    byte3
    endc

;******** MAIN PROGRAM *********

    org    0x00
    goto    Start
    org    0x04
    goto    Start
    
Start

    clrf    PORTA        ; clear both port registers to zero
    clrf    PORTB

    movlw    0x07
    movwf    CMCON        ; Turn comparators OFF on PortA
    
    banksel    TRISA        ; Configure PIC I/O pins
    movlw    b'00000001'   
    movwf    TRISA        ; PORTA 0 input, 1:7 outputs
    clrf    TRISB        ; PORTB all outputs
    
    movlw    b'10110111'
                ; Port B weak pull-ups disabled
                ; INTDEG Don't care
                ; Count RA4/T0CKI
                ; Count on falling edge
                ; Prescaler to Timer/counter
                ; Prescaler = /256

    movwf    OPTION_REG

    banksel    0x00        ; Bank0

    bcf    INTCON,T0IF    ; Clear TMR0 interupt flag to be sure

    nop            ; NOP's in program loop are for breakpoint usage
    nop            ; in simulator (Oshonsoft).
    call    ms_1000        ; FIRST BREAKPOINT
    nop            ; SECOND BREAKPOINT
    nop

    goto    $-5

ms_1000                ; 1 second timing loop with PIC running at 10MHz
    btfss    PORTA,0        ; Select 1 second or 100mS timing
    goto    ms_100
    movlw    d'10'
    movwf    CounterC
ms_100                ; 100mS timing loop with PIC running at 10MHz
    movlw    D'54'
    movwf    CounterB
    movlw    D'255'
    movwf    CounterA
loop
    call    ovcount
    decfsz    CounterA,1
    goto    loop
    decfsz    CounterB,1
    goto    loop

    movlw    D'59'
    movwf    CounterA
loop2
    call    ovcount
    decfsz    CounterA,1
    goto    loop2
    nop
    nop
    nop
    nop
    btfss    PORTA,0
    retlw    0        ; End 100mS timing

    decfsz    CounterC,1
    goto    ms_100

    movlw    d'7'
    movwf    CounterC
loop3
    decfsz    CounterC,1
    goto    loop3
    nop
    retlw    0        ; End 1 second timing

ovcount                ; Fake routine for simulating, real code below
    nop            ; 1
    nop            ; 1
    nop            ; 1
    nop            ; 1
    nop            ; 1    Using this, timing is:-
    nop            ; 1        100,000uS = 100mS
    nop            ; 1        or
    nop            ; 1        1,000,000uS = 1 Sec
    nop            ; 1
    nop            ; 1
    nop            ; 1
    return            ; 2
                ; 13 CYCLES

;;
;;********* COUNT TMR0 OVERFLOWS *** 13 CYCLES  **********************************
;;
;;    Using this that is theoretically the same 13 cycle count, timing is:-
;;
;;                105552.80uS for 100mS
;;                or
;;                1055528uS for 1 Sec
;;
;ovcount                ; cycles
;    btfsc    INTCON,T0IF    ; 1 or 2 if test=true
;    goto    ov1        ; 2    ; TMR0 overflow flag
;    nop            ; 1    ; No overflow.
;    nop            ; 1    ; Trim to match slowest flow through
;    nop            ; 1    ; subroutine: ovcount -> ov2
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1
;    return            ; 2
;                ; 13 cycles
;ov1
;    incf    byte2,f        ; 1    ; TMR0 overflow, add 1 to highest byte
;    btfsc    STATUS,Z    ; 1 or 2 if test=true
;    goto    ov2        ; 2    ; Has h_byte overflowed
;    bcf    INTCON,T0IF    ; 1    ; Reset flag
;    nop            ; 1
;    nop            ; 1
;    nop            ; 1    ; Trim timing
;    nop            ; 1
;    return            ; 2
;                ; 13 cycles
;   
;ov2
;    incf    byte3,f        ; 1    ; Add 1 to highest byte
;    bcf    INTCON,T0IF    ; 1    ; Reset flag
;    return            ; 2
;;                ; 13 cycles

    end
 
If I sim it in ISIS I get what you get... Why are you using the T0IF? Are you intending to use an interrupt at some stage? The delay loop doesn't need the T0IF so I was just wondering
 
As I understand it, BTFSC and the line that follows it always take 2 cycles to execute, unless the line that follows it is a jump, which takes 2 cycles on it's own, so that makes three in total

So

btfsc status, c
incf register_a, f

will always take 2 cycles, whether or not the incf line is actually executed.

As another example,
line10 btfsc status,c
line11 goto line13
line12 nop
line13 nop

That will always take 4 cycles to run.

If carry is clear, line 10 takes 1 cycle, the NOP that is executed instead of line 11 takes 1 cycle, and lines 12 and 13 take 1 cycle each.

If carry is set, line 10 takes 1 cycle, line 11 takes 2 cycles because it is a jump, and line 13 takes 1 cycle.

The PICs pre-fetch the next instruction, and it that can't work because the next instruction in memory is not the next one to be executed, the PIC has to wait one cycle to collect the next instruction.

When normal instructions are being executed, the following instruction is being fetched, so there is no delay.

When a BTFSC is being executed, the following instruction is being fetched. If the following instruction is executed, there is no delay. If that following instruction is skipped, a NOP is run in its place, and while that is happening, the one after is fetched.

When a GOTO or return is executed, the program counter is updated, and while that is happening, the wrong instruction is being fetched, so that has to be discarded, and effectively a NOP is executed while the next operation is fetched.
 
Cheers for that D.. I thought it said in the DATASHEET..
Note I read.. If the next instruction is skipped is taken then 1 cycle. If not its 2 cycles.

1696879871956.png
 
Hi Ian,

I know you have the idea right, but stated it backwards:

1696881160294.png

Thus, when there is a skip, it take 2 cycles.
Simply put, anytime the program counter is changes by more than a single increment, it take two instruction cycles. Hence goto, etc. are also 2-cycle operations.

In reality, the next instruction (i.e., the bit test condition is false) is often a branch, which takes 2 cycles for a total of 3. If not a branch, the next instruction is executed. A structure where a branch isn't used would be something like:
Code:
movlw    d.10
btfsc    RegA,3
movlw    d.20
andwf    RegA,f

Maintaining isochronicity, which I assume is the intent, can become tricky with a lot of branches.
Regards, John
 
In case anyone is interested, final code below (that does work and timings as expected). Gives 1 second or 100mS option.

I borrowed and modified the TMR0/register update routine from the software of the Phil Rice frequency counter - it's much neater.

Code:
    list n=0    ; Turn off page breaks in the List file

; *******************************************************************************
; *    Timer Loop test                                *
; *******************************************************************************

    ERRORLEVEL -302        ;Get rid of 'not in bank 0' warning in assembly

    list     p=16f628a
    #include p16f628a.inc
    
;   Set configuration bits
    __CONFIG _HS_OSC & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF
    
;
    cblock 20h
    CounterA
    CounterB
    CounterC
    CounterD
    byte1
    byte2
    byte3
    endc

;******** MAIN PROGRAM *********

    org    0x00
    goto    Start
    org    0x04
    goto    Start
    
Start

    clrf    PORTA        ; clear both port registers to zero
    clrf    PORTB

    movlw    0x07
    movwf    CMCON        ; Turn comparators OFF on PortA
    
    banksel    TRISA        ; Configure PIC I/O pins
    movlw    b'00000001'   
    movwf    TRISA        ; PORTA 0 input, 1:7 outputs
    clrf    TRISB        ; PORTB all outputs
    
    movlw    b'10110111'
                ; Port B weak pull-ups disabled
                ; INTDEG Don't care
                ; Count RA4/T0CKI
                ; Count on falling edge
                ; Prescaler to Timer/counter
                ; Prescaler = /256

    movwf    OPTION_REG

    banksel    0x00        ; Bank0

    bcf    INTCON,T0IF    ; Clear TMR0 interupt flag to be sure

    clrf    byte1
    clrf    byte2
    clrf    byte3

;    movlw    d'255'        ; for testing register roll-over timing
;    movwf    byte1
;    movlw    d'255'
;    movwf    byte2

    nop            ; NOP's in program loop are for breakpoint usage
    nop            ; in simulator (Oshonsoft).
    call    ms_1000        ; FIRST BREAKPOINT
    nop            ; SECOND BREAKPOINT
    nop

    goto    $-5

ms_1000                ; 1 second timing loop with PIC running at 10MHz
    btfss    PORTA,0        ; Select 1 second or 100mS timing
    goto    ms_100
    movlw    d'10'
    movwf    CounterD
ms_100                ; 100mS timing loop with PIC running at 10MHz
;PIC Time Delay = 0.09999960 s with Osc = 10000000 Hz
    movlw    D'2'
    movwf    CounterC
    movlw    D'64'
    movwf    CounterB
    movlw    D'63'
    movwf    CounterA
loop

    decfsz    CounterA,1
    goto    loop
    call    RollOver
    decfsz    CounterB,1
    goto    loop
    decfsz    CounterC,1
    goto    loop
    nop
    nop
    btfss    PORTA,0
    retlw    0        ; End 100mS timing

    decfsz    CounterD,1
    goto    ms_100

    movlw    d'7'
    movwf    CounterD
loop3
    decfsz    CounterD,1
    goto    loop3
    nop
    retlw    0        ; End 1 second timing
;
; Account for TMR0 overflows when counting
; Check at regular intervals and handle as
; necessary.
;
; Needs to be done at less than 936us (@ 70MHz in)
; intervals, or else it can lose counts.
;
RollOver
    btfss     INTCON,T0IF     ; Rolled over?
    goto     RO3         ; No
RO1
    bcf     INTCON,T0IF     ; Yes. ACK!
    incf    byte1,f
    btfss    STATUS,Z
    goto    RO4
    incf    byte2,f
    btfsc    STATUS,Z
    incf    byte3,f
RO2
    return
                ; Balance path lengths
RO3
    nop
    nop
    nop
    nop
RO4
    nop
    nop
    return

    end
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top