參考梅雪松的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個數,從0
到a
,見上圖紅框部分。
上圖可見,在最後的結尾處,當傳送完最後一個數,也就是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
的查找表的取值。