Control flow

Control statements in Maia are identical to those in C, with some minor exceptions. Maia has the standard if, else, while, do while, for, switch, break, and continue statements. break and continue statements may optionally be followed by an integer, to specify how many levels to break or continue. The level defaults to 1 if it is omitted, which gives the same behaviour as C. Java has a break mechanism which breaks to a named label, although a counted break is arguably more useful.

Some parentheses in the control statements may also be omitted if the omission does not lead to ambiguity. If in doubt, full C-style parentheses can always be used. Deciding whether or not a statement is ambiguous can be difficult; the full set of rules is as follows:

  • The parentheses around the switch selection expression, and the do-while condition, are always optional
  • Parentheses are always required for a for statement
  • For the remaining cases (the if and while conditions) the parentheses may be omitted if the following statement is not an expression statement, or a drive statement

'for all' loops

The standard looping constructs can be cumbersome when applying all possible values of a 'small' object to a DUT. If a DUT has a 5-bit input port, for example, then it might appear that this code would be a suitable way to drive all possible values to that port:

bit5 data;                    // 5-bit data
for(data=0; data<32; data++)
   [data] -> [...];           // drive the input port, test the output against something

However, this won't work: since data is a 5-bit variable, its value will always be less than 32, and this is is simply an infinite loop. One simple way to fix this is to use a plain int as the loop index:

bit5 data;                    // 5-bit data
for(int i=0; i<32; i++) {
   [data] -> [...];           // drive the input port, test the output against something
   data++;
}

Since an int is signed, an int index can also be used for testing against negative or positive termination conditions.

The for all construct may alternatively be used to handle this situation. for all data sets data to 0, and then increments through all values of data. On completion, data will again have a value of 0:

  bit5 data;
  for all data
     [data] -> [...];

The loop variable may be of any scalar data type (int, bit, var, ubit, or uvar, bool, or a mode 1 stream). The semantics of the for all statement, when applied to a stream, are identical to its semantics when applied to a data object. An assignment to a mode 1 stream sets the current position (line number) in that stream; assigning 0 to the stream rewinds it to its first line. This (complete) program therefore reads and displays every line of file 'des_vpkat.dat', after stripping out any comments and superfluous whitespace:

stream {
   mode   1;
   file   "vectors/des_vpkat.dat";
   format "%i %64'h %64'h", round, plain, cipher;
} vpkat;

main() {
   for all vpkat
      report("round %d: plaintext %x; ciphertext %x\n", vpkat.round, vpkat.plain, vpkat.cipher);
}

Tutorial exercises 14 and 16 contain examples of the use of for all. A word of warning, however: if you attempt to iterate over all values of a 32-bit variable, for example, then your program will never complete. The for all construct should only be used for 'small' variables (those with a bit width up to perhaps 16 or 20 bits).