PIT hw boo-boo. Read if you need accurate PIT (CF)

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
已解决

PIT hw boo-boo. Read if you need accurate PIT (CF)

跳至解决方案
7,122 次查看
MrBean
Contributor I

In short:  Do not use the PIT presaler !  (if you want an accurate PIT)

 

 

 

I noticed a small but precise offset between my PIT and RTC (both running from Fsys).

 

We have a 60MHz Fsys.

The prescaler was at 4 (2^4=16), PMR at 1875-1=1874. RLD bit set.

This should yield 1.000000ms. It doesnt, 1.00006666667ms is real.

 

 

Why ?  :

 

There is a hit in the ref. manual stating that the prescaler counter is reset at PCNT reload.

It is not reset to 0 but to all 1's. (Mind: the prescaler-counter, you cannot reach that, you can just select an output of it.)

 

This makes the PIT count to 1875.125 in our case.

After a PCNT reload from PMR the prescaler is reset, so the PIT-counter clock is 1/8th more (in this case) of what one would expect, just in one of the 1875 clocks.

This is always 2* 1/(Fsys/2) (aka 2 busclock cycles). I guess it is the reload itself.

 

The only way to correct it is to adjust PMR with 1/(2^(PRE[3:0])).

If PRE>0 you would need to adjust PMR with a fraction, so that can only be done with PRE=0

 

This renders the prescaler useless.

A shame because with PRE=4 we save 2mA because the PIT counter is not running at 30Mhz.

 

PS: for clarity: In our case we need to have PRE=0 and PMR=29998  to get 1.000000ms exactly.

 

To my knowledge this boo-boo is present in all MCF52223 and MCF52235

But, i expect it to be present in all ColdFires with a PIT.

标签 (1)
0 项奖励
回复
1 解答
4,228 次查看
TomE
Specialist II

I've run some more tests, think I have found the problem and a workaround.

 

The PIT and the Reference Manual don't seem to agree. This problem may need Errata for all Coldfire chips with a PIT that works this way.

 

The problem ISN'T specifically the prescaler. I don't think that the fix proposed by MrBean would work in all cases. I think he's seeing a testing artifact as it depends on the phase of when register writing is happening relative to the prescaler clocking.

 

In my first test, I ran a PIT against a DMA timer and once per second read and printed the DMA counter and the PIT counter. They kept perfect time.

 

In my second test, I did the same as the first, but cleared the MCF_PIT_PCSR_PIF bit in the PCSR like you have to do when using the PIT to generate interrupts. The PIT went haywire. It lost 2000 counts per second or about one count per reload cycle.

 

MrBean thought the loss-of-count is caused by the PCNT Reload, even "automatic" ones. It seems to be caused by the PCSR_PIF clear. Actually it is caused by the USUAL CODE that is used to clear the PCSR_PIF bit. Which is the wrong way to do it. Which is the only way supported by the Freescale Header files provided for these chips. Details on this later.

 

Here's the result or programming FOUR PITs at the same time, all set to 500us timeout, but using /1, /2, /4 and /8 prescaler (code provided later). Once per second the code prints out the current values of the MCF_PIT[0123]_PCNTR registers:

 

 

Test 0, PIF clear by Byte Write to PCSRPITs now at 0x9B5E, 0x4DCD, 0x26F8, 0x1385PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387(All the PIT Counters are the same in this test)

Test 1, PIF clear by Word Write to PCSRPITs now at 0x2332, 0x11BE, 0x08F0, 0x0482PITs now at 0x06E1, 0x0395, 0x0223, 0x04D9PITs now at 0x0EAC, 0x077A, 0x045E, 0x09B7PITs now at 0x1671, 0x0B5C, 0x0696, 0x0E94PITs now at 0x1E3E, 0x0F42, 0x08D0, 0x1371PITs now at 0x2607, 0x1326, 0x0B0B, 0x04C6PITs now at 0x2DD0, 0x170A, 0x0D44, 0x09A3PITs now at 0x3599, 0x1AEE, 0x0F7E, 0x0E81PITs now at 0x3D62, 0x1ED2, 0x11B8, 0x135FPITs now at 0x452A, 0x22B5, 0x13F1, 0x04B4(All the PIT counters are drifting, not keeping time)

Test 2, PIF clear by Write to PMRPITs now at 0x79CE, 0x3D07, 0x2161, 0x0F2DPITs now at 0x54B9, 0x2A7B, 0x17EC, 0x0993PITs now at 0x5C80, 0x2E60, 0x19AC, 0x0995PITs now at 0x644D, 0x3245, 0x1B6F, 0x0996PITs now at 0x6C14, 0x362A, 0x1D2E, 0x0996PITs now at 0x73E1, 0x3A0F, 0x1EF0, 0x0996PITs now at 0x7BA8, 0x3DF4, 0x20AF, 0x099BPITs now at 0x8375, 0x41D9, 0x2270, 0x099BPITs now at 0x8B3C, 0x45BE, 0x2430, 0x09A1PITs now at 0x9308, 0x49A3, 0x25F0, 0x09A1

The four PITs are being run at 80MHz. They're set up to time out in 500us. Thus they've counted 2000 times in the one second between the printout lines above.

 

 

Test ZERO shows no loss of counts. The counters are the same values on successive seconds. This is the way it should work.

 

Test ONE is losing counts. The counters are different on subsequent seconds The test code is resetting the PIF with the following code:

 

 

    MCF_PIT_PCSR(nPit) |= MCF_PIT_PCSR_PIF;

That is writing to the whole PCSR and is thus rewriting the PRE bits This shouldn't cause any problems if the Hardware did what the Reference Manual says it did, which is:

 

 

