一、简介
HLS——High Level Synthesis高等级综合。HLS主要应用:在于某些算法工程或软件工程师尽管学习了HDL(Hardware Description Language),但由于对硬件理解不够,仍然无法熟练掌握硬件编程。而想采用机器学习的很多算法,把他们映射到FPGA上,高等级综合可以把C/C++转换为硬件电路,在使用时通过添加IP的方式,CPU只需调度,由硬件完成计算。例如高清的图像识别,CPU算不过来,需要由硬件去做。
IP核(Intellectual Property core)是一段具有特定电路功能的硬件描述语言程序,该程序与集成电路工艺无关,可以移植到不同的半导体工艺中去生产集成电路芯片。
二、基本流程
在管脚资源有限的情况下,用双路8位锁存器,10位GPIO扩展为24位,把一片时间分成三份,分别送阳极、阴极的数据。我们要做的硬件上的锁存器。
每10ns为一个周期,执行一次。HLS中规定:需要输入的参数可以用形参,输出的必须为指针或者数组(数组也是指针的另一种表达形式),函数里有指针的形式一般为输出。我们所做的是IP,相当于C语言中的库文件,因此是不会有main函数的,测试需要Test Bench中写main()来调用。
选择Top Function以及添加的文件:
part选择将要使用的芯片封装:
C语言代码所实现的时序逻辑:
主函数代码:
void Seg7_Mux(uint8 Input_data,uint1 Data_Latch,uint1 Select_Latch,uint8 *Output_Data_A,uint8 *Output_Data_B,uint8 *Output_Select )
{
static uint1 Data_Latch_New; //本周期显示码锁存信号
static uint1 Data_Latch_Last; //上周期显示码锁存信号
static uint1 Select_Latch_New; //本周期选择码锁存信号
static uint1 Select_Latch_Last; //上周期选择码锁存信号
static uint8 Inner_Data; //IP内部显示码总线
static uint8 Inner_Select; //IP内部选择码总线
Data_Latch_Last = Data_Latch_New; //在时钟边沿将本周期显示码锁存信号赋给上周期显示码锁存信号
Select_Latch_Last = Select_Latch_New; //在时钟边沿将本周期选择码锁存信号赋给上周期选择码锁存信号
Data_Latch_New = Data_Latch; //在时钟边沿将输入显示码锁存信号赋给本周期显示码锁存信号
Select_Latch_New = Select_Latch; //在时钟边沿将输入选择码锁存信号赋给本周期选择码锁存信号
if((Data_Latch_Last == 0) && (Data_Latch_New == 1)) //在时钟边沿判断是否为显示码锁存信号的上升沿
{
Inner_Data = Input_data; //将输入8位数据总线赋给IP内部显示码总线
}else if ((Select_Latch_Last == 0) && (Select_Latch_New == 1)) //在时钟边沿判断是否为选择码锁存信号的上升沿
{
Inner_Select = Input_data; //将输入8位数据总线赋给IP内部选择码总线
}
*Output_Data_A = Inner_Data; //在时钟边沿将IP内部显示码总线赋给输出到A组7段数码管的显示码
*Output_Data_B = Inner_Data; //在时钟边沿将IP内部显示码总线赋给输出到B组7段数码管的显示码
*Output_Select = Inner_Select; //在时钟边沿将IP内部选择码总线赋给输出的7段数码管选择码
};
Test Bench中的test函数代码:
#include "Seg7_Mux_head.h"
int main() {
//input port
uint8 Input_data;
uint1 Data_Latch,Select_Latch;
//output port
uint8 Output_Data_A;
uint8 Output_Data_B;
uint8 Output_Select;
Input_data = 0x77;
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Input_data = 0xfe;
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Select_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Select_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Input_data = 0x88;
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Input_data = 0x7f;
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Select_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Select_Latch = 1;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
Data_Latch = 0;
Select_Latch = 0;
Seg7_Mux(Input_data,Data_Latch,Select_Latch,&Output_Data_A,&Output_Data_B,&Output_Select);
printf("Output_Data_A = %x, Output_Data_B= %x, Output_Select= %x\r\n",Output_Data_A,Output_Data_B,Output_Select);
return 0;
}
Data-Latch_New和Data_Latch_Last(Select_Latch用法相同):把上一次调用的新值赋给本次的旧值,本次的Latch赋给本次的新值,如果旧值等于0,新值等于1,两者差一个周期,说明产生了上升沿。就把输出数据赋给指针。硬件上是没有printf函数的,只能在测试时用。
可以在Synthesis的Directive中设置属性:
进行综合,综合完成后生成vhd硬件描述语言的文件,然后Export Rtl,将这个IP导出。
关闭HLS,打开Vivado:
在块设计中添加IP:首先在IP Catalog中找到Ip目录下我们要添加的IP,如下图所示,然后在bd中添加自己做的IP。
PS部分需要设置宽度为10位的EMIO,由于PS部分的10位是一个口,需要总线拆分器Slice,设置Slice的宽度。
因为PS上是10位,所以所有Slice的宽度均为10位,而根据每个输入宽度设置分段宽度。
将其连接起来。CLK和RST不用自动connect即可。
接下来就是Create Wrapper->生成bitstream->在SDK中运行测试代码。