题解 | #使用握手信号实现跨时钟域数据传输#
使用握手信号实现跨时钟域数据传输
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学习#