就业班学员学习笔记分享:FPGA之HDMI、以太网篇

原创 FPGA技术江湖 2023-04-06 07:57

点击上方蓝字关注我们


一、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接口,如图:

图1.2.1  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,高薪就业,线上线下同步!

END


往期精选 

 
 

【免费】FPGA工程师招聘平台

简谈FPGA设计中系统运行频率计算方法与组合逻辑的层级

SANXIN-B01开发板verilog教程V3电子版

学员笔记连载 | FPGA Zynq 千兆以太网回环

求职面试 | FPGA或IC面试题最新汇总篇

FPGA项目开发:204B实战应用-LMK04821代码详解(二)

项目合作 | 承接FPGA项目公告

资料汇总|FPGA软件安装包、书籍、源码、技术文档…(2023.01.02更新)

FPGA就业班,2023.04.15开班,系统性学习FPGA,高薪就业,线上线下同步!


FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

加群主微信,备注姓名+公司/学校+岗位/专业进群


FPGA技术江湖QQ交流群

备注姓名+公司/学校+岗位/专业进群

FPGA技术江湖 任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
评论
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 40浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 141浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 80浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 167浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 98浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 113浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 87浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 66浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 83浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 103浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 125浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 63浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