28.2.1 PIT Control and Status Register (PCSRn)Changing PRE[3:0] resets the prescaler counter.

 

The above code isn't "changing PRE[3:0]". It is reading the PCSE, setting the PIF bit and then writing the SAME value back into the PCSR. It is WRITING to PRE but it isn't CHANGING its value.

 

 

But the hardware is reacting to the WRITE, even though the value isn't being changed.

 

Test TWO shows the counters being different on subsequent seconds.I would expect the prescaler to be reset if I had the PCSRn[OVW] bit set, but I don't. This should be a safe way to clear the PIF bit as it says in the manual:

 

28.2.1 PIT Control and Status Register (PCSRn)
...
Clear PIF by writing a 1 to it or by writing to PMR.

But it looks like writing to the PMR does mess up the Prescaler. It shouldn't.

 

So why is Test ZERO working? It is writing to the lower byte of the PCSR registers instead of writing to the whole 16 bits. Freescale provide header files defining the PIT registers, including the following:

 

 

 * File:    mcf532x_pit.h * Purpose: Register and bit definitions.../* Register read/write macros */#define MCF_PIT0_PCSR        (*(vuint16*)(0xFC080000))#define MCF_PIT0_PMR         (*(vuint16*)(0xFC080002))#define MCF_PIT0_PCNTR       (*(vuint16*)(0xFC080004))#define MCF_PIT1_PCSR        (*(vuint16*)(0xFC084000))...#define MCF_PIT_PCSR(x)      (*(vuint16*)(0xFC080000+((x)*0x4000)))#define MCF_PIT_PMR(x)       (*(vuint16*)(0xFC080002+((x)*0x4000)))#define MCF_PIT_PCNTR(x)     (*(vuint16*)(0xFC080004+((x)*0x4000)))

 

There are no macros to allow the essential byte-write to the PCSR. In order to do this I had to write and use the following:

 

 

#define MCF_PIT_PCSR_LB(x)      (*(vuint8*)(0xFC080001+((x)*0x4000)))

 

    MCF_PIT_PCSR_LB(nPit) |= MCF_PIT_PCSR_PIF

 

So the WORKAROUND is to clear the PIF bit with a BYTE WRITE.

 

Where else might there be code performing word-writes to the PIT PCSR? It looks like Linux does it this way, and I suspect many/all of the other operating systems do too:

 

http://lxr.free-electrons.com/source/arch/m68knommu/platform/coldfire/pit.c?v=2.6.32

 

Here's the test code. It is all pretty straightforward. In my case the "DEBUG() macro prints. Replace it with whatever prints in your code. DMA Timer 3 is beng used as the "master timer". "asm_set_ipl(7)" disables interrupts.

 

 

#define COUNT_US (MCF_DTIM3_DTCN)#define DELAY_US(us) nTimeUs = COUNT_US; while ((COUNT_US - nTimeUs) < us) { ; }#define MCF_PIT_PCSR_LB(x)      (*(vuint8*)(0xFC080001+((x)*0x4000)))void pitTest(void){ uint16_t old_ipl = asm_set_ipl(7); uint32_t nTimeUs; uint32_t nCount; uint32_t nTest; uint32_t nPit; uint16_t nPcsr; uint16_t nPcntr[4]; /* Initialise DMA Timer 3 to free-run at 1us as a timebase */ MCF_DTIM3_DTMR = MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_FRR |        MCF_DTIM_DTMR_CE_NONE | MCF_DTIM_DTMR_RST |  MCF_DTIM_DTMR_PS( 80 - 1 ); /* 80MHz/80 = 1MHz count */ /* Program all pits to 500us timeout, 4 different dividers */ MCF_PIT0_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT1_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT2_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT3_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT0_PMR = (40000 - 1); MCF_PIT1_PMR = (20000 - 1); MCF_PIT2_PMR = (10000 - 1); MCF_PIT3_PMR = (5000 - 1); nPcsr = MCF_PIT_PCSR_HALTED | MCF_PIT_PCSR_PIF |   MCF_PIT_PCSR_RLD | MCF_PIT_PCSR_EN; /* Enable them all, staggered at 1us */ MCF_PIT0_PCSR = nPcsr | MCF_PIT_PCSR_PRE(0); /* Prescale off */ DELAY_US(1); MCF_PIT1_PCSR = nPcsr | MCF_PIT_PCSR_PRE(1); /* Prescale /2 */ DELAY_US(1); MCF_PIT2_PCSR = nPcsr | MCF_PIT_PCSR_PRE(2); /* Prescale /4 */ DELAY_US(1); MCF_PIT3_PCSR = nPcsr | MCF_PIT_PCSR_PRE(3); /* Prescale /8 */ nTimeUs = COUNT_US - (1000000L - 50000L);  /* Start in 50ms */ for (nTest = 0; nTest < 3; nTest++) {  DEBUG(0, "Test %d, PIF clear by %s", nTest,   (nTest == 0) ? "Byte Write to PCSR" :   (nTest == 1) ? "Word Write to PCSR" :       "Write to PMR");  for (nCount = 0; nCount < 10; nCount++)  {   /* Wait for the rest of the second to pass */   while ((COUNT_US - nTimeUs) < 1000000L)   {    ;   }   /* Read and print current counters */   nPcntr[0] = MCF_PIT0_PCNTR;   nPcntr[1] = MCF_PIT1_PCNTR;   nPcntr[2] = MCF_PIT2_PCNTR;   nPcntr[3] = MCF_PIT3_PCNTR;   DEBUG(0, "PITs now at 0x%04x, 0x%04x, 0x%04x, 0x%04x" /* = %4d, %4d, %4d */,    nPcntr[0], nPcntr[1], nPcntr[2], nPcntr[3] /*,    nPcntr[1] - nPcntr[0], nPcntr[2] - nPcntr[0], nPcntr[3] - nPcntr[0] */);   nTimeUs += 1000000L; /* Set up for next time */   /* Wait for nearly a second to pass */   while ((COUNT_US - nTimeUs) < 999950L)   {    MCF_WTM_WSR=0x5555; /* Watchdog is running, pat it */    MCF_WTM_WSR=0xAAAA;    for (nPit = 0; nPit < 4; nPit++)    {     if ((MCF_PIT_PCSR(nPit) & MCF_PIT_PCSR_PIF) != 0)     {      if (nTest == 0)  /* Reset PIF by byte-write to PCSR */      {       MCF_PIT_PCSR_LB(nPit) |= MCF_PIT_PCSR_PIF;      }      else if (nTest == 1)/* Reset PIF by word-write to PCSR */      {       MCF_PIT_PCSR(nPit) |= MCF_PIT_PCSR_PIF;      }      else    /* Reset PIF by reloading PMR */      {       MCF_PIT_PMR(nPit) = MCF_PIT_PMR(nPit);      }     }    }   }  } } asm_set_ipl(old_ipl); DEBUG(0, "Test done, locking up..."); while (TRUE) {  MCF_WTM_WSR=0x5555;  /* Watchdog is running, pat it */  MCF_WTM_WSR=0xAAAA;  }}

 

 

 

 

 

 

 

