如果比较寄存器设置为 0,则 ENET PTP 比较输出永远不会触发——新的芯片勘误表? 您好!我一直在努力在我们的系统中实现 PTP 比较(又名时间脉冲)。目标是从设备中生成每秒一次的时间脉冲,以便我们可以将其时钟同步与 PTP 系统中的其他设备进行比较。 在执行此操作时,我遇到了一个相当奇怪的问题:如果将值 0 写入 PTP 计时器比较寄存器,则不会生成任何比较事件。(1、ENET->ATPER) 中的任何其他值都可以正常工作并生成事件,但 0 不行。据我所知,这似乎是一个以前未知的芯片勘误表,因为数据表中没有提到 0 对于该寄存器来说是非法的,事实上,在这里使用 0 来在秒的顶部生成脉冲是很常见的。 我从我们的代码库中提炼出了一个产生该问题的小例子: /*
* Timepulse triggering bug demo
*/
#include
#include
#include
#include
// 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);;
}
}
} 执行后,会打印: ============== 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 正如您所见,代码在 0 秒的时间脉冲应该触发的位置冻结。 但是,如果我将行“uint64_t nextTimepulseTime = ONE_SECOND_NS;”更改为“uint64_t nextTimepulseTime = ONE_SECOND_NS + 1;”(确保 0 永远不会写入 TCCR),一切都会正常运行。现在打印: ============== 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
据我所知,该代码应该可以在原始版本中运行,而事实上它却不能运行,这是一个勘误表。如果 NXP 的某个人能告诉我这是否有意义,我将非常感激。这不是什么大问题,但它确实应该出现在勘误表中,以避免未来的人们再次调试同样的问题。 i.MXRT 105x i.MXRT 106x 回复:如果比较寄存器设置为 0,ENET PTP 比较输出将永远不会触发——新的芯片勘误表? 嗨,感谢您的浏览!这是在 MIMXRT1062 上。 Re: ENET PTP Compare output never fires if compare register is set to 0 -- new chip errata? 嗨,Kan! 很抱歉挖坟,但我终于抽出时间在硬件上尝试了一下。我首先尝试了解决方法 1(在 ATCR 中设置 PINPER 和 PEREN 位)。这种方法似乎行不通——当 TCCR 设置为 0 时,计时器仍然不会触发,行为没有任何改变。解决方法 2(防止 TCCR 小于 ATINC)似乎可以正常工作。不过,在我的测试中,将 TCCR 设置为小于 ATINC 但大于 0 的值(例如 1)确实有效,因此这种变通方法的范围可能有点太广了。但是,这对我的申请来说不是问题。 此外,在测试过程中,我还发现了另一个问题。将 TCCR 设置为小于 ATINC 的值(低于 ATPER)也会导致定时器永远不会触发。根据这里的设置,该值将大于 3999999960 且小于 4000000000。这似乎是对现有勘误表的延伸。 我还测试了该错误是否受到校正增量 (INC_CORR) 的影响,方法是将 ATINC.INC_CORR 设置为 42,将 ATCOR 设置为 1(这样每隔一个 PTP 时钟周期就会使用校正)。使用这些设置后,我在使用 INC_CORR 范围内的时间戳时没有遇到问题,例如3999999960。这似乎表明 INC_CORR 不会影响此勘误的范围。(而且这是件好事,我可不想遇到那种因为恰好在校正增量上运行,导致我的时间脉冲在数千次执行中随机失败一次的罕见错误!)
記事全体を表示