SystemC安装教程,可参考 虚拟机安装systemc 

// execute:    
//     g++ -g -Wall -lsystemc -m64 -pthread main.cpp  
//         -L/$(your systemc path)/lib-linux64 
//         -I/$(your systemc path)/include  -I/$(your systemc path)/src/tlm_utils -o sim

#include <systemc>

class MySCModule : public sc_core::sc_module {
 public:
  SC_HAS_PROCESS(MySCModule); // must have this line
  explicit MySCModule(sc_core::sc_module_name name)
  :sc_core::sc_module(name) {
    std::cout<<"top module construct "<<std::endl;

    SC_METHOD(TestMethod_have_Initialization);
    sensitive<<m_update_evt;
    SC_METHOD(TestMethod_no_Initialization);
    sensitive<<m_update_evt;
    dont_initialize();//this SC_METHOD func don't called at Initialization phase

    SC_THREAD(WriteThread);
    SC_THREAD(ReadThread);
  }
  ~MySCModule() override {
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"]"<<" top module destruct "<<std::endl;
  };
  void before_end_of_elaboration() override{
    std::cout<<"before_end_of_elaboration. "<<std::endl;
  }
  void end_of_elaboration() override{
    std::cout<<"end_of_elaboration. "<<std::endl;
  }
  void start_of_simulation() override{
    std::cout<<"start_of_simulation. "<<std::endl;
  }
  void end_of_simulation() override{
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"] end_of_simulation. "<<std::endl;
  }

  void TestMethod_have_Initialization(){
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"]"<< 
      " TestMethod_have_Initialization " <<std::endl;
  }

  void TestMethod_no_Initialization(){
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"]"<< 
      " TestMethod_no_Initialization " <<std::endl;
  }

  void WriteThread(){
    m_reset.write(false);
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"] m_reset write 0 " <<std::endl;
    sc_core::wait(2,sc_core::SC_NS);
    m_reset.write(true);
    std::cout<<" ["<<sc_core::sc_time_stamp()<<"] m_reset write 1 " <<std::endl;
  }

  void ReadThread(){
    while(1){
      bool t_sig = m_reset.read();
      if (t_sig == true){
        std::cout<<" ["<<sc_core::sc_time_stamp() <<
          "] m_reset read is 1, notify event " <<std::endl;
        m_update_evt.notify(sc_core::SC_ZERO_TIME); 
        return; 
      }
      sc_core::wait(1,sc_core::SC_NS);
    }
  }

  sc_core::sc_signal<bool>  m_reset ; 
  sc_core::sc_event         m_update_evt;
};

int sc_main(int argc, char ** argv)
{
  MySCModule m_top_module ("my_top_module");
  std::cout<<"top module construct finish "<<std::endl;
  sc_core::sc_start(10,sc_core::SC_NS);
  // sc_core::sc_start();
  std::cout<<"start finish, beign call sc_stop"<<std::endl;
  sc_core::sc_stop();// only have sc_stop, end_of_simulation can be called.
  std::cout<<"stop finish "<<std::endl;
  return 0;
}

此程序可作为SystemC初学者的第一个测试程序,其实现的功能比较简单:一个reset 信号,初始值为false,2ns的时候赋值为true,触发两个method函数打印。

此程序中包含了SystemC程序代码的常见组件,以下分别进行介绍。

类似与C/C++ 的main函数,sc_main为SystemC/TLM程序的入口函数。

Class MySCModule继承于sc_module (模块),我们的SystemC程序一般都只有一个top module,然后在sc_main中进行例化,其他的子模块都在这个top module的构造函数中进行例化,这就是SystemC spec (以下简称spec)中提到的module hierarchy。一般情况下,我们会给每个module都起一个名字,也就是调用sc_module的含参构造函数,sc_core::sc_module(name);如果某一个类被例化多份,可以使用SystemC自带的函数sc_gen_unique_name("my_module_name")来产生不同的name (name后面加_instance_num),代码中可以使用this->name() 可以得到当前模块的name,如果模块是一个子模块,那么得到的name将是一个包含所有层次关系的name,比如 top_name.sub_module_name.xxx