在原帖中查看解决方案

0 项奖励
回复
13 回复数
4,228 次查看
TomE
Specialist II

When the forum posts were "converted", all "Code" sections got mangled.

Here are relevant ones (manually) reformatted in case they are useful for anyone wanting to run their own tests.

Tom Evans on Oct 11, 2010 10:19 PM

Here's the result or programming FOUR PITs at

the same time, all set to 500us timeout, but

using /1, /2, /4 and /8 prescaler (code provided later).

Once per second the code prints out the current values

of the MCF_PIT[0123]_PCNTR registers:

Test 0, PIF clear by Byte Write to PCSR

PITs now at 0x9B5E, 0x4DCD, 0x26F8, 0x1385

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387

(All the PIT Counters are the same in this test)

Test 1, PIF clear by Word Write to PCSR

PITs now at 0x2332, 0x11BE, 0x08F0, 0x0482

PITs now at 0x06E1, 0x0395, 0x0223, 0x04D9

PITs now at 0x0EAC, 0x077A, 0x045E, 0x09B7

PITs now at 0x1671, 0x0B5C, 0x0696, 0x0E94

PITs now at 0x1E3E, 0x0F42, 0x08D0, 0x1371

PITs now at 0x2607, 0x1326, 0x0B0B, 0x04C6

PITs now at 0x2DD0, 0x170A, 0x0D44, 0x09A3

PITs now at 0x3599, 0x1AEE, 0x0F7E, 0x0E81

PITs now at 0x3D62, 0x1ED2, 0x11B8, 0x135F

PITs now at 0x452A, 0x22B5, 0x13F1, 0x04B4

(All the PIT counters are drifting, not keeping time)

Test 2, PIF clear by Write to PMR

PITs now at 0x79CE, 0x3D07, 0x2161, 0x0F2D

PITs now at 0x54B9, 0x2A7B, 0x17EC, 0x0993

PITs now at 0x5C80, 0x2E60, 0x19AC, 0x0995

PITs now at 0x644D, 0x3245, 0x1B6F, 0x0996

PITs now at 0x6C14, 0x362A, 0x1D2E, 0x0996

PITs now at 0x73E1, 0x3A0F, 0x1EF0, 0x0996

PITs now at 0x7BA8, 0x3DF4, 0x20AF, 0x099B

PITs now at 0x8375, 0x41D9, 0x2270, 0x099B

PITs now at 0x8B3C, 0x45BE, 0x2430, 0x09A1

PITs now at 0x9308, 0x49A3, 0x25F0, 0x09A1

The code that generated the above:

#define COUNT_US (MCF_DTIM3_DTCN)

#define DELAY_US(us) nTimeUs = COUNT_US; while ((COUNT_US - nTimeUs) < us) { ; }

#define MCF_PIT_PCSR_LB(x)      (*(vuint8*)(0xFC080001+((x)*0x4000)))

void pitTest(void)

