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条件判断里面。否则就有出错的风险。比如明明仿真时都好好的,但实际运行时,寄存器(变量)的值乱跳。