Using existing unit test frameworks with SystemC

后端 未结 4 749
野的像风
野的像风 2021-02-05 17:28

I am working on a project in SystemC and want to incorporate unit testing. Is it possible to use existing unit test frameworks with SystemC?

I ask this because it seems

4条回答
  •  清酒与你
    2021-02-05 17:55

    You must create all necessary SystemC signals, SystemC modules and make connection between them before you run any test in GTest. This requires to create own gtest_main.cc implementation. Naturally in SystemC you must put everything in sc_main function.

    For this, I would use registry design pattern.

    First create registry class (registry + factory + singleton). This class will be responsible for storing registered constructors using dynamic allocation with new and smart pointer in lambda expression (see factory::add class). Create all objects using factory::create() method before running all tests. Then you can get object using factory::get()method in you test execution.

    factory.hpp

    #ifndef FACTORY_HPP
    #define FACTORY_HPP
    
    #include 
    #include 
    #include 
    #include 
    
    class factory {
    public:
        static factory& get_instance();
    
        template
        class add {
        public:
            add(Args&&... args);
    
            add(const std::string& name, Args&&... args);
        };
    
        template
        static T* get(const std::string& name = "");
    
        void create();
    
        void destroy();
    private:
        using destructor = std::function;
        using object = std::unique_ptr;
        using constructor = std::function;
    
        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 m_constructors;
        std::map m_objects;
    };
    
    template
    factory::add::add(Args&&... args) {
        add("", args...);
    }
    
    template
    factory::add::add(const std::string& name, Args&&... args) {
        factory::get_instance().add_object(name,
            [args...] () -> object {
                return object{
                    new T(std::forward(args)...),
                    [] (void* obj) {
                        delete static_cast(obj);
                    }
                };
            }
        );
    }
    
    template auto
    factory::get(const std::string& name) -> T* {
        return static_cast(factory::get_instance().get_object(name));
    }
    
    #endif /* FACTORY_HPP */
    

    factory.cpp

    #include "factory.hpp"
    
    #include 
    
    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();
    }
    

    Create your own version of gtest_main.cc implementation. Call factory::create() method to create all SystemC signals and SystemC modules before running any tests RUN_ALL_TESTS(). Because factory class is a singleton design pattern, call factory::destroy() method after finishing all tests to destroy all created SystemC objects.

    main.cpp

    #include "factory.hpp"
    
    #include 
    #include 
    
    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;
    }
    

    Then define dut class in your test than will create SystemC signals and SystemC modules. In constructor do connection between created SystemC signals and modules. Register defined dut class to registry object using global constructor like this factory::add g. After than you can get your dut object using simple factory::get() method.

    test.cpp

    #include "my_module.h"
    #include "factory.hpp"
    
    #include 
    #include 
    
    class dut {
    public:
        sc_core::sc_clock aclk{"aclk"};
        sc_core::sc_signal areset_n{"areset_n"};
        sc_core::sc_signal in{"in"};
        sc_core::sc_signal 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 g;
    
    TEST(my_module, simple) {
        auto test = factory::get();
    
        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());
    }
    

    my_module.h

    #ifndef MY_MODULE_H
    #define MY_MODULE_H
    
    #include 
    
    struct my_module : public sc_core::sc_module {
        my_module(const sc_core::sc_module_name& name): sc_core::sc_module(name) {
            SC_HAS_PROCESS(my_module);
            SC_METHOD(flip_flop_impl);
            sensitive << aclk.pos();
                      << areset_n.neg();
            dont_initialize();
        }
    
        void flip_flop_impl() {
            if(areset_n.read()) {
                out.write(in.read());
            } else {
                out.write(false);
            }
        }
    
        sc_core::sc_in aclk{"aclk"};
        sc_core::sc_in areset_n{"areset_n"};
        sc_core::sc_in in{"in"};
        sc_core::sc_out out{"out"};
    }; //< my_module
    
    #endif /* MY_MODULE_H */
    

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.5)
    
    project(factory_gtest)
    
    find_package(SystemCLanguage CONFIG REQUIRED)
    set(CMAKE_CXX_STANDARD ${SystemC_CXX_STANDARD})
    find_package(GTest REQUIRED)
    
    enable_testing()
    
    add_executable(${PROJECT_NAME} main.cpp factory.cpp test.cpp)
    target_link_libraries(${PROJECT_NAME} ${GTEST_LIBRARIES} SystemC::systemc)
    target_include_directories(${PROJECT_NAME} PRIVATE ${GTEST_INCLUDE_DIRS})
    
    add_test(SystemCGTestExample ${PROJECT_NAME})
    

    For more inspiration, you can check my logic library for SystemC verification: https://github.com/tymonx/logic

提交回复
热议问题