{

    uint16_t old_ipl = asm_set_ipl(7);

    uint32_t nTimeUs;

    uint32_t nCount;

    uint32_t nTest;

    uint32_t nPit;

    uint16_t nPcsr;

    uint16_t nPcntr[4];

    /* Initialise DMA Timer 3 to free-run at 1us as a timebase */

    MCF_DTIM3_DTMR = MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_FRR |

        MCF_DTIM_DTMR_CE_NONE | MCF_DTIM_DTMR_RST |  MCF_DTIM_DTMR_PS( 80 - 1 );

    /* 80MHz/80 = 1MHz count */

    /* Program all pits to 500us timeout, 4 different dividers */

    MCF_PIT0_PCSR = MCF_PIT_PCSR_OVW;

    MCF_PIT1_PCSR = MCF_PIT_PCSR_OVW;

    MCF_PIT2_PCSR = MCF_PIT_PCSR_OVW;

    MCF_PIT3_PCSR = MCF_PIT_PCSR_OVW;

    MCF_PIT0_PMR = (40000 - 1);

    MCF_PIT1_PMR = (20000 - 1);

    MCF_PIT2_PMR = (10000 - 1);

    MCF_PIT3_PMR = (5000 - 1);

    nPcsr = MCF_PIT_PCSR_HALTED | MCF_PIT_PCSR_PIF |

         MCF_PIT_PCSR_RLD | MCF_PIT_PCSR_EN;

    /* Enable them all, staggered at 1us */

    MCF_PIT0_PCSR = nPcsr | MCF_PIT_PCSR_PRE(0);

    /* Prescale off */

    DELAY_US(1);

    MCF_PIT1_PCSR = nPcsr | MCF_PIT_PCSR_PRE(1);

    /* Prescale /2 */

    DELAY_US(1);

    MCF_PIT2_PCSR = nPcsr | MCF_PIT_PCSR_PRE(2);

    /* Prescale /4 */

    DELAY_US(1);

    MCF_PIT3_PCSR = nPcsr | MCF_PIT_PCSR_PRE(3);

    /* Prescale /8 */

    nTimeUs = COUNT_US - (1000000L - 50000L);

     /* Start in 50ms */

    for (nTest = 0; nTest < 3; nTest++)

    {

        DEBUG(0, "Test %d, PIF clear by %s", nTest,

            (nTest == 0) ? "Byte Write to PCSR" :

            (nTest == 1) ? "Word Write to PCSR" :

            "Write to PMR");

        for (nCount = 0; nCount < 10; nCount++)

        {

            /* Wait for the rest of the second to pass */

            while ((COUNT_US - nTimeUs) < 1000000L)

            {

                 ;

            }

            /* Read and print current counters */

            nPcntr[0] = MCF_PIT0_PCNTR;

            nPcntr[1] = MCF_PIT1_PCNTR;

            nPcntr[2] = MCF_PIT2_PCNTR;

            nPcntr[3] = MCF_PIT3_PCNTR;

            DEBUG(0, "PITs now at 0x%04x, 0x%04x, 0x%04x, 0x%04x"

                      /* = %4d, %4d, %4d */,

                      nPcntr[0], nPcntr[1], nPcntr[2], nPcntr[3]

                      /*, nPcntr[1] - nPcntr[0],

                      nPcntr[2] - nPcntr[0], nPcntr[3] - nPcntr[0] */);

            nTimeUs += 1000000L;

            /* Set up for next time */

            /* Wait for nearly a second to pass */

            while ((COUNT_US - nTimeUs) < 999950L)

            {

                MCF_WTM_WSR=0x5555;

                /* Watchdog is running, pat it */

                MCF_WTM_WSR=0xAAAA;

                for (nPit = 0; nPit < 4; nPit++)

                {

                    if ((MCF_PIT_PCSR(nPit) & MCF_PIT_PCSR_PIF) != 0)

                    {

                        if (nTest == 0)  /* Reset PIF by byte-write to PCSR */

                        {

                            MCF_PIT_PCSR_LB(nPit) |= MCF_PIT_PCSR_PIF;

                        }

                        else if (nTest == 1)/* Reset PIF by word-write to PCSR */

                        {

                            MCF_PIT_PCSR(nPit) |= MCF_PIT_PCSR_PIF;

                        }

                        else /* Reset PIF by reloading PMR */

                        {

                            MCF_PIT_PMR(nPit) = MCF_PIT_PMR(nPit);

                        }

                    }

                }

            }

        }

    }

    asm_set_ipl(old_ipl);

    DEBUG(0, "Test done, locking up...");

    while (TRUE)

    {

        MCF_WTM_WSR=0x5555;

        /* Watchdog is running, pat it */

        MCF_WTM_WSR=0xAAAA;

    }

}

Tom Evans Oct 12, 2010 12:38 AM

PIT is 80MHz / 32768 = 2441.4Hz or 409.6us

Modulus is "1" meaning "divide by 2".

Measuring 5000 cycles, expected value 4.096 seconds:

Test 0, PIF clear by Byte Write to PCSR

000.000 pitTest:460 Test 0 took 4096000us

Test 1, PIF clear by Byte Write to PCSR with Delay

000.000 pitTest:460 Test 1 took 4096000us

Test 2, PIF clear by Word Write to PCSR

000.000 pitTest:460 Test 2 took 4097562us

Test 3, PIF clear by Word Write to PCSR with Delay

000.000 pitTest:460 Test 3 took 5094800us

The "normal word write" resulted in an error of

4097.562/4096 = 1.00038 = 0.038% = 381ppm.

That's 11 counts in 32k, so the code took 11 clocks

to write to the PCSR and inadvertently reset the prescaler.

Waiting for 200us (out of the 491us period) and then

performing the word write resulted in an error of

5094.8/4096 = 1.2438 = 24% = 243,847ppm, which

might be a little more obvious!

Tom Evans Oct 31, 2010 6:58 AM

I followed your advice, and after 3 weeks received the following:

I talked with the apps team and they told me that the

redaction doesn’t seems to implies that writing the

same value to the PRE bits doesn't reset the timer.

Anyway they will review and make any necessary change

if necessary. Thanks in advance for your feedback.

Have a nice day.

Tom Evans Mar 1, 2011 1:57 AM

This is a gotcha that is addressed in AN3400, but only

MCF5213 users would be expected to find that App Note.

It would help if it AN3400 was referenced from all

Freescale Product Documentation Web Pages for all

Coldfire chips that have this PIT in it.

That would be simple to do, but hasn't been done either.


0 项奖励
回复
4,228 次查看
TomE
Specialist II

I set up to test this on our MCF5329, but since we're not using the RTC I decided to compare two PITs running from different scaling factors.

 

But first I tested them on the SAME scaling factor.

 

