Let's make TCP/IP really work on mbed!

27 Mar 2011

The current official port of the lwIP TCP/IP stack seems to only kinda work on mbed. It is not clear to me how others live with this, but certainly the lwIP stack is capable of performing better. If you want to see how lwIP is supposed to work on an ARM Cortex-M3 microcontroller, please watch the following YouTube video:

I see absolutely no reason why lwIP shouldn't work on mbed1768 at least as good as on the Stellaris LM3S6965 MCU, which is actually less capable than the LPC1768.

The first thing we need to fix the lwIP implementation on embed is the lwIP stack as a proper mbed library in source code, so that it can be reconfigured and recompiled for specific projects. To this end, I've just published the library lwip_1_4_0_rc2. This is the latest available lwIP 1.4.0.rc2 adapted to compile with the C++ mbed compiler. This library contains an unfinished Ethernet driver for mbed1768 in the file eth_driver.cpp. This is an interrupt-driven driver, which does not depend on the polled mbed Ethernet class. Instead, this driver is written only by means of the low-level CMSIS interface "LPC17xx.h".

The Ethernet driver is incomplete yet, because without the proper debugger for mbed, and without really knowing how to configure the separate PHY of the mbed1768 board I ran out of time to make this work. I have left comments starting with 'TBD:' in all places that still need attention.

I really count here on the open source spirit of the mbed community to finish the job. I think that it would be great to have a robust TCP/IP stack and HTTP server with CGI and SSI, which wouldn't be just a toy, but could work in hard real-time applications that do other things than merely serving web pages or exchange TCP or UDP packets. It is really not much left to do to have such a library.

To test the lwip_1_4_0_rc2 library, I've also just published the (qp_lwip) test program. This test program should work on mbed1768 exactly like in the aforementioned video, except instead of writing to the OLED display it will blink LEDs on the mbed board.

Miro

27 Mar 2011

Hi there,

This is very interest! We seem to have been working on the same problem but from different directions.

I currently have a eth_drv that's been working for months now on my test beds. It's driving LLDP and some home brew etherType handlers (using experiental etherType codes). The driver has etherType plug-in callbacks to allow different etherType protocol handlers to attach/detach as required (no etherType handler and the packet is simply dropped). This plug-in system was one of the reasons I don't use the current "official" Mbed LWIP stack. It's eth_drv.c takes etherTypes it wants and droped the rest. No where to connect in for other low level eth network protocols.

