很多人都比較反感用C/C++開發(fā)(HLS)FPGA,大家第一拒絕的理由就是耗費資源太多。但是HLS也有自己的優(yōu)點,除了快速構建算法外,還有一個就是接口的生成,尤其對于AXI類接口,按照標準語法就可以很方便地生成相關接口。
那么有沒有能利用HLS的優(yōu)點,又囊括HDL的優(yōu)點的方法呢?今天就來介紹一種在HLS中插入HDL代碼的方式,結合兩者的優(yōu)勢為FPGA開發(fā)打造一把“利劍”。
說明
接下來,將介紹如何創(chuàng)建 Vitis-HLS 項目并將其與自定義 Verilog 模塊集成一起。
將插入兩個黑盒函數(shù) - 第一個在流水線區(qū)域(線路接口,ap_none),第二個在數(shù)據(jù)流區(qū)域(FIFO 接口,ap_ctrl_chain)。
步驟
1. 創(chuàng)建C/C++源文件(基于C的HLS模型+Testbench)
創(chuàng)建模塊的 C/C++ 模型,其中包括函數(shù)源代碼(模塊預期行為)和測試平臺(io 刺激和結果檢查)。
根據(jù)ug1399-vitis-hls rtl黑盒,rtl黑盒受到幾個因素的限制:
應該是Verilog(.v)代碼。
必須有一個 CE 信號,用于啟用或停止 RTL IP。
可以使用 ap_ctrl_chain 或 ap_ctrl_none 塊級控制協(xié)議。
僅支持 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——函數(shù)聲明。
#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——函數(shù)源代碼。
#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::stream c_s; hls::stream a_s; hls::stream b_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創(chuàng)建配置文件
Vitis HLS需要配置文件來構建項目?;九渲梦募?/p>
Part——FPGA 部件編號。
syn.top——頂級函數(shù)名稱。
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. 創(chuàng)建并構建最小項目
啟動 Vitis,選擇工作區(qū)并點擊“創(chuàng)建 HLS 組件”。
更改組件位置和名稱,單擊下一步。
選擇從現(xiàn)有配置文件創(chuàng)建,點擊下一步。
項目結構如下所示:
無需添加額外的標志,只需仔細檢查頂部函數(shù)是否是“top_module”,然后單擊下一步。
選擇芯片(默認部分應該是cfg文件中寫的),單擊下一步
確認flow_target和package.output.format,點擊next。
檢查摘要并單擊完成。
最后運行所有步驟以確保所有配置均已配置并正常運行。
4.創(chuàng)建blackbox函數(shù)json
在此步驟中,我們將用 blackbox verilog 代碼替換我們的添加函數(shù)。在pipeline區(qū)域:
右鍵單擊 hls_component 并單擊“創(chuàng)建 RTL blackbox”,將生成 JSON 文件,描述 verilog 模塊與其 C 函數(shù)之間的連接。
選擇包含 C 模塊描述的文件。
選擇端口方向并填寫RTL組配置(verilog模塊中的端口名稱)。
選擇verilog文件,如有必要再填寫其他框,單擊下一步。
刪除 ap_ctrl_chain_protocol 字符串,保留空白。單擊完成。
對 add_stream 函數(shù)重復所有這些步驟。
輸入先進先出:
輸出先進先出:
概括:
不要修改 ap_ctrl_chain 信號,因為該模塊將使用 ap_ctrl_chain 協(xié)議。
此后,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.創(chuàng)建Verilog黑盒函數(shù)
函數(shù)“add”必須具有ap_none接口,并且 ap_none 作為模塊接口。(有關模塊接口的更多信息,請查看https://docs.amd.com/r/en-US/ug1399-vitis-hls/JSON-File-for-RTL-Blackbox 。)
根據(jù)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 協(xié)同仿真。能夠在 HLS 模塊中看到打包的 add.v 文件。
單擊 hls_config.cfg 文件,在 Vitis GUI 的幫助下將 cosim.trace_level 更改為全部并運行聯(lián)合仿真。
單擊波形查看器。Vivado 會彈出 XSIM。
將 grp_add_fu_134 信號添加到 wcfg
函數(shù)行為很奇怪,接下來在 json 中更改黑盒函數(shù) II,看看它如何影響仿真。打開 add.json 并將 II 更改為 10。再次運行 C 綜合并重新運行 C/RTL 協(xié)同仿真。
add.v 模塊是否良好且可以正常工作?其行為是否正確?模塊是否正常工作由哪些因素決定?“fixing”模塊對資源使用有何影響?
那么 add_stream 呢?函數(shù)位于數(shù)據(jù)流區(qū)域,并且必須包含 fifo 端口和 ap_ctrl_chain 協(xié)議。
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
看起來放置在數(shù)據(jù)流區(qū)域的模塊工作正常:
打開 add_stream.json 并將延遲更改為 10。再次運行 C 綜合并重新運行 C/RTL 協(xié)同仿真。這會影響仿真嗎?
-
FPGA
+關注
關注
1629文章
21736瀏覽量
603384 -
函數(shù)
+關注
關注
3文章
4331瀏覽量
62618 -
代碼
+關注
關注
30文章
4788瀏覽量
68611 -
HLS
+關注
關注
1文章
129瀏覽量
24113
原文標題:在HLS中插入HDL代碼
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論