This is an idea that I’ve been playing with for some time. I wrote a web-server with the uIP stack for a mega644 years ago. The uIP stack offers something called protothreads. They are basically co-routines, now this is great, pseudo threading built into the tcp/ip stack. Happy days.
Problem, co-routines don’t maintain their own stack. This means, that every time you re-enter the co-routine, none of the variables maintain their previous values, and you basically have to build a state machine to handle this. The very concept of protothreads is to save memory and space, however I find when you end up making a complex state machine, you end up using a lot more memory than you probably thought you would. This has always irritated me.
My solution was to write a similar system to protothreads, except that the stack is maintained upon re-entrance. In theory this works pretty simply:
When you re-enter a thread, you adjust all the registers to point to the “process” stack associated with that thread, and you update all the registers with their previous values before the process yielded. It took a bit of fiddling to get right, and I’m not 100% convinced it’s still perfect, but it seems to work pretty well. The difference between this, and a proper thread, is that it switches context upon user discretion, although from an application point of view, it looks pretty transparent.
I decided to call my system fibres. When sending data to a socket on the application within uIP now, one calls fib_send, as opposed to proto_send. This sends the data to the socket, and then executes a context switch. This then allows the uIP stack to send the data. Once the data has been sent, the context handler will then swap you back into your application, with all your previous data there. No need for a state machine, it’s like you never left.
In order to make uIP lightweight, the writers decided to enlist the help of the application to re-send packets that get lost or dropped or whatever. So, upon re-entering the application, you have to check and see if your packet received an ACK for the packet you just sent. When you’re in the uIP context, the stack will wait for an ACK or a timeout before re-entering your application. If it times out, your application then needs to ask uIP if you received and ACK or not. If it did not, then you need to re-send the packet. Within fib_send, I’ve handled this for the user. Making it very easy, and transparent to just write application code so no-one has to worry about re-sending packets, this will happen inherently when you send some data.
This seems to work exceptionally well. Some care has to be taken when writing applications, as some strange un-intuitive things happen, and tend to be gotchas, but I won’t go into that here.
Here is a pic of the web-server I made last night.
This is based on an ATMEGA644. This is a nice chip, it has lots of flash space, and a decent amount of ram (relatively speaking). It has a nice new AVR core, so each port also has a port change interrupt, which is pretty awesome. The Ethernet controller can
be seen on the bottom right. This is a Microchip enc28j60. To be fair, it does the job, but it has a gazillion problems. The errata list is about 27 hardware bugs strong. Needless to say, you can work around or avoid most of them. The interface between the mega644 and the enc28j60 is SPI. The SPI is clocked at exactly 5Mhz, as this was one of the problems. 5Mhz seems to be stable. It includes an ISP port for programming, and a JTAG port for live hardware debugging.
It also features a robust PSU. It’ll handle anything from about 5v up to somewhere in the region of 40v. It also includes polarity protection, so it doesn’t matter which way you connect the power. The regulator tends to get hot if the incoming voltage is high (As it needs to dissipate this energy), so I’ll probably stick a heat sink on it. This is basically the same PSU in included in my radio project.
The more eagle eyed readers might notice the lack of Ethernet magnetics. The jack that I’m using is a Tyco rj45 socket that includes a 1:1 magnetics transformer inside. This is nice as it’s a space saver for smaller boards, and it’s just generally convenient.
Currently the mega644 polls the Ethernet controller for new data. I’d like to change this to an interrupt system where the mega644 is interrupted by the enc28j60 when new data arrives on the socket. The PCB I cast, caters for this, so when I have a little time, I’ll change the drivers to cope with this mechanism.
Here is the reverse side, it’s a double layered PCB, so the second layer is made up with jumper wires. URRGGG
So I’ve been at it again with this webserver. As we know, it uses the enc28j60. Happy days!
Not quite. I’m less and less happy with this part. It needs some resistors on the MOSI line to condition the signal for some reason. I’ve not found an explanation for this, I can only presume it can’t understand normal SPI signals. The chip has an errata list as long as my left leg, so I’m going to guess it’s a silicon problem that’s never been addressed. Anyway, so it seems to function pretty well with this resistor in place. Or so I thought.
SPI is quite a clever beast. You can put multiple devices on the bus, and a device will only speak when spoken to. This is achieved by means of a physical slave select line. When it’s pulled low, for a particular device, you have it’s ear, and can thus communicate with a specific device. However, when I added a second device onto my SPI bus, what should happen? Well, an 80% packed loss. Great, maybe I stuffed something up? I unplug the device, and it starts working happily. Now, I know that this other device (nRF905) works perfectly on a standard SPI bus, I’ve used it several times before.
I’m not happy with this. I could try shoving more resistors in places they don’t belong, but that doesn’t bode well for a device that’s happy to sit on a bus without any extra impedance.
Bottom line, is I’m not happy with this part. I’ve ported all my code to a crumb644. It uses a Silicon Labs CP2201 ethernet controller, and it seems to handle well. It uses a parallel interface as opposed to a serial one. The only thing I don’t like about the CP2201, is that it’s 8bit data port is sitting on PORTC of the atmega644, which incidentally is the same port the JTAG sits on. No debugging for you!
I am currently designing my own board, but it’s all very small SMD work, because the CP2201 only comes in an SMD part, unlike the enc28j60. So I’m sitting between a rock and a hard place. Not cool.