THDL++ tutorial - operators and statements

<<Previous Next >>

Overview

This part of the tutorial lists THDL++ operators and statements - everything you need to write process bodies. If you want to skip formal definitions, just scroll to the end of the page to see a code example.

THDL++ operators

THDL++ operators are very similar to C/C++. The operator precedence is similar to C++ operator precedence:

Precedence Operators Associativity
1 Function call, array access, template resolution  
2 Scope resolution (.), ++, -- Left to Right
3 Unary !, ~ and - Left to Right
4 *, /, % Left to Right
5 +,-, vector concatenation (cat keyword) Left to Right
6 lt, le, gt, ge Left to Right
7 Equals (=), Not equals (!=) Left to Right
8 Bitwise AND (&) Left to Right
9 Bitwise XOR (^) Left to Right
10 Bitwise OR (|) Left to Right
11 Logical AND (&&) Left to Right
12 Logical OR (||) Left to Right
13 Assignment operators: =, +=, -=, *=, /=, %=, &=, |=, ^= Right to Left
14 Array range operator ("to" keyword) Left to Right
15 Comma (,) Left to Right

Important note: to avoid ambiguity with template resolution operator (<>) and VHDL-style signal assignment operator (<=), the 'less than', 'greater than', 'less or equals' and 'greater or equals' operators are 'lt', 'gt', 'le' and 'ge' respectively.

Constants

THDL++ supports integral, boolean, logic, time and string constants:

  • Integral constants can be defined using either a decimal (e.g. 15), hexadecimal (e.g. 0xF), or binary (e.g. 0b1111) system. You can safely assign integral values to vectors (e.g. logic[8] instruction = 0x12), THDL++ compiler will automatically convert them into VHDL vector declarations.

  • Boolean constants are equivalent to C++: true and false.

  • Logic constants can be '0', '1', 'X', 'W' and 'Z' matching VHDL constants.

  • Time constants can are defined as {number}{unit prefix}s. E.g.: 100ms or 1ns.

Type conversion

TThe THDL++ compiler automatically converts between integers and logic vectors, as well as between boolean and logic values. Moreover, assigning a logic value (e.g. '1') to a logic vector is equivalent to VHDL "others => '1'" construct. If you want to convert a type explicitly, use the {type}({value}) syntax. E.g.: logic[8] logic_val = logic[8](int_val);

Lists

THDL++ supports compile-time lists. The use of lists makes several operations more compact (see example in the end of this part). To declare a list, use the following syntax:

const list ListName = (val1, val2[, val3, ...]);

Lists can be used in foreach() statements and allow efficiently separating algorithms and data.

Statements

THDL++ supports if(), for() and switch() statements having C++ syntax and adds foreach() statement borrowing syntax from C# and select() statement derived from the switch() one.

Conditional statement (if)

The syntax is identical to C++ syntax:

if (Condition)
    single_statement;
else if (OtherCondition)
    another_statement;
else
    something_else

If you want to use multiple statements instead of single statements, use curly brackets ({}), just like in C.

Note: normally, an if statement will be compiled into an equivalent VHDL if statement. If you want to ensure, that the Condition is a constant known during compilation time, use the keyword ifc (if at compile-time) instead of if. In that case, depending on the Condition value, the contents of 'ifc' block will either be compiled into unconditional VHDL, or skipped completely. If an ifc confition is not a compile-time constant, the THDL++ compiler will report an error.

Loop statement (for)

The for statements in THDL++ are identical to C++:

for (initialization;condition;counter)
    single_statement;

Like in C++, you can declare variables inside for loop initialization statements. Example:

for (int i = 0; i lt 10; i++)
{
    some_function(i);
    something_else(-i);
}

Note: the for statements are ALWAYS unrolled while compiling to VHDL. If the for() statement condition depends on non-constants (e.g. signal values), the compiler will report an error.

Foreach loops

The syntax is similar to C#:

foreach(any IteratorName in List)
    foreach_loop_body;

Foreach loops are very powerful, as they can iterate constants, types, classes, entity instances and many other things.

Switch statements

The syntax matches the C syntax:

switch (expression)
{
    case value_1:
    case value_2:
        some_action();
        break;
    case value_3:
        something_else();
    break;
    default:
        default_action();
}

Select statements

Select() is a short form of switch() practically usable to describe multiplexers:

signal_or_variable = select(expression)
{
    case value_1: result_1;
    case value_2: result_2:
    default: result_3;
}

Conditional generation statements

THDL++ has an equivalent of the 'generate' statement from VHDL. The syntax is the following:

generate block_name if(condition)
{
    block_contents;    //Signals, ports, whatever an entity can have
}
else
{
    //Something else
}

The for/foreach form is:

generate another_block_name for/foreach(...)
{
    block_contents;    //Signals, ports, whatever an entity can have
}

Declaring functions

The function declaration syntax is completely equivalent to C syntax:

returned_type  function_name(type_1 arg_1, [type_2 arg_2, ...])
{
    function_body;
    return returned_value;
}

Built-in functions

THDL++ has the following built-in functions:

Function Description Example
sizeof() Returns a size of a vector or a vector variable/signal port out logic[sizeof(ArrayInput)] ArrayOutput;
typeof() Returns a type of a vector variable/signal signal typeof(SignalA) SignalB;
log2() Returns ceil(log2(argument)) signal logic[log2(sizeof(Array))] ArrayAddress;
pow2() Equivalent to (1 << argument) in C++. bool MSB = (value & pow2(BitCount)) != 0;
min()/max() Returns the minimum/maximum value from a list of constants const int BusSize = min(MinBusSize, comp1.MinBusSize, comp2.MinBusSize);
extend_sign() Performs a sign extension on a given value DataType val = extend_sign(SignedInput, sizeof(DataType));
extend_zero() Performs an unsigned extension of a value DataType val = extend_zero(UnsignedInput, sizeof(DataType));
__defined() Returns true if a given identifier is defined if (__defined(Configuration.AdditionalBusWidth)) {...}
__error() Generates a compilation error with a given text ifc (sizeof(a) != sizeof(b)) __error("Argument size mismatch");
ite If-the-else operator logic[2] encodedValue = ite(inputVal, 0b10, 0b01)l
__equal_obj Returns true if 2 given objects (classes/entities) are equal See code samples

The example

Let's combine the main of the features mentioned in this chapter into a single example.

entity DemoEntity
{
    const int ValueSize = 16;
    typedef logic[ValueSize] ValueType;

    port in logic InputClock;
    signal ValueType Val1 = 0, Val2 = 0, Val3, Val4;

    process main (InputClock.rising)
    {
        Val1++;
        Val2 += 2;
    }

    Val3 = Val2 - Val1;

    ValueType SubtractIfGreater(ValueType x, int limit, int bias)
    {
        if (x gt limit)
        x -= bias;
        return x;
    }

    process ComputeVal4 (any)
    {
        typeof(Val4) tempValue = SubtractIfGreater(Val1, 5, 10);
        tempValue++;
        Val4 = tempValue;
    }
}

entity ClockTest
{
    DemoEntity demo(
        InputClock = auto,
    );

    process GenerateClock ()
    {
    demo.InputClock = '0';
    wait(10ns);
    demo.InputClock = '1';
    wait(10ns);
    }
}

simulate_entity(ClockTest);

 

<<Previous Next >>