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.

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

Current omissions and restrictions

There are currently (in 2019.11) a number of significant language limitations:

  • Arrays cannot be dynamically sized, and 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
  • Function parameter pass-by-reference is not supported if the function is time-consuming (in other words, if it contains wait or drive statements)

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
#else
#include <stdio.h>
#include <assert.h>
#endif

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