diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml index ea44d0cbe..263072b67 100644 --- a/.github/workflows/asan.yml +++ b/.github/workflows/asan.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/amd64] target: [clang-shared-regression-asan] steps: @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/arm64] target: [clang-shared-regression-asan] steps: diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 20bf94ea8..b20ab87a2 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/amd64] target: [gcc-shared, gcc-static, clang-shared, clang-static] steps: @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/arm64] target: [gcc-shared, gcc-static, clang-shared, clang-static] steps: @@ -87,3 +87,41 @@ jobs: cmake -B BUILD/RELEASE/BUILD . cmake --build BUILD/RELEASE/BUILD --config RELEASE --parallel cmake --install BUILD/RELEASE/BUILD --config RELEASE + msys2-ucrt64: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + target: [gcc-shared, gcc-static] + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v5 + - uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: git mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja + - name: Build + run: | + SYSTEMC_CI_TARGET=${{ matrix.target }} SYSTEMC_SRC_PATH=$PWD docker/entrypoint.sh + msys2-clangarm64: + runs-on: windows-11-arm + strategy: + fail-fast: false + matrix: + target: [clang-shared, clang-static] + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v5 + - uses: msys2/setup-msys2@v2 + with: + msystem: CLANGARM64 + update: true + install: git mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja + - name: Build + run: | + SYSTEMC_CI_TARGET=${{ matrix.target }} SYSTEMC_SRC_PATH=$PWD docker/entrypoint.sh diff --git a/.github/workflows/regressions.yml b/.github/workflows/regressions.yml index c2970e0fb..2a3a864f9 100644 --- a/.github/workflows/regressions.yml +++ b/.github/workflows/regressions.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/amd64] target: [gcc-shared-regression, clang-shared-regression] steps: @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/arm64] target: [gcc-shared-regression, clang-shared-regression] steps: @@ -87,3 +87,41 @@ jobs: cmake -B BUILD/RELEASE/BUILD -DENABLE_REGRESSION=true . cmake --build BUILD/RELEASE/BUILD --config RELEASE --parallel cmake --install BUILD/RELEASE/BUILD --config RELEASE + msys2-ucrt64: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + target: [gcc-shared-regression, gcc-static-regression] + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v5 + - uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: git mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja + - name: Build + run: | + SYSTEMC_CI_TARGET=${{ matrix.target }} SYSTEMC_SRC_PATH=$PWD docker/entrypoint.sh + msys2-clangarm64: + runs-on: windows-11-arm + strategy: + fail-fast: false + matrix: + target: [clang-shared-regression, clang-static-regression] + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v5 + - uses: msys2/setup-msys2@v2 + with: + msystem: CLANGARM64 + update: true + install: git mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja + - name: Build + run: | + SYSTEMC_CI_TARGET=${{ matrix.target }} SYSTEMC_SRC_PATH=$PWD docker/entrypoint.sh diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml index 2a058c547..cd67a02b4 100644 --- a/.github/workflows/tsan.yml +++ b/.github/workflows/tsan.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/amd64] target: [clang-shared-regression-tsan] steps: @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/arm64] target: [clang-shared-regression-tsan] steps: diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml index bb983a642..5a145e7ee 100644 --- a/.github/workflows/ubsan.yml +++ b/.github/workflows/ubsan.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/amd64] target: [clang-shared-regression-ubsan] steps: @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - version: [20.04, 22.04, 24.04] + version: [22.04, 24.04] platform: [linux/arm64] target: [clang-shared-regression-ubsan] steps: diff --git a/NOTICE b/NOTICE index 8077e3578..9e3f7a60e 100644 --- a/NOTICE +++ b/NOTICE @@ -68,6 +68,11 @@ This product includes software developed by Mentor Graphics Corporation Copyright 2006-2019 Mentor Graphics Corporation All rights reserved. +This product includes software developed by MINRES Technologies GmbH +Keltenhof 2, 85579 Neubibergm Germany +Copyright 2016-2025 XtremeEDA Corporation. +All rights reserved. + This product includes software developed by NXP B.V. High Tech Campus 60, 5656 AG Eindhoven, Netherlands Copyright 2012-2023 NXP B.V. diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 57207e476..4fd4c1b61 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -11,6 +11,19 @@ fi # Build with -Werror by default CXX_FLAGS="-Werror" +# Don't build with -Werror on AlmaLinux 8 on arm64 hosts +# GCC 8.5 does not ignore false positive -Wshift-negative-value warning +if [[ -f /etc/os-release ]]; then + . /etc/os-release + + ARCH=$(uname -m) + + if [[ "$ID" == "almalinux" && "$VERSION_ID" == 8* ]] && \ + [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + CXX_FLAGS="" + fi +fi + case "$SYSTEMC_CI_TARGET" in gcc-shared) CC=gcc @@ -30,6 +43,12 @@ case "$SYSTEMC_CI_TARGET" in BUILD_SHARED_LIBRARY=true BUILD_REGRESSIONS=true ;; + gcc-static-regression) + CC=gcc + CXX=g++ + BUILD_SHARED_LIBRARY=false + BUILD_REGRESSIONS=true + ;; clang-shared) CC=clang CXX=clang++ @@ -48,6 +67,12 @@ case "$SYSTEMC_CI_TARGET" in BUILD_SHARED_LIBRARY=true BUILD_REGRESSIONS=true ;; + clang-static-regression) + CC=clang + CXX=clang++ + BUILD_SHARED_LIBRARY=false + BUILD_REGRESSIONS=true + ;; clang-shared-regression-asan) CC=clang CXX=clang++ @@ -75,16 +100,20 @@ case "$SYSTEMC_CI_TARGET" in ;; esac -cd $SYSTEMC_SRC_PATH -cmake -B BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD \ - -DCMAKE_INSTALL_PREFIX=${SYSTEMC_SRC_PATH}/BUILD/${SYSTEMC_CI_TARGET} \ + +cd "$SYSTEMC_SRC_PATH" +cmake -B "BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD" \ + -DCMAKE_INSTALL_PREFIX="${SYSTEMC_SRC_PATH}/BUILD/${SYSTEMC_CI_TARGET}" \ -DCMAKE_CXX_FLAGS="$CXX_FLAGS" \ - -DCMAKE_C_COMPILER=$CC \ - -DCMAKE_CXX_COMPILER=$CXX \ - -DENABLE_REGRESSION=$BUILD_REGRESSIONS \ - -DBUILD_SHARED_LIBS=$BUILD_SHARED_LIBRARY . -cmake --build BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/ --parallel -cmake --install BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/ -make -j `getconf _NPROCESSORS_ONLN` -C BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/ check + -DCMAKE_C_COMPILER="$CC" \ + -DCMAKE_CXX_COMPILER="$CXX" \ + -DENABLE_REGRESSION="$BUILD_REGRESSIONS" \ + -DBUILD_SHARED_LIBS="$BUILD_SHARED_LIBRARY" . +cmake --build "BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/" --parallel +cmake --install "BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/" + +if [[ "$BUILD_REGRESSIONS" == "true" ]]; then + cmake --build "BUILD/RELEASE-${SYSTEMC_CI_TARGET}/BUILD/" --parallel "$(getconf _NPROCESSORS_ONLN)" --target check +fi exit 0 diff --git a/examples/sysc/CMakeLists.txt b/examples/sysc/CMakeLists.txt index 60a3eafd6..be0fbbeb1 100644 --- a/examples/sysc/CMakeLists.txt +++ b/examples/sysc/CMakeLists.txt @@ -65,4 +65,5 @@ add_subdirectory (simple_bus) add_subdirectory (simple_fifo) add_subdirectory (simple_perf) add_subdirectory (async_suspend) +add_subdirectory (concurrent) diff --git a/examples/sysc/async_suspend/node.h b/examples/sysc/async_suspend/node.h index 419c882eb..34cb275e2 100644 --- a/examples/sysc/async_suspend/node.h +++ b/examples/sysc/async_suspend/node.h @@ -118,6 +118,7 @@ SC_MODULE (asynctestnode) init_socket("output"), target_socket("input"), txnSent_c(0), + txn(nullptr), suspendReq(false), col(c), running(true), @@ -136,12 +137,22 @@ SC_MODULE (asynctestnode) sensitive << txnSentEvent; } - ~asynctestnode() + virtual ~asynctestnode() { running = false; while (!finished) txnSentMethod(); if (m_thread.joinable()) m_thread.join(); + + delete txn; + txn = nullptr; + + while (!queue.empty()) + { + delete queue.front(); + queue.pop(); + } + } // This will cause SystemC time to be driven forwards. But, if we're not @@ -239,6 +250,7 @@ SC_MODULE (asynctestnode) // Send transaction to a random place init_socket[rand() % init_socket.size()]->b_transport(*txn, myTime); + txn = nullptr; #ifdef DEBUG std::stringstream msg; diff --git a/examples/sysc/concurrent/CMakeLists.txt b/examples/sysc/concurrent/CMakeLists.txt new file mode 100644 index 000000000..3a220aed7 --- /dev/null +++ b/examples/sysc/concurrent/CMakeLists.txt @@ -0,0 +1,32 @@ +############################################################################### +# +# Licensed to Accellera Systems Initiative Inc. (Accellera) under one or +# more contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright ownership. +# Accellera licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +############################################################################### + +############################################################################### +# +# examples/sysc/concurrent/CMakeLists.txt -- +# Build the SC_ALLOW_CONCURRENCY demonstrator. +# +############################################################################### + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) + +add_executable(concurrent_test concurrent_test.cpp) +target_link_libraries(concurrent_test SystemC::systemc Threads::Threads) +configure_and_add_test(concurrent_test) diff --git a/examples/sysc/concurrent/concurrent_test.cpp b/examples/sysc/concurrent/concurrent_test.cpp new file mode 100644 index 000000000..e0f6c908d --- /dev/null +++ b/examples/sysc/concurrent/concurrent_test.cpp @@ -0,0 +1,192 @@ +#include +#include + +#include +#include + +#include "sysc/utils/sc_concurrent.h" +#include + +std::mutex mutex; + +int do_work() { + double f = 0; + for (double j = 0; j < 100; j++) { + for (double k = 0; k < 100; k++) { + f += (j * k); + } + } + return f; +} +sc_core::sc_time time1 = SC_ZERO_TIME; +sc_core::sc_time time2 = SC_ZERO_TIME; +sc_core::sc_time worst = SC_ZERO_TIME; + +std::mutex tm; +void snaptime(sc_time &time) { + std::lock_guard lg(tm); + time = sc_time_stamp(); + auto d = (time1 > time2) ? time1 - time2 : time2 - time1; + if (d > worst) + worst = d; +} + +SC_MODULE(my_parallel_module) { + sc_event send_txn; + + void t1() { SC_REPORT_INFO(name(), "T1"); } + void t2() { + SC_REPORT_INFO(name(), "T2"); + wait(1, sc_core::SC_NS); + SC_REPORT_INFO(name(), "T2 1ns finished wait"); + } + + void t3() { + SC_REPORT_INFO(name(), "T3"); + for (int i = 0; i < 150000; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(100, sc_core::SC_MS); + snaptime(time1); + + SC_REPORT_INFO(name(), "T3 do work"); + int f = do_work(); + SC_REPORT_INFO(name(), "Work done"); + + SC_REPORT_INFO(name(), "T3 done waiting"); + } + SC_REPORT_INFO(name(), "T3 finished loop"); + } + + void t4() { + while (1) { + wait(send_txn); + + SC_REPORT_INFO(name(), "Send TXN"); + tlm::tlm_generic_payload trans; + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + + socket->b_transport(trans, delay); + SC_REPORT_INFO(name(), "done TXN"); + } + } + void m1() { + + SC_REPORT_INFO( + name(), + ("Got signal " + std::string(in.read() ? "True" : "False")).c_str()); + + send_txn.notify(10, sc_core::SC_MS); + } + +public: + SC_CTOR(my_parallel_module) { + SC_REPORT_INFO(name(), "Ading tasks t1,t2 and t3\n"); + SC_THREAD(t1); + SC_THREAD(t2); + SC_THREAD(t3); + SC_THREAD(t4); + + SC_METHOD(m1); + sensitive << in; + dont_initialize(); + + SC_REPORT_INFO(name(), "Constructor done"); + } + + void end_of_elaboration() { SC_REPORT_INFO(name(), "end_of_elaboration"); } + void start_of_simulation() { SC_REPORT_INFO(name(), "start_of_simulation"); } + sc_in in; + + tlm_utils::simple_initiator_socket socket; +}; + +SC_MODULE(My_normal_module) { + + tlm_utils::simple_target_socket socket; + + void t1() { SC_REPORT_INFO(name(), "T1"); } + void t2() { + SC_REPORT_INFO(name(), "T2"); + wait(1, sc_core::SC_NS); + + SC_REPORT_INFO(name(), "T2 1ns finished wait"); + } + + void t3() { + SC_REPORT_INFO(name(), "T3"); + for (int i = 0; i < 150000; i++) { + SC_REPORT_INFO(name(), "T3 waiting"); + wait(100, sc_core::SC_MS); + snaptime(time2); + + SC_REPORT_INFO(name(), "T3 do work"); + + int f = do_work(); + SC_REPORT_INFO(name(), "Work done"); + + SC_REPORT_INFO( + name(), ("Sending signal" + std::string((i & 0x1) ? "True" : "False")) + .c_str()); + // out.write(i & 0x1); + SC_REPORT_INFO(name(), "T3 done waiting"); + } + SC_REPORT_INFO(name(), "T3 finished loop"); + sc_stop(); + } + void b_transport(tlm::tlm_generic_payload & trans, sc_core::sc_time & delay) { + SC_REPORT_INFO(name(), "Got b_transport"); + } + SC_CTOR(My_normal_module) { + SC_THREAD(t1); + SC_THREAD(t2); + SC_THREAD(t3); + socket.register_b_transport(this, &My_normal_module::b_transport); + } + sc_out out; + + void end_of_elaboration() { SC_REPORT_INFO(name(), "end_of_elaboration"); } + void start_of_simulation() { SC_REPORT_INFO(name(), "start_of_simulation"); } +}; + +void report_handler(const sc_core::sc_report &rep, + const sc_core::sc_actions &actions) { +#if DEBUG + std::lock_guard lock(mutex); + auto now = std::chrono::high_resolution_clock::now(); + auto now_ms = std::chrono::duration_cast( + now.time_since_epoch()) + .count(); + + cout << now_ms % 10000 << " thread:" << std::this_thread::get_id() + << " simcontext:" << sc_core::sc_get_curr_simcontext() + << " time:" << sc_core::sc_time_stamp() << " " + << " : [" << rep.get_msg_type() << "] " << rep.get_msg() << std::endl; +#endif +} + +int sc_main(int argc, char *argv[]) { + + ::sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_DEBUG); + ::sc_core::sc_report_handler::set_handler(report_handler); + + tlm_utils::tlm_quantumkeeper::set_global_quantum( + sc_core::sc_time(200, sc_core::SC_MS)); + + My_normal_module mn("Normal"); + //my_parallel_module mp("Parallel"); + SC_ALLOW_CONCURRENCY(my_parallel_module, sc_core::sc_sync_policy_tlm_quantum) mp("Parallel"); + //SC_ALLOW_CONCURRENCY(my_parallel_module, sc_core::sc_sync_policy_in_sync) mp("Parallel"); + + sc_signal sig; + mn.out(sig); + mp.in(sig); + + mp.socket.bind(mn.socket); + + SC_REPORT_INFO("main", "before start"); + sc_start(); + std::cout << "Worst diff: " << worst << "\n"; + SC_REPORT_INFO("main", "finished"); + + return 0; +} \ No newline at end of file diff --git a/examples/sysc/risc_cpu/bios.h b/examples/sysc/risc_cpu/bios.h index 19a0bb056..f0fd6ca8e 100644 --- a/examples/sysc/risc_cpu/bios.h +++ b/examples/sysc/risc_cpu/bios.h @@ -82,6 +82,11 @@ struct bios : sc_module { } } + virtual ~bios() { + delete [] imemory; + delete [] itagmemory; + } + // Process functionality in member function below void entry(); }; diff --git a/examples/sysc/risc_cpu/dcache.h b/examples/sysc/risc_cpu/dcache.h index 93d56fe94..a71e79458 100644 --- a/examples/sysc/risc_cpu/dcache.h +++ b/examples/sysc/risc_cpu/dcache.h @@ -86,6 +86,12 @@ struct dcache : sc_module { } } + virtual ~dcache() { + delete [] dmemory; + delete [] dsmemory; + delete [] dtagmemory; + } + // Process functionality in member function below void entry(); }; diff --git a/examples/sysc/risc_cpu/directive.h b/examples/sysc/risc_cpu/directive.h index cf1f6ae9e..9b43f6bf2 100644 --- a/examples/sysc/risc_cpu/directive.h +++ b/examples/sysc/risc_cpu/directive.h @@ -37,7 +37,7 @@ //#define DEBUG true -#define TRACE false +//#define TRACE false #define PRINT_IFU true #define PRINT_ID true #define PRINT_PU false diff --git a/examples/sysc/risc_cpu/icache.h b/examples/sysc/risc_cpu/icache.h index 44e7ced9e..57a327140 100644 --- a/examples/sysc/risc_cpu/icache.h +++ b/examples/sysc/risc_cpu/icache.h @@ -85,6 +85,11 @@ struct icache : sc_module { } } + virtual ~icache() { + delete [] icmemory; + delete [] ictagmemory; + } + // Process functionality in member function below void entry(); }; diff --git a/examples/sysc/simple_bus/simple_bus_main.cpp b/examples/sysc/simple_bus/simple_bus_main.cpp index 05e4bac2e..8fa7d527b 100644 --- a/examples/sysc/simple_bus/simple_bus_main.cpp +++ b/examples/sysc/simple_bus/simple_bus_main.cpp @@ -44,5 +44,7 @@ int sc_main(int, char **) sc_start(10000, SC_NS); + fflush(stdout); + return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6cca4957..aeb14d5ea 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,14 +46,30 @@ cmake_minimum_required(VERSION 3.16...3.31) option(SYSTEMC_UNITY_BUILD "Enable unity build" OFF) +# Detect format string support: prefer std::format (C++20), fall back to fmt. +include(CheckCXXSourceCompiles) +set(CMAKE_REQUIRED_FLAGS "") +set(CMAKE_CXX_STANDARD 20) +check_cxx_source_compiles(" + #include + int main() { auto s = std::format(\"{}\", 42); return 0; } +" HAS_STD_FORMAT) +unset(CMAKE_CXX_STANDARD) + +if(NOT HAS_STD_FORMAT) + find_package(fmt QUIET) +endif() + function(add_systemc_library libName scBuildDefine) add_library(${libName} ${ARGN}) - target_compile_features( - ${libName} - PUBLIC - cxx_std_17) + target_compile_features(${libName} PUBLIC cxx_std_20) + + if(NOT HAS_STD_FORMAT AND fmt_FOUND) + target_link_libraries(${libName} PUBLIC fmt::fmt) + target_compile_definitions(${libName} PUBLIC FMT_SHARED) + endif() target_compile_definitions ( ${libName} @@ -204,6 +220,7 @@ set(SYSTEMC_CORE_SRC sysc/kernel/sc_ver.cpp sysc/kernel/sc_wait.cpp sysc/kernel/sc_wait_cthread.cpp + sysc/log/sc_log.cpp sysc/tracing/sc_trace.cpp sysc/tracing/sc_trace_file_base.cpp sysc/tracing/sc_vcd_trace.cpp @@ -333,6 +350,8 @@ set(SYSTEMC_CORE_SRC sysc/kernel/sc_ver.h sysc/kernel/sc_wait.h sysc/kernel/sc_wait_cthread.h + sysc/log/sc_log.h + sysc/log/sc_log_types.h sysc/tracing/sc_trace.h sysc/tracing/sc_tracing_ids.h sysc/utils/sc_hash.h @@ -470,10 +489,6 @@ endforeach() add_systemc_library(systemc SC_BUILD_OFF STATIC ${SYSTEMC_SC_MAIN_SRC} ) - add_library(systemc_if INTERFACE) - target_link_libraries(systemc_if INTERFACE systemc systemc-${SystemCLanguage_VERSION}) - - if (MSVC) # lib.exe should be available from MSVC command promt add_custom_command( @@ -500,9 +515,8 @@ endforeach() endif(MSVC) - set(SYSTEMC_TARGETS systemc_if systemc ${SYSTEMC_DLL_TARGET}) + set(SYSTEMC_TARGETS systemc ${SYSTEMC_DLL_TARGET}) - add_library(SystemC::systemc ALIAS systemc_if) else(BUILD_SHARED_LIBS AND (WIN32 OR CYGWIN)) add_systemc_library(systemc @@ -514,9 +528,9 @@ else(BUILD_SHARED_LIBS AND (WIN32 OR CYGWIN)) set(SYSTEMC_TARGETS systemc) - add_library(SystemC::systemc ALIAS systemc) endif(BUILD_SHARED_LIBS AND (WIN32 OR CYGWIN)) +add_library(SystemC::systemc ALIAS systemc) install(TARGETS ${SYSTEMC_TARGETS} EXPORT SystemCLanguageTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/src/sysc/communication/sc_prim_channel.cpp b/src/sysc/communication/sc_prim_channel.cpp index 88afac4b6..8bef64ff5 100644 --- a/src/sysc/communication/sc_prim_channel.cpp +++ b/src/sysc/communication/sc_prim_channel.cpp @@ -63,7 +63,6 @@ sc_prim_channel::sc_prim_channel( const char* name_ ) m_registry->insert( *this ); } - // destructor sc_prim_channel::~sc_prim_channel() @@ -194,6 +193,11 @@ class sc_prim_channel_registry::async_update_list m_pop_queue.clear(); } + // attach/detach_suspending and suspend() all touch m_suspending_channels + // and m_has_suspending_channels. By construction every caller is on the + // owning simcontext's thread (sync_window's step_helper and ctor, and + // sc_simcontext's main loop calling async_suspend), so no lock is + // needed. Do NOT call these from a foreign thread. void attach_suspending( sc_prim_channel& p ) { std::vector::iterator it = @@ -202,7 +206,6 @@ class sc_prim_channel_registry::async_update_list m_suspending_channels.push_back(&p); m_has_suspending_channels = true; } - // return releases the mutex } void detach_suspending( sc_prim_channel& p ) @@ -214,7 +217,6 @@ class sc_prim_channel_registry::async_update_list m_suspending_channels.pop_back(); m_has_suspending_channels = (m_suspending_channels.size() > 0); } - // return releases the mutex } async_update_list() : m_has_suspending_channels() {} diff --git a/src/sysc/communication/sc_prim_channel.h b/src/sysc/communication/sc_prim_channel.h index 917563709..6cafb2919 100644 --- a/src/sysc/communication/sc_prim_channel.h +++ b/src/sysc/communication/sc_prim_channel.h @@ -91,6 +91,14 @@ class SC_API sc_prim_channel void async_attach_suspending(); void async_detach_suspending(); + // Run fn() on this channel's owning simcontext's thread. If the caller + // is already on that thread, fn runs synchronously; otherwise the call + // is posted via the kernel's async runnable helper and returns + // immediately, with the callback firing in the owning sim's next + // update phase. Forwards to sc_simcontext::run_update_async, which is + // accessible here because sc_prim_channel is a friend of sc_simcontext. + inline void run_update_async( std::function fn ); + protected: // to avoid calling sc_get_curr_simcontext() @@ -361,6 +369,13 @@ sc_prim_channel::async_detach_suspending() m_registry->async_detach_suspending(*this); } +inline +void +sc_prim_channel::run_update_async( std::function fn ) +{ + simcontext()->run_update_async( std::move(fn) ); +} + // called during the update phase of a delta cycle (if requested) diff --git a/src/sysc/communication/sc_signal.h b/src/sysc/communication/sc_signal.h index f1504c345..11edacf52 100644 --- a/src/sysc/communication/sc_signal.h +++ b/src/sysc/communication/sc_signal.h @@ -288,6 +288,18 @@ inline void sc_signal_t::write( const T& value_ ) { + // Cross-simcontext write: m_new_val and request_update both touch + // state that belongs to the signal's owning sim, so route the whole + // write onto that sim's update phase via run_update_async. The + // foreign caller returns immediately; the write lands at the next + // update on the owner thread. Reader-side wake-up is already + // handled by the cross-context fan-out path on the value-changed + // event. + if ( sc_get_curr_simcontext() != this->simcontext() ) { + this->run_update_async( [this, value_]{ this->write(value_); } ); + return; + } + // first write per eval phase: m_new_val == m_cur_val bool value_changed = !( m_new_val == value_ ); if ( !policy_type::check_write(this, value_changed) ) diff --git a/src/sysc/datatypes/fx/sc_fxval.cpp b/src/sysc/datatypes/fx/sc_fxval.cpp index a687fe27c..6811858f0 100644 --- a/src/sysc/datatypes/fx/sc_fxval.cpp +++ b/src/sysc/datatypes/fx/sc_fxval.cpp @@ -484,7 +484,7 @@ const char* to_string( const scfx_ieee_double& id, sc_numrep numrep, int w_prefix, sc_fmt fmt, const scfx_params* params = 0 ) { - static scfx_string s; + thread_local scfx_string s; s.clear(); diff --git a/src/sysc/datatypes/fx/scfx_mant.cpp b/src/sysc/datatypes/fx/scfx_mant.cpp index 585c22f7d..346c6eb10 100644 --- a/src/sysc/datatypes/fx/scfx_mant.cpp +++ b/src/sysc/datatypes/fx/scfx_mant.cpp @@ -77,7 +77,7 @@ next_pow2_index( std::size_t size ) return index; } -static word_list* free_words[32] = { 0 }; +thread_local word_list* free_words[32] = { 0 }; word* scfx_mant::alloc_word( std::size_t size ) diff --git a/src/sysc/datatypes/fx/scfx_rep.cpp b/src/sysc/datatypes/fx/scfx_rep.cpp index 385cf091f..64307bdbe 100644 --- a/src/sysc/datatypes/fx/scfx_rep.cpp +++ b/src/sysc/datatypes/fx/scfx_rep.cpp @@ -81,7 +81,7 @@ namespace sc_dt // some utilities // ---------------------------------------------------------------------------- -static scfx_pow10 pow10_fx; +thread_local scfx_pow10 pow10_fx; static const int mantissa0_size = SCFX_IEEE_DOUBLE_M_SIZE - bits_in_int; @@ -373,7 +373,7 @@ union scfx_rep_node }; -static scfx_rep_node* list = 0; +thread_local scfx_rep_node* list = 0; void* @@ -1272,7 +1272,7 @@ const char* scfx_rep::to_string( sc_numrep numrep, int w_prefix, sc_fmt fmt, const scfx_params* params ) const { - static scfx_string s; + thread_local scfx_string s; s.clear(); diff --git a/src/sysc/datatypes/int/sc_bigint_inlines.h b/src/sysc/datatypes/int/sc_bigint_inlines.h index 97d35fd82..ecb2be324 100644 --- a/src/sysc/datatypes/int/sc_bigint_inlines.h +++ b/src/sysc/datatypes/int/sc_bigint_inlines.h @@ -475,7 +475,7 @@ sc_bigint::operator>>=(int v) vector_shift_right(nd, digit, v, (int)digit[nd-1]<0 ? DIGIT_MASK:0); - return *this; + return *this; } // right shift methods: @@ -492,31 +492,33 @@ sc_bigint::operator>>(int v) const // If we shift off the end return the sign bit. if ( 0 >= nb ) { - sc_signed result(1, false); - result.digit[0] = 0 > (int)digit[HOD] ? -1 : 0; - return result; + sc_signed result(W, false); + result = (int)digit[HOD] ? -1 : 0; + return result; } // Return a value that is the width of the shifted value: - sc_signed result(nb, false); if ( W < 33 ) { - result.digit[0] = (int)digit[0] >> v; + sc_signed result(W, false); + result.digit[0] = (int)digit[0] >> v; + return result; } else if ( W < 65 ) { + sc_signed result(W, false); int64 tmp = digit[DIV_CEIL(W)-1]; tmp = (tmp << 32) | digit[0]; - tmp = tmp >> v; - result.digit[0] = tmp; - if ( nb > 32 ) { - result.digit[1] = (tmp >>32); - } + tmp = tmp >> v; + result.digit[0] = tmp; + result.digit[1] = (tmp >>32); + return result; } else { - vector_extract(digit, result.digit, W-1, v); + int nd = DIV_CEIL(W); + sc_signed result(*this); + vector_shift_right(nd, result.digit, v, (int)result.digit[nd-1]<0 ? DIGIT_MASK:0); + return result; } - result.adjust_hod(); - return result; } // sc_bv and sc_lv constructors and assignments using an sc_bigint value: diff --git a/src/sysc/datatypes/int/sc_biguint.h b/src/sysc/datatypes/int/sc_biguint.h index 3cbb19e10..584ac0a03 100644 --- a/src/sysc/datatypes/int/sc_biguint.h +++ b/src/sysc/datatypes/int/sc_biguint.h @@ -319,7 +319,7 @@ class sc_biguint // unary operators: inline const sc_bigint operator - (); - inline const sc_bigint operator ~ (); + inline const sc_biguint operator ~ (); // assignment operators diff --git a/src/sysc/datatypes/int/sc_biguint_inlines.h b/src/sysc/datatypes/int/sc_biguint_inlines.h index 151e5a855..f31ecf47e 100644 --- a/src/sysc/datatypes/int/sc_biguint_inlines.h +++ b/src/sysc/datatypes/int/sc_biguint_inlines.h @@ -226,13 +226,14 @@ sc_biguint::operator - () } template -inline const sc_bigint +inline const sc_biguint sc_biguint::operator ~ () { - sc_bigint result; + sc_biguint result; for ( int digit_i = 0; digit_i <= HOD; ++digit_i ) { result.digit[digit_i] = ~digit[digit_i]; } + result.adjust_hod(); return result; } @@ -468,8 +469,8 @@ sc_biguint::operator>>(int v) const // If we shift off the end return a single bit 0. if ( nb <= 0 ) { - sc_unsigned result(1, true); - return result; + sc_unsigned result(nbits, true); + return result; } @@ -478,38 +479,38 @@ sc_biguint::operator>>(int v) const // Note: sc_biguint values have one extra bit on the top, so the comparison is down // one bit, e.g., W < 32, not W < 33. - sc_unsigned result(nb, false); if ( W < 32 ) { - result.digit[0] = digit[0] >> v; + sc_unsigned result(nbits, false); + result.digit[0] = digit[0] >> v; + return result; } else if ( W < 64 ) { - uint64 tmp = digit[DIV_CEIL(W)-1]; + sc_unsigned result(nbits, false); + uint64 tmp = digit[1]; tmp = (tmp << 32) | digit[0]; - tmp = tmp >> v; - result.digit[0] = tmp; - if ( result.nbits > 32 ) { - result.digit[1] = (tmp >> 32); - } + tmp = tmp >> v; + result.digit[0] = tmp; + result.digit[1] = (tmp >> 32); + return result; } else { - vector_extract(digit, result.digit, W-1, v); + sc_unsigned result(nbits, true); + vector_extract(digit, result.digit, nbits-1, v); + return result; } - result.adjust_hod(); - return result; } template const sc_biguint& sc_biguint::operator>>=(int v) { - if (v <= 0) return *this; int nd = DIV_CEIL(W+1); vector_shift_right(nd, digit, v, 0); - return *this; + return *this; } // sc_bv and sc_lv constructors and assignments using an sc_biguint value: diff --git a/src/sysc/datatypes/int/sc_int_base.h b/src/sysc/datatypes/int/sc_int_base.h index 7a1b9d6eb..ce2d98148 100644 --- a/src/sysc/datatypes/int/sc_int_base.h +++ b/src/sysc/datatypes/int/sc_int_base.h @@ -105,12 +105,6 @@ class sc_fxnum_fast; } // namespace sc_dt -// extern template instantiations -namespace sc_core { -SC_API_TEMPLATE_DECL_ sc_vpool; -SC_API_TEMPLATE_DECL_ sc_vpool; -} // namespace sc_core - namespace sc_dt { extern SC_API const uint_type mask_int[SC_INTWIDTH][SC_INTWIDTH]; @@ -752,8 +746,8 @@ class SC_API sc_int_base : public sc_value_base sc_int_bitref* temporary_bitref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_int_bit_ref_pool(9); + return sc_int_bit_ref_pool.allocate(); } @@ -767,8 +761,8 @@ class SC_API sc_int_base : public sc_value_base sc_int_subref* temporary_subref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_int_subref_pool(9); + return sc_int_subref_pool.allocate(); } diff --git a/src/sysc/datatypes/int/sc_signed.cpp b/src/sysc/datatypes/int/sc_signed.cpp index 229ddefcf..010012b9b 100644 --- a/src/sysc/datatypes/int/sc_signed.cpp +++ b/src/sysc/datatypes/int/sc_signed.cpp @@ -1079,13 +1079,6 @@ sc_signed_subref::scan( ::std::istream& is ) *this = s.c_str(); } -#if defined(SC_BIGINT_CONFIG_TEMPLATE_CLASS_HAS_NO_BASE_CLASS) -// Temporary values: - -sc_signed sc_signed::m_temporaries[SC_SIGNED_TEMPS_N]; -size_t sc_signed::m_temporaries_i = 0; -#endif // defined(SC_BIGINT_CONFIG_TEMPLATE_CLASS_HAS_NO_BASE_CLASS) - } // namespace sc_dt // End of file. diff --git a/src/sysc/datatypes/int/sc_signed.h b/src/sysc/datatypes/int/sc_signed.h index 71f19a022..23c1a83d6 100644 --- a/src/sysc/datatypes/int/sc_signed.h +++ b/src/sysc/datatypes/int/sc_signed.h @@ -667,8 +667,8 @@ class SC_API sc_signed : public sc_value_base sc_signed_bitref* temporary_bitref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_signed_bitref_pool(9); + return sc_signed_bitref_pool.allocate(); } sc_signed_bitref& operator [] ( int i ) @@ -729,14 +729,14 @@ class SC_API sc_signed : public sc_value_base sc_signed_subref* temporary_subref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_signed_subref_pool(9); + return sc_signed_subref_pool.allocate(); } sc_signed_subref& range( int i, int j ) { check_range( i, j ); - sc_signed_subref* result_p = temporary_subref(); + sc_signed_subref* result_p = temporary_subref(); result_p->initialize( this, i, j ); return *result_p; } @@ -839,6 +839,7 @@ class SC_API sc_signed : public sc_value_base digit[digit_num] |= one_and_zeros(bit_num); digit[digit_num] = SC_MASK_DIGIT(digit[digit_num]); + adjust_hod(); } @@ -855,6 +856,7 @@ class SC_API sc_signed : public sc_value_base digit[digit_num] &= ~(one_and_zeros(bit_num)); digit[digit_num] = SC_MASK_DIGIT(digit[digit_num]); + adjust_hod(); } @@ -1080,31 +1082,33 @@ class SC_API sc_signed : public sc_value_base // If we shift off the end return the sign bit. if ( 0 >= nb ) { - sc_signed result(1, false); - result.digit[0] = 0 > (int)digit[ndigits-1] ? -1 : 0; + sc_signed result(nbits, false); + result = 0 > (int)digit[ndigits-1] ? -1 : 0; return result; } // Return a value that is the width of the shifted value: - sc_signed result(nb, false); if ( nbits < 33 ) { + sc_signed result(nbits, false); result.digit[0] = (int)digit[0] >> v; + return result; } else if ( nbits < 65 ) { + sc_signed result(nbits, false); int64 tmp = digit[1]; tmp = (tmp << 32) | digit[0]; tmp = tmp >> v; result.digit[0] = (sc_digit)tmp; - if ( nb > 32 ) { - result.digit[1] = (tmp >>32); - } + result.digit[1] = (tmp >>32); + return result; } else { - vector_extract(digit, result.digit, nbits-1, v); + int nd = DIV_CEIL(nbits); + sc_signed result(*this); + vector_shift_right(nd, result.digit, v, (int)result.digit[nd-1]<0 ? DIGIT_MASK:0); + return result; } - result.adjust_hod(); - return result; } sc_signed operator>>(const sc_unsigned& v) const; @@ -1165,8 +1169,6 @@ class SC_API sc_signed : public sc_value_base #define SC_SIGNED_TEMPS_N (1 << 15) // SC_SIGNED_TEMPS_N must be a power of 2. public: // Temporary object support: - static sc_signed m_temporaries[SC_SIGNED_TEMPS_N]; - static size_t m_temporaries_i; // +-------------------------------------------------------------------------------------------- // |"allocate_temporary" @@ -1182,9 +1184,8 @@ class SC_API sc_signed : public sc_value_base // +-------------------------------------------------------------------------------------------- static inline sc_signed& allocate_temporary( int nb, sc_digit* digits_p ) { - - sc_signed* result_p = &m_temporaries[m_temporaries_i]; - m_temporaries_i = (m_temporaries_i + 1) & (SC_SIGNED_TEMPS_N-1); + thread_local sc_core::sc_vpool sc_signed_temporary_pool(SC_SIGNED_TEMPS_N); + sc_signed* result_p = sc_signed_temporary_pool.allocate(); result_p->digit = digits_p; result_p->nbits = num_bits(nb); result_p->ndigits = DIV_CEIL(result_p->nbits); diff --git a/src/sysc/datatypes/int/sc_signed_ops.h b/src/sysc/datatypes/int/sc_signed_ops.h index faa40e8f0..b4bd0780b 100644 --- a/src/sysc/datatypes/int/sc_signed_ops.h +++ b/src/sysc/datatypes/int/sc_signed_ops.h @@ -1531,19 +1531,17 @@ operator~(const sc_signed& u) } inline -sc_signed +sc_unsigned operator~(const sc_unsigned& u) { - sc_signed result( u.length()+1 ); + sc_unsigned result( u.length() ); sc_digit* result_p = result.get_digits(); sc_digit* source_p = u.get_digits(); int hod = u.get_hod(); for ( int digit_i = 0; digit_i <= hod; ++digit_i ) { result_p[digit_i] = ~source_p[digit_i]; } - if ( result.get_hod() > hod ) { - result_p[hod] = (sc_digit)-1; - } + result.adjust_hod(); return result; } diff --git a/src/sysc/datatypes/int/sc_uint_base.h b/src/sysc/datatypes/int/sc_uint_base.h index 3cf26b5e6..c3ee44b16 100644 --- a/src/sysc/datatypes/int/sc_uint_base.h +++ b/src/sysc/datatypes/int/sc_uint_base.h @@ -720,8 +720,8 @@ class SC_API sc_uint_base : public sc_value_base sc_uint_bitref* temporary_bitref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_uint_bitref_pool(9); + return sc_uint_bitref_pool.allocate(); } @@ -735,8 +735,8 @@ class SC_API sc_uint_base : public sc_value_base sc_uint_subref* temporary_subref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_uint_subref_pool(9); + return sc_uint_subref_pool.allocate(); } diff --git a/src/sysc/datatypes/int/sc_unsigned.cpp b/src/sysc/datatypes/int/sc_unsigned.cpp index 08ee64525..d2a50e1fe 100644 --- a/src/sysc/datatypes/int/sc_unsigned.cpp +++ b/src/sysc/datatypes/int/sc_unsigned.cpp @@ -1058,13 +1058,6 @@ sc_unsigned_subref::scan( ::std::istream& is ) *this = s.c_str(); } -#if defined(SC_BIGINT_CONFIG_TEMPLATE_CLASS_HAS_NO_BASE_CLASS) -// Temporary values: - -sc_unsigned sc_unsigned::m_temporaries[SC_UNSIGNED_TEMPS_N]; -size_t sc_unsigned::m_temporaries_i = 0; -#endif // defined(SC_BIGINT_CONFIG_TEMPLATE_CLASS_HAS_NO_BASE_CLASS) - } // namespace sc_dt diff --git a/src/sysc/datatypes/int/sc_unsigned.h b/src/sysc/datatypes/int/sc_unsigned.h index 41606fc80..f1e0b8039 100644 --- a/src/sysc/datatypes/int/sc_unsigned.h +++ b/src/sysc/datatypes/int/sc_unsigned.h @@ -138,7 +138,7 @@ class sc_fxnum_fast; // Bitwise NOT operator (unary). - SC_API sc_signed operator ~ (const sc_unsigned& u); + SC_API sc_unsigned operator ~ (const sc_unsigned& u); // ---------------------------------------------------------------------------- // CLASS : sc_unsigned_bitref_r @@ -662,8 +662,8 @@ class SC_API sc_unsigned : public sc_value_base sc_unsigned_bitref* temporary_bitref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_unsigned_bitref_pool(9); + return sc_unsigned_bitref_pool.allocate(); } sc_unsigned_bitref& operator [] ( int i ) @@ -724,8 +724,8 @@ class SC_API sc_unsigned : public sc_value_base sc_unsigned_subref* temporary_subref() const { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_unsigned_subref_pool(9); + return sc_unsigned_subref_pool.allocate(); } sc_unsigned_subref& range( int i, int j ) @@ -764,8 +764,8 @@ class SC_API sc_unsigned : public sc_value_base static sc_unsigned* temporary() { - static sc_core::sc_vpool pool(9); - return pool.allocate(); + thread_local sc_core::sc_vpool sc_unsigned_temporary_pool(9); + return sc_unsigned_temporary_pool.allocate(); } // explicit conversions @@ -1073,30 +1073,31 @@ class SC_API sc_unsigned : public sc_value_base // If we shift off the end return the sign bit. if ( 0 >= nb ) { - sc_unsigned result(1, true); + sc_unsigned result(nbits, true); return result; } // Return a value that is the width of the shifted value: - sc_unsigned result(nb, false); if ( nbits < 33 ) { + sc_unsigned result(nbits, false); result.digit[0] = (int)digit[0] >> v; + return result; } else if ( nbits < 65 ) { + sc_unsigned result(nbits, false); int64 tmp = digit[1]; tmp = (tmp << 32) | digit[0]; tmp = tmp >> v; result.digit[0] = (sc_digit)tmp; - if ( nb > 32 ) { - result.digit[1] = (tmp >>32); - } + result.digit[1] = (tmp>>32); + return result; } else { + sc_unsigned result(nbits, true); vector_extract(digit, result.digit, nbits-1, v); + return result; } - result.adjust_hod(); - return result; } sc_unsigned operator>>(const sc_signed& v) const; @@ -1113,7 +1114,7 @@ class SC_API sc_unsigned : public sc_value_base if (v <= 0) return *this; vector_shift_right(ndigits, digit, v, 0); - return *this; + return *this; } const sc_unsigned& operator>>=(const sc_signed& v); const sc_unsigned& operator>>=(const sc_unsigned& v) { return operator>>=(v.to_int()); } @@ -1130,7 +1131,7 @@ class SC_API sc_unsigned : public sc_value_base // Bitwise NOT operator (unary). - friend SC_API sc_signed operator ~ (const sc_unsigned& u); + friend SC_API sc_unsigned operator ~ (const sc_unsigned& u); protected: @@ -1152,11 +1153,6 @@ class SC_API sc_unsigned : public sc_value_base public: // Temporary object support: - #define SC_UNSIGNED_TEMPS_N (1 << 15) // SC_UNSIGNED_TEMPS_N must be a power of 2. - - static sc_unsigned m_temporaries[SC_UNSIGNED_TEMPS_N]; - static size_t m_temporaries_i; - // +-------------------------------------------------------------------------------------------- // |"allocate_temporary" // | @@ -1171,8 +1167,7 @@ class SC_API sc_unsigned : public sc_value_base // +-------------------------------------------------------------------------------------------- static inline sc_unsigned& allocate_temporary( int nb, sc_digit* digits_p ) { - sc_unsigned* result_p = &m_temporaries[m_temporaries_i]; - m_temporaries_i = (m_temporaries_i + 1) & (SC_UNSIGNED_TEMPS_N-1); + sc_unsigned* result_p = temporary(); result_p->digit = digits_p; result_p->nbits = num_bits(nb); result_p->ndigits = DIV_CEIL(result_p->nbits); diff --git a/src/sysc/datatypes/misc/sc_concatref.h b/src/sysc/datatypes/misc/sc_concatref.h index 7ef4923c2..2a3b05866 100644 --- a/src/sysc/datatypes/misc/sc_concatref.h +++ b/src/sysc/datatypes/misc/sc_concatref.h @@ -621,8 +621,8 @@ class SC_API sc_concat_bool : public sc_value_base static inline sc_concat_bool* allocate( bool v ) { - static sc_core::sc_vpool pool(9); - sc_concat_bool* result_p = pool.allocate(); + thread_local sc_core::sc_vpool sc_concat_bool_pool(9); + sc_concat_bool* result_p = sc_concat_bool_pool.allocate(); result_p->m_value = v; return result_p; } @@ -749,8 +749,8 @@ SC_CONCAT_BOOL_OP(<) static inline sc_dt::sc_concatref* temporary_concatref() { - static sc_core::sc_vpool pool(9); - sc_dt::sc_concatref* result_p = pool.allocate(); + thread_local sc_core::sc_vpool sc_concatref_pool(9); + sc_dt::sc_concatref* result_p = sc_concatref_pool.allocate(); return result_p; } diff --git a/src/sysc/kernel/sc_event.cpp b/src/sysc/kernel/sc_event.cpp index 74b6cae3f..a6135a307 100644 --- a/src/sysc/kernel/sc_event.cpp +++ b/src/sysc/kernel/sc_event.cpp @@ -30,6 +30,7 @@ #include #include "sysc/kernel/sc_event.h" +#include "sc_simcontext.h" #include "sysc/kernel/sc_kernel_ids.h" #include "sysc/kernel/sc_stage_callback_registry.h" #include "sysc/kernel/sc_process.h" @@ -61,6 +62,10 @@ sc_event::basename() const void sc_event::cancel() { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this]{ cancel(); } ); + return; + } // cancel a delta or timed notification switch( m_notify_type ) { case DELTA: { @@ -86,6 +91,10 @@ sc_event::cancel() void sc_event::notify() { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this]{ notify(); } ); + return; + } // immediate notification if( !m_simc->evaluation_phase() ) // coming from @@ -103,6 +112,10 @@ sc_event::notify() void sc_event::notify( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this, t]{ notify(t); } ); + return; + } if( m_notify_type == DELTA ) { return; } @@ -168,6 +181,10 @@ static void sc_warn_notify_delayed() void sc_event::notify_delayed() { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this]{ notify_delayed(); } ); + return; + } sc_warn_notify_delayed(); if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); @@ -180,6 +197,11 @@ sc_event::notify_delayed() void sc_event::notify_delayed( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this, t]{ notify_delayed(t); } ); + return; + } + sc_warn_notify_delayed(); if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); @@ -540,7 +562,7 @@ union sc_event_timed_u char dummy[sizeof( sc_event_timed )]; }; -static +thread_local static sc_event_timed_u* free_list = 0; void* diff --git a/src/sysc/kernel/sc_event.h b/src/sysc/kernel/sc_event.h index d7884f6b3..2852702c9 100644 --- a/src/sysc/kernel/sc_event.h +++ b/src/sysc/kernel/sc_event.h @@ -424,6 +424,10 @@ inline void sc_event::notify_internal( const sc_time& t ) { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this, t]{ notify_internal(t); } ); + return; + } if( t == SC_ZERO_TIME ) { // add this event to the delta events set m_delta_event_index = m_simc->add_delta_event( this ); @@ -441,6 +445,10 @@ inline void sc_event::notify_next_delta() { + if (m_simc != sc_get_curr_simcontext()) { + m_simc->run_update_async( [this]{ notify_next_delta(); } ); + return; + } if( m_notify_type != NONE ) { SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 ); } diff --git a/src/sysc/kernel/sc_kernel_ids.h b/src/sysc/kernel/sc_kernel_ids.h index 1ed1ea94d..93b5c52dc 100644 --- a/src/sysc/kernel/sc_kernel_ids.h +++ b/src/sysc/kernel/sc_kernel_ids.h @@ -208,6 +208,8 @@ SC_DEFINE_MESSAGE(SC_ID_UNSUSPENDABLE_NOTHREAD_ , 577, "unsuspendable/suspendable only valid inside a process" ) SC_DEFINE_MESSAGE(SC_ID_UNBALANCED_UNSUSPENDALL_ , 578, "Unmatched unsuspendall/suspendall" ) +SC_DEFINE_MESSAGE(SC_LOG_OVERWRITE_VERBOSITY_FN_, 579, + "Verbosity function already set - ignoring subsequent calls to set_log_verbosity_fn" ) /***************************************************************************** diff --git a/src/sysc/kernel/sc_method_process.cpp b/src/sysc/kernel/sc_method_process.cpp index 9b868b372..bc257be4a 100644 --- a/src/sysc/kernel/sc_method_process.cpp +++ b/src/sysc/kernel/sc_method_process.cpp @@ -195,6 +195,11 @@ void sc_method_process::disable_process( void sc_method_process::enable_process( sc_descendant_inclusion_info descendants ) { + // m_state read-modify-write must happen on the owning sim's thread. + if ( simcontext() != sc_get_curr_simcontext() ) { + simcontext()->run_update_async( [this, descendants]{ enable_process(descendants); } ); + return; + } // IF NEEDED PROPOGATE THE RESUME REQUEST THROUGH OUR DESCENDANTS: @@ -426,6 +431,11 @@ void sc_method_process::suspend_process( void sc_method_process::resume_process( sc_descendant_inclusion_info descendants ) { + // m_state read-modify-write must happen on the owning sim's thread. + if ( simcontext() != sc_get_curr_simcontext() ) { + simcontext()->run_update_async( [this, descendants]{ resume_process(descendants); } ); + return; + } // IF NEEDED PROPOGATE THE RESUME REQUEST THROUGH OUR DESCENDANTS: @@ -771,7 +781,7 @@ bool sc_method_process::trigger_dynamic( sc_event* e ) } else { - simcontext()->push_runnable_method(this); + simcontext()->push_runnable_method_async(this); } return true; diff --git a/src/sysc/kernel/sc_method_process.h b/src/sysc/kernel/sc_method_process.h index b7b0a200e..b177e898f 100644 --- a/src/sysc/kernel/sc_method_process.h +++ b/src/sysc/kernel/sc_method_process.h @@ -362,7 +362,7 @@ sc_method_process::trigger_static() } else { - simcontext()->push_runnable_method(this); + simcontext()->push_runnable_method_async(this); } } diff --git a/src/sysc/kernel/sc_module.h b/src/sysc/kernel/sc_module.h index eb8c4aae8..bcfca4729 100644 --- a/src/sysc/kernel/sc_module.h +++ b/src/sysc/kernel/sc_module.h @@ -41,6 +41,7 @@ #include "sysc/kernel/sc_wait.h" #include "sysc/kernel/sc_wait_cthread.h" #include "sysc/utils/sc_list.h" +#include "sysc/log/sc_log.h" #include // std::remove_reference @@ -88,6 +89,8 @@ class SC_API sc_module friend class sc_process_b; friend class sc_simcontext; friend class sc_initializer_function; +protected: + SC_LOG_HANDLE(); public: sc_simcontext* sc_get_curr_simcontext() diff --git a/src/sysc/kernel/sc_name_gen.cpp b/src/sysc/kernel/sc_name_gen.cpp index 72380bad3..b479b4ef8 100644 --- a/src/sysc/kernel/sc_name_gen.cpp +++ b/src/sysc/kernel/sc_name_gen.cpp @@ -45,6 +45,7 @@ sc_name_gen::sc_name_gen() : m_unique_name_map(), m_unique_name() sc_name_gen::~sc_name_gen() { + std::unique_lock lock(m_mutex); sc_strhash::iterator it( m_unique_name_map ); for( ; ! it.empty(); it ++ ) { delete it.contents(); @@ -58,6 +59,7 @@ sc_name_gen::~sc_name_gen() const char* sc_name_gen::gen_unique_name( const char* basename_, bool preserve_first ) { + std::unique_lock lock(m_mutex); if( basename_ == 0 || *basename_ == 0 ) { SC_REPORT_ERROR( SC_ID_GEN_UNIQUE_NAME_, 0 ); basename_ = "unnamed"; // usually not reached diff --git a/src/sysc/kernel/sc_name_gen.h b/src/sysc/kernel/sc_name_gen.h index 2e5c7622a..8928a0781 100644 --- a/src/sysc/kernel/sc_name_gen.h +++ b/src/sysc/kernel/sc_name_gen.h @@ -30,6 +30,7 @@ #ifndef SC_NAME_GEN #define SC_NAME_GEN +#include #include #include "sysc/utils/sc_hash.h" @@ -52,7 +53,7 @@ class sc_name_gen bool preserve_first = false ); private: - + std::mutex m_mutex; sc_strhash m_unique_name_map; std::string m_unique_name; diff --git a/src/sysc/kernel/sc_object_manager.cpp b/src/sysc/kernel/sc_object_manager.cpp index abcb69b36..a3cd6f43c 100644 --- a/src/sysc/kernel/sc_object_manager.cpp +++ b/src/sysc/kernel/sc_object_manager.cpp @@ -55,13 +55,15 @@ namespace sc_core { // Manager of objects. // ---------------------------------------------------------------------------- +// Per-thread construction-context storage. See comment in the header. +thread_local sc_module_name* sc_object_manager::m_module_name_stack = 0; +thread_local sc_object_manager::object_vector_t sc_object_manager::m_object_stack; + sc_object_manager::sc_object_manager() : m_event_it(), m_event_walk_ok(0), m_instance_table(), - m_module_name_stack(0), m_object_it(), - m_object_stack(), m_object_walk_ok() { } @@ -98,7 +100,9 @@ sc_object_manager::~sc_object_manager() // | Result is an std::string containing the name. // +---------------------------------------------------------------------------- std::string sc_object_manager::create_name(const char* leaf_name) -{ +{ + std::lock_guard lock(m_mutex); + bool clash; // true if path name exists in obj table std::string leafname_string; // string containing the leaf name. std::string parentname_string; // parent path name @@ -169,6 +173,7 @@ std::string sc_object_manager::create_name(const char* leaf_name) bool sc_object_manager::name_exists(const std::string& name) { + std::lock_guard lock(m_mutex); instance_table_t::const_iterator it = m_instance_table.find(name); return (it != m_instance_table.end()) && (it->second.m_name_origin != SC_NAME_NONE); @@ -187,6 +192,7 @@ sc_object_manager::name_exists(const std::string& name) const char* sc_object_manager::get_name(const std::string& name) { + std::lock_guard lock(m_mutex); instance_table_t::iterator it = m_instance_table.find(name); if (it != m_instance_table.end() && it->second.m_name_origin != SC_NAME_NONE) { @@ -209,6 +215,7 @@ sc_object_manager::get_name(const std::string& name) sc_event* sc_object_manager::find_event(const char* name) { + std::lock_guard lock(m_mutex); instance_table_t::iterator it; it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -233,6 +240,7 @@ sc_object_manager::find_event(const char* name) sc_object* sc_object_manager::find_object(const char* name) { + std::lock_guard lock(m_mutex); instance_table_t::iterator it; it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -255,12 +263,13 @@ sc_object_manager::find_object(const char* name) sc_object* sc_object_manager::first_object() { + std::lock_guard lock(m_mutex); sc_object* result_p; // result to return. m_object_walk_ok = true; result_p = NULL; - for ( m_object_it = m_instance_table.begin(); - m_object_it != m_instance_table.end(); + for ( m_object_it = m_instance_table.begin(); + m_object_it != m_instance_table.end(); m_object_it++ ) { if(m_object_it->second.m_name_origin == SC_NAME_OBJECT) { @@ -337,6 +346,8 @@ sc_object_manager::hierarchy_size() bool sc_object_manager::insert_external_name(const std::string& name) { + std::lock_guard lock(m_mutex); + if(!name_exists(name)) { m_instance_table[name].m_element_p = NULL; m_instance_table[name].m_name_origin = SC_NAME_EXTERNAL; @@ -368,6 +379,8 @@ sc_object_manager::insert_external_name(const std::string& name) void sc_object_manager::insert_event(const std::string& name, sc_event* event_p) { + std::lock_guard lock(m_mutex); + m_instance_table[name].m_element_p = static_cast(event_p); m_instance_table[name].m_name_origin = SC_NAME_EVENT; } @@ -385,6 +398,8 @@ sc_object_manager::insert_event(const std::string& name, sc_event* event_p) void sc_object_manager::insert_object(const std::string& name, sc_object* object_p) { + std::lock_guard lock(m_mutex); + m_instance_table[name].m_element_p = static_cast(object_p); m_instance_table[name].m_name_origin = SC_NAME_OBJECT; } @@ -397,6 +412,7 @@ sc_object_manager::insert_object(const std::string& name, sc_object* object_p) sc_object* sc_object_manager::next_object() { + std::lock_guard lock(m_mutex); sc_object* result_p; // result to return. sc_assert( m_object_walk_ok ); @@ -479,6 +495,8 @@ sc_object_manager::top_of_module_name_stack_name() const void sc_object_manager::remove_event(const std::string& name) { + std::lock_guard lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -501,6 +519,8 @@ sc_object_manager::remove_event(const std::string& name) void sc_object_manager::remove_object(const std::string& name) { + std::lock_guard lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() @@ -523,6 +543,8 @@ sc_object_manager::remove_object(const std::string& name) bool sc_object_manager::remove_external_name(const std::string& name) { + std::lock_guard lock(m_mutex); + instance_table_t::iterator it; // instance table iterator. it = m_instance_table.find(name); if(it != m_instance_table.end() diff --git a/src/sysc/kernel/sc_object_manager.h b/src/sysc/kernel/sc_object_manager.h index fabc38d42..9fc83533a 100644 --- a/src/sysc/kernel/sc_object_manager.h +++ b/src/sysc/kernel/sc_object_manager.h @@ -31,6 +31,7 @@ #define SC_OBJECT_MANAGER_H #include +#include #include namespace sc_core { @@ -109,9 +110,17 @@ class sc_object_manager instance_table_t::iterator m_event_it; // event instance iterator. bool m_event_walk_ok; // true if can walk events. instance_table_t m_instance_table; // table of instances. - sc_module_name* m_module_name_stack; // sc_module_name stack. + // m_module_name_stack and m_object_stack are per-thread construction + // context: each thread that constructs sc_objects has its own nesting + // of modules and of sc_module_names. Making them thread_local avoids + // cross-thread interference when multiple sibling simcontexts (running + // on their own std::threads) share this object_manager. + static thread_local sc_module_name* m_module_name_stack; // sc_module_name stack (per thread). + // Recursive because public methods that take the lock (e.g. create_name) + // call back into other public methods (name_exists) that also take the lock. + mutable std::recursive_mutex m_mutex; // Mutex to ensure thread safety. instance_table_t::iterator m_object_it; // object instance iterator. - object_vector_t m_object_stack; // sc_object stack. + static thread_local object_vector_t m_object_stack; // sc_object stack (per thread). bool m_object_walk_ok; // true if can walk objects. }; diff --git a/src/sysc/kernel/sc_process.cpp b/src/sysc/kernel/sc_process.cpp index 54d2c646d..75cc63a1c 100644 --- a/src/sysc/kernel/sc_process.cpp +++ b/src/sysc/kernel/sc_process.cpp @@ -47,13 +47,16 @@ namespace sc_core { // Note the special name for 'non_event' - this makes sure it does not // appear as a named event. +// These two are immutable empty fallbacks returned by sc_process_handle:: +// get_child_events() / get_child_objects() when the handle is empty. +// Read-only after default construction, so no thread_local is needed. std::vector sc_process_handle::empty_event_vector; std::vector sc_process_handle::empty_object_vector; sc_event& sc_process_handle::non_event() { return sc_get_curr_simcontext()->null_event(); } // Last process that was created: -sc_process_b* sc_process_b::m_last_created_process_p = 0; +thread_local sc_process_b* sc_process_b::m_last_created_process_p = 0; //------------------------------------------------------------------------------ //"sc_process_b::add_static_event" diff --git a/src/sysc/kernel/sc_process.h b/src/sysc/kernel/sc_process.h index 87165d2ff..0d3decbb2 100644 --- a/src/sysc/kernel/sc_process.h +++ b/src/sysc/kernel/sc_process.h @@ -214,6 +214,7 @@ class SC_API sc_process_b : public sc_object_host { friend class sc_object; friend class sc_port_base; friend class sc_runnable; + friend class sc_async_runnable_helper; friend class sc_sensitive; friend class sc_sensitive_pos; friend class sc_sensitive_neg; @@ -401,7 +402,7 @@ class SC_API sc_process_b : public sc_object_host { // requesting global suspension. protected: - static sc_process_b* m_last_created_process_p; // Last process created. + thread_local static sc_process_b* m_last_created_process_p; // Last process created. }; diff --git a/src/sysc/kernel/sc_process_handle.h b/src/sysc/kernel/sc_process_handle.h index 4b5657c5c..b9fa2d84f 100644 --- a/src/sysc/kernel/sc_process_handle.h +++ b/src/sysc/kernel/sc_process_handle.h @@ -147,8 +147,10 @@ class SC_API sc_process_handle { sc_process_b* m_target_p; // Target for this object instance. protected: - static std::vector empty_event_vector; // If m_target_p == 0. - static std::vector empty_object_vector; // If m_target_p == 0. + // Immutable empty fallbacks returned when m_target_p == 0. + // Read-only after default construction, so no thread_local needed. + static std::vector empty_event_vector; + static std::vector empty_object_vector; static sc_event& non_event(); // If m_target_p == 0. }; diff --git a/src/sysc/kernel/sc_runnable_int.h b/src/sysc/kernel/sc_runnable_int.h index 06246a84b..b82da9c84 100644 --- a/src/sysc/kernel/sc_runnable_int.h +++ b/src/sysc/kernel/sc_runnable_int.h @@ -36,6 +36,8 @@ #include "sysc/kernel/sc_runnable.h" #include "sysc/kernel/sc_method_process.h" #include "sysc/kernel/sc_thread_process.h" +#include "sysc/kernel/sc_simcontext.h" +#include "sysc/kernel/sc_object_manager.h" // DEBUGGING MACROS: // @@ -145,24 +147,63 @@ inline void sc_runnable::execute_thread_next( sc_thread_handle thread_h ) //------------------------------------------------------------------------------ inline void sc_runnable::init() { - if ( !m_methods_push_head ) + // These list-head sentinels are kernel-internal dummies, one pair + // per sc_runnable (i.e. one pair per sc_simcontext). When two + // simcontexts share an sc_object_manager (the parallel-sim case), + // a hardcoded name like "methods_push_head" would collide between + // them; the second thread to register would race the first on the + // shared instance_table. + // + // To give each simcontext's sentinels a distinct and readable name, + // we temporarily push the simcontext's first top-level module onto + // the hierarchy so the sentinel is constructed as its child. The + // sentinel's full name then naturally becomes + // ".methods_push_head" + // e.g. "prod.methods_push_head" in the child simcontext of an + // SC_ALLOW_CONCURRENCY(prod, ...) wrapper. detach() immediately + // after construction removes it from the parent's child list and + // the shared name table; only the already-unique full name lives on. + // + // Called from prepare_to_simulate, so sc_main has finished and the + // simcontext's child list is already populated. + if ( !m_methods_push_head || !m_threads_push_head ) { - m_methods_push_head = new sc_method_process("methods_push_head", true, - sc_entry_func(), 0, 0); - m_methods_push_head->dont_initialize(true); - m_methods_push_head->detach(); + sc_simcontext* sim = sc_get_curr_simcontext(); + sc_object_host* parent = sim->first_top_level_host(); + + // Push `parent` on the hierarchy stack so the sentinels' + // sc_object_init picks it up via active_object() as their + // parent. Their full names then naturally become e.g. + // "prod.methods_push_head". Pop again before detach() runs + // (detach unhooks them from parent's child list and the + // shared name table; only the unique full name lives on). + sc_object_manager* om = sim->get_object_manager(); + if ( parent ) om->hierarchy_push( parent ); + + if ( !m_methods_push_head ) + { + m_methods_push_head = new sc_method_process("methods_push_head", + true, sc_entry_func(), + 0, 0); + m_methods_push_head->dont_initialize(true); + m_methods_push_head->detach(); + } + if ( !m_threads_push_head ) + { + m_threads_push_head = new sc_thread_process("threads_push_head", + true, sc_entry_func(), + 0, 0); + m_threads_push_head->dont_initialize(true); + m_threads_push_head->detach(); + } + + if ( parent ) om->hierarchy_pop(); } + m_methods_pop = SC_NO_METHODS; m_methods_push_tail = m_methods_push_head; m_methods_push_head->set_next_runnable(SC_NO_METHODS); - if ( !m_threads_push_head ) - { - m_threads_push_head = new sc_thread_process("threads_push_head", true, - sc_entry_func(), 0, 0); - m_threads_push_head->dont_initialize(true); - m_threads_push_head->detach(); - } m_threads_pop = SC_NO_THREADS; m_threads_push_head->set_next_runnable(SC_NO_THREADS); m_threads_push_tail = m_threads_push_head; diff --git a/src/sysc/kernel/sc_simcontext.cpp b/src/sysc/kernel/sc_simcontext.cpp index 065dc6bdb..a2a962aee 100644 --- a/src/sysc/kernel/sc_simcontext.cpp +++ b/src/sysc/kernel/sc_simcontext.cpp @@ -62,6 +62,7 @@ #include #include #include +#include // DEBUGGING MACROS: // @@ -294,6 +295,38 @@ class sc_invoke_method : public sc_module std::vector m_invokers; // list of invoking threads. }; +// ---------------------------------------------------------------------------- +// CLASS : sc_async_runnable_helper +// +// Drain queued foreign runnable-push requests onto the local runnable +// lists during this simcontext's update phase. +// ---------------------------------------------------------------------------- + +void +sc_async_runnable_helper::update() +{ + std::vector ms; + std::vector ts; + std::vector> cbs; + { + std::lock_guard lg( m_mutex ); + ms.swap( m_pending_methods ); + ts.swap( m_pending_threads ); + cbs.swap( m_pending_callbacks ); + } + // Callbacks first: they may themselves enqueue runnables that we + // then process locally below. + for ( auto& cb : cbs ) + cb(); + sc_simcontext* simc = simcontext(); + for ( auto* m : ms ) + if ( !m->is_runnable() ) + simc->push_runnable_method( m ); + for ( auto* t : ts ) + if ( !t->is_runnable() ) + simc->push_runnable_thread( t ); +} + // ---------------------------------------------------------------------------- // CLASS : sc_simcontext // @@ -306,17 +339,35 @@ sc_simcontext::init() // ALLOCATE VARIOUS MANAGERS AND REGISTRIES: - m_object_manager = new sc_object_manager; + if (sc_curr_simcontext && this!=sc_curr_simcontext) { // Requesting a 'parallel' simcontext + m_object_manager = sc_curr_simcontext->get_object_manager(); // share with parent + m_name_gen = sc_curr_simcontext->m_name_gen; + dynamic_log_verbosity = sc_curr_simcontext->dynamic_log_verbosity; + m_parent_context = sc_curr_simcontext; + } else { + m_object_manager = new sc_object_manager; + m_name_gen = new sc_name_gen; + m_parent_context=nullptr; + } m_module_registry = new sc_module_registry( *this ); m_port_registry = new sc_port_registry( *this ); m_export_registry = new sc_export_registry( *this ); m_prim_channel_registry = new sc_prim_channel_registry( *this ); m_stage_cb_registry = new sc_stage_callback_registry( *this ); m_stub_registry = new sc_stub_registry( *this ); - m_name_gen = new sc_name_gen; m_process_table = new sc_process_table; m_current_writer = 0; + // Construct the async runnable helper in *this* simcontext's registry. + // sc_prim_channel's base sc_object captures sc_curr_simcontext, which + // during a child sim's init() still points at the parent — so swap. + { + sc_simcontext* saved = sc_curr_simcontext; + sc_curr_simcontext = this; + m_async_runnable_helper = new sc_async_runnable_helper(); + sc_curr_simcontext = saved; + } + // CHECK FOR ENVIRONMENT VARIABLES THAT MODIFY SIMULATOR EXECUTION: @@ -365,11 +416,13 @@ sc_simcontext::init() void sc_simcontext::clean() { + if (m_parent_context) return; // assume the parent will delete us // remove remaining zombie processes do_collect_processes(); delete m_stub_registry; delete m_method_invoker_p; + delete m_async_runnable_helper; // must precede m_prim_channel_registry delete m_error; delete m_cor_pkg; delete m_time_params; @@ -378,13 +431,15 @@ sc_simcontext::clean() delete m_null_event_p; delete m_timed_events; delete m_process_table; - delete m_name_gen; delete m_stage_cb_registry; delete m_prim_channel_registry; delete m_export_registry; delete m_port_registry; delete m_module_registry; - delete m_object_manager; + if (!m_parent_context) { + delete m_name_gen; + delete m_object_manager; + } m_delta_events.clear(); m_child_objects.clear(); @@ -662,7 +717,7 @@ sc_simcontext::elaborate() // (not added to public object hierarchy) m_method_invoker_p = - new sc_invoke_method("$$$$kernel_module$$$$_invoke_method" ); + new sc_invoke_method(("$$$$kernel_module$$$$_invoke_method$" + std::to_string((uint64_t)((void*)this))).c_str()); set_simulation_status(SC_BEFORE_END_OF_ELABORATION); for( int cd = 0; cd != 4; /* empty */ ) @@ -1280,6 +1335,31 @@ sc_simcontext::add_reset_finder( sc_reset_finder* reset_finder ) m_reset_finder_q = reset_finder; } +sc_object_host* +sc_simcontext::first_top_level_host() const +{ + // Find the first top-level sc_module child. We skip non-module + // children (sc_signal, sc_async_runnable_helper, etc.) so the + // result reflects user intent rather than whichever helper happens + // to be constructed first. Children are appended, never reordered, + // so the result is stable for the simcontext's lifetime. + for ( sc_object* o : m_child_objects ) { + if ( sc_module* mod = dynamic_cast( o ) ) + return mod; + } + return NULL; +} + +const char* +sc_simcontext::name() const +{ + // Use the basename of the first top-level sc_module child as the + // simcontext's logical name. Falls back to a kernel-internal + // sentinel name before any module is registered. + sc_object_host* top = first_top_level_host(); + return top ? top->basename() : SC_DEFAULT_SIMCONTEXT_NAME_; +} + const ::std::vector& sc_simcontext::get_child_objects() const { @@ -1554,8 +1634,8 @@ void sc_simcontext::post_suspend() const static sc_simcontext sc_default_global_context; sc_simcontext* sc_curr_simcontext = &sc_default_global_context; #else - SC_API sc_simcontext* sc_curr_simcontext = 0; - SC_API sc_simcontext* sc_default_global_context = 0; + thread_local SC_API sc_simcontext* sc_curr_simcontext = 0; + thread_local SC_API sc_simcontext* sc_default_global_context = 0; #endif #else // Not MT-safe! @@ -1696,7 +1776,7 @@ sc_start( const sc_time& duration, sc_starvation_policy p ) exit_time = context_p->m_curr_time + duration; // called with duration = SC_ZERO_TIME for the first time - static bool init_delta_or_pending_updates = + thread_local static bool init_delta_or_pending_updates = ( starting_delta == 0 && exit_time == SC_ZERO_TIME ); // If the simulation status is bad issue the appropriate message: @@ -2071,6 +2151,41 @@ SC_API void sc_unregister_stage_callback(sc_stage_callback_if & cb, ->unregister_callback(cb, mask); } +// +// Implementation defined Dynamic log verbosity dispatch +// +void sc_simcontext::set_log_verbosity_fn( + std::function fn) +{ + if (dynamic_log_verbosity) { + SC_REPORT_WARNING(SC_LOG_OVERWRITE_VERBOSITY_FN_, 0); + } else { + dynamic_log_verbosity = std::move(fn); + } +} + +sc_log_level sc_simcontext::get_log_verbosity(sc_log_logger_cache &logger, + const char *file, + int line, + std::string_view local_tag) { + if (dynamic_log_verbosity) + return dynamic_log_verbosity(logger, file, line, local_tag); + else + return as_log(sc_report_handler::get_verbosity_level()); +} + +// NB these functions are NOT a standard API, but are +// exposed to provide access to the (implementation defined) dynamic logging configuration +void sc_log_impl::sc_set_log_verbosity_fn( + std::function fn) +{ + sc_get_curr_simcontext()->set_log_verbosity_fn(std::move(fn)); +} +sc_log_level sc_log_impl::sc_get_log_verbosity(sc_log_logger_cache &logger, const char *file, + int line, std::string_view local_tag) { + return sc_get_curr_simcontext()->get_log_verbosity(logger, file, line, local_tag); +} + } // namespace sc_core /***************************************************************************** diff --git a/src/sysc/kernel/sc_simcontext.h b/src/sysc/kernel/sc_simcontext.h index 7788281f3..6fbff8f47 100644 --- a/src/sysc/kernel/sc_simcontext.h +++ b/src/sysc/kernel/sc_simcontext.h @@ -30,6 +30,8 @@ #ifndef SC_SIMCONTEXT_H #define SC_SIMCONTEXT_H +#include + #include "sysc/kernel/sc_cmnhdr.h" #include "sysc/kernel/sc_process.h" #include "sysc/kernel/sc_status.h" @@ -37,6 +39,8 @@ #include "sysc/kernel/sc_stage_callback_if.h" #include "sysc/utils/sc_hash.h" #include "sysc/utils/sc_pq.h" +#include "sysc/log/sc_log_types.h" +#include "sysc/utils/sc_utils_ids.h" #include "sysc/communication/sc_host_mutex.h" @@ -70,6 +74,12 @@ class sc_prim_channel_registry; class sc_process_table; class sc_signal_bool_deval; class sc_trace_file; + +// Kernel-internal fallback name used for an sc_simcontext that has +// not yet acquired a name from a top-level child object. Using the +// class name keeps the style in line with other kernel-generated +// basenames ("object", "method_p", "thread_p", "invoker"). +#define SC_DEFAULT_SIMCONTEXT_NAME_ "sc_simcontext" class sc_runnable; class sc_process_host; class sc_method_process; @@ -77,6 +87,7 @@ class sc_cthread_process; class sc_thread_process; class sc_reset_finder; class sc_stub_registry; +class sc_async_runnable_helper; extern sc_simcontext* sc_get_curr_simcontext(); @@ -147,6 +158,27 @@ SC_API void sc_register_stage_callback(sc_stage_callback_if & cb, SC_API void sc_unregister_stage_callback(sc_stage_callback_if & cb, unsigned int mask); +struct sc_log_impl { +// +------------------------------------------------------------------------------------------------ +// | Non standard API: Install a callback used to determine the effective log level for a given +// | logger cache and local_tag. The cache parameter may be used to remember +// | a computed level, once set the function will not be re-called. +// +------------------------------------------------------------------------------------------------ +static void sc_set_log_verbosity_fn(std::function + fn); + +// +------------------------------------------------------------------------------------------------ +// | Non standard API: Query the current log verbosity for the given handle cache and local_tag. +// | If no callback has been installed, the implementation falls back to +// | the global report verbosity. +// +------------------------------------------------------------------------------------------------ +static sc_core::sc_log_level sc_get_log_verbosity(sc_core::sc_log_logger_cache &logger, + const char *file, int line, + std::string_view local_tag = {}); +}; + class sc_invoke_method; SC_API void sc_suspend_all(); @@ -174,6 +206,7 @@ class SC_API sc_simcontext friend class sc_time_tuple; friend class sc_clock; friend class sc_method_process; + friend class sc_async_runnable_helper; friend class sc_stage_callback_registry; friend class sc_port_registry; friend class sc_process_b; @@ -181,6 +214,7 @@ class SC_API sc_simcontext friend class sc_prim_channel; friend class sc_cthread_process; friend class sc_thread_process; + friend struct sc_core::sc_log_logger_cache; friend SC_API sc_dt::uint64 sc_delta_count(); friend SC_API const std::vector& sc_get_top_level_events( const sc_simcontext* simc_p); @@ -205,6 +239,8 @@ class SC_API sc_simcontext friend SC_API void sc_unregister_stage_callback(sc_stage_callback_if & cb, unsigned int mask); + friend struct sc_log_impl; + enum sc_signal_write_check { SC_SIGNAL_WRITE_CHECK_DISABLE_ = 0x0, // no multiple writer checks @@ -243,6 +279,24 @@ class SC_API sc_simcontext sc_object_host* active_object(); + // A logical name for this simcontext. Today there is no field that + // holds an explicit simcontext name; we derive one from the basename + // of the first top-level sc_module child, falling back to + // SC_DEFAULT_SIMCONTEXT_NAME_ (a clearly kernel-internal token) when + // no module has been registered yet. Used to qualify kernel- + // internal names (e.g. sc_runnable's list-head sentinels) so that + // simcontexts sharing an sc_object_manager do not collide on those + // names. Returns a pointer that is stable for the simcontext's + // lifetime (basename storage is owned by the child object). + const char* name() const; + + // Returns the sc_object_host* backing name() above, or NULL if no + // top-level sc_module has been registered yet. Exposed so that + // kernel-internal code (e.g. sc_runnable::init, which anchors its + // list-head sentinels under this module for hierarchical naming) + // does not have to walk the deprecated get_child_objects() list. + sc_object_host* first_top_level_host() const; + sc_object* first_object(); sc_object* next_object(); sc_object* find_object( const char* name ); @@ -329,6 +383,17 @@ class SC_API sc_simcontext void pre_suspend() const; void post_suspend() const; +private: + void set_log_verbosity_fn(std::function + fn); + sc_core::sc_log_level get_log_verbosity( + sc_core::sc_log_logger_cache &, + const char *file, + int line, + std::string_view local_tag = {}); + private: void hierarchy_push(sc_object_host*); sc_object_host* hierarchy_pop(); @@ -359,6 +424,18 @@ class SC_API sc_simcontext void push_runnable_method( sc_method_handle ); void push_runnable_thread( sc_thread_handle ); + // Cross-simcontext-safe variants: if the caller's sc_curr_simcontext + // differs from this, route via the async runnable helper so the + // runnable list is only ever mutated on its owning thread. + void push_runnable_method_async( sc_method_handle ); + void push_runnable_thread_async( sc_thread_handle ); + + // Run fn() on this simcontext's thread. If foreign, posts via the + // async helper and returns immediately; the callback fires in the + // owning sim's next update phase. If local, runs synchronously. + // Kernel-internal; channels reach it via sc_prim_channel's forwarder. + void run_update_async( std::function fn ); + void push_runnable_method_front( sc_method_handle ); void push_runnable_thread_front( sc_thread_handle ); @@ -378,6 +455,8 @@ class SC_API sc_simcontext inline void set_simulation_status(sc_status status); + std::function dynamic_log_verbosity; + private: enum execution_phases { @@ -423,6 +502,7 @@ class SC_API sc_simcontext sc_time m_curr_time; sc_invoke_method* m_method_invoker_p; + sc_async_runnable_helper* m_async_runnable_helper; sc_dt::uint64 m_change_stamp; // "time" change occurred. sc_dt::uint64 m_delta_count; sc_dt::uint64 m_initial_delta_count_at_current_time; @@ -447,6 +527,8 @@ class SC_API sc_simcontext int m_suspend; int m_unsuspendable; + sc_simcontext* m_parent_context; + private: // disabled @@ -459,8 +541,8 @@ class SC_API sc_simcontext // Not MT safe. #if 1 -extern SC_API sc_simcontext* sc_curr_simcontext; -extern SC_API sc_simcontext* sc_default_global_context; +extern thread_local SC_API sc_simcontext* sc_curr_simcontext; +extern thread_local SC_API sc_simcontext* sc_default_global_context; inline sc_simcontext* sc_get_curr_simcontext() diff --git a/src/sysc/kernel/sc_simcontext_int.h b/src/sysc/kernel/sc_simcontext_int.h index 54bda9556..28291dbb2 100644 --- a/src/sysc/kernel/sc_simcontext_int.h +++ b/src/sysc/kernel/sc_simcontext_int.h @@ -32,6 +32,11 @@ #ifndef SC_SIMCONTEXT_INT_H #define SC_SIMCONTEXT_INT_H +#include +#include +#include + +#include "sysc/communication/sc_prim_channel.h" #include "sysc/kernel/sc_simcontext.h" #include "sysc/kernel/sc_runnable.h" #include "sysc/kernel/sc_runnable_int.h" @@ -61,6 +66,57 @@ namespace sc_core { +// ---------------------------------------------------------------------------- +// CLASS : sc_async_runnable_helper +// +// Kernel prim_channel owned by each sc_simcontext that accepts +// cross-simcontext work requests and drains them during the owning +// simcontext's update phase. Three entry points: +// post_method / post_thread - fast path, used by trigger fan-out to +// enqueue a single runnable for the foreign sim's next delta. +// post_callback - general path for whole-function calls +// that must happen on the owning sim's thread (e.g. enable/resume +// which read-modify-write m_state). +// Intentionally private to kernel (internal header). +// ---------------------------------------------------------------------------- + +class sc_async_runnable_helper : public sc_prim_channel +{ +public: + sc_async_runnable_helper() : sc_prim_channel() {} + + void post_method( sc_method_process* m ) + { + std::lock_guard lg( m_mutex ); + m_pending_methods.push_back( m ); + async_request_update(); + } + + void post_thread( sc_thread_process* t ) + { + std::lock_guard lg( m_mutex ); + m_pending_threads.push_back( t ); + async_request_update(); + } + + void post_callback( std::function fn ) + { + std::lock_guard lg( m_mutex ); + m_pending_callbacks.push_back( std::move( fn ) ); + async_request_update(); + } + +protected: + // defined in sc_simcontext.cpp (needs full sc_method_process etc. definitions) + virtual void update(); + +private: + std::mutex m_mutex; + std::vector m_pending_methods; + std::vector m_pending_threads; + std::vector> m_pending_callbacks; +}; + // We use m_current_writer rather than m_curr_proc_info.process_handle to // return the active process for sc_signal::check_write since that lets // us turn it off a library compile time, and only incur the overhead at @@ -205,6 +261,40 @@ sc_simcontext::push_runnable_method( sc_method_handle method_h ) m_runnable->push_back_method( method_h ); } +inline +void +sc_simcontext::push_runnable_method_async( sc_method_handle method_h ) +{ + if ( this != sc_get_curr_simcontext() ) + m_async_runnable_helper->post_method( method_h ); + else + push_runnable_method( method_h ); +} + +inline +void +sc_simcontext::push_runnable_thread_async( sc_thread_handle thread_h ) +{ + if ( this != sc_get_curr_simcontext() ) + m_async_runnable_helper->post_thread( thread_h ); + else + push_runnable_thread( thread_h ); +} + +// Run fn() on this simcontext's thread. If we're already on it, run +// synchronously; otherwise post via the async helper to drain in the +// owning sim's next update phase. Used to safely route whole-function +// calls (e.g. enable/resume) that read-modify-write process state. +inline +void +sc_simcontext::run_update_async( std::function fn ) +{ + if ( this != sc_get_curr_simcontext() ) + m_async_runnable_helper->post_callback( std::move( fn ) ); + else + fn(); +} + inline void sc_simcontext::push_runnable_method_front( sc_method_handle method_h ) diff --git a/src/sysc/kernel/sc_thread_process.cpp b/src/sysc/kernel/sc_thread_process.cpp index 36991c113..024bd302d 100644 --- a/src/sysc/kernel/sc_thread_process.cpp +++ b/src/sysc/kernel/sc_thread_process.cpp @@ -220,6 +220,11 @@ void sc_thread_process::disable_process( void sc_thread_process::enable_process( sc_descendant_inclusion_info descendants ) { + // m_state read-modify-write must happen on the owning sim's thread. + if ( simcontext() != sc_get_curr_simcontext() ) { + simcontext()->run_update_async( [this, descendants]{ enable_process(descendants); } ); + return; + } // IF NEEDED PROPOGATE THE ENABLE REQUEST THROUGH OUR DESCENDANTS: @@ -338,6 +343,11 @@ void sc_thread_process::prepare_for_simulation() void sc_thread_process::resume_process( sc_descendant_inclusion_info descendants ) { + // m_state read-modify-write must happen on the owning sim's thread. + if ( simcontext() != sc_get_curr_simcontext() ) { + simcontext()->run_update_async( [this, descendants]{ resume_process(descendants); } ); + return; + } // IF NEEDED PROPOGATE THE RESUME REQUEST THROUGH OUR DESCENDANTS: @@ -832,7 +842,7 @@ bool sc_thread_process::trigger_dynamic( sc_event* e ) } else { - simcontext()->push_runnable_thread(this); + simcontext()->push_runnable_thread_async(this); } return true; diff --git a/src/sysc/kernel/sc_thread_process.h b/src/sysc/kernel/sc_thread_process.h index cf566e160..b845b4478 100644 --- a/src/sysc/kernel/sc_thread_process.h +++ b/src/sysc/kernel/sc_thread_process.h @@ -505,7 +505,7 @@ sc_thread_process::trigger_static() return; } - simcontext()->push_runnable_thread(this); + simcontext()->push_runnable_thread_async(this); } #undef DEBUG_MSG diff --git a/src/sysc/log/sc_log.cpp b/src/sysc/log/sc_log.cpp new file mode 100644 index 000000000..5b1aee3d6 --- /dev/null +++ b/src/sysc/log/sc_log.cpp @@ -0,0 +1,71 @@ +/***************************************************************************** + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + *****************************************************************************/ +/***************************************************************************** + sc_log.h --SystemC logging functions. + Original Author: Eyck Jentzsch, MINRES Technologies GmbH + Mark Burton, Qualcomm Technologies, Inc. + + CHANGE LOG AT THE END OF THE FILE + *****************************************************************************/ + +#include "sysc/kernel/sc_simcontext.h" +#include "sysc/log/sc_log_types.h" +#include +#include +#include + +namespace sc_core { + +// Definition of log level map (declared as extern in sc_log_types.h) +// This avoids creating duplicate copies in each translation unit. +const std::map log_level_map = { + {sc_log_level::CRITICAL, "CRITICAL"}, + {sc_log_level::NONE, "NONE"}, + {sc_log_level::WARN, "WARN"}, + {sc_log_level::INFO, "INFO"}, + {sc_log_level::DEBUG, "DEBUG"}, + {sc_log_level::TRACE, "TRACE"} +}; + +static thread_local sc_log_logger_cache* s_current = nullptr; + +sc_log_logger_cache* sc_log_logger_cache::get_current() { return s_current; } +void sc_log_logger_cache::set_current(sc_log_logger_cache* p) { s_current = p; } + +sc_log_level sc_log_logger_cache::get_log_verbosity_cached( + const char *file, int line, std::string_view local_tag) { + s_current = this; + if (level != sc_log_level::UNSET) { + return level; + } + + return sc_log_impl::sc_get_log_verbosity(*this, file, line, local_tag); +} + +void sc_log_logger_cache::set_tag(std::string new_tag) { + tag = std::move(new_tag); + level = sc_log_level::UNSET; +} + +} // namespace sc_core + +// Global default logger with empty tag (in global namespace for proper name +// shadowing). This logger is used when no specific logger handle is provided. +sc_core::sc_log_logger_cache SC_LOG_LOG_LEVEL_CACHE{ + sc_core::sc_log_level::UNSET, // level + {}, // tag (empty) + {}, // scname (empty) + nullptr // typename_str (no type info) +}; diff --git a/src/sysc/log/sc_log.h b/src/sysc/log/sc_log.h new file mode 100644 index 000000000..73cffd162 --- /dev/null +++ b/src/sysc/log/sc_log.h @@ -0,0 +1,231 @@ +/***************************************************************************** + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + *****************************************************************************/ +/***************************************************************************** + sc_log.h --SystemC logging functions. + Original Author: Eyck Jentzsch, MINRES Technologies GmbH + Mark Burton, Qualcomm Technologies, Inc. + + CHANGE LOG AT THE END OF THE FILE + *****************************************************************************/ + +#ifndef _SC_LOG_H_ +#define _SC_LOG_H_ + +#define SC_HAS_SC_LOG + +#include +#include +#if __has_include() +#include +#elif __has_include() +#include +#endif +#include +#include +#include +#include +#include + +#include + +// Declaration of global default logger (defined in sc_log.cpp, in global +// namespace) +extern sc_core::sc_log_logger_cache SC_LOG_LOG_LEVEL_CACHE; + +/****************************************************************************** + * PRIVATE IMPLEMENTATION HELPER MACROS + * + * These macros are internal implementation details and should NOT be used + * directly by user code. They are prefixed with SC_LOG_PRIV__ to indicate + * they are private implementation details. + ******************************************************************************/ + +// 2-step token pasting (forces expansion before concatenation) +#define SC_LOG_PRIV__CAT_IMPL(a, b) a##b +#define SC_LOG_PRIV__CAT(a, b) SC_LOG_PRIV__CAT_EVAL(a, b) +#define SC_LOG_PRIV__CAT_EVAL(a, b) SC_LOG_PRIV__CAT_IMPL(a, b) + +// Argument counting: supports 0, 1, or 2 arguments +#define SC_LOG_PRIV__NARG(...) SC_LOG_PRIV__NARG_IMPL(0, ##__VA_ARGS__, 2, 1, 0) +#define SC_LOG_PRIV__NARG_IMPL(_0, _1, _2, N, ...) N + +// Dispatch to appropriate macro based on argument count +#define SC_LOG_PRIV__DISPATCH(func, ...) \ + SC_LOG_PRIV__DISPATCH_IMPL(func, SC_LOG_PRIV__NARG(__VA_ARGS__), __VA_ARGS__) +#define SC_LOG_PRIV__DISPATCH_IMPL(func, count, ...) \ + SC_LOG_PRIV__CAT(func, count)(__VA_ARGS__) + +// Detect whether a macro argument is a "logger handle" or a "tag" +#define SC_LOG_PRIV__IS_LOGGER_HANDLE(x) \ + std::is_same_v, sc_core::sc_log_logger_cache> + +// Note: SC_LOG_PRIV__FMT_EMPTY_STR is both a const char* and a function-like +// macro. Without parentheses it's the empty string; with parentheses it calls +// std::format (C++20, preferred) or fmt::format (fallback). +static const char *SC_LOG_PRIV__FMT_EMPTY_STR = ""; +#if defined(__cpp_lib_format) +#define SC_LOG_PRIV__FMT_EMPTY_STR(...) std::format(__VA_ARGS__) +#elif defined(FMT_VERSION) +#define SC_LOG_PRIV__FMT_EMPTY_STR(...) fmt::format(__VA_ARGS__) +#else +#define SC_LOG_PRIV__FMT_EMPTY_STR(...) "" +#endif + +// Internal verbosity check variants (used by public SC_LOG_AT macro) +#define SC_LOG_PRIV__VBSTY_CHECK0(lvl) \ + ((SC_LOG_LOG_LEVEL_CACHE.level >= (lvl)) && \ + (SC_LOG_LOG_LEVEL_CACHE.get_log_verbosity_cached(__FILE__, __LINE__) >= \ + (lvl))) + +#define SC_LOG_PRIV__VBSTY_CHECK1(lvl, arg1) \ + ([&](auto &&x) -> bool { \ + if constexpr (SC_LOG_PRIV__IS_LOGGER_HANDLE(x)) { \ + return ((x.level >= (lvl)) && \ + (x.get_log_verbosity_cached(__FILE__, __LINE__) >= (lvl))); \ + } else { \ + return SC_LOG_PRIV__VBSTY_CHECK2(lvl, SC_LOG_LOG_LEVEL_CACHE_GLOBAL, x); \ + } \ + }(arg1)) + +#define SC_LOG_PRIV__VBSTY_CHECK2(lvl, logger, tag) \ + ((logger.level >= lvl) && \ + (logger.get_log_verbosity_cached(__FILE__, __LINE__, tag) >= lvl)) + +#define SC_LOG_PRIV__VBSTY_CHECK_IMPL(count, lvl, ...) \ + SC_LOG_PRIV__CAT(SC_LOG_PRIV__VBSTY_CHECK, count)(lvl, ##__VA_ARGS__) + +// Internal tag extraction variants (used by public SC_LOG_MSG macro) +#define SC_LOG_PRIV__GET_TAG0() \ + (SC_LOG_LOG_LEVEL_CACHE.tag.empty() ? SC_LOG_LOG_LEVEL_CACHE.scname.c_str() \ + : SC_LOG_LOG_LEVEL_CACHE.tag.c_str()) + +#define SC_LOG_PRIV__GET_TAG1(arg1) \ + ([&](auto &&x) -> const char * { \ + if constexpr (SC_LOG_PRIV__IS_LOGGER_HANDLE(x)) { \ + return (x.tag.empty() ? x.scname.c_str() : x.tag.c_str()); \ + } else { \ + return x; \ + } \ + }(arg1)) + +#define SC_LOG_PRIV__GET_TAG2(logger, tag) tag + +// Internal handle variants (used by public SC_LOG_HANDLE macro) +#define SC_LOG_PRIV__HANDLE0() \ + sc_core::sc_log_logger_cache SC_LOG_LOG_LEVEL_CACHE = \ + sc_core::sc_log_handle_factory::make(sc_core::sc_log_level::UNSET, "", \ + this) + +#define SC_LOG_PRIV__HANDLE1(tag_str) \ + sc_core::sc_log_logger_cache SC_LOG_LOG_LEVEL_CACHE = \ + sc_core::sc_log_handle_factory::make(sc_core::sc_log_level::UNSET, \ + tag_str, this) + +#define SC_LOG_PRIV__HANDLE2(logger_name, tag_str) \ + sc_core::sc_log_logger_cache SC_LOG_PRIV__HANDLE_NAME(logger_name) = \ + sc_core::sc_log_handle_factory::make(sc_core::sc_log_level::UNSET, \ + tag_str, this) + +// Internal static handle variants (used by public SC_LOG_HANDLE_STATIC macro) +#define SC_LOG_PRIV__HANDLE_STATIC1(tag_str) \ + static sc_core::sc_log_logger_cache SC_LOG_LOG_LEVEL_CACHE = \ + sc_core::sc_log_handle_factory::make_static( \ + sc_core::sc_log_level::UNSET, tag_str) + +#define SC_LOG_PRIV__HANDLE_STATIC2(logger_name, tag_str) \ + static sc_core::sc_log_logger_cache SC_LOG_PRIV__HANDLE_NAME(logger_name) = \ + sc_core::sc_log_handle_factory::make_static( \ + sc_core::sc_log_level::UNSET, tag_str) + +// Helper to get logger handle name +#define SC_LOG_PRIV__HANDLE_NAME(x) x + +/****************************************************************************** + * PUBLIC API MACROS + * + * These macros form the public logging API and should be used by user code. + ******************************************************************************/ + +/** + * SC_LOG_HANDLE - Declare a logger handle in a class + * + * Usage: + * SC_LOG_HANDLE() // Default logger with no tag + * SC_LOG_HANDLE("MyTag") // Default logger with tag + * SC_LOG_HANDLE(my_logger, "MyTag") // Named logger with tag + */ +#define SC_LOG_HANDLE(...) \ + SC_LOG_PRIV__DISPATCH(SC_LOG_PRIV__HANDLE, ##__VA_ARGS__) + +/** + * SC_LOG_HANDLE_STATIC - Declare a static/global logger handle + * + * Usage: + * SC_LOG_HANDLE_STATIC("MyTag") // static logger with tag + * SC_LOG_HANDLE_STATIC(my_logger, "MyTag") // Named static logger with tag + * + * Note: The 0 and 1 argument forms create a file-local static logger that + * shadows the global SC_LOG_LOG_LEVEL_CACHE within that translation unit. + * This is useful for .cpp files that want their own default logger. + */ +#define SC_LOG_HANDLE_STATIC(...) \ + SC_LOG_PRIV__DISPATCH(SC_LOG_PRIV__HANDLE_STATIC, ##__VA_ARGS__) + +/** + * SC_LOG_HANDLE_VECTOR - Declare a vector of logger handles + */ +#define SC_LOG_HANDLE_VECTOR(NAME) \ + std::vector SC_LOG_PRIV__HANDLE_NAME(NAME) + +/** + * SC_LOG_HANDLE_VECTOR_PUSH_BACK - Add a logger to a vector + */ +#define SC_LOG_HANDLE_VECTOR_PUSH_BACK(NAME, tag_str) \ + SC_LOG_PRIV__HANDLE_NAME(NAME).push_back( \ + sc_core::sc_log_handle_factory::make( \ + sc_core::sc_log_level::UNSET, tag_str, this)) + +/** + * SC_LOG_AT - Log a message at a specific level + * + * Usage: + * SC_LOG_AT(sc_core::sc_log_level::INFO) << "message"; + * SC_LOG_AT(sc_core::sc_log_level::INFO, my_logger) << "message"; + * SC_LOG_AT(sc_core::sc_log_level::INFO, "tag") << "message"; + */ +#define SC_LOG_AT(lvl, ...) \ + if (SC_LOG_PRIV__VBSTY_CHECK_IMPL(SC_LOG_PRIV__NARG(__VA_ARGS__), lvl, \ + ##__VA_ARGS__)) \ + ::sc_core::sc_logger<::sc_core::SC_INFO, false>(__FILE__, __LINE__, lvl) \ + .type(SC_LOG_PRIV__DISPATCH(SC_LOG_PRIV__GET_TAG, ##__VA_ARGS__)) \ + .get() \ + << SC_LOG_PRIV__FMT_EMPTY_STR + +/** + * Convenience macros for common log levels + * + * Usage: + * SC_CRITICAL() << "message"; + * SC_WARN(my_logger) << "message"; + * SC_INFO("tag") << "message"; + */ +#define SC_CRITICAL(...) \ + SC_LOG_AT(sc_core::sc_log_level::CRITICAL, ##__VA_ARGS__) +#define SC_WARN(...) SC_LOG_AT(sc_core::sc_log_level::WARN, ##__VA_ARGS__) +#define SC_INFO(...) SC_LOG_AT(sc_core::sc_log_level::INFO, ##__VA_ARGS__) +#define SC_DEBUG(...) SC_LOG_AT(sc_core::sc_log_level::DEBUG, ##__VA_ARGS__) +#define SC_TRACE(...) SC_LOG_AT(sc_core::sc_log_level::TRACE, ##__VA_ARGS__) + +#endif /* _SC_LOG_H_ */ diff --git a/src/sysc/log/sc_log_types.h b/src/sysc/log/sc_log_types.h new file mode 100644 index 000000000..74f7c96f3 --- /dev/null +++ b/src/sysc/log/sc_log_types.h @@ -0,0 +1,366 @@ +/***************************************************************************** + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + *****************************************************************************/ +/***************************************************************************** + sc_log_types.h -- SystemC logging type definitions and utilities. + Original Author: Eyck Jentzsch, MINRES Technologies GmbH + Mark Burton, Qualcomm Technologies, Inc. + + CHANGE LOG AT THE END OF THE FILE + *****************************************************************************/ + +#ifndef _SC_LOG_TYPES_H_ +#define _SC_LOG_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** \ingroup sc_log + * @{ + */ +/**@{*/ +//! @brief Logging utilities +namespace sc_core { + +/************************ + * Provide a set of names and conversions that are suitable for logging levels + *based on SystemC "verbosity's" + ************************/ + +enum class sc_log_level { + NONE = sc_core::SC_NONE, + CRITICAL = sc_core::SC_NONE, + WARN = sc_core::SC_LOW, + INFO = sc_core::SC_MEDIUM, + DEBUG = sc_core::SC_HIGH, + TRACE = sc_core::SC_DEBUG, + + UNSET = INT_MAX +}; + +// Map of log levels to their string representations (defined in sc_log.cpp) +extern const std::map log_level_map; + +/** + * @fn log as_log(int) + * @brief safely convert an integer into a log level + * + * @param logLevel the logging level + * @return the log level + */ +inline sc_log_level as_log(int logLevel) { + auto m = log_level_map; + for (auto l : m) { + if (logLevel <= static_cast(l.first)) { + return l.first; + } + } + return sc_log_level::TRACE; +} + +/** + * @fn log as_log(std::string) + * @brief safely convert a string into a log level + * + * @param logName the string name for the log level + * @return the log level + */ +inline sc_log_level as_log(std::string logName) { + auto m = log_level_map; + for (auto l : m) { + if (logName == l.second) + return l.first; + } + return sc_log_level::TRACE; +} +/** + * @fn std::istream& operator >>(std::istream&, log&) + * @brief read a log level from input stream e.g. used by boost::lexical_cast + * + * @param is input stream holding the string representation + * @param val the value holding the resulting value + * @return the input stream + */ +inline std::istream &operator>>(std::istream &is, sc_log_level &val) { + std::string buf; + is >> buf; + val = as_log(buf); + return is; +} +/** + * @fn std::ostream& operator <<(std::ostream&, const log&) + * @brief output the textual representation of the log level + * + * @param os output stream + * @param val logging level + * @return reference to the stream for chaining + */ +inline std::ostream &operator<<(std::ostream &os, sc_log_level const &val) { + auto m = log_level_map; + os << m[val]; + return os; +} + +/* Convenience helper to detect if a type has a name() method. + * This allows logging to work both inside and outside of SystemC hierarchy. */ +class sc_log_priv__call_sc_name_fn { + template + static auto test(T *p) -> decltype(p->name(), std::true_type()); + template static auto test(...) -> decltype(std::false_type()); + + template + static constexpr bool has_method = decltype(test(nullptr))::value; + +public: + // define a function IF the method exists + template + auto operator()(TYPE *p) const + -> std::enable_if_t, const char *> { + return p->name(); + } + + // define a function IF NOT the method exists + template + auto operator()(TYPE *p) const + -> std::enable_if_t, const char *> { + return nullptr; + } +}; + +/******************/ + +// Forward declaration for sc_log_impl (defined in sc_simcontext.h) +struct sc_log_impl; + +/** + * @brief Cached logging information for a logger instance. + * + * Type choices rationale: + * - tag: string_view - references string literals passed to SC_LOG_HANDLE macros + * - scname: string_view - references the result of this->name() which has + * static storage duration in sc_object + * - typename_str: const char* - directly stores the pointer returned by + * typeid().name() which has static storage duration + * + * These choices avoid unnecessary string copies while maintaining safety + * since all referenced strings have appropriate lifetimes. + */ +struct sc_log_logger_cache { + sc_log_level level = sc_log_level::UNSET; + std::string tag{}; // Logger tag/identifier (owns the string) + std::string scname{}; // Captured sc_object name (owns the string) + const char* typename_str = nullptr; // Captured type name (from typeid(*this).name()) + + /** + * @brief Initialize the verbosity cache and/or return the cached value. + * + * This function checks if the level is already cached, and if not, + * queries the dynamic verbosity function. + * + * @param file source file name + * @param line source line number + * @param local_tag optional local tag that overrides the effective name + * @return log level for this logger + */ + sc_log_level get_log_verbosity_cached(const char *file, int line, + std::string_view local_tag = {}); + + /** + * @brief Set the tag to a runtime-computed string. + * + * The cached level is reset so the verbosity function re-evaluates + * with the new tag on the next log statement. + * + * @param new_tag the new tag string + */ + void set_tag(std::string new_tag); + + /// Get/set the logger cache that most recently performed a verbosity + /// check on this thread. Used by the report handler to access + /// scname/tag/typename. + /// + /// Lifecycle: set by get_log_verbosity_cached() (called during the + /// SC_LOG verbosity check), cleared by ~sc_logger() (after the report + /// handler has run). Accessor functions ensure correct linkage across + /// shared library boundaries. + static sc_log_logger_cache* get_current(); + static void set_current(sc_log_logger_cache* p); +}; + +/** + * @brief Factory for constructing sc_log_logger_cache objects with captured + * context. + * + * Note: C++ cannot infer an owning object's name/type from within the cache's + * default constructor, so the owning object pointer must be provided here (when + * available). + * + */ +struct sc_log_handle_factory { + template + static sc_log_logger_cache make(sc_log_level lvl, const char *tag_str, + TYPE *p) { + const char *n = sc_log_priv__call_sc_name_fn{}(p); + const char *t = typeid(*p).name(); + return sc_log_logger_cache{ + lvl, + tag_str ? std::string(tag_str) : std::string(), + n ? std::string(n) : std::string(), + t // typeid().name() returns const char* with static storage + }; + } + + static sc_log_logger_cache make_static(sc_log_level lvl, + const char *tag_str) { + return sc_log_logger_cache{ + lvl, + tag_str ? std::string(tag_str) : std::string(), + std::string(), + nullptr // No type information for static loggers + }; + } +}; + +/** + * @brief Return list of logging parameters that have been used + * + */ +std::vector get_logging_parameters(); + +/** + * @struct sc_logger + * @brief the logger class + * + * The sc_logger creates a RTTI based output stream to be used similar to + * std::cout + * + * @tparam SEVERITY + */ +template +struct sc_logger { + /** + * @fn sc_logger(const char*, int, int=sc_core::SC_MEDIUM) + * @brief + * + * @param file where the log entry originates + * @param line number where the log entry originates + * @param verbosity the log level + */ + sc_logger(const char *file, int line, + sc_log_level verbosity = sc_core::sc_log_level::INFO) + : t(nullptr), file(file), line(line), level(verbosity) {} + + + sc_logger() = delete; + + sc_logger(const sc_logger &) = delete; + + sc_logger(sc_logger &&) = delete; + + sc_logger &operator=(const sc_logger &) = delete; + + sc_logger &operator=(sc_logger &&) = delete; + /** + * @fn ~sc_logger() + * @brief the destructor generating the SystemC report + * + * NB a destructor should not throw an exception, here we attempt to prevent + * the sc_report_handler from throwing The ScLogging interface is _ONLY_ for + * logging, simulation control should happen in user code. + */ + virtual ~sc_logger() noexcept(true) { + auto old = sc_core::sc_report_handler::set_actions(SEVERITY); + if (WITH_ACTIONS == false) { + sc_core::sc_report_handler::set_actions( + SEVERITY, old & ~(sc_core::SC_THROW | sc_core::SC_INTERRUPT | + sc_core::SC_STOP | sc_core::SC_ABORT)); + } + ::sc_core::sc_report_handler::report( + SEVERITY, (t && *t) ? t : "SystemC", os.str().c_str(), + static_cast(level), file, line); + sc_core::sc_report_handler::set_actions(SEVERITY, old); + sc_log_logger_cache::set_current(nullptr); + } + /** + * @fn sc_logger& type() + * @brief reset the category of the log entry + * + * @return reference to self for chaining + */ + inline sc_logger &type() { + this->t = nullptr; + return *this; + } + /** + * @fn sc_logger& type(const char*) + * @brief set the category of the log entry + * + * @param t type of the log entry + * @return reference to self for chaining + */ + inline sc_logger &type(char const *t) { + this->t = const_cast(t); + return *this; + } + /** + * @fn sc_logger& type(std::string const&) + * @brief set the category of the log entry + * + * @param t type of the log entry + * @return reference to self for chaining + */ + inline sc_logger &type(std::string const &t) { + this->t = const_cast(t.c_str()); + return *this; + } + /** + * @fn std::ostream& get() + * @brief get the underlying ostringstream + * + * @return the output stream collecting the log message + */ + inline std::ostream &get() { return os; }; + +protected: + std::ostringstream os{}; + char *t{nullptr}; + const char *file; + const int line; + const sc_log_level level; +}; + +} // namespace sc_core + +// This macro is intentionally in the global namespace to allow it to be +// used as a member variable name in user classes without namespace qualification. +// The macro expands to _m_sc_log_log_level_cache_ which uses a leading underscore +// to indicate it's an implementation detail. While identifiers with leading +// underscores are generally reserved, this pattern (_m_*) is safe as it doesn't +// conflict with reserved patterns (__* or _Capital*). +#define SC_LOG_LOG_LEVEL_CACHE _m_sc_log_log_level_cache_ +#define SC_LOG_LOG_LEVEL_CACHE_GLOBAL ::_m_sc_log_log_level_cache_ + +/** @} */ // end of sc_log +#endif /* _SC_LOG_TYPES_H_ */ diff --git a/src/sysc/utils/sc_concurrent.h b/src/sysc/utils/sc_concurrent.h new file mode 100644 index 000000000..5727dc765 --- /dev/null +++ b/src/sysc/utils/sc_concurrent.h @@ -0,0 +1,256 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_concurrent.h -- Mark an SC_MODULE as safe to run concurrently with + others. Whether the implementation actually exploits this for parallel + execution is implementation-defined; the API is the user's promise that + the module's interactions with the rest of the elaboration are bounded + to its sync_window and to thread-safe channels. + + Original Author: Mark Burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef SC_CONCURRENT_H +#define SC_CONCURRENT_H + +#include "sc_sync_window.h" +#include + +#include +#include +#include +#include + +namespace sc_core { + +// Module-author opt-out. Default == every module is safe to wrap in +// sc_concurrent. An author who knows their module is fundamentally +// unsafe for concurrent execution may specialise this to false_type; +// sc_concurrent's static_assert below then turns any attempt to wrap +// the module into a compile-time error. +template +struct sc_concurrent_safe : std::true_type {}; + +template class sc_concurrent; +/** + * @brief Simcontext handler, encapsulates a simcontext + * This is required for the templated class to work, and hence is required in + * the header. It should not be used directly + * @tparam SYNC_POLICY + */ +template +class _internal_simcontext_handler { + template friend class sc_concurrent; + /** + * @brief Helper to capture start of simulation and start std::thread + * + */ + class control_module : public sc_core::sc_module, + public sc_core::sc_stage_callback_if { + class _internal_simcontext_handler *m_simcontext_h; + + std::thread m_thread; + std::mutex m_mutex; + std::condition_variable m_cv; + + // Prim channel purely to accept async_request_update, to call sc_stop. + class terminator_pc : public sc_core::sc_prim_channel { + void update() { sc_stop(); } + + public: + // terminator_pc() {} + void terminate() { async_request_update(); } + }; + terminator_pc *m_terminator; + sc_event *m_end_ev; + bool started = false; + // Captured on the parent thread in start_of_simulation(), applied on + // the child thread in sc_context_start() so the child inherits the + // parent's sc_report_handler configuration (handler, verbosity, + // suppress/force masks, sev_actions, log_file_name). Counters are + // not in the snapshot — the child starts fresh. + sc_core::sc_report_handler::config_snapshot m_report_config; + void sc_context_start() { + sc_core::sc_report_handler::apply_config(m_report_config); + m_simcontext_h->use_simcontext(); + // Register on the child's stage_callback_registry: POST_START so the + // parent's thread can stop blocking once we are up, and POST_END so we + // can wake the parent if the child's sc_start was the one that ended. + sc_core::sc_register_stage_callback(*this, + sc_core::SC_POST_START_OF_SIMULATION + | sc_core::SC_POST_END_OF_SIMULATION); + sc_core::sc_start(); + m_sync_child->detach(); + } + + public: + void start_of_simulation() { + m_report_config = sc_core::sc_report_handler::snapshot_config(); + m_thread = std::thread(&control_module::sc_context_start, this); + std::unique_lock lg(m_mutex); + m_cv.wait(lg, [this]() { return started; }); + } + void end_thread() { sc_core::sc_stop(); } + void end_of_simulation() { + // Drive the child to stop, then wait for it to fully finish + // BEFORE proceeding with the rest of the parent's teardown. + // Reason: the child and the parent share an sc_object_manager + // (see sc_simcontext::init's sub-sim handling). The hierarchy + // navigation stack m_object_stack is a single shared vector; + // sc_port_base::simulation_done on every port pushes/pops it + // via get_hierarchy_scope. If the parent runs its + // port_registry::simulation_done while the child is still + // tearing down its own ports, both threads concurrently + // mutate that stack and break its discipline. Joining here - + // before the parent's port simulation_done callbacks run - + // serialises the two teardowns and avoids the race. + m_terminator->terminate(); + if (m_thread.joinable() && + m_thread.get_id() != std::this_thread::get_id()) + m_thread.join(); + } + void stage_callback(const sc_core::sc_stage &stage) { + switch (stage) { + case sc_core::SC_POST_END_OF_SIMULATION: + if (sc_core::sc_get_curr_simcontext() == &m_simcontext_h->m_simcontext) { + // Child-side POST_END: wake the parent so it stops too. + // m_end_ev lives in the parent; notify(SC_ZERO_TIME) from the + // child's thread is routed via sc_simcontext::run_update_async + // to the parent's update phase, which delta-schedules the event; + // SC_METHOD(end_thread) then fires on the parent and calls + // sc_stop(). Use the delta variant rather than the immediate + // notify(): the cross-context callback runs in the parent's + // update phase, where immediate notification would error out + // ("immediate notification outside evaluation phase"). + // This handles the child-initiated stop; the parent-initiated + // direction is already covered by end_of_simulation() above + // (which also joins this thread). + m_end_ev->notify(sc_core::SC_ZERO_TIME); + } + // No parent-side join here any more; we joined in + // end_of_simulation(), which fires before any port's + // simulation_done callback. + break; + // Called by the child thread once it's up and running + case sc_core::SC_POST_START_OF_SIMULATION: { + std::lock_guard lg(m_mutex); + started = true; + m_cv.notify_all(); + break; + } + default: + break; + } + } + + ~control_module() { + delete m_end_ev; + delete m_terminator; + } + + SC_CTOR(control_module, _internal_simcontext_handler *p) + : m_simcontext_h(p) { + m_simcontext_h->use_simcontext(); + SYNC_POLICY sync_policy; + m_sync_child = new sc_sync_windowed("sync_child"); + m_terminator = new terminator_pc(); + + m_simcontext_h->revert_simcontext(); + + sc_core::sc_register_stage_callback(*this, + sc_core::SC_POST_END_OF_SIMULATION); + + m_end_ev = new sc_core::sc_event(); + SC_METHOD(end_thread); + sensitive << *m_end_ev; + dont_initialize(); + } + sc_sync_windowed *m_sync_child; + }; + +private: + sc_core::sc_simcontext *m_old_simcontext = + sc_get_curr_simcontext(); // Side effect create simcontext if we are the + // first module + sc_core::sc_simcontext m_simcontext; + + control_module m_ctrl_module; + sc_sync_windowed m_sync_parent; + + void use_simcontext() { + assert(sc_curr_simcontext != &m_simcontext); + m_old_simcontext = sc_curr_simcontext; + sc_curr_simcontext = &m_simcontext; + } + + void revert_simcontext() { sc_curr_simcontext = m_old_simcontext; } + +public: + _internal_simcontext_handler() + // sc_gen_unique_name keeps each wrapper's internal modules + // distinct in the shared sc_object_manager. Hardcoded names + // would collide across multiple SC_ALLOW_CONCURRENCY wrappers + // and trigger an "object already exists" rename warning. + : m_ctrl_module(sc_core::sc_gen_unique_name("ctrl_module"), this), + m_sync_parent(sc_core::sc_gen_unique_name("sync_parent")) { + SYNC_POLICY sync_policy; + + m_ctrl_module.m_sync_child->bind(&m_sync_parent); + m_sync_parent.bind(m_ctrl_module.m_sync_child); + + use_simcontext(); + } +}; + +/** + * @brief sc_concurrent marks a module as safe to run concurrently with + * other modules. It ensures _internal_simcontext_handler is constructed + * first (creating the child simcontext), the simcontext is switched, + * the wrapped module T is then constructed in that child simcontext, + * and the simcontext is restored afterwards. + * + * Whether the runtime actually spins a separate thread for the child + * simcontext is implementation-defined. The user's contract is only + * that this module's interactions with the rest of the design are + * bounded to its sync_window and thread-safe channels. + * + * @tparam T + * @tparam SYNC_POLICY + */ +template +class sc_concurrent : public _internal_simcontext_handler, public T { + static_assert(sc_concurrent_safe::value, + "module type is not declared sc_concurrent_safe " + "(specialise sc_concurrent_safe to std::false_type " + "to forbid wrapping; default is true)"); +public: + template sc_concurrent(A... a) : T(a...) { + _internal_simcontext_handler::revert_simcontext(); + } +}; + +#define SC_ALLOW_CONCURRENCY(mod, policy) sc_core::sc_concurrent + +} // namespace sc_core + +#endif // SC_CONCURRENT_H \ No newline at end of file diff --git a/src/sysc/utils/sc_mempool.cpp b/src/sysc/utils/sc_mempool.cpp index dc0442d1d..535fd0b24 100644 --- a/src/sysc/utils/sc_mempool.cpp +++ b/src/sysc/utils/sc_mempool.cpp @@ -43,15 +43,15 @@ // set the environment variable SYSTEMC_MEMPOOL_DONT_USE to 1. -static const char* dont_use_envstring = "SYSTEMC_MEMPOOL_DONT_USE"; -static bool use_default_new = false; - - +#include #include #include #include "sysc/kernel/sc_cmnhdr.h" #include "sysc/utils/sc_mempool.h" +static const char* dont_use_envstring = "SYSTEMC_MEMPOOL_DONT_USE"; +static std::atomic use_default_new{false}; + using std::printf; namespace sc_core { @@ -244,7 +244,7 @@ sc_mempool_int::~sc_mempool_int() delete[] allocators; } -static sc_mempool_int* the_mempool = 0; +thread_local static sc_mempool_int* the_mempool = 0; void* sc_mempool_int::do_allocate(std::size_t sz) diff --git a/src/sysc/utils/sc_ob_event.h b/src/sysc/utils/sc_ob_event.h new file mode 100644 index 000000000..75a0c4812 --- /dev/null +++ b/src/sysc/utils/sc_ob_event.h @@ -0,0 +1,96 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_ob_event.h -- Event that fires only when the simulator arrives at the + notification time, or there are subsequent events + + Original Author: Mark burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef _SC_OB_EVENT_ +#define _SC_OB_EVENT_ + +#include + +namespace sc_core { +class sc_ob_event : public sc_core::sc_module, + public sc_core::sc_event, + public sc_core::sc_stage_callback_if { + +private: + sc_core::sc_time m_scheduled; + sc_core::sc_process_handle m_th; + + void future_events_notify_th() { + while (true) { + m_th.suspend(); + + auto now = sc_core::sc_time_stamp(); + if (now >= m_scheduled) { + sc_core::sc_event::notify(); + } else { + sc_core::sc_event::notify(m_scheduled - now); + } + } + } + +public: + sc_ob_event(const sc_core::sc_module_name &n = + sc_core::sc_gen_unique_name("sc_ob_event")) + : sc_module(n), m_scheduled(sc_core::SC_ZERO_TIME) { + SC_THREAD(future_events_notify_th); + m_th = sc_core::sc_get_current_process_handle(); + } + void notify() { + sc_core::sc_event::notify(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void notify(double delay, sc_core::sc_time_unit unit) { + notify(sc_core::sc_time(delay, unit)); + } + void notify(sc_core::sc_time delay) { + sc_core::sc_event::cancel(); + m_scheduled = sc_core::sc_time_stamp() + delay; + sc_core::sc_register_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void cancel() { + sc_core::sc_event::cancel(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + void stage_callback(const sc_core::sc_stage &stage) { + sc_core::sc_time pending = sc_core::sc_time_stamp(); + if (sc_core::sc_pending_activity_at_future_time()) { + pending += sc_core::sc_time_to_pending_activity(); + } + + if (pending >= m_scheduled) { + m_th.resume(); + sc_core::sc_unregister_stage_callback(*this, sc_core::SC_POST_UPDATE); + } + } + + ~sc_ob_event() {} +}; +} // namespace sc_core + +#endif // _SC_OB_EVENT_ \ No newline at end of file diff --git a/src/sysc/utils/sc_on_context.h b/src/sysc/utils/sc_on_context.h new file mode 100644 index 000000000..6583ea3b3 --- /dev/null +++ b/src/sysc/utils/sc_on_context.h @@ -0,0 +1,275 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_on_context.h -- Job handler to run on context + + Original Author: Mark burton + + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#ifndef SC_ON_CONTEXT_H +#define SC_ON_CONTEXT_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sc_core { + +class sc_on_context : public sc_core::sc_module +{ +protected: + class AsyncJob + { + public: + using Ptr = std::shared_ptr; + + private: + std::packaged_task m_task; + + bool m_cancelled = false; + + void run_job() { m_task(); } + + public: + AsyncJob(std::function&& job): m_task(job) {} + + AsyncJob(std::function& job): m_task(job) {} + + AsyncJob() = delete; + AsyncJob(const AsyncJob&) = delete; + + void operator()() { run_job(); } + + /** + * @brief Cancel a job + * + * @details Cancel a job by setting m_cancelled to true and by + * resetting the task. Any waiter will then be unblocked immediately. + */ + void cancel() + { + m_cancelled = true; + m_task.reset(); + } + + void wait() + { + auto future = m_task.get_future(); + + while (!m_cancelled && + future.wait_for(std::chrono::milliseconds(500))==std::future_status::timeout) + {} + + if (!m_cancelled) { + future.get(); + } + } + + bool is_cancelled() const { return m_cancelled; } + }; + + + sc_core::sc_simcontext *m_simc=nullptr; + + /* Async job queue */ + std::queue m_async_jobs; + AsyncJob::Ptr m_running_job; + std::mutex m_async_jobs_mutex; + + sc_event m_jobs_handler_event; // NB this event must be threadsafe. + std::atomic running = true; + + // Process inside a thread incase the job calls wait + void jobs_handler() + { + std::unique_lock lock(m_async_jobs_mutex); + running = true; + for (; running;) { + while (running && !m_async_jobs.empty()) { + m_running_job = m_async_jobs.front(); + m_async_jobs.pop(); + + lock.unlock(); + sc_core::sc_unsuspendable(); // a wait in the job will cause systemc time to advance + (*m_running_job)(); + sc_core::sc_suspendable(); + lock.lock(); + + m_running_job.reset(); + } + lock.unlock(); + wait(m_jobs_handler_event); + lock.lock(); + } + SC_REPORT_WARNING("sc_on_context", "Stopped"); + sc_core::sc_stop(); + } + + void cancel_pendings_locked() + { + while (!m_async_jobs.empty()) { + m_async_jobs.front()->cancel(); + m_async_jobs.pop(); + } + } + +public: + sc_on_context(const sc_core::sc_module_name& n = sc_core::sc_module_name("sc_on_ctx")) + : sc_module(n) + { + m_simc = sc_core::sc_get_curr_simcontext(); + SC_THREAD(jobs_handler); + } + + /** + * @brief Cancel all pending jobs + * + * @detail Cancel all the pending jobs. The callers will be unblocked + * if they are waiting for the job. + */ + void cancel_pendings() + { + std::lock_guard lock(m_async_jobs_mutex); + + cancel_pendings_locked(); + } + + /** + * @brief Cancel all pending and running jobs + * + * @detail Cancel all the pending jobs and the currently running job. + * The callers will be unblocked if they are waiting for the + * job. Note that if the currently running job is resumed, the + * behaviour is undefined. This method is meant to be called + * after simulation has ended. + */ + void cancel_all() + { + std::lock_guard lock(m_async_jobs_mutex); + + cancel_pendings_locked(); + + if (m_running_job) { + m_running_job->cancel(); + m_running_job.reset(); + } + } + void stop() + { + running = false; + m_jobs_handler_event.notify(sc_core::SC_ZERO_TIME); + } + + void end_of_simulation() + { + running = false; + cancel_all(); + } + + void fork(std::function job_entry) { run(job_entry, false); } + + /** + * @brief Run a job on the SystemC kernel thread + * + * @param[in] job_entry The job to run + * @param[in] wait If true, wait for job completion + * + * @return true if the job has been succesfully executed or if `wait` + * was false, false if it has been cancelled (see + * `Sc_on_context::cancel_all`). + */ + bool run(std::function job_entry, bool wait = true) + { + if (!running) return false; + if (on_owning_ctx()) { + job_entry(); + return true; + } else { + AsyncJob::Ptr job(new AsyncJob(job_entry)); + + { + std::lock_guard lock(m_async_jobs_mutex); + if (running) { + m_async_jobs.push(job); + } else { + return false; + } + } + + m_jobs_handler_event.notify(); + + if (wait) { + /* Wait for job completion */ + try { + job->wait(); + } catch (std::runtime_error const& e) { + /* Report unknown runtime errors, without causing a futher excetion */ + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR( + "Sc_on_context", + ("Run on systemc received a runtime error from job: " + std::string(e.what())).c_str()); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + stop(); + return false; + } catch (const std::exception& exc) { + if (sc_core::sc_report_handler::get_count(sc_core::SC_ERROR) == 0) { + /* Report exceptions that were not caused by SC_ERRORS (which have already been reported)*/ + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR( + "Sc_on_context", + ("Run on systemc received an exception from job: " + std::string(exc.what())).c_str()); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + } + stop(); + return false; + } catch (...) { + auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, + sc_core::SC_LOG | sc_core::SC_DISPLAY); + SC_REPORT_ERROR("Sc_on_context", "Run on systemc received an unknown exception from job"); + sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old); + stop(); + return false; + } + + return !job->is_cancelled(); + } + + return true; + } + } + + /** + * @return Whether we are on the right SystemC context + */ + bool on_owning_ctx() const { return sc_core::sc_get_curr_simcontext()==m_simc; } +}; +} +#endif // SC_ON_CONTEXT_H \ No newline at end of file diff --git a/src/sysc/utils/sc_report_handler.cpp b/src/sysc/utils/sc_report_handler.cpp index eb48b58e9..7b2da8241 100644 --- a/src/sysc/utils/sc_report_handler.cpp +++ b/src/sysc/utils/sc_report_handler.cpp @@ -42,7 +42,7 @@ namespace std {} namespace sc_core { -int sc_report_handler::verbosity_level = SC_MEDIUM; +thread_local int sc_report_handler::verbosity_level = SC_MEDIUM; // not documented, but available const std::string sc_report_compose_message(const sc_report& rep) @@ -178,7 +178,7 @@ ::std::ofstream& sc_log_file_handle::operator*() { return log_stream; } -static sc_log_file_handle log_stream; +static thread_local sc_log_file_handle log_stream; // @@ -689,6 +689,48 @@ const char * sc_report_handler::get_log_file_name() return log_file_name; } +// Snapshot of the current thread's report-handler configuration, for +// hand-off to a freshly-spawned thread (sc_concurrent's child simcontext). +sc_report_handler::config_snapshot +sc_report_handler::snapshot_config() +{ + config_snapshot s; + s.handler = handler; + s.verbosity = verbosity_level; + s.suppress = suppress_mask; + s.force = force_mask; + s.catch_ = catch_actions; + s.available = available_actions; + for (int i = 0; i < SC_MAX_SEVERITY; ++i) { + s.actions_per_sev[i] = sev_actions[i]; + s.limit_per_sev[i] = sev_limit[i]; + } + if (log_file_name) s.log_file_name = log_file_name; + return s; +} + +void +sc_report_handler::apply_config(const config_snapshot& s) +{ + handler = s.handler; + verbosity_level = s.verbosity; + suppress_mask = s.suppress; + force_mask = s.force; + catch_actions = s.catch_; + available_actions = s.available; + for (int i = 0; i < SC_MAX_SEVERITY; ++i) { + sev_actions[i] = s.actions_per_sev[i]; + sev_limit[i] = s.limit_per_sev[i]; + } + if (!s.log_file_name.empty()) { + // set_log_file_name allocates via malloc. Free any previous + // (default thread_local) value first to avoid the "already set" + // refusal in set_log_file_name. + if (log_file_name) { free(log_file_name); log_file_name = 0; } + set_log_file_name(s.log_file_name.c_str()); + } +} + void sc_report_handler::cache_report(const sc_report& rep) { sc_process_b * proc = sc_get_current_process_b(); @@ -730,10 +772,10 @@ int sc_report_handler::set_verbosity_level( int level ) // static variables // -sc_actions sc_report_handler::suppress_mask = 0; -sc_actions sc_report_handler::force_mask = 0; +thread_local sc_actions sc_report_handler::suppress_mask = 0; +thread_local sc_actions sc_report_handler::force_mask = 0; -sc_actions sc_report_handler::sev_actions[SC_MAX_SEVERITY] = +thread_local sc_actions sc_report_handler::sev_actions[SC_MAX_SEVERITY] = { /* info */ SC_DEFAULT_INFO_ACTIONS, /* warn */ SC_DEFAULT_WARNING_ACTIONS, @@ -743,14 +785,14 @@ sc_actions sc_report_handler::sev_actions[SC_MAX_SEVERITY] = // Note that SC_FATAL has a limit of 1 by default -sc_actions sc_report_handler::sev_limit[SC_MAX_SEVERITY] = +thread_local sc_actions sc_report_handler::sev_limit[SC_MAX_SEVERITY] = { UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX }; -sc_actions sc_report_handler::sev_call_count[SC_MAX_SEVERITY] = { 0, 0, 0, 0 }; +thread_local sc_actions sc_report_handler::sev_call_count[SC_MAX_SEVERITY] = { 0, 0, 0, 0 }; -sc_report* sc_report_handler::last_global_report = NULL; -sc_actions sc_report_handler::available_actions = +thread_local sc_report* sc_report_handler::last_global_report = NULL; +thread_local sc_actions sc_report_handler::available_actions = SC_DO_NOTHING | SC_THROW | SC_LOG | @@ -760,16 +802,16 @@ sc_actions sc_report_handler::available_actions = SC_STOP | SC_ABORT; -sc_report_handler_proc sc_report_handler::handler = +thread_local sc_report_handler_proc sc_report_handler::handler = &sc_report_handler::default_handler; -char * sc_report_handler::log_file_name = 0; +thread_local char * sc_report_handler::log_file_name = 0; -sc_report_handler::msg_def_items * sc_report_handler::messages = +thread_local sc_report_handler::msg_def_items * sc_report_handler::messages = &sc_report_handler::msg_terminator; -sc_actions sc_report_handler::catch_actions = SC_DEFAULT_CATCH_ACTIONS; +thread_local sc_actions sc_report_handler::catch_actions = SC_DEFAULT_CATCH_ACTIONS; sc_actions sc_report_handler::set_catch_actions(sc_actions act) { diff --git a/src/sysc/utils/sc_report_handler.h b/src/sysc/utils/sc_report_handler.h index 541aea33f..a7756e24e 100644 --- a/src/sysc/utils/sc_report_handler.h +++ b/src/sysc/utils/sc_report_handler.h @@ -30,6 +30,8 @@ #ifndef SC_REPORT_HANDLER_H #define SC_REPORT_HANDLER_H +#include + namespace sc_core { // ---------------------------------------------------------------------------- @@ -118,6 +120,26 @@ class SC_API sc_report_handler static bool set_log_file_name(const char* filename); static const char* get_log_file_name(); + // Snapshot of this thread's report-handler configuration, used to + // inherit settings onto a freshly-spawned thread (sc_concurrent's + // child simcontext). Call snapshot_config() on the parent thread + // before the child starts; call apply_config() first thing on the + // child thread. Counters (sev_call_count) are deliberately NOT + // in the snapshot — a new thread starts with fresh counters. + struct config_snapshot { + sc_report_handler_proc handler; + int verbosity; + sc_actions suppress; + sc_actions force; + sc_actions catch_; + sc_actions available; + sc_actions actions_per_sev[SC_MAX_SEVERITY]; + unsigned limit_per_sev[SC_MAX_SEVERITY]; + std::string log_file_name; // empty == none + }; + static config_snapshot snapshot_config(); + static void apply_config(const config_snapshot&); + // how the implementation should handle caught (sc_report) exceptions static sc_actions set_catch_actions(sc_actions); static sc_actions get_catch_actions(); @@ -140,21 +162,31 @@ class SC_API sc_report_handler static void cache_report(const sc_report&); static sc_actions execute(sc_msg_def*, sc_severity); - static sc_actions suppress_mask; - static sc_actions force_mask; - static sc_actions sev_actions[SC_MAX_SEVERITY]; - static unsigned sev_limit[SC_MAX_SEVERITY]; - static unsigned sev_call_count[SC_MAX_SEVERITY]; - static sc_report* last_global_report; - static sc_actions available_actions; - static sc_actions catch_actions; - static char* log_file_name; - static int verbosity_level; - - static msg_def_items* messages; - static msg_def_items msg_terminator; - - static sc_report_handler_proc handler; + // Report-handler state is thread_local so that concurrent calls from + // parallel simcontexts do not race on counters, masks, or the handler + // pointer. Consequence: set_handler / set_verbosity_level / set_actions + // only affect the thread they are called from. A user that wants + // cross-thread policy (unified counters, ordered log output, one handler + // everywhere) should install a thread-safe handler from each thread; + // the back-end is where that coordination belongs. + // + // msg_terminator is read-only shared (compile-time initialised, never + // written), so it does NOT need to be thread_local. + static thread_local sc_actions suppress_mask; + static thread_local sc_actions force_mask; + static thread_local sc_actions sev_actions[SC_MAX_SEVERITY]; + static thread_local unsigned sev_limit[SC_MAX_SEVERITY]; + static thread_local unsigned sev_call_count[SC_MAX_SEVERITY]; + static thread_local sc_report* last_global_report; + static thread_local sc_actions available_actions; + static thread_local sc_actions catch_actions; + static thread_local char* log_file_name; + static thread_local int verbosity_level; + + static thread_local msg_def_items* messages; + static msg_def_items msg_terminator; + + static thread_local sc_report_handler_proc handler; static sc_msg_def* mdlookup(const char* msg_type); diff --git a/src/sysc/utils/sc_sync_window.h b/src/sysc/utils/sc_sync_window.h new file mode 100644 index 000000000..0bfab66ec --- /dev/null +++ b/src/sysc/utils/sc_sync_window.h @@ -0,0 +1,222 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_sync_window.h -- keeps SystemC within a 'window' of time + + Original Author: Mark burton + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable + CHANGE LOG AT END OF FILE + *****************************************************************************/ + +#include +#include +#include +#include + +namespace sc_core { + +/** + * @brief Base class which gives the quantum (dynamically) and specifies if the + * window should stay open indefinitely or detach when there are no more events. + * A SC_ZERO_TIME quantum means sync on every time step + */ +struct sc_sync_policy_base { + virtual sc_core::sc_time quantum() = 0; + virtual bool keep_alive() = 0; +}; +struct sc_sync_policy_tlm_quantum : public sc_sync_policy_base { + sc_core::sc_time quantum() { + return tlm_utils::tlm_quantumkeeper::get_global_quantum(); + } + bool keep_alive() { return true; } +}; +struct sc_sync_policy_in_sync : public sc_sync_policy_base { + sc_core::sc_time quantum() { + return sc_core::sc_pending_activity() + ? sc_core::sc_time_to_pending_activity() + : sc_core::SC_ZERO_TIME; + } + bool keep_alive() { return false; } +}; +/** + * @brief windowed synchronizer, template sc_sync_policy gives the quantum + * (dynamically) and specifies if the window should stay open indefinitely or + * detach when there are no more events. + * + * @tparam sc_sync_policy + */ +template +class sc_sync_windowed : public sc_core::sc_module, + public sc_core::sc_prim_channel { + static_assert(std::is_base_of::value, + "sc_sync_policy must derive from sc_sync_policy_base"); + + sc_core::sc_event m_sweep_ev; + sc_ob_event m_step_ev; + sc_event m_update_ev; + std::mutex m_mutex; + sc_sync_policy policy; + +public: + struct window { + sc_core::sc_time from; + sc_core::sc_time to; + bool operator==(const window &other) const { + return other.to == to && other.from == from; + } + }; + + static inline const struct window zero_window = {sc_core::SC_ZERO_TIME, + sc_core::SC_ZERO_TIME}; + static inline const struct window open_window = {sc_core::SC_ZERO_TIME, + sc_core::sc_max_time()}; + +private: + window m_window; + window m_incomming_window; // used to hold the window values coming in from + // the other side. + + std::function m_other_async_set_window_fn; + + void do_other_async_set_window_fn(window w) { + if (m_other_async_set_window_fn) { + m_other_async_set_window_fn(w); + } + } + + /* Handle suspending/resuming at the end of the window, also inform other side + * if we reach end of window */ + void step_helper() { + auto now = sc_core::sc_time_stamp(); + auto to = m_window.to; + + /* The step helper has to handle both suspend and resume (because of + * SystemC) */ + if (now >= to) { + sc_core::sc_unsuspend_all(); // such that pending activity is valid if + // it's needed below. + + /* We should suspend at this point, and wait for the other side to catch + * up */ + do_other_async_set_window_fn({now, now + policy.quantum()}); + + if (!policy.keep_alive()) + async_attach_suspending(); + sc_core::sc_suspend_all(); + + } else { + /* the only way to get here is if we have a 'new' window from the other + * side. we are here just to unsuspend */ + sc_core::sc_unsuspend_all(); + if (!policy.keep_alive()) + async_detach_suspending(); + //do_other_async_set_window_fn({now, now + policy.quantum()}); + + /* Re-notify event - maybe presumably moved */ + m_step_ev.notify(to - now); + } + } + + /* + * Handle all sweep requests, once we arrive at a sweep point, tell the other + * side. + */ + void sweep_helper() { + auto now = sc_core::sc_time_stamp(); + do_other_async_set_window_fn({now, now + policy.quantum()}); + } + + /* Manage the Sync aync update */ + void update() { + std::lock_guard lg(m_mutex); + // Now we are on our thread, it's safe to update our window. + m_window = m_incomming_window; + auto now = sc_core::sc_time_stamp(); + + if (m_window.from > now) { + m_sweep_ev.notify(m_window.from - now); // Try to move time forward. + } else { + m_sweep_ev.cancel(); // no need to fire event. + } + /* let stepper handle suspend/resume, must time notify */ + m_update_ev.notify(sc_core::SC_ZERO_TIME); +// std::ostringstream s; +// s << "Got Window: " << m_window.from << " - " << m_window.to; +// SC_REPORT_INFO(sc_core::sc_module::name(), s.str().c_str()); + } + +public: + /* API call from other pair, presumably in a different thread. + * The internal window wil be updated atomically. + * + * Input: window - Window to set for sync. Sweep till the 'from' and step to + * the 'to'. + */ + void async_set_window(const window &w) { + /* Only accept updated windows so we dont re-send redundant updates + * safe at this point to compair against m_window as we took the lock + */ + std::lock_guard lg(m_mutex); + m_incomming_window = w; + if (!(w == m_window)) { + async_request_update(); + } + } + void detach() { + async_detach_suspending(); + m_other_async_set_window_fn(open_window); + } + void bind(sc_sync_windowed *other) { + if (m_other_async_set_window_fn) { + SC_REPORT_WARNING( + "sc_sync_window", + "m_other_async_set_window_fn was already registered or other " + "sc_sync_windowed was already bound!"); + } + m_other_async_set_window_fn = std::bind(&sc_sync_windowed::async_set_window, other, std::placeholders::_1); + } + void register_sync_cb(std::function fn) { + if (m_other_async_set_window_fn) { + SC_REPORT_WARNING( + "sc_sync_window", + "m_other_async_set_window_fn was already registered or other " + "sc_sync_windowed was already bound!"); + } + m_other_async_set_window_fn = fn; + } + SC_CTOR(sc_sync_windowed) + : m_window({sc_core::SC_ZERO_TIME, policy.quantum()}) { + + SC_METHOD(sweep_helper); + dont_initialize(); + sensitive << m_sweep_ev; + + SC_METHOD(step_helper); + dont_initialize(); + sensitive << m_step_ev << m_update_ev; + + m_step_ev.notify(policy.quantum()); + + this->sc_core::sc_prim_channel::async_attach_suspending(); + } +}; + +} // namespace sc_core \ No newline at end of file diff --git a/src/systemc b/src/systemc index 23d131175..a30e05a0c 100644 --- a/src/systemc +++ b/src/systemc @@ -134,6 +134,8 @@ #include "sysc/utils/sc_vector.h" #include "sysc/utils/sc_string.h" +#include "sysc/log/sc_log.h" + #endif // !defined(SYSTEMC_INCLUDED) #ifdef SC_INCLUDE_EXTRA_STD_HEADERS diff --git a/src/tlm_utils/simple_target_socket.h b/src/tlm_utils/simple_target_socket.h index 82198fbb6..52b4eedbc 100644 --- a/src/tlm_utils/simple_target_socket.h +++ b/src/tlm_utils/simple_target_socket.h @@ -37,6 +37,7 @@ #include #include "tlm_utils/convenience_socket_bases.h" #include "tlm_utils/peq_with_get.h" +#include "sysc/utils/sc_on_context.h" namespace tlm_utils { @@ -174,6 +175,7 @@ class simple_target_socket_b class fw_process : public tlm::tlm_fw_transport_if, public tlm::tlm_mm_interface { + sc_core::sc_on_context m_on_ctx; public: typedef sync_enum_type (MODULE::*NBTransportPtr)(transaction_type&, phase_type&, @@ -257,7 +259,14 @@ class simple_target_socket_b if (m_nb_transport_ptr) { // forward call sc_assert(m_mod); - return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + if (!m_on_ctx.on_owning_ctx()) + { + sync_enum_type tmp; + m_on_ctx.run([this, &tmp, &trans, &phase, &t](){ tmp=(m_mod->*m_nb_transport_ptr)(trans, phase, t);}); + return tmp; + } else { + return (m_mod->*m_nb_transport_ptr)(trans, phase, t); + } } // nb->b conversion @@ -300,7 +309,15 @@ class simple_target_socket_b if (m_b_transport_ptr) { // forward call sc_assert(m_mod); - (m_mod->*m_b_transport_ptr)(trans, t); + if (!m_on_ctx.on_owning_ctx()) { + sc_core::sc_time our_time=sc_core::sc_time_stamp(); + sc_core::sc_time their_time; + m_on_ctx.run( + [this, &trans, &t, &our_time, &their_time]() { if (our_time>sc_core::sc_time_stamp()) wait(our_time-sc_core::sc_time_stamp()); (m_mod->*m_b_transport_ptr)(trans, t); their_time=sc_core::sc_time_stamp(); }); + if (their_time>sc_core::sc_time_stamp()) wait(their_time-sc_core::sc_time_stamp()); + } else { + (m_mod->*m_b_transport_ptr)(trans, t); + } return; } diff --git a/tests/include/tlm/CoreDecouplingLTInitiator.h b/tests/include/tlm/CoreDecouplingLTInitiator.h index 2a1965c1f..1090a4c4d 100644 --- a/tests/include/tlm/CoreDecouplingLTInitiator.h +++ b/tests/include/tlm/CoreDecouplingLTInitiator.h @@ -132,22 +132,24 @@ class CoreDecouplingLTInitiator : public sc_core::sc_module void run() { - transaction_type trans; - - while (initTransaction(trans)) { - logStartTransation(trans); - - // exec instr - sc_core::sc_time t = mQuantumKeeper.get_local_time(); - socket->b_transport(trans, t); - mQuantumKeeper.set(t); - // Target may have added a delay to the quantum -> sync if needed - if (mQuantumKeeper.need_sync()) { - std::cout << "Sync'ing..." << std::endl; - mQuantumKeeper.sync(); + { + transaction_type trans; + + while (initTransaction(trans)) { + logStartTransation(trans); + + // exec instr + sc_core::sc_time t = mQuantumKeeper.get_local_time(); + socket->b_transport(trans, t); + mQuantumKeeper.set(t); + // Target may have added a delay to the quantum -> sync if needed + if (mQuantumKeeper.need_sync()) { + std::cout << "Sync'ing..." << std::endl; + mQuantumKeeper.sync(); + } + + logEndTransaction(trans); } - - logEndTransaction(trans); } wait(); } diff --git a/tests/include/tlm/SimpleLTInitiator1.h b/tests/include/tlm/SimpleLTInitiator1.h index 38660f7ee..e8b753d6b 100644 --- a/tests/include/tlm/SimpleLTInitiator1.h +++ b/tests/include/tlm/SimpleLTInitiator1.h @@ -124,14 +124,17 @@ class SimpleLTInitiator1 : void run() { - transaction_type trans; - sc_core::sc_time t(sc_core::SC_ZERO_TIME); - while (initTransaction(trans)) { - logStartTransation(trans); - socket->b_transport(trans, t); - wait(t); - logEndTransaction(trans); - t = sc_core::SC_ZERO_TIME; + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + transaction_type trans; + sc_core::sc_time t(sc_core::SC_ZERO_TIME); + while (initTransaction(trans)) { + logStartTransation(trans); + socket->b_transport(trans, t); + wait(t); + logEndTransaction(trans); + t = sc_core::SC_ZERO_TIME; + } } wait(); diff --git a/tests/include/tlm/SimpleLTInitiator1_DMI.h b/tests/include/tlm/SimpleLTInitiator1_DMI.h index 00ec7c582..898feeb05 100644 --- a/tests/include/tlm/SimpleLTInitiator1_DMI.h +++ b/tests/include/tlm/SimpleLTInitiator1_DMI.h @@ -131,73 +131,75 @@ class SimpleLTInitiator1_dmi : void run() { - transaction_type trans; - phase_type phase; - sc_core::sc_time t; - - while (initTransaction(trans)) { - // Create transaction and initialise phase and t - phase = tlm::BEGIN_REQ; - t = sc_core::SC_ZERO_TIME; - - logStartTransation(trans); - - /////////////////////////////////////////////////////////// - // DMI handling: - // We use the DMI hint to check if it makes sense to ask for - // DMI pointers. The pattern is: - // - if the address is covered by a DMI region do a DMI access - // - otherwise do a normal transaction - // -> check if we get a DMI hint and acquire the DMI pointers if it is - // set - /////////////////////////////////////////////////////////// - - // Check if the address is covered by our DMI region - if ( (trans.get_address() >= mDMIData.get_start_address()) && - (trans.get_address() <= mDMIData.get_end_address()) ) { - // We can handle the data here. As the logEndTransaction is assuming - // something to happen in the data structure, we really need to - // do this: - trans.set_response_status(tlm::TLM_OK_RESPONSE); - sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; - - } else { - mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; - } - - // Do the wait immediately. Note that doing the wait here eats almost - // all the performance anyway, so we only gain something if we're - // using temporal decoupling. - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - wait(mDMIData.get_write_latency()); - - } else { - wait(mDMIData.get_read_latency()); - } - - logEndTransaction(trans); + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + transaction_type trans; + phase_type phase; + sc_core::sc_time t; + + while (initTransaction(trans)) { + // Create transaction and initialise phase and t + phase = tlm::BEGIN_REQ; + t = sc_core::SC_ZERO_TIME; + + logStartTransation(trans); + + /////////////////////////////////////////////////////////// + // DMI handling: + // We use the DMI hint to check if it makes sense to ask for + // DMI pointers. The pattern is: + // - if the address is covered by a DMI region do a DMI access + // - otherwise do a normal transaction + // -> check if we get a DMI hint and acquire the DMI pointers if it is + // set + /////////////////////////////////////////////////////////// + + // Check if the address is covered by our DMI region + if ( (trans.get_address() >= mDMIData.get_start_address()) && + (trans.get_address() <= mDMIData.get_end_address()) ) { + // We can handle the data here. As the logEndTransaction is assuming + // something to happen in the data structure, we really need to + // do this: + trans.set_response_status(tlm::TLM_OK_RESPONSE); + sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; + + } else { + mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; + } + // Do the wait immediately. Note that doing the wait here eats almost + // all the performance anyway, so we only gain something if we're + // using temporal decoupling. + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + wait(mDMIData.get_write_latency()); + + } else { + wait(mDMIData.get_read_latency()); + } - } else { // we need a full transaction - sc_dt::uint64 addr = trans.get_address(); //Save address before it is mutated - socket->b_transport(trans, t); - wait(t); - logEndTransaction(trans); - - // Acquire DMI pointer on is available: - if (trans.is_dmi_allowed()) - { - dmi_type tmp; - tmp.init(); - trans.set_address(addr); //restore address, in case it was mutated. - trans.set_write(); - if ( socket->get_direct_mem_ptr(trans, tmp) - && tmp.is_write_allowed() ) - { - mDMIData = tmp; - } - } + logEndTransaction(trans); + + } else { // we need a full transaction + sc_dt::uint64 addr = trans.get_address(); //Save address before it is mutated + socket->b_transport(trans, t); + wait(t); + logEndTransaction(trans); + + // Acquire DMI pointer on is available: + if (trans.is_dmi_allowed()) + { + dmi_type tmp; + tmp.init(); + trans.set_address(addr); //restore address, in case it was mutated. + trans.set_write(); + if ( socket->get_direct_mem_ptr(trans, tmp) + && tmp.is_write_allowed() ) + { + mDMIData = tmp; + } + } + } } } wait(); diff --git a/tests/include/tlm/SimpleLTInitiator2.h b/tests/include/tlm/SimpleLTInitiator2.h index 4fa2903ca..f9b48ac57 100644 --- a/tests/include/tlm/SimpleLTInitiator2.h +++ b/tests/include/tlm/SimpleLTInitiator2.h @@ -119,19 +119,22 @@ class SimpleLTInitiator2 : public sc_core::sc_module void run() { - transaction_type trans; - sc_core::sc_time t; + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + transaction_type trans; + sc_core::sc_time t; - while (initTransaction(trans)) { - // Create transaction and initialise t - t = sc_core::SC_ZERO_TIME; + while (initTransaction(trans)) { + // Create transaction and initialise t + t = sc_core::SC_ZERO_TIME; - logStartTransation(trans); + logStartTransation(trans); - socket->b_transport(trans, t); - wait(t); + socket->b_transport(trans, t); + wait(t); - logEndTransaction(trans); + logEndTransaction(trans); + } } wait(); diff --git a/tests/include/tlm/SimpleLTInitiator2_DMI.h b/tests/include/tlm/SimpleLTInitiator2_DMI.h index 7c5e4c463..c0091a148 100644 --- a/tests/include/tlm/SimpleLTInitiator2_DMI.h +++ b/tests/include/tlm/SimpleLTInitiator2_DMI.h @@ -141,75 +141,77 @@ class SimpleLTInitiator2_dmi : public sc_core::sc_module void run() { - transaction_type trans; - sc_core::sc_time t; - - while (initTransaction(trans)) { - // Create transaction and initialise t - t = sc_core::SC_ZERO_TIME; - - logStartTransation(trans); - - /////////////////////////////////////////////////////////// - // DMI handling: - // We do *not* use the DMI hint to check if it makes sense to ask for - // DMI pointers. So the pattern is: - // - if the address is not covered by a DMI region try to acquire DMI - // pointers - // - if we have a DMI pointer, do the DMI "transaction" - // - otherwise fall back to a normal transaction - /////////////////////////////////////////////////////////// - - std::pair& dmi_data = getDMIData(trans); - - // Check if we need to acquire a DMI pointer - if((trans.get_address() < dmi_data.first.get_start_address()) || - (trans.get_address() > dmi_data.first.get_end_address()) ) - { - sc_dt::uint64 address = trans.get_address(); //save original address - dmi_data.second = - socket->get_direct_mem_ptr(trans, - dmi_data.first); - trans.set_address(address); - } - // Do DMI "transaction" if we have a valid region - if (dmi_data.second && - (trans.get_address() >= dmi_data.first.get_start_address()) && - (trans.get_address() <= dmi_data.first.get_end_address()) ) - { - // We can handle the data here. As the logEndTransaction is assuming - // something to happen in the data structure, we really need to - // do this: - trans.set_response_status(tlm::TLM_OK_RESPONSE); - sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address(); - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) - { - *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData; - } - else - { - mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp]; - } - - // Do the wait immediately. Note that doing the wait here eats almost - // all the performance anyway, so we only gain something if we're - // using temporal decoupling. - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - wait(dmi_data.first.get_write_latency()); - - } else { - wait(dmi_data.first.get_read_latency()); - } - } - else // we need a full transaction - { - socket->b_transport(trans, t); - wait(t); + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + transaction_type trans; + sc_core::sc_time t; + + while (initTransaction(trans)) { + // Create transaction and initialise t + t = sc_core::SC_ZERO_TIME; + + logStartTransation(trans); + + /////////////////////////////////////////////////////////// + // DMI handling: + // We do *not* use the DMI hint to check if it makes sense to ask for + // DMI pointers. So the pattern is: + // - if the address is not covered by a DMI region try to acquire DMI + // pointers + // - if we have a DMI pointer, do the DMI "transaction" + // - otherwise fall back to a normal transaction + /////////////////////////////////////////////////////////// + + std::pair& dmi_data = getDMIData(trans); + + // Check if we need to acquire a DMI pointer + if((trans.get_address() < dmi_data.first.get_start_address()) || + (trans.get_address() > dmi_data.first.get_end_address()) ) + { + sc_dt::uint64 address = trans.get_address(); //save original address + dmi_data.second = + socket->get_direct_mem_ptr(trans, + dmi_data.first); + trans.set_address(address); + } + // Do DMI "transaction" if we have a valid region + if (dmi_data.second && + (trans.get_address() >= dmi_data.first.get_start_address()) && + (trans.get_address() <= dmi_data.first.get_end_address()) ) + { + // We can handle the data here. As the logEndTransaction is assuming + // something to happen in the data structure, we really need to + // do this: + trans.set_response_status(tlm::TLM_OK_RESPONSE); + sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address(); + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) + { + *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData; + } + else + { + mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp]; + } + + // Do the wait immediately. Note that doing the wait here eats almost + // all the performance anyway, so we only gain something if we're + // using temporal decoupling. + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + wait(dmi_data.first.get_write_latency()); + + } else { + wait(dmi_data.first.get_read_latency()); + } + } + else // we need a full transaction + { + socket->b_transport(trans, t); + wait(t); + } + logEndTransaction(trans); } - logEndTransaction(trans); } wait(); - } // Invalidate DMI pointer(s) diff --git a/tests/include/tlm/SimpleLTInitiator3.h b/tests/include/tlm/SimpleLTInitiator3.h index 13a9491c9..74b4c1e42 100644 --- a/tests/include/tlm/SimpleLTInitiator3.h +++ b/tests/include/tlm/SimpleLTInitiator3.h @@ -119,20 +119,22 @@ class SimpleLTInitiator3 : public sc_core::sc_module void run() { - transaction_type trans; - sc_core::sc_time t; + { + transaction_type trans; + sc_core::sc_time t; - while (initTransaction(trans)) { - // Create transaction and initialise t - t = sc_core::SC_ZERO_TIME; + while (initTransaction(trans)) { + // Create transaction and initialise t + t = sc_core::SC_ZERO_TIME; - logStartTransation(trans); + logStartTransation(trans); - socket->b_transport(trans, t); - // Transaction Finished, wait for the returned delay - wait(t); + socket->b_transport(trans, t); + // Transaction Finished, wait for the returned delay + wait(t); - logEndTransaction(trans); + logEndTransaction(trans); + } } wait(); diff --git a/tests/include/tlm/SimpleLTInitiator3_DMI.h b/tests/include/tlm/SimpleLTInitiator3_DMI.h index be8fa006b..0556e7116 100644 --- a/tests/include/tlm/SimpleLTInitiator3_DMI.h +++ b/tests/include/tlm/SimpleLTInitiator3_DMI.h @@ -139,78 +139,80 @@ class SimpleLTInitiator3_dmi : public sc_core::sc_module void run() { - transaction_type trans; - sc_core::sc_time t; - - while (initTransaction(trans)) { - // Create transaction and initialise t - t = sc_core::SC_ZERO_TIME; - - logStartTransation(trans); - - /////////////////////////////////////////////////////////// - // DMI handling: - // We do *not* use the DMI hint to check if it makes sense to ask for - // DMI pointers. So the pattern is: - // - if the address is not covered by a DMI region try to acquire DMI - // pointers - // - if we have a DMI pointer, do the DMI "transaction" - // - otherwise fall back to a normal transaction - /////////////////////////////////////////////////////////// - - std::pair& dmi_data = getDMIData(trans); - - // Check if we need to acquire a DMI pointer - if((trans.get_address() < dmi_data.first.get_start_address()) || - (trans.get_address() > dmi_data.first.get_end_address()) ) - { - sc_dt::uint64 address = trans.get_address(); //save original address - dmi_data.second = - socket->get_direct_mem_ptr(trans, - dmi_data.first); - trans.set_address(address); + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + transaction_type trans; + sc_core::sc_time t; + + while (initTransaction(trans)) { + // Create transaction and initialise t + t = sc_core::SC_ZERO_TIME; + + logStartTransation(trans); + + /////////////////////////////////////////////////////////// + // DMI handling: + // We do *not* use the DMI hint to check if it makes sense to ask for + // DMI pointers. So the pattern is: + // - if the address is not covered by a DMI region try to acquire DMI + // pointers + // - if we have a DMI pointer, do the DMI "transaction" + // - otherwise fall back to a normal transaction + /////////////////////////////////////////////////////////// + + std::pair& dmi_data = getDMIData(trans); + + // Check if we need to acquire a DMI pointer + if((trans.get_address() < dmi_data.first.get_start_address()) || + (trans.get_address() > dmi_data.first.get_end_address()) ) + { + sc_dt::uint64 address = trans.get_address(); //save original address + dmi_data.second = + socket->get_direct_mem_ptr(trans, + dmi_data.first); + trans.set_address(address); + } + // Do DMI "transaction" if we have a valid region + if (dmi_data.second && + (trans.get_address() >= dmi_data.first.get_start_address()) && + (trans.get_address() <= dmi_data.first.get_end_address()) ) + { + // We can handle the data here. As the logEndTransaction is assuming + // something to happen in the data structure, we really need to + // do this: + trans.set_response_status(tlm::TLM_OK_RESPONSE); + + sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address(); + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) + { + *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData; + } + else + { + mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp]; + } + + // Do the wait immediately. Note that doing the wait here eats almost + // all the performance anyway, so we only gain something if we're + // using temporal decoupling. + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + wait(dmi_data.first.get_write_latency()); + + } else { + wait(dmi_data.first.get_read_latency()); + } + } + else // we need a full transaction + { + socket->b_transport(trans, t); + // wait for the returned delay + wait(t); + } + + logEndTransaction(trans); } - // Do DMI "transaction" if we have a valid region - if (dmi_data.second && - (trans.get_address() >= dmi_data.first.get_start_address()) && - (trans.get_address() <= dmi_data.first.get_end_address()) ) - { - // We can handle the data here. As the logEndTransaction is assuming - // something to happen in the data structure, we really need to - // do this: - trans.set_response_status(tlm::TLM_OK_RESPONSE); - - sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address(); - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) - { - *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData; - } - else - { - mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp]; - } - - // Do the wait immediately. Note that doing the wait here eats almost - // all the performance anyway, so we only gain something if we're - // using temporal decoupling. - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - wait(dmi_data.first.get_write_latency()); - - } else { - wait(dmi_data.first.get_read_latency()); - } - } - else // we need a full transaction - { - socket->b_transport(trans, t); - // wait for the returned delay - wait(t); - } - - logEndTransaction(trans); } wait(); - } // Invalidate DMI pointer(s) diff --git a/tests/systemc/1666-2011-compliance/child_proc_control/child_proc_control.cpp b/tests/systemc/1666-2011-compliance/child_proc_control/child_proc_control.cpp index ab70ac7b9..07d2290b9 100644 --- a/tests/systemc/1666-2011-compliance/child_proc_control/child_proc_control.cpp +++ b/tests/systemc/1666-2011-compliance/child_proc_control/child_proc_control.cpp @@ -63,6 +63,17 @@ struct Top: sc_module c0[i] = c1[i] = c2[i] = c3[i] = c4[i] = c5[i] = 0; } } + + virtual ~Top() + { + delete [] given_birth; + delete [] c0; + delete [] c1; + delete [] c2; + delete [] c3; + delete [] c4; + delete [] c5; + } int count; int f0, f1; diff --git a/tests/systemc/1666-2011-compliance/event_list/event_list.cpp b/tests/systemc/1666-2011-compliance/event_list/event_list.cpp index 9b92438de..3893888a5 100644 --- a/tests/systemc/1666-2011-compliance/event_list/event_list.cpp +++ b/tests/systemc/1666-2011-compliance/event_list/event_list.cpp @@ -42,6 +42,7 @@ using std::endl; struct Mod: sc_module { sc_port, 0> p; // Multiport + sc_event_or_list all_events; Mod(sc_module_name _name) { @@ -53,7 +54,7 @@ struct Mod: sc_module { for (;;) { - wait(all_events()); + wait(all_events); cout << "M::T1 awoke with " << p[0]->read() << p[1]->read() << p[2]->read() << " at " << sc_time_stamp() << " on list" << endl; } @@ -67,30 +68,29 @@ struct Mod: sc_module << " at " << sc_time_stamp() << " on list" << endl; } } - sc_event_or_list all_events() const + + void end_of_elaboration() override { sc_assert( p.size() == 3 ); - sc_event_or_list or_list; for (int i = 0; i < p.size(); i++) - or_list |= p[i]->default_event(); + all_events |= p[i]->default_event(); - sc_assert( or_list.size() == 3 ); - return or_list; - } + sc_assert( all_events.size() == 3 ); + } }; struct Top: sc_module { Top(sc_module_name _name) - : finished(false) + : m("m") + , finished(false) , count(0) { - m = new Mod("m"); - m->p.bind(sig1); - m->p.bind(sig2); - m->p.bind(sig3); + m.p.bind(sig1); + m.p.bind(sig2); + m.p.bind(sig3); SC_THREAD(T); SC_METHOD(M); } @@ -105,7 +105,7 @@ struct Top: sc_module } sc_signal sig1, sig2, sig3; - Mod* m; + Mod m; sc_event e1, e2, e3, e4; bool finished; diff --git a/tests/systemc/1666-2011-compliance/method_with_reset/method_with_reset.cpp b/tests/systemc/1666-2011-compliance/method_with_reset/method_with_reset.cpp index cf1fb49ab..ebaff00a1 100644 --- a/tests/systemc/1666-2011-compliance/method_with_reset/method_with_reset.cpp +++ b/tests/systemc/1666-2011-compliance/method_with_reset/method_with_reset.cpp @@ -356,10 +356,12 @@ struct Top: sc_module event_list = m3.reset_event() | m3.terminated_event(); next_trigger(event_list); } + + // outside the SC_THREAD to make the leak sanitizer happy (co-routine stacks are not proper cleaned up) + sc_event_or_list or_list; void multiple_reset_handler() { - sc_event_or_list or_list; or_list |= m1.reset_event(); or_list |= m2.reset_event(); or_list |= m3.reset_event(); diff --git a/tests/systemc/1666-2011-compliance/mixed_child_procs/mixed_child_procs.cpp b/tests/systemc/1666-2011-compliance/mixed_child_procs/mixed_child_procs.cpp index 2e7a6f695..e54ce543d 100644 --- a/tests/systemc/1666-2011-compliance/mixed_child_procs/mixed_child_procs.cpp +++ b/tests/systemc/1666-2011-compliance/mixed_child_procs/mixed_child_procs.cpp @@ -77,6 +77,17 @@ struct Top: sc_module m = sc_spawn(sc_bind(&Top::child_method, this, index++, 3), "m", &opt); } + virtual ~Top() + { + delete [] given_birth; + delete [] f0; + delete [] f1; + delete [] f2; + delete [] f3; + delete [] f4; + delete [] f5; + } + sc_spawn_options opt; sc_process_handle t, m; std::exception ex; diff --git a/tests/systemc/1666-2011-compliance/sc_vector/sc_vector.cpp b/tests/systemc/1666-2011-compliance/sc_vector/sc_vector.cpp index 55cfd31b9..ad1eee047 100644 --- a/tests/systemc/1666-2011-compliance/sc_vector/sc_vector.cpp +++ b/tests/systemc/1666-2011-compliance/sc_vector/sc_vector.cpp @@ -323,9 +323,10 @@ struct Top: sc_module SC_THREAD(T2); } + // outside the SC_THREAD to make the leak sanitizer happy (co-routine stacks are not proper cleaned up) + sc_event_or_list list; void T() { - sc_event_or_list list; for (int i = 0; i < 4; i++) list |= sigs[i].default_event(); for (;;) diff --git a/tests/systemc/compliance_1666/test001/test001.cpp b/tests/systemc/compliance_1666/test001/test001.cpp index 429e7c21b..9e02c543a 100644 --- a/tests/systemc/compliance_1666/test001/test001.cpp +++ b/tests/systemc/compliance_1666/test001/test001.cpp @@ -254,6 +254,11 @@ SC_MODULE(Top) sensitive << *pp; //// Sensitivity separated from SC_METHOD DOULOS011 } + virtual ~Nested() + { + delete pp; + } + void action() { op = sc_min(3, (*pp).read() + 1); } sc_out op; //// Out-of-order declaration DOULOS052 @@ -308,6 +313,19 @@ SC_MODULE(Top) modb.p4(ms); } + virtual ~Top() + { + delete link->link->m; + delete link->link->s; + delete link->link->p; + delete link->link; + delete link->p; + delete link->m; + delete link; + + delete sig; + } + }; diff --git a/tests/systemc/compliance_1666/test234/test234.cpp b/tests/systemc/compliance_1666/test234/test234.cpp index 1b5467544..08fbf5b40 100644 --- a/tests/systemc/compliance_1666/test234/test234.cpp +++ b/tests/systemc/compliance_1666/test234/test234.cpp @@ -19,10 +19,17 @@ struct Chan: i_f, sc_object struct Port: sc_port { - sc_event_finder& find_event() const + Port() : m_event_finder(*this, &i_f::event) { - return *new sc_event_finder_t( *this, &i_f::event ); } + + sc_event_finder& find_event() + { + return m_event_finder; + } + + private: + sc_event_finder_t m_event_finder; }; SC_MODULE(M) @@ -75,14 +82,13 @@ SC_MODULE(M) SC_MODULE(Top) { - M *m; + M m; Chan chan1, chan2, chan3; - SC_CTOR(Top) + SC_CTOR(Top): m("m") { - m = new M("m"); - m->mp(chan1); - m->mp(chan2); - m->mp(chan3); + m.mp(chan1); + m.mp(chan2); + m.mp(chan3); SC_THREAD(T); } void T() diff --git a/tests/systemc/concurrent/child_stop/child_stop.cpp b/tests/systemc/concurrent/child_stop/child_stop.cpp new file mode 100644 index 000000000..5becb8b7f --- /dev/null +++ b/tests/systemc/concurrent/child_stop/child_stop.cpp @@ -0,0 +1,123 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + child_stop.cpp -- Verify that sc_stop called from inside a + SC_ALLOW_CONCURRENCY module (running on the child simcontext's + thread) cleanly stops the whole simulation including the parent. + + Without the child->parent stop propagation, the parent would keep + running until its own events run out. We set up a parent module + that would otherwise run indefinitely (ticker that keeps arming + its own event), so if the child's sc_stop does not propagate, the + test hangs and the outer timeout kills it. + + Pass criteria: + * sc_start returns within the test timeout. + * The child's stop intent is observed (child_stopped flag). + * The parent's own ticker ran at least once but did NOT exhaust + itself, confirming the parent was stopped by the child rather + than by starvation. + + Exits 0 on pass, 1 on fail. + + *****************************************************************************/ + +#include +#include "sysc/utils/sc_concurrent.h" + +#include + +SC_MODULE(child_mod) +{ + bool* m_child_stopped; + + void run() + { + // Run for a few ms, then call sc_stop from the child's thread. + wait(5, sc_core::SC_MS); + *m_child_stopped = true; + sc_stop(); + } + + SC_HAS_PROCESS(child_mod); + child_mod(sc_module_name n, bool* stop_flag) + : sc_module(n), m_child_stopped(stop_flag) + { + SC_THREAD(run); + } +}; + +SC_MODULE(parent_ticker) +{ + int* m_tick_count; + sc_event m_tick; + + void tick() + { + ++(*m_tick_count); + // re-arm: this module would run forever if never stopped + m_tick.notify(sc_core::sc_time(1, sc_core::SC_MS)); + } + + SC_HAS_PROCESS(parent_ticker); + parent_ticker(sc_module_name n, int* tick_count) + : sc_module(n), m_tick_count(tick_count) + { + SC_METHOD(tick); + sensitive << m_tick; + // kick off + m_tick.notify(sc_core::sc_time(1, sc_core::SC_MS)); + } +}; + +int sc_main(int, char*[]) +{ + tlm_utils::tlm_quantumkeeper::set_global_quantum( + sc_core::sc_time(1, sc_core::SC_MS)); + + bool child_stopped = false; + int parent_ticks = 0; + + SC_ALLOW_CONCURRENCY(child_mod, sc_core::sc_sync_policy_tlm_quantum) + child("child", &child_stopped); + parent_ticker parent("parent", &parent_ticks); + + sc_start(); + + // Parent's ticker re-arms itself every 1 ms. If child's sc_stop + // did not propagate, the parent would tick forever. A bounded + // tick count + the outer timeout together catch both the "did + // not stop" case and the "stopped too early" case. + bool ok = + child_stopped && + parent_ticks >= 1 && + parent_ticks < 1000; + + // parent_ticks is timing-dependent (depends on how quickly the + // child schedules sc_stop), so it's deliberately omitted from the + // printed verdict to keep the golden log deterministic. The + // bounds check above is the real pass condition. + std::cout << "child_stopped=" << (child_stopped ? "yes" : "no") + << " result=" << (ok ? "PASS" : "FAIL") + << std::endl; + + return ok ? 0 : 1; +} diff --git a/tests/systemc/concurrent/child_stop/golden/child_stop.log b/tests/systemc/concurrent/child_stop/golden/child_stop.log new file mode 100644 index 000000000..a8e419c2c --- /dev/null +++ b/tests/systemc/concurrent/child_stop/golden/child_stop.log @@ -0,0 +1,6 @@ +SystemC Simulation + +Info: /OSCI/SystemC: Simulation stopped by user. + +Info: /OSCI/SystemC: Simulation stopped by user. +child_stopped=yes result=PASS diff --git a/tests/systemc/concurrent/cross_context_write/cross_context_write.cpp b/tests/systemc/concurrent/cross_context_write/cross_context_write.cpp new file mode 100644 index 000000000..8d1ad9cc7 --- /dev/null +++ b/tests/systemc/concurrent/cross_context_write/cross_context_write.cpp @@ -0,0 +1,120 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + cross_context_write.cpp -- Verify sc_signal::write from a foreign + simcontext (the writer's process belongs to a SC_ALLOW_CONCURRENCY + module) lands correctly on the signal's owning simcontext, and the + reader (in the parent simcontext) observes every write. + + Producer is wrapped in SC_ALLOW_CONCURRENCY -- it lives in a child + simcontext. Consumer is in the parent. The signal that connects + them is constructed at sc_main scope (parent). Producer's + SC_THREAD writes a strictly-increasing sequence; Consumer's + SC_METHOD is sensitive to the signal and records every value seen. + After the expected number of values arrive, Consumer calls sc_stop. + + Pass criteria: + * Consumer method fires at least N times (one per write). + * Each successive observed value is strictly greater than the + previous (no torn read, no out-of-order values). + * The final observed value equals N. + + Exits 0 on pass, 1 on fail. + + *****************************************************************************/ + +#include +#include "sysc/utils/sc_concurrent.h" + +#include + +constexpr int N_WRITES = 5; +static const sc_core::sc_time WRITE_PERIOD(10, sc_core::SC_MS); + +SC_MODULE(producer) +{ + sc_out out; + + void run() + { + for (int i = 1; i <= N_WRITES; ++i) { + wait(WRITE_PERIOD); + out.write(i); + } + } + + SC_CTOR(producer) { SC_THREAD(run); } +}; + +SC_MODULE(consumer) +{ + sc_in in; + + int m_last_seen = 0; + int m_count = 0; + bool m_monotonic = true; + + void on_change() + { + int v = in.read(); + if (v <= m_last_seen) m_monotonic = false; + m_last_seen = v; + ++m_count; + if (v >= N_WRITES) sc_stop(); + } + + SC_CTOR(consumer) + { + SC_METHOD(on_change); + sensitive << in; + dont_initialize(); + } +}; + +int sc_main(int, char*[]) +{ + tlm_utils::tlm_quantumkeeper::set_global_quantum( + sc_core::sc_time(1, sc_core::SC_MS)); + + sc_signal sig; + + SC_ALLOW_CONCURRENCY(producer, sc_core::sc_sync_policy_tlm_quantum) + prod("prod"); + consumer cons("cons"); + + prod.out(sig); + cons.in(sig); + + sc_start(); + + bool ok = + cons.m_count >= N_WRITES && + cons.m_monotonic && + cons.m_last_seen == N_WRITES; + + std::cout << "count=" << cons.m_count + << " last_seen=" << cons.m_last_seen + << " monotonic=" << (cons.m_monotonic ? "yes" : "no") + << " result=" << (ok ? "PASS" : "FAIL") + << std::endl; + + return ok ? 0 : 1; +} diff --git a/tests/systemc/concurrent/cross_context_write/golden/cross_context_write.log b/tests/systemc/concurrent/cross_context_write/golden/cross_context_write.log new file mode 100644 index 000000000..facfac5f6 --- /dev/null +++ b/tests/systemc/concurrent/cross_context_write/golden/cross_context_write.log @@ -0,0 +1,6 @@ +SystemC Simulation + +Info: /OSCI/SystemC: Simulation stopped by user. + +Info: /OSCI/SystemC: Simulation stopped by user. +count=5 last_seen=5 monotonic=yes result=PASS diff --git a/tests/systemc/concurrent/multiple_modules/golden/multiple_modules.log b/tests/systemc/concurrent/multiple_modules/golden/multiple_modules.log new file mode 100644 index 000000000..aa57490c8 --- /dev/null +++ b/tests/systemc/concurrent/multiple_modules/golden/multiple_modules.log @@ -0,0 +1,8 @@ +SystemC Simulation + +Info: /OSCI/SystemC: Simulation stopped by user. + +Info: /OSCI/SystemC: Simulation stopped by user. + +Info: /OSCI/SystemC: Simulation stopped by user. +a:count=4 last=104 mono=yes b:count=4 last=204 mono=yes result=PASS diff --git a/tests/systemc/concurrent/multiple_modules/multiple_modules.cpp b/tests/systemc/concurrent/multiple_modules/multiple_modules.cpp new file mode 100644 index 000000000..0b732d460 --- /dev/null +++ b/tests/systemc/concurrent/multiple_modules/multiple_modules.cpp @@ -0,0 +1,146 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + multiple_modules.cpp -- Verify two independent SC_ALLOW_CONCURRENCY + modules each feeding the same consumer in the parent simcontext. + Each producer lives in its own child simcontext (its own std::thread). + Both signals are owned by the parent, so each producer's write() + crosses a simcontext boundary. + + Pass criteria: + * Consumer observes exactly N_WRITES values on each signal. + * Each stream is monotonic. + + Exits 0 on pass, 1 on fail. + + *****************************************************************************/ + +#include +#include "sysc/utils/sc_concurrent.h" + +#include + +constexpr int N_WRITES = 4; + +SC_MODULE(producer) +{ + sc_out out; + int m_base; + sc_core::sc_time m_period; + + void run() + { + for (int i = 1; i <= N_WRITES; ++i) { + wait(m_period); + out.write(m_base + i); + } + } + + SC_HAS_PROCESS(producer); + producer(sc_module_name n, int base, sc_core::sc_time period) + : sc_module(n), m_base(base), m_period(period) + { + SC_THREAD(run); + } +}; + +SC_MODULE(consumer) +{ + sc_in in_a; + sc_in in_b; + + int m_count_a = 0; + int m_count_b = 0; + int m_last_a = 0; + int m_last_b = 0; + bool m_mono_a = true; + bool m_mono_b = true; + + void on_a() + { + int v = in_a.read(); + if (v <= m_last_a) m_mono_a = false; + m_last_a = v; + ++m_count_a; + maybe_stop(); + } + void on_b() + { + int v = in_b.read(); + if (v <= m_last_b) m_mono_b = false; + m_last_b = v; + ++m_count_b; + maybe_stop(); + } + + void maybe_stop() + { + if (m_count_a >= N_WRITES && m_count_b >= N_WRITES) + sc_stop(); + } + + SC_CTOR(consumer) + { + SC_METHOD(on_a); sensitive << in_a; dont_initialize(); + SC_METHOD(on_b); sensitive << in_b; dont_initialize(); + } +}; + +int sc_main(int, char*[]) +{ + tlm_utils::tlm_quantumkeeper::set_global_quantum( + sc_core::sc_time(1, sc_core::SC_MS)); + + sc_signal sig_a; + sc_signal sig_b; + + // Two producers: each in its OWN child simcontext; bases are + // disjoint (100+ and 200+) so we can check the streams independently. + SC_ALLOW_CONCURRENCY(producer, sc_core::sc_sync_policy_tlm_quantum) + prod_a("prod_a", 100, sc_core::sc_time(10, sc_core::SC_MS)); + SC_ALLOW_CONCURRENCY(producer, sc_core::sc_sync_policy_tlm_quantum) + prod_b("prod_b", 200, sc_core::sc_time(7, sc_core::SC_MS)); + + consumer cons("cons"); + + prod_a.out(sig_a); + prod_b.out(sig_b); + cons.in_a(sig_a); + cons.in_b(sig_b); + + sc_start(); + + bool ok = + cons.m_count_a == N_WRITES && + cons.m_count_b == N_WRITES && + cons.m_last_a == 100 + N_WRITES && + cons.m_last_b == 200 + N_WRITES && + cons.m_mono_a && cons.m_mono_b; + + std::cout << "a:count=" << cons.m_count_a << " last=" << cons.m_last_a + << " mono=" << (cons.m_mono_a ? "yes" : "no") + << " b:count=" << cons.m_count_b << " last=" << cons.m_last_b + << " mono=" << (cons.m_mono_b ? "yes" : "no") + << " result=" << (ok ? "PASS" : "FAIL") + << std::endl; + + return ok ? 0 : 1; +} diff --git a/tests/systemc/datatypes/int/big_datatypes/add_subtracts/add_subtracts.cpp b/tests/systemc/datatypes/int/big_datatypes/add_subtracts/add_subtracts.cpp index e10bdd2bf..1e4027b0c 100644 --- a/tests/systemc/datatypes/int/big_datatypes/add_subtracts/add_subtracts.cpp +++ b/tests/systemc/datatypes/int/big_datatypes/add_subtracts/add_subtracts.cpp @@ -294,7 +294,7 @@ class AddSubtract : public AddSubtract assert( v_difference == v_sc_biguint_a ); } - if ( -v_sc_biguint_a != ( ~v_sc_biguint_a + 1 ) ) { + if ( -v_sc_biguint_a != ( ~(sc_signed)v_sc_biguint_a + 1 ) ) { cout << "ERROR: -a != ~a+1 in " << __FILE__ << " at line " << __LINE__ << endl; cout << " W " << W << endl; cout << " -v_sc_biguint_a " << -v_sc_biguint_a << endl; diff --git a/tests/systemc/kernel/dynamic_processes/test06/test06.cpp b/tests/systemc/kernel/dynamic_processes/test06/test06.cpp index dd5e91691..eca816aef 100644 --- a/tests/systemc/kernel/dynamic_processes/test06/test06.cpp +++ b/tests/systemc/kernel/dynamic_processes/test06/test06.cpp @@ -49,21 +49,21 @@ void p3() { void p2() { cerr << sc_time_stamp() << ":entering p2, spawning p3" << endl; - sc_spawn(sc_bind(&p3)); + sc_spawn(&p3); wait(20, SC_NS); cerr << sc_time_stamp() << ":exiting p2" << endl; } void p1() { cerr << sc_time_stamp() << ":entering p1, spawning p2" << endl; - sc_spawn(sc_bind(&p2)); + sc_spawn(&p2); wait(10, SC_NS); cerr << sc_time_stamp() << ":exiting p1" << endl; } void p0() { cerr << sc_time_stamp() << ":entering p0, spawning p1" << endl; - sc_spawn(sc_bind(&p1)); + sc_spawn(&p1); cerr << sc_time_stamp() << ":exiting p0" << endl; } diff --git a/tests/systemc/kernel/dynamic_processes/test07/test07.cpp b/tests/systemc/kernel/dynamic_processes/test07/test07.cpp index 76f7f48c1..fa4925e80 100644 --- a/tests/systemc/kernel/dynamic_processes/test07/test07.cpp +++ b/tests/systemc/kernel/dynamic_processes/test07/test07.cpp @@ -60,13 +60,17 @@ SC_MODULE(DUT) { cout << sc_time_stamp() << " callback" << endl; } + void thread() { - sc_spawn_options options; - options.spawn_method(); - options.set_sensitivity( &m_port ); - options.dont_initialize(); - sc_spawn( sc_bind(&DUT::method,this), "method", &options ); + // scope to free `options` memory (co-routine stacks are not proper cleaned up at simulation end) + { + sc_spawn_options options; + options.spawn_method(); + options.set_sensitivity( &m_port ); + options.dont_initialize(); + sc_spawn( sc_bind(&DUT::method,this), "method", &options ); + } for ( bool value=true;; value = !value) { wait(); diff --git a/tests/systemc/kernel/process_control/test06/test06.cpp b/tests/systemc/kernel/process_control/test06/test06.cpp index 7149c6944..01547ef5f 100644 --- a/tests/systemc/kernel/process_control/test06/test06.cpp +++ b/tests/systemc/kernel/process_control/test06/test06.cpp @@ -84,32 +84,35 @@ SC_MODULE(top) { wait(); - // copy children (needed, since children may get reordered) - std::vector< sc_object* > children = - sc_get_current_process_handle().get_child_objects(); + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) + { + // copy children (needed, since children may get reordered) + std::vector< sc_object* > children = + sc_get_current_process_handle().get_child_objects(); - std::vector< sc_object* >::const_iterator it = children.begin(); + std::vector< sc_object* >::const_iterator it = children.begin(); - while( it != children.end() ) - { - sc_process_handle h( *it++ ); - sc_assert( h.valid() ); + while( it != children.end() ) + { + sc_process_handle h( *it++ ); + sc_assert( h.valid() ); - std::cout << h.name() << " " - << "kill requested " - << "(" << h.get_process_object()->kind() << ") " - << "(" << sc_time_stamp() << " @ " << sc_delta_count() << ")" - << std::endl; + std::cout << h.name() << " " + << "kill requested " + << "(" << h.get_process_object()->kind() << ") " + << "(" << sc_time_stamp() << " @ " << sc_delta_count() << ")" + << std::endl; - h.kill( SC_INCLUDE_DESCENDANTS ); - } + h.kill( SC_INCLUDE_DESCENDANTS ); + } - wait(); + wait(); - std::cout << sc_get_current_process_handle().name() - << " ended " - << "(" << sc_time_stamp() << " @ " << sc_delta_count() << ")" - << std::endl; + std::cout << sc_get_current_process_handle().name() + << " ended " + << "(" << sc_time_stamp() << " @ " << sc_delta_count() << ")" + << std::endl; + } wait(); sc_stop(); diff --git a/tests/systemc/kernel/sc_event/test15/event_triggered.cpp b/tests/systemc/kernel/sc_event/test15/event_triggered.cpp index 200f2739e..adba37565 100644 --- a/tests/systemc/kernel/sc_event/test15/event_triggered.cpp +++ b/tests/systemc/kernel/sc_event/test15/event_triggered.cpp @@ -117,9 +117,11 @@ SC_MODULE( module ) CHECK( event_return.triggered() ); } + // outside the SC_THREAD to make the leak sanitizer happy (co-routine stacks are not proper cleaned up) + sc_event_or_list events_or; // even events only + void consumer_dynamic() { - sc_event_or_list events_or; // even events only for(unsigned i = 0; i < events.size(); i+=2) events_or |= events[i]; diff --git a/tests/systemc/misc/ieee1666_2023/5.7.3/5.7.3.cpp b/tests/systemc/misc/ieee1666_2023/5.7.3/5.7.3.cpp index ca23a0ae9..7a6ce861a 100644 --- a/tests/systemc/misc/ieee1666_2023/5.7.3/5.7.3.cpp +++ b/tests/systemc/misc/ieee1666_2023/5.7.3/5.7.3.cpp @@ -43,9 +43,12 @@ class chan_class : public if_class, public sc_core::sc_prim_channel { template class port_class : public sc_core::sc_port { public: - sc_core::sc_event_finder& event_finder() const { - return *new sc_core::sc_event_finder_t(*this, &if_class::ev_func); + port_class() : m_event_finder(*this, &if_class::ev_func) {} + sc_core::sc_event_finder& event_finder() { + return m_event_finder; } + private: + sc_core::sc_event_finder_t m_event_finder; }; SC_MODULE(mod_class) diff --git a/tests/systemc/misc/sim_tests/biquad/biquad2/delay_line.h b/tests/systemc/misc/sim_tests/biquad/biquad2/delay_line.h index 60d9805e8..830938cba 100644 --- a/tests/systemc/misc/sim_tests/biquad/biquad2/delay_line.h +++ b/tests/systemc/misc/sim_tests/biquad/biquad2/delay_line.h @@ -64,6 +64,10 @@ SC_MODULE( delay_line ) line = new float[delay]; } + virtual ~delay_line() { + delete[] line; + } + // Process functionality in member function below void entry(); }; diff --git a/tests/systemc/misc/sim_tests/biquad/biquad2/op_queue.h b/tests/systemc/misc/sim_tests/biquad/biquad2/op_queue.h index 63465262a..f4a862426 100644 --- a/tests/systemc/misc/sim_tests/biquad/biquad2/op_queue.h +++ b/tests/systemc/misc/sim_tests/biquad/biquad2/op_queue.h @@ -69,6 +69,10 @@ SC_MODULE( op_queue ) queue = new float[queue_size]; } + virtual ~op_queue() { + delete[] queue; + } + // Process functionality in member function below void entry(); }; diff --git a/tests/systemc/misc/sim_tests/biquad/biquad3/delay_line.h b/tests/systemc/misc/sim_tests/biquad/biquad3/delay_line.h index c99e1076c..b0c0b4623 100644 --- a/tests/systemc/misc/sim_tests/biquad/biquad3/delay_line.h +++ b/tests/systemc/misc/sim_tests/biquad/biquad3/delay_line.h @@ -65,6 +65,10 @@ SC_MODULE( delay_line ) sensitive << in; } + virtual ~delay_line() { + delete[] line; + } + // Process functionality in member function below void entry(); }; diff --git a/tests/systemc/misc/sim_tests/simple_cpu/simple_cpu.cpp b/tests/systemc/misc/sim_tests/simple_cpu/simple_cpu.cpp index 3d941a67f..774c64130 100644 --- a/tests/systemc/misc/sim_tests/simple_cpu/simple_cpu.cpp +++ b/tests/systemc/misc/sim_tests/simple_cpu/simple_cpu.cpp @@ -66,10 +66,11 @@ SC_MODULE( exec_decode ) // Initialize the data memory from file datamem FILE *fp = fopen("simple_cpu/datamem", "r"); - if (fp == (FILE *) 0) return; // No data mem file to read + if (fp == (FILE *) 0) assert(); // No data mem file to read // First field in this file is the size of data memory desired int size; - fscanf(fp, "%d", &size); + int ret = fscanf(fp, "%d", &size); + (void) ret; data_mem = new unsigned[size]; if (data_mem == (unsigned *) 0) { printf("Not enough memory left\n"); @@ -85,6 +86,10 @@ SC_MODULE( exec_decode ) program_counter.write(pc); } + virtual ~exec_decode() { + delete[] data_mem; + } + // Functionality void entry(); }; @@ -207,7 +212,8 @@ SC_MODULE( fetch ) if (fp == (FILE *) 0) return; // No prog mem file to read // First field in this file is the size of program memory desired int size; - fscanf(fp, "%d", &size); + int ret = fscanf(fp, "%d", &size); + (void) ret; prog_mem = new unsigned[size]; if (prog_mem == (unsigned *) 0) { printf("Not enough memory left\n"); @@ -221,6 +227,10 @@ SC_MODULE( fetch ) instruction.write(0); } + virtual ~fetch() { + delete[] prog_mem; + } + // Functionality void entry(); }; diff --git a/tests/systemc/misc/user_guide/chpt3.1/sg.cpp b/tests/systemc/misc/user_guide/chpt3.1/sg.cpp index 65edd73e6..b2bdb6aae 100644 --- a/tests/systemc/misc/user_guide/chpt3.1/sg.cpp +++ b/tests/systemc/misc/user_guide/chpt3.1/sg.cpp @@ -47,7 +47,8 @@ void stimgen::entry() file = fopen("./chpt3.1/testcase", "r"); while (true) { - fscanf(file, "%c", &c); + int ret = fscanf(file, "%c", &c); + (void) ret; data_ready.write(true); stream.write(c); wait(); diff --git a/tests/systemc/sc_log/test01/golden/test01.log b/tests/systemc/sc_log/test01/golden/test01.log new file mode 100644 index 000000000..5820952a4 --- /dev/null +++ b/tests/systemc/sc_log/test01/golden/test01.log @@ -0,0 +1,48 @@ +SystemC Simulation +TEST REPORT: DEBUG : [sc_main] Global Warning from sc_main line:221 +TEST REPORT: WARN : [quiet] Global Warning from sc_main line:222 +TEST REPORT: WARN : [GlobalLogger] My Global Warn from sc_main line:224 +0 is log_level CRITICAL +50 is log_level WARN +100 is log_level WARN +150 is log_level INFO +200 is log_level INFO +250 is log_level DEBUG +300 is log_level DEBUG +350 is log_level TRACE +400 is log_level TRACE +450 is log_level TRACE +Test string based handler +TEST REPORT: CRITICAL : [sc_log_test] CRITICAL line:233 +TEST REPORT: WARN : [sc_log_test] WARN line:233 +test FMT string +TEST REPORT: WARN : [SystemC] Testing FMT Hello world line:237 +construct module +TEST REPORT: WARN : [MyMod] HERE line:49 +TEST REPORT: WARN : [GlobalLogger] Global Warning line:50 +TEST REPORT: CRITICAL : [MyMod] Log to name() (at level CRITICAL) line:53 +TEST REPORT: CRITICAL : [My Tag] Log to My Tag (at level CRITICAL) line:54 +TEST REPORT: CRITICAL : [MyMod] Log to default () (at level CRITICAL) line:55 +TEST REPORT: CRITICAL : [test_handler] Log to test_handler (at level CRITICAL) line:56 +TEST REPORT: CRITICAL : [My TST Tag] Log using () to My TH Tag (at level CRITICAL) line:57 +TEST REPORT: WARN : [MyMod] Log to name() (at level WARN) line:53 +TEST REPORT: WARN : [My Tag] Log to My Tag (at level WARN) line:54 +TEST REPORT: WARN : [MyMod] Log to default () (at level WARN) line:55 +TEST REPORT: WARN : [test_handler] Log to test_handler (at level WARN) line:56 +TEST REPORT: WARN : [My TST Tag] Log using () to My TH Tag (at level WARN) line:57 +TEST REPORT: INFO : [MyMod] Log to name() (at level INFO) line:53 +TEST REPORT: INFO : [My Tag] Log to My Tag (at level INFO) line:54 +TEST REPORT: INFO : [MyMod] Log to default () (at level INFO) line:55 +TEST REPORT: INFO : [test_handler] Log to test_handler (at level INFO) line:56 +TEST REPORT: INFO : [My TST Tag] Log using () to My TH Tag (at level INFO) line:57 +TEST REPORT: DEBUG : [MyMod] Log to name() (at level DEBUG) line:53 +TEST REPORT: DEBUG : [My Tag] Log to My Tag (at level DEBUG) line:54 +TEST REPORT: DEBUG : [MyMod] Log to default () (at level DEBUG) line:55 +TEST REPORT: TRACE : [MyMod] Log to name() (at level TRACE) line:53 +TEST REPORT: TRACE : [My Tag] Log to My Tag (at level TRACE) line:54 +TEST REPORT: TRACE : [MyMod] Log to default () (at level TRACE) line:55 +TEST REPORT: CRITICAL : [MyMod] SC_CRITICAL line:61 +TEST REPORT: WARN : [MyMod] SC_WARN line:62 +TEST REPORT: INFO : [MyMod] SC_INFO line:63 +TEST REPORT: DEBUG : [MyMod] SC_DEBUG line:64 +TEST REPORT: TRACE : [MyMod] SC_TRACE line:65 diff --git a/tests/systemc/sc_log/test01/test01.cpp b/tests/systemc/sc_log/test01/test01.cpp new file mode 100644 index 000000000..7c3e5f5f0 --- /dev/null +++ b/tests/systemc/sc_log/test01/test01.cpp @@ -0,0 +1,244 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + test01.cpp -- + + Original Author: Mark Burton, Qualcomm Innovation Center, Inc. 2024 + + *****************************************************************************/ + +/***************************************************************************** + + MODIFICATION LOG - modifiers, enter your name, affiliation, date and + changes you are making here. + + Name, Affiliation, Date: + Description of Modification: + + *****************************************************************************/ + +#include "systemc.h" +#include +#include +#include +#include + +SC_LOG_HANDLE_STATIC(MY_GLOBAL_LOGGER, "GlobalLogger"); + +SC_MODULE(mod_a) { + SC_LOG_HANDLE(TST, "test_handler"); + SC_CTOR(mod_a) { + SC_WARN() << "HERE"; + SC_WARN(MY_GLOBAL_LOGGER)("Global Warning"); + for (auto l : sc_core::log_level_map) { + auto i = l.first; + SC_LOG_AT(i, name()) << " Log to name()" << " (at level " << i << ")"; + SC_LOG_AT(i, "My Tag") << " Log to My Tag" << " (at level " << i << ")"; + SC_LOG_AT(i) << " Log to default ()" << " (at level " << i << ")"; + SC_LOG_AT(i, TST) << " Log to test_handler" << " (at level " << i << ")"; + SC_LOG_AT(i, TST, "My TST Tag") + << " Log using () to My TH Tag" << " (at level " << i << ")"; + } + + SC_CRITICAL() << "SC_CRITICAL"; + SC_WARN() << "SC_WARN"; + SC_INFO() << "SC_INFO"; + SC_DEBUG() << "SC_DEBUG"; + SC_TRACE() << "SC_TRACE"; + } +}; + +/********************************************************** + * The mechanism by which SC_LOG macros are enabled and + * disabled is implementation-defined. + * + * The Accellera POC provides functions and a class for this + * purpose. + * + * -------------------------------------------------------- + * Non-standard API: + * + * Install a callback used to determine the effective log + * level for a given logger cache and local_tag. + * + * The cache parameter may be used to remember a computed + * level. Once set, the function will not be re-called. + * -------------------------------------------------------- + * + * void sc_set_log_verbosity_fn( + * std::function< + * sc_core::sc_log_level( + * sc_core::sc_log_logger_cache &logger, + * const char *file, + * int line, + * std::string_view local_tag + * ) + * > fn + * ); + * + * -------------------------------------------------------- + * Non-standard API: + * + * Query the current log verbosity for the given cache and + * local_tag. + * + * If no callback has been installed, the implementation + * will fall back to the global report verbosity. + * -------------------------------------------------------- + * + * sc_core::sc_log_level sc_log_impl::sc_get_log_verbosity( + * sc_core::sc_log_logger_cache &logger, + * const char *file, + * int line, + * std::string_view local_tag); + * ); + * + * Together with the sc_log_logger_cache + * class, this is used by the SC_LOG macros to determine + * whether specific loggers should be enabled. + **********************************************************/ + +/********************************************************** + * Example: + * + * This demonstrates how one could construct a class around + * the basic set_log_verbosity_fn API. + * + * A tool could use this to build additional functionality + * (e.g. resetting cached values). This behavior is not + * part of the standard, and tool environments may differ. + **********************************************************/ + +class scp_logger_test { + + std::unordered_map lut; + + // BKDR hash algorithm + auto char_hash(std::string_view str) -> uint64_t { + constexpr unsigned int seed = 131; + uint64_t hash = 0; + for (char c : str) { + hash = (hash * seed) + static_cast(c); + } + return hash; + } + + std::unordered_set loggers; + sc_core::sc_log_level operator()(struct sc_core::sc_log_logger_cache &logger, + const char *file, int line, + std::string_view local_tag) { + loggers.insert(&logger); + + if (logger.tag.empty() && logger.scname.empty()) { + /* This must be a global logger lets base our decision on the local_tag + * And we wont 'cache' the result in the logger, because we need to check + * the local_tag each time + */ + + auto k = char_hash(local_tag); + auto it = lut.find(k); + if (it != lut.end()) { + return it->second; + } + + sc_core::sc_log_level lvl = sc_core::sc_log_level::TRACE; + if (local_tag == "quiet") + lvl = sc_core::sc_log_level::WARN; + if (local_tag == "sc_main") + lvl = sc_core::sc_log_level::DEBUG; + if (local_tag == "sc_log_test") + lvl = sc_core::sc_log_level::WARN; + + lut[k] = lvl; + return lvl; + } + + if (logger.tag == "test_handler") { + return sc_core::sc_log_level::INFO; + } + if (logger.tag == "GlobalLogger") { + return sc_core::sc_log_level::WARN; + } + /* Cache this one which will catch the normal SCMOD case for mod_a */ + logger.level = sc_core::sc_log_level::TRACE; + return sc_core::sc_log_level::TRACE; + } + +public: + scp_logger_test() { + std::function + fn = [&](sc_core::sc_log_logger_cache &logger, const char *file, + int line, + std::string_view local_tag) -> sc_core::sc_log_level { + return operator()(logger, file, line, local_tag); + }; + ::sc_core::sc_log_impl::sc_set_log_verbosity_fn(fn); + ::sc_core::sc_report_handler::set_verbosity_level( + sc_core::SC_DEBUG); // Set the level in the core to DEBUG such that the + // handler can manage all levels of verbosity + } + void reset() { + for (auto *logger : loggers) { + if (logger) { + logger->level = sc_core::sc_log_level::UNSET; + } + } + } +}; + +static scp_logger_test test_logger_handler; + +void report_handler(const sc_core::sc_report &rep, + const sc_core::sc_actions &actions) { + cout << "TEST REPORT: " << sc_core::as_log(rep.get_verbosity()) << " : [" + << rep.get_msg_type() << "] " << rep.get_msg() + << " line:" << rep.get_line_number() << std::endl; +} + +int sc_main(int, char *[]) { + ::sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_DEBUG); + ::sc_core::sc_report_handler::set_handler(report_handler); + + SC_DEBUG("sc_main")("Global Warning from sc_main"); + SC_WARN("quiet")("Global Warning from sc_main"); + SC_INFO("quiet")("Global Warning from sc_main (** filtered out **)"); + SC_WARN(MY_GLOBAL_LOGGER)("My Global Warn from sc_main"); + SC_DEBUG(MY_GLOBAL_LOGGER, + "sc_main")("Global Debug from sc_main (** filtered out **)"); + + for (int i = 0; i < 500; i += 50) { + cout << i << " is log_level " << sc_core::as_log(i) << endl; + } + cout << "Test string based handler" << std::endl; + for (auto l : sc_core::log_level_map) { + SC_LOG_AT(l.first, "sc_log_test") << l.second; + } + + cout << "test FMT string" << std::endl; + SC_WARN()("Testing FMT Hello {}", "world"); + + cout << "construct module" << std::endl; + + mod_a amod("MyMod"); + + return 0; +} diff --git a/tests/systemc/sc_log/test02/golden/test02.log b/tests/systemc/sc_log/test02/golden/test02.log new file mode 100644 index 000000000..1d99427f6 --- /dev/null +++ b/tests/systemc/sc_log/test02/golden/test02.log @@ -0,0 +1,81 @@ +SystemC Simulation +======================================== +SC_LOG API Comprehensive Test (test02) +======================================== + +=== Test Global Static Loggers === +[INFO] SystemC: INFO: Global default logger (no tag) +[WARN] SystemC: WARN: Global default logger, format: test +[INFO] GlobalTag: INFO: Global logger with tag + +=== Test Tag-Based Logging === +[INFO] Tag1: INFO: Using Tag1 +[WARN] Tag2: WARN: Using Tag2 with format: value +[DEBUG] Tag3: DEBUG: Using Tag3 +[TRACE] Tag4: TRACE: Using Tag4 with format: 999 + +=== Test All Log Levels === +[CRITICAL] LevelTest: CRITICAL level test +[WARN] LevelTest: WARN level test +[INFO] LevelTest: INFO level test +[DEBUG] LevelTest: DEBUG level test +[TRACE] LevelTest: TRACE level test +[CRITICAL] LevelTest: CRITICAL level test with format: 1 +[WARN] LevelTest: WARN level test with format: 2 +[INFO] LevelTest: INFO level test with format: 3 +[DEBUG] LevelTest: DEBUG level test with format: 4 +[TRACE] LevelTest: TRACE level test with format: 5 + +=== Test Complex Format Strings === +[INFO] FormatTest: Multiple args: 1, 2, 3 +[INFO] FormatTest: Mixed types: int=42, str=hello, float=3.14 +[INFO] FormatTest: Nested braces: {} and value: 123 +[INFO] FormatTest: Empty format string + +=== Test Module with Default Logger === +[CRITICAL] mod_default: CRITICAL: Default logger, stream syntax +[WARN] mod_default: WARN: Default logger, stream syntax +[INFO] mod_default: INFO: Default logger, stream syntax +[DEBUG] mod_default: DEBUG: Default logger, stream syntax +[TRACE] mod_default: TRACE: Default logger, stream syntax +[WARN] mod_default: WARN: Default logger, format syntax with arg: 42 +[INFO] mod_default: INFO: Default logger, format syntax with multiple args: hello world + +=== Test Module with Tagged Logger === +[CRITICAL] ModuleTag: CRITICAL: Tagged logger +[WARN] ModuleTag: WARN: Tagged logger +[INFO] ModuleTag: INFO: Tagged logger +[DEBUG] ModuleTag: DEBUG: Tagged logger +[INFO] ModuleTag: INFO: Tagged logger with format: 123 + +=== Test Module with Multiple Named Loggers === +[WARN] Logger1Tag: WARN: Using logger1 with format: test +[INFO] Logger2Tag: INFO: Using logger2 +[DEBUG] Logger2Tag: DEBUG: Using logger2 with format: 456 +[INFO] mod_multi: INFO: Using default logger +[TRACE] mod_multi: TRACE: Using default logger with format + +=== Test Module with Vector of Loggers === +[INFO] VecLogger0: INFO: Vector logger 0 +[WARN] VecLogger0: WARN: Vector logger 0 with format +[WARN] VecLogger1: WARN: Vector logger 1 with format +[INFO] VecLogger2: INFO: Vector logger 2 +[WARN] VecLogger2: WARN: Vector logger 2 with format + +=== Test SC_LOG_AT Variants === +[INFO] mod_log_at: SC_LOG_AT: No args, stream syntax +[WARN] mod_log_at: SC_LOG_AT: No args, format syntax: 1 +[INFO] LogAtTag: SC_LOG_AT: With logger handle +[DEBUG] LogAtTag: SC_LOG_AT: With logger handle, format: 2 +[INFO] ExplicitTag: SC_LOG_AT: With explicit tag +[TRACE] ExplicitTag: SC_LOG_AT: With explicit tag, format: 3 +[WARN] OverrideTag: SC_LOG_AT: Logger + override tag +[INFO] OverrideTag: SC_LOG_AT: Logger + override tag, format: 4 + +=== Test Using name() as Tag === +[INFO] mod_name_tag: Using name() as tag +[WARN] mod_name_tag: Using name() as tag with format: mod_name_tag + +======================================== +All tests completed successfully! +======================================== diff --git a/tests/systemc/sc_log/test02/test02.cpp b/tests/systemc/sc_log/test02/test02.cpp new file mode 100644 index 000000000..6e224436c --- /dev/null +++ b/tests/systemc/sc_log/test02/test02.cpp @@ -0,0 +1,310 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + test02.cpp -- Comprehensive test of sc_log API + + Original Author: Mark Burton, Qualcomm Innovation Center, Inc. 2024 + + DESCRIPTION: This test exercises all aspects of the sc_log API: + - All logger handle declaration variants + - All log level macros + - Stream and format string syntax + - Logger handles with and without tags + - Static and member logger handles + - Vector of logger handles + - Tag-based logging + + *****************************************************************************/ + +#include "systemc.h" +#include +#include + +// Test 1: Global static logger handle with tag +// Note: The default global SC_LOG_LOG_LEVEL_CACHE is already declared in sc_log.h +SC_LOG_HANDLE_STATIC(GLOBAL_LOGGER_WITH_TAG, "GlobalTag"); + +// Test 2: Module with default logger (no tag) +SC_MODULE(test_module_default) { + SC_LOG_HANDLE(); + + SC_CTOR(test_module_default) { + cout << "\n=== Test Module with Default Logger ===" << endl; + + // Test all log levels with stream syntax + SC_CRITICAL() << "CRITICAL: Default logger, stream syntax"; + SC_WARN() << "WARN: Default logger, stream syntax"; + SC_INFO() << "INFO: Default logger, stream syntax"; + SC_DEBUG() << "DEBUG: Default logger, stream syntax"; + SC_TRACE() << "TRACE: Default logger, stream syntax"; + + // Test format string syntax + SC_WARN()("WARN: Default logger, format syntax with arg: {}", 42); + SC_INFO()("INFO: Default logger, format syntax with multiple args: {} {}", "hello", "world"); + } +}; + +// Test 3: Module with tagged logger +SC_MODULE(test_module_tagged) { + SC_LOG_HANDLE("ModuleTag"); + + SC_CTOR(test_module_tagged) { + cout << "\n=== Test Module with Tagged Logger ===" << endl; + + SC_CRITICAL() << "CRITICAL: Tagged logger"; + SC_WARN() << "WARN: Tagged logger"; + SC_INFO() << "INFO: Tagged logger"; + SC_DEBUG() << "DEBUG: Tagged logger"; + SC_TRACE() << "TRACE: Tagged logger"; + + SC_INFO()("INFO: Tagged logger with format: {}", 123); + } +}; + +// Test 4: Module with multiple named loggers +SC_MODULE(test_module_multi_logger) { + SC_LOG_HANDLE(logger1, "Logger1Tag"); + SC_LOG_HANDLE(logger2, "Logger2Tag"); + SC_LOG_HANDLE(); // Default logger + + SC_CTOR(test_module_multi_logger) { + cout << "\n=== Test Module with Multiple Named Loggers ===" << endl; + + // Test logger1 + SC_INFO(logger1) << "INFO: Using logger1"; + SC_WARN(logger1)("WARN: Using logger1 with format: {}", "test"); + + // Test logger2 + SC_INFO(logger2) << "INFO: Using logger2"; + SC_DEBUG(logger2)("DEBUG: Using logger2 with format: {}", 456); + + // Test default logger + SC_INFO() << "INFO: Using default logger"; + SC_TRACE()("TRACE: Using default logger with format"); + } +}; + +// Test 5: Module with vector of loggers +SC_MODULE(test_module_vector) { + SC_LOG_HANDLE_VECTOR(logger_vec); + + SC_CTOR(test_module_vector) { + cout << "\n=== Test Module with Vector of Loggers ===" << endl; + + // Populate vector + SC_LOG_HANDLE_VECTOR_PUSH_BACK(logger_vec, "VecLogger0"); + SC_LOG_HANDLE_VECTOR_PUSH_BACK(logger_vec, "VecLogger1"); + SC_LOG_HANDLE_VECTOR_PUSH_BACK(logger_vec, "VecLogger2"); + + // Use loggers from vector + for (size_t i = 0; i < logger_vec.size(); i++) { + SC_INFO(logger_vec[i]) << "INFO: Vector logger " << i; + SC_WARN(logger_vec[i])("WARN: Vector logger {} with format", i); + } + } +}; + +// Test 6: SC_LOG_AT with different argument combinations +SC_MODULE(test_module_log_at) { + SC_LOG_HANDLE(my_logger, "LogAtTag"); + + SC_CTOR(test_module_log_at) { + cout << "\n=== Test SC_LOG_AT Variants ===" << endl; + + // SC_LOG_AT with no additional args (uses default logger) + SC_LOG_AT(sc_core::sc_log_level::INFO) << "SC_LOG_AT: No args, stream syntax"; + SC_LOG_AT(sc_core::sc_log_level::WARN)("SC_LOG_AT: No args, format syntax: {}", 1); + + // SC_LOG_AT with logger handle + SC_LOG_AT(sc_core::sc_log_level::INFO, my_logger) << "SC_LOG_AT: With logger handle"; + SC_LOG_AT(sc_core::sc_log_level::DEBUG, my_logger)("SC_LOG_AT: With logger handle, format: {}", 2); + + // SC_LOG_AT with explicit tag string + SC_LOG_AT(sc_core::sc_log_level::INFO, "ExplicitTag") << "SC_LOG_AT: With explicit tag"; + SC_LOG_AT(sc_core::sc_log_level::TRACE, "ExplicitTag")("SC_LOG_AT: With explicit tag, format: {}", 3); + + // SC_LOG_AT with logger and tag + SC_LOG_AT(sc_core::sc_log_level::WARN, my_logger, "OverrideTag") << "SC_LOG_AT: Logger + override tag"; + SC_LOG_AT(sc_core::sc_log_level::INFO, my_logger, "OverrideTag")("SC_LOG_AT: Logger + override tag, format: {}", 4); + } +}; + +// Test 7: Using name() as tag +SC_MODULE(test_module_name_tag) { + SC_LOG_HANDLE(); + + SC_CTOR(test_module_name_tag) { + cout << "\n=== Test Using name() as Tag ===" << endl; + + SC_LOG_AT(sc_core::sc_log_level::INFO, name()) << "Using name() as tag"; + SC_LOG_AT(sc_core::sc_log_level::WARN, name())("Using name() as tag with format: {}", name()); + } +}; + +// Test 8: Global logger usage +void test_global_loggers() { + cout << "\n=== Test Global Static Loggers ===" << endl; + + SC_INFO() << "INFO: Global default logger (no tag)"; + SC_WARN()("WARN: Global default logger, format: {}", "test"); + + SC_INFO(GLOBAL_LOGGER_WITH_TAG) << "INFO: Global logger with tag"; + SC_DEBUG(GLOBAL_LOGGER_WITH_TAG)("DEBUG: Global logger with tag, format: {}", 789); +} + +// Test 9: Tag-based logging (using string literals as tags) +void test_tag_based_logging() { + cout << "\n=== Test Tag-Based Logging ===" << endl; + + SC_INFO("Tag1") << "INFO: Using Tag1"; + SC_WARN("Tag2")("WARN: Using Tag2 with format: {}", "value"); + SC_DEBUG("Tag3") << "DEBUG: Using Tag3"; + SC_TRACE("Tag4")("TRACE: Using Tag4 with format: {}", 999); +} + +// Test 10: All log levels +void test_all_log_levels() { + cout << "\n=== Test All Log Levels ===" << endl; + + SC_CRITICAL("LevelTest") << "CRITICAL level test"; + SC_WARN("LevelTest") << "WARN level test"; + SC_INFO("LevelTest") << "INFO level test"; + SC_DEBUG("LevelTest") << "DEBUG level test"; + SC_TRACE("LevelTest") << "TRACE level test"; + + SC_CRITICAL("LevelTest")("CRITICAL level test with format: {}", 1); + SC_WARN("LevelTest")("WARN level test with format: {}", 2); + SC_INFO("LevelTest")("INFO level test with format: {}", 3); + SC_DEBUG("LevelTest")("DEBUG level test with format: {}", 4); + SC_TRACE("LevelTest")("TRACE level test with format: {}", 5); +} + +// Test 11: Complex format strings +void test_complex_formats() { + cout << "\n=== Test Complex Format Strings ===" << endl; + + SC_INFO("FormatTest")("Multiple args: {}, {}, {}", 1, 2, 3); + SC_INFO("FormatTest")("Mixed types: int={}, str={}, float={:.2f}", 42, "hello", 3.14159); + SC_INFO("FormatTest")("Nested braces: {{}} and value: {}", 123); + SC_INFO("FormatTest")("Empty format string"); +} + +// Simple verbosity control for testing +class test_verbosity_controller { + std::map tag_levels; + +public: + test_verbosity_controller() { + // Set different levels for different tags + tag_levels["GlobalTag"] = sc_core::sc_log_level::INFO; + tag_levels["ModuleTag"] = sc_core::sc_log_level::DEBUG; + tag_levels["Logger1Tag"] = sc_core::sc_log_level::WARN; + tag_levels["Logger2Tag"] = sc_core::sc_log_level::TRACE; + tag_levels["VecLogger0"] = sc_core::sc_log_level::INFO; + tag_levels["VecLogger1"] = sc_core::sc_log_level::WARN; + tag_levels["VecLogger2"] = sc_core::sc_log_level::DEBUG; + tag_levels["LogAtTag"] = sc_core::sc_log_level::TRACE; + tag_levels["ExplicitTag"] = sc_core::sc_log_level::DEBUG; + tag_levels["OverrideTag"] = sc_core::sc_log_level::INFO; + tag_levels["Tag1"] = sc_core::sc_log_level::INFO; + tag_levels["Tag2"] = sc_core::sc_log_level::WARN; + tag_levels["Tag3"] = sc_core::sc_log_level::DEBUG; + tag_levels["Tag4"] = sc_core::sc_log_level::TRACE; + tag_levels["LevelTest"] = sc_core::sc_log_level::TRACE; + tag_levels["FormatTest"] = sc_core::sc_log_level::INFO; + + std::function + fn = [&](sc_core::sc_log_logger_cache &logger, const char *file, + int line, + std::string_view local_tag) -> sc_core::sc_log_level { + // Check if we have a specific level for this tag + std::string tag_str; + if (!logger.tag.empty()) { + tag_str = std::string(logger.tag); + } else if (!local_tag.empty()) { + tag_str = std::string(local_tag); + } else if (!logger.scname.empty()) { + tag_str = std::string(logger.scname); + } + + auto it = tag_levels.find(tag_str); + if (it != tag_levels.end()) { + if (logger.level == sc_core::sc_log_level::UNSET) { + logger.level = it->second; + } + return it->second; + } + + // Default to TRACE for everything else + if (logger.level == sc_core::sc_log_level::UNSET) { + logger.level = sc_core::sc_log_level::TRACE; + } + return sc_core::sc_log_level::TRACE; + }; + + ::sc_core::sc_log_impl::sc_set_log_verbosity_fn(fn); + ::sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_DEBUG); + } +}; + +void report_handler(const sc_core::sc_report &rep, + const sc_core::sc_actions &actions) { + cout << "[" << sc_core::as_log(rep.get_verbosity()) << "] " + << rep.get_msg_type() << ": " << rep.get_msg() << endl; +} + +int sc_main(int, char *[]) { + ::sc_core::sc_report_handler::set_verbosity_level(sc_core::SC_DEBUG); + ::sc_core::sc_report_handler::set_handler(report_handler); + + test_verbosity_controller verbosity_ctrl; + + cout << "========================================" << endl; + cout << "SC_LOG API Comprehensive Test (test02)" << endl; + cout << "========================================" << endl; + + // Test global loggers + test_global_loggers(); + + // Test tag-based logging + test_tag_based_logging(); + + // Test all log levels + test_all_log_levels(); + + // Test complex format strings + test_complex_formats(); + + // Instantiate test modules + test_module_default mod_default("mod_default"); + test_module_tagged mod_tagged("mod_tagged"); + test_module_multi_logger mod_multi("mod_multi"); + test_module_vector mod_vector("mod_vector"); + test_module_log_at mod_log_at("mod_log_at"); + test_module_name_tag mod_name_tag("mod_name_tag"); + + cout << "\n========================================" << endl; + cout << "All tests completed successfully!" << endl; + cout << "========================================" << endl; + + return 0; +} diff --git a/tests/tlm/cancel_all/cancel_all.cpp b/tests/tlm/cancel_all/cancel_all.cpp index d3b85b715..6c0f973ed 100644 --- a/tests/tlm/cancel_all/cancel_all.cpp +++ b/tests/tlm/cancel_all/cancel_all.cpp @@ -21,11 +21,12 @@ SC_MODULE(Test_peq_with_cb) { section = 1; - tlm::tlm_generic_payload *trans; + std::vector trans_vec; tlm::tlm_phase phase; for (int i = 0; i < 50; i++) { - trans = new tlm::tlm_generic_payload; + tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload; + trans_vec.push_back(trans); trans->set_address(i); m_peq.notify( *trans, phase, sc_time(rand() % 100, SC_NS) ); } @@ -33,17 +34,29 @@ SC_MODULE(Test_peq_with_cb) m_peq.cancel_all(); cout << "cancel_all\n"; + while(!trans_vec.empty()) + { + delete trans_vec.back(); + trans_vec.pop_back(); + } + section = 2; for (int i = 100; i < 150; i++) { - trans = new tlm::tlm_generic_payload; + tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload; + trans_vec.push_back(trans); trans->set_address(i); m_peq.notify( *trans, phase, sc_time(rand() % 100, SC_NS) ); } wait(50, SC_NS); m_peq.cancel_all(); cout << "cancel_all\n"; + while(!trans_vec.empty()) + { + delete trans_vec.back(); + trans_vec.pop_back(); + } wait(50, SC_NS); } @@ -91,10 +104,11 @@ SC_MODULE(Test_peq_with_get) section = 3; - tlm::tlm_generic_payload *trans; + std::vector trans_vec; for (int i = 0; i < 50; i++) { - trans = new tlm::tlm_generic_payload; + tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload; + trans_vec.push_back(trans); trans->set_address(i); m_peq.notify( *trans, sc_time(rand() % 100, SC_NS) ); } @@ -102,17 +116,28 @@ SC_MODULE(Test_peq_with_get) m_peq.cancel_all(); cout << "cancel_all\n"; + while(!trans_vec.empty()) + { + delete trans_vec.back(); + trans_vec.pop_back(); + } section = 4; for (int i = 100; i < 150; i++) { - trans = new tlm::tlm_generic_payload; + tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload; + trans_vec.push_back(trans); trans->set_address(i); m_peq.notify( *trans, sc_time(rand() % 100, SC_NS) ); } wait(50, SC_NS); m_peq.cancel_all(); cout << "cancel_all\n"; + while(!trans_vec.empty()) + { + delete trans_vec.back(); + trans_vec.pop_back(); + } wait(50, SC_NS); } diff --git a/tests/tlm/nb2b_adapter/mm.h b/tests/tlm/nb2b_adapter/mm.h index c91dd3ed5..5cea080fe 100644 --- a/tests/tlm/nb2b_adapter/mm.h +++ b/tests/tlm/nb2b_adapter/mm.h @@ -18,6 +18,7 @@ class mm: public tlm::tlm_mm_interface while (free_list) { + access* node = free_list; ptr = free_list->trans; // Delete generic payload and all extensions @@ -25,6 +26,7 @@ class mm: public tlm::tlm_mm_interface delete ptr; free_list = free_list->next; + delete node; } while (empties) diff --git a/tests/tlm/nb2b_adapter/nb2b_adapter.cpp b/tests/tlm/nb2b_adapter/nb2b_adapter.cpp index 32e58f1f6..923d67b4a 100644 --- a/tests/tlm/nb2b_adapter/nb2b_adapter.cpp +++ b/tests/tlm/nb2b_adapter/nb2b_adapter.cpp @@ -235,8 +235,12 @@ struct Interconnect: sc_module if (status == tlm::TLM_COMPLETED) { - accessor(trans).clear_extension(ext); - delete ext; + accessor(trans).get_extension(ext); + if (ext) + { + accessor(trans).clear_extension(ext); + delete ext; + } } return status; @@ -341,24 +345,23 @@ struct Target: sc_module SC_MODULE(Top) { - Initiator *initiator1; - Initiator *initiator2; - Interconnect *interconnect; - Target *target1; - Target *target2; + Initiator initiator1; + Initiator initiator2; + Interconnect interconnect; + Target target1; + Target target2; SC_CTOR(Top) + : initiator1("initiator1") + , initiator2("initiator2") + , interconnect("interconnect", 1) + , target1("target1") + , target2("target2") { - initiator1 = new Initiator ("initiator1"); - initiator2 = new Initiator ("initiator2"); - interconnect = new Interconnect("interconnect", 1); - target1 = new Target ("target1"); - target2 = new Target ("target2"); - - initiator1->socket.bind(interconnect->targ_socket); - initiator2->socket.bind(interconnect->targ_socket); - interconnect->init_socket.bind(target1->socket); - interconnect->init_socket.bind(target2->socket); + initiator1.socket.bind(interconnect.targ_socket); + initiator2.socket.bind(interconnect.targ_socket); + interconnect.init_socket.bind(target1.socket); + interconnect.init_socket.bind(target2.socket); } }; diff --git a/tests/tlm/static_extensions/ext2gp/SimpleLTInitiator_ext.h b/tests/tlm/static_extensions/ext2gp/SimpleLTInitiator_ext.h index f9875d907..083a75109 100644 --- a/tests/tlm/static_extensions/ext2gp/SimpleLTInitiator_ext.h +++ b/tests/tlm/static_extensions/ext2gp/SimpleLTInitiator_ext.h @@ -131,99 +131,99 @@ class SimpleLTInitiator_ext : public sc_core::sc_module void run() { - transaction_type trans; - phase_type phase; - sc_core::sc_time t; - // make sure that our transaction has the proper extension: - my_extension* tmp_ext = new my_extension(); - tmp_ext->m_data = 11; - - trans.set_extension(tmp_ext); - - while (initTransaction(trans)) - { - // Create transaction and initialise phase and t - phase = tlm::BEGIN_REQ; - t = sc_core::SC_ZERO_TIME; - - logStartTransation(trans); - /////////////////////////////////////////////////////////// - // DMI handling: - // We use the DMI hint to check if it makes sense to ask for - // DMI pointers. The pattern is: - // - if the address is covered by a DMI region do a DMI access - // - otherwise do a normal transaction - // -> check if we get a DMI hint and acquire the DMI pointers if it - // is set - /////////////////////////////////////////////////////////// - - // Check if the address is covered by our DMI region - if ( (trans.get_address() >= mDMIData.get_start_address()) && - (trans.get_address() <= mDMIData.get_end_address()) ) - { - // We can handle the data here. As the logEndTransaction is - // assuming something to happen in the data structure, we really - // need to do this: - trans.set_response_status(tlm::TLM_OK_RESPONSE); - sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; - - } else { - mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; - } - - // Do the wait immediately. Note that doing the wait here eats - // almost all the performance anyway, so we only gain something - // if we're using temporal decoupling. - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - wait(mDMIData.get_write_latency()); - - } else { - wait(mDMIData.get_read_latency()); - } + { + transaction_type trans; + phase_type phase; + sc_core::sc_time t; + // make sure that our transaction has the proper extension: + my_extension* tmp_ext = new my_extension(); + tmp_ext->m_data = 11; - logEndTransaction(trans); - - } else { // we need a full transaction - switch (socket->nb_transport_fw(trans, phase, t)) { - case tlm::TLM_COMPLETED: - // Transaction Finished, wait for the returned delay - wait(t); - break; - - case tlm::TLM_ACCEPTED: - case tlm::TLM_UPDATED: - // Transaction not yet finished, wait for the end of it - wait(mEndEvent); - break; - - default: - sc_assert(0); exit(1); - }; - - logEndTransaction(trans); + trans.set_extension(tmp_ext); - // Acquire DMI pointer if one is available: - if (trans.is_dmi_allowed()) - { - trans.set_write(); - dmi_type tmp; - if (socket->get_direct_mem_ptr(trans, - tmp)) - { - // FIXME: No support for separate read/write ranges - sc_assert(tmp.is_read_write_allowed()); - mDMIData = tmp; - } - } - } - } - delete tmp_ext; - wait(); - + while (initTransaction(trans)) + { + // Create transaction and initialise phase and t + phase = tlm::BEGIN_REQ; + t = sc_core::SC_ZERO_TIME; + + logStartTransation(trans); + /////////////////////////////////////////////////////////// + // DMI handling: + // We use the DMI hint to check if it makes sense to ask for + // DMI pointers. The pattern is: + // - if the address is covered by a DMI region do a DMI access + // - otherwise do a normal transaction + // -> check if we get a DMI hint and acquire the DMI pointers if it + // is set + /////////////////////////////////////////////////////////// + + // Check if the address is covered by our DMI region + if ( (trans.get_address() >= mDMIData.get_start_address()) && + (trans.get_address() <= mDMIData.get_end_address()) ) + { + // We can handle the data here. As the logEndTransaction is + // assuming something to happen in the data structure, we really + // need to do this: + trans.set_response_status(tlm::TLM_OK_RESPONSE); + sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; + + } else { + mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; + } + + // Do the wait immediately. Note that doing the wait here eats + // almost all the performance anyway, so we only gain something + // if we're using temporal decoupling. + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + wait(mDMIData.get_write_latency()); + + } else { + wait(mDMIData.get_read_latency()); + } + + logEndTransaction(trans); + + } else { // we need a full transaction + switch (socket->nb_transport_fw(trans, phase, t)) { + case tlm::TLM_COMPLETED: + // Transaction Finished, wait for the returned delay + wait(t); + break; + + case tlm::TLM_ACCEPTED: + case tlm::TLM_UPDATED: + // Transaction not yet finished, wait for the end of it + wait(mEndEvent); + break; + + default: + sc_assert(0); exit(1); + }; + + logEndTransaction(trans); + + // Acquire DMI pointer if one is available: + if (trans.is_dmi_allowed()) + { + trans.set_write(); + dmi_type tmp; + if (socket->get_direct_mem_ptr(trans, + tmp)) + { + // FIXME: No support for separate read/write ranges + sc_assert(tmp.is_read_write_allowed()); + mDMIData = tmp; + } + } + } + } + } + wait(); } - + sync_enum_type myNBTransport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) diff --git a/tests/tlm/static_extensions/ext2gp2ext/SimpleLTInitiator_ext.h b/tests/tlm/static_extensions/ext2gp2ext/SimpleLTInitiator_ext.h index f9875d907..0b7863b11 100644 --- a/tests/tlm/static_extensions/ext2gp2ext/SimpleLTInitiator_ext.h +++ b/tests/tlm/static_extensions/ext2gp2ext/SimpleLTInitiator_ext.h @@ -131,97 +131,98 @@ class SimpleLTInitiator_ext : public sc_core::sc_module void run() { - transaction_type trans; - phase_type phase; - sc_core::sc_time t; - // make sure that our transaction has the proper extension: - my_extension* tmp_ext = new my_extension(); - tmp_ext->m_data = 11; - - trans.set_extension(tmp_ext); - - while (initTransaction(trans)) + // scope needed to free the memory of the local variables (co-routine stacks are not proper cleaned up at simulation end) { - // Create transaction and initialise phase and t - phase = tlm::BEGIN_REQ; - t = sc_core::SC_ZERO_TIME; - - logStartTransation(trans); - /////////////////////////////////////////////////////////// - // DMI handling: - // We use the DMI hint to check if it makes sense to ask for - // DMI pointers. The pattern is: - // - if the address is covered by a DMI region do a DMI access - // - otherwise do a normal transaction - // -> check if we get a DMI hint and acquire the DMI pointers if it - // is set - /////////////////////////////////////////////////////////// - - // Check if the address is covered by our DMI region - if ( (trans.get_address() >= mDMIData.get_start_address()) && - (trans.get_address() <= mDMIData.get_end_address()) ) + transaction_type trans; + phase_type phase; + sc_core::sc_time t; + // make sure that our transaction has the proper extension: + my_extension* tmp_ext = new my_extension(); + tmp_ext->m_data = 11; + + trans.set_extension(tmp_ext); + + while (initTransaction(trans)) { - // We can handle the data here. As the logEndTransaction is - // assuming something to happen in the data structure, we really - // need to do this: - trans.set_response_status(tlm::TLM_OK_RESPONSE); - sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; - - } else { - mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; - } - - // Do the wait immediately. Note that doing the wait here eats - // almost all the performance anyway, so we only gain something - // if we're using temporal decoupling. - if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { - wait(mDMIData.get_write_latency()); - - } else { - wait(mDMIData.get_read_latency()); - } + // Create transaction and initialise phase and t + phase = tlm::BEGIN_REQ; + t = sc_core::SC_ZERO_TIME; - logEndTransaction(trans); - - } else { // we need a full transaction - switch (socket->nb_transport_fw(trans, phase, t)) { - case tlm::TLM_COMPLETED: - // Transaction Finished, wait for the returned delay - wait(t); - break; - - case tlm::TLM_ACCEPTED: - case tlm::TLM_UPDATED: - // Transaction not yet finished, wait for the end of it - wait(mEndEvent); - break; - - default: - sc_assert(0); exit(1); - }; - - logEndTransaction(trans); + logStartTransation(trans); + /////////////////////////////////////////////////////////// + // DMI handling: + // We use the DMI hint to check if it makes sense to ask for + // DMI pointers. The pattern is: + // - if the address is covered by a DMI region do a DMI access + // - otherwise do a normal transaction + // -> check if we get a DMI hint and acquire the DMI pointers if it + // is set + /////////////////////////////////////////////////////////// - // Acquire DMI pointer if one is available: - if (trans.is_dmi_allowed()) + // Check if the address is covered by our DMI region + if ( (trans.get_address() >= mDMIData.get_start_address()) && + (trans.get_address() <= mDMIData.get_end_address()) ) { - trans.set_write(); - dmi_type tmp; - if (socket->get_direct_mem_ptr(trans, - tmp)) + // We can handle the data here. As the logEndTransaction is + // assuming something to happen in the data structure, we really + // need to do this: + trans.set_response_status(tlm::TLM_OK_RESPONSE); + sc_dt::uint64 tmp = trans.get_address() - mDMIData.get_start_address(); + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp] = mData; + + } else { + mData = *(unsigned int*)&mDMIData.get_dmi_ptr()[tmp]; + } + + // Do the wait immediately. Note that doing the wait here eats + // almost all the performance anyway, so we only gain something + // if we're using temporal decoupling. + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { + wait(mDMIData.get_write_latency()); + + } else { + wait(mDMIData.get_read_latency()); + } + + logEndTransaction(trans); + + } else { // we need a full transaction + switch (socket->nb_transport_fw(trans, phase, t)) { + case tlm::TLM_COMPLETED: + // Transaction Finished, wait for the returned delay + wait(t); + break; + + case tlm::TLM_ACCEPTED: + case tlm::TLM_UPDATED: + // Transaction not yet finished, wait for the end of it + wait(mEndEvent); + break; + + default: + sc_assert(0); exit(1); + }; + + logEndTransaction(trans); + + // Acquire DMI pointer if one is available: + if (trans.is_dmi_allowed()) { - // FIXME: No support for separate read/write ranges - sc_assert(tmp.is_read_write_allowed()); - mDMIData = tmp; + trans.set_write(); + dmi_type tmp; + if (socket->get_direct_mem_ptr(trans, + tmp)) + { + // FIXME: No support for separate read/write ranges + sc_assert(tmp.is_read_write_allowed()); + mDMIData = tmp; + } } } } } - delete tmp_ext; wait(); - } sync_enum_type myNBTransport(transaction_type& trans,