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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- name: clang-format
run: nix develop --command ${{github.workspace}}/support/scripts/cpp-check-format.sh
- name: ast-grep
run: nix develop --command ${{github.workspace}}/support/scripts/run-ast-grep.sh
Configure-Build-and-Unit-Tests:
strategy:
matrix:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.idea
/cmake-build-*
/docs/tutorial/build*
56 changes: 44 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,51 @@ target_include_directories(Simo
)
target_include_directories(Simo PRIVATE src)

# Simo_includes_only is a header-only target
add_library(Simo_includes_only INTERFACE)
target_include_directories(Simo_includes_only
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(Simo_includes_only INTERFACE glaze::glaze)
target_compile_options(Simo_includes_only INTERFACE -fno-rtti)
# Avoid linking against Simo
if (APPLE)
target_link_options(Simo_includes_only INTERFACE
"LINKER:-undefined,dynamic_lookup"
)
else()
target_link_options(Simo_includes_only INTERFACE
"LINKER:--allow-shlib-undefined"
)
endif()


add_executable(SimoSim src/SimoSim/SimoSim.cc)
target_link_libraries(SimoSim PRIVATE Simo)

install(TARGETS Simo SimoSim)
install(TARGETS Simo SimoSim Simo_includes_only
EXPORT SimoTargets
)
install(DIRECTORY include/
DESTINATION include
)
install(EXPORT SimoTargets
FILE SimoTargets.cmake
NAMESPACE Simo::
DESTINATION lib/cmake/Simo
)

# Generate the Package Configuration
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"cmake/Simo/SimoConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake/SimoConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake/Simo/SimoConfigVersion.cmake"
DESTINATION lib/cmake/Simo)



## Unit tests
Expand Down Expand Up @@ -171,19 +208,14 @@ target_link_libraries(test_Module PRIVATE Simo)
add_library(SimoTestPingPongCollection SHARED
tests/collection/PingPongCollection.cc
)
set_target_properties(SimoTestPingPongCollection PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN 1
)
target_compile_options(SimoTestPingPongCollection PRIVATE -fno-exceptions)
# Compile only with includes. Do not link against libraries
target_link_libraries(SimoTestPingPongCollection PRIVATE $<COMPILE_ONLY:Simo>)
# Avoid linking against Simo
if (APPLE)
target_link_options(SimoTestPingPongCollection PRIVATE
"LINKER:-undefined,dynamic_lookup"
)
else ()
target_link_options(SimoTestPingPongCollection PRIVATE
"LINKER:--allow-shlib-undefined"
)
endif ()
target_link_libraries(SimoTestPingPongCollection PRIVATE Simo_includes_only)


# Make this custom command to trigger re-build for targets that uses SimoTestPingPongCollection libraries indirectly (like
# test_Collection)
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Guides (Markdown files inside `docs` folder) and Doxygen built from the main bra
```bash
git clone https://github.com/fusiled/Simo
cd Simo
git submodule update --init
```

