本文重在步骤过程上,对代码讲解较少。
一、Overview
本文首先利用高层次综合工具HLS实现一个矩阵乘IP核;再利用Vivado软件搭建一个系统;最后利用SDK编写驱动程序,在Zedboard上运行。
基本参数
工具版本:2017.4
实现功能:矩阵乘
传输总线:AXI4
二、HLS
矩阵乘的基本运算过程是: C[n][n]=A[n][n]*B[n][n]
HLS部分代码如下
// 矩阵乘主文件 mul.cpp
#include "mul.h"
void mul(data* A_addr,data* B_addr,data* C_addr)
{
#pragma HLS INTERFACE m_axi depth=16 port=C_addr offset=slave bundle=C_array
#pragma HLS INTERFACE m_axi depth=16 port=B_addr offset=slave bundle=B_array
#pragma HLS INTERFACE m_axi depth=16 port=A_addr offset=slave bundle=A_array
#pragma HLS INTERFACE s_axilite port=return
data A[n][n];
data B[n][n];
data C[n][n];
#pragma HLS ARRAY_PARTITION variable=B complete dim=1
#pragma HLS ARRAY_PARTITION variable=A complete dim=2
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
#pragma HLS PIPELINE
A[i][j]=*(A_addr++);
B[i][j]=*(B_addr++);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
#pragma HLS PIPELINE
C[i][j]=0;
for(int k=0;k<n;k++)
{
C[i][j]+=A[i][k]*B[k][j];
}
}
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
#pragma HLS PIPELINE
*(C_addr++)=C[i][j];
}
}
// 矩阵乘头文件 mul.h
#include "ap_fixed.h"
#define n 4
typedef int data;
void mul(data* A_addr,data* B_addr,data* C_addr);
// 测试文件 main.cpp
#include "mul.h"
#include <iostream>
using namespace std;
int main()
{
data A[n][n]={1,1,2,3,4,4,5};
data B[n][n]={1,3,5,6,7,7,6};
data C[n][n];
data C_s[n][n];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
C_s[i][j]=0;
for(int k=0;k<n;k++)
{
C_s[i][j]=C_s[i][j]+A[i][k]*B[k][j];
}
}
}
mul(&A[0][0],&B[0][0],&C[0][0]);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(C[i][j]!=C_s[i][j])
cout<<"fail!!!"<<endl;
}
cout<<"Pass!!!"<<endl;
return 0;
}
实验步骤:
1.将上述三份代码复制到三个文件中,放在一个文件夹下
2.新建HLS项目
3.Run C Simulation
4.C Synthesis
5.导出IP核
导出完成
三、Vivado
1.新建Vivado项目
点击next
2.添加HLS IP核
3.Create Block design
4.添加IP核
右侧3个是Vivado自带的,左侧是我们用HLS封装的。
5.配置Zynq IP核
Preset中选择Zedboard开发板
6.连线
虽然说Vivado提供了自动连线的功能,但我建议手动连接重要的数据通路,时钟和复位可以选择自动连线。
先手动连线
再自动连线
连线完成后的结果(3路数据1个HP接口)
还有另外一种连线方式(3路数据3个HP接口)
7.自动分配地址
8.Generate Output Products
9.Generate Bitstream
直接点击Generate Bitstream即可,会自动完成综合等前序步骤(这里时间较长)
四、SDK
1.新建SDK项目
Project name建议与我的相同,这样SDK代码可以直接使用
2.修改HelloWorld.c中的代码
用下面的代码替换HelloWorld.c的中的全部代码
#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xmul.h"
#include "xil_cache.h"
#include <stdlib.h>
#include "xtime_l.h"
int A[4][4];
int B[4][4];
int C[4][4];
int C_s[4][4];
void init()
{
XTime tEnd,tCur;
int tUsed;
// data init
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
A[i][j]=i+j;
B[i][j]=i+j;
C[i][j]=0;
C_s[i][j]=0;
}
XTime_GetTime(&tCur);
// SW_tb
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
C_s[i][j]=0;
for(int k=0;k<4;k++)
{
C_s[i][j]+=A[i][k]*B[k][j];
}
}
}
XTime_GetTime(&tEnd);
tUsed=((tEnd-tCur)*1000000)/(COUNTS_PER_SECOND);
printf("SW time used is %d us\n",tUsed);
return;
}
int main()
{
init();
init_platform(); //
// HW
XMul hlsconv;
XMul_Config *ExamplePtr;
XTime tEnd,tCur;
int tUsed;
printf("Look up the device configuration.\n");
ExamplePtr=XMul_LookupConfig(XPAR_MUL_0_DEVICE_ID);
if(!ExamplePtr){
printf("Error, lookup failed!");
return XST_FAILURE;
}
printf("Initialize the Device\n");
int status=XMul_CfgInitialize(&hlsconv,ExamplePtr);
if(status!=XST_SUCCESS)
{
printf("Error, can't initialize accelerator.\n");
return XST_FAILURE;
}
XTime_GetTime(&tCur);
Xil_DCacheFlushRange((u32)A,4*4*sizeof(int)); // 刷新
Xil_DCacheFlushRange((u32)B,4*4*sizeof(int));
Xil_DCacheFlushRange((u32)C,4*4*sizeof(int));
XMul_Set_A_addr(&hlsconv,(u32)A); //
XMul_Set_B_addr(&hlsconv,(u32)B);
XMul_Set_C_addr(&hlsconv,(u32)C);
XMul_Start(&hlsconv); // start
while(XMul_IsDone(&hlsconv)==0); // done
Xil_DCacheInvalidateRange((u32)C,4*4*sizeof(int)); // 刷新
XTime_GetTime(&tEnd);
tUsed=((tEnd-tCur)*1000000)/(COUNTS_PER_SECOND);
printf("HW time used is %d us\n",tUsed);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
printf("C[%d][%d]=%d ",i,j,C[i][j]);
printf("C_s[%d][%d]=%d\n",i,j,C_s[i][j]);
}
cleanup_platform();
return 0;
}
Ctrl+S保存+自动编译
#####################下面需要连接Zedboard开发板后进行#####################
3.运行时配置:Run As->Run Configurations
(1)port选择UART1的端口号(这里是COM8,需要根据自己计算机端口选择)
(2)BAUD Rate设定为115200
(3)完成后点击下面的Apply,关闭该页面
4.下载到Zedboard开发板并运行
依次点击1,2部分。1的作用是将比特流下载到FPGA中,下载成功后,FPGA蓝灯会亮起;2是运行SDK程序,printf的打印信息会重定向,输出在SDK软件的Console控制台中。至此,完成整个过程。
Finished.