2011-02-07 1 views
6

SystemC에서 프로젝트를 진행하고 있고 단위 테스트를 통합하려고합니다. SystemC에서 기존의 단위 테스트 프레임 워크를 사용할 수 있습니까?SystemC에서 기존 단위 테스트 프레임 워크 사용

SystemC 모듈이 시뮬레이션 커널에서만 실행되는 것처럼 보이고 모듈 자체에서 단위 테스트를 사용하고 싶습니다.

답변

2

fork 시스템 호출을 사용하여 2 개의 SystemC 테스트를 실행할 수있었습니다. 튜토리얼 예제를 doulos.comGoogle Test 프레임 워크에 사용했습니다. 두 번 테스트를 실행할 수 있었지만 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. 

UPDATE : 요청에 따라 코드 샘플 :

// 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 
+0

감사합니다! 일부 SystemC 코드에 Google Test 프레임 워크를 제공 할 수 있습니까? – Joe

+2

아마도 thread 대신 프로세스를 사용하여 systemc 시뮬레이터가 불평하지 않을 수도 있습니다. – Stephan

+0

동일한 문제가 발생했습니다 (SystemC는 sc_start()가 두 번 이상 호출되는 것에 대해 불평 함). 혹시 당신이 관심이 있다면 [여기] (http://stackoverflow.com/questions/25706294/running-boost-unit-tests-on-different-processes) 내 원래의 질문과 내가 생각해 낸 해결책을 찾을 수있다. . – betabandido

1

나는 "cmake"및 사용이 질문에 두 번째 해결책을 가지고 있습니다 "ctest"(http://cmake.org/) . 필자가 사용한 설정은 각 테스트마다 바이너리를 만듭니다. 여기 내가 사용하는 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) 

각 테스트 _ * 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_start"를 생략하고 특정 모듈에 당신이 원하는 특정 어떤 테스트를 수행 한 후 응용 프로그램을 종료 할 수 있습니다 .

0

매우 자주 SystemC DUT (Device-Under-Test)는 신호를 어서 트함으로써 초기 상태로 재설정 될 수 있습니다. 이 사실을 활용하고 원하는 C++ 단위 테스트 프레임 워크를 사용할 수 있습니다. 각 테스트를 실행하기 전에 DUT를 재설정하기 만하면되므로 두 번 정교하게 다룰 필요가 없습니다. 여기

구글 테스트와 예입니다, 간단한 "배터리"DUT

  1. 초기화 GTest는 (:: 시험 :: InitGoogleTest (&는 argc는 ARGV))에서에게
  2. 는 모델을 정교한 sc_main
  3. RUN_ALL_TESTS()를 호출하여 sc_module 내의 일부 스레드에서 테스트 실행
  4. SystemC DUT 인터페이스에 대한 포인터를 테스트에 전달해야합니다. 내가 그 목적을 위해 전역 변수를 사용했습니다

소스 :

#include <systemc.h> 
#include "gtest/gtest.h" 

class 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(); 
} 

CMakeLists.txt (설치 필요 SystemC를 2.3.2) 당신이 필요한 모든 SystemC를 신호를 생성해야합니다

cmake_minimum_required(VERSION 3.8) 
project(systemc_gtest) 

find_package(SystemCLanguage CONFIG REQUIRED) 

set (CMAKE_CXX_STANDARD ${SystemC_CXX_STANDARD}) 

find_package(GTest REQUIRED) 

enable_testing() 

add_executable(systemc_gtest main.cpp) 
target_link_libraries(systemc_gtest ${GTEST_LIBRARIES} SystemC::systemc) 
target_include_directories(systemc_gtest PRIVATE ${GTEST_INCLUDE_DIRS}) 
add_test(AllTestsInSystemCGtest systemc_gtest) 
0

, SystemC 모듈을 만들고 gtest에서 테스트를 실행하기 전에 이들 사이를 연결하십시오. 이를 위해서는 gtest_main.cc 구현을 생성해야합니다. 당연히 SystemC에서는 sc_main() 함수를 모두 넣어야합니다.

이 경우 레지스트리 디자인 패턴을 사용합니다.

먼저 레지스트리 클래스 (registry + factory + singleton)를 만듭니다. 이 클래스는 람다 식으로 새롭고 스마트 한 포인터를 사용하여 동적 할당을 사용하여 등록 된 생성자를 저장합니다 (factory :: add 클래스 참조). 모든 테스트를 실행하기 전에 factory :: create() 메소드를 사용하여 모든 객체를 만듭니다. 그럼 당신은 실행 테스트를 factory :: get() 메서드를 사용하여 객체를 얻을 수 있습니다.

factory.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 */ 

factory.cpp

#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(); 
} 

는 gtest_main.cc 구현의 자신의 버전을 만듭니다. 어떤 테스트를 실행하기 전에 factory :: create() 메서드를 호출하여 모든 SystemC 신호와 SystemC 모듈을 생성합니다. RUN_ALL_TESTS(). 팩토리 클래스는 싱글 톤 디자인 패턴이기 때문에 모든 테스트를 마친 후 factory :: destroy() 메서드를 호출하여 생성 된 모든 SystemC 객체를 파괴합니다. SystemC를 신호와 SystemC를 모듈을 만드는 것보다

MAIN.CPP

#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; 
} 

그런 다음 테스트에 클래스 DUT를 정의합니다. 생성자에서 생성 된 SystemC 신호와 모듈을 연결합니다. 레지스터 클래스를 정의하십시오. 과 같은 글로벌 생성자를 사용하는 레지스트리 객체에 대한 클래스 factory :: add g. 간단한 factory :: get() 메서드를 사용하여 dut 객체를 가져올 수 있습니다.

Test.cpp에

#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를 확인 내 논리 라이브러리를 확인할 수 있습니다 답장을 https://github.com/tymonx/logic