ENET PTP Compare output never fires if compare register is set to 0 -- new chip errata?

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

ENET PTP Compare output never fires if compare register is set to 0 -- new chip errata?

326件の閲覧回数
MultipleMonomials
Contributor III

Hello!  I've been working on implementing PTP Compare (aka timepulse) in our system.  The goal is to generate a once-a-second timepulse out of the device so that we can compare its clock synchronization with other devices in the PTP system.

While doing this, I ran into a rather strange issue: if the value 0 gets written into the PTP timer compare register, no compare events are ever generated.  Any other value from (1, ENET->ATPER) works fine and generates events, but not 0.  As far as I can tell, this seems to be a previously unknown chip errata as there's no mention in the datasheet of 0 being illegal for this register, and in fact it would be pretty common to use 0 here so as to generate a pulse at the top of the second.

I distilled a minimal example from our codebase that generates the issue:

 

 

/*
 * Timepulse triggering bug demo
 */

#include <fsl_clock.h>
#include <fsl_enet.h>
#include <stdio.h>
#include <inttypes.h>

// PTP clock is ticked at 25MHz, 1/25MHz = 40ns increment
const uint8_t timeIncrement = 40;

// Set HW nanosecond counter to roll over ever 4 seconds.
// The max value here is about 4.2 seconds, so this is as high as we can have it
// while still keeping it an integer number of seconds
const uint32_t nanosecondCounterRolloverVal = 4000000000;

const uint32_t ONE_SECOND_NS = 1000000000;

void enableENETClock()
{
    /* Init Enet PLL. */
    const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN =
    {
        .enableClkOutput = true,                  /* Enable the PLL providing the ENET 125MHz reference clock */
        .enableClkOutput25M = true,               /* Enable the PLL providing the ENET 25MHz reference clock */
        .loopDivider = 1,                         /* Set frequency of ethernet reference clock to 50 MHz */
        .src=0,                                 /* Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N */
        .enableClkOutput1 = true,                 /* Enable the PLL providing the ENET2 125MHz reference clock */
        .loopDivider1 = 1,                        /* Set frequency of ethernet reference clock to 50 MHz */
    };
    CLOCK_InitEnetPll(&enetPllConfig_BOARD_BootClockRUN);
    /* Set ENET Ref clock source. */
    IOMUXC_GPR->GPR1 &= ~IOMUXC_GPR_GPR1_ENET1_TX_CLK_DIR_MASK;
    /* Set ENET2 Ref clock source. */
    IOMUXC_GPR->GPR1 &= ~IOMUXC_GPR_GPR1_ENET2_TX_CLK_DIR_MASK;
}

void initENET()
{
    CLOCK_EnableClock(kCLOCK_Enet);
    ENET_Reset(ENET);
}

void initPTPClock()
{
    // Software reset timestamp module
    ENET->ATCR = ENET_ATCR_RESTART_MASK;
    while(ENET->ATCR & ENET_ATCR_RESTART_MASK) {}

    // Configure increment.
    ENET->ATINC = ENET_ATINC_INC(timeIncrement);

    // Reset fine counter at the defined period
    ENET->ATPER = nanosecondCounterRolloverVal;

    // Start counter counting.
    ENET->ATCR |= ENET_ATCR_EN_MASK;
}

void setCurrentTime(uint32_t timeCounts)
{
    ENET->ATVR = timeCounts;
}

uint32_t readCurrentTime()
{
    // Note: This is based on ENET_Ptp1588GetTimerNoIrqDisable.
    // (...because the datasheet doesn't actually really explain how to read the timer)

    // Issue a capture command and wait for it to complete.
    // A comment in FSL HAL says this takes about 6 timestamp clocks, meaning about 240ns.
    ENET->ATCR = ENET_ATCR_CAPTURE_MASK;
    while(ENET->ATCR & ENET_ATCR_CAPTURE_MASK)
    {}

    return ENET->ATVR;
}

int main()
{
    enableENETClock();
    initENET();
    initPTPClock();

    uint64_t nextTimepulseTime = ONE_SECOND_NS;
    const uint32_t timepulsePeriod = ONE_SECOND_NS;

    setCurrentTime(0);

    // Initialize the timepulse and set it to go off at the initial time
    ENET_Ptp1588SetChannelCmpValue(ENET, kENET_PtpTimerChannel1, nextTimepulseTime % nanosecondCounterRolloverVal);
    printf("First timepulse will fire at %" PRIu64 "\n", nextTimepulseTime % nanosecondCounterRolloverVal);
    nextTimepulseTime += timepulsePeriod;
    ENET_Ptp1588SetChannelOutputPulseWidth(ENET,
            kENET_PtpTimerChannel1,
            false,
            31,
            false);

    // Wait for timer mode change to go through, otherwise we might write TCCR too early
    // and it will replace the first compare value with the second one
    // This is not documented in the datasheet but it doesn't work without it...
    while((ENET->CHANNEL[kENET_PtpTimerChannel1].TCSR & ENET_TCSR_TMODE_MASK) == 0) {}

    // Feed in the next time as well
    ENET_Ptp1588SetChannelCmpValue(ENET, kENET_PtpTimerChannel1, nextTimepulseTime % nanosecondCounterRolloverVal);
    printf("Second timepulse will fire at %" PRIu64 "\n", nextTimepulseTime % nanosecondCounterRolloverVal);
    nextTimepulseTime += timepulsePeriod;

    // Loop, printing when each timepulse happens
    while(true)
    {
        // Did the timepulse fire?
        if(ENET_Ptp1588GetChannelStatus(ENET, kENET_PtpTimerChannel1))
        {
            const uint32_t nextTimepulseCounts = nextTimepulseTime % nanosecondCounterRolloverVal;

            printf("Timepulse fired, current PTP counter register = %010" PRIu32 ". "
                "Configuring timepulse after the next to fire at %010" PRIu32 "\n",
                readCurrentTime(), nextTimepulseCounts);

            // Write the next target time to the register
            ENET_Ptp1588SetChannelCmpValue(ENET, kENET_PtpTimerChannel1, nextTimepulseCounts);
            nextTimepulseTime += timepulsePeriod;

            // Finally clear the timer flag.  This has to be done after loading in the next trigger time
            // as it triggers a load of the TCCR register into the comparator.
            ENET_Ptp1588ClearChannelStatus(ENET, kENET_PtpTimerChannel1);;
        }
    }
}

 

 

