Orangepath/HPRLS Project: Hardware and Embedded Software Synthesis from Executable Specifications.
Compilation from .net CIL Bytecode

Kiwi: High-Level Synthesis from C# (.net CIL bytecode)

The main part of the Kiwi Project is the KiwiC compiler. This, itself, consists of a front end for the HPR L/S (Orangepath) logic synthesis library. KiwiC reads .net CIL portable assembly bytecode files and converts them into the internal HPR virtual machine format. The standard KiwiC recipe invokes processing stages that emit the design is synthesisable Verilog RTL or SystemC. This .net bytecode is generated by Microsoft tools or the MCS compiler from the mono project.

This web page shows output from our first, simple trial.

H/W Compilation from .net CIL Bytecode to RTL/Netlist

In this example, the following C# program was compiled to .net CIL bytecode. The program simply prints out a multiplication table.

C# Source Code

The C# source code is:

using System; //NameSpace
class TimesTable
    static int limit = 5;
    public static void Main()
        int i, j;
 	Console.WriteLine("Times Table Up To " + limit);
	for (i=1;i<=limit;i++)
   	  for (j=1;j<=limit;j++) Console.Write(i*j + " ");

The .net CIL Assembly Code

The C# source code was fed into the mono project's mcs compiler. This generates a .exe file. It can probably also generate the IL assembly code directly but we used monodis to create the following listing from the .exe file.

The .net CIL assembly listing from monodis is:

.assembly extern mscorlib
  .ver 1:0:5000:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.assembly 'timestable'
  .hash algorithm 0x00008004
  .ver  0:0:0:0
.module timestable.exe // GUID = {FCF8AA38-DB18-4622-8121-C10C03FD4078}

  .class private auto ansi beforefieldinit TimesTable
  	extends [mscorlib]System.Object
    .field  private static   int32 limit

    // method line 1
    .method public hidebysig  specialname  rtspecialname 
           instance default void .ctor ()  cil managed 
        // Method begins at RVA 0x20ec
	// Code size 7 (0x7)
	.maxstack 8
	IL_0000:  ldarg.0 
	IL_0001:  call instance void object::.ctor()
	IL_0006:  ret 
    } // end of method TimesTable::instance default void .ctor () 

    // method line 2
    .method private static  specialname  rtspecialname 
           default void .cctor ()  cil managed 
        // Method begins at RVA 0x20f4
	// Code size 7 (0x7)
	.maxstack 8
	IL_0000:  ldc.i4.5 
	IL_0001:  stsfld  int32 TimesTable::limit
	IL_0006:  ret 
    } // end of method TimesTable::default void .cctor () 

    // method line 3
    .method public static  hidebysig 
           default void Main ()  cil managed 
        // Method begins at RVA 0x20fc
	// Code size 103 (0x67)
	.maxstack 8
	.locals init (
		int32	V_0,
		int32	V_1)
	IL_0000:  ldstr "Times Table Up To "
	IL_0005:  ldsfld  int32 TimesTable::limit
	IL_000a:  box [mscorlib]System.Int32
	IL_000f:  call string string::Concat(object, object)
	IL_0014:  call void class [mscorlib]System.Console::WriteLine(string)
	IL_0019:  ldc.i4.1 
	IL_001a:  stloc.0 
	IL_001b:  br IL_005b

	IL_0020:  ldc.i4.1 
	IL_0021:  stloc.1 
	IL_0022:  br IL_0042

	IL_0027:  ldloc.0 
	IL_0028:  ldloc.1 
	IL_0029:  mul 
	IL_002a:  box [mscorlib]System.Int32
	IL_002f:  ldstr " "
	IL_0034:  call string string::Concat(object, object)
	IL_0039:  call void class [mscorlib]System.Console::Write(string)
	IL_003e:  ldloc.1 
	IL_003f:  ldc.i4.1 
	IL_0040:  add 
	IL_0041:  stloc.1 
	IL_0042:  ldloc.1 
	IL_0043:  ldsfld  int32 TimesTable::limit
	IL_0048:  ble IL_0027

	IL_004d:  ldstr ""
	IL_0052:  call void class [mscorlib]System.Console::WriteLine(string)
	IL_0057:  ldloc.0 
	IL_0058:  ldc.i4.1 
	IL_0059:  add 
	IL_005a:  stloc.0 
	IL_005b:  ldloc.0 
	IL_005c:  ldsfld  int32 TimesTable::limit
	IL_0061:  ble IL_0020

	IL_0066:  ret 
    } // end of method TimesTable::default void Main () 

  } // end of class TimesTable

