Zynq开发实践(FPGA之rtl算法加速)

发布于:2025-09-08 ⋅ 阅读:(23) ⋅ 点赞:(0)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        大家都知道fpga是好东西,但是大家不清楚的,是fpga可以帮助我们做什么。平时,我们在电商网站可以买到一些fpga开发板,但是这些开发板提供的demo案例,mcu也可以去做。这样就会造成一个疑问,似乎就没有必要学习fpga。但事实上,fpga本来就应该去做mcu和soc做不了的事情,而不应该重复他们已经做过的事情。

        所以,fpga要做的,应该是数量很多的低速接口、定制的高速接口以及算法加速功能。其中算法加速往往是最容易被大家忽视的部分。不管是pin上的信号,还是bus上的信号,本质上是一回事。所以,算法加速也是fpga非常擅长的一个领域。

1、是算法加速,而不是发明新算法

        有一些应用,本身其实是可以用软件实现的,比如检测轮询、图像处理等等。用软件实现,要么占用cpu资源,要么效率不高,但是用fpga可以极大提高运行效率。所以说这些算法本身就是存在的,并不是fpga发明的,我们用fpga,只是为了让运行速度更快一点。

2、先实现软件算法,再改成verilog代码

        用fpga做算法加速,最好不要一上来就写verilog代码,这是不好的习惯。对于大公司来说,算法工程师、软件工程师、软件工程师往往是分开来的。算法工程师负责算法和matlab仿真,软件工程师负责算法实现,fpga工程则负责算法加速。如果项目不大的话,这个时候可以自己先把软件写好,等软件没有问题之后,再切换成verilog代码,一方面实现起来心里比较有底,二来如果出现错误,也有一个参考比对。

3、以gpadc作为说明,如何做算法加速

        gpadc是一种常用的按键检测手段。很多芯片的外部pin不多,特别是sop或者qfn封装的时候,如果是这种情况,很多人都希望用adc实现按键的检测。因为只要adc检测的精度够,就可以实现多按键的检测,这样还不会浪费pin的资源。

        但是这样一项工作,如果是软件去做,还是会稍微麻烦一点,它需要做到这么几点,

算法步骤:
1)循环检测电压;
2)如果电压大于阈值,调到4;
3)检测有无置位,没有跳到5,有则跳到1;
4)检测有无置位,有则恢复为0,最后都是回到1;
5)等待一段时间;
6)重新检测电压,记录电压、置位,发送按键信息,跳到1.

        整个检测过程其实并不复杂,但是软件做的坏处就是比较费cpu。所以一般的soc都会在底层,也就是硬件层面实现按键检测。用户只要配置好寄存器,设置好驱动就可以了。

4、改造为verilog代码

        这是很明显的循环检测case,如果使用verilog+状态机处理的方法,其实是再合适不过的。首先还是设计好状态机代码,

// about state machine 

always@(posedge clk or negedge rst)
	if(!rst)
		state <= IDLE; // idle
	else
		state <= next_state;

always@(*)
	case(state)
		IDLE:
			if(in_valid)
				next_state = RECV; // receive data
			else
				next_state = IDLE;
		RECV:
			if(voltage_data >= 16'd200)
				next_state = VOLT_HIGH;
			else if(!key1_flag && !key2_flag && !key3_flag) // check voltage_data data
				next_state = WAIT;
			else
				next_state = IDLE;
		WAIT:
			if(counter == 16'd20) // wait for timer expired
				next_state = STABLE;
			else
				next_state = WAIT;
		STABLE:
			if(in_valid) // make sure data is valid
				next_state = TRIG_KEY;
			else
				next_state = IDLE;
		VOLT_HIGH:
			next_state = IDLE;
		TRIG_KEY:
			next_state = IDLE;
		default:
			next_state = IDLE;
	endcase

        有了状态机之后,还需要记录下当前的电压数值,

// about voltage_data

always@(posedge clk or negedge rst)
	if(!rst)
		voltage_data <= 16'h0;
	else if((state == IDLE || state == STABLE) && in_valid)
		voltage_data <= voltage;

        最后就是在不同的状态下,判断电压范围,并且设置不同的标志位信息,

// about key1

always@(posedge clk or negedge rst)
	if(!rst)
		key1_flag_prev <= 1'b0;
	else
		key1_flag_prev <= key1_flag;

always@(posedge clk or negedge rst)
	if(!rst)
		key1_flag <= 1'b0;
	else if(key1_flag && state == VOLT_HIGH)
		key1_flag <= 1'b0;
	else if(!key1_flag && state == TRIG_KEY && voltage_data >= 16'd100 && voltage_data < 16'd200)
		key1_flag <= 1'b1;

// about key2

always@(posedge clk or negedge rst)
	if(!rst)
		key2_flag_prev <= 1'b0;
	else
		key2_flag_prev <= key2_flag;

always@(posedge clk or negedge rst)
	if(!rst)
		key2_flag <= 1'b0;
	else if(key2_flag && state == VOLT_HIGH)
		key2_flag <= 1'b0;
	else if(!key2_flag && state == TRIG_KEY && voltage_data >= 16'd50 && voltage_data < 16'd100)
		key2_flag <= 1'b1;

// about key3

always@(posedge clk or negedge rst)
	if(!rst)
		key3_flag_prev <= 1'b0;
	else
		key3_flag_prev <= key3_flag;
		
always@(posedge clk or negedge rst)
	if(!rst)
		key3_flag <= 1'b0;
	else if(key3_flag && state == VOLT_HIGH)
		key3_flag <= 1'b0;
	else if(!key3_flag && state == TRIG_KEY && voltage_data >= 16'd0 && voltage_data < 16'd50)
		key3_flag <= 1'b1;

// about final result

assign key1 = key1_flag & !key1_flag_prev;
assign key2 = key2_flag & !key2_flag_prev;
assign key3 = key3_flag & !key3_flag_prev;

        理论上,只要adc检测的精度够,那么可以识别出很多的按键,可以帮助大大节省pin资源。

5、用testbench做交叉验证

        编写好verilog代码后,是非常忌讳立即去做fpga测试的。这个时候,我们可以准备一个testbench文件先做好仿真测试,

`timescale 1ns/1ps

module test();

reg clk;
reg rst;
reg in_valid;
reg[15:0] voltage;
wire key1;
wire key2;
wire key4;

initial begin 
	clk = 0;
	rst = 1;
	in_valid = 1;
	voltage = 250;
	#10 rst = 0;
	#30 rst = 1;
	#50 voltage = 80;
	#15 voltage = 0;
	#320 voltage = 250;
	#10000 $finish;
end

initial begin
	while(1)	
		clk = #5 ~clk;
end

voltage_top voltage_top(
	.clk(clk),
	.rst(rst),
	.in_valid(in_valid),
	.voltage(voltage),
	.key1(key1),
	.key2(key2),
	.key3(key3)
);

initial begin
	$dumpfile("hello.vcd");
	$dumpvars(0, test);
end

endmodule

        等到仿真没有问题,仿真的波形也ok的时候,就可以到实际板子看看具体的运行情况了。


网站公告

今日签到

点亮在社区的每一天
去签到