Interop shminterop! What's all this talk of Interop?

NOTE: The technology discussed in this BLOG post is of an early BETA form and may change significantly by the time the product is actually released! You have been warned.

Question: With the .NET Micro Framework providing a bootable runtime for .NET that can even handle interrupts; Is there a need for native code development as well?

Answer: You Betcha! There are a number of reasons why a system designer might want to do some low level lean and mean coding.

1) Memory Mapped Devices. A lot of SOC chips have built-in memory mapped peripherals that we don't have any pre-defined driver type for. Currently the .NET MF only supports managed code drivers for SPI and I2C devices. There is no way to access a memory mapped device from managed code in the 2.x and earlier versions.

2) Complex communication stacks. To maintain maximum performance, communication protocol stacks (like ZigBee, Z-Wave, Bluetooth, etc...) really need to be done in native code.

3) Performance intensive functionality. A good example of this is the common 1-wire serial interface. (See Pavel Bansky's BLOG on 1-wire ) While the protocol is simple there are very tight timing constraints that must be met and the .NET MF interpreter isn't a good way to deal with that problem. Using native code it would be possible to achieve a general 1-wire bus implementation along the lines of the existing I2C and SPI busses.

Introducing Interop:

Based on the above scenarios, and many others provided from customers, we decided to come up with a solution to the problems in a general way. We call this "Interop". Note: this is a completely different meaning from the desktop where Interop usually means COM Interop. Normally I am one to maintain fairly ruthless consistency on terminology and would be appalled by this "abuse". However, the term actually came from customers who didn't really understand the specialized meaning in the context of the desktop framework. (I'll admit I was one of those customers asking for this and using the term "Interop"). In the end - the term just sort of stuck.

An obvious question then becomes: If .NET Micro Framework interop isn't COM interop from the desktop is it P/Invoke? The answer is: No, P/Invoke turns out to be a bit too specialized to loading of a DLL and mapping to exported C functions to meet what we were after. After thinking about what would work best for our customers in relation to the available concepts we realized the best answer was the one we already had. Yep, that's right we already had interop! We just didn't expose it in any way that would allow the Porting Kit Users to access it. The way we do it is with the MethodImpl(MethodImplOptions.InternallCall) attribute tag.

Example:

The following sample code shows one possible implementation of the C# code side of the interop for the Pulse Width Modulator (PWM) on the Freescale i.MXS CPU. (This doesn't take full advantage of all the capabilities of the hardware but makes for a simple example):

using System;

using Microsoft.SPOT;

using System.Runtime.CompilerServices;

namespace Freescale.iMXS

{

    /// <summary>Exposes the PWM controller on the Freescale i.MXS</summary>

    public class PWM : IDisposable

    {

        /// <summary>Creates a new instance of the PWM</summary>

        /// <remarks>

        /// The constructor reserves the GPIO pins for the PWM.

        /// If the pins are already reserved (by another instance

        /// of this class or something else) the reservation fails

        /// and throws an exception. Thus there can be only one

        /// instance of this class at any time. (There is only one

        /// PWM in the silicon so that makes sense)

        /// </remarks>

        public PWM()

        {

            PWM.Reserve(true);

        }

        /// <summary>Finalizer to ensure pins released on GC</summary>

        ~PWM()

        {

            PWM.Reserve(false);

        }

        /// <summary>Configures and enables the PWM</summary>

        /// <param name="Frequency">Frequency of the PWM output</param>

        /// <param name="Duty">Duty cycle of the output</param>

        /// <remarks>

        /// This method will set the frequency and duty cycle for the

        /// PWM and enable its output if not already enabled.

        /// </remarks>

        public void Configure(uint Frequency, byte Duty)

        {

            if (Duty > 100)

                throw new ArgumentOutOfRangeException("Duty > 100");

            PWM.SetPWM(true, Frequency, Duty);

        }

        /// <summary>Disables the PWM output</summary>

        /// <remarks>

        /// Disables and powersdown the PWM output and controller

        /// </remarks>

        public void Disable()

        {

            PWM.SetPWM(false, 0, 0);

        }

       

        #region IDisposable Support

        /// <summary>Disposes the PWM object</summary>

        /// <remarks>

        /// Disposing the PWM ensures that GPIO pins are properly

        /// released as soon as possible

        /// </remarks>

        public void Dispose()

        {

            PWM.Reserve(false);

            GC.SuppressFinalize(this);

        }

        #endregion

       

        #region Native Methods

        [MethodImpl(MethodImplOptions.InternalCall)]

        private extern static void Reserve(bool State);

        [MethodImpl(MethodImplOptions.InternalCall)]

        private extern static void SetPWM( bool Enable

                                         , UInt32 Frequency

                                         , byte Duty

                                         );

        #endregion

    }

}

The example code has two methods that are marked as extern static and have the correct attribute attached to them. This attribute tells the CLR that the method is implemented internally and it can look up where the actual implementation is when it needs to. The actual code is in the libraries created by Microsoft or a system designer with the Porting Kit. I'll cover what the native code side of things looks like in a later post. (Gotta keep ya waitin' for more! ;-) )