Kiwi Scientific Acceleration: C# high-level synthesis for FPGA execution.
Capsule Passing over FIFO channels Between Threads Example

Kiwi Scientific Acceleration: Capsule Passing Example

C# Capsule Passing over FIFO channels Between Threads Example

Here we demonstrate static thread forking and passing of object handles between threads.

Source Code

The Capsule

This is the item passed. A static number is generated in this example but see the linked-list example for dynamic heap coding styles.

class Capsule // Capsules are passed over the hardware channel between threads.
{
  public bool newlinef;
  public int capint;

  public Capsule(int myval)
        { capint = myval;
          newlinef = false;
        }
}

The Forked Process

This independent process does some work and returns the capsules to the main thread on a reverse FIFO channel.

class ConsumerClass
{   
    Kiwi.Channel<Capsule> workin, empties;

    public ConsumerClass(Kiwi.Channel<Capsule> fwd, Kiwi.Channel<Capsule> rev) // constructor
    {  
       workin = fwd;  empties = rev;
       Capsule cap2 = new Capsule(45);
       empties.Write(cap2);
    }

    public void process()
    { 
      Kiwi.Pause();
      for (int count=0; count < 10; count ++)
        {   
            Capsule rat = workin.Read();
            Console.Write("{0} ", rat.capint);
            if (rat.newlinef) Console.WriteLine("");
            Kiwi.Pause();
            empties.Write(rat);
        }
    }
}

The FIFO Channel Definition

The FIFO is defined in C# and separately compiled to a dll file using the C# compiler.

 public class Channel<T>
        {
            T datum;
            volatile bool emptyflag = true;

            public void Write(T v)
            {
                lock (this)
                {
                    while (!emptyflag) { Kiwi.NoUnroll(); Monitor.Wait(this); }
                    datum = v;
                    emptyflag = false;
                    Monitor.PulseAll(this);
                }
            }

            public T Read()
            {
                T r;
                lock (this)
                {
                    while (emptyflag) { Kiwi.NoUnroll(); Monitor.Wait(this); }
                    emptyflag = true;
                    r = datum;
                    Monitor.PulseAll(this);
                }
                return r;
            }

        }

The Main Program

The main program kicks off the child processes using the same primitives as on dot net. Kiwi requires that the number of threads used is static (all created before EndOfStaticElaboration). Here we kick off (thread1.Start) the thread from the outset, but KiwiC can support the thread not being started until later in the run.

// Print the times table by sending messages in capsules over a channel.
class test2r1
{
    static int limit = 3;
    static Capsule cap1 = new Capsule(23);
    [Kiwi.HardwareEntryPoint()]
    public static void Main()
    {
        int i, j;
        Console.WriteLine("Test2r1: Times Table Capsule Channel Test: limit=" + limit);
        Kiwi.Channel<Capsule> fwd = new Kiwi.Channel<Capsule>();
        Kiwi.Channel<Capsule> rev = new Kiwi.Channel<Capsule>();

        ConsumerClass consumer = new ConsumerClass(fwd, rev);

        Thread thread1 = new Thread(new ThreadStart(consumer.process));
        thread1.Start();

        Capsule capx = cap1;

        for (i = 1; i <= limit; i++)
        {
            for (j = 1; j <= limit; j++)
                {
                   // Must not do allocs inside runtime loops pre Kiwi2
                   //Capsule cap = new Capsule(i*j);
                   capx.capint = i*j;
                   capx.newlinef = (j == limit);
                   fwd.Write(capx);
                   capx = rev.Read();
                }
           Kiwi.Pause();
        }
        Console.WriteLine("\nTest2r1: Capsule Times Table Test: Finished\n");
    }
}

Running on Mono

The program and the channel both need compiling with mcs (or Microsoft equivalent) and can run natively:

 $ gmcs test2r1.cs  -r:/home/djg11/d320/hprls/kiwipro/kiwic/distro/support/Kiwi.dll
 $ ls -l test2r1.exe
-rwxrwxr-x. 1 djg11 djg11 4096 Jan 21 16:12 test2r1.exe
 $ MONO_PATH=/home/djg11/d320/hprls/kiwipro/kiwic/distro/support mono test2r1.exe
Test2r1: Times Table Capsule Channel Test: limit=3
1 2 3 
2 4 6 
3 6 9 

Test2r1: Capsule Times Table Test: Finished

Note that the exact same output also comes out of the KiwiC compiler if passed the -sim 100 command line flag, using its builtin simulator. The Kiwi regression test runs this simulator both on the CIL input code and on the RTL output code. We get the same result written to the file diosim.out either way.

Running the Verilog Version

First we generate the hardware by invoking KiwiC (normally via a Makefile). We do not explicitly mention the dll containing the channels since that code is, infact, in the Kiwi.dll standard library that KiwiC always references.

  $ kiwic -kiwic-finish=enable -vnl-resets=synchronous -vnl DUT.v test2r1.exe
  $ wc DUT.v
 388  1099 18510 DUT.v

The generated RTL Verilog is on this link: test2r1.v.

Note that the RTL refers to a DRAM interface and other substrate resources that are not used in this demo. Since we had only two small capsules, KiwiC made them out of flip-flops. If we had 1000 capsules, KiwiC would have used on-FPGA BRAM, and if we had had a million, they would be in off-chip DRAM.

Now to simulate:

iverilog vsys.v DUT.v ../../../hpr/cvgates.v
./a.out
VCD info: dumpfile vcd.vcd opened for output.
Test2r1: Times Table Capsule Channel Test: limit=3
         1          2          3
         2          4          6
         3          6          9

Test2r1: Capsule Times Table Test: Finished

The output is the same, give or take a tab character!

Conclusion

We have demonstrated thread-safe passing of objects between C# threads.


This test first ran in 2007. Recreated January 2016. S Singh + DJ Greaves.               UP.