书接上篇。
这个仿真第一个实验就是要做实体A发送数据给实体B,顶层的verilog代码如下:
module tb_top ;
wire [31:0]my_a_ip ={8'd192,8'd168,8'd0,8'HAA} ;
wire [47:0]my_a_mac =48'HA1A2A3A4A5A6;
wire [31:0]my_a_udp_port = 'h1234 ;
wire [31:0]my_b_ip ={8'd192,8'd168,8'd0,8'HBB} ;
wire [47:0]my_b_mac =48'hB1B2B3B4B5B6;
wire [31:0]my_b_udp_port = 'h2345 ;
reg rst = 0 ,clk = 0 ;
always #5 clk = ~clk ;
integer i; initial begin
$dumpfile("tb_top.vcd");
$dumpvars;
rst = 1;
@(posedge clk) ;@(posedge clk) ;
rst = 0;
@(posedge clk) ;
for(i=0;i<( test_len + 2048 );i=i+1)@(posedge clk);
$finish ;
end
wire [15:0] test_len = 18;
reg [11:0] c;always @(posedge clk) if (rst) c<=0; else c<=(c==(1000-1))?0:c+1; // system counter for debug
reg [7:0] st = 0 ;
reg [7:0] r;
always @(posedge clk) if (rst)st<=0; else case (st)
0:st<=10;
10:if(s_udp_tx_busy==0)st<=20;
20:st<=30;
30:if(r==test_len)st<=40;
40:st<=10;
default st<=0;
endcase
reg s_udp_tx_start=0;
always @(posedge clk) if (rst){s_udp_tx_start,r}<=0; else case (st)
20:begin r<=0;s_udp_tx_start <=1; end
30:begin r<=r+1; s_udp_tx_start <=0; end
default begin r<=0;s_udp_tx_start <=0;end
endcase
wire [7:0] a2b_u8 ,b2a_u8 ;
wire a2b_en,b2a_en;
udp_top a(
.clk (clk),
.rst(rst) ,
.s_udp_dst_ip(my_b_ip),
.s_udp_src_port(my_a_udp_port),
.s_udp_dst_port (my_b_udp_port),
.s_udp_tx_valid (),
.s_udp_tx_busy(s_udp_tx_busy),
.s_udp_tx_len(test_len),
.s_udp_tx_dat(r),
.s_udp_ip_id('hb3fe),
.s_udp_tx_start(s_udp_tx_start),
.phy_tx_dout(a2b_u8) ,
.phy_tx_err(),
.phy_tx_en(a2b_en),
.phy_rx_din(b2a_u8),
.phy_rx_dv(b2a_en),
.cfg_my_ip(my_a_ip),
.cfg_my_mac(my_a_mac)
);
udp_top b(
.clk (clk),
.rst(rst) ,
.s_udp_dst_ip(my_b_ip),
.s_udp_src_port(my_b_udp_port),
.s_udp_dst_port (my_a_udp_port),
.s_udp_tx_valid (1'b1), //?
.s_udp_tx_busy(),
.s_udp_tx_len(),
.s_udp_tx_dat(),
.s_udp_ip_id('hb3fe),
.s_udp_tx_start(1'b0),
.phy_tx_dout(b2a_u8) ,
.phy_tx_err(),
.phy_tx_en(b2a_en),
.phy_rx_din(a2b_u8),
.phy_rx_dv(a2b_en),
.cfg_my_ip(my_b_ip),
.cfg_my_mac(my_b_mac)
);
endmodule
这里首先规定了实体A和B的IP地址以及MAC地址,还有各自端口号。
wire [31:0]my_a_ip ={8'd192,8'd168,8'd0,8'HAA} ;
wire [47:0]my_a_mac =48'HA1A2A3A4A5A6;
wire [31:0]my_a_udp_port = 'h1234 ;
wire [31:0]my_b_ip ={8'd192,8'd168,8'd0,8'HBB} ;
wire [47:0]my_b_mac =48'hB1B2B3B4B5B6;
wire [31:0]my_b_udp_port = 'h2345 ;
为了能在波形界面直接看出来的这些数据我们用了区别度很高的数值。
比如下图以太网总线上出现的A1,A2....A6序列我们就一眼看出是实体的A的mac地址。
在PHY总线上,A和B的接收和发送交叉起来
时钟和复位部分比较简洁:
reg rst = 0 ,clk = 0 ;
always #5 clk = ~clk ;
wire [15:0] test_len = 18;
integer i; initial begin
$dumpfile("tb_top.vcd");
$dumpvars;
rst = 1;
@(posedge clk) ;@(posedge clk) ;
rst = 0;
@(posedge clk) ;
for(i=0;i<( test_len + 2048 );i=i+1)@(posedge clk);
$finish ;
end
跑上2000多个周期也就够了,之后停止仿真。
测试激励部分,我们发送UDP的内容是0,1,2,... TEST_LEN-1 这些字节内容。只要在实体A段试图一直发送(只要实体的UDP端口不是BUSY就一直生成报文控制start给实体A做激励)。
reg [7:0] st = 0 ;
reg [7:0] r;
always @(posedge clk) if (rst)st<=0; else case (st)
0:st<=10;
10:if(s_udp_tx_busy==0)st<=20;
20:st<=30;
30:if(r==test_len)st<=40;
40:st<=10;
default st<=0;
endcase
reg s_udp_tx_start=0;
always @(posedge clk) if (rst){s_udp_tx_start,r}<=0; else case (st)
20:begin r<=0;s_udp_tx_start <=1; end
30:begin r<=r+1; s_udp_tx_start <=0; end
default begin r<=0;s_udp_tx_start <=0;end
endcase
另外我还做了一个全局的计数器,每周期不断累计,显示范围是0-999,到达999后从新归零继续累加。这个主要是为了出现在仿真波形上,别的波形好用对照数周期数。比如下图:
我们数tx_data数据总线出现的0的个数,直接278-271=7 就OK 。