【程序】Altera FPGA Verilog使用三速以太网IP核(Triple-Speed Ethernet)读写MDIO寄存器,并接收以太网数据包

Quartus II 13.0工程下载地址:百度网盘 请输入提取码(提取码:ykfq,内含三速以太网license.dat)

【开发板】

开发板型号:小梅哥AC620
FPGA型号:EP4CE10F17C8N
晶振频率:50MHz
PHY芯片型号:RTL8201CP(MII接口,百兆以太网PHY芯片)

【程序功能】

1. 读写三速以太网IP核本身的寄存器,以及PHY芯片的寄存器
2. 网线热插拔检测,并获取PHY芯片自动协商结果(速率,双工模式)
3. 显示收到的数据包的大小,以及错误码(0x80代表成功,0x85代表CRC错误,0x89代表数据包被截断)

【IP核配置】

三速以太网工作模式配置为10/100Mb Small MAC模式,MII接口:

使能半双工支持,以及MDIO接口(用于读写PHY芯片的寄存器):

FIFO深度保持默认:
深度为2048×4=8192字节,假设每个数据包平均长度为300字节,那么FIFO中最多可以保存27个数据包。

【时序分析】

读写三速以太网IP核本身的0x02号寄存器的时序:

PHY芯片MII接口输出收到的以太网数据包:

三速以太网IP核Avalon Streaming接口输出收到的以太网数据包:

【程序代码】

