一、HDMI
1.1 、HDMI介绍
高清多媒体接口(High Definition Multimedia Interface)是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号。HDMI可用于机顶盒、DVD播放机、个人计算机、电视、游戏主机、综合扩大机、数字音响与电视机等设备。HDMI可以同时发送音频和视频信号,由于音频和视频信号采用同一条线材,大大简化系统线路的安装难度。
HDMI是被设计来取代较旧的模拟信号影音发送接口如SCART或RCA等端子的。它支持各类电视与计算机视频格式,包括SDTV、HDTV视频画面,再加上多声道数字音频。HDMI与去掉音频传输功能的UDI都继承DVI的核心技术“传输最小化差分信号”TMDS,从本质上来说仍然是DVI的扩展。DVI、HDMI、UDI的视频内容都以即时、专线方式进行传输,这可以保证视频流量大时不会发生堵塞的现象。每个像素数据量为24位。信号的时序与VGA极为类似。
1.1.1 、HDMI接口
HDMI 1.0版本于2002年发布,最高数据传输速度为5Gbps;而2017年发布的HDMI 2.1标准的理论带宽可达48Gbps。HDMI的规格书中规定了四种HDMI接口。HDMI向下兼容DVI,但是DVI(数字视频接口)只能用于传输视频,而不能同时传输音频,这是两者最大的区别。此外,DVI接口的尺寸明显大于HDMI接口,如图:
右侧是最常见的A型HDMI接口,其引脚定义如下图:
图1.2.2 HDMI接口引脚图
DVI和HDMI接口协议在物理层使用TMDS标准传输音视频数据。
1.1.2 、TMDS介绍
TMDS(Transition Minimized Differential Signaling,最小化传输差分信号)是美国Silicon Image公司开发的一项高速数据传输技术,在DVI和HDMI视频接口中使用差分信号传输高速串行数据。TMDS差分传输技术使用两个引脚(如图21.1.2中的“数据2+”和“数据2-”)来传输一路信号,利用这两个引脚间的电压差的正负极性和大小来决定传输数据的数值(0或1)。
DVI或HDMI视频传输所使用的TMDS连接通过四个串行通道实现。对于DVI来说,其中三个通道分别用于传输视频中每个像素点的红、绿、蓝三个颜色分量(RGB4:4:4格式)。HDMI默认也是使用三个RGB通道,但是它同样可以选择传输像素点的亮度和色素信息(YCrCb4:4:4或YCrCb4:2:2格式)。第四个通道是时钟通道,用于传输像素时钟。独立的TMDS时钟通道为接收端提供接收的参考频率,保证数据在接收端能够正确恢复。
图1.3.1 、TMDS连接示意图
如果每个像素点的颜色深度为24位,即RGB每个颜色分量各占8位,那么每个通道上的颜色数据将通过一个8B/10B的编码器(Encoder)来转换成一个10位的像素字符。然后这个10位的字符通过并串转换器(Serializer)转换成串行数据,最后由TMDS数据通道发送出去。这个10:1的并转串过程所生成的串行数据速率是实际像素时钟速率的10倍。
在传输视频图像的过程中,数据通道上传输的是编码后的有效像素字符。而在每一帧图像的行与行之间,以及视频中不同帧之间的时间间隔(消隐期)内,数据通道上传输的则是控制字符。每个通道上有两位控制信号的输入接口,共对应四种不同的控制字符。这些控制字符提供了视频的行同步(HZYNC)以及帧同步(VSYNC)信息,也可以用来指定所传输数据的边界(用于同步)。
对于DVI传输,整个视频的消隐期都用来传输控制字符。而HDMI传输的消隐期除了控制字符之外,还可以用于传输音频或者其他附加数据,比如字幕信息等。这就是DVI和HDMI协议之间最主要的差别。从图1.3.1中也可以看出这一差别,即“Auxiliary Data”接口标有“HDMI Olny”,即它是HDMI所独有的接口。
从前面的介绍中我们可以看出,TMDS连接从逻辑功能上可以划分成两个阶段:编码和并串转换。在编码阶段,编码器将视频源中的像素数据、HDMI的音频/附加数据,以及行同步和场同步信号分别编码成10位的字符流。然后在并串转换阶段将上述的字符流转换成串行数据流,并将其从三个差分输出通道发送出去。
DVI编码器在视频有效数据段输出像素数据,在消隐期输出控制数据,如图1.3.2所示。其中VDE(Video Data Enable)为高电平时表示视频数据有效,为低电平代表当前处于视频消隐期。
图1.3.2 DVI编码输出示意图
图1.3.3给出了三个通道的DVI编码器示意图。对于像素数据的RGB三个颜色通道,编码器的逻辑是完全相同的。VDE用于各个通道选择输出视频像素数据还是控制数据。HSYNC和VSYNC信号在蓝色通道进行编码得到10位字符,然后在视频消隐期传输。绿色和红色通道的控制信号C0和C1同样需要进行编码,并在消隐期输出。但是DVI规范中这两个通道的控制信号是预留的(未用到),因此将其置为2‘b00。
图1.3.3 DVI编码示意图
每个通道输入的视频像素数据都要使用DVI规范中的TMDS编码算法进行编码。每个8-bit的数据都将被转换成460个特定10-bit字符中的一个。这个编码机制大致上实现了传输过程中的直流平衡,即一段时间内传输的高电平(数字“1”)的个数大致等于低电平(数字“0”)的个数。同时,每个编码后的10-bit字符中状态跳转(“由1到0”或者“由0到1”)的次数将被限制在五次以内。
除了视频数据之外,每个通道2-bit控制信号的状态也要进行编码,编码后分别对应四个不同的10-bit控制字符,分别是10'b1101010100,10'b0010101011,10'b0101010100,和10'b1010101011。可以看出,每个控制字符都有七次以上的状态跳转。视频字符和控制字符状态跳转次数的不同将会被用于发送和接收设备的同步。
HDMI协议与DVI协议在很多方面都是相同的,包括物理连接(TMDS)、有效视频编码算法以及控制字符的定义等。但是相比于DVI,HDMI在视频的消隐期会传输更多的数据,包括音频数据和附加数据。4-bit音频和附加数据将通过TERC4编码机制转换成10-bit TERC4字符,然后在绿色和红色通道上传输。
HDMI在输入附加数据的同时,还需要输入ADE(Aux/Audio Data Enable)信号,其作用和VDE是类似的:当ADE为高电平时,表明输入端的附加数据或者音频数据有效。如果大家想了解更多有关HDMI的细节,可以参考开发板资料(A盘)/8_FPGA参考资料/HDMI/《HDMI Specification 13a》。为了简单起见,我们在这里把HDMI接口当作DVI接口进行驱动。
在编码之后,3个通道的10-bit字符将进行并串转换,这一过程是使用7系列FPGA中专用的硬件资源来实现的。7系列的FPGA提供了专用的并串转换器——OSERDESE2。单一的OSERDESE2模块可以实现8:1的并串转换,通过位宽扩展可以实现10:1和14:1的转换率。
1.2 HDMI设计思路
对于HDMI,和之前VGA项目差不多,可以通过修改VGA的驱动代码,来实现HDMI的驱动,修改其中的分辨率和时钟,即可完成。
对于HDMI来说,需要将信号转为差分对,再将并转为串。
1.3 代码
module hdmi_driver(
input wire clk,
input wire rst_n,
output wire VSYNC,
output wire HSYNC,
output wire [23:0] RGB,
output wire en_display
);
parameter H_A = 40;
parameter H_B = 220;
parameter H_C = 1280;
parameter H_D = 110;
parameter H_E = 1650;
parameter V_A = 5;
parameter V_B = 20;
parameter V_C = 720;
parameter V_D = 5;
parameter V_E = 750;
reg [10:0] cnt_h;
reg [9:0] cnt_v;
wire en_h; //显示列的C段有效标志信�?
wire en_v; //显示行的C段有效标志信�?
wire addr_en_h;
wire addr_en;
always @ (posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
cnt_h <= 11'd0;
else if(cnt_h == H_E - 1)
cnt_h <= 11'd0;
else
cnt_h <= cnt_h + 1'b1;
end
always @ (posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
cnt_v <= 10'd0;
else if(cnt_h == H_E - 1)
begin
if(cnt_v == V_E - 1)
cnt_v <= 10'd0;
else
cnt_v <= cnt_v + 1'b1;
end
else
cnt_v <= cnt_v;
end
assign HSYNC = (cnt_h < H_A) ? 1'b0 : 1'b1;
assign VSYNC = (cnt_v < V_A) ? 1'b0 : 1'b1;
assign en_h = (cnt_h >= H_A + H_B && cnt_h < H_A + H_B + H_C) ? 1'b1 : 1'b0;
assign en_v = (cnt_v >= V_A + V_B && cnt_v < V_A + V_B + V_C) ? 1'b1 : 1'b0;
// assign en_h = (cnt_h >= 566 && cnt_h < 666) ? 1'b1 : 1'b0;
// assign en_v = (cnt_v >= 277 && cnt_v < 377) ? 1'b1 : 1'b0;
// assign addr_en_h = (cnt_h >= 565 && cnt_h < 665) ? 1'b1 : 1'b0;
// assign addr_en = (addr_en_h && en_v) ? 1'b1 : 1'b0;
assign en_display = (en_h && en_v) ? 1'b1 : 1'b0;
assign RGB = (en_display) ? 24'b11111111_11111111_11111111 : 24'd0;
/*
always @ (posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
addr <= 14'd0;
else if(addr_en)
begin
if(addr == 14'd9999)
addr <= 14'd0;
else
addr <= addr + 1'b1;
end
else
addr <= addr;
end
*/
// assign RGB = (en_display) ? q : 8'd0;
endmodule
Top顶层模块
module top(
input clk,
input rst_n,
output D0_P,
output D0_N,
output D1_P,
output D1_N,
output D2_P,
output D2_N,
output D3_P,
output D3_N
);
wire VSYNC;
wire HSYNC;
wire [23:0] RGB;
wire en_display;
wire clk_1;
wire clk_5;
wire locked;
wire R_b;
wire G_b;
wire B_b;
wire clk_b;
wire [9:0] G,B,R;
pll instance_name
(
// Clock out ports
.clk_out1(clk_1), // output clk_out1
.clk_out2(clk_5), // output clk_out2
// Status and control signals
.reset(~rst_n), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_in1(clk)); // input clk_in1
vga_driver vga_driver_inst(
.clk (clk_1),
.rst_n (locked),
.VSYNC (VSYNC),
.HSYNC (HSYNC),
.RGB (RGB),
.en_display (en_display)
);
dvi_encoder dvi_encoder_inst0(
.clkin (clk_1), // pixel clock input
.rstin (~locked), // async. reset input (active high)
.din (RGB[23:16]), // data inputs: expect registered
.c0 (1'b0), // c0 input
.c1 (1'b0), // c1 input
.de (en_display), // de input
.dout (R) // data outputs
);
serializer_10_to_1 serializer_10_to_1_inst0(
.reset (~locked), //
.paralell_clk (clk_1), //
.serial_clk_5x (clk_5), //
.paralell_data (R), //
.serial_data_out (R_b) //
);
OBUFDS OBUFDS_inst0 (
.O(D2_P), // 1-bit output: Diff_p output (connect directly to top-level port)
.OB(D2_N), // 1-bit output: Diff_n output (connect directly to top-level port)
.I(R_b) // 1-bit input: Buffer input
);
dvi_encoder dvi_encoder_inst1(
.clkin (clk_1), // pixel clock input
.rstin (~locked), // async. reset input (active high)
.din (RGB[15:8]), // data inputs: expect registered
.c0 (1'b0), // c0 input
.c1 (1'b0), // c1 input
.de (en_display), // de input
.dout (G) // data outputs
);
serializer_10_to_1 serializer_10_to_1_inst1(
.reset (~locked), //
.paralell_clk (clk_1), //
.serial_clk_5x (clk_5), //
.paralell_data (G), //
.serial_data_out (G_b) //
);
OBUFDS OBUFDS_inst1 (
.O(D1_P), // 1-bit output: Diff_p output (connect directly to top-level port)
.OB(D1_N), // 1-bit output: Diff_n output (connect directly to top-level port)
.I(G_b) // 1-bit input: Buffer input
);
dvi_encoder dvi_encoder_inst2(
.clkin (clk_1), // pixel clock input
.rstin (~locked), // async. reset input (active high)
.din (RGB[7:0]), // data inputs: expect registered
.c0 (HSYNC), // c0 input
.c1 (VSYNC), // c1 input
.de (en_display), // de input
.dout (B) // data outputs
);
serializer_10_to_1 serializer_10_to_1_inst2(
.reset (~locked),
.paralell_clk (clk_1), //
.serial_clk_5x (clk_5), //
.paralell_data (B), //
.serial_data_out (B_b) //
);
OBUFDS OBUFDS_inst2 (
.O(D0_P), //
.OB(D0_N), //
.I(B_b) //
);
serializer_10_to_1 serializer_10_to_1_inst3(
.reset (~locked),
.paralell_clk (clk_1), //
.serial_clk_5x (clk_5), //
.paralell_data (10'b11111_00000),
.serial_data_out (clk_b) //
);
OBUFDS OBUFDS_inst3 (
.O(D3_P), // 1-bit output: Diff_p output (connect directly to top-level port)
.OB(D3_N), // 1-bit output: Diff_n output (connect directly to top-level port)
.I(clk_b) // 1-bit input: Buffer input
);
endmodule
对于其他模块,都是进行调用,并没有自己编写,即不再简述。
二、以太网
2.1 、UDP数据包介绍
2.2 、MAC协议
2.3 、IP协议
2.4 、UDP协议
2.5 、代码
以下代码即为UDP发送模块。
module eth_tx (
input wire clk,
input wire rst_n,
input wire key,
output reg gmii_tx_en,
output wire gmii_tx_er,
output wire gmii_tx_clk,
output reg [7:0] gmii_tx_data
);
//88-A4-C2-E5-D3-66 {8'h8c,8'h82,8'hb9,8'h95,8'h10,8'hcc};
parameter PC_MAC = {8'h88,8'ha4,8'hc2,8'he5,8'hd3,8'h66};
parameter BOARD_MAC = {8'h88,8'ha4,8'hc2,8'he5,8'hd3,8'h67};
parameter PC_IP = {8'd192,8'd168,8'd0,8'd2};
parameter BOARD_IP = {8'd192,8'd168,8'd0,8'd3};
parameter PC_COM = 16'd65533;
parameter BOARD_COM = 16'd65531;
wire clk_125m;
wire locked;
wire flag;
wire neg_edge;
reg i_en;
reg i_init;
wire [31:0] crc;
wire [31:0] crc_new;
reg [63:0] send_buf;
reg [ 4:0] state;
reg [ 9:0] send_cnt;
wire key_flag;
wire [31:0] IP_HEAD1;
wire [31:0] IP_HEAD2;
wire [31:0] IP_HEAD3;
wire [15:0] DATA_LEN;
wire [15:0] UDP_LEN;
wire [15:0] IP_HEAD_LEN;
wire [15:0] IP_HEAD_SUM;
wire [31:0] IP_HEAD_SUM1;
assign gmii_tx_clk = clk_125m;
assign gmii_tx_er = 1'b0;
assign DATA_LEN = 16'd24;
assign UDP_LEN = DATA_LEN + 16'd8;
assign IP_HEAD_LEN = UDP_LEN + 16'd20;
assign IP_HEAD1 = {8'h45,8'd0,IP_HEAD_LEN};
assign IP_HEAD2 = 32'd0;
assign IP_HEAD3 = {8'hff,8'd17,IP_HEAD_SUM};
assign IP_HEAD_SUM1= IP_HEAD1[31:16] + IP_HEAD1[15:0] + IP_HEAD3[31:16] + PC_IP[31:16] + PC_IP[15:0] + BOARD_IP[31:16] + BOARD_IP[15:0];
assign IP_HEAD_SUM = ~(IP_HEAD_SUM1[31:16] + IP_HEAD_SUM1[15:0]);
my_pll my_pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_125m ),
.locked ( locked )
);
jitter jitter_inst(
.clk (clk_125m),
.rst_n (locked),
.key (key),
.flag (flag)
);
neg_edge neg_edge_inst(
.clk (clk_125m),
.rst_n (locked),
.flag (flag),
.neg_edge (neg_edge)
);
CRC32_D8 CRC32_D8_inst(
.clk (clk_125m),
.rst_n (locked),
.i_en (i_en),
.i_data (gmii_tx_data),
.i_init (i_init),
.crc (crc),
.crc_new (crc_new)
);
always @(posedge clk_125m)
begin
if (locked == 1'b0)
begin
gmii_tx_data <= 8'd0;
gmii_tx_en <= 1'b0;
state <= 5'd0;
i_en <= 1'b0;
i_init <= 1'b0;
send_buf <= 64'd0;
send_cnt <= 10'd0;
end
else
case (state)
5'd0 : begin
if (neg_edge)
state <= 5'd1;
else
state <= 5'd0;
end
5'd1 : begin
state <= 5'd2;
i_init <= 1'b1;
send_buf <= {{7{8'h55}},8'hd5};
end
5'd2 : begin
i_init <= 1'b0;
gmii_tx_en <= 1'b1;
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {PC_MAC,16'd0};
state <= 5'd3;
end
end
5'd3 : begin
i_en <= 1'b1;
if(send_cnt < 10'd5)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {BOARD_MAC,16'h0800};
state <= 5'd4;
end
end
5'd4 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {IP_HEAD1,IP_HEAD2};
state <= 5'd5;
end
end
5'd5 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {IP_HEAD3,BOARD_IP};
state <= 5'd6;
end
end
5'd6 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {PC_IP,BOARD_COM,PC_COM};
state <= 5'd7;
end
end
5'd7 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= {UDP_LEN,16'd0,32'd0};
state <= 5'd8;
end
end
5'd8 : begin
if(send_cnt < 10'd3)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= "Hello!!!";
state <= 5'd9;
end
end
5'd9 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= "World!!!";
state <= 5'd10;
end
end
5'd10 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= "Lichuang";
state <= 5'd11;
end
end
5'd11 : begin
if(send_cnt < 10'd7)
begin
send_cnt <= send_cnt + 1'b1;
gmii_tx_data <= send_buf[63:56];
send_buf <= {send_buf[55:0],send_buf[63:56]};
end
else
begin
send_cnt <= 10'd0;
gmii_tx_data <= send_buf[63:56];
send_buf <= 64'd0;
state <= 5'd12;
end
end
5'd12 : begin
i_en <= 1'b0;
gmii_tx_data <= ~{crc_new[24],crc_new[25],crc_new[26],crc_new[27],crc_new[28],crc_new[29],crc_new[30],crc_new[31]};
state <= 5'd13;
end
5'd13 : begin
i_en <= 1'b0;
gmii_tx_data <= ~{crc[16],crc[17],crc[18],crc[19],crc[20],crc[21],crc[22],crc[23]};
state <= 5'd14;
end
5'd14 : begin
i_en <= 1'b0;
gmii_tx_data <= ~{crc[8],crc[9],crc[10],crc[11],crc[12],crc[13],crc[14],crc[15]};
state <= 5'd15;
end
5'd15 : begin
i_en <= 1'b0;
gmii_tx_data <= ~{crc[0],crc[1],crc[2],crc[3],crc[4],crc[5],crc[6],crc[7]};
state <= 5'd16;
end
5'd16 : begin
gmii_tx_en <= 1'b0;
state <= 5'd0;
end
default: ;
endcase
end
endmodule
以下为发送模块代码:
module eth_rx(
input wire rx_clk,
input wire rst_n,
input wire rx_er,
input wire rx_en,
input wire [7:0] rx_data,
output reg fifo_wr_en,
output reg fifo_data_valid,
output reg fifo_data_clr,
output reg [7:0] fifo_data,
output reg [15:0] data_cnt
);
parameter PC_MAC = {8'h88,8'ha4,8'hc2,8'he5,8'hd3,8'h66};
parameter BOARD_MAC = {8'h88,8'ha4,8'hc2,8'he5,8'hd3,8'h67};
parameter PC_IP = {8'd192,8'd168,8'd0,8'd2};
parameter BOARD_IP = {8'd192,8'd168,8'd0,8'd3};
parameter PC_COM = 16'd65533;
parameter BOARD_COM = 16'd65531;
reg [4:0] state;
reg i_en;
reg i_init;
wire [31:0] crc;
reg [47:0] rx_buf;
reg [9:0] rx_cnt;
reg [31:0] IP_HEAD_SUM;
reg [15:0] UDP_LEN;
CRC32_D8 CRC32_D8_inst(
.clk (rx_clk),
.rst_n (rst_n),
.i_en (i_en),
.i_data (rx_data),
.i_init (i_init),
.crc (crc),
.crc_new ()
);
always @(posedge rx_clk)
begin
if (rst_n == 1'b0)
begin
fifo_wr_en <= 1'b0;
fifo_data <= 8'd0;
fifo_data_valid <= 1'b0;
fifo_data_clr <= 1'b0;
state <= 5'd1;
i_en <= 1'b0;
i_init <= 1'b0;
rx_buf <= 48'd0;
rx_cnt <= 10'd0;
IP_HEAD_SUM <= PC_IP[31:16] + PC_IP[15:0] + BOARD_IP[31:16] + BOARD_IP[15:0];
UDP_LEN <= 16'd0;
data_cnt <= 16'd0;
end
else
case (state)
5'd0 : begin
fifo_wr_en <= 1'b0;
fifo_data <= 8'd0;
fifo_data_valid <= 1'b0;
fifo_data_clr <= 1'b0;
state <= 5'd1;
i_en <= 1'b0;
i_init <= 1'b0;
rx_buf <= 48'd0;
rx_cnt <= 10'd0;
IP_HEAD_SUM <= PC_IP[31:16] + PC_IP[15:0] + BOARD_IP[31:16] + BOARD_IP[15:0];
UDP_LEN <= 16'd0;
end
5'd1 : begin
if (rx_en && rx_er == 1'b0)
begin
i_init <= 1'b1;
state <= 5'd2;
rx_buf <= {rx_buf[39:0],rx_data};
end
else
state <= 5'd1;
end
5'd2 : begin
i_init <= 1'b0;
data_cnt <= 16'd0;
rx_buf <= {rx_buf[39:0],rx_data};
if (rx_buf == {{5{8'h55}},8'hd5})
begin
state <= 5'd3;
i_en <= 1'd1;
end
else
state <= 5'd2;
end
5'd3 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd5)
rx_cnt <= rx_cnt + 1;
else
begin
rx_cnt <= 10'd0;
if(rx_buf == BOARD_MAC)
state <= 5'd4;
else
state <= 5'd20;
end
end
5'd4 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd5)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
if(rx_buf == PC_MAC)
state <= 5'd5;
else
state <= 5'd18;
end
end
5'd5 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd5)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
IP_HEAD_SUM <= IP_HEAD_SUM + rx_buf[31:16] + rx_buf[15:0];
if(rx_buf[47:24] == {16'h0800,8'h45})
state <= 5'd6;
else
state <= 5'd18;
end
end
5'd6 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
state <= 5'd7;
IP_HEAD_SUM <= IP_HEAD_SUM + rx_buf[31:16] + rx_buf[15:0];
end
end
5'd7 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
IP_HEAD_SUM <= IP_HEAD_SUM + rx_buf[31:16] + rx_buf[15:0];
if(rx_buf[23:16] == 8'd17)
state <= 5'd8;
else
state <= 5'd18;
end
end
5'd8 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
if(rx_buf[31:0] == PC_IP)
state <= 5'd9;
else
state <= 5'd18;
end
end
5'd9 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
IP_HEAD_SUM <= IP_HEAD_SUM + rx_buf[31:16] + rx_buf[15:0];
if(rx_buf[31:0] == BOARD_IP && (IP_HEAD_SUM[31:16] == ~IP_HEAD_SUM[15:0]))
state <= 5'd10;
else
state <= 5'd18;
end
end
5'd10 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
if(rx_buf[31:0] == {PC_COM,BOARD_COM})
state <= 5'd11;
else
state <= 5'd18;
end
end
5'd11 : begin
i_en <= 1'b1;
rx_buf <= {rx_buf[39:0],rx_data};
if(rx_cnt < 10'd3)
begin
rx_cnt <= rx_cnt + 1'b1;
end
else
begin
rx_cnt <= 10'd0;
state <= 5'd12;
UDP_LEN <= rx_buf[31:16];
data_cnt <= rx_buf[31:16] - 16'd9;
end
end
5'd12 : begin
if(rx_cnt < UDP_LEN - 16'd9)
begin
rx_cnt <= rx_cnt + 1'b1;
fifo_wr_en <= 1'b1;
fifo_data <= rx_data;
end
else
begin
fifo_wr_en <= 1'b0;
fifo_data <= rx_data;
state <= 5'd13;
end
end
5'd13 : begin
state <= 5'd14;
end
5'd14 : begin
state <= 5'd15;
end
5'd15 : begin
state <= 5'd16;
i_en <= 1'd0;
end
5'd17 : begin
if(crc == 32'hc704dd7b)
begin
fifo_data_valid <= 1'b1;
state <= 5'd0;
end
else
begin
fifo_data_clr <= 1'b1;
state <= 5'd0;
end
end
5'd18 : begin
if(rx_en == 1'b0)
state <= 5'd0;
else
state <= 5'd18;
end
default: ;
endcase
end
endmodule
其实现原理就是发送模块的逆过程。
三 、 总结
以太网实现过程基于UDP,代码思路根据每一层顺序写下来即可完成,其中需要注意的是MAC的地址需要提前准备好,因为没有写ARP协议,不可通过代码来获取MAC地址。端口号也需要提前准备好。在接收模块中需要考虑CRC获取数据的时机,不可提前也不可推迟。
未待完续,持续更新,大家请多多关注。有需要的也可以报名参加叁芯智能科技FPGA工程师就业班,主打Xilinx,课程内容丰富,系统性学习FPGA,高薪就业,线上线下同步,近期新班04月15号开班,可以实地考察,免费试听,常年开班,欢迎报名参加!
FPGA就业班,2023.04.15开班,系统性学习FPGA,高薪就业,线上线下同步!