Maia and C

The 'base' language (in other words, everything which is not specifically related to accessing the DUT, concurrency, and advancing time) looks very much like C. A program consists of a sequence of external declarations, which are either declarations or function definitions. The program has a 'main' entry point. Maia provides the standard if, else, for, while, do while, switch, break, and continue statements (although it adds for all, and extends break and continue). The expression syntax is almost identical to C, although Maia adds a number of operators. Scoping is, for all practical purposes, identical to C scoping.

There are, however, a number of additions to handle hardware descriptions, and a number of simplifications to remove complexity which isn't required. The differences between the base language and C are detailed in the sections below. The main simplifications are as follows:

  • An entire program must be compiled at the same time, as a single compilation unit (in the same way as Verilog or Pascal, for example); there is no provision to link separately-compiled units
  • There are no pointers. However, parameters (including HDL signals) can be passed by reference to functions (in the same way as C++)
  • There is no standard library, although a small number of functions (rand, exit, and so on) are built into the language

The primary difference between Maia and C is in the handling of 'data' types. Maia has three basic data types:

  • int is a 2-state signed 'small' integer (it defaults to 32 bits). This type is of little value in hardware description or verification, but is useful for general software 'housekeeping': loop indexing, counting, and so on
  • bitn is a 2-state data type; the n denotes the number of bits in the type (a bit192, for example, is a 192-bit 2-state type)
  • varn is a 4-state data type; the n denotes the number of bits in the type. The 4 states are the same as the Verilog states: 0, 1, X, and Z

These types handle all integer and floating-point operations (C has 12 integer and floating-point types for C89, and 14 for C99). In Maia, complexity is provided by operators, and not types. This is essentially the opposite of modern general-purpose programming languages. The reason is simply that Maia is not a general-purpose language: it is intended for modelling and verifying hardware, and 'data', or 'objects', are not primary concerns in hardware, as they are in general-purpose languages.

Most of the remaining differences within the base language subset can be summarised as follows:

  • Typedefs, enumerations, unions, and bit-fields are not supported
  • Maia does not (with one minor exception) require external objects to be declared before they are used; a compiler pre-pass collates information on type definitions, functions, and object declarations
  • Left-to-right evaluation is guaranteed (like Java and C#, for example, and unlike C, which instead uses the concept of 'sequence points')
  • Functions may return arrays
  • Operations on arrays are supported, and arrays may be assigned to
  • break and continue statements are followed by an optional integer, which specifies the number of levels to break or continue
  • Both C and Maia initialise objects with static linkage (globals and statics). Maia additionally initialises automatic objects. Initialisation of aggregates (arrays and structures) is essentially identical to C89; named initialisation of structure members (introduced in C99) is not supported
  • Arrays are declared using a Java-style syntax, which is a superset of C syntax. In C, an integer array might be declared as int foo[4][3], for example. This style is supported, but the style int[4][3] foo is preferred (and is the only way to declare functions which return arrays)
  • Multi-dimensional arrays may be declared and accessed using either an Algol-style or a C-style syntax. The array int[4][3] foo, for example, may equivalently be declared as int[4,3] foo. The expressions foo[0][0] and foo[0,0] both access the first element of this array
  • Recursive function calls are not supported. This is primarily a limitation of the Verilog code generator
  • All non-void functions have a predefined variable named result, which is initialised on function entry (to 0 for functions which return 2-state data, or all X's for 4-state data). The current value of this variable is returned if the function 'falls off the end', or if a return statement has no expression
  • The type-checking strength is set by a pragma, and defaults to a level which is broadly C-like. The value of the pragma can be decreased (which allows, among other things, implicitly-declared variables), or increased, to strengthen type checking
  • Maia uses dynamic (run-time) type checking, as well as static (compile-time) type checking. Array indexes, for example, are checked for validity during execution.

Things the language doesn't do

Maia is a lightweight language. It doesn't do, for example:

  • Dynamically-sized arrays; there are no built-in types with dynamic sizing (lists, sets, and so on)
  • There is no string type. Strings are used in the report statement (which is equivalent to C's printf), but it is not possible to manipulate strings
  • Floating-point arithmetic on anything other than double-precision (at run-time, anyway; the compiler itself can handle single, double, and extended double operations for constant expressions)
  • Recursive function calls

This might be a feature, or a limitation, depending on your point of view. However, it's fundamentally because Maia generates only Verilog code, and there are various things that you simply can't do in plain Verilog, without resorting to the PLI and writing C code. You can do this yourself, if you need to (Maia has a foreign language interface, which can be used to call Verilog tasks). The compiler might at some point in the future do this directly to handle limitations, but the design philosophy at the moment is to keep it simple, and to make sure that the compiler output is easy to run on multiple simulators, without making making life too difficult for the user.

Running C programs

Many simple C programs can be run directly by rtv, and simple Maia programs will also compile as C. This is not of any practical value, but might be helpful when carrying out simple sanity checking (in fact, many of the Maia development regressions do in fact run C code, with the output compared against a C compiler).

The preprocessor can be used to handle specific differences between the languages. If, for example, the C code uses only the printf and assert library routines, and uses only the int data type, and does not use the various unsupported constructs discussed above (pointers, enums, and so on), then it can be converted to valid Maia code by adding this at the top of the file:

#ifdef __MAIA__
#define printf report
#include <stdio.h>
#include <assert.h>

C code can be run by rtv with the --input switch (rtv --input test.c).