It also supports interrupt driven systems and can use PendSV to push the packet injection into the "PendSV context" (still handler mode but makes sure all other pending interrupts get serviced) and also supports using the GPDMA to move packets around (if you have to move them faster than *dst++ = *src++ CPU can do (and allows you to move EMAC DMA buffers into any SRAM position). All done in PendSV (if you want, you can have the normal irq handling if you want). Oh, and yeah, it supports full 1500MTU and RAMBANK1 handling. It also allows for the "spare ram" from the rambank to be exported (I did this as I knew at some point lwip would want some pbuf space :)

So just last week I grabbed lwip_1_4_0_rc2 and began work on a NetIf "plug-in" to my driver. But... if you have a sensible lwip starting point already then we should look at plugging these two projects together? Message me if you want to get in touch and see how we can take this further.

28 Mar 2011

AFAIK the NetServices libraries (in all their variants) contain the lwIP code (I saw it when it threw out some stuff while building the NetServicesMin library).

I should try to merge your code with this version. Also, I'm thinking about some other layer above lwIP - the code I saw add like 4 layers of objects above the raw stack, which I think is overkill...

24 May 2011

Good day,

I cannot say whether the current lwIP on mbed is 'broken', but I do like the idea that we have a build-able alternative. And I think this request is related:

I really want to hook up a WiFi module to an mbed, but use the internal IP stack (as opposed to something like a WiFly which is just a glorified serial port). I need this because we are working on a small form factor project that will support a number of different services, which means we need control at the IP level.

I am not an expert in lwIP either, so I am curious how difficult it would be to add the backend code needed to support a WiFi module via SPI, like this one: http://www.redpinesignals.com/Products/Modules/n-Link/RS9110-N-11-02.html or something similar.

Ideas, suggestions (not including adding an external router :-)

Thank you, Tyler

24 May 2011

Daniel Short and I are working on an LWIP stack that is modular. We are first looking to support the ethernet port as a starting point. However, once we have the modularity in place then doing what you are looking for will become a lot easier. Just hang in there! (or offer to help out :)

25 May 2011

I was really excited by the mbed when I saw it had ethernet capabilities but the actual workings of it are a complete let down.

I wish I could help but my programming knowledge of ethernet/IP is not up to speed. I will keep an eye on this project and hope it works out

25 May 2011

I would certainly like to assist in this effort. I have a WiFly GSX module, and the specs needed to use it in a mode where it is simply a WiFi NIC. This should be prety close to what you did/are doing/will do with the ZeroG part you mention in a different thread.

Would it be possible to review your module lwIP code to see where I need to plug in the low-level packet handling, etc.?

Thanks, Tyler

27 May 2011

Hi,

Inside the CMSIS package available from NXP, there is a driver for ethernet: /media/uploads/Benoit/ethdev.7z, along with a description of the interrupt-based implementation:

/media/uploads/Benoit/_scaled_cmsis_ethdev_interrupt.png

I wish I could finish what you started, but I'm not familiar enough with the LPC1768 yet, although the mbed platform is fantastic and I'm learning a lot about it.

However I had many deceptions when it comes to the ip stack (specially UDP), where all implementations of lwIP are not perfect yet, so I have a great hope in your approach with lwIP (by the way, lwIP-1.4.0 is out: http://download.savannah.gnu.org/releases/lwip/lwip-1.4.0.zip)

Kind Regards,

Benoît.

27 May 2011

The ethernet EMAC driver is well under development, in fact, I'm beta testing it now. And the stack we are using is Lwip 1.4.0 ;)

31 May 2011

Hi Benoît,

Thanks for the update.

Have you actually tried the NXP library with mebed1768 yet?

It would be fantastic if it actually worked without tweaking. I've tried a couple of downloads from NXP and they didn't work on mbed for some trivial reason. For example, I was never sure of the PHY configuration. (The LPC1768 microcontroller has no internal PHY, so various boards provide different PHYs).

31 May 2011

Hi 'Quantum' (sorry, can't find your given name),

I've been playing with the latest CMSIS-2.0 from NXP and lwip-1.4.0 since more than one week now. First I started to integrate all drivers I could find. So far I had some success with the driver found on CoOS website (http://www.coocox.org/EXAMPLE/NXP_LPC1766_ARMCC.htm). The problem I have so far is that the driver hangs the complete micro-controller and stops receiving data.

I feel that, apart from the ethernet driver, the correct setting of all lwIP options (from lwipopts.h / opt.h) is also tricky.

So after all this frustration, today I started to write a simple application from scratch using interrupts and lpx17xx_emac sublayer from pure CMSIS drivers and using CoOS for the moment. So far so good, the reception of packets is very stable. I resist to the temptation of writing my own IP stack (with arp, icmp and UDP to begin)...

My general feelings are that I really feel frustrated with all the broken implementation of tcp/ip on mbed, it could be a show stopper for me, as I started to use the easy programming provided by all the available classes, and now that I move back to CMSIS, I must rewrite almost everything (the most time consuming for me being the LocalFilesystem, so I postpone it for later).

Best Regards,

Benoît.

31 May 2011

Benoît,

I wouldn't waste to much of your time on a driver for the LPC1768/9 EMAC. I'm very close to publishing a driver that uses the DMA directly into LWIP pbufs (zero copy) and a tuned LWIP stack to match the driver. Daniel is cuurently working on the services layer that sits on top of that. We're still thrashing that out but for a 1.0 release we want to support the NetServices API of the previous stack as a starting point (so all the existing libraries for higher level protocols wil just work on the new stack).

I guess we'll announce the publishing of as soon as we can (or a call for beta testers maybe).

@Quantum, you'll be pleased to hear that I have put in the QP "wrappers" for QK. However, having not yet worked with QP I'm hoping that you'll assist in that department. I'd like the driver/stack to be as useful as possible across as much existing code as possible.

One more thing, for 1.0 we are looking only initially to support the EMAC/Ethernet. Once we have a stable version we'll look at adding on some of the other available options (USB nic drivers/wifi/etc).

31 May 2011

Benoît,

Have you looked the uIP stack? I do not have any experience with it, but I understand it is another major embedded IP stack for systems like the mbed.

- Tyler

31 May 2011

Tyler, I looked at uIP as well as LWIP. From a performance point of view, on a 32bit Cortex-M3 with EMAC/DMA capability uIP really isn't suitable (workable but LWIP is a better fit, esp when you start using the DMA for both Rx and Tx packets).

31 May 2011

Andy, good to know you checked it out as an option.

I am curious as well about something: when you and Benoît are developing this code, are you doing it all with the online mbed compiler environment? If so, how does it handle any potential conflicts between the mbed-provided libraries and the overrides that you are developing?

If not using the mbed online environment, what are you using?

Thank you, Tyler

31 May 2011

Tyler,

It's being deved mainly on the Mbed cloud compiler. However, it'll be available also for LPCXpresso (1769 version) and will compile under the commercial version of the Code Red/GCC compiler. All the low level code doesn't use the Mbed libraries, it's all written directly at a lower level (CMSIS) for driving the EMAC. One proviso, LPCXpresso users will either need to use the RIT (code conditionally compiled in) for a timebase or provide their own time triggers. The Mbed version uses standard Mbed Tickers.

Andy

31 May 2011

Tyler Wilson wrote:

Benoît,

Have you looked the uIP stack? I do not have any experience with it, but I understand it is another major embedded IP stack for systems like the mbed.

- Tyler

I've considered it, but so far I would like to use the socket API, which doesn't seem available from µIP, and also taking performance into consideration.

Benoît.

01 Jun 2011

Andy,

Can you tell us when do you think you can post a working Ethernet driver for lwIP on mbed?

As I said before, I'm interested in a driver for the QP framework, because I believe that a powerful MCU like the LPC1768 can benefit from a multitasking infrastructure which would allow to do many things in parallel. But we certainly can design the driver to use callbacks or some similar mechanism, so it is generic and not necessarily married to QP.

Miro

01 Jun 2011

Tyler Wilson wrote:

[...]

If not using the mbed online environment, what are you using?

I'm using both Keil µVision 4.20 (eval) + online tools on mbed.org. I hope one day mbed.org will make the source code of their classes available so that we can improve them :)

02 Jun 2011

Hi,

While debugging the current code, it seems that the major source of problem is with the EMAC driver. I could see that frame reception stops because the circular descriptor buffer gets full, so it seems the driver code and its interaction with lwip low level functions gets out of sync and allow this condition.

I think the EMAC interrupt handler for frame reception could just be left aside, because there is already a protection mechanism provided by the EMAC circular descriptor buffer. The low_level_input() task could just wait on the condition where the descriptor circular buffer is not empty and consume every data it cans to eat it up.

I'll give it a try and come back with my findings.

Benoît.

02 Jun 2011

Hi Benoît,

I already identified this issue. The current system, afaict, sets aside buffers for the EMAC descriptors (length unknown, but assume it's around 700 bytes rather than the full MTU based on reading other forum threads). When a buffer is filled a call is made to pbuf_alloc() and then the data is copied, by the CPU from the EMAC descriptor buffer into the pbuf.

In my new driver, it's "tightly bound" to the LWIP memory manager. The EMAC itself doesn't have any buffers. LWIP is compiled to have a large number of small PBUF_POOL pbufs. At EMAC init each descriptor is pre-allocated a PBUF_POOL. The EMAC DMA is then setup to directly write the incoming data into a series of pbufs. When the LAST_FRAG is detected on teh frame, if more than one pbuf was used to aquire the packet then the driver chains the pbufs together, replaces descriptors with new pbuf allocs and then injects the packet into a "pre-queue". The user process then loops around and empties this queue into LWIP.

It's far more efficient as:-

  • There's no CPU copy from EMAC buffer to pbuf memory (zero-copy driver)
  • There's no duplication of buffers (ie the EMAC and LWIP don't have seperate buffers) so I can make much better use of the 16k ram bank.

Like I said eariler, I'm a bit ahead of you on the driver side and low_level_input. The issues I'm having at the moment is trying to make LWIP keep up with things. Packet injection is, err, rather faster as you can imagine.

Still a lot to do but in principle the the alpha version of the driver is currently managing multi-megabit transfers (so long as the data ends up in dev/null at each end that is, ie no real application!)

Andy

02 Jun 2011

Hi Andy,

Thanks for your feedback, it is good to know people are making progress :) On my side, I continue for the sake of having a working emac driver and for...curiosity, this LPC1768 is really neatly designed.

I think I found an implementation bug in the current CMSIS emac driver for function Bool EMAC_CheckTransmitIndex(void):

/*********************************************************************//**
 * @brief		Check whether if the current TxProduceIndex is not equal to the
 * 				current RxProduceIndex - 1.
 * @param[in]	None
 * @return		TRUE if they're not equal, otherwise return FALSE
 *
 * Note: In case the RxConsumeIndex is equal to the RxProduceIndex - 1,
 * it means the transmit buffer is available and data can be written to transmit
 * buffer to be sent.
 **********************************************************************/
Bool EMAC_CheckTransmitIndex(void)
{
	uint32_t tmp = LPC_EMAC->TxConsumeIndex -1;
	if (LPC_EMAC->TxProduceIndex == tmp) {
		return FALSE;
	} else {
		return TRUE;
	}
}

Comparing TxConsumeIndex - 1 with TxProduceIndex will not always give the expected result when wrapping is needed, when for example TxConsumeIndex is equal to zero.

Instead of comparing:

TxConsumeIndex - 1 with TxProduceIndex

let's rather compare

TxConsumeIndex with TxProduceIndex + 1

and check if wraps:

Bool EMAC_CheckTransmitIndex(void)
{
	uint32_t	tmp = LPC_EMAC->TxProduceIndex + 1;
	
	if (tmp == EMAC_NUM_TX_FRAG) tmp = 0;
	return (LPC_EMAC->TxConsumeIndex != tmp) ? TRUE : FALSE;
}

Benoît.

02 Jun 2011

Benoît,

Allow me to help you save wasting a bunch of time. In the sample driver you mentioned (it's not CMSIS, it's just a collection of useful code) you want to keep all the code relating to the PHY. The rest of the code you need to deposit in the nearest waste paper bin (or /dev/null, which ever is closer). :-)

And here's a real time saver clue for you. The EMAC DMA is fast, very fast. Do not rely on RxProduceIndex to be where you may think of it to be, especially when you see the LAST_FRAG set. It may have advanced one or two by the time your ISR is invoked ;)

Lastly, pay close attention to RxConsumeIndex and descriptor ownshership. Read that section of the manual and then, read it again ;)

