题解 | #多bit MUX同步器#
多bit MUX同步器
https://www.nowcoder.com/practice/30e355a04a454e16811112cb82af591e
思路
这个题我是看别人的题解。因为看题目我是一脸懵。根本不知道在干什么......
其实题目表达得不太清楚,题目是说,有两个时钟域clk_a和clk_b。现在来自clk_a的数据data_in让我们同步到clk_b的时钟域中去。题目还有一个条件没有说清楚,就是一个clk_b的时间是比clk_a长的,clk_b打一拍的时间够clk_a打两拍。
题目理解了,下面就是做题。这本质上就是一个跨时钟域处理的问题,跨时钟域处理涉及到亚稳态的问题。
什么是亚稳态呢?
举一个例子。在抛硬币的时候,硬币可以是正面也可以是反面,这是确定的。但是硬币在空中的过程,我们是不知道它是正面还是反面的。假设把触发器看做硬币,当信号到达触发器相当于抛硬币,在信号输出触发器相当于抛硬币的结果;如果在信号到达触发器时,时钟信号正好在改变,就会造成采样了多种输入信号,在一段时间内,输出信号也是不稳定的。这就是亚稳态。
如何解决亚稳态呢?在时钟域对输入信号进行多次采样。
代码
`timescale 1ns/1ns module mux( input clk_a , input clk_b , input arstn , input brstn , input [3:0] data_in , input data_en , output reg [3:0] dataout ); reg [3:0]data_in_reg; reg data_en_reg; reg data_en_b0,data_en_b1; //对data_in和data_en打一拍 always @(posedge clk_a or negedge arstn ) begin if (!arstn)begin data_in_reg<=0; data_en_reg<=0; end else begin data_in_reg<=data_in; data_en_reg<=data_en; end end //对data_en在clk_b下打两拍 always @(posedge clk_b or negedge brstn ) begin if (!brstn)begin data_en_b0<=0; data_en_b1<=0; end else begin data_en_b0<=data_en_reg; data_en_b1<=data_en_b0; end end //待同步之后输出dataout always @(posedge clk_b or negedge brstn ) begin if (!arstn)dataout<=0; else begin if(data_en_b1)dataout<=data_in_reg; end end endmodule
问题
有几个问题:
1.为什么data_in和data_en都要在clk_a下打一拍?
- 为了避免毛刺。打一拍让data_in和data_en信号都稳定。
2.为什么要把data_en在clk_b下打两拍?
- 刚刚提到过,跨时钟域容易出现亚稳态问题。为了避免它,就要尽量打多的拍。那为什么打两拍而不打一拍呢?原因是能多打拍就多打拍。为什么打两拍而不打三拍呢?因为题目说data_en能在clk_b下稳定3拍。之前data_en_reg消耗了半拍clk_b,所以后面只能打两拍了。两拍是上限了。
3.为什么下面这段代码就能实现data_en_reg打两拍?
always @(posedge clk_b or negedge brstn ) begin if (!brstn)begin data_en_b0<=0; data_en_b1<=0; end else begin data_en_b0<=data_en_reg; data_en_b1<=data_en_b0; end end
注意,data_en_b0<=data_en_reg;和data_en_b1<=data_en_b0;都是非阻塞赋值。也就是执行data_en_b1<=data_en_b0时data_en_b1得到的其实是data_en_b0的旧值,这两条语句是同时发生的。所以这样写也能实现打两拍的功能。