Unit testing 在SystemC中使用现有的单元测试框架

Unit testing 在SystemC中使用现有的单元测试框架,unit-testing,systemc,Unit Testing,Systemc,我正在SystemC的一个项目中工作,希望加入单元测试。可以将现有的单元测试框架与SystemC一起使用吗 我这样问是因为SystemC模块似乎只在模拟内核中执行,我想在模块本身上使用单元测试。我能够使用fork系统调用运行两个SystemC测试。我在和框架上使用了教程示例。我可以运行两次测试,但是在调用sc_stop之后,SystemC模拟器打印出了一个关于启动测试的错误。然而,不管错误是什么,模拟器在第二次运行时都可以正常运行 SystemC 2.2.0 --- Feb 24 2011 1

我正在SystemC的一个项目中工作,希望加入单元测试。可以将现有的单元测试框架与SystemC一起使用吗


我这样问是因为SystemC模块似乎只在模拟内核中执行,我想在模块本身上使用单元测试。

我能够使用fork系统调用运行两个SystemC测试。我在和框架上使用了教程示例。我可以运行两次测试,但是在调用sc_stop之后,SystemC模拟器打印出了一个关于启动测试的错误。然而,不管错误是什么,模拟器在第二次运行时都可以正常运行

 SystemC 2.2.0 --- Feb 24 2011 15:01:50
        Copyright (c) 1996-2006 by all Contributors
                    ALL RIGHTS RESERVED
Running main() from gtest_main.cc
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from systemc_test
[ RUN      ] systemc_test.test1
      Time A B F
       0 s 0 0 0
       0 s 0 0 1
     10 ns 0 1 1
     20 ns 1 0 1
     30 ns 1 1 0
SystemC: simulation stopped by user.
[       OK ] systemc_test.test1 (1 ms)
[ RUN      ] systemc_test.test2

