PTP 对时协议 IEEE1588 网络对时 硬件基础

前言

在很多应用场景有精确对时的需求,例如车载网络,音视频流,工业网络。本文档将会阐述对时的硬件需求。

协议

流行的协议为 IEEE1588 标准指定的对时方法,名为 PTP 对时协议。

网卡硬件要求

找到某型网卡的特性描述:Standard for a Precision Clock Synchronization Protocol for Networked Measurement and Control Systems, Standard 1588-2008, IEEE

网卡相关寄存器

  • MAC_Timestamp_Control
  • MAC_Sub_Second_Increment
  • MAC_System_Time_Seconds
  • MAC_System_Time_Nanoseconds
  • MAC_System_Time_Seconds_Update
  • MAC_System_Time_Nanoseconds_Update
  • MAC_Timestamp_Addend
  • MAC_System_Time_Higher_Word_Seconds
  • Timestamp Status register

MAC_Sub_Second_Increment 用于设置系统亚秒增加

Timestamp Addend register的值会被用于微调系统时间,在Fine Update模式下,系统会根据这个值以微小的步长来更新时间。这样可以实现对系统时间的精细调整,以满足精确时间同步的需求。

MAC_System_Time_Nanoseconds + MAC_System_Time_Seconds 从这2个寄存器读出时间

MAC_System_Time_Nanoseconds_Update + MAC_System_Time_Seconds_Update 写入这2个寄存器配置时间

配置代码

tstamp 的初始化代码

配置了 MAC_Sub_Second_Increment 和 Timestamp Addend register

/**
 * stmmac_init_tstamp_counter - init hardware timestamping counter
 * @priv: driver private structure
 * @systime_flags: timestamping flags
 * Description:
 * Initialize hardware counter for packet timestamping.
 * This is valid as long as the interface is open and not suspended.
 * Will be rerun after resuming from suspend, case in which the timestamping
 * flags updated by stmmac_hwtstamp_set() also need to be restored.
 */
int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
{
	bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
	struct timespec64 now;
	u32 sec_inc = 0;
	u64 temp = 0;
	int ret;

	if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
		return -EOPNOTSUPP;

	ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
	if (ret < 0) {
		netdev_warn(priv->dev,
			    "failed to enable PTP reference clock: %pe\n",
			    ERR_PTR(ret));
		return ret;
	}

	stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
	priv->systime_flags = systime_flags;

	/* program Sub Second Increment reg */// 配置 MAC_Sub_Second_Increment 
	stmmac_config_sub_second_increment(priv, priv->ptpaddr,
					   priv->plat->clk_ptp_rate,
					   xmac, &sec_inc);
	temp = div_u64(1000000000ULL, sec_inc);

	/* Store sub second increment for later use */
	priv->sub_second_inc = sec_inc;

//配置 Timestamp Addend register
	/* calculate default added value:
	 * formula is :
	 * addend = (2^32)/freq_div_ratio;
	 * where, freq_div_ratio = 1e9ns/sec_inc
	 */
	temp = (u64)(temp << 32);
	priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
	stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);

	/* initialize system time */
	#ifdef CONFIG_SEMIDRIVE_TIME_SYNC
	now.tv_sec = 0;
	now.tv_nsec = 0;
	#else
	ktime_get_real_ts64(&now);
	#endif

	/* lower 32 bits of tv_sec are safe until y2106 */
	stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec);

	return 0;
}
更新时间的代码
#include "stmmac.h"
#include "stmmac_ptp.h"

/**
 * stmmac_adjust_freq
 *
 * @ptp: pointer to ptp_clock_info structure
 * @ppb: desired period change in parts ber billion
 *
 * Description: this function will adjust the frequency of hardware clock.
 */