`include "includes/StringManipulator.v"

module main(
    input clock,
    input uart_rx,
    output uart_tx,
    output eth_rst_n,
    output eth_mdc,
    inout eth_mdio,
    input eth_col,
    input eth_crs,
    input eth_tx_clk,
    output eth_tx_en,
    output [3:0] eth_txd,
    input eth_rx_clk,
    input eth_rx_dv,
    input [3:0] eth_rxd,
    input eth_rx_er
    );
    
    /* 产生复位信号 */
    wire nrst;
    Reset reset(clock, nrst);
    
    /* 缓冲eth_rx_clk和eth_tx_clk时钟输入信号, 避免CRC错误 */
    wire eth_rx_clk_g;
    wire eth_tx_clk_g;
    altclkctrl_0 altclkctrl_0(
        .inclk(eth_rx_clk),
        .outclk(eth_rx_clk_g)
    );
    altclkctrl_1 altclkctrl_1(
        .inclk(eth_tx_clk),
        .outclk(eth_tx_clk_g)
    );
    
    /* 串口发送 */
    wire uart_tx_request;
    wire [7:0] uart_tx_data;
    wire uart_tx_ready;
    wire uart_sent;
    UARTTransmitter #(50000000) uart_transmitter(clock, nrst, uart_tx, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent);
    
    localparam UART_MAXSIZE = 8'd20;
    reg [8 * UART_MAXSIZE - 1:0] uart_bytearray_tx_data;
    reg [1:0] uart_bytearray_tx_mode;
    reg uart_bytearray_tx_request;
    reg [7:0] uart_bytearray_tx_size;
    reg uart_bytearray_tx_endl;
    wire uart_bytearray_tx_ready;
    wire uart_bytearray_sent;
    ByteArrayTransmitter #(UART_MAXSIZE) uart_bytearray_transmitter(clock, nrst, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent, 
      uart_bytearray_tx_mode, uart_bytearray_tx_request, uart_bytearray_tx_data, uart_bytearray_tx_size, uart_bytearray_tx_endl, uart_bytearray_tx_ready, uart_bytearray_sent);
    
    /* 以太网 */
    reg [7:0] eth_address;
    reg [31:0] eth_ff_tx_data;
    reg eth_ff_tx_eop;
    reg eth_ff_tx_err;
    reg [1:0] eth_ff_tx_mod;
    reg eth_ff_tx_sop;
    reg eth_ff_tx_wren;
    reg eth_read;
    reg eth_write;
    reg [31:0] eth_writedata;
    (* keep *) wire eth_ena_10;
    (* keep *) wire eth_mode;
    (* keep *) wire eth_ff_rx_a_empty;
    (* keep *) wire eth_ff_rx_a_full;
    (* keep *) wire [31:0] eth_ff_rx_data;
    (* keep *) wire eth_ff_rx_dsav;
    (* keep *) wire eth_ff_rx_dval;
    (* keep *) wire eth_ff_rx_eop;
    (* keep *) wire [1:0] eth_ff_rx_mod;
    (* keep *) wire eth_ff_rx_sop;
    (* keep *) wire eth_ff_tx_a_empty;
    (* keep *) wire eth_ff_tx_a_full;
    wire eth_ff_tx_rdy;
    wire eth_ff_tx_septy;
    wire eth_tx_er;
    wire eth_magic_wakeup;
    wire eth_mdio_oen;
    wire eth_mdio_out;
    (* keep *) wire [31:0] eth_readdata;
    (* keep *) wire [5:0] eth_rx_err;
    (* keep *) wire [17:0] eth_rx_err_stat;
    (* keep *) wire [3:0] eth_rx_frm_type;
    wire eth_tx_ff_uflow;
    (* keep *) wire eth_waitrequest;
    
    assign eth_mdio = (!eth_mdio_oen) ? eth_mdio_out : 1'bz;
    assign eth_rst_n = nrst;
    
    eth_0 eth_0(
        .reset(~nrst),
        .clk(clock),
        .address(eth_address),
        .ena_10(eth_ena_10),
        .eth_mode(eth_mode),
        .ff_rx_a_empty(eth_ff_rx_a_empty),
        .ff_rx_a_full(eth_ff_rx_a_full),
        .ff_rx_clk(clock), // avalon streaming接收时钟
        .ff_rx_data(eth_ff_rx_data),
        .ff_rx_dsav(eth_ff_rx_dsav),
        .ff_rx_dval(eth_ff_rx_dval),
        .ff_rx_eop(eth_ff_rx_eop),
        .ff_rx_mod(eth_ff_rx_mod),
        .ff_rx_rdy(1'b1),
        .ff_rx_sop(eth_ff_rx_sop),
        .ff_tx_a_empty(eth_ff_tx_a_empty),
        .ff_tx_a_full(eth_ff_tx_a_full),
        .ff_tx_clk(clock), // avalon streaming发送时钟
        .ff_tx_crc_fwd(1'b0),
        .ff_tx_data(eth_ff_tx_data),
        .ff_tx_eop(eth_ff_tx_eop),
        .ff_tx_err(eth_ff_tx_err),
        .ff_tx_mod(eth_ff_tx_mod),
        .ff_tx_rdy(eth_ff_tx_rdy),
        .ff_tx_septy(eth_ff_tx_septy),
        .ff_tx_sop(eth_ff_tx_sop),
        .ff_tx_wren(eth_ff_tx_wren),
        .m_rx_d(eth_rxd),
        .m_rx_en(eth_rx_dv),
        .m_rx_err(eth_rx_er),
        .m_tx_d(eth_txd),
        .m_tx_en(eth_tx_en),
        .m_tx_err(eth_tx_er),
        .magic_sleep_n(1'b1),
        .magic_wakeup(eth_magic_wakeup),
        .mdc(eth_mdc),
        .mdio_in(eth_mdio),
        .mdio_oen(eth_mdio_oen),
        .mdio_out(eth_mdio_out),
        .read(eth_read),
        .readdata(eth_readdata),
        .rx_clk(eth_rx_clk_g),
        .rx_err(eth_rx_err),
        .rx_err_stat(eth_rx_err_stat),
        .rx_frm_type(eth_rx_frm_type),
        .set_10(1'b0),
        .set_1000(1'b0),
        .tx_clk(eth_tx_clk_g),
        .tx_ff_uflow(eth_tx_ff_uflow),
        .waitrequest(eth_waitrequest),
        .write(eth_write),
        .writedata(eth_writedata),
        .xoff_gen(1'b0),
        .xon_gen(1'b0)
    );
    
    /* 统计帧的长度 */
    reg eth_frame_recved;
    reg signed [15:0] eth_frame_len;
    reg [5:0] eth_frame_err;
    always @(posedge clock, negedge nrst) begin
        if (!nrst) begin
            eth_frame_recved <= 0;
            eth_frame_len <= 0;
        end
        else if (eth_ff_rx_dval) begin
            if (eth_ff_rx_sop) begin
                eth_frame_recved <= 0;
                eth_frame_len <= 2'd2 - eth_ff_rx_mod;
                // 假设第一次收到1字节(eth_ff_rx_mod=3), 第二次收到4字节(eth_ff_rx_mod=1), 那么有效数据量就是1+4-2=3字节
                // 这里如果eth_ff_rx_mod=3, 那么eth_frame_len=-1, 下一次接收就会自动减去1, 即2-3=-1, -1+4=3
            end
            else begin
                eth_frame_len <= eth_frame_len + (3'd4 - eth_ff_rx_mod);
                if (eth_ff_rx_eop) begin
                    eth_frame_recved <= 1;
                    eth_frame_err <= eth_rx_err;
                end
            end
        end
    end
    
    /* 字符串转换 */
    localparam STRMANIP_MAXSIZE = 8'd16;
    reg [3:0] strmanip_request;
    reg [STRMANIP_MAXSIZE * 8 - 1:0] strmanip_str1_in;
    reg [STRMANIP_MAXSIZE * 8 - 1:0] strmanip_str2_in;
    reg signed [31:0] strmanip_num_in;
    wire [STRMANIP_MAXSIZE * 8 - 1:0] strmanip_str_out;
    wire signed [31:0] strmanip_num_out;
    wire strmanip_complete;
    StringManipulator #(STRMANIP_MAXSIZE) strmanip(clock, nrst, strmanip_request, strmanip_str1_in, strmanip_str2_in, strmanip_str_out, strmanip_num_in, strmanip_num_out, strmanip_complete);
    
    localparam STATE_TITLE = 4'd0;
    localparam STATE_ETH_INIT = 4'd1;
    localparam STATE_ETH_WAIT = 4'd2;
    localparam STATE_IDLE = 4'd3;
    localparam STATE_UART_REQUEST = 4'd4;
    localparam STATE_UART_WAIT = 4'd5;
    localparam STATE_STRMANIP_WAIT = 4'd6;
    integer delay;
    (* noprune *) reg [31:0] eth_cmdcfg;
    reg eth_frame_displayed;
    reg [31:0] eth_readdata_cache;
    reg [4:0] i, j;
    (* preserve *) reg [3:0] state;
    (* preserve *) reg [3:0] next_state;
    always @(posedge clock, negedge nrst) begin
        if (!nrst) begin
            uart_bytearray_tx_request <= 0;
            
            eth_address <= 0;
            eth_ff_tx_eop <= 0;
            eth_ff_tx_err <= 0;
            eth_ff_tx_mod <= 0;
            eth_ff_tx_sop <= 0;
            eth_ff_tx_wren <= 0;
            eth_read <= 0;
            eth_write <= 0;
            eth_writedata <= 0;
            
            strmanip_request <= 0;
            
            delay <= 0;
            eth_frame_displayed <= 0;
            i <= 0;
            j <= 0;
            state <= STATE_TITLE;
        end
        else if (delay != 0)
            delay <= delay - 1;
        else begin
            if (eth_frame_displayed && !eth_frame_recved)
                eth_frame_displayed <= 0;
                
            case (state)
                STATE_TITLE: begin
                    /* 开机后首先串口打印标题 */
                    uart_bytearray_tx_data <= "EP4CE10F17C8 TSE";
                    uart_bytearray_tx_mode <= 1;
                    uart_bytearray_tx_size <= UART_MAXSIZE;
                    uart_bytearray_tx_endl <= 1;
                    state <= STATE_UART_REQUEST;
                    next_state <= STATE_ETH_INIT;
                end
                STATE_ETH_INIT: begin
                    /* 初始化三速以太网 */
                    if (eth_waitrequest) begin
                        case (i)
                            0, 9: begin
                                // 读0x02寄存器(Command_Config), 保存到eth_cmdcfg中
                                eth_address <= 8'h02;
                                eth_read <= 1;
                                i <= i + 1'b1;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            1, 3, 6, 10, 12, 14: begin
                                // 串口打印寄存器的内容
                                case (j)
                                    0: begin
                                        uart_bytearray_tx_data <= "Reg 0x";
                                        uart_bytearray_tx_mode <= 1;
                                        uart_bytearray_tx_size <= UART_MAXSIZE;
                                        uart_bytearray_tx_endl <= 0;
                                    end
                                    1: begin
                                        uart_bytearray_tx_data <= eth_address;
                                        uart_bytearray_tx_mode <= 2;
                                        uart_bytearray_tx_size <= 1;
                                        uart_bytearray_tx_endl <= 0;
                                    end
                                    2: begin
                                        uart_bytearray_tx_data <= ": 0x";
                                        uart_bytearray_tx_mode <= 1;
                                        uart_bytearray_tx_size <= UART_MAXSIZE;
                                        uart_bytearray_tx_endl <= 0;
                                    end
                                    3: begin
                                        uart_bytearray_tx_data <= eth_readdata_cache;
                                        uart_bytearray_tx_mode <= 2;
                                        uart_bytearray_tx_size <= 4;
                                        uart_bytearray_tx_endl <= 1;
                                    end
                                endcase
                                
                                if (j == 4) begin
                                    i <= i + 1'b1;
                                    j <= 0;
                                end
                                else begin
                                    j <= j + 1'b1;
                                    state <= STATE_UART_REQUEST;
                                    next_state <= STATE_ETH_INIT;
                                end
                            end
                            2: begin
                                // 外部PHY芯片RTL8201CP接的地址是1
                                // 读0xa1寄存器: 即1号PHY芯片的1号寄存器
                                eth_address <= 8'ha1;
                                eth_read <= 1;
                                i <= 3;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            4: begin
                                // 检查连接状态
                                if (eth_readdata_cache[2]) begin
                                    // 插了网线
                                    if (eth_readdata_cache[5]) begin
                                        // 自动协商已完成
                                        uart_bytearray_tx_data <= "Link is up!";
                                        uart_bytearray_tx_mode <= 1;
                                        uart_bytearray_tx_size <= UART_MAXSIZE;
                                        uart_bytearray_tx_endl <= 1;
                                        state <= STATE_UART_REQUEST;
                                        i <= 5;
                                        next_state <= STATE_ETH_INIT;
                                    end
                                    else
                                        i <= 2; // 自动协商未完成
                                end
                                else begin
                                    // 没有插网线
                                    i <= 2;
                                    delay <= 12500000;
                                end
                            end
                            5: begin
                                // 读0xa0寄存器: 即1号PHY芯片的0号寄存器
                                eth_address <= 8'ha0;
                                eth_read <= 1;
                                i <= 6;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            7: begin
                                // 串口打印速率和双工模式
                                case (j)
                                    0: begin
                                        if (eth_readdata_cache[13]) begin
                                            uart_bytearray_tx_data <= "Speed: 100Mbps";
                                            eth_cmdcfg[25] = 0; // ENA_10=0
                                        end
                                        else begin
                                            uart_bytearray_tx_data <= "Speed: 10Mbps";
                                            eth_cmdcfg[25] = 1; // ENA_10=1
                                        end
                                        j <= 1;
                                    end
                                    1: begin
                                        if (eth_readdata_cache[8]) begin
                                            uart_bytearray_tx_data <= "Duplex: full";
                                            eth_cmdcfg[10] = 0; // HD_ENA=0
                                        end
                                        else begin
                                            uart_bytearray_tx_data <= "Duplex: half";
                                            eth_cmdcfg[10] = 1; // HD_ENA=1
                                        end
                                        i <= 8;
                                        j <= 0;
                                    end
                                endcase
                                
                                uart_bytearray_tx_mode <= 1;
                                uart_bytearray_tx_size <= UART_MAXSIZE;
                                uart_bytearray_tx_endl <= 1;
                                state <= STATE_UART_REQUEST;
                                next_state <= STATE_ETH_INIT;
                            end
                            8: begin
                                // 写0x02寄存器配置速率和双工模式, 并打开收发
                                eth_address <= 8'h02;
                                eth_write <= 1;
                                eth_writedata = eth_cmdcfg | 2'b11; // TX_ENA=RX_ENA=1
                                i <= 9;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            11: begin
                                // 读0xa3寄存器: 即1号PHY芯片的3号寄存器
                                eth_address <= 8'ha3;
                                eth_read <= 1;
                                i <= 12;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            13: begin
                                // 初始化结束
                                i <= 0;
                                state <= STATE_IDLE;
                            end
                            15: begin
                                // 关闭收发
                                eth_address <= 8'h02;
                                eth_write <= 1;
                                eth_writedata = eth_cmdcfg & 'hfffffffc; // TX_ENA=RX_ENA=0
                                i <= 16;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_ETH_INIT;
                            end
                            16:
                                i <= 0; // 重新开始初始化
                        endcase
                    end
                end
                STATE_ETH_WAIT: begin
                    /* 等待寄存器读写完毕 */
                    if (!eth_waitrequest) begin
                        // 寄存器读写完毕
                        if (eth_read) begin
                            // eth_read清零后, eth_readdata的值会消失, 所以必须要用另一个变量保存起来
                            eth_readdata_cache <= eth_readdata;
                            eth_read <= 0; // 关闭读请求
                            
                            // 保存0x02寄存器的内容
                            if (eth_address == 8'h02)
                                eth_cmdcfg <= eth_readdata;
                        end
                        eth_write <= 0; // 关闭写请求
                        state <= next_state;
                    end
                end
                STATE_IDLE: begin
                    case (i)
                        0: begin
                            if (!eth_frame_displayed && eth_frame_recved) begin
                                // 收到数据包, 打印提示信息
                                eth_frame_displayed <= 1;
                                uart_bytearray_tx_data <= "[Recv] len=";
                                uart_bytearray_tx_mode <= 1;
                                uart_bytearray_tx_size <= UART_MAXSIZE;
                                uart_bytearray_tx_endl <= 0;
                                state <= STATE_UART_REQUEST;
                                i <= 1;
                                next_state <= STATE_IDLE;
                            end
                            else begin
                                // 检查连接是否断开
                                eth_address <= 8'ha1;
                                eth_read <= 1;
                                i <= 5;
                                state <= STATE_ETH_WAIT;
                                next_state <= STATE_IDLE;
                            end
                        end
                        1: begin
                            // eth_frame_len转字符串
                            if (strmanip_complete) begin
                                if (strmanip_request == 0) begin
                                    strmanip_request <= `STRMANIP_ITOA;
                                    strmanip_num_in <= eth_frame_len;
                                end
                            end
                            else begin
                                i <= 2;
                                state <= STATE_STRMANIP_WAIT;
                                next_state <= STATE_IDLE;
                            end
                        end
                        2: begin
                            // 打印数据包长度
                            uart_bytearray_tx_data <= strmanip_str_out;
                            uart_bytearray_tx_mode <= 1;
                            uart_bytearray_tx_size <= UART_MAXSIZE;
                            state <= STATE_UART_REQUEST;
                            next_state <= STATE_IDLE;
                            
                            if (eth_frame_err == 0) begin
                                // 无错误
                                i <= 0;
                                uart_bytearray_tx_endl <= 1;
                            end
                            else begin
                                // 有错误, 打印错误值
                                i <= 3;
                                uart_bytearray_tx_endl <= 0;
                            end
                        end
                        3: begin
                            uart_bytearray_tx_data <= ", err=0x";
                            uart_bytearray_tx_mode <= 1;
                            uart_bytearray_tx_size <= UART_MAXSIZE;
                            uart_bytearray_tx_endl <= 0;
                            state <= STATE_UART_REQUEST;
                            i <= 4;
                            next_state <= STATE_IDLE;
                        end
                        4: begin
                            // 打印错误值
                            uart_bytearray_tx_data <= eth_frame_err;
                            uart_bytearray_tx_mode <= 2;
                            uart_bytearray_tx_size <= 1;
                            uart_bytearray_tx_endl <= 1;
                            state <= STATE_UART_REQUEST;
                            i <= 0;
                            next_state <= STATE_IDLE;
                        end
                        5: begin
                            if (!eth_readdata_cache[2]) begin
                                // 连接已断开, 回到初始化模式
                                uart_bytearray_tx_data <= "Link is down!";
                                uart_bytearray_tx_mode <= 1;
                                uart_bytearray_tx_size <= UART_MAXSIZE;
                                uart_bytearray_tx_endl <= 1;
                                state <= STATE_UART_REQUEST;
                                i <= 14;
                                next_state <= STATE_ETH_INIT;
                            end
                            else
                                i <= 0;
                        end
                    endcase
                end
                STATE_UART_REQUEST: begin
                    /* 开始发送串口数据 */
                    if (uart_bytearray_tx_ready) begin
                        if (!uart_bytearray_tx_request)
                            uart_bytearray_tx_request <= 1;
                    end
                    else
                        state <= STATE_UART_WAIT;
                end
                STATE_UART_WAIT: begin
                    /* 等待串口数据发送完毕 */
                    if (uart_bytearray_tx_ready && uart_bytearray_tx_request)
                        uart_bytearray_tx_request <= 0;
                    else if (uart_bytearray_sent)
                        state <= next_state;
                end
                STATE_STRMANIP_WAIT: begin
                    /* 等待字符串操作完毕 */
                    if (strmanip_complete) begin
                        strmanip_request <= 0;
                        state <= next_state;
                    end
                end
            endcase
        end
    end
  
    (* noprune *) reg [10:0] rx_cnt;
    always @(posedge eth_rx_clk_g) begin
        if (!eth_rx_dv)
            rx_cnt <= 0;
        else
            rx_cnt <= rx_cnt + 1'b1;
    end
    
    (* noprune *) reg [15:0] sop_cnt;
    always @(posedge eth_ff_rx_sop, negedge nrst) begin
        if (!nrst)
            sop_cnt <= 0;
        else
            sop_cnt <= sop_cnt + 1'b1;
    end
    
    (* noprune *) reg [15:0] eop_cnt;
    always @(posedge eth_ff_rx_eop, negedge nrst) begin
        if (!nrst)
            eop_cnt <= 0;
        else
            eop_cnt <= eop_cnt + 1'b1;
    end
    
    /* 防止wire信号被优化掉, 无法查看 */
    (* noprune *) reg [58:0] debug;
    always @(posedge clock) begin
        debug[0] <= eth_ff_rx_dsav;
        debug[32:1] <= eth_ff_rx_data;
        debug[36:33] <= eth_rx_frm_type;
        debug[37] <= eth_ff_rx_a_empty;
        debug[38] <= eth_ff_rx_a_full;
        debug[56:39] <= eth_rx_err_stat;
        debug[57] <= eth_ena_10;
        debug[58] <= eth_mode;
    end
    
