From d9944f43c835540c1f686d1f618358ee73df0da4 Mon Sep 17 00:00:00 2001 From: Chris Hurley Date: Tue, 2 Dec 2025 15:47:59 -0800 Subject: [PATCH 1/4] update CI dependencies to enable act - act allows you to run GitHub Actions locally but requires minor changes to the workflow --- .github/workflows/ci-compliance.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-compliance.yml b/.github/workflows/ci-compliance.yml index 2ca8beac..aede1885 100644 --- a/.github/workflows/ci-compliance.yml +++ b/.github/workflows/ci-compliance.yml @@ -31,8 +31,8 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y g++ python3-pip cmake - pip3 install conan + sudo apt-get install -y g++ python3-pip cmake ninja-build + pip3 install conan --break-system-packages cmake --version - name: Configure From ad6167dba858d05167a5f9906cffdddb40682c33 Mon Sep 17 00:00:00 2001 From: Chris Hurley Date: Tue, 2 Dec 2025 15:48:29 -0800 Subject: [PATCH 2/4] .gitignore coan generated files - when running locally conan creates files that should be ignored by git --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c73eb7e9..6d38d085 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /build/ /.cproject /.venv +/conan/ From c38c577bf597eecb28a1a5e71a257b57e6cbf67f Mon Sep 17 00:00:00 2001 From: Chris Hurley Date: Tue, 2 Dec 2025 16:31:02 -0800 Subject: [PATCH 3/4] Add memory boundary test - Added unit tests to CI workflow. - Added Dockerfile for testing environment setup. - Created `TESTING.md` for guidelines on running tests. - Implemented memory boundary test in `tests/memory_boundary_test.cpp` with CMake configuration. --- .github/workflows/ci-unit-tests.yml | 53 ++++++++++++++++++++++ CMakeLists.txt | 9 ++++ Dockerfile.test | 17 +++++++ TESTING.md | 70 +++++++++++++++++++++++++++++ conanfile.py | 5 +++ tests/CMakeLists.txt | 16 +++++++ tests/gtest_sc_main.cpp | 7 +++ tests/memory_boundary_test.cpp | 39 ++++++++++++++++ 8 files changed, 216 insertions(+) create mode 100644 .github/workflows/ci-unit-tests.yml create mode 100644 Dockerfile.test create mode 100644 TESTING.md create mode 100644 tests/CMakeLists.txt create mode 100644 tests/gtest_sc_main.cpp create mode 100644 tests/memory_boundary_test.cpp diff --git a/.github/workflows/ci-unit-tests.yml b/.github/workflows/ci-unit-tests.yml new file mode 100644 index 00000000..93a1bbf6 --- /dev/null +++ b/.github/workflows/ci-unit-tests.yml @@ -0,0 +1,53 @@ +name: CI Unit Tests + +on: + push: + paths: + - '**.cpp' + - '**.h' + - '**CMakeLists.txt' + - '.github/workflows/**' + - 'conanfile.py' + pull_request: + paths: + - '**.cpp' + - '**.h' + - '**CMakeLists.txt' + - '.github/workflows/**' + - 'conanfile.py' + +jobs: + unit-tests: + name: unit-tests (C++20) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache Conan + uses: actions/cache@v4 + with: + path: ~/.conan + key: conan-${{ runner.os }}-unit-cpp20-${{ hashFiles('conanfile.txt') }} + + - name: Update submodules + run: git submodule update --init --recursive + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y g++ python3-pip cmake ninja-build + pip3 install conan --break-system-packages + cmake --version + + - name: Configure + run: > + cmake --preset Debug -B build + -DBUILD_TESTING=ON + -DCMAKE_CXX_STANDARD=20 + + - name: Build + run: cmake --build build -j + + - name: Run unit tests + run: ctest --output-on-failure + working-directory: build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index df44e40c..d8a262f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ project(scc VERSION 2025.09 LANGUAGES CXX C) option(USE_CWR_SYSTEMC "Use Synopsys Virtualizer SystemC" OFF) option(USE_NCSC_SYSTEMC "Cadence Xcelium SystemC" OFF) +option(BUILD_TESTING "Enable building tests" OFF) option(BUILD_SCC_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" OFF) option(FULL_TRACE_TYPE_LIST "Test for extended set of templated datatypes" OFF) #Note: this needs to match the SystemC kernel build options @@ -106,6 +107,10 @@ include(CheckSymbolExists) # Check for function getenv() check_symbol_exists(getenv "stdlib.h" HAVE_GETENV) +if(BUILD_TESTING) + include(CTest) +endif() + if(NOT TARGET lz4::lz4) message(STATUS "${PROJECT_NAME}: using built-in version of lz4") add_subdirectory(third_party/lz4-1.9.4) @@ -126,6 +131,10 @@ if(SystemC_FOUND) endif() endif() + if(BUILD_TESTING) + add_subdirectory(tests) + endif() + # Define the scc library add_library(scc INTERFACE) add_library(scc::scc ALIAS scc) diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 00000000..e3011cac --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,17 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + ninja-build \ + git \ + python3 \ + python3-pip \ + ca-certificates && \ + pip3 install "conan" && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /work diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..dd45690d --- /dev/null +++ b/TESTING.md @@ -0,0 +1,70 @@ +# Running Tests + +This repository has a GoogleTest-based suite (see `tests/`). The tests target the CMake option `BUILD_TESTING=ON` and require the same dependencies as the main build plus GoogleTest (fetched automatically via `FetchContent`). + +## Prerequisites + +- CMake ≥ 3.20 +- A C++ compiler (GCC/Clang) with C++17 or later +- Ninja build system +- Python 3 with `conan` installed. A virtual environment is recommended: + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install conan +``` + +If Conan cannot supply SystemC/CCI on your host, use the Docker flow below or point CMake at a local SystemC install via `SYSTEMC_HOME`. + +## Configure and Build + +```bash +cmake --preset Debug -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=20 +cmake --build build -j +``` + +## Run Tests + +```bash +cd build + +# unit tests +ctest --output-on-failure + +# transaction recording example binary +./examples/transaction_recording/transaction_recording +``` + +## Run Tests in Docker (Linux toolchain) + +If your host setup cannot fetch SystemC via Conan, use the provided Dockerfile: + +```bash +# from repo root +docker build -f Dockerfile.test -t scc-tests . +docker run --rm -v "$PWD":/work -w /work scc-tests /bin/bash -lc "\ + cmake --preset Debug -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=20 && \ + cmake --build build -j && \ + cd build && ctest --output-on-failure && \ + ./examples/transaction_recording/transaction_recording" +``` + +## CI + +### Unit Tests +Tests run in `.github/workflows/ci-unit-tests.yml`, which configures with `-DBUILD_TESTING=ON`, builds, and runs `ctest` with C++20. + +### C++ Standards Compliance +The repository is tested for compliance with multiple C++ standards in `.github/workflows/ci-compliance.yml`. This workflow: +- Tests against C++11, C++14, C++17, and C++20 +- Builds the project with each standard +- Runs the `transaction_recording` example to verify basic functionality + +To test a specific C++ standard locally: + +```bash +cmake --preset Debug -B build -DCMAKE_CXX_STANDARD=<11|14|17|20> +cmake --build build -j +./build/examples/transaction_recording/transaction_recording +``` diff --git a/conanfile.py b/conanfile.py index ccbe5ae3..c2988022 100644 --- a/conanfile.py +++ b/conanfile.py @@ -39,8 +39,13 @@ def requirements(self): self.requires("systemc/2.3.4") else: self.requires("systemc/3.0.1") + if cppstd == "11": + self.requires("gtest/1.10.0") + else: + self.requires("gtest/1.14.0") else: self.requires("systemc/2.3.4") + self.requires("gtest/1.14.0") self.requires("fmt/8.0.1") self.requires("spdlog/1.9.2") self.requires("boost/1.85.0") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..3b913668 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +find_package(GTest REQUIRED) + +add_executable(memory_boundary_test + memory_boundary_test.cpp + gtest_sc_main.cpp +) + +target_link_libraries(memory_boundary_test + PRIVATE + GTest::gtest + scc::scc + SystemC::systemc +) +target_compile_definitions(memory_boundary_test PRIVATE SC_DISABLE_API_VERSION_CHECK) + +add_test(NAME memory_boundary_test COMMAND memory_boundary_test) diff --git a/tests/gtest_sc_main.cpp b/tests/gtest_sc_main.cpp new file mode 100644 index 00000000..5f4bf87f --- /dev/null +++ b/tests/gtest_sc_main.cpp @@ -0,0 +1,7 @@ +#include +#include + +int sc_main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/memory_boundary_test.cpp b/tests/memory_boundary_test.cpp new file mode 100644 index 00000000..506942af --- /dev/null +++ b/tests/memory_boundary_test.cpp @@ -0,0 +1,39 @@ +// Validates that handle_operation does not overwrite past the requested length +#include +#include +#include +#include + +#include "components/scc/memory.h" +#include "scc/cci_broker.h" + +TEST(MemoryBoundaryTest, ReadAcrossPageDoesNotOverwriteBuffer) { + static scc::cci_broker global_broker("global_broker"); + static auto broker_handle = cci::cci_register_broker(&global_broker); + constexpr uint64_t kPageSize = 1ull << 24; // matches util::sparse_array default page size + scc::memory<(kPageSize + 2u)> mem{"mem"}; + + std::array write_data{{0xAAu, 0xBBu}}; + tlm::tlm_generic_payload write; + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + write.set_command(tlm::TLM_WRITE_COMMAND); + write.set_address(kPageSize - 1); // straddles page boundary + write.set_data_length(write_data.size()); + write.set_streaming_width(write_data.size()); + write.set_data_ptr(write_data.data()); + mem.handle_operation(write, delay); + + std::array read_buf{{0xEEu, 0x00u, 0x00u, 0xEEu}}; + tlm::tlm_generic_payload read; + read.set_command(tlm::TLM_READ_COMMAND); + read.set_address(kPageSize - 1); + read.set_data_length(write_data.size()); + read.set_streaming_width(write_data.size()); + read.set_data_ptr(read_buf.data() + 1); // leave guard bytes at both ends + mem.handle_operation(read, delay); + + EXPECT_EQ(read_buf[0], 0xEEu); // leading guard untouched + EXPECT_EQ(read_buf[1], 0xAAu); // first byte read correctly + EXPECT_EQ(read_buf[2], 0xBBu); // second byte read correctly + EXPECT_EQ(read_buf[3], 0xEEu); // trailing guard must remain +} From 482e736c72aa9e966e48529df74dcf9630de6bfb Mon Sep 17 00:00:00 2001 From: Chris Hurley Date: Tue, 2 Dec 2025 17:00:15 -0800 Subject: [PATCH 4/4] Fix memory handling for page boundary operations in handle_operation --- src/components/scc/memory.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/scc/memory.h b/src/components/scc/memory.h index 910296ed..71c0aff4 100644 --- a/src/components/scc/memory.h +++ b/src/components/scc/memory.h @@ -251,7 +251,8 @@ int memory::handle_operation(tlm::tlm_generic_payload& trans, sc auto first_part = mem.page_size - offs; std::copy(p.data() + offs, p.data() + offs + first_part, ptr); const auto& p2 = mem((adr / mem.page_size) + 1); - std::copy(p2.data(), p2.data() + len, ptr + first_part); + auto second_part = len - first_part; + std::copy(p2.data(), p2.data() + second_part, ptr + first_part); } else { std::copy(p.data() + offs, p.data() + offs + len, ptr); }