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