本次学习参考Stratus内置的学习例程(simple_p2p),学习内容主要如下所示:
- Stratus HLS软件运行需要的必要文件及其写法
- Stratus HLS软件操作方式
- Stratus HLS内置的p2p端口的基本使用(非流水线)
- Stratus HLS自定义数据类型
1.Stratus HLS必要文件与写法
Stratus工程所需要的文件如下图所示:
文件 |
类型 |
说明 |
设计文件 |
cpp+h |
描述设计的头文件和cpp文件 |
TestBench |
cpp+h |
描述测试平台的头文件和cpp文件 |
System |
cpp+h |
连接设计文件和TestBench的头文件和cpp文件 |
main.cpp |
cpp |
整个仿真平台的顶层文件 |
project.tcl |
tcl |
指定工程配置(仿真选项和综合选项)的tcl文件 |
Makefile |
makefile |
由project.tcl生成的makefile文件 |
1.1.设计文件
设计文件的头文件如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #ifndef __NEW1__H #define __NEW1__H
#include "cynw_p2p.h"
#include "new1_input_type.h" #include "new1_output_type.h"
SC_MODULE(new1) { public: cynw_p2p < new1_INPUT_DT >::in inputs; cynw_p2p < new1_OUTPUT_DT >::out outputs; sc_in_clk clk; sc_in < bool > rst; SC_CTOR(new1):inputs("inputs"), outputs("outputs"), clk("clk"), rst("rst") { SC_CTHREAD(thread1, clk.pos()); reset_signal_is(rst,0); inputs.clk_rst(clk, rst); outputs.clk_rst(clk, rst); } void thread1(); new1_OUTPUT_DT my_function(new1_INPUT_DT); };
#endif
|
在设计头文件中,定义了一个模块new1,具有一个p2p输入端口和一个p2p输出端口以及时钟和复位端口,并声明函数thread1为线程,为其绑定了时钟和复位,thread1的实现在cpp文件中如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "new1.h"
void new1::thread1(){ { CYN_PROTOCOL("reset"); inputs.reset(); outputs.reset(); wait(); } while (1){ new1_INPUT_DT input_val = inputs.get(); new1_OUTPUT_DT output_val = my_function(input_val); outputs.put(output_val); } }
new1_OUTPUT_DT new1::my_function(new1_INPUT_DT var){ new1_OUTPUT_DT my_outputs; my_outputs.out1 = var.in1 + 1; return (my_outputs); }
|
1.2.TB文件
TestBench的头文件如下所示,其定义了一个模块tb,其他部分预设计文件类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #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; sc_in_clk clk; sc_out < bool > rst; sc_in < bool > rst_in; 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两个线程,线程描述如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include "tb.h"
void tb::source(){ outputs.reset(); rst = 0; wait(2); rst = 1; wait(); for (int i = 0; i < 10; i++){ new1_INPUT_DT tmp; tmp.in1 = i; outputs.put(tmp); } }
void tb::sink() { inputs.reset(); wait();
for (int i = 0; i < 10; i++) { new1_OUTPUT_DT input_val = inputs.get(); cerr << "Read " << input_val.out1 << "\n"; } esc_stop(); }
|
可以发现TB的行为没有按设计文件编写,因为TB并需要综合,所以可以用更符合C的方式编写。
1.3.system文件
system文件用于连接TB和设计,头文件如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #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 < new1_INPUT_DT > inputs_chan; cynw_p2p < new1_OUTPUT_DT > outputs_chan; sc_clock clk; sc_signal < bool > rst; new1_wrapper *m_dut; tb *m_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定义了模块TOP,即整个仿真系统的顶层,使用指针的方式声明子模块,需要注意的是,Stratus会自动为设计的模块添加wrapper,因此设计指针的类型为new1_wrapper
而不是new
,连线的部分在cpp文件中如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include "system.h"
void TOP::initInstances() { m_dut = new new1_wrapper("new1_wrapper"); m_dut->clk(clk); m_dut->rst(rst); m_dut->inputs(inputs_chan); m_dut->outputs(outputs_chan); 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文件用于启动仿真、连接设计和连接联合仿真等功能,一般不需要修改,main.cpp文件如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #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用于指定综合信息和仿真信息,其主体主要需要执行以下步骤:
- 指定库信息
- 指定时钟信息
- 设置仿真信息,包括仿真工具信息和仿真平台信息
- 设置高级综合信息
- 设置物理综合信息(如果需要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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" # 设置物理库
set_attr clock_period 5.0 # 设置时钟周期为5ns set_attr default_input_delay 0.1 # 设置输入delay为0.1ns
set_attr message_detail 2 # 设置信息等级为2
use_verilog_simulator incisive # 使用仿真器incisive(Stratus内置,其实为ncverilog) set_attr cc_options " -g" # 仿真后选项,可直接照抄 enable_waveform_logging -vcd # 设置输出波形文件为vcd,还可以选择fsdb set_attr end_of_sim_command "make saySimPassed" # 仿真后执行的命令
define_system_module main.cpp # 设置main为system_module define_system_module system.cpp # 设置system部分为system_module define_system_module tb.cpp # 设置仿真平台tb为system_module
define_hls_module new1 new1.cpp # 设置hls_module为new1 define_hls_config new1 BASIC # 设置hls_config为BASIC
define_sim_config B "new1 RTL_V BASIC" # 定义仿真目标,仿真目标为RTL_V级
|
设置物理库到设置仿真平台信息都比较容易理解,比较复杂的是设置高级综合信息这个部分。高级综合信息的设置分为两个部分,分别是设置待综合的模块和综合等级,分别对应define_hls_module
和define_hls_config
命令。define_hls_module
用于指定高级综合的对象,即指定待综合的模块和描述该模块的文件指令如下所示:
1
| define_hls_module 模块名 文件名
|
一个例子如下所示,指定需要对new1.cpp
的中包含的new1
模块进行高级综合:
1
| define_hls_module new1 new1.cpp
|
第二个部分为指定高级综合等级,高级综合具有多个等级,对应不同的性能和面积的折中,这里使用BASIC,指定指令如下所示:
1 2
| define_hls_config 模块名 综合等级 define_hls_config new1 BASIC # 指定模块new1高级综合等级为BASIC
|
随后需要设置仿真信息,只能对一个高级综合对象进行仿真,每次进行高级综合,生成3个模型,RTL_V是其中的一种,设置仿真信息需要指定对哪一个高级综合等级的哪一个高级综合对象中的哪一个模型进行仿真,仿真指令如下所示:
1 2
| define_sim_config 配置名称 "模块名称 模型类型 综合等级" define_sim_config B "new1 RTL_V BASIC" # 定义仿真目标,仿真目标为RTL_V级
|
1.6.Makefile
Makefile由project.tcl直接生成,不需要手动编写
2.操作方式
2.1.makefile生成
Makefile通过project.tcl自动生成,指令如下所指示:
2.2.进行高级综合
高级综合指令如下所示:
例如上述脚本,高级综合指令为:
生成的文件位于bdw_work/modules
文件夹下
2.3.进行仿真
进行仿真指令的命令如下所示:
例如上述脚本,进行仿真指令为:
生成的波形文件位于bdw_work/sim
文件夹下
3.debug
当dump fsdb波形时,会发生fsdb连接的错误,此时解决方法为:
- 进行
make clean
操作
- 将dump的波形类型改为vcd并重新生成Makefile
- 进行仿真
- 将dump类型的模型改为fsdb并重新生成Makefile
- 进行仿真即可