原程序是运行在小梅哥AC620开发板上的:基于小梅哥AC620开发板的NIOS II LWIP百兆以太网例程_ZLK1214的专栏-CSDN博客_小梅哥ac620【开发板】开发板型号:小梅哥AC620FPGA型号:EP4CE10F17C8N晶振频率:50MHzPHY芯片型号:RTL8201CP(MII接口,百兆以太网PHY芯片)【程序功能展示】1. ping通开发板的NetBIOS设备名,IPv4地址和IPv6地址2. 访问开发板上的http服务器(设备名方式、IPv6方式):3. 在路由器管理页面看到开发板的信息:【主要程序代码】main.v:module main( input clock,https://blog.csdn.net/ZLK1214/article/details/115337628鉴于小梅哥AC620 RTL8201CP百兆网口版本的板子已经买不到了(现在只有RTL8211千兆版本了),笔者根据小梅哥板子的电路,自己新做了一块板子,如下图所示。
【新电路板与小梅哥AC620电路的不同之处】 (1)FPGA芯片由EP4CE10F17C8(BGA封装)换成了EP4CE10E22C8(LQFP封装)。 (2)SDRAM由W9812G6KH(16MB)换成了W9825G6KH(32MB),多了A12这根地址线。
(3)网口的灯改成了绿灯表示连接状态,黄灯表示数据包收发状态,灯的限流电阻大小改成了330Ω。 (4)数码管添加了OE使能引脚,用一个上拉电阻拉高,默认是关闭状态。这样做的目的是防止刚通电时数码管乱显示。 只有当RCLK出现上升沿后(正确的数据存入了芯片),才将OE拉低,开显示。
【程序改动】 修改Verilog程序: (1)main模块的sdram_addr位宽改为[12:0]。 (2)修改Pin Planner里面的引脚定义,电平全部改为3.3V。 (3)altpll_0改成输出altpll_c0(100MHz 0deg)和altpll_c1(100MHz -63deg)两路时钟。 ???? main模块中assign sdram_clk = altpll_c0改为altpll_c1。 (4)QSYS里面再添加一个clock source,频率设为100MHz。 ???? SDRAM的row width改为13,refresh command间隔改为7.8125us(即64ms/8192)。 ???? SDRAM IP核的时钟和复位引脚修改为连接100MHz的那个新clock source。 ???? main模块的nios设置clk_0_clk为clock,clk_1_clk为altpll_c0,reset_0_reset_n和reset_1_reset_n为altpll_locked。 (5)eth_tx_clk和eth_rx_clk接的是时钟专用引脚,所以main模块中去除altclkctrl_0和altclkctrl_1时钟缓冲。 ???? eth_tse_0_pcs_mac_rx_clock_clk括号里改成eth_rx_clk,eth_tse_0_pcs_mac_tx_clock_clk括号里改成eth_tx_clk。
修改C程序: (1)main函数里面的netbiosns_set_name设备名 (2)ethernetif_init函数里面的netif->hostname设备名 (3)添加了串口非阻塞接收功能,串口收到t会显示sys_now()的值
【遇到的问题记录】 (1)SDRAM某些地址要读5次才能读正确。 ???? 解决办法:SDRAM时钟sdram_clk由50MHz -63deg改成100MHz -63deg。NIOS IP核和SDRAM IP核的时钟都改为100MHz 0deg。 (2)插上网线后,两个网口灯不亮。但RTL8201CP复位瞬间,不管插不插网线,两个灯都要亮一下,然后熄灭。 ???? 原因:原理图画错。CRS和RX_ER接的应该是下拉电阻,原理图画错了画成了上拉电阻。 ?????????? RX_ER接上拉电阻,选择的是光纤模式,而非网线模式,所以网口灯不亮。 ?????????? 解决办法:板子上飞线,把RX_ER电阻的另一端由3V3改成GND。CRS不用管。 ???? 提示:虽然RTL8201芯片内RX_ER自带有弱下拉,但RX_ER和FPGA的I/O口相连,FPGA在未配置状态下所有I/O口都是带上拉电阻的,这又会导致RX_ER被FPGA上拉,因此RX_ER必须外接下拉电阻。 (3)运行C程序后,串口打印Hello fr后,nios就死机。 ???? 原因:UART和SGDMA都不能在100MHz频率下正常工作。 ???? 解决办法:QSYS里面,把UART和SGDMA的时钟和复位改成50MHz的那个clock source。
【调通后的程序截图】
程序和PCB文件下载链接:https://pan.baidu.com/s/18lYFoZGcAetSqgeIS1_KNg(提取码:pimw)
【Qsys框图】
SDRAM IP核时钟为100MHz,其余IP核时钟为50MHz。
SDRAM参数配置:
除了刷新间隔为7.8125us(=64ms/8912),其他的时序参数都是默认值。
【程序代码】
main.v:
module main(
input clock,
input uart_rx,
output uart_tx,
output sdram_clk,
output [12:0] sdram_addr,
output [1:0] sdram_ba,
output sdram_cas_n,
output sdram_cke,
output sdram_cs_n,
inout [15:0] sdram_dq,
output [1:0] sdram_dqm,
output sdram_ras_n,
output sdram_we_n,
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,
output epcs_dclk,
output epcs_sce,
output epcs_sdo,
input epcs_data0
);
/* 产生复位信号 */
wire nrst;
Reset reset(clock, nrst);
/* PLL倍频 */
wire altpll_c0;
wire altpll_c1;
wire altpll_locked;
altpll_0 altpll_0(
.areset(~nrst),
.inclk0(clock), // 50MHz
.c0(altpll_c0), // 100MHz, 0deg
.c1(altpll_c1), // 100MHz, -63deg
.locked(altpll_locked)
);
/* NIOS II */
wire eth_tse_0_mac_mdio_mdio_oen;
wire eth_tse_0_mac_mdio_mdio_out;
wire eth_tse_0_mac_mii_mii_tx_err;
wire eth_tse_0_mac_misc_ff_rx_a_empty;
wire eth_tse_0_mac_misc_ff_rx_a_full;
wire eth_tse_0_mac_misc_ff_rx_dsav;
wire eth_tse_0_mac_misc_ff_tx_a_empty;
wire eth_tse_0_mac_misc_ff_tx_a_full;
wire eth_tse_0_mac_misc_ff_tx_septy;
wire eth_tse_0_mac_misc_magic_wakeup;
wire [17:0] eth_tse_0_mac_misc_rx_err_stat;
wire [3:0] eth_tse_0_mac_misc_rx_frm_type;
wire eth_tse_0_mac_misc_tx_ff_uflow;
wire eth_tse_0_mac_status_ena_10;
wire eth_tse_0_mac_status_eth_mode;
assign sdram_clk = altpll_c1;
assign eth_mdio = (!eth_tse_0_mac_mdio_mdio_oen) ? eth_tse_0_mac_mdio_mdio_out : 1'bz;
assign eth_rst_n = altpll_locked;
nios nios(
.clk_0_clk(clock),
.reset_0_reset_n(altpll_locked),
.clk_1_clk(altpll_c0),
.reset_1_reset_n(altpll_locked),
.eth_tse_0_mac_mdio_mdc(eth_mdc),
.eth_tse_0_mac_mdio_mdio_in(eth_mdio),
.eth_tse_0_mac_mdio_mdio_oen(eth_tse_0_mac_mdio_mdio_oen),
.eth_tse_0_mac_mdio_mdio_out(eth_tse_0_mac_mdio_mdio_out),
.eth_tse_0_mac_mii_mii_rx_d(eth_rxd),
.eth_tse_0_mac_mii_mii_rx_dv(eth_rx_dv),
.eth_tse_0_mac_mii_mii_rx_err(eth_rx_er),
.eth_tse_0_mac_mii_mii_tx_d(eth_txd),
.eth_tse_0_mac_mii_mii_tx_en(eth_tx_en),
.eth_tse_0_mac_mii_mii_tx_err(eth_tse_0_mac_mii_mii_tx_err),
.eth_tse_0_mac_misc_ff_rx_a_empty(eth_tse_0_mac_misc_ff_rx_a_empty),
.eth_tse_0_mac_misc_ff_rx_a_full(eth_tse_0_mac_misc_ff_rx_a_full),
.eth_tse_0_mac_misc_ff_rx_dsav(eth_tse_0_mac_misc_ff_rx_dsav),
.eth_tse_0_mac_misc_ff_tx_a_empty(eth_tse_0_mac_misc_ff_tx_a_empty),
.eth_tse_0_mac_misc_ff_tx_a_full(eth_tse_0_mac_misc_ff_tx_a_full),
.eth_tse_0_mac_misc_ff_tx_crc_fwd(1'b0),
.eth_tse_0_mac_misc_ff_tx_septy(eth_tse_0_mac_misc_ff_tx_septy),
.eth_tse_0_mac_misc_magic_sleep_n(1'b1),
.eth_tse_0_mac_misc_magic_wakeup(eth_tse_0_mac_misc_magic_wakeup),
.eth_tse_0_mac_misc_rx_err_stat(eth_tse_0_mac_misc_rx_err_stat),
.eth_tse_0_mac_misc_rx_frm_type(eth_tse_0_mac_misc_rx_frm_type),
.eth_tse_0_mac_misc_tx_ff_uflow(eth_tse_0_mac_misc_tx_ff_uflow),
.eth_tse_0_mac_misc_xoff_gen(1'b0),
.eth_tse_0_mac_misc_xon_gen(1'b0),
.eth_tse_0_mac_status_ena_10(eth_tse_0_mac_status_ena_10),
.eth_tse_0_mac_status_eth_mode(eth_tse_0_mac_status_eth_mode),
.eth_tse_0_mac_status_set_10(1'b0),
.eth_tse_0_mac_status_set_1000(1'b0),
.eth_tse_0_pcs_mac_rx_clock_clk(eth_rx_clk),
.eth_tse_0_pcs_mac_tx_clock_clk(eth_tx_clk),
.sdram_0_addr(sdram_addr),
.sdram_0_ba(sdram_ba),
.sdram_0_cas_n(sdram_cas_n),
.sdram_0_cke(sdram_cke),
.sdram_0_cs_n(sdram_cs_n),
.sdram_0_dq(sdram_dq),
.sdram_0_dqm(sdram_dqm),
.sdram_0_ras_n(sdram_ras_n),
.sdram_0_we_n(sdram_we_n),
.uart_0_rxd(uart_rx),
.uart_0_txd(uart_tx),
.epcs_flash_0_dclk(epcs_dclk),
.epcs_flash_0_sce(epcs_sce),
.epcs_flash_0_sdo(epcs_sdo),
.epcs_flash_0_data0(epcs_data0)
);
endmodule
hello_world.c:
/*
* "Hello World" example.
*
* This example prints 'Hello from Nios II' to the STDOUT stream. It runs on
* the Nios II 'standard', 'full_featured', 'fast', and 'low_cost' example
* designs. It runs with or without the MicroC/OS-II RTOS and requires a STDOUT
* device in your system's hardware.
* The memory footprint of this hosted application is ~69 kbytes by default
* using the standard reference design.
*
* For a reduced footprint version of this template, and an explanation of how
* to reduce the memory footprint for a given application, see the
* "small_hello_world" template.
*
*/
#include <fcntl.h>
#include <lwip/apps/httpd.h>
#include <lwip/apps/netbiosns.h>
#include <lwip/dhcp.h>
#include <lwip/dns.h>
#include <lwip/init.h>
#include <lwip/netif.h>
#include <lwip/timeouts.h>
#include <netif/ethernetif.h>
#include <stdio.h>
#include <sys/alt_alarm.h>
#include <system.h>
#include <unistd.h>
static struct netif netif_rtl8201cp;
alt_u32 sys_now(void)
{
// BSP Editor里面Settings -> Common -> hal -> sys_clk_timer必须选择一个定时器
// 而且该定时器的计时间隔必须为1ms
LWIP_ASSERT("incorrect tick rate", alt_ticks_per_second() == 1000);
return alt_nticks();
}
static void display_ip(void)
{
const ip_addr_t *addr;
static uint8_t ip_displayed = 0;
static uint8_t ip6_displayed = 0;
int i, ip_present;
int dns = 0;
if (netif_dhcp_data(&netif_rtl8201cp) == NULL)
ip_present = 1; // 使用静态IP地址
else if (dhcp_supplied_address(&netif_rtl8201cp))
ip_present = 2; // 使用DHCP获得IP地址, 且已成功获取到IP地址
else
ip_present = 0; // 使用DHCP获得IP地址, 且还没有获取到IP地址
// 显示IPv4地址
if (ip_present)
{
if (ip_displayed == 0)
{
ip_displayed = 1;
if (ip_present == 2)
printf("DHCP supplied address!\r\n");
printf("IP address: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.ip_addr));
printf("Subnet mask: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.netmask));
printf("Default gateway: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.gw));
dns = 1;
}
}
else
ip_displayed = 0;
// 显示IPv6地址
for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) // 0号地址是本地链路地址, 不需要显示
{
if (ip6_addr_isvalid(netif_ip6_addr_state(&netif_rtl8201cp, i)))
{
if ((ip6_displayed & _BV(i)) == 0)
{
ip6_displayed |= _BV(i);
printf("IPv6 address %d: %s\r\n", i, ipaddr_ntoa(netif_ip_addr6(&netif_rtl8201cp, i)));
dns = 1;
}
}
else
ip6_displayed &= ~_BV(i);
}
// 显示DNS服务器地址
// 在lwip中, IPv4 DHCP和IPv6 SLAAC获取到的DNS地址会互相覆盖
if (dns)
{
addr = dns_getserver(0);
if (ip_addr_isany(addr))
return;
printf("DNS Server: %s", ipaddr_ntoa(addr));
addr = dns_getserver(1);
if (!ip_addr_isany(addr))
printf(" %s", ipaddr_ntoa(addr));
printf("\r\n");
}
}
static void net_config(int use_dhcp)
{
ip4_addr_t ipaddr, netmask, gw;
if (use_dhcp)
netif_add_noaddr(&netif_rtl8201cp, NULL, ethernetif_init, netif_input);
else
{
IP4_ADDR(&ipaddr, 192, 168, 0, 19);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 0, 1);
netif_add(&netif_rtl8201cp, &ipaddr, &netmask, &gw, NULL, ethernetif_init, netif_input);
}
netif_set_default(&netif_rtl8201cp);
netif_set_up(&netif_rtl8201cp);
if (use_dhcp)
dhcp_start(&netif_rtl8201cp);
netif_create_ip6_linklocal_address(&netif_rtl8201cp, 1);
printf("IPv6 link-local address: %s\r\n", ipaddr_ntoa(netif_ip_addr6(&netif_rtl8201cp, 0)));
netif_set_ip6_autoconfig_enabled(&netif_rtl8201cp, 1);
}
int main(void)
{
char ch;
int ret;
printf("Hello from Nios II!\r\n");
lwip_init();
net_config(1);
httpd_init();
netbiosns_init();
netbiosns_set_name("EP4CE10E22C8");
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // 串口设为非阻塞接收模式
while (1)
{
ethernetif_check_link(&netif_rtl8201cp);
display_ip();
ethernetif_input(&netif_rtl8201cp);
sys_check_timeouts();
ret = read(STDIN_FILENO, &ch, 1);
if (ret != -1)
{
// 收到串口字符
switch (ch)
{
case 't':
printf("sys_now()=%lu\r\n", sys_now());
break;
}
}
}
}
|