Orangepath/HPR Logic Synthesis Project: Hardware and Embedded Software Synthesis from Executable Specifications.
Compilation from .net CIL Bytecode (second example)

Kiwi: Synthesis from .net CIL Bytecode: Simple RPC Call

In this example, the following C# program was compiled to .net CIL bytecode. The program has a second entry point that is callable from a separate compilation unit.

Object-oriented software sends threads between compilation units to perform actions. Synthesisable Verilog and VHDL do not allow threads to be passed between separately compiled circuits: instead, additional I/O ports must be added to each circuit and then wired together at the top level. Accordingly, we mark up methods that are to be called from separate compilations with a remote attribute.

  [Kiwi.Remote("parallel:four-phase")]
  public return_type entry_point(int a1, bool a2, ...)
  { ... }

When an implemented or up-called method is marked as `Remote', a protocol is given and kiwic generates additional I/O terminals on the generated RTL that implement a stub for the call. The currently implemented protocol is asynchronous, using a four-phase handshake and a wide bus that carries all of the arguments in parallel. Another bus, of the reverse direction, conveys the result where non-void. Further protocols can be added to the compiler in future, but we would like to instead lift them so they can be specified with assertions in C# itself.

Four-phase Handshake

Reference material regarding the four-phase handshake protocol is described HERE.

C# Source Code: Remote Procedure Call

The C# code has both a 'Main' entry point that is mentioned on the kiwic command line and a secondary entry point, defined by the Kiwi.Remote attribute, that is compiled by kiwic so as to be invoked by a separately-compiled hardware component. We can also use this style of interface when calling from software on a soft-core to hardware generated by kiwic.

//
// Remote procedure call test (H/W port generation).
//
class test10
{
    static int limit = 10;
    static int jvar;

    [ Kiwi.Remote("client1-port", "parallel: four-phase") ]
    public static int bumper(int delta)
    {
        jvar += delta;
	return jvar;
    }

    public static void Main()
    {
 	Console.WriteLine("Test 10 Limit=" + limit);
   	for (jvar=1;jvar<=limit;jvar+=2) 
	{
	  Console.Write(jvar + " ");
	  Kiwi.Pause();
        }  
     	Console.WriteLine(" Test 10 finished.");
    }
}

A problem with the above code is a structural hazard that leads to a possible race when the variable jvar is potentially updated twice on the same clock cycle: once by the Main method and once by the remote invoker.

Kiwic detects the race problem, giving the following cryptic warning and will chose an arbitrary resolution:

VNL.v_1 incompatible assigns: consistency check
  lhs=pcnet105  g=true
  v=1:pcIS1:AG
 v'=4:pcIS4:AG

Output code

Kiwic will generate hardware both for the client and the server as separate RTL files. In more-realistic examples, there will be multiple files, with one being the top-level that contains client calls to some of the others which in turn make client calls to others, with the leaf modules in the design heirarchy being servers only.

One can also envision leaf modules in the design heirarchy making upcalls to parents, but this is not currently implemented in Kiwi.

For the above example, the generated server RTL looks as follows:

// CBG Orangepath HPRLS System
// Verilog output file generated at Thu Sep 18 23:43:12 BST 2008
// Kiwic: HPR Orange IL/.net front end: Version alpha 21: 16-Sep-08
// -root test10 -vnl VNL.v -preserve-sequencer 1 
module VNL(reset, clk, bumper_delta, bumper_ack, bumper_req, bumper_retval);
  input reset;
  input clk;
  input bumper_delta;
  output bumper_ack;
  input bumper_req;
  output bumper_retval;
  reg [1:0] testtest10pc;
  reg [2:0] testtest11pc;
  reg bumper_ack;
  integer test10_jvar;
  integer test10_limit;
   always @(posedge clk) begin //Start HPR test10_test13
          if (reset) testtest10pc <= 0;
           else 
          case (testtest10pc)
          
          0:  begin testtest10pc <= 1;
                 test10_jvar <= 1;
                 $display("%s%d", "Test 10 Limit=", 10);
                 
                 $write("%d%s", 1, " ");
                 
                 
                  end 
          1:  begin if (9<=test10_jvar) testtest10pc <= 2;
                 if (test10_jvar<9) test10_jvar <= test10_jvar+2;
                 if (9<=test10_jvar) 
                   begin test10_jvar <= test10_jvar+2;
                      $display(" Test 10 finished.");
                      
                      
                       end if (test10_jvar<9) $write("%d%s", test10_jvar+2, " ");
                 
                 
                  end 
          2:  begin 
                  end endcase
          //End HPR test10_test13


          //Start HPR test10_test12
          if (reset) testtest11pc <= 0;
           else 
          case (testtest11pc)
          
          0: testtest11pc <= 1;
          
          1: if (!bumper_req) testtest11pc <= 2;
          
          2:  begin testtest11pc <= 3;
                 bumper_ack <= 0;
                 
                  end 
          3: if (bumper_req) testtest11pc <= 4;
          
          4:  begin testtest11pc <= 5;
                 test10_jvar <= test10_jvar+bumper_delta;
                 
                  end 
          5:  begin 
                  end endcase
          //End HPR test10_test12


           end 
          // Start delx test10_test13
  // End delx test10_test13


  // Start delx test10_test12
  // End delx test10_test12


  endmodule

// eof (hprls verilog)

Variation 1: Using an Explicit Mutex

To overcome the race condition, we can add explict mutex variables to the code.

Each update to the shared variable must be guarded. Here is one of the access sites augmented with explicit synchronisation primitives:

  [ Kiwi.Remote("client1-port", "parallel: four-phase") ]
    public static int bumper(int delta)
    {
        while(!mutex1) hpr_testandset(ref mutex1, 1);
        jvar += delta;
        hpr_testandset(ref mutex1, 0);
        return jvar;
    }

Variation 2: Using additional Kiwic Directives

Rather than adding explict mutexes, the system designer might feel he knows whether or not the race condition will arise in reality. For instance, he might know that when the RPC is exercised the clock will be mid-cycle or stopped. Therefore, we allow additional markup attributes to allow the designer to enforce a compile-time scheduling decision, making the stub effectively run before or after the Main routine.

This will be illustrated here ....

Simulation Test Bench

THIS PAGE UNDER CONSTRUCTION.!!

Results

THIS PAGE UNDER CONSTRUCTION.!!


(C) DJ Greaves and S Singh, April 2008.               UP.