【Verilog学习日常】—牛客网刷题—Verilog企业真题—VL69

脉冲同步器(快到慢)

描述

sig_a 是 clka(300M) 时钟域的一个单时钟脉冲信号(高电平持续一个时钟clka周期),请设计脉冲同步电路将sig_a信号同步到时钟域 clkb(100M)中 ,++产生sig_b单时钟脉冲信号(高电平持续一个时钟clkb周期)输出。++ 请用 Verilog 代码描述。

clka时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题。

电路的接口如下图所示:

输入描述:

input clka ,

input clkb ,

input rst_n ,

input sig_a ,

输出描述:

output sig_b

解题思路

在了解脉冲同步器的工作原理之前,我们先来了解"单比特信号同步"的相关概念:

主要参考以下博文:

"单比特信号同步" 学习笔记

单比特信号同步

概述
  • 信号同步的目的是++防止新时钟域中第一级触发器的亚稳态信号对下一级逻辑造成影响++ 。简单的同步器由两个触发器串联而成,中间没有其它组合电路。这种设计可以保证后面的触发器获得前一个触发器输出时,前一个触发器已退出了亚稳态,并且其输出已稳定。
信号同步的要求
  • 为了使同步工作能正常进行,从某个时钟域传来的信号应++先通过原时钟域上的一个触发器++ ,然后不经过两个时钟域间的任何组合逻辑**直接进入同步器的第一个触发器中。**这一要求非常重要,因为同步器的第一级触发器对组合逻辑所产生的毛刺非常敏感。如果一个足够长的毛刺正好满足建立-保持时间的要求,则同步器的第一级触发器会将其放行,给新时钟域的后续逻辑送出一个虚假的信号。
同步造成的延时
  • 一个经同步后的信号在两个时钟沿以后就成为新时钟域中的有效信号。信号的延迟是新时钟域的一到两个时钟周期。一种粗略的估算方法是同步器电路在新时钟域中造成++两个时钟周期的延迟++,设计者需要考虑同步延迟对跨时钟域的信号时序造成的影响。
三种常用的同步器(重点)
  • **电平(level signal)**同步器

在电平同步器中,跨时钟域的信号在新时钟域中要保持高电平或低电平两个时钟周期以上。同步之后的信号是电平的形式 ,而++该电平所维持的时钟周期个数是其在跨时钟域期间被上升沿检测到的次数++;

电平同步器设计电路如下:

  • **边沿(edge detecting)**检测同步器

边沿检测同步器在电平同步器的输出端增加了一个触发器。新增触发器的输出经反相 后和电平同步器的输出进行操作;

该电路会检测同步器输入的上升沿,产生一个与时钟周期等宽、高电平有效的脉冲。

如果将与门的两个输入端交换 使用,就可以构成一个++检测输入信号下降沿的同步器++。

与门改为非门 可以构建一个++产生低电平有效脉冲的电路++。当一个脉冲进入更快的时钟域中时,边沿检测同步器可以工作的很好,该电路会产生一个脉冲,用来指示输入信号上升或下降沿。

该电路有一个限制,即输入脉冲的宽度必须大于同步时钟周期与第一个同步触发器所需保持时间之和。最保险的脉冲宽度是同步器时钟周期的两倍。如果输入是一个单时钟宽度脉冲进入一个较慢的时钟域,则这种同步器没有作用,在这种情况下就要采用脉冲同步器。

  • **脉冲(Pulse)**同步器

脉冲同步器工作原理

主要参考以下博文:

CDC(二) 单bit 脉冲跨时钟域处理

verilog设计-CDC:单bit脉冲快时钟域到慢时钟域

脉冲同步器 就是带边沿检测的单bit同步器,基本原理就是把++脉冲信号进行展宽++。

脉冲同步器应用场景
  • 适用于单bit脉冲信号跨时钟域。++慢到快,快到慢++均可,源脉冲间隔至少要为2个目的时钟周期,否则会被漏采。当然,在慢到快时钟比率大于2倍以上时也是可以实时采样的。
脉冲同步器原理
  • 脉冲同步器的原理为将快时钟域的脉冲拓展为电平信号,使得该电平的宽度大于慢时钟域 的时钟周期,从而可以使用两级同步器进行同步,然后在同步后的时钟域进行脉冲恢复,从而完成将信号(尤其是脉冲信号)从快时钟域传递到慢时钟域。
  • 电路设计图如下所示:
脉冲同步器电路特点
  1. 该电路只能传递脉冲信号(也还能传递一直是0的信号,只不过工程意义不大)。对于输入信号一直是1,或是只有0到1的阶跃信号,只有1到0的信号均无法正确传输;
  2. 该电路将输入端的脉冲传输给慢时钟域,无论输入端的脉冲有多宽,都只能在慢时钟域恢复成一个时钟周期宽度的脉冲;
  3. 输入信号的脉冲之间间隔要大于1倍的慢时钟周期,否则连着的两个脉冲问题会在慢时钟域合并成一个脉宽为2个满时钟周期的脉冲,导致丢失一个脉冲。
完整代码如下
cpp 复制代码
`timescale 100ps/100ps

module pulse_detect(
	input 				clka	, 
	input 				clkb	,   
	input 				rst_n		,
	input				sig_a		,

	output  		 	sig_b
);
    reg sig_a_r;
	reg q_buff0, q_buff1;
    //1.翻转电路部分(快时钟域)
	always @(posedge clka or negedge rst_n) begin
		if (!rst_n) sig_a_r = 1'b0;
		else begin
			if (sig_a)	sig_a_r = ~sig_a_r;
			else 	sig_a_r = sig_a_r;
		end
	end
    //2.电平同步器部分
	always @(posedge clkb or negedge rst_n) begin
		if (!rst_n)  begin q_buff0 <= 1'b0; 	q_buff1 <= 1'b0;  end
				else begin q_buff0 <= sig_a_r;  q_buff1 <= q_buff0; end
	end


	reg q_slow;

	always @(posedge clkb or negedge rst_n) begin
		if (!rst_n) begin q_slow <= 1'b0;  end
		else 		begin q_slow <= q_buff1; end
	end

	assign sig_b = q_buff1 ^ q_slow;
endmodule

另一种写法:单独例化D触发器;

cpp 复制代码
`timescale 100ps/100ps

module pulse_detect(
	input 				clka	, 
	input 				clkb	,   
	input 				rst_n		,
	input				sig_a		,

	output  		 	sig_b
);
    reg sig_a_r;
	wire q_buff0, q_buff1, q_slow;

	always @(posedge clka or negedge rst_n) begin
		if (!rst_n) sig_a_r = 1'b0;
		else begin
			if (sig_a)	sig_a_r = ~sig_a_r;
			else 	sig_a_r = sig_a_r;
		end
	end

	DFF_R D0 (.D(sig_a_r), .clk(clkb), .rst_n(rst_n), .Q(q_buff0));
	DFF_R D1 (.D(q_buff0), .clk(clkb), .rst_n(rst_n), .Q(q_buff1));
	DFF_R D2 (.D(q_buff1), .clk(clkb), .rst_n(rst_n), .Q(q_slow));

	assign sig_b = q_buff1 ^ q_slow;

endmodule

//带异步复位端的D触发器
module DFF_R(
	input D,
	input clk,
	input rst_n,

	output reg	Q
);
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) Q <= 1'b0;
		else 		Q <= D;
	end
endmodule
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
ZPC82104 天前
docker 镜像备份
人工智能·算法·fpga开发·机器人
ZPC82104 天前
docker 使用GUI ROS2
人工智能·算法·fpga开发·机器人
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习