题解 | #使用握手信号实现跨时钟域数据传输#

使用握手信号实现跨时钟域数据传输

https://www.nowcoder.com/practice/2bf1b28a4e634d1ba447d3495134baac

结合本题目,由于两个模块时钟是异步时钟,clk_a为33M,clk_b为50M,不管是高到低,还是低到高,都需要对信号做同步,以严格约束自己的时序。在发送模块中,由于采用状态机实现的,因此对状态机的跳转以及各个信号的初态都需要详细描述,按照初态--->准备---->发送---->结束,这4个流程执行,在准备状态下,将内部计数器的值赋值给r_data_buf,为发送数据做准备,在发送阶段,将r_data_buf的值赋值到data上,传输给接收模块,同时data_req仅在发送阶段为高,当发送模块接收到data_ack==1时,发送状态跳转到结束状态,data_req变低,等待5个clk_a的时钟长度返回初态;接收模块监测到data_req==1时,将data的值接收赋值到内部的r_data_receive寄存器,同时给出data_ack==1的信号,这个信号是高频到低频,需要做延时处理,将ack信号拉长3个clk_b的时钟长度即可。

`timescale 1ns/1ns
//发送模块
module data_driver(
	input clk_a,            //33M
	input rst_n,
	input data_ack,
	output reg [3:0]data,
	output reg data_req
	);
//clk_a为33MHz,clk_b为50MHz
//高频率到低频率处理ack信号,跨时钟域处理,需要在接收模块将ack信号拉长
//-------------------------------------------------------------------//
reg		[3:0]		r_data_buf;				//发送数字0-7,每发送一个数字,握一次手,两个数之间需要间隔5个clk_ad的时钟
reg		[3:0]		r_data_cnt;
reg					r_enable;

always@(posedge clk_a)begin
	if(!rst_n)
		r_enable <= 1'b0;
	else begin
		r_enable <= 1'b1;
	end
end

reg         [3:0]       r_c_state;
reg         [3:0]       r_n_state;
reg         [2:0]       r_flow_cnt;         //计数5个时钟clk

parameter   IDLE    =       4'b0001,        //初态1
            READY   =       4'b0010,        //准备2
            SEND    =       4'b0100,        //发送4
            STOP    =       4'b1000;        //停止8

always @(posedge clk_a) begin
    if (!rst_n) begin
        r_c_state <= IDLE;
    end
    else begin
        r_c_state <= r_n_state;
    end
end

always @(posedge clk_a) begin
    if (!rst_n) begin
        r_n_state <= IDLE;
    end
    else begin
        case (r_c_state)
            IDLE: begin
                if (r_enable) begin
                    r_n_state <= READY;
                end
                else begin
                    r_n_state <= IDLE;
                end
            end 
            READY: begin
                r_n_state <= SEND;
            end
            SEND: begin
                if(data_ack) begin
                    r_n_state <= STOP;
                end
                else begin
                    r_n_state <= SEND;
                end
            end
            STOP: begin
                if (r_flow_cnt == 3'd4) begin
                    r_n_state <= IDLE;
                end
                else begin
                    r_n_state <= STOP;
                end
            end
            default: begin
                r_n_state <= IDLE;
            end
        endcase
    end
end

always @(posedge clk_a) begin
    if (!rst_n) begin
        r_data_buf <= 4'd0;
        data_req <= 1'b0;
        r_flow_cnt <= 3'd0;
        r_data_cnt <= 4'd0;
        data <= 4'd0;
    end
    else begin
        case (r_n_state)
            IDLE: begin
                r_data_buf <= 4'd0;
                data_req <= 1'b0;
                r_flow_cnt <= 3'd0;
            end 
            READY: begin
                data_req <= 1'b0;
                r_data_buf <= r_data_cnt;
                r_flow_cnt <= 3'd0;
            end
            SEND: begin
                data_req <= 1'b1;
                data <= r_data_buf;
                r_flow_cnt <= 3'd0;
            end
            STOP: begin
                data_req <= 1'b0;
                if (r_flow_cnt == 3'd1) begin
                    if (r_data_cnt == 4'd7) begin
                        r_data_cnt <= 4'd0;
                    end
                    else begin
                        r_data_cnt <= r_data_cnt + 1;
                    end
                end
                else begin
                    r_data_cnt <= r_data_cnt;
                end
                
                if (r_flow_cnt == 3'd4) begin
                    r_flow_cnt <= 3'd4;
                end
                else begin
                    r_flow_cnt <= r_flow_cnt + 1;
                end
            end
            default: begin
                r_data_buf <= 4'd0;
                data_req <= 1'b0;
                r_flow_cnt <= 3'd0;
                r_data_cnt <= 4'd0;
                data <= 4'd0;
            end
        endcase
    end
end

endmodule


//接收模块
module data_receiver(
	input clk_b,            //50M
	input rst_n,
	input [3:0]data,
	output reg data_ack,
	input data_req
	);
//clk_a为33MHz,clk_b为50MHz
//高频率到低频率处理ack信号,跨时钟域处理,需要在接收模块将ack信号拉长
//-------------------------------------------------------------------//
reg         [3:0]       r_data;
reg         [3:0]       r_data_buff;
reg         [1:0]       r_data_req;
reg         [1:0]       r_req;


//data和data_req信号做同步处理
always @(posedge clk_b) begin
    if (!rst_n) begin
        r_data <= 4'd0;
        r_data_buff <= 4'd0;
        r_data_req <= 2'b00;
        r_req <= 2'b00;
    end
    else begin
        r_data <= data;
        r_data_buff <= r_data;
        r_data_req[0] <= data_req;
        r_data_req[1] <= r_data_req[0];
        r_req <= {r_req[0],r_data_req[1]};
    end
end

reg     [3:0]       c_state;
reg     [2:0]       r_data_rece;
reg                 r_data_ack;
reg     [2:0]       r_delay_cnt;

always @(posedge clk_b) begin
    if (!rst_n) begin
        c_state <= 4'd0;
        r_delay_cnt <= 3'd0;
        r_data_rece <= 3'd0;
        r_data_ack <= 1'b0;
    end
    case (c_state)
        4'd0: begin
            r_data_rece <= 3'd0;
            r_data_ack <= 1'b0;
            r_delay_cnt <= 3'd0;
            if (r_data_req[1]) begin
                c_state <= 4'd1;
            end
            else begin
                c_state <= 4'd0;
            end
        end
        4'd1: begin
            if (r_delay_cnt == 3'd3) begin
                r_delay_cnt <= 3'd3;
                c_state <= 4'd2;
            end
            else begin
                r_delay_cnt <= r_delay_cnt + 1;
            end

            if (r_delay_cnt <= 3'd2) begin
                r_data_ack <= 1'b1;
            end
            else begin
                r_data_ack <= 1'b0;
            end

            if (r_delay_cnt == 3'd0) begin
                r_data_rece <= r_data_buff;
            end
        end
        4'd2: begin
            r_delay_cnt <= 3'd0;
            r_data_ack <= 1'b0;
            r_data_rece <= r_data_rece;
            if (r_req == 2'b01) begin
                c_state <= 4'd0;
            end
            else begin
                c_state <= 4'd2;
            end
        end
        default: begin
            c_state <= 4'd0;
            r_delay_cnt <= 3'd0;
            r_data_rece <= 3'd0;
            r_data_ack <= 1'b0;
        end
    endcase
end

always @(*) begin
    data_ack <= r_data_ack;
end           

endmodule


#FPGA学习#
全部评论

相关推荐

沉淀一会:1.同学你面试评价不错,概率很大,请耐心等待; 2.你的排名比较靠前,不要担心,耐心等待; 3.问题不大,正在审批,不要着急签其他公司,等等我们! 4.预计9月中下旬,安心过节; 5.下周会有结果,请耐心等待下; 6.可能国庆节前后,一有结果我马上通知你; 7.预计10月中旬,再坚持一下; 8.正在走流程,就这两天了; 9.同学,结果我也不知道,你如果查到了也告诉我一声; 10.同学你出线不明朗,建议签其他公司保底! 11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务