SC_HAS_PROCESS(MySCModule);是声明本module中有SC_THREAD / SC_METHOD关联函数,必须要在类中进行声明,否则会导致关联函数在Simulation阶段不能被SystemC kernel调用。

SC_THREAD / SC_METHOD是进程宏,进程宏的目的是将关联的函数注册到SystemC kernel,以便调度程序可以在模拟期间回调该成员函数。Spec中有如下解释:

The purpose of the process macros is to register the associated function with the kernel such that the scheduler can call back that member function during simulation. It is also possible to use spawned processes for this same purpose. The process macros are provided for backward compatibility with earlier versions of SystemC and to provide clocked threads for hardware synthesis.

Sensitive用于添加敏感事件列表,dont_initialize用于disable initialization phase调用。Sensitive 和dont_initialize都只对离它最近的上一条SC_THREAD / SC_METHOD关联函数有效。比如实例中 TestMethod_have_Initialization和TestMethod_no_Initialization都有敏感事件 m_update_evt,但必须分别写在两句SC_METHOD的后面。实例中的dont_initialize只对TestMethod_no_Initialization有效。如果我们希望多个事件都能触发关联函数,则可以使用sensitive<< event1 <<event 2 << xxx 来添加多个敏感事件。

sc_start和sc_stop从字面意思我们就知道前者是启动SystemC运行,后者是停止。其中,sc_start是一个SystemC代码中必须有的;但sc_stop可以没有。因为SystemC Kernel在调度过程中,如果发现没有processes需要被update,则自动停止Simulation阶段;sc_start函数如果不加任何参数,则必须依赖这种停止方式。本实例中,sc_start函数中有一个10ns的时间参数,表明我只希望此程序的SystemC 仿真时间推进到10ns,即使还有任务未完成(需要在下一个SystemC仿真时间,比如11ns才会执行),到10ns时程序依然立即停止Simulation阶段。

以下是示例程序的执行打印结果,可以看到最终的stop 打印时间(SystemC仿真时间)为10ns。

top module construct 
top module construct finish 
before_end_of_elaboration. 
end_of_elaboration. 
start_of_simulation. 
 [0 s] TestMethod_have_Initialization 
 [0 s] m_reset write 0 
 [2 ns] m_reset write 1 
 [3 ns] m_reset read is 1, notify event 
 [3 ns] TestMethod_no_Initialization 
 [3 ns] TestMethod_have_Initialization 
start finish, beign call sc_stop

Info: /OSCI/SystemC: Simulation stopped by user.
 [10 ns] end_of_simulation. 
stop finish 
 [10 ns] top module destruct 

如果将sc_core::sc_start(10,sc_core::SC_NS);改为sc_core::sc_start();则会发现最终的stop 打印时间(SystemC仿真时间)为3ns。这是因为3ns之后所有的SC_THREAD / SC_METHOD关联函数都没有更新了,SystemC 自动停止Simulation阶段。

top module construct 
top module construct finish 
before_end_of_elaboration. 
end_of_elaboration. 
start_of_simulation. 
 [0 s] TestMethod_have_Initialization 
 [0 s] m_reset write 0 
 [2 ns] m_reset write 1 
 [3 ns] m_reset read is 1, notify event 
 [3 ns] TestMethod_no_Initialization 
 [3 ns] TestMethod_have_Initialization 
start finish, beign call sc_stop

Info: /OSCI/SystemC: Simulation stopped by user.
 [3 ns] end_of_simulation. 
stop finish 
 [3 ns] top module destruct 

代码中涉及的其他问题,可参考以下相关内容。

 SystemC的运行阶段

SystemC Simulation调度机制

SystemC time

SystemC:SC_THREAD和SC_METHOD

SystemC中的Wait 函数

Logo

更多推荐