Vitis HLS版本 2022.2
在hls中常使用maxi接口用于从内存中加载数据至片上RAM,默认情况下axi的总线数据位宽为32位,当需要并行加载数据或一次加载多个数据时则可通过多个maxi接口或扩展maxi数据位宽。
example
有一个[4][768][64]的权重数据进行加载,可在存储数据时调换维度,将第一维度通道调换至最后一维度,使得多个通道的数据相邻,扩展位宽可一次并行读取多个通道的数据,方便后续计算。
1.在工程设置中,修改以下参数
2. 在设置Interface pragama时要求ddr接口绑定(命名)bundle,
3. m_axi_max_widen_bitwidth为总线的最大位宽,alignment_byte_size
为字节对其数,我们设置最大位宽为128,则对齐字节字节数为128/8=16,意味着每次传输数据会对齐。
4. 并声明max_widen_bitwidth(总线位宽)的大小,必须为2的n次幂,其次要将用语缓存数据的RAM进行分块,分块数量与总线位宽相关联,
5. 最后在用for循环读取数据时,相邻数据必须连续,for循环的最内层利用pragama unroll factor展开
方法1
代码如下:
// 方法1
void load_w(float *wqkv_ddr, float wqkv_buff[768*64*4]){
#pragma HLS INTERFACE mode=m_axi bundle=gmem_0 max_widen_bitwidth=128 port=wqkv_ddr
#pragma HLS ARRAY_PARTITION dim=1 factor=4 type=cyclic variable=wqkv_buff
for(int i=0;i<768*64*4;i++){
#pragma HLS UNROLL factor=4
wqkv_buff[i] = wqkv_ddr[i];
}
}
综合结果如下:可以看到不论是时钟周期(768*64)还是数据位宽128都可以证明已经提高了数据读取的并行度
方法2
以上为方法1的代码与结果,方法1存在一定的局限性,例如在当只需加载768*64*3的权重数据时,则必须在片上定义768*64*4的片上缓存空间,浪费掉了宝贵的片上BRAM,于是可以采用方法2,保持接口位宽不变,即访问地址的数据量不变,但仅读取部分的数据位宽,代码如下
void load_w(float*wqkv_ddr, floatwqkv_buff[768][64][3]){
#pragma HLS INTERFACE mode=m_axi bundle=gmem_0 max_widen_bitwidth=128 port=wqkv_ddr
#pragma HLS ARRAY_PARTITION dim=3 factor=3 type=cyclic variable=wqkv_buff
for(int i=0;i<768;i++)
for(int j=0;j<64;j++)
for(int k=0;k<3;k++){ //chin*win*hin
#pragma HLS UNROLL factor=3
wqkv_buff[i][j][k] = wqkv_ddr[i*64*4+j*4+k];
//此处数据偏移量为4*float,因此内存中数据要扩展一个维度[768][64][4]
}
}
此方法需保证在ddr中存储的数据是整个位宽扩展对应的数据,例如:定义接口是4*32=128 bit,则内存中应对应单次访问4个数据,当所需数据仅有3个时,补充最后一个数据为0。此方法可避免因提高并行度扩展位宽所带来的额外存储资源开销
综合结果如图