endmodule

【程序运行结果】

EP4CE10F17C8 TSE
Reg 0x02: 0x01000000
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x0000786d
Link is up!
Reg 0xa0: 0x00003100
Speed: 100Mbps
Duplex: full
Reg 0x02: 0x01000003
Reg 0xa3: 0x00008201
Link is down!
Reg 0xa1: 0x00007849
Reg 0x02: 0x01000000
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x0000786d
Link is up!
Reg 0xa0: 0x00003100
Speed: 100Mbps
Duplex: full
Reg 0x02: 0x01000003
Reg 0xa3: 0x00008201
Link is down!
Reg 0xa1: 0x00007849
Reg 0x02: 0x01000000
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x00007849
Reg 0xa1: 0x0000786d
Link is up!
Reg 0xa0: 0x00001100
Speed: 10Mbps
Duplex: full
Reg 0x02: 0x03000003
Reg 0xa3: 0x00008201
[Recv] len=60
[Recv] len=60
[Recv] len=60
[Recv] len=208
[Recv] len=60
[Recv] len=60
[Recv] len=208
[Recv] len=60
[Recv] len=60
[Recv] len=60
[Recv] len=208
[Recv] len=60
[Recv] len=60
[Recv] len=208
[Recv] len=60
[Recv] len=60
[Recv] len=208
[Recv] len=60

