FPGA之以太網ARP發送


參考梅雪松的2017年以太網發送部分的視頻教程以及代碼,對自己編寫代碼過程中遇到的一些問題進行了調試,是爲此篇。

1 代碼段

自己編寫的代碼段如下

module eth_send(
	input rstn,
	//MII
	input 	mii_tx_clk	,
	output 	mii_tx_en	,
//	output   mii_tx_error,
	
	output reg [3:0]	mii_tx_data	,
	
	//FIFO
	input [3:0] fifo_rddata	,
	output 		fifo_rdreq	,
	output 		fifo_rdclk	,
	
	input 		pulse			,
	input [47:0]des_mac		,
	input [47:0]src_mac		,
	input [15:0]type_length	,
	input [31:0]crc_result	,
	input [11:0]data_length		//ARP數據段的長度
); 

parameter LSM_CNT_MAX = 6'd53;

reg [5:0] lsm_cnt	;
reg 		 en_tx	;

reg [47:0]des_mac_tmp		;
reg [15:0]type_length_tmp	;
reg [11:0]data_length_tmp	;

wire tx_arp;

assign mii_tx_en  = en_tx		;
assign fifo_rdreq = tx_arp		;
assign fifo_rdclk = mii_tx_clk;

//en_tx============================================
wire tx_done = (lsm_cnt == LSM_CNT_MAX);
always @ (posedge mii_tx_clk or negedge rstn) begin
	if (!rstn)
		en_tx <= 1'b0;
	else if (pulse)
		en_tx <= 1'b1;
	else if (tx_done)
		en_tx <= 1'b0;
	else
		en_tx <= en_tx;
end

