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

原创 FPGA技术江湖 2024-01-14 06:39

点击上方蓝字关注我们


一、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软件安装包、书籍、源码、技术文档…(2024.01.06更新)

FPGA就业班,2024.02.26开班,新增课程内容不加价,高薪就业,线上线下同步!


FPGA技术江湖广发江湖帖

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


FPGA技术江湖微信交流群

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


FPGA技术江湖QQ交流群

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

FPGA技术江湖 任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
评论
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 68浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 98浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 102浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 119浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 120浏览
  • TOF多区传感器: ND06   ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。                 如果用ND06进行手势识别,只需要经过三个步骤: 第一步&
    esad0 2024-12-04 11:20 58浏览
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 119浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 73浏览
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 112浏览
  • 《高速PCB设计经验规则应用实践》+PCB绘制学习与验证读书首先看目录,我感兴趣的是这一节;作者在书中列举了一条经典规则,然后进行详细分析,通过公式推导图表列举说明了传统的这一规则是受到电容加工特点影响的,在使用了MLCC陶瓷电容后这一条规则已经不再实用了。图书还列举了高速PCB设计需要的专业工具和仿真软件,当然由于篇幅所限,只是介绍了一点点设计步骤;我最感兴趣的部分还是元件布局的经验规则,在这里列举如下:在这里,演示一下,我根据书本知识进行电机驱动的布局:这也算知行合一吧。对于布局书中有一句:
    wuyu2009 2024-11-30 20:30 125浏览
  • 学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&
    youyeye 2024-11-30 14:30 78浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 87浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 103浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