I'm running two PITs (PIT0 and PIT3) on Scaling factor "2" and Modulus 39999 from an 80MHz clock. That's a 1ms timeout for both of them.

 

Both are programmed identically and started at the same time. They drift apart! The counters have drifted apart by about 1684 counts in 2 minutes. That's 350PPB. I'll have to test this properly on Monday and look for any software bugs that might be causing this.

 

Meanwhile, can you test two PITs against each other? Just program them the same, start them at the same time and periodically compare the count registers.

0 项奖励
回复
4,229 次查看
TomE
Specialist II

I've run some more tests, think I have found the problem and a workaround.

 

The PIT and the Reference Manual don't seem to agree. This problem may need Errata for all Coldfire chips with a PIT that works this way.

 

The problem ISN'T specifically the prescaler. I don't think that the fix proposed by MrBean would work in all cases. I think he's seeing a testing artifact as it depends on the phase of when register writing is happening relative to the prescaler clocking.

 

In my first test, I ran a PIT against a DMA timer and once per second read and printed the DMA counter and the PIT counter. They kept perfect time.

 

In my second test, I did the same as the first, but cleared the MCF_PIT_PCSR_PIF bit in the PCSR like you have to do when using the PIT to generate interrupts. The PIT went haywire. It lost 2000 counts per second or about one count per reload cycle.

 

MrBean thought the loss-of-count is caused by the PCNT Reload, even "automatic" ones. It seems to be caused by the PCSR_PIF clear. Actually it is caused by the USUAL CODE that is used to clear the PCSR_PIF bit. Which is the wrong way to do it. Which is the only way supported by the Freescale Header files provided for these chips. Details on this later.

 

Here's the result or programming FOUR PITs at the same time, all set to 500us timeout, but using /1, /2, /4 and /8 prescaler (code provided later). Once per second the code prints out the current values of the MCF_PIT[0123]_PCNTR registers:

 

 

Test 0, PIF clear by Byte Write to PCSRPITs now at 0x9B5E, 0x4DCD, 0x26F8, 0x1385PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387PITs now at 0x9B5D, 0x4DD3, 0x26FB, 0x1387(All the PIT Counters are the same in this test)

Test 1, PIF clear by Word Write to PCSRPITs now at 0x2332, 0x11BE, 0x08F0, 0x0482PITs now at 0x06E1, 0x0395, 0x0223, 0x04D9PITs now at 0x0EAC, 0x077A, 0x045E, 0x09B7PITs now at 0x1671, 0x0B5C, 0x0696, 0x0E94PITs now at 0x1E3E, 0x0F42, 0x08D0, 0x1371PITs now at 0x2607, 0x1326, 0x0B0B, 0x04C6PITs now at 0x2DD0, 0x170A, 0x0D44, 0x09A3PITs now at 0x3599, 0x1AEE, 0x0F7E, 0x0E81PITs now at 0x3D62, 0x1ED2, 0x11B8, 0x135FPITs now at 0x452A, 0x22B5, 0x13F1, 0x04B4(All the PIT counters are drifting, not keeping time)

Test 2, PIF clear by Write to PMRPITs now at 0x79CE, 0x3D07, 0x2161, 0x0F2DPITs now at 0x54B9, 0x2A7B, 0x17EC, 0x0993PITs now at 0x5C80, 0x2E60, 0x19AC, 0x0995PITs now at 0x644D, 0x3245, 0x1B6F, 0x0996PITs now at 0x6C14, 0x362A, 0x1D2E, 0x0996PITs now at 0x73E1, 0x3A0F, 0x1EF0, 0x0996PITs now at 0x7BA8, 0x3DF4, 0x20AF, 0x099BPITs now at 0x8375, 0x41D9, 0x2270, 0x099BPITs now at 0x8B3C, 0x45BE, 0x2430, 0x09A1PITs now at 0x9308, 0x49A3, 0x25F0, 0x09A1

The four PITs are being run at 80MHz. They're set up to time out in 500us. Thus they've counted 2000 times in the one second between the printout lines above.

 

 

Test ZERO shows no loss of counts. The counters are the same values on successive seconds. This is the way it should work.

 

Test ONE is losing counts. The counters are different on subsequent seconds The test code is resetting the PIF with the following code:

 

 

    MCF_PIT_PCSR(nPit) |= MCF_PIT_PCSR_PIF;

That is writing to the whole PCSR and is thus rewriting the PRE bits This shouldn't cause any problems if the Hardware did what the Reference Manual says it did, which is:

 

 

28.2.1 PIT Control and Status Register (PCSRn)Changing PRE[3:0] resets the prescaler counter.

 

The above code isn't "changing PRE[3:0]". It is reading the PCSE, setting the PIF bit and then writing the SAME value back into the PCSR. It is WRITING to PRE but it isn't CHANGING its value.

 

 

But the hardware is reacting to the WRITE, even though the value isn't being changed.

 

Test TWO shows the counters being different on subsequent seconds.I would expect the prescaler to be reset if I had the PCSRn[OVW] bit set, but I don't. This should be a safe way to clear the PIF bit as it says in the manual:

 

28.2.1 PIT Control and Status Register (PCSRn)
...
Clear PIF by writing a 1 to it or by writing to PMR.

But it looks like writing to the PMR does mess up the Prescaler. It shouldn't.

 

