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

前言

在很多应用场景有精确对时的需求,例如车载网络,音视频流,工业网络。本文档将会阐述对时的硬件需求,网卡硬件以 Synopsys DesignWare Cores Ethernet MAC QoS 为例来展开说明。

协议

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

网卡硬件要求

找到 DW qos 网卡的特性描述: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_SYNCnow.tv_sec = 0;now.tv_nsec = 0;#elsektime_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); // 获取时间戳}

接收报文时间

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

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django 静态文件管理与部署指南

title: Django 静态文件管理与部署指南 date: 2024/5/10 17:38:36 updated: 2024/5/10 17:38:36 categories: 后端开发 tags: WebOptCDN加速DjangoCompressWebpackStaticDeployCICD-ToolsSecStatic 第一章&#xff1a;介绍 Django 静态文件的概念和重要性 在 Web 开发中&a…

爆爽,英语小白怒刷 50 课!像玩游戏一样学习英语~

重点!!!(先看这) 清楚自己学英语的目的, 先搞清楚目标&#xff0c;再行动自身现在最需要的东西&#xff1a;词汇量&#xff1f;口语&#xff1f;还是阅读能力&#xff1f;找对应的书籍,学习资料往兴趣靠拢&#xff1a;网上有大量的推荐美剧学习、小说学习&#xff0c;不要被他…

技术创作者在千帆AppBuilder中获得的极致体验

目录 前言 千帆AppBuilder简介 传统的技术文章写作方式 借助千帆AppBuilder提高写作质量和效率 千帆AppBuilder详细搭建步骤 1、注册百度智能云账号 2、登录百度智能云控制台 3、创建千帆AppBuilder应用 4、配置千帆AppBuilder应用 5、调试和发布千帆AppBuilder应用 …

你写的每条SQL都是全表扫描吗

你写的每条SQL都是全表扫描吗&#xff1f;如果是&#xff0c;那MySQL可太感谢你了&#xff0c;每一次SQL执行都是在给MySQL上压力、上对抗。MySQL有苦难言&#xff1a;你不知道索引吗&#xff1f;你写的SQL索引都失效了不知道吗&#xff1f;慢查询不懂啊&#xff1f;建那么多索…

Dependencies:查找项目中dll关联文件是否缺失。

前言 Dependencies工具作为一款优秀的DLL解析工具&#xff0c;能让你很直观地看到DLL的相关信息&#xff0c;如具备哪些功能函数、参数&#xff0c;又比如该DLL基于哪些DLL运行。判断该dll基于哪些dll运行&#xff0c;如果基于的dll丢失&#xff0c;那么就会提示。就能判断缺少…

Docker笔记(七)使用Docker部署Spring Boot项目

本文介绍如何使用Docker打包并部署Spring Boot多模块项目。 其中本文涉及的Docker的私库是用Nexus3搭建的。 使用Docker部署Spring Boot项目有三种方式 &#xff08;1&#xff09;使用 spring-boot-maven-plugin内置的build-image. &#xff08;2&#xff09;使用 Google 的 j…

手把手系列!使用 Zilliz Cloud 和 AWS Bedrock 搭建 RAG 应用

检索增强生成&#xff08;Retrieval Augemented Generation, RAG&#xff09;是一种 AI 框架&#xff0c;它通过结合信息检索和自然语言处理&#xff08;NLP&#xff09;能力从而增强文本生成。具体而言&#xff0c;RAG 系统中的语言模型通过一种检索机制查询和搜索知识库或外部…

rocketmq控制台部署

网络找rocketmq控制台有源码包&#xff0c;还需要编译太麻烦&#xff0c;找到了 rocketmq-dashboard-1.0.1-SNAPSHOT.jar已经编译后的jar&#xff0c;只有简单修改服务器端口和监控rocketmq集群地址和端口就可以。需要jar资源的可以咨询本人。 步骤1&#xff1a;根据需要配置ro…

LinkedList链表

LinkedList 的全面说明 LinkList底层实现了双向链表和双端队列特点可以添加任意元素&#xff08;元素可以重复&#xff09;&#xff0c;包括null线程不安全&#xff0c;没有实现同步 LinkedList 的底层操作机制 LinkedList底层维护了一个双向链表LinkList中维护了两个属性fi…

VBA_NZ系列工具NZ06:VBA创建PDF文件说明

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

CoT个人记录

1.Few-shot COT&#xff08;CoT, 思维链&#xff09; 通过向大语言模型展示一些少量的例子(Few-shot )&#xff0c;在样例中解释推理过程&#xff0c;大语言模型在回答时也会模拟人类思考推理的过程生成中间的推理步骤,&#xff0c;再得到答案。这种推理的解释往往会引导出更准…

1725 ssm资产管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java ssm资产管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/…