Casting and Datatype Conversion - Casting and Datatype Conversion - 2025.1 English - UG1079

AI Engine Kernel and Graph Programming Guide (UG1079)

Document ID
UG1079
Release Date
2025-06-04
Version
2025.1 English

Casting functions (aie::vector_cast<DstT>(const Vec& v) and aie::vector.cast_to<DstT>()) allow value casting between vector types with the same size in bits. Accumulator vector types have the casting function aie::accum.cast_to<DstT>(). Generally, using the smallest data type possible reduces register spillage and improve performance. For example, if a 48-bit accumulator (acc48) can meet the design requirements then it is preferable to use that instead of a larger 80-bit accumulator (acc80).

Note: The acc80 vector data type occupies two neighboring 48-bit lanes.
aie::vector<int16,8> iv;
aie::vector<cint16,4> cv=iv.cast_to<cint16>();
aie::vector<cint16,4> cv2=aie::vector_cast<cint16>(iv);
aie::accum<cacc48,4> acc=aie::mul(cv,cv2);
aie::accum<acc64,4> acc2=acc.cast_to<acc64>();

Standard C++ casts can be also used. But the recommended ways of reading vectors from a buffer are as follows.

  • Use aie::load_v and increment the scalar pointer by the number of elements in the vector.
  • Using vector iterators.

Additional details about aie::load_v and iterators are covered in the following sections.

int16 coeff_buffer[16];

// cast to int32 and load
aie::vector<int32,8> coeff=aie::load_v<8>((int32*)coeff_buffer);

// create vector<int16,8> iterator 
auto it = aie::begin_vector<8>(coeff_buffer);

// read first vector<int16,8>
aie::vector<int16,8> vec0=*it++;

// read second vector<int16,8>
aie::vector<int16,8> vec1=*it;

The API supports floating-point to fixed-point (to_fixed()) and fixed-point to floating-point (to_float()) conversions. The conversion functions (to_float() and to_fixed()) can be handled by either the vector or scalar engines depending on the function called.

Note: The AI Engine floating-point is not completely compliant with the IEEE standards. For more information about the exceptions, see Versal Adaptive SoC AI Engine Architecture Manual (AM009).
int a=48;
float f1=aie::to_float(a); 

// first argument is the value of f1
// second argument is the position of input decimal point
float f2=aie::to_float(a,2); 

int b1=aie::to_fixed(f1);

// first argument is the value of f1
// second argument is the position of output decimal point
int b2=aie::to_fixed(f1,2);
aie::vector<float,32> fv;
aie::vector<int32,32> iv=aie::to_fixed<int32>(fv,2);

The vector engine offers two implementations of the to_fixed() functions: safe, which is the default, and fast. The safe implementation offers more strict data type checks. For the fast implementation, you can run the v++ -c --mode aie command with the --fastmath option, which lets to_fixed() choose the fast implementation.

The scalar engine offers three implementations for the to_fixed() function for floating point scalar operations:

Table 1. Scalar Floating-Point Operations Options
Compiler Option Description
--fastmath The to_fixed and floating-point comparison has two implementations. Fast to_fixed can have wrong results if the shift amount is greater than 1 while the safe version requires more cycles to complete. Fast floating-point comparison gives wrong results with +0 and -0 while the safe version is correct but takes more cycles.
--fast-floats Floating point scalar operations, like add, subtract, multiply, and compare, can either be mapped on vector floating point or on softfloat lib. By default, softfloat lib implementation is chosen (--fast-floats=false). This takes quite a few cycles because it is emulated, but the vector floating-point processor can be used at the same time.
--fast-nonlinearfloats Floating point non-linear scalar operations, like sine/cosine, sqrt, and inv, can either be mapped on scalar non-linear function or on runtime lib (math.c). By default, runtime lib implementation is chosen (--fast-nonlinearfloats=false) which takes quite a few cycles because it is emulated.

Data can be moved from vector to accumulator using aie::accum.from_vector() or from accumulator to vector using aie::accum.to_vector<DstT>() with shifting and rounding, as shown in the following example.

aie::vector<int16,16> v;
aie::accum<acc32,16> acc; 
acc.from_vector(v, 0);

aie::accum<acc32,16> acc2;
aie::vector<int16,16> v2; 
v2 = acc2.to_vector<int16>(15);
The API supports vector conversions between data types:
  • aie::pack: Returns a vector by converting each element into half number of bits.
  • aie::unpack: Returns a vector by converting each element into twice the number of bits.
Following is an example code of data conversion and its example output:
aie::vector<int16,16> data;
aie::print(data,true,"data=");
aie::vector<int8,16> data_smaller=data.pack();
aie::vector<int16,16> data_larger=data_smaller.unpack();
aie::print(data_smaller,true,"smaller data=");
aie::print(data_larger,true,"larger data=");
//Example output:
//data=0 1 2 -32768 -4 -5 -6 32767 3 4 126 130 -8 -9 -300 0 
//smaller data=0 1 2 0 -4 -5 -6 -1 3 4 126 -126 -8 -9 -44 0 
//larger data=0 1 2 0 -4 -5 -6 -1 3 4 126 -126 -8 -9 -44 0