## Build
Expand All @@ -41,6 +40,10 @@ The [nix](https://nixos.org/learn/) flake simplifies the setup of the dependenci
- Run `nix develop` to enter a virtual environment with all the required tools installed.
The GitHub actions run entering the virtual environment of `nix develop`.

## First Steps

Look inside [docs/tutorial](./docs/tutorial) .

## Features

- Loading components from shared objects at runtime ([examples](./tests/collection/CollectionTest.cc))
Expand All @@ -65,7 +68,5 @@ This can be achieved with:

## To Do
- Create a collector with periodic window collection functionality
- Create logging system for components
- More documentation and examples
- Error config in Simo::Context
- More documentation and examples
- Benchmark Simo against Gem5 and SystemC
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Find the tutorial in [tutorial folder](./tutorial)
Find the tutorial in [guided_examples folder](./guided_examples)

## Motivations

Expand Down
41 changes: 41 additions & 0 deletions docs/guided_examples/1_FirstModule_and_SimoSim/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2026 Matteo Fusi and Contributors
#
# Licensed 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.

cmake_minimum_required(VERSION 3.31.0)
project(SimoTutorial VERSION 0.0.1)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


# Look for Simo package
find_package(Simo REQUIRED)
get_filename_component(SIMO_PACKAGE_PREFIX "${Simo_DIR}/../../.." ABSOLUTE)


# Create you collection of modules
add_library(TutorialCollection SHARED
FirstModule.cc
)
# Hide symbols by default
set_target_properties(TutorialCollection PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN 1
)

# Use Simo::Simo_includes_only to only use the include files and avoid direct linking to Simo
target_link_libraries(TutorialCollection PRIVATE Simo::Simo_includes_only)
target_include_directories(TutorialCollection PRIVATE
"${SIMO_PACKAGE_PREFIX}/include"
)
121 changes: 121 additions & 0 deletions docs/guided_examples/1_FirstModule_and_SimoSim/FirstModule.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 2026 Matteo Fusi and Contributors
*
* Licensed 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.
*/

// Note 0: this is the general include for Simo
#include <Simo/Simo.h>

namespace Simo::Examples {

/// Log periodically a message and count the number of time the log took place
class SIMO_PUBLIC FirstModule : public Module {
public:
// Note 1
// Module initialization takes place here
InitializationStatus initialize(Context& sim_ctx_p,
const Parameters& parameters) override {
// Call initialization of subclass
if (const auto status = Module::initialize(sim_ctx_p, parameters);
!status.success()) {
// Fail if something bad happened
return status;
}
// Initialize variables from parameters
period = parameters.get<Time>("period")->value();

// Allocate statistics
num_events = &create_statistic<Statistics::Count>("num_events");

// Setup logging
if (const auto status = log_setup("tutorial_module.log");
!status.success()) {
return status;
}
populate_default_log_levels();
log_level(parameters.get<std::string>("log_level")->value());

// Start scheduling events
// schedule_at schedule at the specified time
sim_ctx().schedule_at(Time::zero, [this]() { log_event(); });
return InitializationStatus::ok(this);
}

void log_event() {
// schedule_in schedule and event at current_time + period
++(*num_events);
log(Log::LogLevel::INFO, [this]() {
return std::format("{}, Hello from TestModule!", num_events->value());
});
sim_ctx().schedule_in(period, [this]() { log_event(); });
}

protected:
// Remember the period to use to schedule events
Time period;
// Pointer to a statistic of the class
Statistics::Count* num_events = nullptr;
};

/// Parameters for TestModule
class SIMO_PUBLIC FirstModuleParameters : public Parameters {
public:
// Note 2
// Parameters describe inputs of a Module.
// Parameters with a default value are added with `trie.add<>`
// and without a default with `trie.add_unset<>`
FirstModuleParameters() {
// validator method is used to verify if a parameter is valid
trie.add_unset<Time>("period").validator(
[](const auto& t) { return t > Time::one; });
trie.add<std::string>("log_level", "WARNING");
}
};

} // namespace Simo::Examples

// Note 3 : SimoSim can dynamically load components through collections with
// the logic below.
// A collection is a list of factories.
// A factory tells how to instantiate a Module and an associated parameter
extern "C" {
constexpr Simo::Collections::SimoCollectionVersion VERSION{
.major = 0, .minor = 0, .patch = 1};

constexpr Simo::Collections::Factory TUTORIAL_FACTORY{
.name = "TestModule",
.get_module_function =
[] {
return static_cast<Simo::Module*>(new Simo::Examples::FirstModule());
},
.get_parameters_function =
[] {
return static_cast<Simo::Parameters*>(
new Simo::Examples::FirstModuleParameters());
}};

const char* collection_name = "TestCollection";
constexpr std::array FACTORY_LIST{TUTORIAL_FACTORY};
static Simo::Collections::SimoCollection collection{
.name = collection_name,
.version = VERSION,
.factory_list = FACTORY_LIST.data(),
.factory_list_size = FACTORY_LIST.size(),
};

// This exposes the collection
SIMO_PUBLIC const Simo::Collections::SimoCollection* simo_get_collection() {
return &collection;
}
}
76 changes: 76 additions & 0 deletions docs/guided_examples/1_FirstModule_and_SimoSim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# FirstModule SimoSim

## Introduction
The documents highlights some location in the source of [FirstModule.cc](./FirstModule.cc)
to better understand how to write your own `Module` class and how to use it
in `SimoSim`, a general purpose executable to run simulations.


### You first module

Modules are the basic block of the simulation. They have the ability to
schedule events in the simulation loop. To create a new module, it is
enough to inherit from `Simo::Module` class.


#### Module initialization
All the classed are declared in the
include `Simo/Simo.h` and they are contained in the `Simo` namespace.

```cpp
#include <Simo/Simo.h>
// ...
class SIMO_PUBLIC FirstModule : public Module {
public:
InitializationStatus initialize(Context& sim_ctx_p,
const Parameters& parameters) override {
// Initialization
}
// ...
};
```

The `initialize` method allows to initialize a module with the initial state of
the simulation.

`Context& sim_ctx_p` is a data-structure that represents the
global state of the simulation. Events are scheduled using this class. It is good
practice to call the method of the inherited class `Module::initialize` to start
the initialization. This will set the simulation context and do checks on the
parameters.

`const Parameters& parameters` stores a set of parameters that the class can query
and use. In the example, the period is fetched and set:

```cpp
period = parameters.get<Time>("period")->value();
```

In the initialization, a first event is scheduled at time zero to call the `log_event` method:

```cpp
sim_ctx().schedule_at(Time::zero, [this]() { log_event(); });
```

Other interesting content in the `initialize` method:
- a new statistic is created with `create_statistic` method.
- logging is set up to save the log at `tutorial_module.log`

#### Module event

`event_log` runs first at time zero, and it re-schedules itself every `period` time units
using the simulation context. Look for `Simo/core/Context.h` for the difference between
`schedule_at` and `schedule_in` of .
Source of the method:


```cpp
void log_event() {
// schedule_in schedule and event at current_time + period
++(*num_events);
log(Log::LogLevel::INFO, [this]() {
return std::format("{}, Hello from TestModule!", num_events->value());
});
sim_ctx().schedule_in(period, [this]() { log_event(); });
}
```
Loading