HLS #1 - 使用HLS生成的带有AXI4Stream接口的IP核

本篇中使用HLS进行IP核生成,使用AXI4Stream接口,完成50个int类型数的输入输出。IP核内部将输入的50个数与5相加后输出。

目录

1.1 - HLS工程建立

1.2 - 接口定义

1.3 - HLS优化

1.4 - 联合仿真查看波形

1.5 - IP导出

2 - VIVADO工程

3 - SDK工程测试


1.1 - HLS工程建立

新建HLS工程,添加TOP函数。这里A数组为输入数组,B数组为输出数组。

TOP函数表示,在这个IP核中,将A数组的每个成员加5后赋值给B数组。

#include "ap_axi_sdata.h"


void example(ap_axis<32,1,1,1> A[50], ap_axis<32,1,1,1> B[50]){

  int i;

  add_loop:
  for(i = 0; i < 50; i++){
    B[i].data = A[i].data.to_int() + 5;
    B[i].keep = A[i].keep;
    B[i].strb = A[i].strb;
    B[i].user = A[i].user;
    B[i].last = A[i].last;
    B[i].id = A[i].id;
    B[i].dest = A[i].dest;
  }
}

这里输入A和输出B都使用了 ap_axis 数据类型。在这里的主要目的是为了后续定义为AXI4Stream接口后,显式的定义好TLAST信号,若只使用 int 类型来进行定义,则会导致后续定义为AXI4Stream接口后没有TLAST信号。

添加完TOP函数后,为了让HLS综合能够良好的评估TOP函数综合后的效果,Test Bench 是必不可少的,我们在Test Bench添加以下代码。

#include <stdio.h>
#include "ap_axi_sdata.h"

void example(ap_axis<32,1,1,1> A[50], ap_axis<32,1,1,1> B[50]);

int main()
{
  ap_axis<32,1,1,1> A[50];
  ap_axis<32,1,1,1> B[50];

  for(int i=0; i < 50; i++){
    A[i].data = i;
    A[i].keep = 1;
    A[i].strb = 1;
    A[i].user = 1;
    A[i].last = 0;
    A[i].id = 0;
    A[i].dest = 1;
  }

  example(A,B);

  for(int i=0; i < 50; i++){
    if(B[i].data.to_int() != (i+5)){
      printf("ERROR: HW and SW results mismatch\n");
      return 1;
    }
  }

  printf("Success: HW and SW results match\n");
  return 0;
}

testbench代码比较简单,首先其定义了三个数组A、B、C,对数组A的每个元素的data成员填充了测试数据 0~50。

接着,其调用我们TOP函数 “example” ,将数组A作为输入,数组B接收TOP函数的输出。

我们还需要进行比较,看是否TOP函数正确的完成了我们的算法,这里我们直接用软件将数组C填充为数组A对应成员加5,然后和通过TOP函数输出的数组B进行比较,查看TOP函数是否正确的完成了计算。

1.2 - 接口定义

我们期望的是,输入数组A和输入数组B在PL上能通过AXI4Stream接口进行输入输出,控制端口通过AXI4-Lite总线进行控制,这里我们需要对接口进行定义约束。

切换回 Source->example.cpp 我们的TOP函数上。

打开Directive指令侧栏,开始接口定义。

我们首先定义控制端口是axilite总线,双击Directive侧栏中的函数名,选择 INTERFACE,模式选择为 s_axilite

接着同样的方式定义输入数组A,选择INTERFACE ,模式选择为 axis,对数组B也进行同A一样的操作。

name可选项可以设置你想要的端口名称,这里给A设为"INPUT_ARR" B设为"OUTPUT_ARR"

我们运行综合,查看报告

从报告可以看出,我们当前的设计,其输入至输出的延迟latency有151个时钟周期,前次输入输出完毕到下次输入输出的间隔则是151个时钟周期。

我们看接口方面,IP的控制信号被定义为了AXI-Lite类型,而两个数据流A和B则被定义为了AXI4-Stream类型。

这里如果没有像前文所述使用 ap_axis 类型进行输入输出数组的定义,则会在综合后,输入输出的axis通道丢失部分信号,从而导致在PL上使用时无法成功的通过TLAST信号线向DMA发送信号。

1.3 - HLS优化

打开Directive指令侧栏,我们进行优化。

首先对 add_loop进行优化,这里我们可以使用 UNROOL 解循环优化以及 PIPELINE 流水线优化

对于HLS不太了解的可以先看一下这个视频:跟Xilinx SAE 学HLS系列视频讲座-高亚军  

UNROOL 解循环优化可以看做使用 x 倍的空间生成能够同时进行操作的功能块,同时对 x 个连续的数组成员进行操作

PIPELINE 则是将原本一条流水线复制多条进行同时处理。

首先对数组进行 UNROOL 解循环优化,双击 add_loop,如下进行设置

