0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一种在HLS中插入HDL代码的方式

OpenFPGA 来源:OpenFPGA 2024-07-16 18:01 次阅读

很多人都比较反感用C/C++开发(HLS)FPGA,大家第一拒绝的理由就是耗费资源太多。但是HLS也有自己的优点,除了快速构建算法外,还有一个就是接口的生成,尤其对于AXI类接口,按照标准语法就可以很方便地生成相关接口。

那么有没有能利用HLS的优点,又囊括HDL的优点的方法呢?今天就来介绍一种在HLS中插入HDL代码的方式,结合两者的优势为FPGA开发打造一把“利剑”。

c56d0048-4279-11ef-b8af-92fbcf53809c.png

说明

接下来,将介绍如何创建 Vitis-HLS 项目并将其与自定义 Verilog 模块集成一起。

将插入两个黑盒函数 - 第一个在流水线区域(线路接口,ap_none),第二个在数据流区域(FIFO 接口,ap_ctrl_chain)。

步骤

1. 创建C/C++源文件(基于C的HLS模型+Testbench)

创建模块的 C/C++ 模型,其中包括函数源代码(模块预期行为)和测试平台(io 刺激和结果检查)。

根据ug1399-vitis-hls rtl黑盒,rtl黑盒受到几个因素的限制:

应该是Verilog(.v)代码。

必须具有唯一的时钟信号和唯一的高电平有效复位信号。

必须有一个 CE 信号,用于启用或停止 RTL IP。

可以使用 ap_ctrl_chain 或 ap_ctrl_none 块级控制协议。

仅支持 C++。

无法连接到顶层接口 I/O 信号。

不能直接作为被测设计(DUT)。

不支持结构或类类型接口。

main.cpp ——C/C++ 测试台。

