Setting an interrupt on the timer 0 overflow appears to cause a hardware interrupt at 8x the timer interrupt frequency

Flenser

Senior Member
#1
I am attempting to implement a cooperative task scheduler, as described in the book Patterns for time-triggered embedded systems by Michael J. Pont, on the X2 chips and for this I need:
1) to be able to put the chip to sleep, and
2) a timer interrupt that will wake the chip from sleep and that can be configured as a sytem tick in the 10 - 100ms range. The interrupt subroutine is used to run the task scheduling code.

The SETINTFLAGS command allows me to set an interrupt on the timer 0 overflow.
The SETTIMER command together with TIMER=65535 allows me to configure the timer 0 overflow in the 10 - 100ms range.
The "DOZE 0" command puts the chip to sleep puts the chip to sleep while leaving the timers running.

If I run this first code I get the character "." sent to the terminal once per second, which is what I expect.
Code:
#NO_DATA
#NO_TABLE
#PICAXE 20X2
SETFREQ M4

; Setup timer0 to overflow for the scheduler tick
SETTIMER t1s_4
timer=65535
; Set a polled interrupt on the timer0 overflow
SETINTFLAGS %10000000,%10000000

Main:
    GOTO Main

interrupt:
    toflag=0
    timer=65535
    ; We need to re-enable our interrupt. This won't take effect until after we return.
    SETINTFLAGS %10000000,%10000000
    SERTXD (".")
    RETURN

If I run this second code I get no characters sent to the terminal, which is what I expect.
Code:
#NO_DATA
#NO_TABLE
#PICAXE 20X2
SETFREQ M4

Main:
    DOZE 0
    SERTXD ("M")
    GOTO Main
If I run this third code, which has both the timer interrupt and the DOZE command, I get a sequence of characters where the character "." is sent once per second and the character "M" is sent eight times per second: MMMMMMMM.MMMMMMMM.MMMMMMMM.
Code:
#NO_DATA
#NO_TABLE
#PICAXE 20X2
SETFREQ M4

; Setup timer0 to overflow for the scheduler tick
SETTIMER t1s_4
timer=65535
; Set a polled interrupt on the timer0 overflow
SETINTFLAGS %10000000,%10000000

Main:
    DOZE 0
    SERTXD ("M")
    GOTO Main

interrupt:
    toflag=0
    timer=65535
    ; We need to re-enable our interrupt. This won't take effect until after we return.
    SETINTFLAGS %10000000,%10000000
    SERTXD (".")
    RETURN
i.e. I appear to be getting a hardware timer interrupt waking the chip from the DOZE command at a frequency which is eight times my setting for the PICAXE interrupt on the overflow of timer 0.

All tests done on a 20X2 chip reporting the vC.3 firmware at 4MHz.

Does anyone have any insight on how the hardware interrupts associated with the PICAXE timer interrupt combine to give this behaviour and/or can anyone spot something I'm not doing right?
 

hippy

Technical Support
Staff member
#2
It's been a while since I looked at SETTIMER but what seems to match the documentation is -
Code:
                       .-------.
                 t1s_4 | 49910 |
                       `-------'
                           |<--------- Reload <---.
           .-----.     .-------.                  |
4MHz ----->| 256 |-----| Timer |-----> Overflow --^--> Inc 'timer' variable
       :   `-----'  :  `-------'  :
       :            :             :
       250ns        64us          1 second, (65535-49910)*64us
From getting your eight "M" transmissions it would seem DOZE is waking up every 125ms. It is hard to see how that is an interrupt from the timer overflow, or an interrupt from the 'every 64us' input to it. It seems to be something outside that.

The SERTXD("M"), GOTO, DOZE sequence shouldn't take 125ms, probably more like 5ms at 4MHz and 4800 baud, so you should see more characters sent if it were that.

That would also be true for anything which wakes DOZE up more frequently than every 125ms so it seems it is an 'every 125ms' wake-up.

It's hard to see what would cause an 'every 125ms' wake-up. A 16-bit timer being clocked every 2us is the best I can think of.

But I have no idea how that could arise or where it would be coming from, and it could be something else. It is indeed intriguing.

Given though it does only wake-up every 125ms you could probably just use some flag to determine whether it should have woken up, DOZE again if it shouldn't have. That would probably have little effect on low-power operation.

And, if you are waking up every 10-100ms then this false wake-up may never actually be seen anyway.
 
