已经写好了并调试好了代码,直接上代码:
module tb_axis_user2ether_if ;
reg clk = 0 ,rst = 0 ;
always#5 clk =~clk ;
initial begin
$dumpfile("tb_axis_user2ether_if.vcd");
$dumpvars;
end
reg [31:0] c = 0 ;always @(posedge clk) c<=c+1;
reg[15:0] r=0;
reg s_valid = 0 , s_last = 0 , s_pkt_end = 0 ;
wire s_ready ;
integer i ;
task fill_data ;
input [15:0] len ;
begin
@(posedge clk) ;
r = 0 ;s_valid = 1 ;
for(i=0;i<len;i=i+1)begin r<=r+1;@(posedge clk);end
s_valid=0;
end
endtask
wire [15:0] test_len = 1000;
initial begin
rst =1 ;
#100 rst = 0 ;
#100 fill_data (test_len) ;
for(i=0;i< 2000 ;i=i+1) @(posedge clk);
$finish ;
end
axis_user2ether_if#(.AW(10),.DW(16))
axis_user2ether_if(
.clk( clk ),
.rst( rst ),
//cfg if
.cfg_udp_port('h8080 ),
//axi stream to ether if
.s_din( r ),
.s_valid( s_valid ),
.s_last( s_last ),
.s_pkt_end( s_pkt_end ),
.s_ready( s_ready ),
//ether udp if
.m_udp_tx_busy( 1'b0 ) ,
.m_udp_tx_len( ),
.m_udp_tx_dat( ),
.m_udp_tx_start( )
);
endmodule
module axis_user2ether_if#(
parameter AW = 12 , // NOT LESS THAN 10
parameter DW = 16 // 16 FOR SIMULATION ,8 FOR REAL USE
)(
input clk,rst,
//cfg if
input cfg_udp_port,
//axi stream to ether if
input [15:0]s_din,
input s_valid,s_last,s_pkt_end,
output s_ready,
//ether udp if
input m_udp_tx_busy ,
output reg [15:0]m_udp_tx_len,
output reg [DW-1:0]m_udp_tx_dat,
output reg m_udp_tx_start ,
output reg m_udp_tx_end
);
wire [DW-1:0] fifo_u8 ,fwft_dout;
wire [AW:0] fifo_cntr;
wire [DW-1:0] u8 ;
reg rd_fifo ;
wire wr_fifo = s_valid & ~full ;
sc_fifo#(
.AW(AW),
.DW(DW)
)sc_fifo(
.clk(clk),
.rst(rst),
.din(s_din),
.wr( wr_fifo ),
.full( full ),
.dout( fifo_u8 ),
.rd(rd_fifo),
.empty(empty),
.fwft_dout(fwft_dout),
.fifo_cntr(fifo_cntr)
);
localparam WAIT_HI_BIT = 5 ;
reg s_lastr ;always@(posedge clk) s_lastr<=s_last ;
wire pkt_end = ( s_lastr || s_pkt_end );
reg need_flush ;
always @(posedge clk) if (rst) need_flush<=0; else if (pkt_end)need_flush<=0; else if (st==10 && fifo_cntr==0) need_flush<=0;
always @( * ) m_udp_tx_dat = fifo_u8 ;
reg [WAIT_HI_BIT:0] wait_cntr ;
always @ (posedge clk) if (rst |empty | wr_fifo ) wait_cntr<=0;else if (wait_cntr[WAIT_HI_BIT] == 0) wait_cntr<= wait_cntr+1 ;
reg [7:0] st ;
reg m_udp_tx_start_d1 ;
always @( posedge clk ) m_udp_tx_start_d1 <= ( st==20) && (c ==1 );
always @( posedge clk ) m_udp_tx_start <= m_udp_tx_start_d1 ;
always @( posedge clk ) if (st==10)m_udp_tx_len<= (fifo_cntr > 1456) ? 1456 : fifo_cntr;
reg [11:0] c; always @ (posedge clk) case (st) 20 : c<=1+c;default c<=1;endcase
always@(posedge clk) if (rst) st<=0; else case (st)
0 : st <= 10 ;
10 : if ( wait_cntr[WAIT_HI_BIT] || ( fifo_cntr[AW:9] !=0 ) || need_flush )st <=((m_udp_tx_busy==0 )&& (fifo_cntr != 0))?20: 10 ; //
20 : if (c == m_udp_tx_len) st<=30;
30 : st<=40;
40 : st<=10;
default st<=0;
endcase
always@(posedge clk) if (rst) rd_fifo <= 0 ;else case (st) 20 : rd_fifo <= 1 ;default rd_fifo<=0; endcase
reg m_udp_tx_end_d1 , m_udp_tx_end_d2 ;
always@(posedge clk )m_udp_tx_end_d1 <= (st==20 ) &&( c == m_udp_tx_len );
always@(posedge clk )m_udp_tx_end_d2 <= m_udp_tx_end_d1 ;
always@(posedge clk )m_udp_tx_end <= m_udp_tx_end_d1 ;
assign s_ready = ~full & ~need_flush ;
endmodule
/*
module sc_fifo#(
.AW(4),
.DW(32)
)(
.clk(),
.rst(),
.din(),
.wr(),
.full(),
.dout(),
.rd(),
.empty(),
.fwft_dout(),
.fifo_cntr()
);
*/
module sc_fifo#(
parameter AW = 5 ,
parameter DW = 64,
parameter ALMOST_FULL = 10 ,
parameter ALMOST_EMPTY = 12
)(
input clk,rst,
input [ DW-1:0] din,
input wr,rd,
output full,empty,
output reg almost_full,
output reg almost_empty,
output reg [ DW-1:0] dout,
output [ DW-1:0] fwft_dout,
output reg [ AW:0] fifo_cntr
);
parameter MAX_FIFO_LEN = (1<<AW ) ;
parameter ALMOST_FULL_LEN = MAX_FIFO_LEN - 10 ;
parameter ALMOST_EMPTY_LEN = 5 ;
reg [ DW-1:0] buff[0: MAX_FIFO_LEN -1] ;
reg [ AW-1:0] wr_ptr, rd_ptr ;
assign full = fifo_cntr == ( MAX_FIFO_LEN ) ;
assign empty = fifo_cntr == 0 ;
always @* almost_full <= fifo_cntr > ALMOST_FULL_LEN ;
always @* almost_empty <= fifo_cntr < ALMOST_EMPTY_LEN ;
wire valid_rd = rd ;
wire valid_wr = wr ;
always@(posedge clk) if (rst) wr_ptr <= 0;else if(valid_wr)wr_ptr<=wr_ptr+1;
always@(posedge clk) if (rst)rd_ptr <= 0 ;else if (valid_rd)rd_ptr <= rd_ptr+1;
always@(posedge clk)
casex ({rst,valid_wr,valid_rd})
3'b1xx : fifo_cntr<=0;
3'b010 : fifo_cntr<=fifo_cntr+1;
3'b001 : fifo_cntr<=fifo_cntr-1;
3'b011 ,3'b000 :fifo_cntr<=fifo_cntr ;
endcase
always@(posedge clk) if (valid_wr) buff[wr_ptr] <=din ;
always@(posedge clk) if (valid_rd) dout <= buff[rd_ptr] ;
assign fwft_dout = buff[rd_ptr] ;
endmodule
工作原理:
1,里面有一个FIFO,用户往FIFO写内容,程序状态机根据情况从读端口读出内容组成UDP接口发送时序给UDP发送模块。
2,在UDP的BUSY信号输出为0,并且满足以下三种情况下程序状态机将FIFO的数据读出,之后给UDP发送接口:
A,FIFO数据大于512字节。
B,已经有连续64个周期FIFO未被写入了。
C,用户触发了LAST或者PKT_END,立即发送当前的包。
几个想法:
1, 使用的时候可以设置FIFO的深度,如果FPGA剩余的BRAM多不妨设置大一些。
2,BRAM在调试时候要做是否发送溢出的检测。
3,发送条件中的A,B可以参数化,用户可以自己修改。我琢磨512和64这俩参数应该不错了。
4,尽量不要使用LAST和PKT_END,因为会将状态机挂起知道所有的用户数据传递到UDP发送端。
仿真截图,因为代码已经调试好了,直接看效果图:
这是发送7个字节的
-----
发送1000个字节
可见发送1000个字节分成了两次,第一次是512,第二次是1000-512.
这里我们看到两次发送之间间隔大约有不到10个周期,如果假设UDP通道被此用户独占,实际接UDP发送时候两个包的间隔时间是(n+95)个周期,再加上这个10个周期大约就是 n+110个周期的样子。
这里我们考虑将这个发送阈值设置为1024,但是也有取舍。就是用户数据写入512到1024期间,UDP是一直再傻等发送的。这实际是浪费了1024-512个周期,而实际如果这期间发送数据的话,用了512+110个周期发送数据,这样看来傻等合适。但是这里的假设就是写端每个周期都写入数据,如果从写入512到1024字节期间零零散散等待了超过110秒,就不如再512字节时候触发UDP实际发送了。另外因为写入时间和UDP发送事件是异步的,再次UDP可以发送数据数据时候,写入的已经完全可以超过512了,比如是1400字节,也就可以一次性把这1400字节发出。512只是一个允许发送的最小阈值。