接着添加PIPELINE优化

添加UNROOL和PIPELINE优化后如图所示

我们再运行一次综合,查看结果

可以很明显的看到我们的延迟和间隔时间降低了,但是取而代之的,我们的资源占用提升了,我们查看一下报告比较

1.4 - 联合仿真查看波形

我们点击 Run-C/RTL cosimulation 运行联合仿真

这里 Dump Trace 选择 all 以查看全部端口以及信号的波形。

仿真完毕后我们可以查看联合仿真报告

我们点击 Open Wave Viewer ,将会在新打开的vivado中查看仿真波形

1.5 - IP导出

我们点击 导出RTL 

点击OK,等待导出完成。导出完成后,在当前的solution解决方案下会多出一个 impl文件夹,其下的 ip 文件夹内的压缩包便是我们的IP核。

由于我们使用了AXI-Lite总线,伴随着我们IP核生成的,还有 drivers 文件夹下的相关驱动代码。

2 - VIVADO工程

我们将生成的IP放到事先准备好的VIVADO工程目录下,添加这个IP

这里我们使用这个IP搭建了一个简单的测试环境。使用的是PS -> AXIDMA -> HLS_IP -> AXIDMA -> PS环路

AXI-DMA这里关闭了SG模式,将吞吐调到最大。

ZYNQ 的 AXI_HP0 如下设置

综合VIVADO工程,生成bitstream比特流文件,导出到HDL文件,加载SDK。

3 - SDK工程测试

打开SDK后,我们先新建BSP,File -> New -> Board Support Package

新建好BSP后,我们看打开的mss文件,直接载入axidma的环路测试样例,在其上稍作修改进行我们的IP测试

在打开的样例程序中,我们主要修改以下几个部分

  • 修改发送长度为50
  • 修改重复测试次数为10
  • 将TxBuffer与RxBuffer修改为int类型,同样的将源代码中有关的字节长度部分进行修改
  • 在传输完毕后,打印TxBuffer与RxBuffer内容
  • 添加XExample(HLS生成的IP核)的初始化和开始操作
  • Xil_DCacheDisable() 关闭缓存,以确保写入数据不存在缓存中而是写到了DDR上
  • XExample_EnableAutoRestart() 启用自动开始信号,这样多次调用不需要单独进行start操作
/***************************** Include Files *********************************/
#include "xaxidma.h"
#include "xparameters.h"
#include "xdebug.h"
#include "xexample.h"

#if defined(XPAR_UARTNS550_0_BASEADDR)
#include "xuartns550_l.h"       /* to use uartns550 */
#endif

/******************** Constant Definitions **********************************/

/*
 * Device hardware build related constants.
 */

#define DMA_DEV_ID		XPAR_AXIDMA_0_DEVICE_ID

#ifdef XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#define DDR_BASE_ADDR		XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#elif XPAR_MIG7SERIES_0_BASEADDR
#define DDR_BASE_ADDR	XPAR_MIG7SERIES_0_BASEADDR
#elif XPAR_MIG_0_BASEADDR
#define DDR_BASE_ADDR	XPAR_MIG_0_BASEADDR
#elif XPAR_PSU_DDR_0_S_AXI_BASEADDR
#define DDR_BASE_ADDR	XPAR_PSU_DDR_0_S_AXI_BASEADDR
#endif

#ifndef DDR_BASE_ADDR
#warning CHECK FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, \
		 DEFAULT SET TO 0x01000000
#define MEM_BASE_ADDR		0x01000000
#else
#define MEM_BASE_ADDR		(DDR_BASE_ADDR + 0x1000000)
#endif

#define TX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x004FFFFF)

#define MAX_PKT_LEN		50


#define NUMBER_OF_TRANSFERS	10

/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/


/************************** Function Prototypes ******************************/

#if (!defined(DEBUG))
extern void xil_printf(const char *format, ...);
#endif

int XAxiDma_SimplePollExample(u16 DeviceId);
static int CheckData(void);

/************************** Variable Definitions *****************************/
/*
 * Device instance definitions
 */
XAxiDma AxiDma;
XExample gExample;
int *TxBufferPtr;
int *RxBufferPtr;


/*****************************************************************************/
/**
* The entry point for this example. It invokes the example function,
* and reports the execution status.
*
* @param	None.
*
* @return
*		- XST_SUCCESS if example finishes successfully
*		- XST_FAILURE if example fails.
*
* @note		None.
*
******************************************************************************/
int main()
{
	int Status;

	xil_printf("\r\n--- Entering main() --- \r\n");

	//init HLS IP
	int _status = XExample_Initialize(&gExample,XPAR_EXAMPLE_0_DEVICE_ID);
	if(_status != XST_SUCCESS)
	{
		xil_printf("XExample_initialize failed\n");
		return XST_FAILURE;
	}

	//start HLS IP
	XExample_EnableAutoRestart(&gExample);
	XExample_Start(&gExample);

	/* Run the poll example for simple transfer */
	Status = XAxiDma_SimplePollExample(DMA_DEV_ID);

	if (Status != XST_SUCCESS) {
		xil_printf("XAxiDma_SimplePoll Example Failed\r\n");
		return XST_FAILURE;
	}

	xil_printf("Successfully ran XAxiDma_SimplePoll Example\r\n");

	xil_printf("--- Exiting main() --- \r\n");

	return XST_SUCCESS;

}

