Refer to AN-1794 section 4.1 Figure 6, I connect two DP83630s to a MCU which has two RMII mode MACs, one for upstream port, the other for downstream port.
For the upstream port, the PHY works at RMII Master mode with SYNC_ENET_EN enabled, for the downsteam port, the PHY works at RMII slave mode with SYNC_ENET_EN disabled.
The upstream port PHY feeds with a 25MHz TCXO, it output the 50MHz through it's TX_CLK and feed to downstream port's X1 input.
Using above topology, both upstream port PHY and downstream port PHY work at same clock domain, so I expect their PTP clock can be synchronized to sub nanosecond accuracy.
Refer to AN-1838 section 3.1 Figure 2, I implement the synchronization PTP clock between upstream PHY and downstream PHY.
However, when I test the PPS output from each PHY using a oscilloscope, I found the phase error is uncertainty.
Everytime I power on the system, after PTP synchronization finished, PPS phase error vary among three values (0ns, 4ns and 8ns, which is multiple of 4ns).
Do you think the phase error is expected or not?
Is it possible the achieve the sub nanosecond accuracy between the two PHYs ptp clock?
My source code is attached as followed:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NS_UINT SimpleSyncTwoPHY(RunTimeOpts *rtOpts, NS_BOOL SyncEth1ToEth0, NS_BOOL DoSync)
{
NS_UINT eventNum[2] = {0xFF,0xFF};
NS_UINT riseFlag[2];
NS_UINT eventsMissed[2];
NS_UINT events;
NS_STATUS status;
int deltaRate;
int WaitTime;
NS_UINT phaseError;
NS_BOOL negAdj;
TimeInternal eventTime[2];
TimeInternal TimeDiff;
TimeInternal TimeDiffPhy1ToPhy2;
TimeInternal TimeDiffPhy2ToPhy1;
long long val;
long long val1;
long long val2;
long long DeltaT;
long long DeltaP;
// Flush eth0 PHY's Event Timestamp
while ( (events = PTPCheckForEvents( pEPL_HANDLE1)))
{
if ( events & PTPEVT_EVENT_TIMESTAMP_BIT)
{
PTPGetEvent(pEPL_HANDLE1, &eventNum[0], &riseFlag[0], &(eventTime[0].seconds),&eventTime[0].nanoseconds, &eventsMissed[0]);
}
else
{
break;
}
}
// Flush eth1 PHY's Event Timestamp
while ( (events = PTPCheckForEvents( pEPL_HANDLE2)))
{
if ( events & PTPEVT_EVENT_TIMESTAMP_BIT)
{
PTPGetEvent(pEPL_HANDLE2, &eventNum[1], &riseFlag[1], &(eventTime[1].seconds), &eventTime[1].nanoseconds, &eventsMissed[1]);
}
else
{
break;
}
}
//Set PHY1's GPIO3(SYNC_TRIGGER) as a riging edge event number 0 capture
PTPSetEventConfig(pEPL_HANDLE1, 0, TRUE, FALSE, TRUE, 3);
//Set PHY2's GPIO3(SYNC_TRIGGER) as a riging edge event number 0 capture
PTPSetEventConfig(pEPL_HANDLE2, 0, TRUE, FALSE, TRUE, 3);
//Set MCU's SYNC_TRIGGER pin as input, to avoid conflict with PHY's trigger output
FM3_GPIO->PFRA &= ~(1u<<0x0); // PA0, not special function
FM3_GPIO->DDRA &= ~(1u<<0x0); // Set SYNC_TRIGGER pin as input
//Calculate the trigger timing offset 500ms from current PHY1's PTP time
PTPClockReadCurrent(pEPL_HANDLE1, &(eventTime[0].seconds),&(eventTime[0].nanoseconds));
val = (long long)(eventTime[0].seconds * 1e9) + (long long)eventTime[0].nanoseconds;
val += 50000000;
//Phase align the trigger signal
val = val - val % 8;
eventTime[0].seconds = (int)(val / 1e9);
eventTime[0].nanoseconds = (int)(val % (long long)1e9);
//Set PHY1's GPIO3(SYNC_TRIGGER) as a trigger 1 output, and output a 1us positive pulse
PTPCancelTrigger(pEPL_HANDLE1, 1);
PTPSetTriggerConfig( pEPL_HANDLE1, 1, TRGOPT_PULSE | TRGOPT_NOTIFY_EN, 3);
PTPArmTrigger( pEPL_HANDLE1, 1, eventTime[0].seconds, eventTime[0].nanoseconds,
FALSE, FALSE, 10000, 10000);
//Waiting for trigger 1 to trigger
WaitTime = 0x10000000;
status = PTPHasTriggerExpired(pEPL_HANDLE1,1);
while((NS_STATUS_SUCCESS != status) && (WaitTime>0))
{
status = PTPHasTriggerExpired(pEPL_HANDLE1,1);
WaitTime--;
}
if(!WaitTime)
{
printf("Error waiting for trigger!\n");
return -1;
}
else
{
printf("PHY1 Triggerred!\n");
}
PTPCancelTrigger(pEPL_HANDLE1, 1);
PTPSetTriggerConfig( pEPL_HANDLE1, 1, TRGOPT_PULSE | TRGOPT_NOTIFY_EN, 0);
// Flush Transmit and Receive Timestamps and get Event Timestamp
while ( (events = PTPCheckForEvents( pEPL_HANDLE1)))
{
if ( events & PTPEVT_TRANSMIT_TIMESTAMP_BIT)
PTPGetTransmitTimestamp( pEPL_HANDLE1, &(eventTime[0].seconds), &(eventTime[0].nanoseconds),&eventsMissed[0]);
else if ( events & PTPEVT_RECEIVE_TIMESTAMP_BIT)
PTPGetTransmitTimestamp( pEPL_HANDLE1, &(eventTime[0].seconds), &(eventTime[0].nanoseconds),&eventsMissed[0]);
else if ( events & PTPEVT_EVENT_TIMESTAMP_BIT)
{
PTPGetEvent(pEPL_HANDLE1, &eventNum[0], &riseFlag[0], &(eventTime[0].seconds), &(eventTime[0].nanoseconds), &eventsMissed[0]);
if(eventNum[0] & (1<<0))
break;
}
}
if(!(eventNum[0] & (1<<0)) )
{
printf("Error, PHY1 no SYNC_Trigger event captured!!!\n");
return -1;
}
// Disconnect event 0 from GPIO 3
PTPSetEventConfig( pEPL_HANDLE1, 0, FALSE, FALSE, FALSE, 0);
// Flush Transmit and Receive Timestamps and get Event Timestamp
while ( (events = PTPCheckForEvents( pEPL_HANDLE2)))
{
if ( events & PTPEVT_TRANSMIT_TIMESTAMP_BIT)
PTPGetTransmitTimestamp( pEPL_HANDLE2, &(eventTime[1].seconds), &(eventTime[1].nanoseconds),&eventsMissed[1]);
else if ( events & PTPEVT_RECEIVE_TIMESTAMP_BIT)
PTPGetTransmitTimestamp( pEPL_HANDLE2, &(eventTime[1].seconds), &(eventTime[1].nanoseconds),&eventsMissed[1]);
else if ( events & PTPEVT_EVENT_TIMESTAMP_BIT)
{
PTPGetEvent(pEPL_HANDLE2, &eventNum[1], &riseFlag[1], &(eventTime[1].seconds), &(eventTime[1].nanoseconds), &eventsMissed[1]);
if(eventNum[1] & (1<<0))
break;
}
}
if(!(eventNum[1] & (1<<0)) )
{
printf("Error, PHY2 no SYNC_Trigger event captured!!!\n");
return -1;
}
// Disconnect event 0 from GPIO 3
PTPSetEventConfig( pEPL_HANDLE2, 0, FALSE, FALSE, FALSE, 0);
subTime(&TimeDiffPhy1ToPhy2, &eventTime[0],&eventTime[1]);
val = (long long)(TimeDiffPhy1ToPhy2.seconds * 1e9) + (long long)TimeDiffPhy1ToPhy2.nanoseconds;
val +=16;
negAdj = FALSE;
if(val < 0)
negAdj = TRUE;
TimeDiff.seconds = (int)(val / 1e9);
TimeDiff.nanoseconds = (int)(val % (long long)1e9);
PTPClockStepAdjustment( pEPL_HANDLE2 , abs(TimeDiff.seconds), abs(TimeDiff.nanoseconds), negAdj);
PTPClockReadCurrent(pEPL_HANDLE1, &(eventTime[0].seconds),&(eventTime[0].nanoseconds));
val = (long long)(eventTime[0].seconds * 1e9) + (long long)eventTime[0].nanoseconds;
val += 50000000;
//PPS phase alignment
val = val - val%32;
eventTime[0].seconds = (int)(val / 1e9);
eventTime[0].nanoseconds = (int)(val % (long long)1e9);
// Setup PHY1's PPS configuration
PTPSetTriggerConfig( pEPL_HANDLE1, 0, TRGOPT_PULSE |TRGOPT_PERIODIC| TRGOPT_NOTIFY_EN, 0);
PTPCancelTrigger( pEPL_HANDLE1, 0);
PTPSetTriggerConfig( pEPL_HANDLE1, 0, TRGOPT_PULSE|TRGOPT_PERIODIC|TRGOPT_NOTIFY_EN, 1);
PTPArmTrigger( pEPL_HANDLE1, 0, eventTime[0].seconds, eventTime[0].nanoseconds,
FALSE, FALSE, 16, 16);
// Setup PHY2's PPS configuration
PTPSetTriggerConfig( pEPL_HANDLE2, 0, TRGOPT_PULSE |TRGOPT_PERIODIC| TRGOPT_NOTIFY_EN, 0);
PTPCancelTrigger( pEPL_HANDLE2, 0);
PTPSetTriggerConfig( pEPL_HANDLE2, 0, TRGOPT_PULSE|TRGOPT_PERIODIC|TRGOPT_NOTIFY_EN, 1);
PTPArmTrigger( pEPL_HANDLE2, 0, eventTime[0].seconds, eventTime[0].nanoseconds,
FALSE, FALSE, 16, 16);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////