一、前言
前两篇已经讲述了SRIO协议的概况,以及xilinx SRIO IP核的使用方式,已经在搭建工程的过程中时钟和复位的注意事项。
二、设计框图
整个框图也是按照之前的工程进行搭建,首先时SRIO_Channel,由SRIO IP核和时钟、复位模块组成,这是在之前的工程搭建中一种采用的一种Channel结构。其次,SRIO Module是按照QPLL共享的方式去管理Channel,使得多个通道能够共享QPLL。最后,用户侧逻辑采用了一个SRIO Engine一共有4次的事务传输,分别是:
- 写事务。写256字节的数据。
- 发送Doorbell。通知写完成。
- 读事务。读取写入的数据
- 发送消息,通知读完毕。(消息包一直有负载)
三、SRIO Engine
这部分参考FPGA奇哥的系列网课。
module SRIO_Engine(
input i_clk ,
input i_rst ,
output m_axis_ireq_tvalid ,
input m_axis_ireq_tready ,
output m_axis_ireq_tlast ,
output [63:0] m_axis_ireq_tdata ,
output [7: 0] m_axis_ireq_tkeep ,
output [31:0] m_axis_ireq_tuser ,
input s_axis_iresp_tvalid ,
output s_axis_iresp_tready ,
input s_axis_iresp_tlast ,
input [63:0] s_axis_iresp_tdata ,
input [7: 0] s_axis_iresp_tkeep ,
input [31:0] s_axis_iresp_tuser ,
input s_axis_treq_tvalid ,
output s_axis_treq_tready ,
input s_axis_treq_tlast ,
input [63:0] s_axis_treq_tdata ,
input [7: 0] s_axis_treq_tkeep ,
input [31:0] s_axis_treq_tuser ,
output m_axis_tresp_tvalid ,
input m_axis_tresp_tready ,
output m_axis_tresp_tlast ,
output [63:0] m_axis_tresp_tdata ,
output [7: 0] m_axis_tresp_tkeep ,
output [31:0] m_axis_tresp_tuser
);
// assign s_axis_iresp_tready = 0 ;
// assign s_axis_treq_tready = 0 ;
reg rm_axis_ireq_tvalid ;
reg rm_axis_ireq_tlast ;
reg [63:0] rm_axis_ireq_tdata ;
reg [7: 0] rm_axis_ireq_tkeep ;
reg [31:0] rm_axis_ireq_tuser ;
reg rs_axis_iresp_tready ;
reg rs_axis_treq_tready ;
reg rm_axis_tresp_tvalid ;
reg rm_axis_tresp_tlast ;
reg [63:0] rm_axis_tresp_tdata ;
reg [7: 0] rm_axis_tresp_tkeep ;
reg [31:0] rm_axis_tresp_tuser ;
reg [15:0] r_cnt ;
reg [7: 0] r_read_cmd ;
reg r_read_cmd_valid ;
reg r_read_triger ;
reg [15:0] r_treq_cnt ;
reg [15:0] r_read_cnt ;
wire w_m_axi_ireq_act ;
wire w_s_axi_iresp_act ;
wire w_s_axi_treq_act ;
wire w_m_axi_tresp_act ;
assign m_axis_ireq_tvalid = rm_axis_ireq_tvalid ;
assign m_axis_ireq_tlast = rm_axis_ireq_tlast ;
assign m_axis_ireq_tdata = rm_axis_ireq_tdata ;
assign m_axis_ireq_tkeep = rm_axis_ireq_tkeep ;
assign m_axis_ireq_tuser = rm_axis_ireq_tuser ;
assign s_axis_iresp_tready = rs_axis_iresp_tready ;
assign s_axis_treq_tready = rs_axis_treq_tready ;
assign m_axis_tresp_tvalid = rm_axis_tresp_tvalid ;
assign m_axis_tresp_tlast = rm_axis_tresp_tlast ;
assign m_axis_tresp_tdata = rm_axis_tresp_tdata ;
assign m_axis_tresp_tkeep = rm_axis_tresp_tkeep ;
assign m_axis_tresp_tuser = rm_axis_tresp_tuser ;
assign w_m_axi_ireq_act = m_axis_ireq_tready & rm_axis_ireq_tvalid;
assign w_s_axi_iresp_act = s_axis_iresp_tvalid & rs_axis_iresp_tready;
assign w_s_axi_treq_act = s_axis_treq_tvalid & rs_axis_treq_tready;
assign w_m_axi_tresp_act = m_axis_tresp_tready && rm_axis_tresp_tvalid;
localparam P_ST_IDLE = 5'b00001 ,
P_ST_WRITE = 5'b00010 ,
P_ST_DB = 5'b00100 ,
P_ST_READ = 5'b01000 ,
P_ST_MESSAGE = 5'b10000 ;
reg [4: 0] r_st_nstate ;
reg [4: 0] r_st_cstate ;
reg [15:0] r_st_cnt ;
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_st_cstate <= P_ST_IDLE;
else
r_st_cstate <= r_st_nstate;
end
always@(*)begin
r_st_nstate = P_ST_IDLE;
case(r_st_cstate)
P_ST_IDLE:begin
if(r_st_cnt == 1000)
r_st_nstate = P_ST_WRITE;
else
r_st_nstate = P_ST_IDLE;
end
P_ST_WRITE:begin
if(w_m_axi_ireq_act && m_axis_ireq_tlast)
r_st_nstate = P_ST_DB;
else
r_st_nstate = P_ST_WRITE;
end
P_ST_DB:begin
if(w_m_axi_ireq_act && m_axis_ireq_tlast)
r_st_nstate = P_ST_READ;
else
r_st_nstate = P_ST_DB;
end
P_ST_READ:begin
if(w_s_axi_iresp_act && s_axis_iresp_tlast)
r_st_nstate = P_ST_MESSAGE;
else
r_st_nstate = P_ST_READ;
end
P_ST_MESSAGE:begin
if(w_m_axi_ireq_act && m_axis_ireq_tlast)
r_st_nstate = P_ST_IDLE;
else
r_st_nstate = P_ST_MESSAGE;
end
default:begin
r_st_nstate = P_ST_IDLE;
end
endcase
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_st_cnt <= 'd0;
else
if(r_st_cstate != r_st_nstate)
r_st_cnt <= 'd0;
else
r_st_cnt <= r_st_cnt + 1;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_cnt <= 'd0;
else
if(r_cnt == 32 && w_m_axi_ireq_act)
r_cnt <= 'd0;
else if(r_st_nstate == P_ST_WRITE && w_m_axi_ireq_act)//??
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt ;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_ireq_tdata <= 'd0;
else
if(r_st_nstate == P_ST_WRITE && r_cnt == 0)
rm_axis_ireq_tdata <= {8'd0,4'b0110,4'd0,1'b0,2'b00,1'b0,8'hFF,1'b0,1'b0,34'b0};//流写
else if(r_st_nstate == P_ST_DB && r_cnt == 0)
rm_axis_ireq_tdata <= {8'd0,4'b1010,4'd0,1'b0,2'b00,1'b0,8'h00,1'b0,1'b0,34'b0};//门铃
else if(r_st_nstate == P_ST_READ && r_cnt == 0)
rm_axis_ireq_tdata <= {8'd0,4'b0010,4'b0100,1'b0,2'b00,1'b0,8'hFF,1'b0,1'b0,34'b0};//读
else if(r_st_nstate == P_ST_MESSAGE && r_cnt == 0)
rm_axis_ireq_tdata <= {4'd0,4'd0,4'b1011,4'b0000,1'b0,2'b00,1'b0,8'd63,1'b0,1'b0,34'b0};//消息
else if(w_m_axi_ireq_act)
case(r_cnt)
0 : rm_axis_ireq_tdata <= {4{r_cnt}} ;
default : rm_axis_ireq_tdata <= {4{r_cnt}} ;
endcase
else
rm_axis_ireq_tdata <= rm_axis_ireq_tdata;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_ireq_tlast <= 'd0;
else
if(rm_axis_ireq_tlast && w_m_axi_ireq_act)
rm_axis_ireq_tlast <= 'd0;
else if(r_st_nstate == P_ST_WRITE && r_cnt == 32 -1 )
rm_axis_ireq_tlast <= 1'b1;
else if(r_st_nstate == P_ST_DB && r_st_cnt == 0)
rm_axis_ireq_tlast <= 1'b1;
else if(r_st_nstate == P_ST_READ && r_st_cnt == 0)
rm_axis_ireq_tlast <= 1'b1;
else if(r_st_nstate == P_ST_MESSAGE && w_m_axi_ireq_act)
rm_axis_ireq_tlast <= 1'b1;
else
rm_axis_ireq_tlast <= rm_axis_ireq_tlast ;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_ireq_tvalid <= 'd0;
else
if(rm_axis_ireq_tlast && w_m_axi_ireq_act)
rm_axis_ireq_tvalid <= 'd0;
else if(r_st_nstate == P_ST_WRITE && r_st_cnt == 0)
rm_axis_ireq_tvalid <= 1'b1;
else if(r_st_nstate == P_ST_DB && r_st_cnt == 0)
rm_axis_ireq_tvalid <= 1'b1;
else if(r_st_nstate == P_ST_READ && r_st_cnt == 0)
rm_axis_ireq_tvalid <= 1'b1;
else if(r_st_nstate == P_ST_MESSAGE && r_st_cnt == 0)
rm_axis_ireq_tvalid <= 1'b1;
else
rm_axis_ireq_tvalid <= rm_axis_ireq_tvalid;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_ireq_tkeep <= 'd0;
else
rm_axis_ireq_tkeep <= 8'hff;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_ireq_tuser <= 'd0;
else
rm_axis_ireq_tuser <= 8'h00;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_treq_cnt <= 'd0;
else
if(w_s_axi_treq_act && s_axis_treq_tlast)
r_treq_cnt <= 'd0;
else if(w_s_axi_treq_act)
r_treq_cnt <= r_treq_cnt + 1;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_read_cmd <= 'd0;
else
if(r_treq_cnt == 0 && w_s_axi_treq_act)
r_read_cmd <= s_axis_treq_tdata[55:48];
else
r_read_cmd <= r_read_cmd + 1;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_read_cmd_valid <= 'd0;
else
if(r_treq_cnt == 0 && w_s_axi_treq_act)
r_read_cmd_valid <= 1'b1;
else
r_read_cmd_valid <= 'd0;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_read_triger <= 'd0;
else
if(r_read_cmd_valid && r_read_cmd == 8'b0010_0100)
r_read_triger <= 1'b1;
else
r_read_triger <= 'd0;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_tresp_tvalid <= 'd0;
else
if(rm_axis_tresp_tlast && w_m_axi_tresp_act)
rm_axis_tresp_tvalid <= 'd0;
else if(r_read_triger)
rm_axis_tresp_tvalid <= 1'b1;
else
rm_axis_tresp_tvalid <= rm_axis_tresp_tvalid;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_tresp_tlast <= 'd0;
else
if(rm_axis_tresp_tlast && w_m_axi_tresp_act)
rm_axis_tresp_tlast <= 'd0;
else if(r_read_cnt == 31)
rm_axis_tresp_tlast <= 1'b1;
else
rm_axis_tresp_tlast <= rm_axis_tresp_tlast;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_tresp_tdata <= 'd0;
else
if(r_read_triger)
rm_axis_tresp_tdata <= {8'd0,4'b1101,4'b1000,1'b0,2'b0,1'b0,8'd0,1'b0,1'b0,34'd0};
else
rm_axis_tresp_tdata <= {4{r_read_cnt - 1}};
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_tresp_tkeep <= 'd0;
else
rm_axis_tresp_tkeep <= 'd0;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rm_axis_tresp_tuser <= 'd0;
else
rm_axis_tresp_tuser <= 'd0;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
r_read_cnt <= 'd0;
else
if(r_read_cnt == 32 )
r_read_cnt <= 'd0;
else if(r_read_triger || w_m_axi_tresp_act)
r_read_cnt <= r_read_cnt + 1;
else
r_read_cnt <= r_read_cnt;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rs_axis_treq_tready <= 'd1;
else
rs_axis_treq_tready <= 1'b1;
end
always@(posedge i_clk,posedge i_rst)begin
if(i_rst)
rs_axis_iresp_tready <= 'd1;
else
rs_axis_iresp_tready <= 1'b1;
end
endmodule
仿真图如下:
如图在ireq端口上首先发送写事务和数据,之后发起门铃事务,在之后发起读事务。
在iresp端口上返回读过来的数据
最后读完数据后,可以发起消息事务,通知对端已经接收到数据
四、总结
本篇文章目的在于简单的实践一下SRIO IP核,所以操作非常简单。如果真的要去用SRIO协议,需要涉及的理论还是挺多的。