Page 1 of 1

Small PIT Frequency Error in MOD5213

Posted: Wed Sep 24, 2014 5:05 pm
by joepasquariello
Hello All,

While working on a MOD5213 that uses a DMA timer, I added code to report the number of DMA timer interrupts versus seconds as computed by the OS, and found there was a small discrepancy. Since the DMA timer and PIT are both driven by the same clock, and the values I had chosen for both divide evenly into the clock frequency, it seemed wrong that I wasn't getting precisely X interrupts for Y ticks of the OS. I've tracked down the issues, and they probably don't matter in 99.9% of all cases, but I thought I would report what I found, just in case in matters to someone else.

The NB appnote for PIT on MOD5213 has the correct formula for computing PIT timeout (T) as a function of system clock frequency (S), prescaler (P), and modulus (M):

T = [P * (M+1) * 2] / S

If you want to compute the modulus (M) to get a specific PIT interrupt frequency (F = 1/T):

F = 1/T = S / [P * (M+1) * 2]
M+1 = S / (P*2) / F = (S/2) / P / F
M = [(S/2) / P / F] - 1

The first issue I found is that MOD5213\system\bsp.c does not make the "-1" correction when it computes the modulus for a given value of TICKS_PER_SECOND, so the PIT period is 1 clock longer than it should be, and the PIT frequency is ever-so-slightly lower than intended. I fixed that issue, and the error in number of interrupts became smaller, but did not go away entirely.

The second issue is trickier, and I found it on a freescale community board.

PIT interrupts are cleared by writing PIF=1 in the PCSR register:

sim.pit[0].pcsr |= 0x0004;

However, the PCSR register is a word, so the code above generates word accesses, and the result is that the upper byte of PCSR, which contains the prescaler (PRE), is also written. Any time the prescaler is written, even if it's the same value, the prescaler counter is reset. Assuming this happens early in the ISR, the effect is small, but it cause the new PIT period to "start over", and to again be slightly longer than it should be. The code below writes only the LOW byte of PCSR, and it fixes the second problem:

((BYTE*)&sim.pit[0].pcsr)[1] |= 0x04;

With these two changes, I get the exact count of DMA timer interrupts per "second" as measured by the OS, and that's true for all values of PIT prescaler.

Here's the link to the message on the Freescale community board that explains the issue with clearing PIT interrupts:

https://community.freescale.com/thread/70487

Joe

Re: Small PIT Frequency Error in MOD5213

Posted: Wed Sep 24, 2014 5:38 pm
by joepasquariello
Here is the code from MOD5213\system\bsp.c, with my comments. I looked at MOD5234, and it has similar code, so if I'm correct about all of this, they all have the same minor issue.

void xTick()
{
asm(".global Tick5213");
asm(".extern OSTickISR");
asm(".extern nIsrCount");
asm("Tick5213:"); // start of PIT ISR
asm(" move.l %d0,-(%a7)");
asm(" move.w #0x60F,%d0"); // WORD value includes prescaler
asm(" move.w %d0,0x40150000"); // WORD write to PCSR, including prescaler
asm(" move.l (%a7)+,%d0");
asm(" jmp OSTickISR");
}

// from HardwareSetup()....

tv = ( CLOCK_FREQ / 128 );
tv /= TICKS_PER_SECOND;

sim.pit[0].pmr = ( WORD ) tv; // should be (WORD)(tv - 1)
BspTickMaxCount = ( WORD ) tv;
sim.pit[0].pcsr = 0x060F;

Re: Small PIT Frequency Error in MOD5213

Posted: Wed Sep 24, 2014 6:44 pm
by rnixon
Hey Joe, that is really interesting, thanks for taking the time to write it up. I've never would have thought an |= operation would have a side effect like that!

Re: Small PIT Frequency Error in MOD5213

Posted: Thu Sep 25, 2014 9:40 am
by joepasquariello
You're welcome. The new PIT-based API for OS_SEM (e.g. MOD5234\system\pitr_sem.cpp) has the correct formula for computing the PIT modulus, but it also has the issue of writing to the prescaler bits of PCSR when it clears the PIT interrupt. There are global variables that hold the values to write for pit[1] and pit[2], and those could be eliminated with the code shown below. This should work for the PIT on all modules.

existing code: sim.pit[1].pcsr = pit_pcsr_clr1;

proposed code: ((BYTE*)&sim.pit[1].pscr)[1] |= 0x04;

There is one more thing I want to mention, and that is the use of the OVW flag in the PCSR register. According to freescale appnote AN3400 (attached), if OVW=1 when writing to the PMR register, the value written to PMR is immediately loaded to PCNTR. If OVW=0, PCNTR is not loaded until the current period completes, so the first PIT period will not have the desired duration. According to the appnote, the process for initializing a PIT for continuous operation should be:

pcsr = prescaler
pcsr |= 0x1F (OVW=PIE=PIF=RLD=EN=1)
pmr = modulus (gets loaded immediately since OVW=1)

