Step 3.1: EMAC hardware timestamps for PTP over UDP/IPv4
- aes67_ptp/ptp_hw.c: start the EMAC IEEE 1588 clock (IDF
ETH_MAC_ESP_CMD_PTP_ENABLE) and additionally enable snapshots for
PTP over UDP/IPv4 (IDF only enables PTP over Ethernet/L2). An RX hook
on the driver's info input path records {type, seq, sourcePortId, HW
timestamp} of port-319 PTPv2 event messages, then hands every frame to
lwIP unchanged.
- aes67_ptp.c: temporary 3.1 logger joins 224.0.1.129:319/320 and pairs
Sync HW RX timestamps with Follow_Up origin timestamps.
- Checked against linuxptp ptp4l (-4 -E -H, Intel igb) as GM: every Sync
has a HW timestamp; HW Sync intervals track the GM intervals with a
constant -39.8 us/s (+-0.3 us), i.e. local clock -39.8 ppm vs GM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#include "ptp_hw.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_eth_mac_esp.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "hal/emac_ll.h"
|
||||
#include "soc/emac_ptp_struct.h"
|
||||
|
||||
#define RX_RING 16
|
||||
#define PTP_EVENT_PORT 319
|
||||
|
||||
static const char *TAG = "ptp_hw";
|
||||
|
||||
typedef struct {
|
||||
bool used;
|
||||
uint8_t type;
|
||||
uint16_t seq;
|
||||
uint8_t port_id[10];
|
||||
eth_mac_time_t ts;
|
||||
} rx_rec_t;
|
||||
|
||||
static esp_eth_handle_t s_eth;
|
||||
static rx_rec_t s_rx[RX_RING];
|
||||
static int s_rx_next;
|
||||
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// Runs in the EMAC RX task for every frame: note PTP event timestamps, then pass to lwIP.
|
||||
static esp_err_t rx_hook(esp_eth_handle_t eth, uint8_t *buf, uint32_t len, void *priv, void *info)
|
||||
{
|
||||
const eth_mac_time_t *ts = info;
|
||||
// Ethernet (untagged) + IPv4 + UDP to port 319 + PTP header (34 bytes)
|
||||
if (ts && (ts->seconds | ts->nanoseconds) && len >= 14 + 20 + 8 + 34 &&
|
||||
buf[12] == 0x08 && buf[13] == 0x00 && buf[23] == 17) {
|
||||
const uint8_t *ip = buf + 14;
|
||||
size_t ihl = (ip[0] & 0x0f) * 4;
|
||||
const uint8_t *udp = ip + ihl;
|
||||
const uint8_t *ptp = udp + 8;
|
||||
if (ihl >= 20 && ptp + 34 <= buf + len && ((udp[2] << 8) | udp[3]) == PTP_EVENT_PORT &&
|
||||
(ptp[1] & 0x0f) == 2) {
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
rx_rec_t *r = &s_rx[s_rx_next];
|
||||
s_rx_next = (s_rx_next + 1) % RX_RING;
|
||||
r->used = true;
|
||||
r->type = ptp[0] & 0x0f;
|
||||
r->seq = (ptp[30] << 8) | ptp[31];
|
||||
memcpy(r->port_id, ptp + 20, 10);
|
||||
r->ts = *ts;
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
}
|
||||
}
|
||||
return esp_netif_receive((esp_netif_t *)priv, buf, len, NULL);
|
||||
}
|
||||
|
||||
bool ptp_hw_rx_ts(uint8_t msg_type, uint16_t seq, const uint8_t *src_port_id, eth_mac_time_t *ts)
|
||||
{
|
||||
bool found = false;
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
for (int i = 0; i < RX_RING; i++) {
|
||||
rx_rec_t *r = &s_rx[i];
|
||||
if (r->used && r->type == msg_type && r->seq == seq && memcmp(r->port_id, src_port_id, 10) == 0) {
|
||||
*ts = r->ts;
|
||||
r->used = false;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
esp_err_t ptp_hw_get_time(eth_mac_time_t *t)
|
||||
{
|
||||
return esp_eth_ioctl(s_eth, ETH_MAC_ESP_CMD_G_PTP_TIME, t);
|
||||
}
|
||||
|
||||
esp_err_t ptp_hw_init(esp_eth_handle_t eth, esp_netif_t *netif)
|
||||
{
|
||||
s_eth = eth;
|
||||
bool on = true;
|
||||
esp_err_t err = esp_eth_ioctl(eth, ETH_MAC_ESP_CMD_PTP_ENABLE, &on);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "EMAC PTP enable failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
// IDF enables timestamping for PTP over Ethernet (L2) only; AES67 uses UDP/IPv4.
|
||||
emac_ll_ts_ptp_ip4_enable(&EMAC_PTP, true);
|
||||
|
||||
// The netif glue registered its own input path; take it over and forward to the netif.
|
||||
err = esp_eth_update_input_path_info(eth, rx_hook, netif);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "EMAC IEEE 1588 clock running, HW timestamps for PTP over UDP/IPv4");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
Reference in New Issue
Block a user