diff --git a/.bazelrc b/.bazelrc deleted file mode 100644 index 0222a99..0000000 --- a/.bazelrc +++ /dev/null @@ -1,7 +0,0 @@ -# Compile cpp with c++20 standard -build --cxxopt='-std=c++2a' - -# "bazel build --config debug" -build:debug -c dbg -build:debug --cxxopt="-g" -build:debug --strip="never" \ No newline at end of file diff --git a/.gitignore b/.gitignore index e28b710..45519c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/bazel-* +/build .DS_Store \ No newline at end of file diff --git a/BUILD b/BUILD deleted file mode 100644 index da25e64..0000000 --- a/BUILD +++ /dev/null @@ -1 +0,0 @@ -# empty BUILD file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..92850cd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,153 @@ +cmake_minimum_required(VERSION 3.31.6) + +project(googletest-verilog + VERSION 0.2.0 + LANGUAGES CXX +) + +set(CMAKE_CXX_STANDARD 20) + +# +# GoogleTest Library +# + +# download google test +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/releases/download/v1.17.0/googletest-1.17.0.tar.gz +) + +# prevent googletest from overriding our compiler/linker options +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +# +# Verilator library +# + +# based on verilator documentation +# https://veripool.org/guide/latest/verilating.html#cmake + +find_package(verilator REQUIRED HINTS $ENV{VERILATOR_ROOT}) + +message(STATUS "VERILATOR_ROOT = ${VERILATOR_ROOT}") +message(STATUS "VERILATOR_BIN = ${VERILATOR_BIN}") + +# +# googletest-verilog test bench generation +# + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +function(googletest_verilog_testbench TB_TARGET) + cmake_parse_arguments( + TB # prefix + "" # list of options (true / false based on whether option is present or not) + "SOURCE" # single value keywords + "INCLUDE_DIRS" # multi-value keywords + ${ARGN} # arguments provided to the function + ) + + message(STATUS "SOURCE = ${TB_SOURCE}") + message(STATUS "TARGET = ${TB_TARGET}") + message(STATUS "INCLUDE_DIRS = ${TB_INCLUDE_DIRS}") + + get_filename_component(TB_SOURCE_BASENAME "${TB_SOURCE}" NAME_WE) + set(TB_TESTBENCH "${TB_SOURCE_BASENAME}TestBench") + + set(TB_VERILATED_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/verilated/${TB_TESTBENCH}) + set(TB_TESTBENCH_HEADER "${TB_VERILATED_OUTPUT_DIR}/${TB_TESTBENCH}.h") + set(TB_TESTBENCH_SOURCE "${TB_VERILATED_OUTPUT_DIR}/${TB_TESTBENCH}.cpp") + + message(STATUS "TB_TESTBENCH = ${TB_TESTBENCH}") + message(STATUS "TB_VERILATED_OUTPUT_DIR = ${TB_VERILATED_OUTPUT_DIR}") + message(STATUS "TB_TESTBENCH_HEADER = ${TB_TESTBENCH_HEADER}") + message(STATUS "TB_TESTBENCH_SOURCE = ${TB_TESTBENCH_SOURCE}") + message(STATUS "CMAKE_BINARY_DIR = " ${CMAKE_BINARY_DIR}) + message(STATUS "CMAKE_CURRENT_BINARY_DIR = " ${CMAKE_CURRENT_BINARY_DIR}) + + # 1. verilate the verilog module + + verilate(${TB_TARGET} SOURCES ${TB_SOURCE} INCLUDE_DIRS ${TB_INCLUDE_DIRS} DIRECTORY ${TB_VERILATED_OUTPUT_DIR}) + + # 2. generate test bench + set(TOOL_GENERATE_TESTBENCH ${CMAKE_SOURCE_DIR}/tools/generate_testbench.py) + + add_custom_command( + OUTPUT ${TB_TESTBENCH_HEADER} ${TB_TESTBENCH_SOURCE} + COMMAND ${Python3_EXECUTABLE} ${TOOL_GENERATE_TESTBENCH} --name ${TB_TESTBENCH} --verilated-header-dir ${TB_VERILATED_OUTPUT_DIR} --output-header ${TB_TESTBENCH_HEADER} --output-source ${TB_TESTBENCH_SOURCE} + DEPENDS ${TOOL_GENERATE_TESTBENCH} ${TB_SOURCE} + ) + + target_sources(${TB_TARGET} PRIVATE ${TB_TESTBENCH_HEADER} ${TB_TESTBENCH_SOURCE}) + + set(TB_TESTBENCH_TARGET "googletest_verilog_testbench-${TB_TESTBENCH}") + + # 3. use a custom target to force cmake to create testbench first, before compiling TARGET + + add_custom_target(${TB_TESTBENCH_TARGET} + DEPENDS ${TB_TESTBENCH_HEADER} ${TB_TESTBENCH_SOURCE} + ) + + add_dependencies(${TB_TARGET} ${TB_TESTBENCH_TARGET}) + +endfunction() + +# +# googletest-verilog static library +# + +include_directories(include) + +add_library(googletest-verilog + src/ConsoleColour.cpp + src/MatchesTrace.cpp + src/PortDescription.cpp + src/Step.cpp + src/Trace.cpp + src/TraceBuilder.cpp +) + +# add googletest/googlemock includes, without linking to gtest +get_target_property(GTEST_INCLUDES GTest::gtest INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(GMOCK_INCLUDES GTest::gmock INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(googletest-verilog PUBLIC ${GTEST_INCLUDES} ${GMOCK_INCLUDES}) + +# +# googletest-verilog internal Unit Tests +# + +add_executable(googletest-verilog-test + test/main.cpp + test/MatchesTrace.test.cpp + test/Step.test.cpp + test/TestBench.test.cpp + test/Trace.test.cpp + test/TraceBuilder.test.cpp +) + +target_link_libraries(googletest-verilog-test + gtest + gmock + googletest-verilog +) + +# +# googletest-verilog Example Usage +# + +add_executable(googletest-verilog-example + example/main.cpp + example/Counter.test.cpp +) + +googletest_verilog_testbench(googletest-verilog-example SOURCE example/Counter.v) + +target_link_libraries(googletest-verilog-example PRIVATE + googletest-verilog + gtest + gmock +) + +target_include_directories(googletest-verilog-example PRIVATE ${VERILATOR_INCLUDE_DIRS}) diff --git a/README.md b/README.md index 2e23268..eac5dcf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,13 @@ -- bazel build //gtestverilog:test && ./bazel-bin/gtestverilog/test --gtest_filter="*" +# googletest-verilog -- bazel build //gtestverilog:example --incompatible_require_linker_input_cc_api=false && ./bazel-bin/gtestverilog/example --gtest_filter="*" +Add support for writing C++ unit-tests for verilog modules with [googletest](https://github.com/google/googletest). +Unit-tests are run in simulation with [verilator](https://github.com/verilator/verilator). + +# Dependencies + +MacOS - to install with homebrew: + +``` brew +brew install verilator cmake +``` diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index ff8b5ab..0000000 --- a/WORKSPACE +++ /dev/null @@ -1,42 +0,0 @@ -# BAZEL Build System - -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") - -###################################################################### -# GoogleTest - -git_repository( - name = "com_google_googletest", - remote = "https://github.com/google/googletest", - commit = "18f8200e3079b0e54fa00cb7ac55d4c39dcf6da6", -) - -###################################################################### -# Verilator - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "rules_verilator", - strip_prefix = "rules_verilator-jim-rc1", - url = "https://github.com/JimKnowler/rules_verilator/archive/jim-rc1.zip", -) - -load( - "@rules_verilator//verilator:repositories.bzl", - "rules_verilator_dependencies", - "rules_verilator_toolchains", -) - -rules_verilator_dependencies() -rules_verilator_toolchains() - -# Register toolchain dependencies -load("@rules_m4//m4:m4.bzl", "m4_register_toolchains") -m4_register_toolchains() - -load("@rules_flex//flex:flex.bzl", "flex_register_toolchains") -flex_register_toolchains() - -load("@rules_bison//bison:bison.bzl", "bison_register_toolchains") -bison_register_toolchains() \ No newline at end of file diff --git a/gtestverilog/example/Counter.test.cpp b/example/Counter.test.cpp similarity index 96% rename from gtestverilog/example/Counter.test.cpp rename to example/Counter.test.cpp index 35e3f27..5def066 100644 --- a/gtestverilog/example/Counter.test.cpp +++ b/example/Counter.test.cpp @@ -1,10 +1,10 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; -#include "gtestverilog/CounterTestBench.h" +#include "CounterTestBench.h" using namespace countertestbench; namespace { @@ -88,4 +88,4 @@ TEST_F(Counter, ShouldHandleLargeTraces) { .port(i_simulate_sequential).signal( "10" ).repeat(1001); ASSERT_THAT(testBench.trace, MatchesTrace(traceExpected)); -} \ No newline at end of file +} diff --git a/gtestverilog/example/Counter.v b/example/Counter.v similarity index 100% rename from gtestverilog/example/Counter.v rename to example/Counter.v diff --git a/gtestverilog/example/main.cpp b/example/main.cpp similarity index 100% rename from gtestverilog/example/main.cpp rename to example/main.cpp diff --git a/gtestverilog/BUILD b/gtestverilog/BUILD deleted file mode 100644 index aa020fe..0000000 --- a/gtestverilog/BUILD +++ /dev/null @@ -1,64 +0,0 @@ -load("@rules_verilator//verilator:defs.bzl", "verilator_cc_library") -load(":defs.bzl", "gtest_verilog_testbench") - -py_binary( - name='generate_testbench', - srcs=['tools/generate_testbench.py'], - visibility=['//visibility:public'], -) - -cc_library( - name = "lib", - srcs = glob([ - "lib/*.cpp", - "lib/*.h" - ]), - hdrs = glob([ - "*.h" - ]), - deps = [ - "@com_google_googletest//:gtest", - ], - visibility = ["//visibility:public"] -) - -cc_test( - name = "test", - srcs = glob( - include =[ - "test/*.cpp", - "test/*.h" - ] - ), - deps = [ - "@com_google_googletest//:gtest", - ":lib" - ], -) - -verilator_cc_library( - name = "Counter", - srcs = ["example/Counter.v"], -) - -gtest_verilog_testbench( - name = "CounterTestBench", - deps = [":Counter"], -) - -cc_test( - name = "example", - srcs = [ - ":CounterTestBench", - ] + glob( - include = [ - "example/*.cpp", - "example/*.h", - ] - ), - deps = [ - "@com_google_googletest//:gtest", - ":lib", - ":Counter", - ] -) \ No newline at end of file diff --git a/gtestverilog/defs.bzl b/gtestverilog/defs.bzl deleted file mode 100644 index ca481b0..0000000 --- a/gtestverilog/defs.bzl +++ /dev/null @@ -1,45 +0,0 @@ - -def _gtest_verilog_testbench_impl(ctx): - input = None - verilated_header_dir = None - - for dep in ctx.attr.deps: - headers = dep[CcInfo].compilation_context.direct_headers - for header in headers: - if not header.is_directory: - fail("header is not a directory") - - input = header - verilated_header_dir = header.path - - if None == verilated_header_dir: - fail("missing header directory in output from verilator") - - ctx.actions.run( - inputs = [input], - outputs = [ - ctx.outputs.source_file, - ctx.outputs.header_file - ], - arguments = [ - "--name", ctx.attr.name, - "--verilated-header-dir", verilated_header_dir, - "--output-source", ctx.outputs.source_file.path, - "--output-header", ctx.outputs.header_file.path - ], - executable = ctx.executable.generate_testbench, - progress_message = "generating gtest-testbench for " + ctx.attr.name, - mnemonic = "GTestVerilogTestbench" - ) - -gtest_verilog_testbench = rule( - implementation = _gtest_verilog_testbench_impl, - attrs = { - "deps" : attr.label_list(providers = [CcInfo]), - "generate_testbench" : attr.label(executable=True, cfg="host", default="//gtestverilog:generate_testbench") - }, - outputs = { - "source_file": "%{name}.cpp", - "header_file": "%{name}.h" - }, -) \ No newline at end of file diff --git a/gtestverilog/gtestverilog.h b/gtestverilog/gtestverilog.h deleted file mode 100644 index fb45382..0000000 --- a/gtestverilog/gtestverilog.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "lib/MatchesTrace.h" -#include "lib/TestBench.h" -#include "lib/TraceBuilder.h" \ No newline at end of file diff --git a/gtestverilog/lib/ConsoleColour.h b/include/gtest-verilog/ConsoleColour.h similarity index 100% rename from gtestverilog/lib/ConsoleColour.h rename to include/gtest-verilog/ConsoleColour.h diff --git a/gtestverilog/lib/MatchesTrace.h b/include/gtest-verilog/MatchesTrace.h similarity index 70% rename from gtestverilog/lib/MatchesTrace.h rename to include/gtest-verilog/MatchesTrace.h index b3bb6fb..3f22fb9 100644 --- a/gtestverilog/lib/MatchesTrace.h +++ b/include/gtest-verilog/MatchesTrace.h @@ -1,14 +1,19 @@ +#include #include #include "Trace.h" +#ifndef MATCHER_P +#error "matcher_p is not defined here" +#endif + namespace gtestverilog { namespace matches_trace { bool compare(const Trace& a, const Trace& b, ::testing::MatchResultListener& listener); } - MATCHER_P(MatchesTrace, trace, "") { + MATCHER_P(MatchesTrace, trace, "Matches Trace") { if (!matches_trace::compare(arg, trace, *result_listener)) { return false; } diff --git a/gtestverilog/lib/PortDescription.h b/include/gtest-verilog/PortDescription.h similarity index 100% rename from gtestverilog/lib/PortDescription.h rename to include/gtest-verilog/PortDescription.h diff --git a/gtestverilog/lib/PortValue.h b/include/gtest-verilog/PortValue.h similarity index 100% rename from gtestverilog/lib/PortValue.h rename to include/gtest-verilog/PortValue.h diff --git a/gtestverilog/lib/Step.h b/include/gtest-verilog/Step.h similarity index 100% rename from gtestverilog/lib/Step.h rename to include/gtest-verilog/Step.h diff --git a/gtestverilog/lib/TestBench.h b/include/gtest-verilog/TestBench.h similarity index 100% rename from gtestverilog/lib/TestBench.h rename to include/gtest-verilog/TestBench.h diff --git a/gtestverilog/lib/Trace.h b/include/gtest-verilog/Trace.h similarity index 100% rename from gtestverilog/lib/Trace.h rename to include/gtest-verilog/Trace.h diff --git a/gtestverilog/lib/TraceBuilder.h b/include/gtest-verilog/TraceBuilder.h similarity index 100% rename from gtestverilog/lib/TraceBuilder.h rename to include/gtest-verilog/TraceBuilder.h diff --git a/include/gtest-verilog/gtest-verilog.h b/include/gtest-verilog/gtest-verilog.h new file mode 100644 index 0000000..3120565 --- /dev/null +++ b/include/gtest-verilog/gtest-verilog.h @@ -0,0 +1,5 @@ +#pragma once + +#include "MatchesTrace.h" +#include "TestBench.h" +#include "TraceBuilder.h" \ No newline at end of file diff --git a/gtestverilog/lib/ConsoleColour.cpp b/src/ConsoleColour.cpp similarity index 97% rename from gtestverilog/lib/ConsoleColour.cpp rename to src/ConsoleColour.cpp index cca6989..a67fea1 100644 --- a/gtestverilog/lib/ConsoleColour.cpp +++ b/src/ConsoleColour.cpp @@ -1,4 +1,4 @@ -#include "ConsoleColour.h" +#include "gtest-verilog/ConsoleColour.h" namespace gtestverilog { diff --git a/gtestverilog/lib/MatchesTrace.cpp b/src/MatchesTrace.cpp similarity index 98% rename from gtestverilog/lib/MatchesTrace.cpp rename to src/MatchesTrace.cpp index b431a02..14a94e0 100644 --- a/gtestverilog/lib/MatchesTrace.cpp +++ b/src/MatchesTrace.cpp @@ -1,4 +1,4 @@ -#include "MatchesTrace.h" +#include "gtest-verilog/MatchesTrace.h" #include diff --git a/gtestverilog/lib/PortDescription.cpp b/src/PortDescription.cpp similarity index 92% rename from gtestverilog/lib/PortDescription.cpp rename to src/PortDescription.cpp index 10bdce5..ecaa320 100644 --- a/gtestverilog/lib/PortDescription.cpp +++ b/src/PortDescription.cpp @@ -1,4 +1,4 @@ -#include "PortDescription.h" +#include "gtest-verilog/PortDescription.h" namespace gtestverilog { diff --git a/gtestverilog/lib/Step.cpp b/src/Step.cpp similarity index 97% rename from gtestverilog/lib/Step.cpp rename to src/Step.cpp index bb0eedc..bb941d9 100644 --- a/gtestverilog/lib/Step.cpp +++ b/src/Step.cpp @@ -1,4 +1,4 @@ -#include "Step.h" +#include "gtest-verilog/Step.h" namespace gtestverilog { diff --git a/gtestverilog/lib/Trace.cpp b/src/Trace.cpp similarity index 94% rename from gtestverilog/lib/Trace.cpp rename to src/Trace.cpp index a3e9c26..378ec28 100644 --- a/gtestverilog/lib/Trace.cpp +++ b/src/Trace.cpp @@ -1,4 +1,4 @@ -#include "Trace.h" +#include "gtest-verilog/Trace.h" #include #include @@ -42,7 +42,7 @@ namespace gtestverilog { static void renderTimeline(std::ostream& os, size_t x, size_t numSteps) { char buffer[64]; - sprintf(buffer, "%*s", int(x), " "); + snprintf(buffer, sizeof(buffer), "%*s", int(x), " "); os << buffer; const int kDividerSize = 5; @@ -50,7 +50,7 @@ namespace gtestverilog { for (int i=0; i 1) { - sprintf(buffer, "[%02lu:%02lu] ", ((nibbleIndex + 1) * 4) - 1, (nibbleIndex * 4)); + snprintf(buffer, sizeof(buffer), "[%02lu:%02lu] ", ((nibbleIndex + 1) * 4) - 1, (nibbleIndex * 4)); os << buffer; } else { os << "....... "; @@ -98,7 +98,7 @@ namespace gtestverilog { } uint8_t nibble = uint8_t( (value >> (nibbleIndex * 4)) & 0xf); - sprintf(buffer, "%X", nibble); + snprintf(buffer, sizeof(buffer), "%X", nibble); // use null terminator to force limit to a single hex character buffer[1] = 0; diff --git a/gtestverilog/lib/TraceBuilder.cpp b/src/TraceBuilder.cpp similarity index 99% rename from gtestverilog/lib/TraceBuilder.cpp rename to src/TraceBuilder.cpp index a498bf1..fd8f962 100644 --- a/gtestverilog/lib/TraceBuilder.cpp +++ b/src/TraceBuilder.cpp @@ -1,4 +1,4 @@ -#include "TraceBuilder.h" +#include "gtest-verilog/TraceBuilder.h" namespace gtestverilog { diff --git a/gtestverilog/test/MatchesTrace.test.cpp b/test/MatchesTrace.test.cpp similarity index 98% rename from gtestverilog/test/MatchesTrace.test.cpp rename to test/MatchesTrace.test.cpp index 4e47a38..297609e 100644 --- a/gtestverilog/test/MatchesTrace.test.cpp +++ b/test/MatchesTrace.test.cpp @@ -1,7 +1,7 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; namespace { diff --git a/gtestverilog/test/Step.test.cpp b/test/Step.test.cpp similarity index 98% rename from gtestverilog/test/Step.test.cpp rename to test/Step.test.cpp index 988116a..3f8641e 100644 --- a/gtestverilog/test/Step.test.cpp +++ b/test/Step.test.cpp @@ -1,7 +1,7 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; namespace { diff --git a/gtestverilog/test/TestBench.test.cpp b/test/TestBench.test.cpp similarity index 98% rename from gtestverilog/test/TestBench.test.cpp rename to test/TestBench.test.cpp index b77e8f1..3a12d71 100644 --- a/gtestverilog/test/TestBench.test.cpp +++ b/test/TestBench.test.cpp @@ -1,7 +1,7 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; namespace { diff --git a/gtestverilog/test/Trace.test.cpp b/test/Trace.test.cpp similarity index 99% rename from gtestverilog/test/Trace.test.cpp rename to test/Trace.test.cpp index b55527f..c5c879a 100644 --- a/gtestverilog/test/Trace.test.cpp +++ b/test/Trace.test.cpp @@ -1,7 +1,7 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; namespace { diff --git a/gtestverilog/test/TraceBuilder.test.cpp b/test/TraceBuilder.test.cpp similarity index 99% rename from gtestverilog/test/TraceBuilder.test.cpp rename to test/TraceBuilder.test.cpp index ae80cba..33e8ee3 100644 --- a/gtestverilog/test/TraceBuilder.test.cpp +++ b/test/TraceBuilder.test.cpp @@ -1,7 +1,7 @@ #include using namespace testing; -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" using namespace gtestverilog; namespace { diff --git a/gtestverilog/test/main.cpp b/test/main.cpp similarity index 100% rename from gtestverilog/test/main.cpp rename to test/main.cpp diff --git a/gtestverilog/tools/generate_testbench.py b/tools/generate_testbench.py similarity index 97% rename from gtestverilog/tools/generate_testbench.py rename to tools/generate_testbench.py index 4e1d1ac..99dfa70 100644 --- a/gtestverilog/tools/generate_testbench.py +++ b/tools/generate_testbench.py @@ -26,7 +26,7 @@ for filepath in verilated_header_filepaths: with open(filepath, "r") as file: for line in file: - results = re.search("\w*VL_[IN|OUT].*\((.*),(.*),(.*)\);\w*", line.lstrip().rstrip()) + results = re.search(r"\w*VL_[IN|OUT].*\((.*),(.*),(.*)\);\w*", line.lstrip().rstrip()) if results: # this is the header with port definitions @@ -138,7 +138,7 @@ def write_header_file(): header_top = """ #pragma once -#include "gtestverilog/gtestverilog.h" +#include "gtest-verilog/gtest-verilog.h" #include "{verilated_header_filepath}" namespace {namespace} {{