Andy

02 Jun 2011

Hi guys,

I'm really happy reading that post and seeing that someone is working on that topic, too. I also have to face some strange problems with the recent NetServices lib (within my HTTPServer and HTTPClient implementations) resulting in system hung ups and TCP packets lost inside the lwIP stack.

If there is some implementation including the lates lwIP (http://download.savannah.gnu.org/releases/lwip/lwip-1.4.0.zip) I would be happy to get beta tester and help to increase performance on mbed!!!

Best wishes

04 Jun 2011

Andy K wrote:

The ethernet EMAC driver is well under development, in fact, I'm beta testing it now. And the stack we are using is Lwip 1.4.0 ;)

Hi Andy,

I'm a bit frustrated about the current solutions, like not being able to set or read the ethernet link type, can we have a look at the beta version yet ?

Ivo

05 Jun 2011

Hi,

Some news, I finally started to write a TCP/IP stack from scratch (named mbedNet), with performance and simplicity in head, using zero copy. All using 100% C code. This will be easily embeddable in C++ objects.

So far ping time on a 100Mbps LAN is around 150µs:

20 packets transmitted, 20 received, 0% packet loss, time 18997ms
rtt min/avg/max/mdev = 0.137/0.146/0.212/0.020 ms

I started to implement the socket API for UDP (the one I need the most for now).

Here is a sample initialization code:

#include <mbednet/mbedNetIF.h>
#include <mbednet/ARP.h>
#include <mbednet/ICMPv4.h>
#include <mbednet/UDPv4.h>
#include <mbednet/Sockets.h>

Ethernet_Addr_t myMAC =   {0x00, 0x10, 0x33, 0x2e, 0x18, 0x7f};
IPv4_Addr_t     ip =      {192, 168, 138, 47},
                netmask = {255, 255, 255, 0},
                gateway = {192, 168, 138, 254};

NetIF_t         *mbedNetIF;

mbedNetIF = NetIF_RegisterInterface(&ip, &netmask, &gateway, &mbedNetIF_Driver, (void *)&myMAC);

ethernet.RegisterProtocol(&arp);     /* add ARP support over ethernet */
ethernet.RegisterProtocol(&ipv4);    /* add IPv4 support over ethernet */

ipv4.RegisterProtocol(&icmpv4);      /* add ICMPv4 support over IPv4 */
ipv4.RegisterProtocol(&udpv4);       /* add UDPv4 support over IPv4 */

udpv4.RegisterAPI(&sockets);         /* add socket API on UDPv4 */


The stack is very modular, and if a protocol or api isn't needed, it is easy to leave it aside.

Then have the frame processing in the application loop(s), or from a thread in a RTOS environment, by calling:

NetIF_ProcessFrames();               /* Non blocking call, will check for frames in the registered interfaces, process them if any, then return */

The Ethernet driver only uses 100% CMSIS code, no interrupt handler. I take advantage of the nice circular buffers provided by the EMAC block on the LPC1768 (nice piece of hardware).

My plans are to finish the stack up with complete implementation (v4 only, no IPv6 for the moment, but there is room for it) of:

  • ARP (with arp cache and expiration)
  • ICMP echo/reply
  • IP
  • UDP
  • Sockets for raw frames and UDP
  • loopback interface

The stack will be tunable for use in no RTOS environment as well as inside an RTOS environment which is providing the minimum services like FreeRTOS and CoOS for example (mutexes, mailboxes, semaphores, ...)

So far the stack is very fast and stable, it doesn't crash like all the bad experiences I had previously with the adaptations of lwIP (I'm not saying lwIP is problematic, but just that the ports so far to LPC1768 are not perfects, and lwIP is rather complex to configure).