Note, the KiwiC front end can also write its own disassembly if requested (default output file is called obj/ast.cil). There are minor syntax differences in the disassemblies.

Internal Representation

The CIL bytecode was read in by the CIL parser of the Orange tool and converted from stack code to a sequential program in the internal HPR L/S virtual machine format. The VM code is as follows:

0:*APPLYprintf(*APPLYconcat("Times Table Up To ", limit));
1:V_0 := 1;

2:Xgoto(IL_005b, 13);

4:V_1 := 1;

5:Xgoto(IL_0042, 9);

7:*APPLYwrite(*APPLYconcat(V_0*V_1, " "));
8:V_1 := V_1+1;

10:beq( !(V_1<=limit),IL_0027, 6)
12:V_0 := V_0+1;

14:beq( !(V_0<=limit),IL_0020, 3)
15:return 0;
0:limit := 5;

1:return 0;
0:return 0;

Verilog Output

The design was output as Verilog in two forms: high-level and gate-level.

The high-level Verilog RTL was output as follows:

module timetable1(clk, reset);
   input clk;
   input reset;
   reg [31:0] limit;
   reg [31:0] V_1;
   reg [31:0] V_0;
   reg pcnet105;
   reg [1:0] pcnet106;
 always @(posedge clk) begin if (!((pcnet106[1]|pcnet106[0]))) $display("%s%d", "Times Table Up To ", limit);
 always @(posedge clk) begin if (!((!(pcnet106[1])|pcnet106[0]))) $write("%d%s", (V_0*V_1), " ");
 always @(posedge clk) begin if (V_1==6) $display("");
 always @(posedge clk) begin V_1 <= ((!((pcnet106^2))&(V_1<=limit))) ? (V_1+1):((!((pcnet106^1))&(V_0<=limit))) ? 1:V_1;
   V_0 <= ((!((pcnet106^2))&!((V_1<=limit)))) ? (V_0+1):(!((pcnet106^0))) ? 1:V_0;
   pcnet106 <= ((!((pcnet106^2))&!((V_1<=limit)))) ? 1:((!((pcnet106^2))&(V_1<=limit))) ? 2:((!((pcnet106^1))&(V_0<=limit))) ? 2:(!((pcnet106^0))) ? 1:pcnet106;
 always @(posedge clk) begin limit <= (!((pcnet105^0))) ? 5:limit;
   pcnet105 <= pcnet105;

The low-level Verilog netlist is on this link: netlist.v.

Simulation Output

One of the Verilog forms was placed in a simple testbench that provides clock and reset. An RTL simulator was then run. The same output is generated when using either Verilog form, although a gate cell library must also be loaded for the netlist version. HPR L/S contains an internal simulator that operates on any of the intermediate forms generated by the Kiwi recipe and these all produce the same output as well.


module SIMSYS();
   reg clk, reset;
   initial begin reset = 1; clk = 0; # 4000 reset = 0; end
   always #1000 clk = !clk;
   initial #200000 $finish;
   timetable1 dut(clk, reset);

The following screen shot shows the nets being traced in the simulator:

The console output from the simulator was:

Times Table Up To 00
01 02 03 04 05 
02 04 06 08 10 
03 06 09 12 15 
04 08 12 16 20 
05 10 15 20 25 

Note that there are race conditions between the assignment to limit and its printing in the title banner and in the output of the newline at the end of each row and the last item on each row. But this was the first ever run from C# input code and we need to adopt some coding conventions and so on...

The race on the setting up of limit will be solved by causing the constructor code to run before entering the main body, instead of concurrently! Also, the reset input is not being used, but that's a small oversight as well.

31st October 2007.               UP.