HLS:专业点灯工程师闪烁、流水、键控与呼吸


一、声明

学门技术,最常见的就是点灯了,HLS也不例外。这里把LED的闪烁、流水、按键控制和呼吸模式都弄了。第一次搞,工程就按四个的搞吧,后面如果还有这种多模块的,就用setting方式。


二、闪烁灯

1、led_twinkle.h文件。

#ifndef __LED_TWINKLE_H__
#define __LED_TWINKLE_H__

// HLS提供的任意精度定点数文件
#include "ap_fixed.h"

#define DELAY 25000000

void led_twinkle(ap_int<3> *led);

#endif

2、led_twinkle.cpp文件。

#include "led_twinkle.h"

void led_twinkle(ap_int<3> *led){
	int i = 0;
	for(i = 0; i < DELAY; i++){
		if(i < DELAY/2) *led = 1;
		else *led = 6;
	}
}

功能简单,不适用main.cpp做C simulation了。

3、接口更改。
在这里插入图片描述
不进行优化时,综合出来的接口中,clk和rst是必须的,因为使用了同步设计(for中计数值到,才同步触发)。模块的接口使用了ap_ctrl_hs控制握手协议,但为了简单,可以不用到这么多信号,改为ap_ctrl_none,即不需要额外的控制接口。

除了模块级的接口,还有输入输出端口,输出端口为led_V,使用了ap_vld的这种有效信号协议,但led功能简单,也是不需要的,直接改为ap_none。

	#pragma HLS INTERFACE ap_none port=led
	#pragma HLS INTERFACE ap_ctrl_none port=return

4、实验平台搭建测试。
在这里插入图片描述
实验管脚约束配置。

set_property PACKAGE_PIN N18     [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN T19     [get_ports sys_rst_n[0]]
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n[0]]
set_property PACKAGE_PIN G20     [get_ports led[0]]
set_property IOSTANDARD LVCMOS33 [get_ports led[0]]
set_property PACKAGE_PIN G18     [get_ports led[1]]
set_property IOSTANDARD LVCMOS33 [get_ports led[1]]
set_property PACKAGE_PIN J20     [get_ports led[2]]
set_property IOSTANDARD LVCMOS33 [get_ports led[2]]

出现下面这个错误,是因为复位的管脚约束弄到PS端的复位上了,这个是板子设计时用来refresh板子的,不能复用,从C8换到T19就没这错误了。但板卡不知是不是LED坏了,只好从正点的板卡上入手验证。后续打算涉及总线的,就到Zynq上验证,不涉及总线的逻辑开发,全到Artix7上验证。
在这里插入图片描述

[DRC UCIO-1]无约束逻辑端口:4个逻辑端口中有1个没有用户指定的特定位置约束(LOC)。这可能会导致I/O争用或与板的电源或连接不兼容,影响性能,信号完整性,在极端情况下会导致设备或其连接的组件损坏。要纠正这个错误,请指定所有的pin位置。这种设计将无法生成位流,除非所有逻辑端口都定义了用户指定的站点LOC约束。要允许使用未指定pin位置的比特流创建(不推荐),使用这个命令:set_property SEVERITY {Warning} [get_drc_checks UCIO-1]。注意:当使用Vivado运行基础设施(例如launch_runs Tcl命令)时,将这个命令添加到。Tcl文件中,并将该文件添加为实现运行的write_bitstream步骤的前钩子。问题端口:sys_rst_n[0]

5、实验结果。果然,换了板卡直接就能用了,Zynq上半天没反应。。醉了。
在这里插入图片描述
吐槽,周末这两天没来实验室,或者来了没怎么搞板子,今晚实验,就不知咋出了问题,就很无语。还是得天天来啊!


三、流水灯

1、led_shift.h文件。

#ifndef _LED_SHIFT_H_
#define _LED_SHIFT_H_

#include "ap_fixed.h"
#define DELAY 100000000
#define SHIFT_FLAG  DELAY-2
typedef ap_int<32> cnt32;
void led_shift(ap_int<4> *led);

#endif

2、led_shift.cpp文件。

#include "led_shift.h"

void led_shift(ap_int<4> *led)
{
	#pragma HLS INTERFACE ap_none port=led
	#pragma HLS INTERFACE ap_ctrl_none port=return

	cnt32 i;
	ap_int<4> temp;

	for(i = 0;i < DELAY; i++)
	{
		if(i < DELAY/4) *led = 1;
		else if((i > DELAY/4) && (i < DELAY/2)) *led = 2;
		else if((i > DELAY/2) && (i < (DELAY*3)/4)) *led = 4;
		else *led = 8;
	}
}

