前面文章中我们利用AXI-stream总线实现整数类型的数据传输。本文我们将利用AXI-stream总线传输float类型数据,额外需要一个数据转换过程。
HLS
首先,如果想要利用AXI-stream总线传输数据,HLS中的接口需要用ap_axis,ap_axiu类型的结构体定义,结构体中的data数据是ap_uint<32>的,也就是说AXI-stream传输的数据会被统一被封装为ap_uint<32>类型。如果想要传输其它类型数据,就需要转换,可以利用union实现。下面例子中是传输float类型数据,用int类型接收后,union是空间共享的,按照float类型使用数据就可以了。
// test.cpp
#include "ap_axi_sdata.h"
#define SIZE 50
typedef ap_axiu<32,4,5,5> AXI_type; // 32位无符号数
float pop(AXI_type const &e)
{
#pragma HLS INLINE
union
{
int ival;
float oval;
}converter;
converter.ival=e.data;
float ret=converter.oval; //
volatile ap_uint<sizeof(float)> strb=e.strb;
volatile ap_uint<sizeof(float)> keep=e.keep;
volatile ap_uint<4> user=e.user;
volatile ap_uint<1> last=e.last;
volatile ap_uint<5> id=e.id;
volatile ap_uint<5> dest=e.dest;
return ret;
}
AXI_type push(float const &v,bool last=false)
{
#pragma HLS INLINE
AXI_type e;
union
{
int oval;
float ival;
}converter;
converter.ival=v;
e.data=converter.oval;
e.strb=-1;
e.keep=15;
e.user=0;
e.last=last? 1:0;
e.id=0;
e.dest=0;
return e;
}
void test(AXI_type A[SIZE],AXI_type B[SIZE])
{
#pragma HLS INTERFACE s_axilite port=return bundle=ctrl
#pragma HLS INTERFACE axis register both port=B
#pragma HLS INTERFACE axis register both port=A
float a[SIZE],b[SIZE];
// read input
for(int i=0;i<SIZE;i++)
{
#pragma HLS PIPELINE
a[i]=pop(A[i]);
}
// compute
for(int i=0;i<SIZE;i++)
{
#pragma HLS PIPELINE
b[i]=a[i];
}
// write output
for(int i=0;i<SIZE;i++)
{
#pragma HLS PIPELINE
B[i]=push(b[i],i==SIZE-1);
}
}
Testbench代码中的数据类型只能是ap_axiu<32,4,5,5>形式,才能满足接口需求,而不能用float类型。感觉我们没法完全测试。
// tb_test
#include <stdio.h>
#include "ap_axi_sdata.h"
#define SIZE 50
typedef ap_axiu<32,4,5,5> AXI_type; // 32位无符号数
void test(AXI_type A[SIZE],AXI_type B[SIZE]);
int main()
{
AXI_type A[SIZE],B[SIZE];
for(int i=0;i<SIZE;i++)
{
A[i].data=i;
}
test(A,B);
printf("Pass!!");
return 0;
}
Vivado
这里与AXI-stream传输整数数据相同
SDK
只需要定义float类型数据传输即可,不需要额外封装。转换过程发生在HLS代码中。
#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "xaxidma.h"
#include "xtest.h"
#include "xtime_l.h"
XAxiDma AxiDma;
//
int init_DMA()
{
int status;
// 初始化DMA
XAxiDma_Config *CfgPtr;
CfgPtr = XAxiDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID);
if(!CfgPtr){
print("Error looking for AXI DMA config\n\r");
return XST_FAILURE;
}
status = XAxiDma_CfgInitialize(&AxiDma,CfgPtr);
if(status != XST_SUCCESS){
print("Error initializing DMA\n\r");
return XST_FAILURE;
}
//check for scatter gather mode
if(XAxiDma_HasSg(&AxiDma)){
print("Error DMA configured in SG mode\n\r");
return XST_FAILURE;
}
/* Disable interrupts, we use polling mode */
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
// Reset DMA
XAxiDma_Reset(&AxiDma);
while (!XAxiDma_ResetIsDone(&AxiDma)) {}
return XST_SUCCESS;
}
int Run_HW_Accelerator(float A[50], float B[50], int size)
{
//transfer A to the Vivado HLS block
int status = XAxiDma_SimpleTransfer(&AxiDma, (unsigned int)A, size, XAXIDMA_DMA_TO_DEVICE);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Wait for transfer to be done */
while (XAxiDma_Busy(&AxiDma, XAXIDMA_DMA_TO_DEVICE)) ;
//get results from the Vivado HLS block
status = XAxiDma_SimpleTransfer(&AxiDma, (unsigned int)B, size,
XAXIDMA_DEVICE_TO_DMA);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Wait for transfer to be done */
while (XAxiDma_Busy(&AxiDma, XAXIDMA_DMA_TO_DEVICE)) ;
//poll the DMA engine to verify transfers are complete
while ((XAxiDma_Busy(&AxiDma, XAXIDMA_DEVICE_TO_DMA)) || (XAxiDma_Busy(&AxiDma, XAXIDMA_DMA_TO_DEVICE))) ;
return 0;
}
int main()
{
int status;
float A[50],B[50];
int size=50*sizeof(float);
XTime tStart,tEnd;
u32 tUsed;
status=init_DMA();
// DMA init
if(status != XST_SUCCESS){
print("\rError: DMA init failed\n");
return XST_FAILURE;
}
print("\nDMA Init done\n");
// data
init_platform();
for(int i=0;i<50;i++)
A[i]=(float)(i+0.2333);
// HW init
XTest hlsconv;
XTest_Config *ExamplePtr;
printf("Look up the device configuration.\n");
ExamplePtr=XTest_LookupConfig(XPAR_TEST_0_DEVICE_ID);
if(!ExamplePtr){
printf("Error, lookup failed!");
return XST_FAILURE;
}
printf("Initialize the Device\n");
status=XTest_CfgInitialize(&hlsconv,ExamplePtr);
if(status!=XST_SUCCESS)
{
printf("Error, can't initialize accelerator.\n");
return XST_FAILURE;
}
XTest_Start(&hlsconv); // start
//flush the cache
Xil_DCacheFlushRange((unsigned int)A,size);
Xil_DCacheFlushRange((unsigned int)B,size);
print("\rCache cleared\n\r");
// HW compute
XTime_GetTime(&tStart);
status=Run_HW_Accelerator(A,B,size);
XTime_GetTime(&tEnd);
// 放在最后打印不出来
for(int i=0;i<50;i++)
printf("%f\n",B[i]);
tUsed=((tEnd-tStart)*1000)/(COUNTS_PER_SECOND);
printf("HW time is %ldms\n",tUsed);
cleanup_platform();
return 0;
}
运行结果:第一次运行数据可能存在问题,硬件设备的原因。后续数据是正常的,浮点数传输有些小误差也算正常的。