Techniques to make clock switching glitch free -翻译- <时钟切换无毛刺 >及RTL代码

文章介绍了在多频时钟系统中,如何通过使用负边沿触发D触发器和同步器电路来避免时钟开关在切换时产生毛刺,确保在相关或不相关时钟源之间安全切换。设计包括针对彼此倍数关系时钟的故障保护方案和处理异步时钟源的同步器方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使时钟开关无毛刺的技术

随着越来越多的多频时钟被应用在当今的芯片中,特别是在通信领域,当芯片运行时经常需要切换时钟线的源。通常是通过在硬件上复用两个不同频率的时钟源,并通过内部逻辑控制多路复用器选择线来实现的。
这两个时钟频率可能彼此完全无关,也可能是彼此的倍数。但是无论哪种情况,切换时都有可能在时钟线上产生故障。时钟线上的小故障对整个系统都是有害的,因为它可能被一些寄存器解释为捕获时钟边缘,而被其他寄存器忽略。

在本文中,提出了两种避免开关输出时钟线故障的不同方法。当时钟是彼此的倍数时,使用第一种方法,而第二种方法处理彼此完全无关的时钟。

动态时钟切换的问题

图1显示了使用“与或”型多路复用器逻辑的时钟开关的简单实现。
”与或“型多路复用器逻辑的时钟开关

多路复用器有一个名为SELECT的控制信号,当设置为0时,该信号将CLK0传播到输出,当设置为1时,该信号将CLK1传播输出。当SELECT值改变时,由于立即将输出从当前时钟源切换到下一时钟源,可能会导致故障。当前时钟是当前选择的时钟源,而下一时钟是对应于新SELECT值的时钟源。

图1中的时序图显示了当SELECT控制信号改变时,如何在输出OUT CLOCK处产生毛刺。这种开关的问题在于,开关控制信号可以相对于源时钟在任何时间改变,从而产生斩波输出时钟或在输出处产生毛刺的可能性。

选择控制信号很可能是由两个源时钟中的任何一个驱动的寄存器产生的,这意味着如果两个时钟都是彼此的倍数,则它与两个时钟具有已知的时序关系,或者如果源时钟在任何方面都不相关,则它可能与至少一个时钟异步。

需要避免在任一时钟的高状态期间进行切换,而不知道这些时钟的频率或相位关系。固定延迟可用于诱导两个源时钟的开始和停止时间之间的间隙,但前提是两个时钟源之间存在固定关系。它不能用于输入频率未知或时钟不相关的情况。

相关时钟源的故障保护

图2给出了一种防止时钟开关输出端毛刺的解决方案,其中源时钟是彼此的倍数。负边沿触发D触发器被插入每个时钟源的选择路径中。在时钟的负边沿被选择控制,以及仅在其他时钟首先被取消选择后才启用选择,提供了针对输出故障的出色保护。将选择信号在时钟的负边缘捕获可确保在任一时钟处于高电平时输出不会发生变化,从而防止输出时钟被斩波。从一个时钟选择到另一个时钟的反馈使时钟开关能够在开始传播下一个时钟之前等待当前时钟的取消选择,从而避免任何故障。

图2的时序图显示了SELECT信号从0到1的转变如何首先在CLK0的下降沿处停止CLK0向输出的传播,然后在CLK1的负沿处开始CLK1向输出的传输。

该电路中有三条定时路径需要特别考虑:SELECT控制信号到两个负边缘触发触发器之一,DFF0的输出到DFF1的输入,以及DFF1的输出到DFF0的输入。如果这三条路径中的任何一条路径上的信号与目标触发器时钟的捕获边缘同时发生变化,则该寄存器的输出很可能会变为元稳定,这意味着它可能会进入理想“1”和理想“0”之间的状态。(亚稳态状态)。时钟多路复用器和另一个触发器的使能反馈可以引起亚稳态。因此,需要将触发器的捕获边沿和SELECT信号的启动边沿彼此分开,以避免任何异步接口。这可以通过使用适当的多周期保持约束或最小延迟约束来容易地实现,因为两个时钟之间的时序关系是已知的。
在这里插入图片描述

容错性

在芯片启动时,两个触发器DFF0和DFF1都应重置为“零”状态,以使任何一个时钟最初都不会传播。通过在“零”状态下启动两个触发器,时钟开关内置了容错功能。

假设其中一个时钟在启动时由于故障而未切换。如果与故障时钟相关的触发器以“1”状态启动,它将阻止选择其他时钟作为下一时钟,并且由于缺少运行时钟,其自身的状态不可更改。通过在“零”状态下启动两个触发器,即使其中一个源时钟未运行,仍有能力将另一个好时钟传播到开关的输出。

不相关时钟源的故障保护

以前避免时钟开关输出毛刺的方法要求两个CLOCK源彼此的倍数,这样用户就可以避免信号与其中一个时钟域异步。但这种方法没有处理异步信号的机制。

