关注、星标公众号,精彩内容每日送达
来源:网络素材
一.UDP发送模块
1.1模块框图
接口描述:
//输入接口:
gmii_clk:发送时钟。
sys_rst_n:系统复位。
send_en:发送使能。
send_data:发送的数据,位宽为32位。
send_data_num:发送数据的字节数,单位是byte。
crc_data/crc_next:crc检验数据。
//输出接口:
send_end:发送结束使能。
read_data_req:fifo读数据请求。
eth_tx_en:发送字节数据有效信号。
eth_tx_data:发送的数据,单位是字节。
crc_en/crc_clr:crc使能和清空信号。
1.2.波形图
1.3逻辑实现思路
开始状态是空闲状态;当trig_tx_en信号为高电平时,进入CHECK_SUM状态,在这个状态进行IP_UDP校验,当计数器cnt计数到3的时候,跳转到下一个状态,PACKET_HEAD,在这这个状态下进行以太网帧头的发送。由于发送的帧头为七个8‘h55加一个8’hd5,所以计数器计数到7时,跳转到下一个状态EHT_HEAD,在这个状态下,发送的数据为目的地址(6个个字节),源地址(6个字节),加两个字节的ETH类型。所以计数到13时跳转到下一个IP_UDP_HEAD状态。在IP_UDP_HEAD状态下,发送IP_UDP头部,以4个字节为单位,一个7个四个字节,所以帧计数器frame_cnt计数到6,这时候跳转到SEND_DATA状态,在这个状态下,data_cnt为实际发送数据的计数值,又设置变量send_data_len,此变量为实际要发送的数据值,值得注意的是,又上一篇博客可知,以太网数据发送部分最低为46个字节,而IP_UDP头部数据就占用了28个字节,所以带发送的数据最少为18个字节,为了满足实际情况的合理性,故考虑实际发送的数据少于18个字节的情况,设置add_cnt变量,此变量为少于18个最小字节的,待补充字节的个数。当add_cnt和data_cnt两者相加的值等于send_data_len-1时,状态跳转到CRC状态,在此状态接受四个字节的crc校验数据,cnt等于3时,跳转到IDLE状态,至此一帧的以太网数据发送完成。
1.4 发送状态机代码
module udp_tx
#(
parameter BOARD_MAC = 48'hff_ff_ff_ff_ff_ff ,
parameter BOARD_IP = 32'hff_ff_ff_ff ,
parameter BOARD_PORT = 16'd1234 ,
parameter PC_MAC = 48'hff_ff_ff_ff_ff_ff ,
parameter PC_IP = 32'hff_ff_ff_ff ,
parameter PC_PORT = 16'd1234
)
(
input wire [31:0] crc_data ,
input wire [7:0] crc_next ,
input wire [15:0] tx_byte_num ,
input wire [31:0] tx_data ,
input wire clk ,
input wire rst_n ,
input wire tx_start_en ,
output wire [7:0] gmii_txd ,
output wire crc_clr ,
output wire crc_en ,
output wire gmii_tx_en ,
output wire tx_done ,
output wire tx_req
);
reg tx_start_en_d0;
reg tx_start_en_d1;
wire pos_start_en;
reg trig_tx_en;
assign pos_start_en = ~tx_start_en_d1 & tx_start_en_d0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
tx_start_en_d0 <= 'd0;
tx_start_en_d1 <= 'd0;
end
else begin
tx_start_en_d0 <= tx_start_en;
tx_start_en_d1 <= tx_start_en_d0;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
trig_tx_en <= 'd0;
else
trig_tx_en <= pos_start_en;
end
//状态变量
reg [6:0] cur_state;
reg [6:0] next_state;
localparam IDLE = 7'b0_000_001,
CHECK_SUM = 7'b0_000_010,
PACKET_HEAD = 7'b0_000_100,
ETH_HEAD = 7'b0_001_000,
IP_UDP_HEAD = 7'b0_010_000,
SEND_DATA = 7'b0_100_000,
CRC = 7'b1_000_000;
localparam ETH_TYPE = 16'h0800 ;
//数据长度变量
reg [31:0] check_sum ;
reg [15:0] data_len ;
reg [15:0] ip_len ;
reg [15:0] udp_len ;
//状态转移变量
reg sw_en ;
//计数变量
reg [4:0] cnt ;
reg [2:0] frame_cnt ;
reg [15:0] data_cnt ;
reg [4:0] cnt_add ;
reg [1:0] cnt_send_state ;
//寄存器输出变量,防止时序违例
reg [7:0] r_gmii_txd ;
reg r_crc_clr ;
reg r_crc_en ;
reg r_gmii_tx_en ;
reg r_tx_done ;
reg r_tx_req ;
assign gmii_txd = r_gmii_txd ;
assign crc_clr = r_crc_clr ;
assign crc_en = r_crc_en ;
assign gmii_tx_en = r_gmii_tx_en ;
assign tx_done = r_tx_done ;
assign tx_req = r_tx_req ;
//存以太网数据变量
reg [7:0] packet_head[7:0] ;
reg [7:0] eth_head[13:0] ;
reg [31:0] ip_udp_head[6:0] ;
wire [15:0] send_data_len ;
assign send_data_len = (data_len >= 16'd18)?data_len : 16'd18;
//时序逻辑表示状态寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
end
//组合逻辑表示状态转移
always@(*) begin
next_state = IDLE;
case(cur_state)
IDLE:begin
if(sw_en)
next_state <= CHECK_SUM;
else
next_state <= IDLE;
end
CHECK_SUM:begin
if(sw_en)
next_state <= PACKET_HEAD;
else
next_state <= CHECK_SUM;
end
PACKET_HEAD:begin
if(sw_en)
next_state <= ETH_HEAD;
else
next_state <= PACKET_HEAD;
end
ETH_HEAD:begin
if(sw_en)
next_state <= IP_UDP_HEAD;
else
next_state <= ETH_HEAD;
end
IP_UDP_HEAD:begin
if(sw_en)
next_state <= SEND_DATA;
else
next_state <= IP_UDP_HEAD;
end
SEND_DATA:begin
if(sw_en)
next_state <= CRC;
else
next_state <= SEND_DATA;
end
CRC:begin
if(sw_en)
next_state <= IDLE;
else
next_state <= CRC;
end
default:next_state <= IDLE;
endcase
end
//时序逻辑表示状态输出
always@(posedge clk or negedge rst_n) begin
if(!rst_n)begin
check_sum <= 'd0;
data_len <= 'd0;
ip_len <= 'd0;
udp_len <= 'd0;
sw_en <= 'd0;
cnt <= 'd0;
frame_cnt <= 'd0;
data_cnt <= 'd0;
cnt_add <= 'd0;
cnt_send_state <= 'd0;
packet_head[0] <= 'd0;
packet_head[1] <= 'd0;
packet_head[2] <= 'd0;
packet_head[3] <= 'd0;
packet_head[5] <= 'd0;
packet_head[6] <= 'd0;
packet_head[7] <= 'd0;
eth_head[0] <= 'd0;
eth_head[1] <= 'd0;
eth_head[2] <= 'd0;
eth_head[3] <= 'd0;
eth_head[4] <= 'd0;
eth_head[5] <= 'd0;
eth_head[6] <= 'd0;
eth_head[7] <= 'd0;
eth_head[8] <= 'd0;
eth_head[9] <= 'd0;
eth_head[10] <= 'd0;
eth_head[11] <= 'd0;
eth_head[12] <= 'd0;
eth_head[13] <= 'd0;
ip_udp_head[1][31:16] <= 'd0;
r_crc_en <= 'd0;
r_gmii_txd <= 'd0;
r_gmii_tx_en <= 'd0;
end
else begin
//initial PACKET_HEAD
packet_head[0] <= 8'h55;
packet_head[1] <= 8'h55;
packet_head[2] <= 8'h55;
packet_head[3] <= 8'h55;
packet_head[4] <= 8'h55;
packet_head[5] <= 8'h55;
packet_head[6] <= 8'h55;
packet_head[7] <= 8'hd5;
//initial ETH_HEAD
eth_head[0] <= PC_MAC[47:40] ;
eth_head[1] <= PC_MAC[39:32] ;
eth_head[2] <= PC_MAC[31:24] ;
eth_head[3] <= PC_MAC[23:16] ;
eth_head[4] <= PC_MAC[15:8] ;
eth_head[5] <= PC_MAC[7:0] ;
eth_head[6] <= BOARD_MAC[47:40] ;
eth_head[7] <= BOARD_MAC[39:32] ;
eth_head[8] <= BOARD_MAC[31:24] ;
eth_head[9] <= BOARD_MAC[23:16] ;
eth_head[10] <= BOARD_MAC[15:8] ;
eth_head[11] <= BOARD_MAC[7:0] ;
eth_head[12] <= ETH_TYPE[15:8] ;
eth_head[13] <= ETH_TYPE[7:0] ;
//清楚变量锁存
sw_en <= 1'b0;
r_tx_req <= 1'b0;
r_tx_done <= 1'b0;
case(next_state)
IDLE:begin
r_gmii_tx_en <= 1'b0;
if(pos_start_en)begin
data_len <= tx_byte_num;
udp_len <= tx_byte_num + 'd8;
ip_len <= tx_byte_num + 'd28;
end
else if(trig_tx_en) begin
sw_en <= 1'b1;
ip_udp_head[0] <= {8'h45,8'h00,ip_len};
ip_udp_head[1][31:16] <= ip_udp_head[1][31:16] + 1'b1;
ip_udp_head[1][15:0] <= 16'h4000;
ip_udp_head[2] <= {8'h40,8'd17,16'h0};
ip_udp_head[3] <= BOARD_IP;
ip_udp_head[4] <= PC_IP;
ip_udp_head[5] <= {BOARD_PORT,PC_PORT};
ip_udp_head[6] <= {udp_len,16'h0000};
end
end
CHECK_SUM:begin
cnt <= cnt + 1'd1;
if(cnt == 'd0)
check_sum <= ip_udp_head[0][31:16] + ip_udp_head[0][15:0]
+ip_udp_head[1][31:16] + ip_udp_head[1][15:0]
+ip_udp_head[2][31:16] + ip_udp_head[2][15:0]
+ip_udp_head[3][31:16] + ip_udp_head[3][15:0]
+ip_udp_head[4][31:16] + ip_udp_head[4][15:0];
else if(cnt == 'd1)
check_sum <= check_sum[31:16] + check_sum[15:0];
else if(cnt == 'd2)
check_sum <= check_sum[31:16] + check_sum[15:0];
else begin
check_sum <= check_sum;
cnt <= 'd0;
sw_en <= 1'b1;
end
end
PACKET_HEAD:begin
cnt <= cnt + 1'd1;
r_gmii_txd <= packet_head[cnt];
r_gmii_tx_en <= 1'b1;
if(cnt == 'd7) begin
sw_en <= 1'b1;
cnt <= 'd0;
end
end
ETH_HEAD:begin
cnt <= cnt + 1'd1;
r_crc_en<= 1'b1;
r_gmii_txd <= eth_head[cnt];
if(cnt == 'd13)begin
sw_en <= 1'b1;
cnt <= 'd0;
end
end
IP_UDP_HEAD:begin
cnt <= cnt + 'd1;
case(cnt)
'd0:r_gmii_txd <= ip_udp_head[frame_cnt][31:24];
'd1:r_gmii_txd <= ip_udp_head[frame_cnt][23:16];
'd2:begin
r_gmii_txd <= ip_udp_head[frame_cnt][15:8];
if(frame_cnt == 'd6)
r_tx_req <= 1'b1;
end
'd3:begin
frame_cnt <= frame_cnt + 'd1;
r_gmii_txd <= ip_udp_head[frame_cnt][7:0];
cnt <= 'd0;
if(frame_cnt == 'd6)begin
sw_en <= 1'b1;
frame_cnt <= 'd0;
end
end
default:;
endcase
end
SEND_DATA:begin
cnt_send_state <= cnt_send_state + 'd1;
case(cnt_send_state)
'd0:r_gmii_txd <= tx_data[31:24];
'd1:r_gmii_txd <= tx_data[23:16];
'd2:begin
r_gmii_txd <= tx_data[15:8];
if(data_cnt != data_len -'d1)
r_tx_req <= 1'b1;
end
'd3:r_gmii_txd <= tx_data[7:0];
default:;
endcase
if(data_cnt <data_len - 'd1)
data_cnt <= data_cnt + 'd1;
else
data_cnt <= data_cnt;
if((data_cnt + cnt_add < send_data_len - 'd1)&&(data_cnt == data_len -'d1)) //最少发18个数据
cnt_add <= cnt_add + 'd1;
else
cnt_add <= cnt_add;
if(data_cnt+cnt_add == send_data_len - 'd1)
sw_en <= 1'b1;
end
CRC:begin
r_crc_en <= 1'b0;
cnt <= cnt + 'd1;
case(cnt)
0:r_gmii_txd <={~crc_next[0],~crc_next[1],~crc_next[2],~crc_next[3],~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
1:r_gmii_txd <={~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
2:r_gmii_txd <={~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
3:r_gmii_txd <={~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
default:;
endcase
if(cnt == 'd3)begin
cnt <= 'd0;
sw_en <=1'b1;
r_tx_done <= 1'b1;
end
end
endcase
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
r_crc_clr <= 'd0;
else
r_crc_clr <= r_tx_done;
end
endmodule
1.5.仿真波形图
二.UDP接受模块
2.1 模块框图
#输入接口
gmii_clk:接受时钟。
sys_rst_n:系统复位。
gmii_rxdv:接受使能。
gmii_rx_data:接受数据,单位是字节。
#输出接口
rec_data_en:数据有效使能。
rec_data:接受的数据。
rec_end:接受完成标志。
rec_data_num:接受数据的个数。
2.2.波形图
2.3逻辑实现思路
首先将输入的rxdv和rx_data数据同步在时钟沿下,在开始状态时,当gmii_rxdv_reg和gmii_rx_data分别为高电平和8‘h55时,进入PACK_HEAD状态,当cnt计数到6且此时的数据是8’hd5时,进入下一个状态ETH_HEAD状态,在这个状态下,要接受目的mac地址,源mac地址,和eth类型,将目的mac地址寄存在des_mac变量中,当des_mac和输入的目的地址相同时,拉高mac_flag信号,同时当cnt等于13的条件下,进入IP_HEAD状态,在此状态下,将目的ip号寄存在des-ip变量下,当des_ip和输入的目的ip相同时,拉高ip_flag信号,同时当cnt等于19的时候,进入UDP_HEAD状态,在此状态下,当cnt计数到7的时候,进入REC_DATA状态,当data_cnt等于dala_len时,进入REC_END状态,当gmii_rxdv_reg为低时,进入IDLE状态。以此循环。
2.4 接受状态机代码
2.5 仿真波形
仿真整体波形
仿真局部波形
三.rgmii接口和gmii接口
3.1rgmii_to_gmii
模块框图
波形图
注:双沿采样变成单沿采样,同步到clk时钟的上升沿。
仿真波形:
3.2gmii_to_rgmii
模块框图
波形图
注:单沿采样同步到双沿采样。
仿真波形:
总结:千兆以太网就到这里了,以后有机会更新ddraxi接口设计或者万兆以太网。
(全文完)
声明:我们尊重原创,也注重分享;文字、图片版权归原作者所有。转载目的在于分享更多信息,不代表本号立场,如有侵犯您的权益请及时联系,我们将第一时间删除,谢谢!
原文链接:
https://blog.youkuaiyun.com/qq_52245062/article/details/140014969