基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)

一、简介

HLS——High Level Synthesis高等级综合。HLS主要应用:在于某些算法工程或软件工程师尽管学习了HDL(Hardware Description Language),但由于对硬件理解不够,仍然无法熟练掌握硬件编程。而想采用机器学习的很多算法,把他们映射到FPGA上,高等级综合可以把C/C++转换为硬件电路,在使用时通过添加IP的方式,CPU只需调度,由硬件完成计算。例如高清的图像识别,CPU算不过来,需要由硬件去做。

IP核(Intellectual Property core)是一段具有特定电路功能的硬件描述语言程序,该程序与集成电路工艺无关,可以移植到不同的半导体工艺中去生产集成电路芯片。

二、基本流程

在管脚资源有限的情况下,用双路8位锁存器,10位GPIO扩展为24位,把一片时间分成三份,分别送阳极、阴极的数据。我们要做的硬件上的锁存器。

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第1张图片

每10ns为一个周期,执行一次。HLS中规定:需要输入的参数可以用形参,输出的必须为指针或者数组(数组也是指针的另一种表达形式),函数里有指针的形式一般为输出。我们所做的是IP,相当于C语言中的库文件,因此是不会有main函数的,测试需要Test Bench中写main()来调用。

选择Top Function以及添加的文件:

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第2张图片

part选择将要使用的芯片封装:

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第3张图片

C语言代码所实现的时序逻辑:

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第4张图片

主函数代码:

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中设置属性:

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第5张图片

进行综合,综合完成后生成vhd硬件描述语言的文件,然后Export Rtl,将这个IP导出。

关闭HLS,打开Vivado:

在块设计中添加IP:首先在IP Catalog中找到Ip目录下我们要添加的IP,如下图所示,然后在bd中添加自己做的IP。

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第6张图片

PS部分需要设置宽度为10位的EMIO,由于PS部分的10位是一个口,需要总线拆分器Slice,设置Slice的宽度。

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第7张图片

因为PS上是10位,所以所有Slice的宽度均为10位,而根据每个输入宽度设置分段宽度。

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第8张图片

将其连接起来。CLK和RST不用自动connect即可。

基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示)_第9张图片

接下来就是Create Wrapper->生成bitstream->在SDK中运行测试代码。

你可能感兴趣的:(基于ZYNQ的嵌入式学习笔记七(HSL设计基本刷新显示))