next up previous contents index
Next: Synthesisable Language Subset Up: Kiwi kiwic Compiler Users' Previous: Kiwic Internal Operation   Contents   Index


C# Attributes

The Kiwic compiler understands various .NET assembly language custom attributes that the user has added to the source code. In this section we present the attributes available. These control thinks such as I/O net widths and assertions and to mark up I/O nets and embed assertions that control unwinding.

C# definitions of the attributes can be taken from the file support/attribute-definitions in the distribution.

The Kiwi attributes can be used by referencing their dll during the C# compiler.

  gmcs /target:library mytest.dll -r:attribute_definitions.dll

Many attributes are copied into the resulting .dll file by the gmcs compiler. Other code from such libraries is not copied and must be supplied separately to Kiwic. To do this, list the libraries along with the main executable on the Kiwic command line.

WARNING: THE ATTRIBUTE LIST IS CURRENTLY NOT STABLE AND THIS LIST IS NOT COMPLETE. For the most up-to-date listing, see hprls/kiwi/attribute_definitions.cs.

The C# language provides a mechanism for defining declarative tags, called attributes, that the programmer may place on certain entities in the source code to specify additional information. An attribute is specified by placing the name of the attribute, enclosed in square brackets, in front of the declaration of the entity to which it applies. We present design decisions regarding attributes that allow a C# program to be marked up for synthesis to hardware using the kiwic compiler that we are developing [#!greaves:singh:08!#]. This compiler accepts CIL (common intermediate language) output from either the .NET or Mono C# compilers and generates Verilog RTL.

Flag Unreachable Code

Kiwi.NeverReached("This code is not reached during kiwic compilation.");

This call can be inserted in user code to create an error if elaborated by kiwic. If a thread of control that is being expanded by kiwic encounters this call, it is an error.

Hard and Soft Clock Control

Many net-level hardware protocols are intolerant to clock dilation. In other words, their semantics are defined in terms of the number of clock cycles for which a condition holds. A thread being compiled by Kiwic defaults to soft pause control (or other default set in the recipe or command line), meaning that Kiwic is free to stall the progress of a thread at any point, such as when it needs to use extra clock cycles to overcome structural hazards. These two approaches are incompatible. Therefore, for a region of code where clock cycle allocation is important, Kiwic must be instructed to use hard pause control.

The Kiwi.Pause() primitive may be called without an argument, when it will pause according to the current pause control mode of the calling thread. It may also be called with the explict argument ` soft' or `hard'.

The current pause control mode of the current thread can be updated by calling `Kiwi.SetPauseControl'.

When a thread calls Kiwi.SetPauseControl(hardPauseControl) its subsequent actions will not be split over runtime clock cycles except at places where that thread makes explict calls to Kiwi.Pause() or makes a blocking primitive call.

The default shedulling mode for a thread can be restored by making the thread calls Kiwi.SetPauseControl(autoPauseControl).

A third mode for a thread, called soft, is under development. Kiwi.SetPauseControl(softPauseControl).

Finally, blockb pause control places a clock pause at every basic block and maximal pause control turns every statement into a separately-clocked operation Kiwi.SetPauseControl(maximalPauseControl).

Loop NoUnroll Manual Control

Put a call to `NoUnroll()' in the body of a loop that is NOT to be unrolled by kiwic.

If there is a `Kiwic.Pause()' in the loop, that's the default anyway, so the addition of a NoUnroll makes no difference.

The number of unwinding steps attempted by the CIL front end can be set with the `-cil-uwind-budget N' command line flag. This is different from the ubudget command line flag used by the FSM/RTL generation phase.

