pragma HLS data_pack

Description

Packs the data fields of a struct into a single scalar with a wider word width.

The DATA_PACK pragma is used for packing all the elements of a struct into a single wide vector to reduce the memory required for the variable, while allowing all members of the struct to be read and written to simultaneously. The bit alignment of the resulting new wide-word can be inferred from the declaration order of the struct fields. The first field takes the LSB of the vector, and the final element of the struct is aligned with the MSB of the vector.

If the struct contains arrays, the DATA_PACK pragma performs a similar operation as the ARRAY_RESHAPE pragma and combines the reshaped array with the other elements in the struct. Any arrays declared inside the struct are completely partitioned and reshaped into a wide scalar and packed with other scalar fields. However, a struct cannot be optimized with DATA_PACK and ARRAY_PARTITION or ARRAY_RESHAPE, as those pragmas are mutually exclusive.

IMPORTANT!: You should exercise some caution when using the DATA_PACK optimization on struct objects with large arrays. If an array has 4096 elements of type int, this will result in a vector (and port) of width 4096*32=131072 bits. Vivado HLS can create this RTL design, however it is very unlikely logic synthesis will be able to route this during the FPGA implementation.

In general, Xilinx recommends that you use arbitrary precision (or bit-accurate) data types. Standard C types are based on 8-bit boundaries (8-bit, 16-bit, 32-bit, 64-bit), however, using arbitrary precision data types in a design lets you specify the exact bit-sizes in the C code prior to synthesis. The bit-accurate widths result in hardware operators that are smaller and faster. This allows more logic to be placed in the FPGA and for the logic to execute at higher clock frequencies. However, the DATA_PACK pragma also lets you align data in the packed struct along 8-bit boundaries if needed.

If a struct port is to be implemented with an AXI4 interface you should consider using the DATA_PACK <byte_pad> option to automatically align member elements of the struct to 8-bit boundaries. The AXI4-Stream protocol requires that TDATA ports of the IP have a width in multiples of 8. It is a specification violation to define an AXI4-Stream IP with a TDATA port width that is not a multiple of 8, therefore, it is a requirement to round up TDATA widths to byte multiples. Refer to "Interface Synthesis and Structs" in Vivado Design Suite User Guide: High-Level Synthesis (UG902) for more information.

Syntax

Place the pragma near the definition of the struct variable to pack:

#pragma HLS data_pack variable=<variable> \
instance=<name> <byte_pad>

Where:

  • variable=<variable>: is the variable to be packed.
  • instance=<name>: Specifies the name of resultant variable after packing. If no <name> is specified, the input <variable> is used.
  • <byte_pad>: Optionally specifies whether to pack data on an 8-bit boundary (8-bit, 16-bit, 24-bit...). The two supported values for this option are:
    • struct_level: Pack the whole struct first, then pad it upward to the next 8-bit boundary.
    • field_level: First pad each individual element (field) of the struct on an 8-bit boundary, then pack the struct.
    TIP: Deciding whether multiple fields of data should be concatenated together before (field_level) or after (struct_level) alignment to byte boundaries is generally determined by considering how atomic the data is. Atomic information is data that can be interpreted on its own, whereas non-atomic information is incomplete for the purpose of interpreting the data. For example, atomic data can consist of all the bits of information in a floating point number. However, the exponent bits in the floating point number alone would not be atomic. When packing information into TDATA, generally non-atomic bits of data are concatenated together (regardless of bit width) until they form atomic units. The atomic units are then aligned to byte boundaries using pad bits where necessary.

Example 1

Packs struct array AB[17] with three 8-bit field fields (R, G, B) into a new 17 element array of 24 bits.

typedef struct{
unsigned char R, G, B;
} pixel;

pixel AB[17];
#pragma HLS data_pack variable=AB

Example 2

Packs struct pointer AB with three 8-bit fields (typedef struct {unsigned char R, G, B;} pixel) in function foo, into a new 24-bit pointer.

typedef struct{
unsigned char R, G, B;
} pixel;

pixel AB;
#pragma HLS data_pack variable=AB

Example 3

In this example the DATA_PACK pragma is specified for in and out arguments to rgb_to_hsv function to instruct the compiler to do pack the structure on an 8-bit boundary to improve the memory access:

void rgb_to_hsv(RGBcolor* in,  // Access global memory as RGBcolor struct-wise
                HSVcolor* out, // Access Global Memory as HSVcolor struct-wise
                int size) {
#pragma HLS data_pack variable=in struct_level
#pragma HLS data_pack variable=out struct_level
...
}

See Also