static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
{
	struct stmmac_priv *priv =
	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
	unsigned long flags;
	u32 diff, addend;
	int neg_adj = 0;
	u64 adj;

	if (ppb < 0) {
		neg_adj = 1;
		ppb = -ppb;
	}

	addend = priv->default_addend;
	adj = addend;
	adj *= ppb;
	diff = div_u64(adj, 1000000000ULL);
	addend = neg_adj ? (addend - diff) : (addend + diff);

	spin_lock_irqsave(&priv->ptp_lock, flags);
	stmmac_config_addend(priv, priv->ptpaddr, addend);
	spin_unlock_irqrestore(&priv->ptp_lock, flags);

	return 0;
}

/**
 * stmmac_adjust_time
 *
 * @ptp: pointer to ptp_clock_info structure
 * @delta: desired change in nanoseconds
 *
 * Description: this function will shift/adjust the hardware clock time.
 */
static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
{
	struct stmmac_priv *priv =
	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
	unsigned long flags;
	u32 sec, nsec;
	u32 quotient, reminder;
	int neg_adj = 0;
	bool xmac;

	xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;

	if (delta < 0) {
		neg_adj = 1;
		delta = -delta;
	}

	quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
	sec = quotient;
	nsec = reminder;

	spin_lock_irqsave(&priv->ptp_lock, flags);
	stmmac_adjust_systime(priv, priv->ptpaddr, sec, nsec, neg_adj, xmac);
	spin_unlock_irqrestore(&priv->ptp_lock, flags);

	return 0;
}

/**
 * stmmac_get_time
 *
 * @ptp: pointer to ptp_clock_info structure
 * @ts: pointer to hold time/result
 *
 * Description: this function will read the current time from the
 * hardware clock and store it in @ts.
 */
static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
	struct stmmac_priv *priv =
	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
	unsigned long flags;
	u64 ns = 0;

	spin_lock_irqsave(&priv->ptp_lock, flags);
	stmmac_get_systime(priv, priv->ptpaddr, &ns);
	spin_unlock_irqrestore(&priv->ptp_lock, flags);

	*ts = ns_to_timespec64(ns);

	return 0;
}

/**
 * stmmac_set_time
 *
 * @ptp: pointer to ptp_clock_info structure
 * @ts: time value to set
 *
 * Description: this function will set the current time on the
 * hardware clock.
 */
static int stmmac_set_time(struct ptp_clock_info *ptp,
			   const struct timespec64 *ts)
{
	struct stmmac_priv *priv =
	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
	unsigned long flags;

	spin_lock_irqsave(&priv->ptp_lock, flags);
	stmmac_init_systime(priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec);
	spin_unlock_irqrestore(&priv->ptp_lock, flags);

	return 0;
}

static int stmmac_enable(struct ptp_clock_info *ptp,
			 struct ptp_clock_request *rq, int on)
{
	struct stmmac_priv *priv =
	    container_of(ptp, struct stmmac_priv, ptp_clock_ops);
	struct stmmac_pps_cfg *cfg;
	int ret = -EOPNOTSUPP;
	unsigned long flags;

	switch (rq->type) {
	case PTP_CLK_REQ_PEROUT:
		/* Reject requests with unsupported flags */
		if (rq->perout.flags)
			return -EOPNOTSUPP;

		cfg = &priv->pps[rq->perout.index];

		cfg->start.tv_sec = rq->perout.start.sec;
		cfg->start.tv_nsec = rq->perout.start.nsec;
		cfg->period.tv_sec = rq->perout.period.sec;
		cfg->period.tv_nsec = rq->perout.period.nsec;

		spin_lock_irqsave(&priv->ptp_lock, flags);
		ret = stmmac_flex_pps_config(priv, priv->ioaddr,
					     rq->perout.index, cfg, on,
					     priv->sub_second_inc,
					     priv->systime_flags);
		spin_unlock_irqrestore(&priv->ptp_lock, flags);
		break;
	default:
		break;
	}

	return ret;
}

时间使用

上面配置了网卡相关的硬件时间寄存器,那么网卡具有了硬件计时的能力了,在 PTP 对时中如何使用这个能力呢?