I'll share a first version of the source code when it is usable.

Regards,

Benoît.

PS: I use this stack with CoOS v1.14 + latest CMSIS as libraries, it is very stable so far.

11 Jun 2011

I have made the files available on SourceForge: http://mbednet.svn.sourceforge.net. For the moment, they are only usable with CMSIS + offline compiler (like Keil or GCC).

Working features in this alpha release:

Driver:

  • EMAC driver

Protocols:

  • Ethernet protocol
  • ARP over Ethernet for IPv4
  • IPv4 over Ethernet
  • ICMPv4 over IPv4
  • UDPv4 over IPv4

APIs:

  • Socket for UDPv4

The stack is very modular, as each protocol can register one or more protocols on top of itself. The same is valid for API (like sockets), they can be registered on top of every protocol.

Next to come in order:

  1. integration inside an RTOS environment (similar to what lwIP does), only using mutexes & semaphores.
  2. TCPv4 protocol
  3. Sockets for TCPv4
12 Jun 2011

Benoit,

A commendable effort, but to be of use to the vast majority of mbed users, this really has to build with the online compiler and co-exist with other mbed libraries.

For me, two of the big attractions of mbed are the online compiler and the libraries (both official and user-created). If I had wanted to use an offline compiler, then I would probably not have chosen mbed.

So, may I request that you add "build with online compiler" towards the top of your to do list :-).

Paul

12 Jun 2011

Hi,

I have adapted the library for mbed "online": http://mbed.org/users/Benoit/libraries/mbedNet/lsolap

An example program is available here: http://mbed.org/users/Benoit/notebook/mbednet-example/

Next I think I'll replace the polling of network events by an interrupt driven routine and see how it goes.

Benoît.

12 Jun 2011

Great project!