Error: (E546) sc_start called after sc_stop has been called
In file: ../../../../src/sysc/kernel/sc_simcontext.cpp:1315
[       OK ] systemc_test.test2 (2 ms)
[----------] 2 tests from systemc_test (3 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (3 ms total)
[  PASSED  ] 2 tests.
[       OK ] systemc_test.test1 (3 ms)
[ RUN      ] systemc_test.test2
      Time A B F
       0 s 0 0 0
       0 s 0 0 1
     10 ns 0 1 1
     20 ns 1 0 1
     30 ns 1 1 0
SystemC: simulation stopped by user.
[       OK ] systemc_test.test2 (1 ms)
[----------] 2 tests from systemc_test (4 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (4 ms total)
[  PASSED  ] 2 tests.
[       OK ] systemc_test.test2 (1 ms)
[----------] 2 tests from systemc_test (4 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (4 ms total)
[  PASSED  ] 2 tests.
更新:根据要求编写示例代码:

// main_1.cxx

#include "systemc.h"
#include "stim.hxx"
#include "exor2.hxx"
#include "mon.hxx"


//#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>


void run_1()
{
  sc_signal<bool> ASig, BSig, FSig;
  sc_clock TestClk("TestClock", 10, SC_NS,0.5);

  stim* Stim1 = new stim("Stimulus1_1");
  Stim1->A(ASig);
  Stim1->B(BSig);
  Stim1->Clk(TestClk);

  exor2* DUT = new exor2("exor2_1");
  DUT->A(ASig);
  DUT->B(BSig);
  DUT->F(FSig);

  mon* Monitor1 = new mon("Monitor_1");
  Monitor1->A(ASig);
  Monitor1->B(BSig);
  Monitor1->F(FSig);
  Monitor1->Clk(TestClk);


  Stim1->run();
  delete Stim1;
  delete DUT;
  delete Monitor1;
}

bool sc_main_1()
{
        //int rc;
        //pthread_t thread;
        //if( (rc = pthread_create( &thread, NULL, &run_1, NULL)) )
        //{
        //      printf("Thread creation failed: %d\n", rc);
        //};

        //pthread_join(thread, NULL);

        int pid = fork();
        if(pid == 0)
        {
                run_1();
        };
        waitpid(pid, NULL, 0);
        return true;
};


// main_2.cxx    

#include "systemc.h"
#include "stim.hxx"
#include "exor2.hxx"
#include "mon.hxx"


//#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>


void run_2()
{
  sc_signal<bool> ASig, BSig, FSig;
  sc_clock TestClk("TestClock", 10, SC_NS,0.5);

  stim* Stim1 = new stim("Stimulus1_2");
  Stim1->A(ASig);
  Stim1->B(BSig);
  Stim1->Clk(TestClk);

  exor2* DUT = new exor2("exor2_2");
  DUT->A(ASig);
  DUT->B(BSig);
  DUT->F(FSig);

  mon* Monitor1 = new mon("Monitor_2");
  Monitor1->A(ASig);
  Monitor1->B(BSig);
  Monitor1->F(FSig);
  Monitor1->Clk(TestClk);


  Stim1->run();
  delete Stim1;
  delete DUT;
  delete Monitor1;
}

bool sc_main_2()
{
        //int rc;
        //pthread_t thread;
        //if( (rc = pthread_create( &thread, NULL, &run_1, NULL)) )
        //{
        //      printf("Thread creation failed: %d\n", rc);
        //};

        //pthread_join(thread, NULL);

        int pid = fork();
        if(pid == 0)
        {
                run_2();
        };
        waitpid(pid, NULL, 0);
        return true;
};


// main.cxx

#include "systemc.h"

#include "gtest/gtest.h"


extern bool sc_main_1();
extern bool sc_main_2();

TEST(systemc_test, test1)
{
        EXPECT_TRUE(sc_main_1());
};

TEST(systemc_test, test2)
{
        EXPECT_TRUE(sc_main_2());
};

int sc_main(int argc, char* argv[])
{
  std::cout << "Running main() from gtest_main.cc\n";
  testing::InitGoogleTest(&argc, argv);
  RUN_ALL_TESTS();
  return 0;

}

// stim.hxx

#ifndef stim_hxx
#define stim_hxx

#include "systemc.h"
SC_MODULE(stim)
{
  sc_out<bool> A, B;
  sc_in<bool> Clk;

  void StimGen()
  {
    A.write(false);
    B.write(false);
    wait();
    A.write(false);
    B.write(true);
    wait();
    A.write(true);
    B.write(false);
    wait();
    A.write(true);
    B.write(true);
        wait();
    sc_stop();
  }

  SC_CTOR(stim)
  {
    SC_THREAD(StimGen);
    sensitive << Clk.pos();
  }

  bool run()
  {
                sc_start();  // run forever
                return true;
  };

};

#endif


// exor2.hxx

#ifndef exor_hxx
#define exor_hxx

#include "systemc.h"
#include "nand2.hxx"
SC_MODULE(exor2)
{
  sc_in<bool> A, B;
  sc_out<bool> F;

  nand2 n1, n2, n3, n4;

  sc_signal<bool> S1, S2, S3;

  SC_CTOR(exor2) : n1("N1"), n2("N2"), n3("N3"), n4("N4")
  {
    n1.A(A);
    n1.B(B);
    n1.F(S1);

    n2.A(A);
    n2.B(S1);
    n2.F(S2);

    n3.A(S1);
    n3.B(B);
    n3.F(S3);

    n4.A(S2);
    n4.B(S3);
    n4.F(F);
  }
};

#endif


// mon.hxx

#ifndef mon_hxx
#define mon_hxx

#include "systemc.h"
#include <iomanip>
#include <iostream>


using namespace std;

SC_MODULE(mon)
{
    sc_in<bool> A,B,F;
    sc_in<bool> Clk;

  void monitor()
  {
    cout << setw(10) << "Time";
    cout << setw(2) << "A" ;
    cout << setw(2) << "B";
    cout << setw(2) << "F" << endl;
    while (true)
    {
      cout << setw(10) << sc_time_stamp();
      cout << setw(2) << A.read();
      cout << setw(2) << B.read();
      cout << setw(2) << F.read() << endl;
      wait();    // wait for 1 clock cycle
    }
  }

  SC_CTOR(mon)
  {
    SC_THREAD(monitor);
    sensitive << Clk.pos();
  }
};

#endif
//main_1.cxx
#包括“systemc.h”
#包括“刺激hxx”
#包括“exor2.hxx”
#包括“mon.hxx”
//#包括
#包括
#包括
无效运行_1()
{
sc_信号ASig、BSig、FSig;
sc_时钟测试时钟(“测试时钟”,10,sc_NS,0.5);
stim*Stim1=新的stim(“stimuls1_1”);
刺激1->A(ASig);
刺激1->B(BSig);
Stim1->Clk(测试Clk);
exor2*DUT=新的exor2(“exor2_1”);
DUT->A(ASig);
DUT->B(BSig);
DUT->F(FSig);
mon*Monitor1=新mon(“监视器1”);
监视器1->A(ASig);
监视器1->B(BSig);
监视器1->F(FSig);
监视器1->Clk(测试Clk);
Stim1->run();
删除第1条;
删除DUT;
删除监视器1;
}
bool sc_main_1()
{
//int rc;
//pthread\u t线程;
//if((rc=pthread_create(&thread,NULL,&run_1,NULL)))
//{
//printf(“线程创建失败:%d\n”,rc);
//};
//pthread_join(线程,NULL);
int-pid=fork();
如果(pid==0)
{
运行_1();
};
waitpid(pid,NULL,0);
返回true;
};
//main_2.cxx
#包括“systemc.h”
#包括“刺激hxx”
#包括“exor2.hxx”
#包括“mon.hxx”
//#包括
#包括
#包括
无效运行_2()
{
sc_信号ASig、BSig、FSig;
sc_时钟测试时钟(“测试时钟”,10,sc_NS,0.5);
stim*Stim1=新的stim(“stimuls1_2”);
刺激1->A(ASig);
刺激1->B(BSig);
Stim1->Clk(测试Clk);
exor2*DUT=新的exor2(“exor2_2”);
DUT->A(ASig);
DUT->B(BSig);
DUT->F(FSig);
mon*Monitor1=新mon(“监视器2”);
监视器1->A(ASig);
监视器1->B(BSig);
监视器1->F(FSig);
监视器1->Clk(测试Clk);
Stim1->run();
删除第1条;
删除DUT;
删除监视器1;
}
bool sc_main_2()
{
//int rc;
//pthread\u t线程;
//if((rc=pthread_create(&thread,NULL,&run_1,NULL)))
//{
//printf(“线程创建失败:%d\n”,rc);
//};
//pthread_join(线程,NULL);
int-pid=fork();
如果(pid==0)
{
运行_2();
};
waitpid(pid,NULL,0);
返回true;
};
//main.cxx
#包括“systemc.h”
#包括“gtest/gtest.h”
extern bool sc_main_1();
extern bool sc_main_2();
测试(系统C_测试,测试1)
{
EXPECT_TRUE(sc_main_1());
};
测试(系统C_测试,测试2)
{
EXPECT_TRUE(sc_main_2());
};
int sc_main(int argc,char*argv[])
{

std::cout对于这个问题,我有第二个解决方案,它使用CMkae和CTest()。我使用的设置为每个测试创建一个二进制文件。下面是我使用的
CMakeLists.txt
文件:

project(sc_unit_test)
include_directories(/home/stephan/local/include)
find_library(systemc systemc /home/stephan/local/lib-linux64)
link_directories(/home/stephan/local/lib-linux64)

add_executable(test_1 test_1.cxx)
target_link_libraries(test_1 systemc)

add_executable(test_2 test_2.cxx)
target_link_libraries(test_2 systemc)

enable_testing()
add_test(test_1 test_1)
add_test(test_2 test_2)
每个
test.*.cxx
文件都有一个执行测试的
sc_main
方法,返回值指示测试是否通过或失败。要运行测试,只需执行以下操作:

$ cmake .
$ make
$ ctest
Test project
  1/  2 Testing test_1                           Passed
  2/  2 Testing test_2                           Passed

100% tests passed, 0 tests failed out of 2

如果您不想运行模拟器,只需跳过调用
sc\u start
,并在对特定模块进行任何特定测试后退出应用程序。

通常情况下,SystemC设备处于测试状态(DUT)可以通过声明一些信号来重新初始化为初始状态。您可以利用这个事实,并启用任何您想要的C++单元测试框架。在每次运行之前只需重置DUT,这样就不需要再详细说明两次。 下面是一个GoogleTest示例和一个简单的“累加器”DUT

  • sc\u main
  • 详细说明你的模型
  • 调用
    Run\u ALL\u tests()
  • 您需要以某种方式将指向SystemC DUT接口的指针传递给您的测试
  • 资料来源:

    #include <systemc.h>
    #include "gtest/gtest.h"
    
    struct test_driver;
    
    test_driver *test_driver_p = nullptr;
    
    void register_test_driver(test_driver *td) {
        test_driver_p = td;
    }
    
    test_driver* get_test_driver() {
        assert(test_driver_p);
        return test_driver_p;
    }
    
    
    SC_MODULE(dut_accum) {
        sc_in_clk   clk{"clk"};
        sc_in<bool> reset{"reset"};
    
        sc_in<bool> en{"en"};
        sc_in<int>  din{"din"};
        sc_out<int> dout{"dout"};
    
        SC_CTOR(dut_accum) {
            SC_METHOD(accum_method);
            sensitive << clk.pos();
        };
    
        void accum_method() {
            if (reset)
                dout = 0;
            else if (en)
                dout = dout + din;
        }
    };
    
    SC_MODULE(test_driver) {
    
        sc_signal<bool> reset{"reset",1};
        sc_signal<bool> en{"en",0};
        sc_signal<int> din{"din",0};
        sc_signal<int> dout{"dout"};
    
        SC_CTOR(test_driver) {
            dut_inst.clk(clk);
            dut_inst.reset(reset);
            dut_inst.en(en);
            dut_inst.din(din);
            dut_inst.dout(dout);
            SC_THREAD(test_thread);
            sensitive << clk.posedge_event();
            register_test_driver(this);
        }
    
    private:
        void test_thread() {
            if (RUN_ALL_TESTS())
                SC_REPORT_ERROR("Gtest", "Some test FAILED");
            sc_stop();
        }
    
        dut_accum dut_inst{"dut_inst"};
        sc_clock clk{"clk", 10, SC_NS};
    };
    
    
    
    namespace {
        // The fixture for testing dut_accum
        class accum_test: public ::testing::Test {
        protected:
    
            test_driver & td;
    
            accum_test(): td(*get_test_driver()){
                reset_dut();
            }
    
            virtual ~accum_test() {}
    
            void reset_dut(){
                td.reset = 1;
                wait();
                td.reset = 0;
            }
        };
    
        TEST_F(accum_test, test0) {
            td.din = 10;
            td.en = 1;
            wait();
            wait();
            EXPECT_EQ(td.dout.read(), 10);
        }
    
        TEST_F(accum_test, test1_no_en) {
            td.din = 10;
            td.en = 0;
            wait();
            wait();
            EXPECT_EQ(td.dout.read(), 10); // this test will fail, since en is 0
        }
    
        TEST_F(accum_test, test2_reset_asserted) {
            td.din = 10;
            td.en = 1;
            td.reset = 1;
            wait();
            wait();
            EXPECT_EQ(td.dout.read(), 0);
        }
    }
    
    int sc_main(int argc, char **argv) {
        ::testing::InitGoogleTest(&argc, argv);
        test_driver td{"td"};
        sc_start();
    }
    

    在GTest中运行任何测试之前,您必须创建所有必要的SystemC信号、SystemC模块并在它们之间建立连接。这需要创建自己的
    GTest_main.cc
    实现。当然,在SystemC中,您必须将所有内容放入
    sc_main
    函数中

    为此,我将使用注册表设计模式

    首先创建注册表类(registry+factory+singleton)。该类将负责使用动态分配和lambda表达式中的new和smart指针存储注册的构造函数(请参见
    factory::add
    class)。使用
    factory::create()创建所有对象
    方法,然后再运行所有测试。然后您可以在测试执行中使用
    工厂::get()
    方法获取对象

    工厂hpp

    #ifndef FACTORY_HPP
    #define FACTORY_HPP
    
    #include <map>
    #include <string>
    #include <memory>
    #include <functional>
    
    class factory {
    public:
        static factory& get_instance();
    
        template<typename T, typename ...Args>
        class add {
        public:
            add(Args&&... args);
    
            add(const std::string& name, Args&&... args);
        };
    
        template<typename T>
        static T* get(const std::string& name = "");
    
        void create();
    
        void destroy();
    private:
        using destructor = std::function<void(void*)>;
        using object = std::unique_ptr<void, destructor>;
        using constructor = std::function<object(void)>;
    
        factory();
    
        factory(const factory& other) = delete;
    
        factory& operator=(const factory& other) = delete;
    
        void add_object(const std::string& name, constructor create);
    
        void* get_object(const std::string& name);
    
        std::map<std::string, constructor> m_constructors;
        std::map<std::string, object> m_objects;
    };
    
    template<typename T, typename ...Args>
    factory::add<T, Args...>::add(Args&&... args) {
        add("", args...);
    }
    
    template<typename T, typename ...Args>
    factory::add<T, Args...>::add(const std::string& name, Args&&... args) {
        factory::get_instance().add_object(name,
            [args...] () -> object {
                return object{
                    new T(std::forward<Args>(args)...),
                    [] (void* obj) {
                        delete static_cast<T*>(obj);
                    }
                };
            }
        );
    }
    
    template<typename T> auto
    factory::get(const std::string& name) -> T* {
        return static_cast<T*>(factory::get_instance().get_object(name));
    }
    
    #endif /* FACTORY_HPP */
    
    #include "factory.hpp"
    
    #include <stdexcept>
    
    auto factory::get_instance() -> factory& {
        static factory instance{};
        return instance;
    }
    
    factory::factory() :
        m_constructors{},
        m_objects{}
    { }
    
    void factory::create() {
        for (const auto& item : m_constructors) {
            m_objects[item.first] = item.second();
        }
    }
    
    void factory::destroy() {
        m_objects.clear();
    }
    
    void factory::add_object(const std::string& name, constructor create) {
        auto it = m_constructors.find(name);
    
        if (it == m_constructors.cend()) {
            m_constructors[name] = create;
        }
        else {
            throw std::runtime_error("factory::add(): "
                    + name + " object already exist in factory");
        }
    }
    
    auto factory::get_object(const std::string& name) -> void* {
        auto it = m_objects.find(name);
    
        if (it == m_objects.cend()) {
            throw std::runtime_error("factory::get(): "
                    + name + " object doesn't exist in factory");
        }
    
        return it->second.get();
    }
    
    #include "factory.hpp"
    
    #include <systemc>
    #include <gtest/gtest.h>
    
    int sc_main(int argc, char* argv[]) {
    
        factory::get_instance().create();
    
        testing::InitGoogleTest(&argc, argv);
        int status = RUN_ALL_TESTS();
    
        factory::get_instance().destroy();
    
        return status;
    }
    
    #include "my_module.h"
    #include "factory.hpp"
    
    #include <gtest/gtest.h>
    #include <systemc>
    
    class dut {
    public:
        sc_core::sc_clock aclk{"aclk"};
        sc_core::sc_signal<bool> areset_n{"areset_n"};
        sc_core::sc_signal<bool> in{"in"};
        sc_core::sc_signal<bool> out{"out"};
    
        dut() {
            m_dut.aclk(aclk);
            m_dut.areset_n(areset_n);
            m_dut.in(in);
            m_dut.out(out);
        }
    private:
        my_module m_dut{"my_module"};
    };
    
    static factory::add<dut> g;
    
    TEST(my_module, simple) {
        auto test = factory::get<dut>();
    
        test->areset_n = 0;
        test->in = 0;
        sc_start(3, SC_NS);
    
        test->areset_n = 1;
        test->in = 1;
        sc_start(3, SC_NS);
    
        EXPECT_TRUE(test->out.read());
    }
    
    然后在测试中定义dut类,然后创建SystemC信号和SystemC模块。在构造函数中,在创建的SystemC信号和模块之间建立连接。使用全局构造函数将定义的dut类注册到注册表对象,如下图所示工厂::添加g。之后,您可以获得dut对象ect使用简单的工厂::get()方法

    测试.cpp

    #ifndef FACTORY_HPP
    #define FACTORY_HPP
    
    #include <map>
    #include <string>
    #include <memory>
    #include <functional>
    
    class factory {
    public:
        static factory& get_instance();
    
        template<typename T, typename ...Args>
        class add {
        public:
            add(Args&&... args);
    
            add(const std::string& name, Args&&... args);
        };
    
        template<typename T>
        static T* get(const std::string& name = "");
    
        void create();
    
        void destroy();
    private:
        using destructor = std::function<void(void*)>;
        using object = std::unique_ptr<void, destructor>;
        using constructor = std::function<object(void)>;
    
        factory();
    
        factory(const factory& other) = delete;
    
        factory& operator=(const factory& other) = delete;
    
        void add_object(const std::string& name, constructor create);
    
        void* get_object(const std::string& name);
    
        std::map<std::string, constructor> m_constructors;
        std::map<std::string, object> m_objects;
    };
    
    template<typename T, typename ...Args>
    factory::add<T, Args...>::add(Args&&... args) {
        add("", args...);
    }
    
    template<typename T, typename ...Args>
    factory::add<T, Args...>::add(const std::string& name, Args&&... args) {
        factory::get_instance().add_object(name,
            [args...] () -> object {
                return object{
                    new T(std::forward<Args>(args)...),
                    [] (void* obj) {
                        delete static_cast<T*>(obj);
                    }
                };
            }
        );
    }
    
    template<typename T> auto
    factory::get(const std::string& name) -> T* {
        return static_cast<T*>(factory::get_instance().get_object(name));
    }
    
    #endif /* FACTORY_HPP */
    
    #include "factory.hpp"
    
    #include <stdexcept>
    
    auto factory::get_instance() -> factory& {
        static factory instance{};
        return instance;
    }
    
    factory::factory() :
        m_constructors{},
        m_objects{}
    { }
    
    void factory::create() {
        for (const auto& item : m_constructors) {
            m_objects[item.first] = item.second();
        }
    }
    
    void factory::destroy() {
        m_objects.clear();
    }
    
    void factory::add_object(const std::string& name, constructor create) {
        auto it = m_constructors.find(name);
    
        if (it == m_constructors.cend()) {
            m_constructors[name] = create;
        }
        else {
            throw std::runtime_error("factory::add(): "
                    + name + " object already exist in factory");
        }
    }
    
    auto factory::get_object(const std::string& name) -> void* {
        auto it = m_objects.find(name);
    
        if (it == m_objects.cend()) {
            throw std::runtime_error("factory::get(): "
                    + name + " object doesn't exist in factory");
        }
    
        return it->second.get();
    }
    
    #include "factory.hpp"
    
    #include <systemc>
    #include <gtest/gtest.h>
    
    int sc_main(int argc, char* argv[]) {
    
        factory::get_instance().create();
    
        testing::InitGoogleTest(&argc, argv);
        int status = RUN_ALL_TESTS();
    
        factory::get_instance().destroy();
    
        return status;
    }
    
    #include "my_module.h"
    #include "factory.hpp"
    
    #include <gtest/gtest.h>
    #include <systemc>
    
    class dut {
    public:
        sc_core::sc_clock aclk{"aclk"};
        sc_core::sc_signal<bool> areset_n{"areset_n"};
        sc_core::sc_signal<bool> in{"in"};
        sc_core::sc_signal<bool> out{"out"};
    
        dut() {
            m_dut.aclk(aclk);
            m_dut.areset_n(areset_n);
            m_dut.in(in);
            m_dut.out(out);
        }
    private:
        my_module m_dut{"my_module"};
    };
    
    static factory::add<dut> g;
    
    TEST(my_module, simple) {
        auto test = factory::get<dut>();
    
        test->areset_n = 0;
        test->in = 0;
        sc_start(3, SC_NS);
    
        test->areset_n = 1;
        test->in = 1;
        sc_start(3, SC_NS);
    
        EXPECT_TRUE(test->out.read());
    }
    

    要获得更多灵感,您可以查看我的逻辑库以进行SystemC验证:

    谢谢您的回复!您可以为Google测试框架提供一些SystemC代码吗?也许您可以使用进程而不是线程,这样SystemC模拟器就不会抱怨了。我遇到了同样的问题(SystemC抱怨sc_启动)()被称为不止一个