Joe

Re: Small PIT Frequency Error in MOD5213

Posted: Thu Sep 25, 2014 3:24 pm
by joepasquariello
Here's a 5213 program that shows the issue with clearing PIT interrupts. It configures PIT1 for periodic interrupts (f = TICKS_PER_SECOND), and DTIN3 as a free running counter (f = fsys/64 = 66335200/64 = 1036800 Hz).

In the ISR for PIT1, the free-running DTIN3 counter is read every TICKS_PER_SECOND ticks, and the number of 1036800-Hz clocks is calculated. Every 5 seconds, the ISR switches between clearing with WORD accesses and with BYTE accesses. When BYTE accesses are used, the number of DTIN3 clocks for TICKS_PER_SECOND ticks is exactly 1036800, which is what it should be. When WORD accesses are used to clear the PIT interrupt, and no other changes whatsoever, the number of DTIN3 clocks for the same number of PIT interrupts is about 1037265 clocks, or 465 more than expected, and is slightly variable.

Application started
PIT1 prescaler = 6, modulus = 2024

PIT interrupt cleared with WORD accesses
--------------------------------------------
Secs= 1, dtim3_delta= 1037472
Secs= 2, dtim3_delta= 1037265
Secs= 3, dtim3_delta= 1037266
Secs= 4, dtim3_delta= 1037265
Secs= 5, dtim3_delta= 1037266

PIT interrupt cleared with BYTE accesses
--------------------------------------------
Secs= 6, dtim3_delta= 1036838
Secs= 7, dtim3_delta= 1036800
Secs= 8, dtim3_delta= 1036800
Secs= 9, dtim3_delta= 1036800
Secs= 10, dtim3_delta= 1036800

Re: Small PIT Frequency Error in MOD5213

Posted: Thu Oct 02, 2014 12:49 pm
by joepasquariello
Here's a simpler MOD5213 test program that uses DMA timer 3 running at fsys/64 (1036800 Hz) to measure the period of TICKS_PER_SECOND uCOS ticks. If the PIT is operating as desired, the DMA timer should change by 1036800 counts every TICKS_PER_SECOND ticks. The DMA timer frequency is close to 1 MHz, so each count is about 1 us.

I ran the program with the 5 configurations listed below and recorded the results:

1) Default BSP.C with TICKS_PER_SECOND = 20 (each sec is about 50 us too long)
2) Default BSP.C with TICKS_PER_SECOND = 200 (each sec is about 500 us too long)
3) Set OVW=1 before setting value of PMR (fixes issue of first PIT period being much too long)
4) Modify ISR to write only lower byte of PCSR (fixes prescaler reset --> each sec only 400 us too long)
5) Add "- 1" to formula for modulus (fixes modules --> each sec exactly 1036800 counts)

Here are the measurements...

1) Default BSP.C w/ TICKS_PER_SECOND = 20
--> first set of OS ticks very long, others ~50us too long
TPS=20, PMR=25920
Secs= 1, dtim3_delta= 1072957
Secs= 2, dtim3_delta= 1036851
Secs= 3, dtim3_delta= 1036852
Secs= 4, dtim3_delta= 1036851
Secs= 5, dtim3_delta= 1036852

2) Default BSP.C w/ TICKS_PER_SECOND = 200
--> first set of OS ticks very long, others ~500 us too long
TPS=200, PMR=2592 (OVW=0)
Secs= 1, dtim3_delta= 1120072
Secs= 2, dtim3_delta= 1037303
Secs= 3, dtim3_delta= 1037300
Secs= 4, dtim3_delta= 1037300
Secs= 5, dtim3_delta= 1037300

3) Modify BSP.C to set OVW=1 before setting PMR register
--> first set of OS ticks shorter (as it should be), others still ~500 us too long
TPS=200, PMR=2592
Secs= 1, dtim3_delta= 1037075
Secs= 2, dtim3_delta= 1037303
Secs= 3, dtim3_delta= 1037300
Secs= 4, dtim3_delta= 1037300
Secs= 5, dtim3_delta= 1037300

4) Modify BSP.C to write only the LOWER byte of PCSR when clearing PIT interrupt.
--> first set of OS ticks shorter (as it should be), others now ~400 us too long
TPS=200, PMR=2592
Secs= 1, dtim3_delta= 1036964
Secs= 2, dtim3_delta= 1037200
Secs= 3, dtim3_delta= 1037200
Secs= 4, dtim3_delta= 1037200
Secs= 5, dtim3_delta= 1037200

5) Modify BSP.C to include "- 1" in formula for PIT modulus written to PMR register.
--> first set of OS ticks shorter (as it should be), others now exactly 1036800
TPS=200, PMR=2591
Secs= 1, dtim3_delta= 1036559
Secs= 2, dtim3_delta= 1036800
Secs= 3, dtim3_delta= 1036800
Secs= 4, dtim3_delta= 1036800
Secs= 5, dtim3_delta= 1036800