Last edited:

Buzby

Senior Member
#3
Keep the frequency the same (M4), but change the preset (t1s_4) to a t1s_8. This will make the overflow happen every 2 seconds.

Now see how many 'M' characters happen between each '.'
 

hippy

Technical Support
Staff member
#4
change the preset (t1s_4) to a t1s_8 ... Now see how many 'M' characters happen between each '.'
There are 7 M's before the first '.' then 8 M's between each subsequent '.'

I'm seeing the same with t1s_4, t1s_8, and t1s_16.

That is consistent with a 2us clocked 16-bit timer causing an overflow every 131ms. There would be 7 overflows in a one second period, the additional M being issued immediately after the '.' when the interrupt routine is returned from. Also proven by changing what gets printed.

However, the period between the wakes from DOZE is more like 125ms, 8Hz. I am guessing the expected interrupt and the waking up DOZE interrupt actually do fire at the same time.
Code:
#Picaxe 20X2
#Terminal 4800
#No_Table
#No_Data

Symbol WOKEN = C.1
Symbol INIRQ = C.0

Main:
  Pause 2000
  SetFreq M4
  SerTxd( "-" )
  SetTimer T1S_4
  Gosub Interrupt_Enable
  Do
    Doze 0
    High WOKEN
    SerTxd( "M" )
    Low WOKEN
  Loop

Interrupt:
  High INIRQ
  SerTxd( "." )
  Low INIRQ

Interrupt_Enable:
  toFlag = 0
  timer  = -1
  SetIntFlags %10000000, %10000000
  Return
SetTimer.jpg
 
Last edited:

hippy

Technical Support
Staff member
#5
The mystery deepens, then an epiphany.

Turn Timer 1 off and there's no output, no wake from DOZE, no toFlag interrupt as expected. Turning Timer 2 and Timer 3 off has no effect, also as expected. That confirms it's Timer 1 in use, as expected.

But looking in the datasheet at Timer 1 and I can't see anything which clocks Timer 1 other than Fosc/4 or an actual oscillator. So what provides the 'every 64us' tick for Timer 1 ?

I'm guessing that Timer 1 is free-running and the divide by 256 is handled somewhat differently on the 20X2 than my earlier diagram. With a Timer 1 pre-scaler of 8 the timer overflow interrupt will be happening every 125ms but firmware is dividing that down by 8 before invoking the actual interrupt routine -
Code:
                         .-------.
                   t1s_4 | 49910 |
                         `-------'
                             |<-----.
         .---.   .---.   .-------.  |  .---.
4MHz --->| 4 |---| 8 |---| Timer |--^--| 8 |-----> Inc 'timer' variable
      :  `---' : `---' : `-------'  :  `---'  :
      :        :       :            :         :
      250ns    1us     8us          125ms     1 second
 
Last edited:

hippy

