一文详解HLS从C/C++到VHDL的转换

高层次综合(high level synthesis, hls)是xilinx公司推出的最新一代的fpga设计工具,它能让用户通过编写c/c++等高级语言代码实现rtl级的硬件功能。随着这款工具的出现,软硬之间的区别越来越模糊,即使你对于硬件完全不懂,你也能编写出符合工程功能要求的rtl代码。看到hls工具具有如此神奇的功能,你是否想立即尝试一下呢?
接下来我们将谈谈hls相关的简单操作以及c/c++到vhdl的一个转换关系:
首先确保你已经下好了vivado相关套件,并且hls的license(官网有试用的,但只能用一个月)已经下到,然后打开桌面上,就可以按照一般的软件建工程一样,这里我们写了一个很简单的函数int andfunction(int array[2],int array1[2]);这里我们注意到函数返回值是整形,同时两个形参是都是数组,这个函数代码如下:
int andfunction(int array[2],int array1[2])
{
int i,sum;
sum=0;
for(i=0;i《2;i++)
{
array[i]=i*2;
array1[i]=array[i]+i;
sum+=array1[i];
}
return sum;
}
写好了这个函数后,那么我们不妨看看综合后的vhdl是怎样的。点击绿色三角按钮后生成硬件描述语言,solution1-》syn-》vhdl里可以看到生成的vhdl,代码如下:
-- ==============================================================
-- rtl generated by vivado(tm) hls - high-level synthesis from c, c++ and systemc
-- version: 2012.4
-- copyright (c) 2012 xilinx inc. all rights reserved.
--
-- ===========================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity andfunction is
port (
ap_clk : in std_logic;
ap_rst : in std_logic;
ap_start : in std_logic;
ap_done : out std_logic;
ap_idle : out std_logic;
ap_ready : out std_logic;
array_r_address0 : out std_logic_vector (0 downto 0);
array_r_ce0 : out std_logic;
array_r_we0 : out std_logic;
array_r_d0 : out std_logic_vector (31 downto 0);
array1_address0 : out std_logic_vector (0 downto 0);
array1_ce0 : out std_logic;
array1_we0 : out std_logic;
array1_d0 : out std_logic_vector (31 downto 0);
ap_return : out std_logic_vector (31 downto 0) );
end;
architecture behav of andfunction is
attribute core_generation_info : string;
attribute core_generation_info of behav : architecture is
“andfunction,hls_ip_2012_4,{hls_input_type=c,hls_input_float=0,hls_input_fixed=0,hls_input_part=xc5vlx110tff1136-1,hls_input_clock=10.000000,hls_input_arch=others,hls_syn_clock=3.090000,hls_syn_lat=3,hls_syn_tpt=none,hls_syn_mem=0,hls_syn_dsp=0,hls_syn_ff=3,hls_syn_lut=8}”;
constant ap_const_logic_1 : std_logic := ‘1’;
constant ap_const_logic_0 : std_logic := ‘0’;
constant ap_st_st1_fsm_0 : std_logic_vector (0 downto 0) := “0”;
constant ap_st_st2_fsm_1 : std_logic_vector (0 downto 0) := “1”;
constant ap_const_lv2_0 : std_logic_vector (1 downto 0) := “00”;
constant ap_const_lv1_0 : std_logic_vector (0 downto 0) := “0”;
constant ap_const_lv2_2 : std_logic_vector (1 downto 0) := “10”;
constant ap_const_lv2_1 : std_logic_vector (1 downto 0) := “01”;
constant ap_const_lv32_3 : std_logic_vector (31 downto 0) := “00000000000000000000000000000011”;
signal ap_cs_fsm : std_logic_vector (0 downto 0) := “0”;
signal i_1_fu_66_p2 : std_logic_vector (1 downto 0);
signal i_reg_42 : std_logic_vector (1 downto 0);
signal exitcond1_fu_60_p2 : std_logic_vector (0 downto 0);
signal i_cast_fu_54_p1 : std_logic_vector (31 downto 0);
signal tmp_fu_72_p2 : std_logic_vector (1 downto 0);
signal tmp_1_fu_83_p2 : std_logic_vector (1 downto 0);
signal ap_ns_fsm : std_logic_vector (0 downto 0);
begin
-- the current state (ap_cs_fsm) of the state machine. --
ap_cs_fsm_assign_proc : process(ap_clk)
begin
if (ap_clk‘event and ap_clk = ’1‘) then
if (ap_rst = ’1‘) then
ap_cs_fsm 《= ap_st_st1_fsm_0;
else
ap_cs_fsm 《= ap_ns_fsm;
end if;
end if;
end process;
-- ap_reg assign process. --
ap_reg_proc : process(ap_clk)
begin
if (ap_clk’event and ap_clk = ‘1’) then
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0))) then
i_reg_42 《= i_1_fu_66_p2;
elsif (((ap_st_st1_fsm_0 = ap_cs_fsm) and not((ap_start = ap_const_logic_0)))) then
i_reg_42 《= ap_const_lv2_0;
end if;
end if;
end process;
-- the next state (ap_ns_fsm) of the state machine. --
ap_ns_fsm_assign_proc : process(ap_start, ap_cs_fsm, exitcond1_fu_60_p2)
begin
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and not((exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
ap_ns_fsm 《= ap_st_st1_fsm_0;
elsif ((((ap_st_st1_fsm_0 = ap_cs_fsm) and not((ap_start = ap_const_logic_0))) or ((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
ap_ns_fsm 《= ap_st_st2_fsm_1;
else
ap_ns_fsm 《= ap_cs_fsm;
end if;
end process;
-- ap_done assign process. --
ap_done_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and not((exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
ap_done 《= ap_const_logic_1;
else
ap_done 《= ap_const_logic_0;
end if;
end process;
-- ap_idle assign process. --
ap_idle_assign_proc : process(ap_start, ap_cs_fsm)
begin
if ((not((ap_const_logic_1 = ap_start)) and (ap_st_st1_fsm_0 = ap_cs_fsm))) then
ap_idle 《= ap_const_logic_1;
else
ap_idle 《= ap_const_logic_0;
end if;
end process;
-- ap_ready assign process. --
ap_ready_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and not((exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
ap_ready 《= ap_const_logic_1;
else
ap_ready 《= ap_const_logic_0;
end if;
end process;
ap_return 《= ap_const_lv32_3;
array1_address0 《= i_cast_fu_54_p1(1 - 1 downto 0);
-- array1_ce0 assign process. --
array1_ce0_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0))) then
array1_ce0 《= ap_const_logic_1;
else
array1_ce0 《= ap_const_logic_0;
end if;
end process;
array1_d0 《= std_logic_vector(resize(unsigned(tmp_1_fu_83_p2),32));
-- array1_we0 assign process. --
array1_we0_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if ((((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
array1_we0 《= ap_const_logic_1;
else
array1_we0 《= ap_const_logic_0;
end if;
end process;
array_r_address0 《= i_cast_fu_54_p1(1 - 1 downto 0);
-- array_r_ce0 assign process. --
array_r_ce0_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if (((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0))) then
array_r_ce0 《= ap_const_logic_1;
else
array_r_ce0 《= ap_const_logic_0;
end if;
end process;
array_r_d0 《= std_logic_vector(resize(unsigned(tmp_fu_72_p2),32));
-- array_r_we0 assign process. --
array_r_we0_assign_proc : process(ap_cs_fsm, exitcond1_fu_60_p2)
begin
if ((((ap_st_st2_fsm_1 = ap_cs_fsm) and (exitcond1_fu_60_p2 = ap_const_lv1_0)))) then
array_r_we0 《= ap_const_logic_1;
else
array_r_we0 《= ap_const_logic_0;
end if;
end process;
exitcond1_fu_60_p2 《= “1” when (i_reg_42 = ap_const_lv2_2) else “0”;
i_1_fu_66_p2 《= std_logic_vector(unsigned(i_reg_42) + unsigned(ap_const_lv2_1));
i_cast_fu_54_p1 《= std_logic_vector(resize(unsigned(i_reg_42),32));
tmp_1_fu_83_p2 《= std_logic_vector(unsigned(tmp_fu_72_p2) + unsigned(i_reg_42));
tmp_fu_72_p2 《= std_logic_vector(shift_left(unsigned(i_reg_42),to_integer(unsigned(‘0’ & ap_const_lv2_1(2-1 downto 0)))));
end behav;
看到这么长一大串代码后,你或许会说看起来太复杂来人,还不如自己写;一两个简单的可以自己写,成千上万个呢?这里我们不去关注architecture里面具体的实现过程(这里面包含很多优化),这里我们仅仅讨论从c/c++到vhdl的entity的关系。
看到vhdl后,你也许第一眼就看到了vhdl中的entity了,但是你不一定理解其管脚到底指代什么,下面给出一张int andfunction(int array[2],int array1[2])这个函数的硬件结构图
硬件引脚的具体含义是(这部分是参考的):
ap_clk:设计的时钟信号
ap_rst:设计的复位信号
ap_start:开始计算的开始信号
ap_done:计算结束和输出就绪的完成信号
ap_idle:表示实体(设计)空闲的空闲信号
ap_ready:表示设计为新输入数据做好准备,与ap_idle 配合使用
ap_return:设计的返回值
name_address:存储器的读地址 (name指代array或array1,因为它们都是数组,在硬件中综合成了寄存器)
name_ce0:存储器的芯片使能
name_we0:存储器的写使能
name_do0:存储器的写数据
ap_return: 函数返回值端口
分析上面的硬件管脚,其中ap_clk、ap_rst、ap_start、ap_done、ap_idle、ap_ready为大多数综合后硬件默认必有的,因为这些引脚便于处理器对其进行控制;name_address0、name_ce0、name_we0、name_do0这些引脚是为了获取所使用到寄存器的状态,在这里我们应当注意:数组开辟的空间尽量不要大,过大容易造成硬件资源不足,无法开辟满足要求的寄存器。
今天就对hls从c/c++到vhdl的转换大致说到这里,由于刚接触这一块,难免有所纰漏,欢迎大家指出!

微芯宣布与Cartesiam、Edge Impulse和Motion Gestures达成合作
proteus步进电机仿真
语音合成芯片的特点及选型
SKJ-II型数字随动系统中PWM功率变换控制系统
Omniverse 课程系列 7:使用 Isaac Sim 实现机器人仿真入门
一文详解HLS从C/C++到VHDL的转换
ARHT和伦敦商学院合作推出了实时全息讲师
全新结构开放数控系统的研究
c语言中的转义字符
3D面部模型实现一部智能手机就行了
磁敏二极管的工作原理_磁敏二极管适用于哪些场合
Aptiv公司已经在世界多地投放了自动驾驶汽车
汉莎航空集团已批准旗下航空公司订购40架当前最为先进的飞机
基于Moxa UC系列应用的嵌入式系统解决方案
智能家居催生的智能摄像头会走向何方?
赛灵思FPGA与VMware vSphere相结合实现高吞吐量、低时延ML推断性能
高通2亿像素传感器的智能手机不远了
四大瓶颈制约新能源汽车产业发展
KTV包房中如何摆放音响之八大法则
如何保障汽车信息娱乐和机群系统的热安全性