数字IC面试手撕代码(二)

1、如何将单bit信号完成从快时钟域到慢时钟域的传输?
这个题目是比较常见的,我知道的一般处理方法有两种:一是:脉冲扩展(一般扩展到慢时钟域的1.5倍脉冲宽度左右),另一种是:进行握手信号处理。

进行握手信号处理的时候由简到繁又分为以下几种情况:
(1)只考虑单比特信号的跨时钟域

module handshake_pulse_sync(
	input src_clk,
	input src_rst_n,
	input src_pulse,
	
	input dst_clk,
	input dst_rst_n,
	
	output dst_pulse
);

reg src_sync_req;
reg src_sync_ack;
always @ (posedge src_clk or negedge src_rst_n) begin
	if(!src_rst_n) begin
		src_sync_req <= 1'b0;
	end
	else if(src_pulse) begin
		src_sync_req <= 1'b1;
	end
	else if(src_sync_ack) begin
		src_sync_req <= 1'b0;
	end
end

reg req_src_dst1;
reg req_src_dst2;
reg req_src_dst2;

always @ (posedge dst_clk or negedge dst_rst_n) begin
	if(!dst_rst_n) begin
		req_src_dst1 <= 1'b0;
		req_src_dst2 <= 1'b0;
		req_src_dst3 <= 1'b0;
	end
	else begin
		req_src_dst1 <= src_sync_req;
		req_src_dst2 <= req_src_dst1;
		req_src_dst3 <= req_src_dst1;
	end
end

wire dst_pulse;
assign dst_pulse = (~req_src_dst3) & req_src_dst2;

reg dst_src_ack1;
always @ (posedge src_clk or negedge src_rst_n) begin
	if(!src_rst_n) begin
		dst_src_ack1 <= 1'b0;
		src_sync_ack <= 1'b0
	end
	else begin
		dst_src_ack1 <= req_src_dst2;
		src_sync_ack <= dst_src_ack1
	end
end

endmodule


这个代码比较简单,就没有进行仿真,感觉应该没错误。哈哈
在这里插入图片描述

(2)如果两个脉冲信号离的比较近,那么刚进来的脉冲就会同步失败,但是不会得到反馈,致使以为同步成功了。下面这个代码就提出来了同步失败信号。

//若是在同步一个脉冲的过程中,输入端又接收到一个输入进来的脉冲,那么此时刚输入进来的脉冲就会同步失败。
//没有同步失败反馈,导致使用者误以为正确同步了信号。当同步失败后输出src_sync_fail信号指示同步失败

module handshack_pulse_sync(
	input src_clk,
	input src_rst_n,
	input src_pulse,
	
	input dst_clk,
	input dst_rst_n,
	
	output reg src_sync_fail,
	output dst_pulse
);

wire src_sync_idle;
reg src_sync_req;
reg src_sync_ack;
reg ack_state_dly1;
reg ack_state_dly2;
reg req_state_dly1;
reg req_state_dly2;
reg dst_req_state;
reg dst_sync_ack;

assign src_sync_idle = ~(src_sync_req | src_sync_ack);


always @ (posedge src_clk or negedge src_rst_n) begin
	if(!src_rst_n) begin
		src_sync_fail <= 1'b0;
	end
	else if(src_pulse & (~src_sync_idle)) begin
		src_sync_fail <= 1'b1;
	end
	else begin
		src_sync_fail <= 1'b0;
	end
end


always @ (posedge src_clk or negedge src_rst_n) begin
	if(!src_rst_n) begin
		src_sync_req <= 1'b0;
	end
	else if(src_pulse & src_sync_idle) begin
		src_sync_req <= 1'b1;
	end
	else if(src_sync_ack) begin
		src_sync_req <= 1'b0;
	end
end

always @ (posedge dst_clk or negedge dst_rst_n) begin
	if(!dst_rst_n) begin
		req_state_dly1 <= 1'b0;
		req_state_dly2 <= 1'b0;
		dst_req_state <= 1'b0;
	end
	else begin
		req_state_dly1 <= src_sync_req;
		req_state_dly2 <= req_state_dly1;
		dst_req_state <= req_state_dly2;
	end
end

assign dst_pulse = req_state_dly2 & (~dst_req_state);

always @ (posedge dst_clk or negedge dst_rst_n) begin
	if(!dst_rst_n) begin
		dst_sync_ack <= 1'b0;
	end
	else if(req_state_dly2) begin
		dst_sync_ack <= 1'b1;
	end
	else begin
		dst_sync_ack <= 1'b0;
	end
end


