SystemC에서 프로젝트를 진행하고 있고 단위 테스트를 통합하려고합니다. SystemC에서 기존의 단위 테스트 프레임 워크를 사용할 수 있습니까?SystemC에서 기존 단위 테스트 프레임 워크 사용
SystemC 모듈이 시뮬레이션 커널에서만 실행되는 것처럼 보이고 모듈 자체에서 단위 테스트를 사용하고 싶습니다.
SystemC에서 프로젝트를 진행하고 있고 단위 테스트를 통합하려고합니다. SystemC에서 기존의 단위 테스트 프레임 워크를 사용할 수 있습니까?SystemC에서 기존 단위 테스트 프레임 워크 사용
SystemC 모듈이 시뮬레이션 커널에서만 실행되는 것처럼 보이고 모듈 자체에서 단위 테스트를 사용하고 싶습니다.
fork 시스템 호출을 사용하여 2 개의 SystemC 테스트를 실행할 수있었습니다. 튜토리얼 예제를 doulos.com과 Google 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
나는 "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"를 생략하고 특정 모듈에 당신이 원하는 특정 어떤 테스트를 수행 한 후 응용 프로그램을 종료 할 수 있습니다 .
매우 자주 SystemC DUT (Device-Under-Test)는 신호를 어서 트함으로써 초기 상태로 재설정 될 수 있습니다. 이 사실을 활용하고 원하는 C++ 단위 테스트 프레임 워크를 사용할 수 있습니다. 각 테스트를 실행하기 전에 DUT를 재설정하기 만하면되므로 두 번 정교하게 다룰 필요가 없습니다. 여기
구글 테스트와 예입니다, 간단한 "배터리"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)
, 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
감사합니다! 일부 SystemC 코드에 Google Test 프레임 워크를 제공 할 수 있습니까? – Joe
아마도 thread 대신 프로세스를 사용하여 systemc 시뮬레이터가 불평하지 않을 수도 있습니다. – Stephan
동일한 문제가 발생했습니다 (SystemC는 sc_start()가 두 번 이상 호출되는 것에 대해 불평 함). 혹시 당신이 관심이 있다면 [여기] (http://stackoverflow.com/questions/25706294/running-boost-unit-tests-on-different-processes) 내 원래의 질문과 내가 생각해 낸 해결책을 찾을 수있다. . – betabandido