assign tx_arp = (lsm_cnt == 44) && (data_length_tmp!=12'h0);  

//lsm_cnt-----------------------------------------
always @ (posedge mii_tx_clk or negedge rstn) begin
	if (!rstn)
		lsm_cnt <= 6'b0;
	else if (en_tx) begin
		if (tx_arp)
			lsm_cnt <= lsm_cnt;
		else if (tx_done)
			lsm_cnt <= 6'b0;
		else
			lsm_cnt <= lsm_cnt + 1'b1;
	end
	else
		lsm_cnt <= 6'b0;
end

//tmp---------------------------------------------
always @ (posedge mii_tx_clk or negedge rstn) begin
	if (!rstn) begin
		des_mac_tmp 	<= 48'b0;
		type_length_tmp<= 16'b0;
	end
	else if (pulse) begin
		des_mac_tmp 	<= des_mac		;
		type_length_tmp<= type_length	;
	end
	else begin
		des_mac_tmp 	<= des_mac_tmp		;
		type_length_tmp<= type_length_tmp;
	end
end

//data_length_tmp-------------------------------------
always @ (posedge mii_tx_clk or negedge rstn) begin
	if (!rstn) 
		data_length_tmp <= 12'b0;
	else if (pulse)
		data_length_tmp <= data_length;
	else if (tx_arp)
		data_length_tmp <= data_length_tmp - 1'b1;
	else
		data_length_tmp <= data_length_tmp;
end

//LSM-----------------------------------------------
always @ (posedge mii_tx_clk or negedge rstn) begin
    if (!rstn)
        mii_tx_data <= 4'b0;
	 else if (mii_tx_en) begin
		  case (lsm_cnt)
            'd0, 'd1, 'd2, 'd3, 'd4, 'd5, 'd6, 
            'd7, 'd8, 'd9, 'd10,'d11,'d12,'d13,'d14: 
                mii_tx_data <= 4'h5;
            
            'd15: mii_tx_data <= 4'hd;

            'd16: mii_tx_data <= des_mac_tmp[43:40];
            'd17: mii_tx_data <= des_mac_tmp[47:44];
            'd18: mii_tx_data <= des_mac_tmp[35:32];
            'd19: mii_tx_data <= des_mac_tmp[39:36];
            'd20: mii_tx_data <= des_mac_tmp[27:24];
            'd21: mii_tx_data <= des_mac_tmp[31:28];
            'd22: mii_tx_data <= des_mac_tmp[19:16];
            'd23: mii_tx_data <= des_mac_tmp[23:20];
            'd24: mii_tx_data <= des_mac_tmp[11: 8];
            'd25: mii_tx_data <= des_mac_tmp[15:12];
            'd26: mii_tx_data <= des_mac_tmp[ 3: 0];
            'd27: mii_tx_data <= des_mac_tmp[ 7: 4];

            'd28: mii_tx_data <= src_mac[43:40];
            'd29: mii_tx_data <= src_mac[47:44];
            'd30: mii_tx_data <= src_mac[35:32];
            'd31: mii_tx_data <= src_mac[39:36];
            'd32: mii_tx_data <= src_mac[27:24];
            'd33: mii_tx_data <= src_mac[31:28];
            'd34: mii_tx_data <= src_mac[19:16];
            'd35: mii_tx_data <= src_mac[23:20];
            'd36: mii_tx_data <= src_mac[11: 8];
            'd37: mii_tx_data <= src_mac[15:12];
            'd38: mii_tx_data <= src_mac[ 3: 0];
            'd39: mii_tx_data <= src_mac[ 7: 4];

            'd40: mii_tx_data <= type_length_tmp[11: 8];
            'd41: mii_tx_data <= type_length_tmp[15:12];
            'd42: mii_tx_data <= type_length_tmp[ 3: 0];
            'd43: mii_tx_data <= type_length_tmp[ 7: 4];

            'd44: mii_tx_data <= fifo_rddata;

			'd45: mii_tx_data <= crc_result[27:24];
			'd46: mii_tx_data <= crc_result[31:28];
			'd47: mii_tx_data <= crc_result[19:16];
			'd48: mii_tx_data <= crc_result[23:20];
			'd49: mii_tx_data <= crc_result[11: 8];
			'd50: mii_tx_data <= crc_result[15:12];
			'd51: mii_tx_data <= crc_result[ 3: 0];
			'd52: mii_tx_data <= crc_result[ 7: 4];

            default : mii_tx_data <= 4'h0;
        endcase
    end
end

endmodule

2 TB段

自己編寫的tb段如下:

`timescale 1ns/1ns
`define p 40

module eth_send_tb;

	reg 		rstn		;
	reg 		mii_tx_clk	;
	reg [ 3:0]	fifo_rddata	;
	reg 		pulse		;
	reg [47:0]	des_mac		;
	reg [47:0] 	src_mac		;
	reg [15:0] 	type_length	;
	reg [31:0]	crc_result	;
	reg [11:0]	data_length	;
	
	wire	   mii_tx_en	;
	wire 	   fifo_rdreq	;
	wire 	   fifo_rdclk	;
	wire [3:0] mii_tx_data	;
	
initial 			mii_tx_clk = 1'b0			;
always #(`p/2)	mii_tx_clk = ~mii_tx_clk;

initial begin
	#(`p*2000);
	$stop;
end

initial begin
	rstn 		= 1'b0;	#(`p*10+3);
	rstn 		= 1'b1;
	
	//round one
	des_mac		= 48'hff_ff_ff_ff_ff_ff	;
	src_mac 		= 48'h00_21_85_c5_2b_8f	;
	type_length = 16'h08_06				;
	crc_result  = 32'hab_cd_ef_21		;
	data_length = 12'd10					;
	
	#(`p);	pulse = 1'b1;
	#(`p); 	pulse = 1'b0;
	
	#(`p*1000)
	
	//round two
	des_mac		= 48'hff_ff_ff_ff_ff_ff	;
	src_mac 	= 48'h00_21_85_c5_2b_8f	;
	type_length = 16'h08_06				;
	crc_result  = 32'hab_cd_ef_21		;
	data_length = 12'd50				;
	
	#(`p);	pulse = 1'b1;
	#(`p); 	pulse = 1'b0;
end

//for fifo_rddata
//��������FIFO���������Ѷ�

always @(posedge mii_tx_clk) begin
	if (!rstn)
		fifo_rddata = 0;
	else if (fifo_rdreq)
		fifo_rddata = fifo_rddata + 1'b1;
	else
		fifo_rddata = 0;
end


eth_send eth_send(
	.rstn		(rstn		),
	.mii_tx_clk	(mii_tx_clk	),
	.mii_tx_en	(mii_tx_en	),
	.mii_tx_data(mii_tx_data),
	.fifo_rddata(fifo_rddata),
	.fifo_rdreq	(fifo_rdreq	),
	.fifo_rdclk	(fifo_rdclk	),
	.pulse		(pulse		),
	.des_mac	(des_mac	),
	.src_mac	(src_mac	),
	.type_length(type_length),
	.crc_result	(crc_result	),
	.data_length(data_length)
);
	
endmodule

3 分析

仿真結果分析如下:

3.1 分析一

在這裏插入圖片描述
上圖可見,當mii_tx_en拉高之時,本應該傳輸數字4’h5卻多傳輸了一個4’h0(此數值是復位時的數值);
在這裏插入圖片描述

上圖可見,在data_length_tmp != 12’h0的計數中,我在tb中寫道data_length = 10,那麼編譯後,data_length_tmp也應該等於10,也就是說我想要傳遞的數據長度爲10個,但是這裏名明顯傳輸了11個數,從0a,見上圖紅框部分。

在這裏插入圖片描述

上圖可見,在最後的結尾處,當傳送完最後一個數,也就是crc_result中的4’h2時,mii_tx_en拉低,符合實際情況。

綜上,在mii_tx_en拉高的過程中,最開始多傳遞了一個數4’h5,以及在傳輸ARP字段時只想傳10個數卻傳了11個數。將data_length_tmp != 12’h0改爲data_length_tmp != 12’h1,可使ARP字段正確傳輸,而如何將mii_tx_en的拉高時刻正確拉高呢?

3.2 分析二

代碼中,mii_tx_en等同於en_tx,若改爲
assign mii_tx_en = (lsm_cnt >= 6’d1) && (lsm_cnt <= 6’d53);
仿真如下:
在這裏插入圖片描述
則還是會將復位時候的mii_tx_data傳輸出去,也就是將4’h0傳出去,同時,在lsm_cnt = 6’d15時發現最終傳出的4’h5只有14個,而比本應該傳15個4’h5少了一個。

那如果在LUT中將case語句之上的
else if (mii_tx_en) begin 去掉呢?
直接改成else語句又是如何呢?

通過仿真發現,是個好辦法(這裏不貼圖了,請自行仿真)。

3.3 分析三

當復位鍵按下時,lsm_cnt = 0
當外部脈衝沒有到來的時候,lsm_cnt = 0
當傳輸完成產生tx_done信號之後,lsm_cnt = 0

也就是說,當lsm_cnt = 0時,對應了很多種電路狀態,那麼在查找表中(LUT中),讓lsm_cnt = 0時就讓查找表工作,或者說讓查找表在lsm_cnt = 0的時候取值爲4’h5,是非常不妥的。

換句話說,當lsm_cnt = 0時,電路根本沒有工作,如果這時讓查找表工作,那豈不是沒有意義?

所以避開lsm_cnt = 0時在查找表中的取值,具體做法爲讓lsm_cnt = 0時,case的取值爲0;同時將之前的case語句中的取值全體加1,對應如下圖所示:
在這裏插入圖片描述
在這裏插入圖片描述
同時還要注意更改以下3個地方:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

4 收穫

4.1 收穫一

在編寫代碼中,根本看不懂梅哥的這一行代碼:
在這裏插入圖片描述
最終自己編寫代碼才發現此段代碼背後的深意,果然還得自己動手。

4.2 收穫二

在線性序列機中,或者別的查找表中,儘量避開cnt取值爲0的查找表的取值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章