发送报文时间

如何获得报文从网卡发送的精确时间?很容易想到的办法就是在触发网卡DMA发送的时刻马上读上面提到的时间寄存器。这种方法可能引入几个问题:若是这个连续操作被中断打断了怎么办?若是网卡队列里还有其他没有发送的其他包,软件触发了DMA发送,但是硬件并没有及时发送出去怎么办?
实际解决办法是:由网卡的硬件实现的,网卡在把网络包发送出去的同时,往指定的内存里保存发送的时间数据,这个指定的内存就是发送描述符。
在这里插入图片描述
代码里这样读取

static inline void dwmac4_get_timestamp(void *desc, u32 ats, u64 *ts)
{
	struct dma_desc *p = (struct dma_desc *)desc;
	u64 ns;

	ns = le32_to_cpu(p->des0);  // 读描述符
	/* convert high/sec time stamp value to nanosecond */
	ns += le32_to_cpu(p->des1) * 1000000000ULL;

	*ts = ns;
}


/* stmmac_get_tx_hwtstamp - get HW TX timestamps
 * @priv: driver private structure
 * @p : descriptor pointer
 * @skb : the socket buffer
 * Description :
 * This function will read timestamp from the descriptor & pass it to stack.
 * and also perform some sanity checks.
 */
static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
				   struct dma_desc *p, struct sk_buff *skb)
{
	struct skb_shared_hwtstamps shhwtstamp;
	bool found = false;
	u64 ns = 0;

	if (!priv->hwts_tx_en)
		return;

	/* exit if skb doesn't support hw tstamp */
	if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
		return;

	/* check tx tstamp status */
	if (stmmac_get_tx_timestamp_status(priv, p)) {
		stmmac_get_timestamp(priv, p, priv->adv_ts, &ns);
		found = true;
	} else if (!stmmac_get_mac_tx_timestamp(priv, priv->hw, &ns)) {
		found = true;
	}

	if (found) {
		memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
		shhwtstamp.hwtstamp = ns_to_ktime(ns);

		netdev_dbg(priv->dev, "get valid TX hw timestamp %llu\n", ns);
		/* pass tstamp to stack */
		skb_tstamp_tx(skb, &shhwtstamp); // 保存到协议栈里
	}
}

/**
 * stmmac_tx_clean - to manage th
 * e transmission completion
 * @priv: driver private structure
 * @budget: napi budget limiting this functions packet handling
 * @queue: TX queue index
 * Description: it reclaims the transmit resources after transmission completes.
 */