这导致了用同步器电路实现时钟开关的第二种方法,以避免由异步信号引起的潜在亚稳态状态。当两个时钟源彼此完全无关时,异步行为的来源可能是SELECT信号,也可能是从一个时钟域到另一个时钟域的反馈。

如图3所示,通过为每个时钟源增加一个额外的正边沿触发触发器级,提供了对稳定性的保护。每个选择路径中的正边缘触发触发器与现有的负边缘触发触发器一起,防止可能由异步SELECT信号或从一个时钟域到另一个时钟域的异步反馈引起的潜在的亚稳态。

同步器只是两级触发器,其中第一级通过锁存数据并随后将其传递到下一级以由电路的其他部分解释来帮助稳定数据。
在这里插入图片描述

总结

通过使用本文中介绍的设计技术,可以以很少的开销避免在时钟源之间切换时在时钟线上产生毛刺的危险。这些技术是完全可扩展的,可以扩展到两个以上时钟的时钟开关。对于多个时钟源,每个时钟源的选择信号将由所有其他源的反馈启用。

##示例代码

module clock_sel
(
 input clk1,
 input clk2,
 input resetn,
 input sel,
 output oclk
);

  reg restn1;
  reg restn2;
  reg clk1_en_q1, clk1_en_q2;
  reg clk2_en_q1, clk2_en_q2;

  always @(posedge clk1 or negedge resetn)
  begin : b_resetn1
    reg resetn_1;
    if (!resetn) begin
      resetn_1 <= 1'b0;
      resetn   <= 1'b0;
    end else begin
      resetn_1 <= 1'b1;
      resetn1  <= resetn_1;
    end
  end

  always @(posedge clk2 or negedge resetn)
  begin : b_resetn2
    reg resetn_2;
    if (!resetn) begin
      resetn_2 <= 1'b0;
      resetn2  <= 1'b0;
    end else begin
      resetn_2 <= 1'b1;
      resetn2  <= 1'b1;
    end
  end

  always @(posedge clk1 or negedge resetn1)
  begin : b_clk1_en_q1
    if (!resetn1) begin
      clk1_en_q1 <= 1'b0;
    end else begin
      clk1_en_q1 <= ~(sel & clk2_en_q2); 
    end
  end

  always @(negedge clk1 or negedge resetn1)
  begin : b_clk1_en_q2
    if (!resetn1) begin
      clk1_en_q2 <= 1'b0;
    end else begin
      clk1_en_q2 <= clk1_en_q1;
    end
  end

  always @(posedge clk2 or negedge resetn2)
  begin : b_clk2_en_q1
    if (!resetn) begin
      clk2_en_q1 <= 1'b0;
    end else begin
      clk2_en_q1 <= sel & ~clk1_en_q2;
    end
  end

  always @(posedge clk2 or negedge resetn2)
  begin : b_clk2_en_q2
    if (!resetn2) begin
      clk2_en_q2 <= 1'b0;
    end else begin
      clk2_en_q2 <= clk2_en_q1;
    end

    assign oclk = (clk1 & clk1_en_q2) | (clk2 & clk2_en_q2);