#include"add.hpp"
intmain(void){
staticuint32_ta[1024];
staticuint32_tb[1024];
staticuint32_tc[1024];
staticuint32_tc_stream[1024];
for(uint32_ti=0;i< 1024; ++i) {
        a[i] = i;
        b[i] = i;
        c[i] = 0;
        c_stream[i] = 0;
    }
    top_module(a, b, c, c_stream);
    for (uint32_t i = 0; i < 1024; ++i) {
        if (c[i] != a[i] + b[i]) {
            printf("Data does not match. %d vs %d
", c[i], a[i] + b[i]);
            return -1;
        }
        if (c[i] != c_stream[i]) {
            printf("Data does not match. %d vs %d
", c[i], c_stream[i]);
            printf("Add modules have different results.
");
            return -2;
        }
    }
    printf("Test succesfull.
");
    return 0;
}

add.hpp——函数声明。

#ifndefADD_HPP
#defineADD_HPP
#include
#include
voidadd(uint32_ta,uint32_tb,uint32_t&c);
voidadd_stream(
hls::stream&a,
hls::stream&b,
hls::stream&c
);
voidscalar_to_stream(uint32_ta,hls::stream&a_stream);
voidstream_to_scalar(hls::stream&a_stream,uint32_t&a);
voidwrap(uint32_ta,uint32_tb,uint32_t&c);
voidtop_module(uint32_t*a,uint32_t*b,uint32_t*c,uint32_t*c_stream);
#endif

add.cpp——函数源代码。

#include"add.hpp"
voidadd(uint32_ta,uint32_tb,uint32_t&c){
c=a+b;
};
voidadd_stream(
hls::stream&a,
hls::stream&b,
hls::stream&c
){
c.write(a.read()+b.read());
};
voidscalar_to_stream(uint32_ta,hls::stream&a_stream){
a_stream.write(a);
};
voidstream_to_scalar(hls::stream&a_stream,uint32_t&a){
a=a_stream.read();
};
voidwrap(uint32_ta,uint32_tb,uint32_t&c){
#pragmaHLSDATAFLOW
hls::streamc_s;
hls::streama_s;
hls::streamb_s;
scalar_to_stream(a,a_s);
scalar_to_stream(b,b_s);
add_stream(a_s,b_s,c_s);
stream_to_scalar(c_s,c);
};
voidtop_module(uint32_t*a,uint32_t*b,uint32_t*c,uint32_t*c_stream){
#pragmaHLSINTERFACEmode=m_axiport=adepth=1024bundle=first
#pragmaHLSINTERFACEmode=m_axiport=bdepth=1024bundle=second
#pragmaHLSINTERFACEmode=m_axiport=cdepth=1024bundle=first
#pragmaHLSINTERFACEmode=m_axiport=c_streamdepth=1024bundle=second
#pragmaHLSINTERFACEmode=s_axiliteport=return
main_loop_pipeline:for(uint32_ti=0;i< 1024; ++i) {
        uint32_t c_o;
        uint32_t const a_t = a[i];
        uint32_t const b_t = b[i];
        add(a_t, b_t, c_o);
        c[i] = c_o;
    }
    main_loop_stream: for (uint32_t i = 0; i < 1024; ++i) {
        wrap(a[i], b[i], c_stream[i]);
    }
};

2. 为 Vitis HLS创建配置文件

Vitis HLS需要配置文件来构建项目。基本配置文件应包含

Part——FPGA 部件编号。

syn.top——顶级函数名称。

tb.file——测试台文件。

syn.file — HLS 中使用的文件。

在此示例中,cfg 文件的最小版本如下所示:

part=xc7z007sclg225-1
[hls]
syn.top=top_module
tb.file=main.cpp
syn.file=add.cpp
syn.file=add.hpp
package.output.format=ip_catalog
flow_target=vivado

3. 创建并构建最小项目

启动 Vitis,选择工作区并点击“创建 HLS 组件”。

c59fdf72-4279-11ef-b8af-92fbcf53809c.png

更改组件位置和名称,单击下一步。

c5bb16c0-4279-11ef-b8af-92fbcf53809c.png

选择从现有配置文件创建,点击下一步。

c5d8977c-4279-11ef-b8af-92fbcf53809c.png

项目结构如下所示:

c5dc3ada-4279-11ef-b8af-92fbcf53809c.png

无需添加额外的标志,只需仔细检查顶部函数是否是“top_module”,然后单击下一步。

c5f061a4-4279-11ef-b8af-92fbcf53809c.png

选择芯片(默认部分应该是cfg文件中写的),单击下一步

c61d5574-4279-11ef-b8af-92fbcf53809c.png

确认flow_target和package.output.format,点击next。

c6378ad4-4279-11ef-b8af-92fbcf53809c.png

检查摘要并单击完成。

c6637f72-4279-11ef-b8af-92fbcf53809c.png

最后运行所有步骤以确保所有配置均已配置并正常运行。

c681d5b2-4279-11ef-b8af-92fbcf53809c.png

4.创建blackbox函数json

在此步骤中,我们将用 blackbox verilog 代码替换我们的添加函数。在pipeline区域:

c6a4b29e-4279-11ef-b8af-92fbcf53809c.png

右键单击 hls_component 并单击“创建 RTL blackbox”,将生成 JSON 文件,描述 verilog 模块与其 C 函数之间的连接。

c74dba6a-4279-11ef-b8af-92fbcf53809c.png

选择包含 C 模块描述的文件。

c761e184-4279-11ef-b8af-92fbcf53809c.png

选择端口方向并填写RTL组配置(verilog模块中的端口名称)。

c784ef58-4279-11ef-b8af-92fbcf53809c.pngc79ca01c-4279-11ef-b8af-92fbcf53809c.png

选择verilog文件,如有必要再填写其他框,单击下一步。

c7b584f6-4279-11ef-b8af-92fbcf53809c.png

删除 ap_ctrl_chain_protocol 字符串,保留空白。单击完成。

c7d1d89a-4279-11ef-b8af-92fbcf53809c.png

对 add_stream 函数重复所有这些步骤。

输入先进先出:

c7eb3524-4279-11ef-b8af-92fbcf53809c.png

输出先进先出:

c8042cbe-4279-11ef-b8af-92fbcf53809c.png

概括:

c8182f16-4279-11ef-b8af-92fbcf53809c.pngc82d1c32-4279-11ef-b8af-92fbcf53809c.png

不要修改 ap_ctrl_chain 信号,因为该模块将使用 ap_ctrl_chain 协议。

c843380a-4279-11ef-b8af-92fbcf53809c.png

此后,hls_component 文件夹中应该会生成两个 json 文件。

add.json

{
"c_files":[
{
"c_file":"add.cpp",
"cflag":""
}
],
"c_function_name":"add",
"rtl_files":[
"add.v"
],
"c_parameters":[
{
"c_name":"a",
"c_port_direction":"in",
"rtl_ports":{
"data_read_in":"a"
}
},
{
"c_name":"b",
"c_port_direction":"in",
"rtl_ports":{
"data_read_in":"b"
}
},
{
"c_name":"c",
"c_port_direction":"out",
"rtl_ports":{
"data_write_out":"c",
"data_write_valid":"c_vld"
}
}
],
"rtl_top_module_name":"add",
"rtl_performance":{
"II":"0",
"latency":"0"
},
"rtl_resource_usage":{
"BRAM":"0",
"DSP":"0",
"FF":"0",
"LUT":"0",
"URAM":"0"
},
"rtl_common_signal":{
"module_clock":"ap_clk",
"module_reset":"ap_rst",
"module_clock_enable":"ap_ce",
"ap_ctrl_chain_protocol_idle":"",
"ap_ctrl_chain_protocol_start":"",
"ap_ctrl_chain_protocol_ready":"",
"ap_ctrl_chain_protocol_done":"",
"ap_ctrl_chain_protocol_continue":""
}
}

add_stream.json

{
"c_files":[
{
"c_file":"add.cpp",
"cflag":""
}
],
"c_function_name":"add_stream",
"rtl_files":[
"add_stream.v"
],
"c_parameters":[
{
"c_name":"a",
"c_port_direction":"in",
"rtl_ports":{
"FIFO_empty_flag":"a_empty_flag",
"FIFO_read_enable":"a_read_enable",
"FIFO_data_read_in":"a"
}
},
{
"c_name":"b",
"c_port_direction":"in",
"rtl_ports":{
"FIFO_empty_flag":"b_empty_flag",
"FIFO_read_enable":"b_read_enable",
"FIFO_data_read_in":"b"
}
},
{
"c_name":"c",
"c_port_direction":"out",
"rtl_ports":{
"FIFO_full_flag":"c_full_flag",
"FIFO_write_enable":"c_write_enable",
"FIFO_data_write_out":"c"
}
}
],
"rtl_top_module_name":"add_stream",
"rtl_performance":{
"II":"0",
"latency":"0"
},
"rtl_resource_usage":{
"BRAM":"0",
"DSP":"0",
"FF":"0",
"LUT":"0",
"URAM":"0"
},
"rtl_common_signal":{
"module_clock":"ap_clk",
"module_reset":"ap_rst",
"module_clock_enable":"ap_ce",
"ap_ctrl_chain_protocol_idle":"ap_idle",
"ap_ctrl_chain_protocol_start":"ap_start",
"ap_ctrl_chain_protocol_ready":"ap_ready",
"ap_ctrl_chain_protocol_done":"ap_done",
"ap_ctrl_chain_protocol_continue":"ap_continue"
}
}

主文件夹应与此类似:

c86c30b6-4279-11ef-b8af-92fbcf53809c.png

hls_config.cfg 文件应该添加两新行( syn.blackbox.file)

part=xc7z007sclg225-1
[hls]
flow_target=vivado
csim.code_analyzer=0
syn.top=top_module
syn.blackbox.file=add.json
syn.blackbox.file=add_stream.json
tb.file=main.cpp
syn.file=add.cpp
syn.file=add.hpp

5.创建Verilog黑盒函数

函数“add”必须具有ap_none接口,并且 ap_none 作为模块接口。(有关模块接口的更多信息,请查看https://docs.amd.com/r/en-US/ug1399-vitis-hls/JSON-File-for-RTL-Blackbox 。)

c87f4f5c-4279-11ef-b8af-92fbcf53809c.pngc8abed0a-4279-11ef-b8af-92fbcf53809c.png

根据UG1399,端口a和b是32位宽度的输入端口,输出c端口也是32位宽度,但带有额外的有效信号,我们称之为c_vld。模块还需要ap_clk,ap_ce,ap_rst端口。

Verilog 如下所示:

add.v

`timescale1ns/1ps
moduleadd(
input[31:0]a,
input[31:0]b,
output[31:0]c,
outputc_vld,
inputap_ce,
inputap_rst,
inputap_clk
);
reg[31:0]c_d;
regc_vld_d;
assignc=c_d;
assignc_vld=c_vld_d;
always@(posedgeap_clk)begin
if(ap_rst==1'b1)begin
c_d<= 32'b0;
        c_vld_d <= 1'b0;
    end else begin
        c_d <= (a + b) & {32{ap_ce}};
        c_vld_d <= ap_ce;
    end
end
endmodule

运行 C 综合和 C/RTL 协同仿真。能够在 HLS 模块中看到打包的 add.v 文件。

c8bf1c86-4279-11ef-b8af-92fbcf53809c.png

单击 hls_config.cfg 文件,在 Vitis GUI 的帮助下将 cosim.trace_level 更改为全部并运行联合仿真。

c8dd1fb0-4279-11ef-b8af-92fbcf53809c.png

单击波形查看器。Vivado 会弹出 XSIM。

c902e68c-4279-11ef-b8af-92fbcf53809c.png

将 grp_add_fu_134 信号添加到 wcfg

c91651d6-4279-11ef-b8af-92fbcf53809c.pngc92b4244-4279-11ef-b8af-92fbcf53809c.png

函数行为很奇怪,接下来在 json 中更改黑盒函数 II,看看它如何影响仿真。打开 add.json 并将 II 更改为 10。再次运行 C 综合并重新运行 C/RTL 协同仿真。

c9487dc8-4279-11ef-b8af-92fbcf53809c.png

add.v 模块是否良好且可以正常工作?其行为是否正确?模块是否正常工作由哪些因素决定?“fixing”模块对资源使用有何影响?

那么 add_stream 呢?函数位于数据流区域,并且必须包含 fifo 端口和 ap_ctrl_chain 协议。

add_stream.v

`timescale1ns/1ps
moduleadd_stream(
input[31:0]a,
inputa_empty_flag,
outputa_read_enable,
input[31:0]b,
inputb_empty_flag,
outputb_read_enable,
output[31:0]c,
inputc_full_flag,
outputc_write_enable,
outputap_idle,
inputap_start,
outputap_ready,
outputap_done,
inputap_continue,
inputap_ce,
inputap_rst,
inputap_clk
);
rega_read_enable_d;
regb_read_enable_d;
regc_write_enable_d;
reg[31:0]c_d;
assigna_read_enable=a_read_enable_d;
assignb_read_enable=b_read_enable_d;
assignc_write_enable=c_write_enable_d;
assignc=c_d;
assignap_idle=!ap_start;
assignap_ready=ap_start;
assignap_done=ap_start;
//Flagsarenegated...
assignflags_good=a_empty_flag&&b_empty_flag&&c_full_flag;
assignhs_good=ap_start&&ap_continue;
always@(posedgeap_clk)begin
if(ap_rst==1'b1)begin
a_read_enable_d<= 0;
            b_read_enable_d <= 0;
            c_write_enable_d <= 0;
            c_d <= 0;
    end else if (ap_ce == 1'b1) begin
            a_read_enable_d <= flags_good && hs_good;
            b_read_enable_d <= flags_good && hs_good;
            c_write_enable_d <= flags_good && hs_good;
            c_d <= a + b;
        end
    end
endmodule

看起来放置在数据流区域的模块工作正常:

c970332c-4279-11ef-b8af-92fbcf53809c.png

打开 add_stream.json 并将延迟更改为 10。再次运行 C 综合并重新运行 C/RTL 协同仿真。这会影响仿真吗?

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1620

    文章

    21494

    浏览量

    598396
  • 函数
    +关注

    关注

    3

    文章

    4232

    浏览量

    61930
  • 代码
    +关注

    关注

    30

    文章

    4656

    浏览量

    67698
  • HLS
    HLS
    +关注

    关注

    1

    文章

    128

    浏览量

    23898

原文标题:在HLS中插入HDL代码

文章出处:【微信号:Open_FPGA,微信公众号:OpenFPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何用HLS实现UART

    UART 是一种旧的串行通信机制,但仍在很多平台中使用。它在 HDL 语言中的实现并不棘手,可以被视为本科生的作业。在这里,我将通过这个例子来展示 HLS
    的头像 发表于 11-20 09:48 434次阅读
    如何用<b class='flag-5'>HLS</b>实现UART

    如何用HLS实现UART呢?

    UART 是一种旧的串行通信机制,但仍在很多平台中使用。它在 HDL 语言中的实现并不棘手,可以被视为本科生的作业。在这里,我将通过这个例子来展示 HLS
    的头像 发表于 11-20 09:50 511次阅读
    如何用<b class='flag-5'>HLS</b>实现UART呢?

    编程是一种思维方式,而代码一种表现形式,硬件只不过是对思维方式的物理体现

    编程是一种思维方式,而代码一种表现形式,硬件只不过是对思维方式的物理体现关于这句话,你怎么看?
    发表于 08-25 13:18

    如何在HLS 14.3编写pow功能?

    嗨〜 如何在HLS 14.3编写pow功能? HLS 14.3不支持exp和pow功能。我我的代码
    发表于 03-05 13:40

    手动插入代码MHC合并过程不会出现延迟吗?

    HIIS有一种方法将HANCE项目中的手动代码结合起来,当从配置器生成代码时,这些手动插入代码
    发表于 05-05 16:55

    Vivado中进行HDL代码设计

    Vivado中进行HDL代码设计,不仅需要描述数字逻辑电路的常用功能,还要考虑如何发挥Xilinx器件的架构优势。目前常用的HDL语言有
    发表于 09-29 10:08

    【正点原子FPGA连载】第HLS简介-领航者ZYNQ之HLS 开发指南

    Vivado HLS可以使用三语言进行设计开发,分别是 C、C++ 和 SystemC。其中C语言是一种非常通用的面向过程的编程语言,我们
    发表于 10-10 16:44

    一种基于信号延迟的光网络攻击方式

    针对光网络攻击易被发现的问题,提出一种基于信号延迟插入的光网络攻击方式。该方法不改变链路光学性能的基础上,利用信号延迟系统
    发表于 03-20 15:34 27次下载
    <b class='flag-5'>一种</b>基于信号延迟的光网络攻击<b class='flag-5'>方式</b>

    使用教程分享:Zynq AP SoC设计中高效使用HLS IP(

    高层次综合设计最常见的的使用就是为CPU创建个加速器,将在CPU执行的代码移动到FPGA可编程逻辑去提高性能。本文展示了如何在Zynq AP SoC设计中使用HLS IP。
    发表于 02-07 18:08 3487次阅读
    使用教程分享:<b class='flag-5'>在</b>Zynq AP SoC设计中高效使用<b class='flag-5'>HLS</b> IP(<b class='flag-5'>一</b>)

    如何在C代码插入寄存器?

    寄存。这起到了隔离关键路径的作用。 但是,如果使用的RTL代码HLS转换生成的,例如使用Vitis HLS综合的,其可读性较差,想要在其生成的HDL
    的头像 发表于 02-02 17:07 2946次阅读
    如何在C<b class='flag-5'>代码</b><b class='flag-5'>中</b><b class='flag-5'>插入</b>寄存器?

    并行CRC电路HDL代码的快速生成

    CRC校验的实现基于串行位移寄存器,如果要处理并行数据,需要对电路进行改进。本文介绍了一种并行CRC电路HDL代码的快速生成钟算法,只需要带入不同的参数,可自动生成不同长度以及并行度的并行CRC电路的
    发表于 03-28 09:29 16次下载
    并行CRC电路<b class='flag-5'>HDL</b><b class='flag-5'>代码</b>的快速生成

    一种可以快速将CFG文件参数固化到应用代码的实现方式

    固化参数的产品而言,将参数固化代码中会是项明确的需求。本文介绍一种可以快速将CFG文件参数固化到应用代码
    的头像 发表于 02-15 13:36 2887次阅读
    <b class='flag-5'>一种</b>可以快速将CFG文件参数固化到应用<b class='flag-5'>代码</b><b class='flag-5'>中</b>的实现<b class='flag-5'>方式</b>

    Vitis HLS工具简介及设计流程

    Vitis HLS一种高层次综合工具,支持将 C、C++ 和 OpenCL 函数硬连线到器件逻辑互连结构和 RAM/DSP 块上。Vitis HLS 可在Vitis 应用加速开发流程
    的头像 发表于 05-25 09:43 2199次阅读

    MATLAB/simulink建模时的两不同实现方式

    导读:本期文章主要介绍MATLAB/simulink建模时的两不同实现方式一种是直接用现成的文件库
    的头像 发表于 09-15 10:07 1709次阅读

    FPGA基础之HLS

    1、HLS简介 HLS(High-Level Synthesis)高层综合,就是将 C/C++的功能用 RTL 来实现,将 FPGA 的组件个软件环境
    的头像 发表于 12-02 12:30 4460次阅读