一、摘要
Altera軟件NIOS II高版本(7.2版本以上,本例程中使用的是9.0版本)中實現TCP/IP所用的協議棧為NicheStack,常用的例程有2個,web_server和simple_socket_server,這篇文章只敘述simple_socket_server例程實現的過程。這里DM9000A的驅動和上篇博文中基於LWIP的驅動不同。
二、實驗平台
軟件平台:Quartus II 9.0 + Nios II 9.0
硬件平台:DIY_DE2
三、實驗內容——>實現simple_socket_server
1、采用SOPC定制軟核

定制軟核的詳細步驟不再贅述,以上為定制的軟核。
cpu_0需要設置的地方:
Reset Vector:cfi_flash_0、
Exception Vector:sram_16bit_512k_0
必須要添加sys_timer_0,供uC/OS系統所使用
第二個標簽頁:Data Master處,Data Cache設置為None
之后分配地址,分配中斷號,生成即可。
2、硬件電路
采用原理圖的形式,創建頂層文件。
(1)添加生成的軟核;
(2)調用鎖相環IP核;
(3)連線、分配管腳;
(4)編譯、綜合,生成配置文件。
最后原理圖如下圖所示。

需要注意的問題:
(1)軟核程序在SDRAM里面運行,為了使軟核的速度提升,因此SDRAM的頻率和cpu的頻率都設置為100M。cpu時鍾clk_100和sdram操作時鍾clk_50都接PLL的c0,100M,無相位偏移;SDRAM的時鍾管腳SDRAM_CLK連接PLL的c1,100M,偏移-3ns。
(2)DM9000A的時鍾管腳接50M,直接連接晶振的輸入端即可。
(3)復位管腳接高電平VCC即可。
(4)CFI_FLASH的復位管腳FLASH_RESET接高電平VCC即可。
3、軟件方面
(1)打開NIOS II,新建工程,調用simple_socket_server工程模板。
(2)添加DM9000A驅動:dm9000a.h和dm9000a.c,將上述兩個文件復制到上步建立的工程文件夾下。
(3)打開network_utilities.c文件,將附錄代碼覆蓋原始代碼,這里采用的是使用靜態IP的方法(IP的值將在后面給出說明),並且賦給MAC值。
(4)打開iniche_init.c文件,
添加頭文件#include"dm9000a.h",
添加DM9000A接口語句DM9000A_INSTANCE(DM9000A_0, dm9000a_0);
在函數void SSSInitialTask(void *task_data)中,
添加DM9000A的初始化語句DM9000A_INIT(DM9000A_0, dm9000a_0);
(5)編譯、下載、運行,之前要先將.sof的配置文件下載到FPGA內。在DOS下輸入ping命令:ping 192.168.2.1,如下圖所示,則可以正常ping通。

再輸入telnet命令:telnet 192.168.2.1,則得到如下圖所示:

在PC鍵盤輸入0-7數字,則DIY_DE2上的8個LED就會相應的亮或者滅。至此,說明,telnet正常。
4、工程文件解讀
(1)alt_error_handler.h、alt_error_handler.c:錯誤類型句柄文件;
(2)dm9000a_regs.h、dm9000a.h、dm9000a.c:DM9000A的驅動;
(3)network_utilities.h、network_utilities.c:設置IP,設置MAC;
(4)simple_socket_server.h、simple_socket_server.c:工程的主體程序,包括任務調度優先級、缺省IP設置、套接字、各種任務調度等等工作;
(5)led.c:LED、七段數碼管顯示程序;
(6)iniche_init.c:程序主函數。
四、實驗結果分析
NIOS II運行結果:

稍過2分鍾后,得到如下結果:

