很多人都比較反感用C/C++開發(HLS)FPGA,大家第一拒絕的理由就是耗費資源太多。但是HLS也有自己的優點,除了快速構建算法外,還有一個就是接口的生成,尤其對于AXI類接口,按照標準語法就可以很方便地生成相關接口。
那么有沒有能利用HLS的優點,又囊括HDL的優點的方法呢?今天就來介紹一種在HLS中插入HDL代碼的方式,結合兩者的優勢為FPGA開發打造一把“利劍”。

說明
接下來,將介紹如何創建 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;i1024;?++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;i1024;?++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 組件”。

更改組件位置和名稱,單擊下一步。

選擇從現有配置文件創建,點擊下一步。

項目結構如下所示:

無需添加額外的標志,只需仔細檢查頂部函數是否是“top_module”,然后單擊下一步。

選擇芯片(默認部分應該是cfg文件中寫的),單擊下一步

確認flow_target和package.output.format,點擊next。

檢查摘要并單擊完成。

最后運行所有步驟以確保所有配置均已配置并正常運行。

4.創建blackbox函數json
在此步驟中,我們將用 blackbox verilog 代碼替換我們的添加函數。在pipeline區域:

右鍵單擊 hls_component 并單擊“創建 RTL blackbox”,將生成 JSON 文件,描述 verilog 模塊與其 C 函數之間的連接。

選擇包含 C 模塊描述的文件。

選擇端口方向并填寫RTL組配置(verilog模塊中的端口名稱)。


選擇verilog文件,如有必要再填寫其他框,單擊下一步。

刪除 ap_ctrl_chain_protocol 字符串,保留空白。單擊完成。

對 add_stream 函數重復所有這些步驟。
輸入先進先出:

輸出先進先出:

概括:


不要修改 ap_ctrl_chain 信號,因為該模塊將使用 ap_ctrl_chain 協議。

此后,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"
}
}
主文件夾應與此類似:

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 。)


根據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 文件。

單擊 hls_config.cfg 文件,在 Vitis GUI 的幫助下將 cosim.trace_level 更改為全部并運行聯合仿真。

單擊波形查看器。Vivado 會彈出 XSIM。

將 grp_add_fu_134 信號添加到 wcfg


函數行為很奇怪,接下來在 json 中更改黑盒函數 II,看看它如何影響仿真。打開 add.json 并將 II 更改為 10。再次運行 C 綜合并重新運行 C/RTL 協同仿真。

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
看起來放置在數據流區域的模塊工作正常:

打開 add_stream.json 并將延遲更改為 10。再次運行 C 綜合并重新運行 C/RTL 協同仿真。這會影響仿真嗎?
-
FPGA
+關注
關注
1660文章
22408瀏覽量
636199 -
函數
+關注
關注
3文章
4417瀏覽量
67499 -
代碼
+關注
關注
30文章
4967瀏覽量
73954 -
HLS
+關注
關注
1文章
135瀏覽量
25829
原文標題:在HLS中插入HDL代碼
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
Vivado中進行HDL代碼設計
【正點原子FPGA連載】第一章HLS簡介-領航者ZYNQ之HLS 開發指南
一種基于信號延遲的光網絡攻擊方式
使用教程分享:在Zynq AP SoC設計中高效使用HLS IP(一)
并行CRC電路HDL代碼的快速生成
一種在HLS中插入HDL代碼的方式
評論