diff --git a/.gitignore b/.gitignore index e19e25a0..ea1d8b70 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode/ +.idea/ __pycache__/ *.egg-info/ diff --git a/NetX/inc/u_nx_ethernet.h b/NetX/inc/u_nx_ethernet.h index 95afd7e9..9c42da75 100644 --- a/NetX/inc/u_nx_ethernet.h +++ b/NetX/inc/u_nx_ethernet.h @@ -90,10 +90,16 @@ ethernet_message_t ethernet_create_message(uint8_t message_id, ethernet_node_t r uint8_t ethernet_send_message(ethernet_message_t *message); /** - * @brief Retrieves the time from PTP stack. + * @brief Retrieves the time of day from PTP stack. * @return The UTC time */ -NX_PTP_DATE_TIME ethernet_get_time(void); +NX_PTP_DATE_TIME ethernet_get_timeofday(void); + +/** + * @brief Retrieves the time from PTP stack. + * @return The Unix epoch time + */ +NX_PTP_TIME ethernet_get_time(void); // clang-format on #endif /* u_nx_ethernet.h */ diff --git a/NetX/inc/u_rtc.h b/NetX/inc/u_rtc.h new file mode 100644 index 00000000..26b0f510 --- /dev/null +++ b/NetX/inc/u_rtc.h @@ -0,0 +1,12 @@ +#ifndef RTC_H +#define RTC_H + +#include "nxd_ptp_client.h" +#include "nx_api.h" + +UINT nx_ptp_client_hard_clock_callback(NX_PTP_CLIENT *client_ptr, + UINT operation, NX_PTP_TIME *time_ptr, + NX_PACKET *packet_ptr, + VOID *callback_data); + +#endif // RTC_H diff --git a/NetX/src/nxd_ptp_client.c b/NetX/src/nxd_ptp_client.c index 98804715..9477177b 100644 --- a/NetX/src/nxd_ptp_client.c +++ b/NetX/src/nxd_ptp_client.c @@ -6152,6 +6152,6 @@ LONG ns; /* return result */ time_ptr -> second_high = sec_hi; time_ptr -> second_low = sec_lo; - time_ptr -> nanosecond = ns; + time_ptr -> nanosecond = ns; } - // clang-format on \ No newline at end of file + // clang-format on diff --git a/NetX/src/u_nx_ethernet.c b/NetX/src/u_nx_ethernet.c index fa282fe9..8af9d972 100644 --- a/NetX/src/u_nx_ethernet.c +++ b/NetX/src/u_nx_ethernet.c @@ -1,5 +1,6 @@ // clang-format off #include "u_nx_ethernet.h" +#include "u_rtc.h" #include "nx_stm32_eth_driver.h" #include "nxd_ptp_client.h" #include "u_nx_debug.h" @@ -227,7 +228,7 @@ uint8_t ethernet_init(ethernet_node_t node_id, DriverFunction driver, OnRecieve /* Create the PTP client instance */ status = nx_ptp_client_create(&device.ptp_client, &device.ip, 0, &device.packet_pool, _PTP_THREAD_PRIORITY, (UCHAR *)&device.ptp_stack, sizeof(device.ptp_stack), - _nx_ptp_client_soft_clock_callback, NX_NULL); + nx_ptp_client_hard_clock_callback, NX_NULL); if(status != NX_SUCCESS) { PRINTLN_ERROR("Failed to create PTP client (Status: %d/%s).", status, nx_status_toString(status)); return status; @@ -392,7 +393,7 @@ uint8_t ethernet_send_message(ethernet_message_t *message) { return U_SUCCESS; } -NX_PTP_DATE_TIME ethernet_get_time(void) { +NX_PTP_DATE_TIME ethernet_get_timeofday(void) { NX_PTP_TIME tm; NX_PTP_DATE_TIME date; /* read the PTP clock */ @@ -404,4 +405,13 @@ NX_PTP_DATE_TIME ethernet_get_time(void) { return date; } +NX_PTP_TIME ethernet_get_time(void) { + NX_PTP_TIME tm; + + /* read the PTP clock */ + nx_ptp_client_time_get(&device.ptp_client, &tm); + + return tm; +} + // clang-format on diff --git a/NetX/src/u_rtc.c b/NetX/src/u_rtc.c new file mode 100644 index 00000000..ebba2817 --- /dev/null +++ b/NetX/src/u_rtc.c @@ -0,0 +1,163 @@ +#include "nxd_ptp_client.h" +#include "u_rtc.h" +#include "stm32h5xx_hal.h" +#include "u_tx_debug.h" +#include "tx_api.h" + +/// This file implements the PTP client callback to manage time for a STM32H563. +/// This is where the interface between the PTP client and the timebase is. +/// This was written using RM0481 and partially using source code from UM3131. +/// +/// The timebase for the clock used is listed as clk_ptp_ref_i. +/// The PTP Offload feature is NOT utilized because it is very half-baked. +/// Instead we simply use the internal clock reference and a userspace UDP impl. +/// +/// Tuning: The status of this operation can be found by using eth_ptp_pps_out. +/// There may be some register setup to enable PPS. +/// +/// Docs: This mostly follows the procedure in page RM0481 2792 57.9.9 +/// This uses coarse timestamping digital rollover mode. +/// +/// Requirements: +/// 1 billion must be divisible by the frequency value of Pll1_Q. +/// This will turn on Pll1_Q if it is not on already. +/// +/// Future improvements: +/// - Use fine update mode to reduce jitter +/// - Use binary rollover mode for sub-ns precision (lol) +/// + +extern ETH_HandleTypeDef heth; + +#define PTP_UTC_OFFSET 0 // UTC 0 + +UINT nx_ptp_client_hard_clock_callback(NX_PTP_CLIENT *client_ptr, + UINT operation, NX_PTP_TIME *time_ptr, + NX_PACKET *packet_ptr, + VOID *callback_data) +{ + TX_INTERRUPT_SAVE_AREA + switch (operation) { + case NX_PTP_CLIENT_CLOCK_INIT: + TX_DISABLE + + // see 57.9.9 + + // Mask the Timestamp Trigger interrupt + CLEAR_BIT(heth.Instance->MACIER, ETH_MACIER_TSIE); + SET_BIT(heth.Instance->MACTSCR, ETH_MACTSCR_TSENA); + + SET_BIT(heth.Instance->MACTSCR, + ETH_MACTSCR_TSCTRLSSR); // 1 ns + + // each step is 1ns, with a clock of clk_ptp_ref_i + // clk_ptp_ref_i comes from pll1_q_clk (eth clock) (pg 456) + // this cool API gives us the frequency (in hz) + + // for some reason we need to enable pll1q output? + SET_BIT(RCC->PLL1CFGR, RCC_PLL1CFGR_PLL1QEN); + + PLL1_ClocksTypeDef pll1; + HAL_RCCEx_GetPLL1ClockFreq(&pll1); + if (1000000000 % pll1.PLL1_Q_Frequency != 0) { + PRINTLN_ERROR( + "NOT PERFEECT DIVISOR PTP TIME WILL BE OFF -> CHECK PLL1Q!"); + } + uint32_t period_ns = 1000000000 / pll1.PLL1_Q_Frequency; + MODIFY_REG(heth.Instance->MACSSIR, + ETH_MACMACSSIR_SNSINC, + period_ns << ETH_MACMACSSIR_SSINC_Pos); + + // placeholder just to get the clock ticking + WRITE_REG(heth.Instance->MACSTNUR, 0); + + // September 9th, 2001 + WRITE_REG(heth.Instance->MACSTSUR, 1000000000); + + SET_BIT(heth.Instance->MACTSCR, ETH_MACTSCR_TSINIT); + + while (READ_BIT(heth.Instance->MACTSCR, + ETH_MACTSCR_TSINIT)) { + } + // clear the seconds register now so we dont need to do it for a subsecond update + CLEAR_REG(heth.Instance->MACSTSUR); + + TX_RESTORE + + break; + case NX_PTP_CLIENT_CLOCK_SET: + /// A large jump in time + TX_DISABLE + + // use overwrite, not add + WRITE_REG(heth.Instance->MACSTNUR, + time_ptr->nanosecond); + + // WARNING travelers of future: this code suffers from the year 2106 problem + // Use seconds_high to account for this, but note the silicon also has this issue + WRITE_REG(heth.Instance->MACSTSUR, + time_ptr->second_low); + + SET_BIT(heth.Instance->MACTSCR, ETH_MACTSCR_TSINIT); + + while (READ_BIT(heth.Instance->MACTSCR, + ETH_MACTSCR_TSINIT)) { + } + // clear the seconds register now so we dont need to do it for a subsecond update + CLEAR_REG(heth.Instance->MACSTSUR); + + TX_RESTORE + + break; + case NX_PTP_CLIENT_CLOCK_PACKET_TS_EXTRACT: // FALL THROUGH + case NX_PTP_CLIENT_CLOCK_GET: + /// Retrieve time, usually for packet timestamping + TX_DISABLE + + time_ptr->nanosecond = READ_REG(heth.Instance->MACSTNR); + time_ptr->second_low = READ_REG(heth.Instance->MACSTSR); + time_ptr->second_high = 0; + TX_RESTORE + break; + case NX_PTP_CLIENT_CLOCK_ADJUST: + /// A small adjustment in time (<1 second) + TX_DISABLE + // use add, not overwrite. + // MACTSUR must be 0 at this point, not wasting CPU time though + if (time_ptr->nanosecond >= 0) { + // this will automatically clear the ADDSUB so we add + WRITE_REG(heth.Instance->MACSTNUR, + time_ptr->nanosecond); + } else { + // take complement starting from one million + uint32_t complement = + 1000000000 - abs(time_ptr->nanosecond); + // with ADDSUB defined it will subtract, but complement must be taken + WRITE_REG(heth.Instance->MACSTNUR, complement); + SET_BIT(heth.Instance->MACSTNUR, + ETH_MACSTNUR_ADDSUB); + } + + SET_BIT(heth.Instance->MACTSCR, ETH_MACTSCR_TSUPDT); + + TX_RESTORE + break; + case NX_PTP_CLIENT_CLOCK_PACKET_TS_PREPARE: + /// Push a packet with a timestamp + + time_ptr->nanosecond = READ_REG(heth.Instance->MACSTNR); + time_ptr->second_low = READ_REG(heth.Instance->MACSTSR); + time_ptr->second_high = 0; + + nx_ptp_client_packet_timestamp_notify( + client_ptr, packet_ptr, time_ptr); + break; + case NX_PTP_CLIENT_CLOCK_SOFT_TIMER_UPDATE: // do nothing + break; + default: + PRINTLN_ERROR("How Did We Get Here? (rtc)"); + break; + } + + return NX_SUCCESS; +}