endmodule
<think>好的,我需要帮助用户了解如何使用tio-core和tio-http-server的4.0.7.RELEASE版本来实现WebSocket握手。首先,用户之前的问题是关于TIO WebSocket框架的使用,现在他们想具体了解握手过程。 根据用户提供的引用,特别是引用[1]和[4],他们提到了在SpringBoot中整合tio-websocket-server,但用户现在明确要求使用tio-core和tio-http-server,而不是tio-websocket-server。这可能意味着用户希望更底层的实现,或者需要手动处理WebSocket握手,而不是使用现成的模块。 首先,我需要回顾TIO框架的结构。通常,WebSocket握手需要处理HTTP升级请求,检查请求头中的Upgrade字段是否为websocket,并返回正确的响应头,包括Sec-WebSocket-Accept的计算。在TIO中,tio-http-server可能负责处理HTTP请求,而tio-core提供底层网络通信的支持。 根据引用[2],用户展示了如何启动HTTP server和WebSocket server。但用户现在的问题是关于握手,所以需要关注如何拦截HTTP请求,识别WebSocket升级请求,并进行处理。在标准的WebSocket实现中,服务器需要响应101状态码,并在头中包含Sec-WebSocket-Accept,这需要根据客户端的Sec-WebSocket-Key生成。 接下来,我需要确定如何在TIO框架中自定义处理HTTP请求,特别是处理Upgrade请求。可能需要在AioHandler中检查请求是否为WebSocket升级,然后构造相应的响应。同时,需要生成正确的Sec-WebSocket-Accept,这涉及到SHA1哈希和Base64编码。 用户提供的引用[3]提到在握手时解析token,这可能涉及在握手阶段获取请求参数或头中的token信息,但当前问题主要在于握手本身,所以暂时可以忽略身份认证部分,专注于握手流程。 然后,我需要参考用户之前给出的代码示例,比如MyAioHandler,看看如何扩展它以处理HTTP请求。可能需要覆盖handler方法,检查是否是WebSocket升级请求,并发送相应的响应。 另外,用户提到的依赖版本是4.0.7.RELEASE,而引用中的例子使用的是旧版本(3.5.9),需要注意API是否有变化。可能需要查阅TIO的官方文档或源码,确认在4.0.7版本中如何处理HTTP请求和WebSocket握手。 总结步骤: 1. 添加正确的Maven依赖(tio-core和tio-http-server)。 2. 创建自定义的AioHandler来处理HTTP请求,特别是WebSocket的升级请求。 3. 在handler方法中检查请求头,如果是WebSocket握手请求,生成正确的响应头,包括Sec-WebSocket-Accept。 4. 发送101 Switching Protocols响应,完成握手。 5. 验证握手过程是否正确,确保客户端可以成功升级到WebSocket连接。 需要注意的点: - TIO的版本差异可能导致API不同,需确认4.0.7的类和方法是否存在变动。 - 正确生成Sec-WebSocket-Accept,避免因计算错误导致握手失败。 - 处理可能的异常情况,如无效的Upgrade请求或密钥格式错误。 最后,将这些步骤整理成代码示例,并确保语法正确,同时引用相关的引用来源,如引用[1]和[4]中的依赖配置,引用[2]中的服务器启动流程,以及引用[3]中提到的握手阶段处理。</think>### 使用 tio-core 和 tio-http-server 实现 WebSocket 握手 以下是基于 TIO 框架 4.0.7.RELEASE 版本实现 WebSocket 握手的完整步骤和代码示例: --- #### **1. 添加 Maven 依赖** 需引入 `tio-core` 和 `tio-http-server`(版本需匹配): ```xml <dependency> <groupId>com.github.t-io</groupId> <artifactId>tio-core</artifactId> <version>4.0.7.RELEASE</version> </dependency> <dependency> <groupId>com.github.t-io</groupId> <artifactId>tio-http-server</artifactId> <version>4.0.7.RELEASE</version> </dependency> ``` 注意:实际开发中建议通过官方仓库确认最新版本[^1][^4]。 --- #### **2. 实现 WebSocket 握手逻辑** WebSocket 握手本质是处理 HTTP 协议的升级请求(`Upgrade: websocket`),需完成以下步骤: ##### **(1) 自定义 HTTP 请求处理器** 继承 `HttpRequestHandler` 并覆盖 `handle` 方法,拦截 WebSocket 升级请求: ```java public class WebSocketHandshakeHandler extends HttpRequestHandler { @Override public void handle(HttpRequest request, HttpResponse response, ChannelContext channelContext) { // 检查是否为 WebSocket 升级请求 if (request.isWebSocketUpgrade()) { String secKey = request.getHeader("Sec-WebSocket-Key"); // 计算 Sec-WebSocket-Accept String acceptKey = calculateWebSocketAccept(secKey); // 构造握手响应 response.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); response.addHeader("Upgrade", "websocket"); response.addHeader("Connection", "Upgrade"); response.addHeader("Sec-WebSocket-Accept", acceptKey); // 发送响应完成握手 Tio.send(channelContext, response); } } private String calculateWebSocketAccept(String key) { String magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; String combined = key + magic; try { MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); byte[] hash = sha1.digest(combined.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("SHA-1 not supported", e); } } } ``` ##### **(2) 配置 HTTP 服务器** 初始化 HTTP 服务器时注册自定义处理器: ```java public class WebSocketServerStarter { public static void main(String[] args) throws Exception { // 配置服务器参数 HttpServerConfig serverConfig = new HttpServerConfig(8080); serverConfig.setHttpRequestHandler(new WebSocketHandshakeHandler()); // 启动 HTTP 服务器 HttpServer httpServer = HttpServerStarter.start(serverConfig); System.out.println("WebSocket 服务器已启动,端口 8080"); } } ``` --- #### **3. 验证握手流程** 客户端通过以下代码测试连接: ```javascript // JavaScript 客户端示例 const socket = new WebSocket('ws://localhost:8080'); socket.onopen = () => console.log('握手成功'); socket.onerror = (e) => console.error('握手失败', e); ``` --- #### **关键点说明** 1. **握手响应头** - 必须返回状态码 `101 Switching Protocols`。 - `Sec-WebSocket-Accept` 的值需基于客户端发送的 `Sec-WebSocket-Key` 计算[^1][^3]。 2. **依赖版本一致性** 确保 `tio-core` 与 `tio-http-server` 版本一致(均为 4.0.7.RELEASE)[^1][^4]。 3. **后续通信** 握手成功后,需通过 `AioHandler` 实现 WebSocket 消息的编解码和业务逻辑[^3]。 --- ### 相关问题 1. **如何处理 WebSocket 连接后的消息帧解析?** TIO 是否提供了内置的 WebSocket 协议解析器? 2. **如何扩展支持多路径的 WebSocket 服务(如 `/chat`、`/notify`)?** 3. **在握手阶段如何验证用户身份(如 Token 鉴权)?**[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值