static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
{
	struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
	unsigned int bytes_compl = 0, pkts_compl = 0;
	unsigned int entry, count = 0;


省略部分代码
		/* Make sure descriptor fields are read after reading
		 * the own bit.
		 */
		dma_rmb();

		/* Just consider the last segment and ...*/
		if (likely(!(status & tx_not_ls))) {
			/* ... verify the status error condition */
			if (unlikely(status & tx_err)) {
				priv->dev->stats.tx_errors++;
			} else {
				priv->dev->stats.tx_packets++;
				priv->xstats.tx_pkt_n++;
			}
			stmmac_get_tx_hwtstamp(priv, p, skb); // 获取时间戳
		}

接收报文时间

与发送的类似,也是保存在描述符里,不赘述。

总结

至此,由网卡硬件实现的硬件精确时间对时的基础已经分析完毕。主要精髓是网卡会自动保存发送和接收的时间到描述符里,这个时刻及其精确,不受代码运行抖动的影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/608534.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Antd Table组件,state改变,但是render并不会重新渲染

背景 在table上面&#xff0c;当鼠标放在cell上面的时候&#xff0c;需要去请求接口拉取数据&#xff0c;然后setList(res.result)后&#xff0c;希望render中的traceIds也能够实时更新渲染。 const [traceIds, setTraceIds] useState() // 需要展示在popover上面的数据&…

基于STM32F401RET6智能锁项目(环境搭建)

工程搭建 MDK&#xff0c;固件库&#xff0c;芯片包下载 下载keil5&#xff0c;stm32f4xx的固件库以及stm32f4的芯片包 keil官网&#xff1a;https://www2.keil.com/mdk5/ stm32中国官网&#xff1a;https://www.stmcu.com.cn/ 创建工程 1、新建一个工程文件夹&#xff0c;…

漫威争锋Marvel Rivals怎么搜索 锁区怎么搜 游戏搜不到怎么办

即将问世的《漫威争锋》&#xff08;Marvel Rivals&#xff09;作为一款万众期待的PvP射击游戏新星&#xff0c;荣耀携手漫威官方网站共同推出。定档5月11日清晨9时&#xff0c;封闭Alpha测试阶段将正式揭开序幕&#xff0c;持续时间长达十天之久。在此首轮测试窗口&#xff0c…

加速科技突破2.7G高速数据接口测试技术

随着显示面板分辨率的不断提升&#xff0c;显示驱动芯片&#xff08;DDIC&#xff09;的数据接口传输速率越来越高&#xff0c;MIPI、LVDS/mLVDS、HDMI等高速数据接口在DDIC上广泛应用。为满足高速数据接口的ATE测试需求&#xff0c;作为国内少数拥有完全自研的LCD Driver测试解…

Facebook消息群发脚本的制作思路!

在数字化社交日益盛行的今天&#xff0c;Facebook作为全球最大的社交平台之一&#xff0c;为企业和个人提供了广阔的交流与合作空间。 然而&#xff0c;手动向大量用户发送消息既耗时又低效&#xff0c;因此&#xff0c;开发一款能够自动群发消息的脚本成为了许多人的需求&…

JavaWeb之Servlet(上)

前言 1. 什么是Servlet (1) Servlet介绍 (2) Servlet运行于支持Java的应用服务器中。 (3) Servlet工作模式&#xff1a; 2. Servlet API 3. 第一个Servlet (1) 创建一个类实现Servlet接口,重写方法。或继承HttpServlet亦可 (2) 在web.xml文档中配置映射关系 标签的执行…

48. UE5 RPG 实现攻击伤害数字显示

在前面的文章中&#xff0c;我们实现了对敌人的攻击的受击效果&#xff0c;并且能够降低目标的血量&#xff0c;实现死亡效果。相对于正常的游戏&#xff0c;我们还需要实现技能或者攻击对敌人造成的伤害数值&#xff0c;并直观的显示出来。 所以&#xff0c;接下来&#xff0c…

电商核心技术揭秘52:数字化内容营销创新

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘相关系列文章合集&#xff08;3&#xff09; 电商技术揭秘四十一&#xff1a;电商平台的营销系统浅析 电商技术揭秘四十二&#…

通过Docker Compose部署GitLab和GitLab Runner(一)

GitLab 是一个用于版本控制、项目管理和持续集成的开源软件平台&#xff0c;它提供了一整套工具&#xff0c;能够帮助团队高效地协作开发。而 GitLab Runner 则是 GitLab CI/CD 的执行者&#xff0c;用于运行持续集成和持续交付任务。 在本文中&#xff0c;我们将使用 Docker …

虚拟机装CentOS镜像

起先&#xff0c;是先安装一个VM虚拟机&#xff0c;再去官方网站之类的下载一些镜像&#xff0c;常见镜像有CentOS镜像&#xff0c;ubantu镜像&#xff0c;好像还有一个树莓还是什么的&#xff0c;软件这块&#xff0c;日新月异&#xff0c;更新太快&#xff0c;好久没碰&#…

C程序内存分布及static变量

C程序内存分布及static变量 C语言中程序的内存分布 [&#x1f517;1](https://www.cnblogs.com/miaoxiong/p/11021827.html)[&#x1f517;2](https://blog.csdn.net/chen1083376511/article/details/54930191)c/c编译连接后二进制文件的存储动静态存储方式和存储区动态存储方式…

贪心算法--将数组和减半的最小操作数

本题是力扣2208---点击跳转题目 思路&#xff1a; 要尽快的把数组和减小&#xff0c;那么每次挑出数组中最大的元素减半即可&#xff0c;由于每次都是找出最值元素&#xff0c;可以用优先队列来存储这些数组元素 每次取出最值&#xff0c;减半后再放入优先队列中&#xff0c;操…

Centos7使用kubeadm搭建k8s集群(一主两从)----(mac版)

一、环境准备 1、下载centos7镜像 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 下载地址: centos安装包下载_开源镜像站-阿里云 选择对应的版本即可&#xff0c;我下载的&#xff1a;CentOS-7-x86_64-DVD-2207-02.iso 2、使用VirtualBox安装centos 选择新建&#xff0c…

Rust 通用代码生成器莲花,红莲尝鲜版二十三,多对多候选,增强数据库反射项目功能

Rust 通用代码生成器莲花&#xff0c;红莲尝鲜版二十三&#xff0c;此版本新增了多对多候选功能&#xff0c;增强了数据库自动反射功能和模板向导的编辑器。请部署在 Tomcat9 的 webapps 目录下。 多对多候选功能大大增强了一个数据库自动反射成一个项目的功能&#xff0c;它可…

Docx文件误删除如何恢复?别再花冤枉钱了,4个高效恢复软件!

不管是工作还是学习&#xff0c;总是会与各种各样的文件打交道。文件量越多就越容易出现文件丢失、文件误删的情况。遇到这些情况&#xff0c;失去的文件还能找回来吗&#xff1f;只要掌握了一些数据恢复方法&#xff0c;是很有机会恢复回来的&#xff0c;下面我会将这些方法分…

MacBook Pro(Intel集成显卡)成功安装启动ComfyUI详细教程

Mac配置 MAC CPU: 2.2 GHz 四核Intel Core i7MAC 系统版本&#xff1a;12.3MAC 显卡&#xff1a; Intel Iris Pro 1536 MBMAC 内存&#xff1a;16Gpython 3.12.2 ComfyUI 的安装方法介绍 ComfyUI 是一个模块化的 Stable Diffusion GUI&#xff0c;工作界面是可视化的流程节点…

图神经网络——GCN,GraphSAGE

1、应用 生物化学&#xff1a;分子指纹识别、药物分子设计、疾病分类等 交通领域&#xff1a;对交通需求的预测、对道路速度的预测 计算机图像处理&#xff1a;目标检测、视觉推理等 自然语言处理&#xff1a;实体关系抽取、关系推理等 2、数据集介绍 CORA数据集由2708篇论文&…

影视极品转场音效大全,经典获奖通用音效素材

一、素材描述 本套音效素材&#xff0c;大小15.02G&#xff0c;16个压缩文件。 二、素材目录 01-华纳兄弟电影音效库合辑&#xff08;2个压缩文件&#xff09; 02-影视极品转场音效&#xff08;2个压缩文件&#xff09; 03-好莱坞经典综合音效&#xff08;4个压缩文件&…

线程安全问题、同步代码块、同步方法

线程安全问题就是 用线程同步来解决线程安全问题 同步&#xff1a;一个线程接着一个线程等待执行 同步代码块&#xff1a; 通过锁来解决卖到重复票的问题&#xff1a;卖票问题和存钱取钱问题&#xff08;见其他两篇文章&#xff09; 同步方法&#xff1a;

杨辉三角的打印

题目内容&#xff1a; 在屏幕上打印杨辉三角。 思路&#xff1a; 首先我们通过观察发现&#xff0c;每一步的打印都与行列数有关&#xff0c;中间的数据由这一列和上一行的前一列数据控制。所以我们可以使用二维数组进行操作&#xff1a; &#xff08;&#xff11;&#xff…