So why is Test ZERO working? It is writing to the lower byte of the PCSR registers instead of writing to the whole 16 bits. Freescale provide header files defining the PIT registers, including the following:

 

 

 * File:    mcf532x_pit.h * Purpose: Register and bit definitions.../* Register read/write macros */#define MCF_PIT0_PCSR        (*(vuint16*)(0xFC080000))#define MCF_PIT0_PMR         (*(vuint16*)(0xFC080002))#define MCF_PIT0_PCNTR       (*(vuint16*)(0xFC080004))#define MCF_PIT1_PCSR        (*(vuint16*)(0xFC084000))...#define MCF_PIT_PCSR(x)      (*(vuint16*)(0xFC080000+((x)*0x4000)))#define MCF_PIT_PMR(x)       (*(vuint16*)(0xFC080002+((x)*0x4000)))#define MCF_PIT_PCNTR(x)     (*(vuint16*)(0xFC080004+((x)*0x4000)))

 

There are no macros to allow the essential byte-write to the PCSR. In order to do this I had to write and use the following:

 

 

#define MCF_PIT_PCSR_LB(x)      (*(vuint8*)(0xFC080001+((x)*0x4000)))

 

    MCF_PIT_PCSR_LB(nPit) |= MCF_PIT_PCSR_PIF

 

So the WORKAROUND is to clear the PIF bit with a BYTE WRITE.

 

Where else might there be code performing word-writes to the PIT PCSR? It looks like Linux does it this way, and I suspect many/all of the other operating systems do too:

 

http://lxr.free-electrons.com/source/arch/m68knommu/platform/coldfire/pit.c?v=2.6.32

 

Here's the test code. It is all pretty straightforward. In my case the "DEBUG() macro prints. Replace it with whatever prints in your code. DMA Timer 3 is beng used as the "master timer". "asm_set_ipl(7)" disables interrupts.

 

 

#define COUNT_US (MCF_DTIM3_DTCN)#define DELAY_US(us) nTimeUs = COUNT_US; while ((COUNT_US - nTimeUs) < us) { ; }#define MCF_PIT_PCSR_LB(x)      (*(vuint8*)(0xFC080001+((x)*0x4000)))void pitTest(void){ uint16_t old_ipl = asm_set_ipl(7); uint32_t nTimeUs; uint32_t nCount; uint32_t nTest; uint32_t nPit; uint16_t nPcsr; uint16_t nPcntr[4]; /* Initialise DMA Timer 3 to free-run at 1us as a timebase */ MCF_DTIM3_DTMR = MCF_DTIM_DTMR_CLK_DIV1 | MCF_DTIM_DTMR_FRR |        MCF_DTIM_DTMR_CE_NONE | MCF_DTIM_DTMR_RST |  MCF_DTIM_DTMR_PS( 80 - 1 ); /* 80MHz/80 = 1MHz count */ /* Program all pits to 500us timeout, 4 different dividers */ MCF_PIT0_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT1_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT2_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT3_PCSR = MCF_PIT_PCSR_OVW; MCF_PIT0_PMR = (40000 - 1); MCF_PIT1_PMR = (20000 - 1); MCF_PIT2_PMR = (10000 - 1); MCF_PIT3_PMR = (5000 - 1); nPcsr = MCF_PIT_PCSR_HALTED | MCF_PIT_PCSR_PIF |   MCF_PIT_PCSR_RLD | MCF_PIT_PCSR_EN; /* Enable them all, staggered at 1us */ MCF_PIT0_PCSR = nPcsr | MCF_PIT_PCSR_PRE(0); /* Prescale off */ DELAY_US(1); MCF_PIT1_PCSR = nPcsr | MCF_PIT_PCSR_PRE(1); /* Prescale /2 */ DELAY_US(1); MCF_PIT2_PCSR = nPcsr | MCF_PIT_PCSR_PRE(2); /* Prescale /4 */ DELAY_US(1); MCF_PIT3_PCSR = nPcsr | MCF_PIT_PCSR_PRE(3); /* Prescale /8 */ nTimeUs = COUNT_US - (1000000L - 50000L);  /* Start in 50ms */ for (nTest = 0; nTest < 3; nTest++) {  DEBUG(0, "Test %d, PIF clear by %s", nTest,   (nTest == 0) ? "Byte Write to PCSR" :   (nTest == 1) ? "Word Write to PCSR" :       "Write to PMR");  for (nCount = 0; nCount < 10; nCount++)  {   /* Wait for the rest of the second to pass */   while ((COUNT_US - nTimeUs) < 1000000L)   {    ;   }   /* Read and print current counters */   nPcntr[0] = MCF_PIT0_PCNTR;   nPcntr[1] = MCF_PIT1_PCNTR;   nPcntr[2] = MCF_PIT2_PCNTR;   nPcntr[3] = MCF_PIT3_PCNTR;   DEBUG(0, "PITs now at 0x%04x, 0x%04x, 0x%04x, 0x%04x" /* = %4d, %4d, %4d */,    nPcntr[0], nPcntr[1], nPcntr[2], nPcntr[3] /*,    nPcntr[1] - nPcntr[0], nPcntr[2] - nPcntr[0], nPcntr[3] - nPcntr[0] */);   nTimeUs += 1000000L; /* Set up for next time */   /* Wait for nearly a second to pass */   while ((COUNT_US - nTimeUs) < 999950L)   {    MCF_WTM_WSR=0x5555; /* Watchdog is running, pat it */    MCF_WTM_WSR=0xAAAA;    for (nPit = 0; nPit < 4; nPit++)    {     if ((MCF_PIT_PCSR(nPit) & MCF_PIT_PCSR_PIF) != 0)     {      if (nTest == 0)  /* Reset PIF by byte-write to PCSR */      {       MCF_PIT_PCSR_LB(nPit) |= MCF_PIT_PCSR_PIF;      }      else if (nTest == 1)/* Reset PIF by word-write to PCSR */      {       MCF_PIT_PCSR(nPit) |= MCF_PIT_PCSR_PIF;      }      else    /* Reset PIF by reloading PMR */      {       MCF_PIT_PMR(nPit) = MCF_PIT_PMR(nPit);      }     }    }   }  } } asm_set_ipl(old_ipl); DEBUG(0, "Test done, locking up..."); while (TRUE) {  MCF_WTM_WSR=0x5555;  /* Watchdog is running, pat it */  MCF_WTM_WSR=0xAAAA;  }}

 

 

 

 

 

 

 