#if defined(XPAR_UARTNS550_0_BASEADDR)
/*****************************************************************************/
/*
*
* Uart16550 setup routine, need to set baudrate to 9600, and data bits to 8
*
* @param	None.
*
* @return	None
*
* @note		None.
*
******************************************************************************/
static void Uart550_Setup(void)
{

	/* Set the baudrate to be predictable
	 */
	XUartNs550_SetBaud(XPAR_UARTNS550_0_BASEADDR,
			XPAR_XUARTNS550_CLOCK_HZ, 9600);

	XUartNs550_SetLineControlReg(XPAR_UARTNS550_0_BASEADDR,
			XUN_LCR_8_DATA_BITS);

}
#endif

/*****************************************************************************/
/**
* The example to do the simple transfer through polling. The constant
* NUMBER_OF_TRANSFERS defines how many times a simple transfer is repeated.
*
* @param	DeviceId is the Device Id of the XAxiDma instance
*
* @return
*		- XST_SUCCESS if example finishes successfully
*		- XST_FAILURE if error occurs
*
* @note		None
*
*
******************************************************************************/
int XAxiDma_SimplePollExample(u16 DeviceId)
{
	XAxiDma_Config *CfgPtr;
	int Status;
	int Index;

	TxBufferPtr = (int *)TX_BUFFER_BASE ;
	RxBufferPtr = (int *)RX_BUFFER_BASE;

	/* Initialize the XAxiDma device.
	 */
	CfgPtr = XAxiDma_LookupConfig(DeviceId);
	if (!CfgPtr) {
		xil_printf("No config found for %d\r\n", DeviceId);
		return XST_FAILURE;
	}

	Status = XAxiDma_CfgInitialize(&AxiDma, CfgPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed %d\r\n", Status);
		return XST_FAILURE;
	}

	if(XAxiDma_HasSg(&AxiDma)){
		xil_printf("Device configured as SG mode \r\n");
		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);

	//disable the cache
    Xil_DCacheDisable();

	for(Index = 0; Index < NUMBER_OF_TRANSFERS; Index ++) {

		//write to the TxBuffer and RxBuffer default value
		for(int i = 0; i < MAX_PKT_LEN; i++)
		{
				TxBufferPtr[i] = i + Index;
				RxBufferPtr[i] = 0;
		}

		Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
					MAX_PKT_LEN*sizeof(int), XAXIDMA_DEVICE_TO_DMA);

		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
					MAX_PKT_LEN*sizeof(int), XAXIDMA_DMA_TO_DEVICE);

		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		while ((XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA)) ||
			(XAxiDma_Busy(&AxiDma,XAXIDMA_DMA_TO_DEVICE))) {
				/* Wait */
		}

		Status = CheckData();
		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

	}

	/* Test finishes successfully
	 */
	return XST_SUCCESS;
}

/*****************************************************************************/
/*
*
* This function checks data buffer after the DMA transfer is finished.
*
* @param	None
*
* @return
*		- XST_SUCCESS if validation is successful.
*		- XST_FAILURE otherwise.
*
* @note		None.
*
******************************************************************************/
static int CheckData(void)
{
	xil_printf("\nThe TxData is\n");
	for(int _index=0;_index < MAX_PKT_LEN;_index++)
	{
		xil_printf("%d ",TxBufferPtr[_index]);
	}
	xil_printf("\nThe RxData is\n");
	for(int _index=0;_index < MAX_PKT_LEN;_index++)
	{
		xil_printf("%d ",RxBufferPtr[_index]);
	}
	xil_printf("\n\n");

	return XST_SUCCESS;
}

不要忘记,我们的IP核需要通过AXI-Lite总线对其进行start触发信号操作。由于生成IP核时同样的生成了相关的驱动代码,这里我们直接调用驱动代码即可。

打开SDK Terminal,运行Debug,我们可以看到串口输出

可见,我们的初始的数据,在通过AXIDMA传输到我们使用HLS生成的IP核进行运算,传输回来后各个项已经被自增了5。

这个HLS IP虽然被调用多次,但是由于之前开启了自动开始功能,故不需要每次传输前发送start指令,这一技巧可以参考xilinx官方HLS手册 UG902

发布了38 篇原创文章 · 获赞 23 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013662665/article/details/95658792
HLS
今日推荐