Netduino home hardware projects downloads community

Jump to content


Photo

Managed code network drivers and memory management


  • Please log in to reply
2 replies to this topic

#1 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 18 January 2015 - 01:29 AM

We just posted the ENC28J60 driver source for Netduino.IP, so I wanted to take a few moments to talk about how network interface drivers are implemented in C# and how Netduino.IP manages memory.

Here is a link to the ENC28J60 driver source which I'll be discussing briefly in this post:
https://github.com/n...J60/ENC28J60.cs

Netduino.IP.ILinkLayer interface
IP packets are sent/received via the bottom layer of the TCP/IP stack (the link layer). Network chips such as the ENC28J60 used on Netduino Plus 2 handle the physical transmission of packets, but a software driver is necessary to drive and interface with those network chips.

The Netduino.IP.ILinkLayer interface abstracts away differences between various Ethernet chips, boiling network connectivity down to a simple set of functions and events.
 
public delegate void LinkStateChangedEventHandler(object sender, bool state);
public delegate void PacketReceivedEventHandler(object sender, byte[] buffer, int index, int count);

public interface ILinkLayer
{
    bool GetLinkState();
    byte[] GetMacAddress();
    void SendFrame(int numBuffers, byte[][] buffer, int[] index, int[] count, Int64 timeoutInMachineTicks);
    void SetMacAddress(byte[] macAddress);
    void Start();
    void Stop();
    event LinkStateChangedEventHandler LinkStateChanged;
    event PacketReceivedEventHandler PacketReceived;
}
The GetLinkState() function queries the network controller for its current link state. True means connected; False means disconnected.

Whenever the link state changes (i.e. the network cable is attached/detached, a connected router reboots, etc.), the LinkStateChanged event is raised.

To set or retrieve the hardware MAC address used by the network controller, the network stack calls SetMacAddress(byte[] macAddress) or GetMacAddress() respectively.

Netduino.IP is designed so that network chips can be initialized (and powered down) on the fly. This creates plug-and-play capabilities for add-on networking hardware and saves on RAM/energy resources until networking is actually being used in an application.

With Netduino.IP on Netduino Plus 2, boot times are a little bit faster (since networking is not initialized unless/until used). When networking is used, the network chip is quickly and seamlessly initialized and the ACT LED will glow green showing a valid network link. The RAM required for networking is allocated only when networking is used.

At a low level, the Netduino.IP stack calls the Start() function to initialize a network interface chip and allocate buffers. It calls Stop() to de-initialize the network interface, dispose of buffers, and minimize energy consumption.

Sending and receiving data
Network frames are sent using the SendFrame(int numBuffers, byte[][] buffer, int[] index, int[] count, Int64 timeoutInMachineTicks) function.

The numBuffers parameter, jagged buffer array, and index/count parameters are part of the conservative memory management scheme in Netduino.IP.

To explain this a bit further...

ICMP packets, for instance, do not actually contain any information about the origin or designation IP address. So before transmission they are packed inside of an IP packet whose header contains those addresses. The packets also don't know anything about the peculiarities of Ethernet transmission. So before transmission that IP packet is wrapped in Ethernet frame(s) which add on source and destination hardware MAC addresses, etc.

It would be easy to create a new concatenated byte array at each layer of the protocol stack and count on the garbage collector to free up that memory after the packet is sent. But doing so creates a lot of unnecessary and expensive GC time; and with the low-memory devices that NETMF is targeted for, unnecessary allocations could create unfortunate OutOfMemoryExceptions for application code.

So we took a different path: as a data packet is wrapped inside each successive layer of TCP/IP headers/footers, we simply collate all of those buffers in a single array of buffers. When the network interface driver receives the buffers it transmits each one in order, thereby placing a complete Ethernet frame on the wire. Memory usage is minimized and no garbage collection is necessary.

This procedure also works in reverse to a large extent, when Ethernet frames are received from the network: the Ethernet frame is received into a single ~1500 byte buffer; the IP portion of the frame is passed up the stack; the inner UDP/TCP/DHCP portion is passed to its respective protocol; etc. When the frame's payload is needed by your managed code application, it is copied into a new byte array which is passed to your application as expected--but otherwise we minimize memory overhead and GC activity.

For reference, the network interface's data received event looks like PacketReceivedEventHandler(object sender, byte[] buffer, int index, int count). There is only one buffer here--and the index and count values are adjusted (to point to the appropriate inner data for each layer of the protocol stack) as the frame is passed up the stack.

More about the ENC28J60 driver
If you take a look at the ENC28J60 driver source, it is largely interrupt driven: when the ENC28J60 asserts the INT pin, the driver springs into action, looking for link state changes or received data or transmit errors, etc.

The driver also includes abstractions for reading/writing network chip registers (including PHY registers which are special-case) and the source there will be useful for those implementing Netduino.IP support for other network interface chips. Registers are represented in enums (and for clarity, we include but comment out the unused registers).

For writing/reading data to/from the network chip, we have included buffer management and IO code. Different network chips will vary in how the IO works (circular vs. non-circular buffers, partial frames with pointers, etc.)--but this serves as both a good production and good example implementation.

We have included ENC28J60 Errata notes in the driver. We have a few Exception placeholders where we want to create more-specific Exceptions or where we may add additional functionality in the future.

And for concurrency protection, we have created a giant SPI bus lock which is intended to prevent SPI bus contention and data corruption.

Summary
In the end, the ILinkLayer interface for Netduino.IP provides a simple and abstracted way to connect the TCP/IP stack to a wide variety of network chips. Network drivers can be written and debugged rapidly and easily directly in Visual Studio. And by following best practices, NETMF hardware can minimize both RAM and energy usage with Netduino.IP.

We'll have lots more to cover as the technical preview continues. If you find this level of detail interesting, please let me know. This is a really rich stack with a lot of NETMF-optimized design decisions--so there is a lot we can share.

Chris

#2 JoopC

JoopC

    Advanced Member

  • Members
  • PipPipPip
  • 148 posts

Posted 19 January 2015 - 08:27 AM

Chris,
The program code looks very professional through the eyes of an amateur (like me).

The most important for us all is that we can change the program code fast when there is a bug or something without begging Microsoft for a solution. Is there anywhere a small working example to download, it will save me plenty time to find out howto.....

Also, for the Visual Basic users like me (and there are a lot like me) please also the DLL. (I know, it's only compiling but..)
Thank you in advance.

#3 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 19 January 2015 - 09:02 AM

Hi JoopC,

Netduino.IP will be included in the standard Netduino Plus 2 and Netduino Go 4.3.2+ firmware, so you will be able to use it without even needing to pull it into your solution.

Just use System.Net.Sockets, etc. as usual and you'll be off to the races.

We're pulling together Technical Preview Build 1 now, for testing configuration of MAC and static IP addresses. We'll also have Build 2 shortly which will add in ARP (where you'll be able to see the network address advertisement in Wireshark when the network cable is plugged in). Then DHCP. Lots to IP goodness to come over the next 12 weeks :)

Thanks for your enthusiasm,

Chris




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.