【调试时遇到的问题】
笔者在编写三速以太网nios lwip例程时,遇到了以下几个问题。
问题一:为什么一上电,三速以太网IP核的eth_waitrequest信号就一直为高?那怎么读写IP核的寄存器?
正常。Avalon Memory-mapped协议规定,上电时waitrequest信号必须一直为高电平。读写寄存器前,首先判断eth_waitrequest是否为高,为高才使能eth_read或eth_write信号。然后等待eth_waitrequest出现1个时钟周期的低电平脉冲,表示读写寄存器完毕。这个低电平脉冲只有一个时钟周期,之后马上又会变成高电平。读寄存器时,应该在这个低电平脉冲处马上读取eth_readdata(寄存器值),低电平脉冲一消失,eth_readdata上的数据就没了。

问题二:如图所示,为什么eth_ff_rx_dsav和eth_ff_rx_dval一直为高,且eth_ff_rx_data一直输出一大堆垃圾数据,无法停止?在NIOS里面一使用DMA接收,马上缓冲区就溢出了!

回答:这是因为,三速以太网IP核的速率和双工配置与PHY芯片自动协商出来的不匹配,eth_rx_clk输入信号没有加缓冲(注意“且”字,这两个条件必须要同时成立),导致IP核的FIFO出现故障。
当SGDMA的length_or_eop=0时,只要不遇到endofpacket信号,DMA就会一直接收并填充接收缓冲区,如果缓冲区不够大,就会导致缓冲区溢出,从而破坏C语言堆栈或篡改其他变量。
而且,RTL8201 PHY芯片在运行中,速率和双工配置会有突变的可能,但在突变前一定会有连接断开事件(link is down)发生。在上面程序运行结果里面就可以看到,网线没有动,但速率就突变了一次,每次突变前都会先断开一下连接,然后马上又连上。实测时发现有的时候duplex也会从full突变成half,然后又变回来。
但是不用担心,程序里面的eth_rx_clk信号是用ALTCLKCTRL IP核缓冲过的,切断了上述两个条件的其中一个条件,不会导致FIFO故障,使DMA接收缓冲区溢出。
实测发现eth_rx_clk缓冲了的情况下,即使PHY芯片用的是10Mbps速率,IP核配置的是100Mbps速率,仍能正确接收数据包,获取数据包的大小,不会出现CRC错误。(但是数据包能否发送成功就不知道了,没测过)
提示:eth_tx_clk和eth_rx_clk对FPGA来说都是时钟输入信号。如果这两个信号接的是时钟专用引脚(比如CLK1、CLK2等等),那就不用ALTCLKCTRL IP核缓冲。小梅哥的AC620板子上,这两个信号都是接的普通I/O口,所以必须要用ALTCLKCTRL IP核缓冲。