Because a subsume attribute cannot be placed on a local variable in C#, an alternative syntax based on dummy calls to Unroll is provided.

   public static void Unroll(int a)
    {  // Use these unroll functions to instruct kiwic to subsume a variable (or variables)
       // during compilation.  It should typically be used with loop variables:
       // for (int cpos = 0; cpos < height; cpos++)
       //   { Kiwi.Unroll(cpos);
       //     ...
       //   }
    public static void Unroll(int a, int b)
    { // To subsume annotate two variables at once.  

    public static void Unroll(int a, int b, int c)
    { // To annotate three variables.
      // To request subsumation of more than three variables note that
      // calling Unroll(v1, v2) is the same as Unroll(v1 + v2).  I.e. the
      // support of the expressions passed is flagged to be subsumed in total or
      // at least in the currently enclosing loop.

Elaborate/Subsume Manual Control

Kiwic implements an elaboration decision algorithm. It decides which variables to subsume at compile time and which to elaborate into concrete variables in the output RTL design.

The decisions it made can be examined by grepping for the word `decided' in the obj/h1.log file.

The algorithm sometimes makes the wrong decision. This is being improved on in future releases.

For variables that can take attributes in C# (i.e. not all variables), it can be forced one way or the other by instantiating one of the pair of attributes, Elaborate or Subsume.

For example, to force a variable to be elaborated, use:
bool empty = true;

Examples of variables that cannot be attributed is the implied index variable used in a foreach loop, or the explicit local defined inside a for loop using the for (int i=...;... ; ...) syntax.

The force of an elab can also be made using the -fecontrol command line option. For instance, one might put -fecontrol 'elab=var1;elab=var2';

Offchip or Output Memory Array Mapping

The OutboardArray attribute indicates that an array is to be mapped to a region of external memory instead of being allocated a private array inside the current compilation.

Hardware Server

The Server attribute indicates that a method and the methods it calls in turn are to be allocated to a separate RTL module that is instantiated once and shared over all calling threads.

Register Widths and Wrapping

Integer variables of width 1, 8, 16, 32 and 64 bits are native in C# and CIL but hardware designers frequently use other widths. We support declaration of registers with width up to 64 bits that are not a native width using an `HwWidth' attribute. For example, a five-bit register is defined as follows.
\begin{lstlisting}[Kiwi.HwWidth(5)]static byte fivebits;
When running the generated C# natively as a software program (as opposed to compiling to hardware), the width attribute is ignored and wrapping behaviour is governed by the underlying type, which in the example is a byte. We took this approach, rather than implementing a genuine implementation of specific-precision arithmetic by overloading every operator, as done in OSCI SystemC [#!bruschi:03!#], because it results in much more efficient simulation, i.e. when the C# program is run natively.

Although differences between simulation and synthesis can arise, we expect static analysis in kiwic to report the vast majority of differences likely to be encountered in practice. Current development of kiwic is addressing finding the reachable state space, not only so that these warnings can be generated, but also so that efficient output RTL can be generated, such that tests that always hold (or always fail) in the reachable state space are eliminated from the code.

The following code produces a kiwic compile-time error because the wrapping behaviour in hardware and software is different.

\begin{lstlisting}[Kiwi.HwWidth(5)]byte fivebits;
void f()
fivebits = (byte)(fivebits + 1);

The cast of the rhs to a byte is needed by normal C# semantics.

Compiling this example gives an error:
kiwic assign wrap error:
(widthclocks_fivebits{storage=8 }+1)&mask(7..0):
assign wrap condition test rw=8, lw=5, sw=8

Input and Output Ports

Input and Output Ports can arise and be defined in a number of ways.

I/O ports are inferred from static variables in top-most class being compiled. The following two examples show input and output port declarations, where the input and output have their width specified by the underlying type and by attribute, respectively.
\begin{lstlisting}[Kiwi.InputPort(''serin'')]static bool serialin;
[Kiwi.HwWidth(5)] [Kiwi.OutputPort(''data_out'')] static byte out5;
The contents of the string are a friendly name used in output files.

For designers used to the VDHL concept of a bit vector, we also allow arrays of bools to be designated as I/O ports. This can generate more efficient circuits when a lot of bitwise operations are performed on an I/O port.
\begin{lstlisting}[Kiwi.OutputWordPort(11, 0, ''dvi_d'')]public static int[] dvi...
...rt(11, 0, ''dvi_i'')] public static int[] dvi_i = new int [12];
Although it makes sense to denote bitwise outputs using booleans, this may require castings, so ints are also allowed, but only the least significant bit will be an I/O port in Verilog output forms.

Currently we are extending the associated kiwi library so that abstract data types can be used as ports, containing a mixture of data and control wires of various directions. Rather than the final direction attribute being added to each individual net of the port, we expect to instantiate the same abstract datatype on both the master and slave sides of the interface and use a master attribute, such as `forwards' or `reverse', to determine the detailed signal directions for the complete instance.

The following examples work

     //  four bit input port
    [Kiwi.InputPort("")] static byte din;

    // six bit local var
    [Kiwi.HwWidth(6)] static int j = 0;

A short-cut form for declaring input and output ports

public static int result;
[Kiwi.OutputWordPort(31, 0)]
public static int bitvec_result;

Clock Domains

A synchronous subsystem designed with kiwi requires a master clock and reset input. The allocation of work to clock cycles in the generated hardware is controlled by an unwind budget described in [#!greaves:singh:08!#] and the user's call to built-in functions such as `Kiwi.Pause'. By default, one clock domain is used and default net names clock and reset are automatically generated. To change the default names, or when more than one clock domain is used, the `ClockDom' attribute is used to mark up a method, giving the clock and reset nets to be used for activity generated by the process loop of that method.
\begin{lstlisting}[Kiwi.ClockDom(''clknet1'', ''resetnet1'')]
public static void Work1()
{ while(true) { ... } }
A method with one clock domain annotation must not call directly, or indirectly, a method with a differing such annotation.


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.
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.

Universal assertions about a design can be expressed with a combination of a predicate method (i.e. one that returns a bool) and a temporal logic quantifier embedded in an attribute. For instance, to assert that whenever the following method is called, it will return true, one can put
\begin{lstlisting}[Kiwi.AssertCTL(''AG'', ''pred1 failed'')]
public bool pred1()
{ return (... ); }
where the string AG is a computational tree logic (CTL) universal path quantifier and the second argument is a message that can be printed should the assertion be violated. Although the function `pred1' is not called by any C# code, kiwic generates an RTL monitor for the condition and Verilog $display statements are executed should the assertion be violated. In order to nest one CTL quantifier in another, the code of the former can simply call the latter's method. Since this is rather cumbersome for the commonly used AX and EX quantifiers that denote behaviour in the next state, an alternative designation is provided by passing the predicate to a function called `'. A second argument is an optional number of cycles to wait, defaulting to one if not given. Other temporal shorthands are provided by `Kiwi.rose', `Kiwi.fell', `Kiwi.prev', `Kiwi.until' and `Kiwi.wunitl'. These all have the same meaning as in PSL.

We are currently exploring the use of assertions to describe the complete protocol of an I/O port. Such a description, when compiled to a monitor, serves as an interface automaton. To automatically synthesise glue logic between I/O ports, the method of [#!passerone02convertibility!#] can be used, which implements all non-blocking paths through the product of a pair of such interface automata.

next up previous contents index
Next: Synthesisable Language Subset Up: Kiwi kiwic Compiler Users' Previous: Kiwic Internal Operation   Contents   Index
David Greaves 2011-03-31