實驗現象,程序一開始運行,先賦給靜態IP,這時候ping能夠跑通,但telnet卻不能跑通。稍過2分鍾之后(這個默認時間在LWIP協議棧實現的時候可以調整,但在NicheStack協議棧中不能調整,至少在工程文件里是這樣),出現上面第二幅結果圖的時候,能夠ping正常,telnet正常。
分析可得,雖賦予靜態IP,但是系統仍是先通過DHCP獲取IP,獲取超時,使用缺省IP,缺省IP的設置在simple_socket_server.h中。而真正能夠ping正常,telnet正常的卻是事先賦予的靜態IP。
注:取消DHCP的方法同上一篇博文。
五、實驗的幾點說明
1、IP值設置:
因為是采用局域網通信,所以要將PC和DIY_DE2的IP的前3位設置為相同,最后一位不同。
2、MAC值設置:
直接采用程序設定即可,或者是將MAC值存儲在FLASH中,上電讀取即可。本例采用的是前一種方法。
3、端口設定:
telnet的時候,需要偵聽端口,當偵聽的端口號和DIY_DE2中設定的相同的時候,才能正常通信。方法:telnet 192.168.2.1時,會有一個專用的端口23,將DIY_DE2中設定的端口號改為23即可(在文件simple_socket_server.h中#define SSS_PORT 23)。
4、關於這個例程在NIOS II方面:
關於Software Components這個按鈕下Lightweight TCP/IP Stack下選項為灰色的原因,其實這個不必理他。這一點也得到了友晶科技的證實。如果用LAN91c111這個網卡,上述位置的選項則可以正常使用,這說明NIOS II軟件只認SOPC中原裝的器件。
附錄:
network_utilities.c文件
#include <alt_types.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <sys/alt_flash.h> #include "includes.h" #include "io.h" #include "simple_socket_server.h" #include <alt_iniche_dev.h> #include "ipport.h" #include "tcpport.h" #include "network_utilities.h" #define IP4_ADDR(ipaddr, a,b,c,d) ipaddr = \ htonl((((alt_u32)(a & 0xff) << 24) | ((alt_u32)(b & 0xff) << 16) | \ ((alt_u32)(c & 0xff) << 8) | (alt_u32)(d & 0xff))) /* * get_mac_addr * * Read the MAC address in a board specific way * */ static unsigned char macaddr[6] = { 0x00, 0x07, 0xed, 0xff, 0x06, 0x00 }; int get_mac_addr(NET net, unsigned char mac_addr[6]) { int rv = -1; /* first 3 bytes are altera's vendor id */ /* last 3 bytes are picked from serial number sticker */ mac_addr[0] = macaddr[0]; mac_addr[1] = macaddr[1]; mac_addr[2] = macaddr[2]; mac_addr[3] = macaddr[3]; mac_addr[4] = macaddr[4]; mac_addr[5] = macaddr[5]; /* return the mac address in the array */ rv = 0; return rv; } /* * get_ip_addr() * * This routine is called by InterNiche to obtain an IP address for the * specified network adapter. Like the MAC address, obtaining an IP address is * very system-dependant and therefore this function is exported for the * developer to control. * * In our system, we are either attempting DHCP auto-negotiation of IP address, * or we are setting our own static IP, Gateway, and Subnet Mask addresses our * self. This routine is where that happens. */ int get_ip_addr(alt_iniche_dev *p_dev, ip_addr* ipaddr, ip_addr* netmask, ip_addr* gw, int* use_dhcp) { IP4_ADDR(*ipaddr, 192, 168, 2, 1); IP4_ADDR(*gw, 192, 168, 2, 1); IP4_ADDR(*netmask, 255, 255, 255, 0); #ifdef DHCP_CLIENT *use_dhcp = 1; #else /* not DHCP_CLIENT */ *use_dhcp = 0; printf("Static IP Address is %d.%d.%d.%d\n", ip4_addr1(*ipaddr), ip4_addr2(*ipaddr), ip4_addr3(*ipaddr), ip4_addr4(*ipaddr)); #endif /* not DHCP_CLIENT */ /* Non-standard API: return 1 for success */ return 1; }