When executed, this prints:

 

 

============== Power On ==============
First timepulse will fire at 1000000000
Second timepulse will fire at 2000000000
Timepulse fired, current PTP counter register = 1000008160. Configuring timepulse after the next to fire at 3000000000
Timepulse fired, current PTP counter register = 2000000440. Configuring timepulse after the next to fire at 0000000000
Timepulse fired, current PTP counter register = 3000000440. Configuring timepulse after the next to fire at 1000000000

 

 

As you can see, the code freezes at the point where the timepulse at 0 seconds is supposed to fire.

However if I change the line "uint64_t nextTimepulseTime = ONE_SECOND_NS;" to "uint64_t nextTimepulseTime = ONE_SECOND_NS + 1;" (ensuring that 0 never gets written to TCCR), everything works perfectly.  It now prints:

 

 

============== Power On ==============
First timepulse will fire at 1000000001
Second timepulse will fire at 2000000001
Timepulse fired, current PTP counter register = 1000009000. Configuring timepulse after the next to fire at 3000000001
Timepulse fired, current PTP counter register = 2000000480. Configuring timepulse after the next to fire at 0000000001
Timepulse fired, current PTP counter register = 3000000440. Configuring timepulse after the next to fire at 1000000001
Timepulse fired, current PTP counter register = 0000000480. Configuring timepulse after the next to fire at 2000000001
Timepulse fired, current PTP counter register = 1000000480. Configuring timepulse after the next to fire at 3000000001
Timepulse fired, current PTP counter register = 2000000480. Configuring timepulse after the next to fire at 0000000001
Timepulse fired, current PTP counter register = 3000000440. Configuring timepulse after the next to fire at 1000000001
Timepulse fired, current PTP counter register = 0000000480. Configuring timepulse after the next to fire at 2000000001
Timepulse fired, current PTP counter register = 1000000480. Configuring timepulse after the next to fire at 3000000001
Timepulse fired, current PTP counter register = 2000000480. Configuring timepulse after the next to fire at 0000000001
Timepulse fired, current PTP counter register = 3000000480. Configuring timepulse after the next to fire at 1000000001
Timepulse fired, current PTP counter register = 0000000440. Configuring timepulse after the next to fire at 2000000001
Timepulse fired, current PTP counter register = 1000000440. Configuring timepulse after the next to fire at 3000000001
Timepulse fired, current PTP counter register = 2000000480. Configuring timepulse after the next to fire at 0000000001
Timepulse fired, current PTP counter register = 3000000480. Configuring timepulse after the next to fire at 1000000001
Timepulse fired, current PTP counter register = 0000000480. Configuring timepulse after the next to fire at 2000000001
Timepulse fired, current PTP counter register = 1000000480. Configuring timepulse after the next to fire at 3000000001
Timepulse fired, current PTP counter register = 2000000480. Configuring timepulse after the next to fire at 0000000001
Timepulse fired, current PTP counter register = 3000000480. Configuring timepulse after the next to fire at 1000000001
Timepulse fired, current PTP counter register = 0000000480. Configuring timepulse after the next to fire at 2000000001
<snip repetitions of the above output>

 

 

As far as I can tell, this code should work in the original version, and the fact that it doesn't is an errata.  I'd appreciate it if someone from NXP could let me know if this makes sense or not.  It's not a huge deal but it really should be in the errata sheet to save future people from having to debug this same issue again. 

ラベル(2)
0 件の賞賛
返信
3 返答(返信)

294件の閲覧回数
Kan_Li
NXP TechSupport
NXP TechSupport

Hi @MultipleMonomials ,

 

Which platform was tested with this? Would you please clarify? I will try to reproduce it here.

 

Have a great day,
Kan


-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!
- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 件の賞賛
返信

260件の閲覧回数
MultipleMonomials
Contributor III

Hi, thanks for taking a look!   This was on MIMXRT1062.

0 件の賞賛
返信

202件の閲覧回数
Kan_Li
NXP TechSupport
NXP TechSupport

Hi @MultipleMonomials ,

 

Thanks for the info! I am checking internally regarding this issue, and will let you know when I have any more info.

 

Thanks for your patience!

 

Best Regards,

Kan

0 件の賞賛
返信