Unit testing 在SystemC中使用现有的单元测试框架
我正在SystemC的一个项目中工作,希望加入单元测试。可以将现有的单元测试框架与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模块似乎只在模拟内核中执行,我想在模块本身上使用单元测试。我能够使用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_启动)()被称为不止一个