Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-compliance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/ci-unit-tests.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/build/
/.cproject
/.venv
/conan/
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -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
70 changes: 70 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -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
```
5 changes: 5 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 2 additions & 1 deletion src/components/scc/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ int memory<SIZE, BUSWIDTH>::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);
}
Expand Down
16 changes: 16 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
7 changes: 7 additions & 0 deletions tests/gtest_sc_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <gtest/gtest.h>
#include <systemc>

int sc_main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
39 changes: 39 additions & 0 deletions tests/memory_boundary_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Validates that handle_operation does not overwrite past the requested length
#include <array>
#include <systemc>
#include <tlm>
#include <gtest/gtest.h>

#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<uint8_t, 2> 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<uint8_t, 4> 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
}