always @ (posedge src_clk or negedge src_rst_n) begin
	if(!src_rst_n) begin
		ack_state_dly1 <= 1'b0;
		ack_state_dly2 <= 1'b0;
		src_sync_ack <= 1'b0;
	end
	else begin
		ack_state_dly1 <= dst_sync_ack;
		ack_state_dly2 <= ack_state_dly1;
		src_sync_ack <= ack_state_dly2;
	end
end



endmodule

在这里插入图片描述

针对这个情况,在网上还看到过有人设计了这样的方法,让两次脉冲都会同步成功。
在这里插入图片描述

在上图中in_pulse为输入的待同步的脉冲信号,receive_enable为向上游电路输出的backpressure信号,只有当该信号有效时候,电路才可以接收输入脉冲。

在握手机制下上下游电路可以源源不断地向其输入脉冲信号,而由握手电路决定要同步哪些电路,并将同步失败的信号向上游提供反馈。而backpressure则在电路处理不了下一个要到来的信号时及时告知上游,使上游停止输入。

module sync_pulse__backpressure(
        input wire src_clk,
        input wire dst_clk,
        input wire rst,
        input wire in_pulse,
        output wire out_pulse,
        output wire receive_en
    );

    reg src_sync,dst_sync;
    reg sync_dly1,sync_dly2;

    reg sync_back_dly1,sync_back_dly2;

    always @(posedge src_clk or posedge rst )  begin
        if(rst)
            src_sync <= 0;
        else 
            src_sync <= src_sync ^ in_pulse;
    end

    always @(posedge dst_clk or posedge rst) begin
        if(rst)
            {sync_dly1,sync_dly2,dst_sync} <= 3'b000;
        else
            {sync_dly1,sync_dly2,dst_sync} <= {src_sync,sync_dly1,sync_dly2};
    end

    assign out_pulse = sync_dly2 ^ dst_sync ;

    always @(posedge src_clk or posedge rst ) begin
        if (rst)
            {sync_back_dly1,sync_back_dly2} <= 2'b00;
        else  
            {sync_back_dly1,sync_back_dly2} <= {sync_dly2,sync_back_dly1};
    end

    assign receive_en = ~(sync_back_dly2 ^ src_sync) ;

endmodule

将上述电路改为握手电路需要修改两个关键部分:
(1)将receive_enable信号改为同步失败信号,在脉冲信号到来时,若sync_fail信号同步输出1,则证明同步失败。可以看出只需将receive_enable信号月in_pulse进行逻辑操作,即可得到sync_fail。
(2)在上一个信号同步过程中的忙状态,若此时又一个脉冲同步信号输入,则握手电路会组止握手回路的信号受输入的影响,而在上述backpressure电路中,一旦有in_pulse,内部信号会在下一个时钟上升沿进行翻转。
在这里插入图片描述
如果两个脉冲之间的间隔比较小还是会出现同步失败的问题,但是间隔稍微大点,就可以避免这个问题

module sync_pulse_fast_to_slow_handshake(
	input src_clk,
	input dst_clk,
	input rst_n,
	input in_pulse,
	
	output out_pulse,
	output sync_fail
);

reg src_sync;
reg dst_sync;
reg sync_dly1;
reg sync_dly2;
reg sync_back_dly1;
reg sync_back_dly2;

wire sync_idle;
reg sync_idle_dly;   //用于产生sync_fail信号


always @ (posedge src_clk or negedge rst_n) begin
	if(!rst_n) begin
		src_sync <= 1'b0;
	end
	else begin
		src_sync <= sync_idle ? (src_sync ^ in_pulse): src_sync;
	end
end 


always @ (posedge dst_clk or negedge rst_n) begin
	if(!rst_n) begin
		{sync_dly1,sync_dly2,dst_sync} <= 3'b000;
	end
	else begin
		{sync_dly1,sync_dly2,dst_sync} <= {src_sync,sync_dly1,sync_dly2};
	end
end

assign out_pulse = sync_dly2 ^ dst_sync;


always @ (posedge src_clk or negedge rst_n) begin
	if(!rst_n) begin
		{sync_back_dly1,sync_back_dly2} <= 2'b00;
	end
	else begin
		{sync_back_dly1,sync_back_dly2} <= {sync_dly2,sync_back_dly1};
	end
end

assign sync_idle = ~(sync_back_dly2 ^ src_sync);


always @(posedge src_clk or posedge rst_n) begin
        if(!rst_n)
            sync_idle_dly <= 0;
        else
            sync_idle_dly <= sync_idle;
    end

assign sync_fail = in_pulse & ( ~(sync_idle | sync_idle_dly) );

endmodule

细品一下确实感觉这也就那样(膨胀了)。

猜你喜欢

转载自blog.csdn.net/weixin_43727437/article/details/107021322
今日推荐