3、实验平台搭建测试。
在这里插入图片描述
管脚约束配置。

#时钟周期约束
create_clock -period 20.000 -name clk [get_ports sys_clk]

#IO管脚约束
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports {led[1]}]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS15} [get_ports {led[2]}]
set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS15} [get_ports {led[3]}]

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

综合结果。
在这里插入图片描述
4、实现结果。
在这里插入图片描述


四、键控灯

1、led_button.h文件。

#include "ap_fixed.h"
//#include <ap_cint.h>

#ifndef _KEY_LED_H_
#define _KEY_LED_H_
void led_button(ap_uint<2> key, ap_uint<2> * led);
#endif

2、led_button.cpp文件。

#include "led_button.h"

void led_button(ap_uint<2> key, ap_uint<2> * led)
{
	#pragma HLS INTERFACE ap_none port=led
	#pragma HLS INTERFACE ap_none port=key
	#pragma HLS INTERFACE ap_ctrl_none port=return

	// 定义了一个key_value变量来寄存按键的值
	ap_uint<2> key_value = key;

	/*
	if(key_value == 3 ) *led = 3;
	if(key_value == 2 ) *led = 2;
	if(key_value == 1 ) *led = 1;
	if(key_value == 0 ) *led = 0;*/

	switch(key_value)
	{
		// no key
		case 3:  *led = 3;
				 break;
		// key1
		case 2:  *led = 2;
				 break;
		// key2
		case 1:  *led = 1;
				 break;
		// all key
		default: *led = 0;
				 break;
	}
}

3、main.cpp文件。

#include "led_button.h"

int main()
{
	ap_uint<2> key,led;
	led_button(0,&led);
	led_button(1,&led);
	led_button(2,&led);
	led_button(3,&led);
	return 0;
}

4、一些说明。
1°为提高效率,使用测试平台验证C函数功能上是否正确。
2°按键key的C语言类型是变量,在RTL级别中被映射为输入端口。 led的C语言类型是指针,在RTL级别中被映射为输出端口。
在这里插入图片描述
3°参数类型支持的接口协议说明。
D表示默认,即HLS默认综合出来的接口。S表示HLS工具支持综合出来的接口。比如使用ap_none时,变量为形参,则接口只能综合成输入而不能是输出,当指针作形参时,接口可综合成输入、输出和双向端口。
在这里插入图片描述
5、C仿真调试说明。
在test bench中写好测试的top后,点击Run C Simulation,在弹出的配置界面中启动 C 调试器,并清除编译残留文件。Ok后就会仿真启动调试器,选择调试的方式,可以看到寄存器的变化。
在这里插入图片描述

6、一个很重要的踩坑点。
之前一直使用ap_int<2>的方式来定义变量的类型,但第一次综合生成RTL导入板卡后,却无法显示正常,学习了软件调试后,开始用C仿看变量数据,但实验中发现用ap_int<2>定义的数据,会出现负数的情况,于是改用uint2的方式定义变量,关于负数这个问题就解决了,可还是出现了一个新的问题,就是包含文件<ap_cint.h>的时候没有问题,但综合时没找到,并且提示下图这个错误。看来还是得包含ap_fixed.h文件,并把数据类型改为了ap_uint<2>,同时使用C仿真软件调试的方式,确定了没有负数产生,才进行综合并生成RTL。上面贴的代码是最终版的,但中途还是有一些测试过程的。
在这里插入图片描述
7、实验测试平台。
在这里插入图片描述
管脚约束文件。

#时钟周期约束
create_clock -period 20.000 -name clk [get_ports sys_clk]

#IO管脚约束
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN T4 IOSTANDARD LVCMOS15} [get_ports key[0]]
set_property -dict {PACKAGE_PIN T3 IOSTANDARD LVCMOS15} [get_ports key[1]]
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports led[0]]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports led[1]]

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

8、实验结果。
在这里插入图片描述


五、呼吸灯

1、基本原理。
PWM调制:在固定频率下,调整占空比,以实现LED灯亮度的变化。IF占空比为0,则LED不亮,反之最亮。如果将占空比从0%变到100%,从100%变到0%,就可以实现呼吸的效果。下图中,LED高电平的时间由长渐渐变短,再由短渐渐变长,如果LED灯是高电平点亮,则LED灯会呈现出亮度由亮到暗,再由暗到亮的过程。
在这里插入图片描述
2、实验内容。
实现一个频率可调,开关可控的呼吸灯,并需要通过一个接口来对频率与开关进行配置(*使用AXI_Lite)。重温下,AXI_Lite为单个外设数据传输,来访问一些低俗外设中的寄存器。AXI_Stream则如FIFO,数据传输不用地址,主从设备之间可以连续读写数据,常用于视频、高速AD、PCLe和DMA等高速数据传输场合。