Technical Support
Staff member
#6
And the above is confirmed with -
Code:
Do
    Doze 0
    PeekSfr $CE, b0 ; TMR1L
    PeekSfr $CF, b1 ; TMR1H
    SerTxd( #w0,"-" )
  Loop
There's plenty of time to print the Timer 1 value before the next 125ms overflow, and when we do we see Timer 1 has consistently incremented form its t1s_4 reload value, except when we've spent some time in the Interrupt routine when it's longer.
 

Buzby

Senior Member
#7
Oh !, I do love these 'under the hood' explorations.

Unfortunatley I'm in France, without a PICAXE, so I can't explore myself.

( Should I be posting in the French forum ? )
 

Flenser

Senior Member
#8
Hippy,

Your suggestions are a good pickup. I'm going to do some peeksfr'ing to see what I can discover about the underlying configuration of the PIC timer.

It later occurred to me that the PICAXE firmware likely will need to setup a PIC hardware timer interrupt in order for the DOZE 1, 2, etc commands to work. How else can the chip be woken from the low power state at our chosen multiple of 2.1s?

Another test I did was to run the third code in my post #1 using DOZE 0, DOZE 1 and DOZE 2.
They all give the same terminal output with the "." character being output at the expected period of 1 sec. (I don't always capture the 7 "M" characters before the first "." character, depending upon how slow I am opening the terminal window.)
DOZE 0: MMMMMMMM.MMMMMMMM.MMMMMMMM.
DOZE 1: MMMMMMMM.MMMMMMMM.MMMMMMMM.
DOZE 2: MMMMMMMM.MMMMMMMM.MMMMMMMM.

So I'm wondering if the PIC hardware timer interrupt configuration that is done to setup a PICAXE interrupt on the timer 0 overflow conflicts with the PIC hardware timer interrupt configuration that needs to be done to wake the chip from the DOZE 1, 2, etc command?
The chip being woken from the DOZE 0 under these circumstances might then just be a side-effect of this conflict.
 

hippy

Technical Support
Staff member
#9
I don't off-hand recall how DOZE is actually implemented but I doubt it's anything as complicated as using timer interrupts. I would expect it's simply -
Code:
DozeCommand:
  n = <value>
  Do
    Sleep
    If n > 0 Then
      n = n - 1
      If n = 0 Then
        Return
      End If
    End If
  Loop
Now that should mean that DOZE 2 should only return every second wake up interupt and you'd be getting less M's, but it probably doesn't only return on 'n=0' but when there's an interrupt, a bit like PAUSE can be prematurely terminated. The Timer 1 interrupt itself will have premature terminated the SLEEP.

You are right there is a timer which wakes the PICAXE from being in sleep; the watchdog timer which times out 2.1 seconds after sleep is entered which brings it out of sleeping But the two interrupts are distinguishable. The WDT only ends the sleeping, doesn't do anything else.
 

Flenser

Senior Member
#10
Hippy,

The resuls of this test:
Code:
#NO_DATA
#NO_TABLE
#PICAXE 20X2
#TERMINAL 4800
SETFREQ M4

SETTIMER t1s_4

SYMBOL TMR1L    = $CE    ;    FCEh
SYMBOL TMR1H    = $CF    ;    FCFh        

Main:
    ;pause 1000
    PEEKSFR TMR1L, b0    ; must read TMR0L before TMR0H
    PEEKSFR TMR1H, b1    ; must read TMR0L before TMR0H
    SERTXD (b1, b0, timer, 13, 10)
    GOTO Main
Gives the following output, which is consistent with the model in your post #5:

[C3]L[00] <-- Start
[CA][B4][00]
[D2][1C][00]
[D9][83][00]
[E0][EB][00]
[E8]S[00]
[EF][BB][00]
[F7]"[00]
[FE][8A][00]
[C3][91][00] <-- Timer overflow #1
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #2
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #3
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #4
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #5
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #6
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][91][00] <-- Timer overflow #7
[CA][F9][00]
[D2]`[00]
[D9][C8][00]
[E1]0[00]
[E8][98][00]
[EF][FF][00]
[F7]g[00]
[FE][CF][00]
[C3][92][01] <-- Timer overflow #8 and timer value incrmented by 1
 
Last edited:

hippy

Technical Support
Staff member
#11
Gives the following output, which is consistent with the model in your post #5
I was expecting it would be. It's is a limitation of the hardware in the 20X2 and therefore the only way it could be made to work.

While the description of the details in the manual is therefore not completely accurate with respect to the 20X2 implementation, the overall description is accurate with respect to SETTIMER functionality, and all which comes with it.

Except for the internal interrupt which wakes DOZE up.

Without a DOZE an end user and programmer would never know there were more internal interrupts happening than on other PICAXE, and those would have bare minimum effects on execution times.

It's only the DOZE which makes the difference apparent.

There would seem to be three ways to deal with the issue. First to simply set a flag in the Interrupt routine so the DOZE isn't considered done until that flag is set ...
Code:
Main:
  Do
    dozeFlag = 0
    Do
        Doze 0
    Loop Until dozeFlag <> 0
    SerTxd( "M" )
  Loop

Interrupt:
  dozeFlag = 1
Or just use eight "DOZE 0" commands instead of one -
Code:
  Do
    Doze 0 : Doze 0 : Doze 0 : Doze 0
    Doze 0 : Doze 0 : Doze 0 : Doze 0
    SerTxd( "M" )
  Loop
Another would be to increase the SETTIMER pre-load count so it wakes from DOZE at the rate desired -
Code:
Main:
  SetFreq M4
  SetTimer 1
  Do
    Doze 0
    SerTxd( "M" )
  Loop
Note that you cannot use "SETTIMER 0" because that's "SETTIMER OFF". But also note you don't need to have an Interrupt routine or anything more than the above.

The above issues an "M" roughly every 524ms. "SETTIMER 32768" issues an "M" at twice that rate. It should be possible to produce a calculation to determine the pre-load for any given wake-up period desired, but I haven't quite figured it out yet.
 
Last edited:

Flenser

Senior Member
#12
Hippy,

Thanks for the suggestions.

I have a workaorund that will allow my PICAXE version of this scheduler to largely work as described for the original:
Code:
; Setup timer0 to overflow for the scheduler tick
SETTIMER t1ms_4
timer=65535
; Set a polled interrupt on the timer0 overflow
SETINTFLAGS %10000000,%10000000

main:
    doze 0

    ; The task dispatcher code runs in the main loop
    if runtask1=true then
        <task1 code.
        runtask1=false
    endif
    if runtask2=true then
        <task2 code.
        runtask2=false
    endif
    .. etc...
    goto main

interrupt:
    ; The task scheduler code runs in the interrupt handler
    if <task1 should run in this slot> then
        runtask1=true
    endif
    if <task2 should run in this slot> then
        runtask2=true
    endif
    ... etc...
    return
My PICAXE implementation will have a couple of extra limitations due to what we've discovered about the way the PICAXE timer interrupt works.
  • Because there are multiple PIC timer interrupts for each PICAXE timer interrupt you will get several smaller DOZE's rather than one long DOZE. This is likely only to be a consideration if you are running on batteries. It means that you won't get as much energy savings as you would from one DOZE 0. However during the extra times that the PIC interrupt causes the code to to go around the main loop it will only be executing the IF test code so the time the chip spends executing code could be quite small for each extra PIC interrupt
  • If the time to execute the code for a task varies, so that the number of extra PIC timer interrupts before the next PICAXE timer interrupt varies, and your PICAXE timer interrupt is short enough that the time it takes to execute the IF test code after those extra PIC timer interrupts is longer than the PIC timer interrupt period then this could introduce some extra jitter in the period your task is executed.
I plan to do a couple of extra posts to this thread but this will only be to add more detail of what I've discovered about the PIC timer interrupts that are associated with the PICAXE timer interrupt for people that read this post later on.
 

Flenser

Senior Member
#13
Hippy,

I am able to reconcile the values I get from the timer registers using PEEKSFR with both the PIC timer interrupt behaviour I observe and the model in your post #5

I do a PEEKSFR of the TxCON registers with a 1sec PICAXE timer interrupt set using this code:
Code:
SETTIMER t1s_4
timer=65535
SETINTFLAGS %10000000,%10000000

                       ;    SFR     Value
SYMBOL T0CON    = $D5  ;    FD5h    $FF
SYMBOL T1CON    = $CD  ;    FCDh    $B1
SYMBOL T2CON    = $CA  ;    FCAh    $00
SYMBOL T3CON    = $B1  ;    FB1h    $00

SYMBOL PIE1     = $9D  ;    F9Dh    $01
SYMBOL PIE2     = $A0  ;    FA0h    $00

Main:
    PEEKSFR T6CON, b0 
    SERTXD (b0)
    GOTO Main
  
interrupt:
    toflag=0
    timer=65535
    ; We need to re-enable our interrupt. This won't take effect until after we return.
    SETINTFLAGS %10000000,%10000000
    SERTXD (".")
    RETURN
The value $01 I get back from a PEEKSFR of the PIE1 register shows that a PIC timer interrupt is only set on timer1:
Code:
PIE1=$01
bit 0 TMR1IE: 1 = Timer 1 interrupt is enabled
bit 1 TMR2IE: 0 = Timer 2 interrupt is not enabled
The value $B1 I get back from a PEEKSFR of the T1CON register shows that timer1 is enabled, with a 1:8 prescale value and configured to use the Internal clock (FOSC/4):
Code:
T1CON = $B1 (10110001)
bit 7 RD16: 1 = Enables register read/write of TImer1 in one 16-bit operation
bit 6 T1RUN: 0 = Main system clock is derived from another source
bit 5-4 T1CKPS<1:0>: 11 = 1:8 Prescale value
bit 3 T1OSCEN: 0 = Timer1 oscillator is shut off
The oscillator inverter and feedback resistor are turned off to eliminate power drain.
bit 2 T1SYNC: When TMR1CS = 0: This bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.
bit 1 TMR1CS: 0 = Internal clock (FOSC/4)
bit 0 TMR1ON: 1 = Enables Timer1
 
Top