Forum Discussion
Altera_Forum
Honored Contributor
16 years agoAltera TSE driver and example program for lwIP (1.3.2)
After many many requests and complaints about lack of support and/or documentation for support of lwIP for the Altera TSE, I have developed a drop-in TSE driver and example program and made this available to the NIOS II community. This was done for NIOS II 8.1 SP0.01. I don't expect difficulty with version 9.x.
This is for the latest version of lwIP (the latest is as of this post) for a minimal program and HTTP server based on the http server in the lwIP contrib folder. The lwIP TSE driver uses the altera_avalon_tse driver and SGDMA as-is. There is a complete (as in 41-step) set of instructions on creating the project and example program. More information and the link to the driver is available here: http://lwip.wikia.com/wiki/available_device_drivers#lwip_1.3.2 Please direct any questions, changes for NIOS II 9.1, or comments to this thread. 12-16-2010 update: This example works with NIOS Version 10.0 with some tweaks to the procedure to create the project. Also, a lwIP 1.4 release candidate has been out for a while and it drops into this example (in place of 1.3) without changes. Bill257 Replies
- Altera_Forum
Honored Contributor
Did You copy RGMII initialization file into the project? You can take it from RGMII web server example or RGMII simple socket server example projects. Afaik it's called tse_my_system.c. Just copy it to the project tree and recompile.
- Altera_Forum
Honored Contributor
Thank you, Socrates, that was exactly what I needed to get it running!!!
P.S. The recipe how to get lwIP 1.3.2 running didn't work. I needed to edit the makefile and add the source as well as header files manually: # Paths to C, C++, and assembly source files. C_SRCS += lwip/src/api/api_lib.c C_SRCS += lwip/src/api/api_msg.c C_SRCS += lwip/src/api/err.c C_SRCS += lwip/src/api/netbuf.c C_SRCS += lwip/src/api/netdb.c C_SRCS += lwip/src/api/netifapi.c C_SRCS += lwip/src/api/sockets.c C_SRCS += lwip/src/api/tcpip.c C_SRCS += lwip/src/core/dhcp.c C_SRCS += lwip/src/core/dns.c C_SRCS += lwip/src/core/ipv4/autoip.c C_SRCS += lwip/src/core/ipv4/icmp.c C_SRCS += lwip/src/core/ipv4/igmp.c C_SRCS += lwip/src/core/ipv4/inet_chksum.c C_SRCS += lwip/src/core/ipv4/inet.c C_SRCS += lwip/src/core/ipv4/ip_frag.c C_SRCS += lwip/src/core/ipv4/ip_addr.c C_SRCS += lwip/src/core/ipv4/ip.c C_SRCS += lwip/src/core/init.c C_SRCS += lwip/src/core/mem.c C_SRCS += lwip/src/core/memp.c C_SRCS += lwip/src/core/netif.c C_SRCS += lwip/src/core/pbuf.c C_SRCS += lwip/src/core/raw.c C_SRCS += lwip/src/core/stats.c C_SRCS += lwip/src/core/sys.c C_SRCS += lwip/src/core/tcp_in.c C_SRCS += lwip/src/core/tcp_out.c C_SRCS += lwip/src/core/snmp/asn1_dec.c C_SRCS += lwip/src/core/snmp/asn1_enc.c C_SRCS += lwip/src/core/snmp/mib_structs.c C_SRCS += lwip/src/core/snmp/mib2.c C_SRCS += lwip/src/core/snmp/msg_in.c C_SRCS += lwip/src/core/snmp/msg_out.c C_SRCS += lwip/src/core/tcp.c C_SRCS += lwip/src/core/udp.c C_SRCS += lwip/src/netif/etharp.c C_SRCS += lwip/src/netif/ppp/auth.c C_SRCS += lwip/src/netif/ppp/chpms.c C_SRCS += lwip/src/netif/ppp/fsm.c C_SRCS += lwip/src/netif/ppp/lcp.c C_SRCS += lwip/src/netif/ppp/pap.c C_SRCS += lwip/src/netif/ppp/ppp.c C_SRCS += lwip/src/netif/ppp/ppp_oe.c C_SRCS += lwip/src/netif/ppp/randm.c C_SRCS += alteraTseEthernetif.c C_SRCS += fs.c C_SRCS += fsdata.c C_SRCS += httpd.c C_SRCS += lwip_tse_mac.c C_SRCS += main.c C_SRCS += tse_my_system.c # List of application specific include directories, library directories and library names APP_INCLUDE_DIRS += ../clean_lwip_project/lwip/src/include APP_INCLUDE_DIRS += ../clean_lwip_project/lwip/src/include/ipv4 APP_INCLUDE_DIRS += ../clean_lwip_project - Altera_Forum
Honored Contributor
Maybe this would be helpful for those who need to know link status.
I've added this to lwip_tse_mac.c int tse_link_status(int iface, struct ethernetif *ethernetif) { np_tse_mac *mi_base; mi_base = tse[iface].mi.base; alt_tse_mac_info *pmac_info; tse_sgdma_read_init(&tse[iface]); pmac_info = alt_tse_get_mac_info(mi_base); alt_tse_phy_wr_mdio_addr(pmac_info->pphy_info, pmac_info->pphy_info->mdio_address); int link_alive; link_alive = alt_tse_phy_rd_mdio_reg(pmac_info->pphy_info, TSE_PHY_MDIO_STATUS, TSE_PHY_MDIO_STATUS_AN_COMPLETE, 1) != 0; return link_alive; } Or is there another way to check for link status ? - Altera_Forum
Honored Contributor
Petrak,
That will work fine unless you're doing realtime processing and using Ethernet too. Reading the PHY like this stalls the NIOS II interrupt response for a significant time period. I know this because it affected my project. When I reported this to Altera the response was "this is how it works". While the PHY shifts in serial data on this MDIO read, the NIOS II will not respond to interrupt requests. We used logic in the Cyclone to delay the task the interrupt was performing to hide this latency that the PHY causes. It was a real pain but we have to respond to an external interrupt within a few uS. Fortunately there was a workaround or our product would have been dead. The MDIO read was causing additional interrupt latency from 31 to 48uS over normal latency times. Bill A. - Altera_Forum
Honored Contributor
--- Quote Start --- Petrak, That will work fine unless you're doing realtime processing and using Ethernet too. Reading the PHY like this stalls the NIOS II interrupt response for a significant time period. I know this because it affected my project. When I reported this to Altera the response was "this is how it works". While the PHY shifts in serial data on this MDIO read, the NIOS II will not respond to interrupt requests. We used logic in the Cyclone to delay the task the interrupt was performing to hide this latency that the PHY causes. It was a real pain but we have to respond to an external interrupt within a few uS. Fortunately there was a workaround or our product would have been dead. The MDIO read was causing additional interrupt latency from 31 to 48uS over normal latency times. Bill A. --- Quote End --- I assumed that this will cause something like this. Interrupts you are talking about are all interrupts in system ? During the read from PHY all interrupts will be delayed ? For us this is not big issue, we don't have such realtime processing. So for Altera devices there is no ("right") way to handle info about link state from PHY without this issues ? :( Thanks - Altera_Forum
Honored Contributor
--- Quote Start --- I assumed that this will cause something like this. Interrupts you are talking about are all interrupts in system ? During the read from PHY all interrupts will be delayed ? For us this is not big issue, we don't have such realtime processing. So for Altera devices there is no ("right") way to handle info about link state from PHY without this issues ? :( Thanks --- Quote End --- PETRAK, PHY chips usually have some pins intended to drive status leds. It is convenient to use them as a workaround to reduce slow MDIO exchange. For example, 88E1119R asserts LED2 pin on connect and de-asserts on disconnect. If you wire this signal into FPGA you will be able to read link status by PIO with zero overhead. - Altera_Forum
Honored Contributor
--- Quote Start --- PETRAK, PHY chips usually have some pins intended to drive status leds. It is convenient to use them as a workaround to reduce slow MDIO exchange. For example, 88E1119R asserts LED2 pin on connect and de-asserts on disconnect. If you wire this signal into FPGA you will be able to read link status by PIO with zero overhead. --- Quote End --- IMS, in fact that is *exactly* what we did - we use the same PHY and used that LED on an FPGA input pin. I did not experiment with making that an interrupt but I wanted to. Detecting loss of link was not a real-time problem for us. The workaround I mentioned wasn't just for the PHY but also for general poor and inconsistent interrupt latency on the external interrupt. The MDIO made it so bad we had to use the LED. We latched the effective start of the external interrupt to hide the normal latency that is added since we need to do something exactly timed off the external interrupt without bobble. So in effect the ISR handling is in the middle of the window from external interrupt to when we need the hardware to do its thing. We can handle this skew as long as the interrupt to hardware processing is the same. Bill A. - Altera_Forum
Honored Contributor
Hello,
we used the lwIP port in our project, and experienced problems when there is heavy traffic. After some research, two bugs were fixed, related to concurrency (variable names are renamed w.r.t. the original sources): - function low_level_input in alteraTseEthernetif: critical section needs to be extended (next_packet must be assigned within critical section; otherwise, when the ISR would be called in the mean time, it may result in the current packet being overwritten by the new one):
- function tse_mac_recv in lwip_tse_mac.c: (mRxCount > TSE_RX_BUFFER) ==> (mRxCount >= (TSE_RX_BUFFER - 1)); may otherwise result in packets being overwritten// start the critical section by disabling IRQs const alt_irq_context cpu_sr = alt_irq_disable_all(); // decrement packet counter --tse_info->mRxCount; // assign current packet from lwIP packet buffer as the new data *data = tse_info->mRxBuffer; // assign next packet instead and move forward in buffer tse_info->mRxBuffer = (void*)next_packet; if (++tse_info->mRxIndex >= TSE_RX_BUFFER) { tse_info->mRxIndex = UINT32_L(0); } // stop the critical section by enabling IRQs alt_irq_enable_all(cpu_sr);
greetings, Stephan Orban// check if contents of memory are valid if ((IORD_ALTERA_TSE_SGDMA_DESC_STATUS(&tse_info->mSgdmaDescriptor) & (ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK | ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK | ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK |ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK | ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK | ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK | ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK )) == 0) { // contents are valid // check if there is place for the next packet // this function always prepares for the next DMA transfer, so at least one packet must be available for that if (tse_info->mRxCount >= (TSE_RX_BUFFER - UINT32_L(1))) { // there is no place // drop this packet LINK_STATS_INC(link.drop); } else { // there is place // allow this packet ++tse_info->mRxCount; if (++tse_info->mRxIndexIsr >= TSE_RX_BUFFER) { tse_info->mRxIndexIsr = UINT32_L(0); } } } else { // contents are invalid // drop this packet LINK_STATS_INC(link.drop); } - Altera_Forum
Honored Contributor
Stephan,
This is interesting. I see how you're right, but I also think there is a change that does not increase latency. The reason it was kept so short is that our system requires as close to 0 latency as possible. I think the root problem is the location of this line:
I believe the following also solves the problem since the packet count (which tells us if there is a free space) is decremented last:// decrement packet counter --tse_info->mRxCount;
This also could make the second change uneccessary since the count doesn't show a free slot until the pbuf pointer has replaced the previous one. Good catch. We, as well might be the case with most users, don't load the system until the buffers are full. We have enough memory that this array is quite large. Bill// start the critical section by disabling IRQs const alt_irq_context cpu_sr; // assign current packet from lwIP packet buffer as the new data *data = tse_info->mRxBuffer; // assign next packet instead and move forward in buffer tse_info->mRxBuffer = (void*)next_packet; if (++tse_info->mRxIndex >= TSE_RX_BUFFER) { tse_info->mRxIndex = UINT32_L(0); } cpu_sr = alt_irq_disable_all(); // decrement packet counter --tse_info->mRxCount; // stop the critical section by enabling IRQs alt_irq_enable_all(cpu_sr); - Altera_Forum
Honored Contributor
Hi BillA (and others !),
Really big thanks for your lwip port on altera. I have succefully implemented the webserver template with lwip on a bemicro SDK from arrow. I have a question because i don't know how to do it : actually i have my sgdma Tx and sgdma Rx mapped on a DDR , the same DDR where the CPU data an instruction is mapped. Now imagine i want to use an onchip ram for sgdma tx and sgdmarx instead of DDR. (but CPU still mapped to DDR) I tried direclty but this is not working. if you have any idea . thanks