3、led_breathe.h文件。

#include "ap_fixed.h"

#ifndef _BREATH_LED_H
#define _BREATH_LED_H_

void led_breathe(ap_uint<32> sw_ctrl, ap_uint<32> freq_step, ap_uint<1> * led);

#endif

4、led_breathe.cpp文件。

#include "led_breathe.h"

void led_breathe(ap_uint<32> sw_ctrl, ap_uint<32> freq_step, ap_uint<1>* led)
{
	#pragma HLS INTERFACE ap_none port=led
	// 频率和开关,使用slave axi接口进行设置
	#pragma HLS INTERFACE s_axilite port=freq_step
	#pragma HLS INTERFACE s_axilite port=sw_ctrl
	// 将模块的start与done本来也可以由s_axi来控制的
	// 但这里让模块一直工作,s_axilite接口就可以不用了,直接ap_ctrl_none
	#pragma HLS INTERFACE ap_ctrl_none port=return

	ap_uint<32> duty_cycle, period_cnt;  // 占空比循环变化,周期循环变化变量

	// 按键没按下时,是开的,按键按下后,关闭模块,led输出为0,0是关闭
	if(sw_ctrl == 1){
		// freq_step是外部设置的一个32位的值
		// 先假设freq_step是个固定的值,这样就看占空比
		// 不断增加每个 PWM 周期中的占空比,来实现呼吸灯从暗变亮
		// 若freq_step是变化的,则可以控制呼吸灯占空比变化的“步长”,步长越大,占空比变化的越快
		// 那么呼吸灯由暗到亮的时间越短,即呼吸频率变快
		for(duty_cycle=0; duty_cycle<50000; duty_cycle=duty_cycle+freq_step){
			for(period_cnt=0; period_cnt<50000; period_cnt++)
				*led = (period_cnt <= duty_cycle) ? 1 : 0 ;
		}

		for(duty_cycle=50000; duty_cycle>0; duty_cycle=duty_cycle-freq_step){
			for(period_cnt=0; period_cnt<50000; period_cnt++)
				*led = (period_cnt <= duty_cycle) ? 1 : 0;
		}
	}else *led = 0;
}

5、main.cpp文件。

#include "led_breathe.h"

int main(void)
{
	ap_uint<1> led;

	// 开关打开,设置步长2000来验证呼吸灯功能。
	led_breathe(1, 2000, &led); //打开呼吸灯,频率步长设置为 2000
	led_breathe(0, 2000, &led); //关闭呼吸灯
	printf("test passed!\n");
	return 0;
}

6、C联合RTL仿真波形查看。
在模块完整工作一次期间,即done信号产生前,生成 PWM 波的占空比逐渐增大后又逐渐减小, 说明呼吸灯由暗到亮后又由亮变暗。功能是正确的。
在这里插入图片描述
7、实验平台搭建。
在这里插入图片描述
管脚约束。

set_property -dict {PACKAGE_PIN G20 IOSTANDARD LVCMOS33} [get_ports led]

消耗资源。
在这里插入图片描述
8、SDK开发。

#include "xled_breathe.h"
#include "sleep.h"

int main()
{
	XLed_breathe led;
	XLed_breathe_Initialize(&led, XPAR_LED_BREATHE_0_DEVICE_ID);

	while(1)
	{
		XLed_breathe_Set_freq_step_V(&led,40);
		XLed_breathe_Set_sw_ctrl_V(&led,1);
		sleep(5);
		XLed_breathe_Set_sw_ctrl_V(&led,0);
		sleep(2);
		XLed_breathe_Set_freq_step_V(&led,200);
		XLed_breathe_Set_sw_ctrl_V(&led,1);
		sleep(5);
	}
	return 0;
}

9、实验结果。
在这里插入图片描述

Note:Vivado HLS工具不支持“ ap_ctrl_none”包级别协议的仿真, 所以我们在仿真前先删除所有的优化指令。


六、时间线

一些关键时间点的记录:

2021年04月18日晚上:闪烁灯的实验,并解决了一些问题。
2021年04月19日早上:流水灯、键控灯的实验,同时使用了下纯C语言的仿真调试。
2021年04月19日中下午:呼吸灯的实验,搭建了下平台。
后续可能做的工作:暂无。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATian+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值