专栏首页文武兼修ing——机器学习与IC设计高级综合工具Stratus学习笔记(1)

高级综合工具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
  • 进行仿真即可

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 分离链接的散列散列代码实现

    散列 散列为一种用于以常数平均时间执行插入,删除和查找的技术。一般的实现方法是使通过数据的关键字可以计算出该数据所在散列中的位置,类似于Python中的字典。关...

    月见樽
  • 有基础(Pytorch/TensorFlow基础)mxnet+gluon快速入门mxnet基本数据结构mxnet的数据载入网络搭建模型训练准确率计算模型保存与载入

    import numpy as np import mxnet as mx import logging logging.getLogger().setLeve...

    月见樽
  • 基于sklearn的主成分分析理论部分代码实现

    理论部分 特征降维 特征降维是无监督学习的一种应用:将n维的数据降维为m维的数据(n>m)。可应用于数据压缩等领域 主成分分析(PCA) 主成分分析是一种常用的...

    月见樽
  • windows下MmAccessFault->MiDispatchFault处理过程说明

    MmAccessFault函数中查看到faultaddress对应的pte无效之时,查看TempPte.u.Soft.Prototype,

    kkindof
  • Redis 高效删除大key

    大key(bigkey)是指 key 的 value 是个庞然大物,例如 Hashes, Sorted Sets, Lists, Sets,日积月累之后,会变得...

    dys
  • PyTorch还是TensorFlow?这有一份新手指南

    问耕 编译整理 量子位 出品 | 公众号 QbitAI 前几天,量子位发过一篇《忽悠VC指南》。其中有一条建议是,当你假装AI专家时,最好别谈众人皆知的Tens...

    量子位
  • DataRobot和Automation Anywhere合作提出关于数字转换的人工智能解决方案

    机器人过程自动化(RPA)的领导者Automation Anywhere近日宣布与自动化机器学习的先驱架构师DataRobot合作。DataRobot平台的集成...

    AiTechYun
  • 推荐一款实用神器

    chrome的插件,集json串格式化、代码美化、代码压缩、二维码生成、页面取色等于一身,强烈推荐大家安装使用。

    我的小碗汤
  • 机器学习之Apriori算法

    Apriori算法是常用于挖掘出数据关联规则的算法,能够发现事物数据库中频繁出现的数据集,这些联系构成的规则可帮助用户找出某些行为特征,以便进行企业决策。例如,...

    小一
  • 深入理解JAVA中的NIO

    传统的 IO 流还是有很多缺陷的,尤其它的阻塞性加上磁盘读写本来就慢,会导致 CPU 使用效率大大降低。

    AI乔治

扫码关注云+社区

领取腾讯云代金券