问题三:为什么SGDMA发不出来数据?用SignalTap抓取信号,发现输出信号一直为低电平,startofpacket和endofpacket信号也没有产生,且alt_avalon_sgdma_do_sync_transfer函数的返回值为0x08不是0x0c。
问题四:SGDMA描述符和接收缓冲区都位于同一块SDRAM里面。为什么SGDMA接收数据明明接收成功了,但是数据内容全零?或者部分接收正确,其余部分为零,丢字节非常厉害!是不是因为SDRAM要时不时刷新一下,SGDMA反应不过来造成数据丢失?
甚至在接收之前,用memset将接收缓冲区全部填充成0xcd,接收完成后,接收缓冲区的内容还是0xcd!

回答:见【程序】Altera FPGA NIOS实现Scatter-Gather DMA(SGDMA)收发回环测试,描述符和缓冲区全部放在同一块SDRAM里面_ZLK1214的专栏-CSDN博客

问题五:如图所示,为什么以太网接收的数据全部都是CRC错误(0x85)?

回答:这是因为eth_rx_clk没有使用寄存器缓冲,或者用ALTCLKCTRL IP核缓冲。
FPGA外部信号必须要经过寄存器缓冲后才能使用,尤其是在if条件判断里面。否则就有出错的风险。比如明明仿真时都好好的,但实际运行时,寄存器(变量)的值乱跳

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值