本次學(xué)習(xí)參考Stratus內(nèi)置的學(xué)習(xí)例程(simple_p2p)挠唆,學(xué)習(xí)內(nèi)容主要如下所示:
- Stratus HLS軟件運行需要的必要文件及其寫法
- Stratus HLS軟件操作方式
- Stratus HLS內(nèi)置的p2p端口的基本使用(非流水線)
- Stratus HLS自定義數(shù)據(jù)類型
1.Stratus HLS必要文件與寫法
Stratus工程所需要的文件如下圖所示:
文件 | 類型 | 說明 |
---|---|---|
設(shè)計文件 | cpp+h | 描述設(shè)計的頭文件和cpp文件 |
TestBench | cpp+h | 描述測試平臺的頭文件和cpp文件 |
System | cpp+h | 連接設(shè)計文件和TestBench的頭文件和cpp文件 |
main.cpp | cpp | 整個仿真平臺的頂層文件 |
project.tcl | tcl | 指定工程配置(仿真選項和綜合選項)的tcl文件 |
Makefile | makefile | 由project.tcl生成的makefile文件 |
1.1.設(shè)計文件
設(shè)計文件的頭文件如下所示:
#ifndef __NEW1__H
#define __NEW1__H
#include "cynw_p2p.h" // p2p端口的頭文件,如需使用cynw_p2p則需要引用該頭文件
#include "new1_input_type.h" // 類型new1_INPUT_DT的頭文件
#include "new1_output_type.h" // 類型new1_OUTPUT_DT的頭文件
SC_MODULE(new1) { // 定義模塊new1
public:
cynw_p2p < new1_INPUT_DT >::in inputs; // 一個p2p輸入端口
cynw_p2p < new1_OUTPUT_DT >::out outputs; // 一個p2p輸出端口
// Declaration of clock and reset parameters
sc_in_clk clk; // 時鐘端口狈谊,類型為sc_in_clk
sc_in < bool > rst; // 復(fù)位端口
SC_CTOR(new1):inputs("inputs"), outputs("outputs"), clk("clk"), rst("rst") {// 構(gòu)造函數(shù)
SC_CTHREAD(thread1, clk.pos()); // 定義線程thread1,綁定時鐘上升沿
reset_signal_is(rst,0); // 定義復(fù)位為0時有效
// Connect the clk and rst signals to the metaports
inputs.clk_rst(clk, rst); // 綁定輸入端口的時鐘和復(fù)位
outputs.clk_rst(clk, rst); // 綁定輸出端口的時鐘和復(fù)位
}
void thread1();
new1_OUTPUT_DT my_function(new1_INPUT_DT);
};
#endif
在設(shè)計頭文件中作瞄,定義了一個模塊new1美莫,具有一個p2p輸入端口和一個p2p輸出端口以及時鐘和復(fù)位端口,并聲明函數(shù)thread1為線程瓣窄,為其綁定了時鐘和復(fù)位,thread1的實現(xiàn)在cpp文件中如下所示:
#include "new1.h"
// The thread function for the design
void new1::thread1(){
// Reset the interfaces
{ // 復(fù)位行為部分
CYN_PROTOCOL("reset");
inputs.reset(); // 輸入端口復(fù)位
outputs.reset(); // 輸出端口復(fù)位
wait(); // 復(fù)位行為以wait結(jié)束
}
// Main execution loop
while (1){ // 模塊行為被包括在該無限循環(huán)中
new1_INPUT_DT input_val = inputs.get(); // get為阻塞的從input端口中獲取一個數(shù)據(jù)
new1_OUTPUT_DT output_val = my_function(input_val); // 執(zhí)行數(shù)據(jù)處理
outputs.put(output_val); // put為阻塞的發(fā)送一個數(shù)據(jù)
}
}
//
// User's computation function
//
new1_OUTPUT_DT new1::my_function(new1_INPUT_DT var){ // 進行數(shù)據(jù)處理纳鼎,處理方式為+1
new1_OUTPUT_DT my_outputs;
my_outputs.out1 = var.in1 + 1;
return (my_outputs);
}
1.2.TB文件
TestBench的頭文件如下所示俺夕,其定義了一個模塊tb,其他部分預(yù)設(shè)計文件類似
#ifndef __TB__H
#define __TB__H
#include "cynw_p2p.h"
#include "new1_input_type.h"
#include "new1_output_type.h"
SC_MODULE(tb)
{
public:
cynw_p2p < new1_OUTPUT_DT >::base_in inputs;
cynw_p2p < new1_INPUT_DT >::base_out outputs;
// Declaration of clock and reset parameters
sc_in_clk clk;
sc_out < bool > rst;
sc_in < bool > rst_in; // sampling version of "rst"
SC_CTOR(tb)
{
SC_CTHREAD(source, clk.pos());
SC_CTHREAD(sink, clk.pos());
reset_signal_is(rst_in,0);
rst_in(rst);
}
void source();
void sink();
};
#endif
tb定義了source和sink兩個線程贱鄙,線程描述如下所示:
#include "tb.h"
// Source thread
void tb::source(){
// Reset the output metaport and cycle the design's reset
outputs.reset(); // 輸出端口復(fù)位
// 端口復(fù)位劝贸,拉低rst兩個時鐘周期后拉高
rst = 0;
wait(2);
rst = 1;
wait();
// 輸入激勵,這里的激勵是從0發(fā)到9
for (int i = 0; i < 10; i++){
// Write values to the DUT
new1_INPUT_DT tmp;
tmp.in1 = i;
outputs.put(tmp);
}
}
// Read all the expected values from the design
void tb::sink() {
inputs.reset(); // 復(fù)位行為
wait(); // to synchronize with reset
// 接收10個輸出后結(jié)束
for (int i = 0; i < 10; i++) {
// Read values from the DUT
new1_OUTPUT_DT input_val = inputs.get();
// printf("%d\n", input_val);
cerr << "Read " << input_val.out1 << "\n";
}
esc_stop();
}
可以發(fā)現(xiàn)TB的行為沒有按設(shè)計文件編寫逗宁,因為TB并需要綜合映九,所以可以用更符合C的方式編寫。
1.3.system文件
system文件用于連接TB和設(shè)計瞎颗,頭文件如下所示:
#ifndef SYSTEM_H_INCLUDED
#define SYSTEM_H_INCLUDED
#include <systemc.h>
#include <esc.h>
#include "cynw_p2p.h"
#include "tb.h"
#include "new1_input_type.h"
#include "new1_output_type.h"
#include "new1_wrap.h"
SC_MODULE(TOP)
{
public:
// cynw_p2p channels
cynw_p2p < new1_INPUT_DT > inputs_chan;
cynw_p2p < new1_OUTPUT_DT > outputs_chan;
// clock and reset signals
sc_clock clk;
sc_signal < bool > rst;
// The testbench and DUT modules.
new1_wrapper *m_dut; // 聲明指向設(shè)計的指針
tb *m_tb; // 聲明指向tb的指針
void initInstances();
void deleteInstances();
SC_CTOR(TOP): clk("clk", 5, SC_NS, 0.5, 0, SC_NS, true), // 定義時鐘
inputs_chan("inputs_chan"),
outputs_chan("outputs_chan"),
rst("rst")
{
initInstances();
}
~TOP()
{
deleteInstances();
}
};
#endif // SYSTEM_H_INCLUDED
system定義了模塊TOP件甥,即整個仿真系統(tǒng)的頂層,使用指針的方式聲明子模塊哼拔,需要注意的是引有,Stratus會自動為設(shè)計的模塊添加wrapper,因此設(shè)計指針的類型為new1_wrapper
而不是new
管挟,連線的部分在cpp文件中如下所示:
#include "system.h"
void TOP::initInstances()
{
m_dut = new new1_wrapper("new1_wrapper");
// Connect the design module
m_dut->clk(clk);
m_dut->rst(rst);
m_dut->inputs(inputs_chan);
m_dut->outputs(outputs_chan);
// Connect the testbench
m_tb = new tb("tb");
m_tb->clk(clk);
m_tb->rst(rst);
m_tb->outputs(inputs_chan);
m_tb->inputs(outputs_chan);
}
void TOP::deleteInstances()
{
delete m_tb;
delete m_dut;
}
1.4.main
main文件用于啟動仿真轿曙、連接設(shè)計和連接聯(lián)合仿真等功能,一般不需要修改僻孝,main.cpp文件如下所示:
#include "system.h"
TOP *top = NULL;
extern void esc_elaborate() {
top = new TOP("top");
}
extern void esc_cleanup() {
delete top;
}
int sc_main(int argc, char *argv[]) {
esc_initialize(argc, argv);
esc_elaborate();
sc_start();
return 0;
}
1.5.project.tcl
project.tcl用于指定綜合信息和仿真信息,其主體主要需要執(zhí)行以下步驟:
- 指定庫信息
- 指定時鐘信息
- 設(shè)置仿真信息守谓,包括仿真工具信息和仿真平臺信息
- 設(shè)置高級綜合信息
- 設(shè)置物理綜合信息(如果需要)
# 設(shè)置物理庫信息穿铆,這里的寫法可以照抄,調(diào)用的是Stratus內(nèi)置的一個庫
set LIB_PATH "[get_install_path]/share/stratus/techlibs/GPDK045/gsclib045_svt_v4.4/gsclib045/timing"
set LIB_LEAF "slow_vdd1v2_basicCells.lib"
use_tech_lib "$LIB_PATH/$LIB_LEAF" # 設(shè)置物理庫
# set clock:設(shè)置時鐘庫
set_attr clock_period 5.0 # 設(shè)置時鐘周期為5ns
set_attr default_input_delay 0.1 # 設(shè)置輸入delay為0.1ns
# message level
set_attr message_detail 2 # 設(shè)置信息等級為2
# set dump setting:設(shè)置仿真工具信息
use_verilog_simulator incisive # 使用仿真器incisive(Stratus內(nèi)置斋荞,其實為ncverilog)
set_attr cc_options " -g" # 仿真后選項荞雏,可直接照抄
enable_waveform_logging -vcd # 設(shè)置輸出波形文件為vcd,還可以選擇fsdb
set_attr end_of_sim_command "make saySimPassed" # 仿真后執(zhí)行的命令
# system config:設(shè)置仿真平臺信息,這一步需要設(shè)置所有描述不需要進行綜合部分的文件為system_module
define_system_module main.cpp # 設(shè)置main為system_module
define_system_module system.cpp # 設(shè)置system部分為system_module
define_system_module tb.cpp # 設(shè)置仿真平臺tb為system_module
# hls config:設(shè)置高級綜合信息
define_hls_module new1 new1.cpp # 設(shè)置hls_module為new1
define_hls_config new1 BASIC # 設(shè)置hls_config為BASIC
# define_hls_config new1 DPA --dpopt_auto=op,expr
# 設(shè)置仿真信息
define_sim_config B "new1 RTL_V BASIC" # 定義仿真目標(biāo)凤优,仿真目標(biāo)為RTL_V級
設(shè)置物理庫到設(shè)置仿真平臺信息都比較容易理解悦陋,比較復(fù)雜的是設(shè)置高級綜合信息這個部分。高級綜合信息的設(shè)置分為兩個部分筑辨,分別是設(shè)置待綜合的模塊和綜合等級俺驶,分別對應(yīng)define_hls_module
和define_hls_config
命令。define_hls_module
用于指定高級綜合的對象棍辕,即指定待綜合的模塊和描述該模塊的文件指令如下所示:
define_hls_module 模塊名 文件名
一個例子如下所示暮现,指定需要對new1.cpp
的中包含的new1
模塊進行高級綜合:
define_hls_module new1 new1.cpp
第二個部分為指定高級綜合等級,高級綜合具有多個等級楚昭,對應(yīng)不同的性能和面積的折中栖袋,這里使用BASIC,指定指令如下所示:
define_hls_config 模塊名 綜合等級
define_hls_config new1 BASIC # 指定模塊new1高級綜合等級為BASIC
隨后需要設(shè)置仿真信息抚太,只能對一個高級綜合對象進行仿真塘幅,每次進行高級綜合,生成3個模型尿贫,RTL_V是其中的一種晌块,設(shè)置仿真信息需要指定對哪一個高級綜合等級的哪一個高級綜合對象中的哪一個模型進行仿真,仿真指令如下所示:
define_sim_config 配置名稱 "模塊名稱 模型類型 綜合等級"
define_sim_config B "new1 RTL_V BASIC" # 定義仿真目標(biāo)帅霜,仿真目標(biāo)為RTL_V級
1.6.Makefile
Makefile由project.tcl直接生成匆背,不需要手動編寫
2.操作方式
2.1.makefile生成
Makefile通過project.tcl自動生成,指令如下所指示:
bdw_makegen project.tcl
2.2.進行高級綜合
高級綜合指令如下所示:
make hls_配置名稱
例如上述腳本身冀,高級綜合指令為:
make hls_B
生成的文件位于bdw_work/modules
文件夾下
2.3.進行仿真
進行仿真指令的命令如下所示:
make sim_配置名稱
例如上述腳本钝尸,進行仿真指令為:
make sim_B
生成的波形文件位于bdw_work/sim
文件夾下
3.debug
當(dāng)dump fsdb波形時,會發(fā)生fsdb連接的錯誤搂根,此時解決方法為:
- 進行
make clean
操作 - 將dump的波形類型改為vcd并重新生成Makefile
- 進行仿真
- 將dump類型的模型改為fsdb并重新生成Makefile
- 進行仿真即可