# Arx Data Types

## Scalar Data Types

The basic data types of Arx can be subdivided in scalar and vector data types.

The scalar types are given in the table below:

Data type Explanation
`bit` The data type for binary signals. Possible values are `0` and `1`
`boolean` A data type with possible values `true` and `false`
`integer` The integer as found in most common programming languages.
`real` Floating-point number; becomes `float` in C

Remark: Arx code that uses the `real` data type, cannot be converted into VHDL!

Integer constants can be specified in decimal, hexadecimal or binary notation. A hexadecimal constant starts with `0h` and may use the characters 'a' to 'f' in either upper or lower case. A binary constant starts with `0b`. Hexadecimal and binary constants always designate a positive integer, unless preceded by a minus sign. Real constants can be used to specify fixed-point constants. This may lead to loss of precision as a consequence of quantization and overflow processing.

The code fragement below shows some examples of the use of constants.

```register
# three registers initialized with the same value
bval1: bitvector(8) = 0b10101010
bval2: bitvector(8) = 0haa
bval3: bitvector(8) = 170

# more examples of constants
bval4: unsigned(8)   = 0haa
bval5: unsigned(8,2) = 1.75 # no loss of precision
bval6: signed(8,2)   = -1.5 # no loss of precision
bval7: signed(8,4)   = 3.14 # will be converted to 3.125 = 50/16```

## Vector Data Types

The vector types all have in common that they consist of a sequence of bits. They differ in the interpretation of these bits. The vector types are listed below:

Data type Explanation
`bitvector`(<n>) A vector of <n> bits
`unsigned`(<n>) An <n>-bit vector, interpreted as an unsigned integer
`signed`(<n>) An <n>-bit vector, interpreted as a signed two's complement integer
`unsigned`(<n>, <m>) An <n>-bit vector, interpreted as an unsigned fixed-point number with <m> integer bits
`signed`(<n>, <m>) An <n>-bit vector, interpreted as a signed fixed-point number with <m> integer bits

The index that addresses individual positions in a vector type, is always in the range 0 to <n>–1. Square brackets are used in the syntax for indexing.

Two indices separated by a colon and enclosed between square brackets select a subrange or slice of a bitvector. As opposed to other HDLs the left index should always be smaller or equal to the right one (when both indices are equal a single bit is selected). A subrange can be used both at the left as well as the right-hand side of an assignment. The example below illustrates the addressing of bits in a vector.

```component top
word_length : generic integer = 8
T_IO        : generic type = bitvector(word_length)
data_in     : in T_IO
data_out    : out T_IO

register
storage: T_IO = 0

begin
storage = storage[word_length-1]
storage[1:word_length-1] = data_in[0:word_length-2]
data_out = storage
end```

## Quantization and Overflow in Fixed-Point Data Types

Consider the case that a signal carrying a fixed-point value is wired to another signal with fewer bits. In Arx, this amounts to a fixed-point signal being assigned to another fixed-point signal (variable or register). In such an assignment the binary points of the fixed-point patterns are always aligned. If the signal at the left-hand side of the assignment has fewer bits at the least-significant side of the bit pattern, quantization is necessary. If there are fewer bits available at the most-significant side, overflow occurs.

One can deal in many ways with overflow and quantization. The desired behavior should be indicacted as part of the fixed-point type declaration with two optional parameters, respectively `<o-mode>` and `<q-mode>`:

```unsigned(<n>, <m>, <o-mode>, <q-mode>)
signed(<n>, <m>, <o-mode>, <q-mode>)```

The following quantization modes exist:

Quantization mode Explanation
`trunc` Truncate (default).
`round` Round to the closest value; break ties by going up.
`round_zero` As `round`, but break ties by going towards zero.
`round_inf` As `round`, but break ties by going away from zero.

The following overflow modes exist:

Overflow mode Explanation
`wrap` Wrap around (default).
`sat` Saturate to the extreme values.
`sat_sym` Saturate symmetrical, the minimal extreme value is the negative of the positive extreme value.

## Enumeration Data Type

As in many other languages for hardware description or programming, Arx allows the definition of new data types with a finite number of values. The possible values are identifiers that are enumerated at the time of the declaration of the so-called enumeration data type.

Example:

```type

The value of an enumerated type is indicated by preceding the declared value by the type name and a dot. Example:

```# a registered signal of type input_state with its reset value
register
current_state: input_state = input_state.start

# later on in the code
begin
if current_state == input_state.start
current_state = input_state.processing
end```

## Arrays

Arx supports 1-dimensional arrays. The array elements can be any of the basic types mentioned above as well as enumeration data types. Arrays declarations contain a <size> enclosed in square brackets, which gives the number of elements that the array will have. An array index runs from 0 to <size> – 1. Individual array elements are selected by an index enclosed in square brackets. Initial values of registers of an array type should be provided in a list delimited by curly braces and separated by commas in order of increasing index. When a single value is provided, this value is used as the initial value of all vector elements.

When the base type of an array is a vector, an individual bits or a bit slice can be selected in a similar way as a single vector. In the syntax, there are two pairs of indices enclosed in square brackets. The first one selects the array element, the second the bits within the vector. The code below, shows an example:

The following code fragment illustrates the use of arrays:

```component top
T_IO        : generic type = signed(10, 5, sat, round)
data_in     : in T_IO
data_out    : out T_IO

type
T_enum: enum(one, two, three)
T_ar1: array of T_IO
T_ar2: array of T_enum

register
v1 : T_ar1 = 0
v2 : T_ar2 = {T_enum.three, T_enum.two, T_enum.one}
v3 : array of T_IO = {5, 4, 3, 2, 1}

begin
v1 = data_in
for i in 0:1
v2[i] = v2[i+1]
end
# example of accessing individual bits in an array of vectors
v3[0:4] = v1[5:9]
v3[5:9] = v1[0:4]
# rest of description left out```

## Explicit Type Conversion

In a situations where a signal of some type is wired to one of another type, Arx is able to check whether the type conversion is possible and insert overflow and quantization logic if necessary (see also above). There exists, however, situations where one would like to impose a type to an intermediate result in an expression. In such a case the Arx function `convert` can be used.

The example below illustrates the use of `convert`; without it, the addition would be performed at the resolution of the widest operand instead of the narrowest one.

convert-example.arx
```component top
T_IO        : generic type = signed(10, 5)
data_in     : in T_IO
data_out    : out T_IO

type
T_narrow: signed(6, 4, sat, round)

register
storage: T_narrow = 0

begin
storage = storage + convert(T_narrow, data_in)
data_out = storage
end```

## Reinterpretation

A sequence of bits can be interpreted in many ways. There are situations in which the interpretation of the same pattern changes across the hardware. The hardware itself does not change but the type change needs to be made explicit in the hardware description. For these situations, Arx uses the function `reinterpret`.

The example below illustrates the use of reinterpretation. The input to the hardware consists of a bit vector composed of two signed numbers. In the description, the two numbers are first extracted, added together, and the numerical result is then interpreted again as a bit vector.

reinterpret-example.arx
```component top
word_length : generic integer = 8
T_in        : generic type = bitvector(2*word_length)
T_out       : generic type = bitvector(word_length+1)
data_in     : in T_in
data_out    : out T_out

type
T_num   : signed(word_length)
T_num_p1: signed(word_length+1)

register
storage: T_out = 0

variable
left, right: T_num
sum: T_num_p1

begin
left  = reinterpret(T_num, data_in[0:word_length-1])
right = reinterpret(T_num, data_in[word_length:2*word_length-1])
sum = left + right
storage = reinterpret(T_out, sum)
data_out = storage
end``` 