FPGA之HDMI与以太网篇分享

描述

一、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接口,其引脚定义如下图:

FPGA

图1.2.2HDMI接口引脚图

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通道,但是它同样可以选择传输像素点的亮度和色素信息(YCrCb44或YCrCb42格式)。第四个通道是时钟通道,用于传输像素时钟。独立的TMDS时钟通道为接收端提供接收的参考频率,保证数据在接收端能够正确恢复。

FPGA

图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)为高电平时表示视频数据有效,为低电平代表当前处于视频消隐期。

FPGA

图1.3.2DVI编码输出示意图

图1.3.3给出了三个通道的DVI编码器示意图。对于像素数据的RGB三个颜色通道,编码器的逻辑是完全相同的。VDE用于各个通道选择输出视频像素数据还是控制数据。HSYNC和VSYNC信号在蓝色通道进行编码得到10位字符,然后在视频消隐期传输。绿色和红色通道的控制信号C0和C1同样需要进行编码,并在消隐期输出。但是DVI规范中这两个通道的控制信号是预留的(未用到),因此将其置为2‘b00。

FPGA

图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的转换率。

FPGA

FPGA

FPGA

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数据包介绍

FPGA

2.2 、MAC协议

FPGA

2.3 、IP协议

FPGA

2.4 、UDP协议

FPGA

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
FPGA

 

以下为发送模块代码:

 

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获取数据的时机,不可提前也不可推迟。






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分