高级综合工具Stratus学习笔记(1)

本次学习参考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.设计文件

设计文件的头文件如下所示:

#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;                 // 复位端口
    SC_CTOR(new1):inputs("inputs"), outputs("outputs"), clk("clk"), rst("rst") {// 构造函数
        SC_CTHREAD(thread1, clk.pos());          // 定义线程thread1,绑定时钟上升沿
        reset_signal_is(rst,0);                  // 定义复位为0时有效
        
        // Connect the clk and rst signals to the metaports
        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文件中如下所示:

#include "new1.h"

// The thread function for the design
void new1::thread1(){
    // Reset the interfaces
    { // 复位行为部分
        CYN_PROTOCOL("reset");
        
        inputs.reset();  // 输入端口复位
        outputs.reset(); // 输出端口复位
        
        wait();          // 复位行为以wait结束
    }
    
    // Main execution loop
    while (1){  // 模块行为被包括在该无限循环中
        new1_INPUT_DT  input_val = inputs.get();   // get为阻塞的从input端口中获取一个数据     
        new1_OUTPUT_DT output_val = my_function(input_val); // 执行数据处理
        outputs.put(output_val);   // put为阻塞的发送一个数据
    }
}
//
//  User's computation function
//
new1_OUTPUT_DT new1::my_function(new1_INPUT_DT var){ // 进行数据处理,处理方式为+1
    new1_OUTPUT_DT my_outputs;
    my_outputs.out1 = var.in1 + 1;
    return (my_outputs);
}

1.2.TB文件

TestBench的头文件如下所示,其定义了一个模块tb,其他部分预设计文件类似

#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();    // 输出端口复位
    
    // 端口复位,拉低rst两个时钟周期后拉高
    rst = 0;
    wait(2);
    rst = 1;
    wait();
    
    // 输入激励,这里的激励是从0发到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();      // 复位行为
    wait();                     // to synchronize with reset

    // 接收10个输出后结束
    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();
}

可以发现TB的行为没有按设计文件编写,因为TB并需要综合,所以可以用更符合C的方式编写。

1.3.system文件

system文件用于连接TB和设计,头文件如下所示:

#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;  // 声明指向设计的指针
    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,即整个仿真系统的顶层,使用指针的方式声明子模块,需要注意的是,Stratus会自动为设计的模块添加wrapper,因此设计指针的类型为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文件用于启动仿真、连接设计和连接联合仿真等功能,一般不需要修改,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用于指定综合信息和仿真信息,其主体主要需要执行以下步骤:

  • 指定库信息
  • 指定时钟信息
  • 设置仿真信息,包括仿真工具信息和仿真平台信息
  • 设置高级综合信息
  • 设置物理综合信息(如果需要)
# 设置物理库信息,这里的写法可以照抄,调用的是Stratus内置的一个库
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 clock:设置时钟库
set_attr clock_period 5.0 # 设置时钟周期为5ns
set_attr default_input_delay 0.1 # 设置输入delay为0.1ns

# message level
set_attr message_detail 2 # 设置信息等级为2

# set dump setting:设置仿真工具信息
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" # 仿真后执行的命令

# system config:设置仿真平台信息,这一步需要设置所有描述不需要进行综合部分的文件为system_module
define_system_module main.cpp  # 设置main为system_module
define_system_module system.cpp # 设置system部分为system_module
define_system_module tb.cpp # 设置仿真平台tb为system_module

# hls config:设置高级综合信息
define_hls_module new1 new1.cpp # 设置hls_module为new1
define_hls_config new1 BASIC    # 设置hls_config为BASIC
# define_hls_config new1 DPA --dpopt_auto=op,expr

# 设置仿真信息
define_sim_config B "new1 RTL_V BASIC" # 定义仿真目标,仿真目标为RTL_V级

设置物理库到设置仿真平台信息都比较容易理解,比较复杂的是设置高级综合信息这个部分。高级综合信息的设置分为两个部分,分别是设置待综合的模块和综合等级,分别对应define_hls_moduledefine_hls_config命令。define_hls_module用于指定高级综合的对象,即指定待综合的模块和描述该模块的文件指令如下所示:

define_hls_module 模块名 文件名

一个例子如下所示,指定需要对new1.cpp的中包含的new1模块进行高级综合:

define_hls_module new1 new1.cpp

第二个部分为指定高级综合等级,高级综合具有多个等级,对应不同的性能和面积的折中,这里使用BASIC,指定指令如下所示:

define_hls_config 模块名 综合等级
define_hls_config new1 BASIC # 指定模块new1高级综合等级为BASIC

随后需要设置仿真信息,只能对一个高级综合对象进行仿真,每次进行高级综合,生成3个模型,RTL_V是其中的一种,设置仿真信息需要指定对哪一个高级综合等级的哪一个高级综合对象中的哪一个模型进行仿真,仿真指令如下所示:

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自动生成,指令如下所指示:

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

当dump fsdb波形时,会发生fsdb连接的错误,此时解决方法为:

  • 进行make clean操作
  • 将dump的波形类型改为vcd并重新生成Makefile
  • 进行仿真
  • 将dump类型的模型改为fsdb并重新生成Makefile
  • 进行仿真即可
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容