0 项奖励
回复
4,228 次查看
TomE
Specialist II

If you're using a small prescaler and a modulus between 30,000 and 60,000 then the expected error is "only" going to be one part in 30,000 to 60,000. Given that 2 seconds per day is one part in 43,200 you might not notice this when comparing with an external clock. That's the crystal accuracy anyway.

 

If you're trying to get a PIT timer to remain phase-locked with a DMA Timer or a PWM or even another PIT timer you may have these problems.

 

But how about a 24% error? This is possible.

 

It gets a lot worse if you're using a large prescaler value, a small modulus (to save power pehaps like MrBean is doing) and also have an interrupt service routine that is coded like:

 

 

pit_n_isr(){    Do something that takes a long time, like half the prescaler time
    ...
    MCF_PIT_PCSR(n) |= MCF_PIT_PCSR_PIF;
}

 

 

 In that case the PIT interrupt rate can be a LOT slower than you might expect:

 

 

PIT is 80MHz / 32768 = 2441.4Hz or 409.6us
Modulus is "1" meaning "divide by 2".
Measuring 5000 cycles, expected value 4.096 seconds:

Test 0, PIF clear by Byte Write to PCSR000.000 pitTest:460 Test 0 took 4096000usTest 1, PIF clear by Byte Write to PCSR with Delay000.000 pitTest:460 Test 1 took 4096000usTest 2, PIF clear by Word Write to PCSR000.000 pitTest:460 Test 2 took 4097562usTest 3, PIF clear by Word Write to PCSR with Delay000.000 pitTest:460 Test 3 took 5094800us

 The "normal word write" resulted in an error of 4097.562/4096 = 1.00038 = 0.038% = 381ppm. That's 11 counts in 32k, so the code took 11 clocks to write to the PCSR and inadvertently reset the prescaler.

 

Waiting for 200us (out of the 491us period) and then performing the word write resulted in an error of 5094.8/4096 = 1.2438 = 24% = 243,847ppm, which might be a little more obvious!

 

 

 

0 项奖励
回复
4,228 次查看
bastian_schick
Contributor IV

Tom,

 

did you try what happens if OVW is not set using the PMR-rewrite ? I use (better our customers) the PIT for a long time on 5329 and I got no report about loss of counts.

 

Anyway, well done. I think we'd expect a FSL statement now :smileyhappy:

0 项奖励
回复
4,228 次查看
TomE
Specialist II

42BS wrote:

> did you try what happens if OVW is not set using the PMR-rewrite ?

 

My test code may look as if OVR is set when it is running, but it isn't:

 

    /* Program all pits to 500us timeout, 4 different dividers */
    MCF_PIT0_PCSR = MCF_PIT_PCSR_OVW;
    ...
    MCF_PIT0_PMR = (40000 - 1);
    ...
    nPcsr = MCF_PIT_PCSR_HALTED | MCF_PIT_PCSR_PIF
            | CF_PIT_PCSR_RLD | MCF_PIT_PCSR_EN;
    /* Enable them all, staggered at 1us */
    MCF_PIT0_PCSR = nPcsr | MCF_PIT_PCSR_PRE(0);

I only set OVW in order to force the PMR into the counter. In all other test code using "nPcsr", OVW is not set.

 

It makes perfect sense for the Prescaler to be reset when the PMR is loaded with OVW set.

 

It makes no sense at all for this to happen with OVW clear. The chip does it anyway.

 

If anyone has code that "ping-pongs" two different values into the PMR in order to either fine-tune the period or to have alternating Short and Long periods, they'll be resetting the prescaler when doing this.

 

WARNING ON WRITING PMR DURING PIT INITIALISATION

 

I'm guessing a lot of pregrammers don't know that when they first set up the PIT they MUST OVW, write PMR then (optionally) clear OVW. If they do this the counter will then start from the PMR value. If they don't it will run from 0xFFFF the first time. This would make the first timeout a lot longer than expected - up to 26 SECONDS on an 80MHz clock if using a prescaler of 32768. This is not mentioned in the Reference Manual, but is noted in AN3400 for the MCF5213:

 

 

5         Configuration NotesThe following details are important when configuring the PIT:
    ...
    • The order of the initialization process is important if a
      desired modulus value is needed. First, configure the PCSR
      and set the overwrite (OVW) bit. Then, write the PMR.
      This causes the PCNTR to load the PMR value to the PCNTR.

 

 

I've tracked PIT documentation back to the MMC2107 Reference Manual. It is word-for-word identical with the MCF5329 manual. Unusual word patterns like "loading of a new value into the counter also reset" can be used in Google to find many of the manuals. The MMC2107 PIT was derived from the 1998 MMC2001 "IT" Programmable Interval Timer. It didn't have this problem as it ran from a 32kHz crystal and didn't have a prescaler. The rest of the control register is identical though.

 

> I use (better our customers) the PIT for a long time on 5329 and I got no report about loss of counts.

 

Would you notice an error of 2 seconds per day? You might notice a 24% error, but that requires a huge prescaler, tiny modulus and a long delay between the ISR entry and the PIF clear. You need to be using a PIT and another timer, and expecting them to retain a fixed phase relationship to see this problem.

 

