来自牛客网上的题,是比较简单的。
主机Master(driver)的行为:复位后,计数五个时钟周期后,准备第一个数据要发送,向从机发出request信号,然后等待从机接收,从机接收后会拉高ack,反馈应答信号到主机,主机接收后撤掉req信号,并且等待五个时钟周期后,发下一个数据...
从机slave(receiver)行为:复位后,ack为0,等待接收数据,当接收到主机发送请求信号时,直接接收数据并将ack拉高,当主机撤回req后再将data_ack置零。(确认主机知道已经接收数据了。如果只拉高ack一个时钟周期,下个时钟周期ack拉低,那么有可能主机因为时钟较慢的原因没有检测到响应信号,因此主机就不会撤掉req,而一直在发上一个数据,直到被接收才会发下一个。)
主机,驱动。几个关键点:
(1)从机的应答信号需要同步到主机的时钟域,并且进行上升沿检测,上升沿检测的目的是,更换数据,因为两次发送数据之间只能更换一次数据,不能在ack保持的每个时钟周期都更换数据,会造成中途丢失数据。因此只能在ack拉高的第一个周期检测出上升沿,并换掉数据data,变成下一个要发送的数据。
(2)计数器Cnt,计数器cnt必须在无应答的时候进行计数,无应答时计数到5个时钟周期,发送数据请求从机响应,等从机接收数据,主机接收到响应信号后再把请求撤出。
`timescale 1ns/1ps
module data_driver(Clk_a,Rst_n,data_ack,data,data_req);
input Clk_a,Rst_n,data_ack;
output reg [3:0] data;
output reg data_req;
reg data_ack_s0,data_ack_s1,data_ack_s2;
reg [2:0] cnt ;
always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n) begin
data_ack_s0 <= 0;
data_ack_s1 <= 0;
data_ack_s2 <= 0;
end
else begin
data_ack_s0 <= data_ack;
data_ack_s1 <= data_ack_s0;
data_ack_s2 <= data_ack_s1;
end
end
always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
cnt <= 0;
else if (~data_ack_s1) begin
if(cnt == 3'd4)
cnt <= 0;
else
cnt <= cnt + 'b1;
end
else
cnt <= 0;
end
always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
data <= 0;
else if (~data_ack_s2&data_ack_s1 ) begin
if(data==4'd7)
data <= 0;
else
data <= data + 1;
end
else
data <= data;
end
always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
data_req <= 0;
else if (cnt==3'd4)
data_req <= 1;
else if (data_ack_s1)
data_req <= 0;
end
endmodule
从机行为
(1)对主机发来的发送请求进行同步。当检测到发送请求且此时没有响应上一个信号,将数据接收。(这里为什么必须要检测没有响应信号ack,因为跨时钟,如果只是检测发送请求,此时有可能之前的响应信号还没有撤除,会再次接收数据,此时接收到的数据可能是主机更换了的数据,数据被提前接收了,增加了不必要的接收次数)
(2)响应信号的产生,当准备接收数据时就在下一个周期将ack信号拉高。并且没有撤销发送请求时一直拉高,直到撤销请求后,表示主机已经知道从机正确接收了数据。主机撤销请求,从机此时撤销响应。
`timescale 1ns/1ps
module data_receiver(Clk_b,Rst_n,ack,data_req,data,data_o);
input Clk_b,Rst_n;
input data_req;
output reg ack;
input [3:0] data;
output reg [3:0] data_o;
reg data_req_s0,data_req_s1;
always@(posedge Clk_b,negedge Rst_n ) begin
if(~Rst_n)
ack <= 0;
else if (data_req_s1)
ack <= 1;
else
ack <= 0;
end
always@(posedge Clk_b,negedge Rst_n) begin
if(~Rst_n)
data_o <= 0;
else if (data_req_s1&~ack)
data_o <= data;
end
always@(posedge Clk_b,negedge Rst_n) begin
if(~Rst_n)
begin
data_req_s0 <= 0;
data_req_s1 <= 0;
end
else
begin
data_req_s0 <= data_req;
data_req_s1 <= data_req_s0;
end
end
endmodule
仿真