> I think we'd expect a FSL statement now Smiley Happy

 

Do you think they read this list? Smiley Happy

 

I'm about to send note of this back through our distributor. I've already posted on CodeSorcery's Coldfire list.

 

 

0 项奖励
回复
4,228 次查看
J2MEJediMaster
Specialist I

Yes we do.

 

And the link to this thread with some comments was sent to the appropriate people yesterday.

 

You might also file a service request, calling out that the documentation needs enhancement/fixing.

 

I've done a fair share of embedded programming (8- and 16-bit), and nothing irks me more than struggling with a problem for days and then discovering that something about the documentation was the culprit.

 

---Tom

0 项奖励
回复
4,228 次查看
TomE
Specialist II

I'm going to start referring to this peripheral as the "Bear PIT", taking the SECOND meaning in the following:

 

http://en.wikipedia.org/wiki/Bear_pit#Other_meanings

 

As far as I can tell,everyone who has read the PIT manual and then programmed this part has unwittingly toppled into the same two "Bear PITS", making the same two mistakes. Linux code, Freescale samples, "MrBean"'s code and mine (until I fixed it). For the last 10 years. In most cases this is relatively harmless. Who cares about periodic timer accuracy? Who cares if the first programmed time period is out by a factor of 1000? Some people might.

 

J2MEJediMaster wrote:

> You might also file a service request, calling out that the documentation needs enhancement/fixing.

 

I followed your advice, and after 3 weeks received the following:

 

 

I talked with the apps team and they told me that the redaction doesn’t seems to implies that writing the same value to the PRE bits doesn't reset the timer. Anyway they will review and make any necessary change if necessary. Thanks in advance for your feedback. Have a nice day.

 

Redaction? I can't see how a chapter which hasn't changed in over 10 years can be "redacted".

 

I was hoping for "it is misleading, we'll fix it". I'll keep waiting.

 

> I've done a fair share of embedded programming (8- and 16-bit), and nothing irks me

> more than struggling with a problem for days and then discovering that something about

> the documentation was the culprit.

 

And then I try to stop people making the same programming mistakes I've made. If I can't get the documentation changed at least Google can find this article. Try Googling "coldfire bear pit". I'm already getting this article at the top of the list and I haven't posted it yet Smiley Happy.

 

 

0 项奖励
回复
4,228 次查看
J2MEJediMaster
Specialist I

> And then I try to stop people making the same programming mistakes I've made.

 

I totally agree with you on this. It's why I'm manning this post. I just saved one of our engineers countless hours of grief with a tech problem by pointing him to a solution posted in one of these forums. Send me a private message as to the SR number you submitted. I am going to try to work this from another angle.

 

---Tom

0 项奖励
回复
4,228 次查看
TomE
Specialist II

J2MEJediMaster wrote nearly 3 months ago:

> Send me a private message as to the SR number you submitted. I am going to try to work this from another angle.

 

Has this resulted in any progress?

 

My SR issues have been closed with:

 

 

"I talked with the apps team and they will review and make any necessary change.""Thank you for your feedback. I will inform this to the team."

 

There is a new MCF5329 Errata document, but no mention of these "Bear PITs". In truth these problems aren't chip bugs, they're there by design, so that may not be an appropriate place for Freescale to document this. There are no new versions of any other documents for the MCF5329 that might address this.

 

 

I suggested on one of the traps:

 

This is a gotcha that is addressed in AN3400, but only MCF5213 users would be expected to find that App Note. It would help if it AN3400 was referenced from all Freescale Product Documentation Web Pages for all Coldfire chips that have this PIT in it.

That would be simple to do, but hasn't been done either.

 

Se we'll all have to keep checking these Forum articles.

 

Tom

 

0 项奖励
回复
4,227 次查看
MrBean
Contributor I

I set the PIT back the way it was (see top post, ie. prescaler back to 2^4, PMR=1874, RLD=1 (@60MHz) )

and tested clearing PIF with a byte write (bset.b).

Works like a charm.

 

It seems you are 100% correct Tom.

Rewriting the PRE bits, be it with the same value, and thus resetting the prescaler is likely to cause it.

Thanx a lot for the research !

0 项奖励
回复
4,228 次查看
Tanuka
Contributor I

TomE,

 

Great example of a truly analytical approach, testing to verify your theory.

 

Scary thought - where else might there exist byte-vs-word conflicts in the CodeWarrior headers??? I'm about to 'take the plunge' and buy CodeWarrior Standard Edition and the Tower Kit for ColdFire V2, and it's nice to know we've got this support forum with its savvy contributors and a good, workable process for getting a response from the software engineers at Freescale.

0 项奖励
回复
4,228 次查看
TomE
Specialist II

Tanuka wrote:

> Scary thought - where else might there exist byte-vs-word conflicts in the CodeWarrior headers???

 

I've checked Linux and the MCF5329_HEADERS file, both use or support WORD access to the PCSR.

 

Could others with installed versions of:

 

1 - CodeWarrior

2 - IAR

3 - Green Hills

4 - MQX

5 - Anything else I've missed

 

please check to see how the headers allow access to the PIT PCSR register.

 

Also check how any PIT sample code or driver code resets the PIF flag in the PIT.

 

Please report anything you find back to this thread. If you find your Development Environment isn't handling the PIT properly, please report that back to the supplier.

 

> and a good, workable process for getting a response from the software engineers at Freescale.

 

I've noticed  "J2MEJediMaster" helping here a lot. Are there any other regular posters from Freescale?

 


0 项奖励
回复