Dynamic Processes (sc_spawn)
How to dynamically create processes at runtime instead of relying solely on static elaboration.
How to Read This Lesson
These core semantics are where experienced SystemC engineers earn their calm. We will name the scheduler rule, then show how the source enforces it.
Dynamic Processes (sc_spawn)
Traditionally, SystemC processes (SC_METHOD and SC_THREAD) are static. They are registered during the elaboration phase (in the module's constructor) and exist for the entire duration of the simulation.
But what if you are building an RTOS model and need to dynamically spawn a thread when an interrupt fires? Or what if you want to spawn a watcher thread for every active TLM transaction?
Enter sc_spawn, introduced in IEEE 1666 to provide dynamic process capabilities.
Under the Hood: C++ Implementation in Accellera SystemC
How does SystemC spawn a thread mid-simulation? The implementation relies heavily on C++ functors and the sc_process_b base class.
sc_bindand Functors: When you usesc_bind, SystemC leverages C++ template metaprogramming (similar tostd::bind) to package your function pointer and its arguments into a singlesc_process_hostfunctor object.- Dynamic Process Creation: The
sc_spawnfunction allocates a newsc_thread_process(orsc_method_process) object on the heap, passes it the functor, and registers it with the globalsc_simcontext. sc_process_handleReference Counting: SystemC returns ansc_process_handlerather than a raw pointer. Why? Because dynamic processes can die. If the thread function returns or is killed, thesc_thread_processobject is destroyed. Thesc_process_handleacts like a smart pointer; it safely tracks whether the underlying process object is still valid (handle.valid()) to prevent segfaults when trying to kill an already-dead thread.
Source and LRM Trail
Advanced core behavior should always be checked against Docs/LRMs/SystemC_LRM_1666-2023.pdf before source details. For implementation, read .codex-src/systemc/src/sysc/kernel and .codex-src/systemc/src/sysc/communication, especially the scheduler, events, object hierarchy, writer policy, report handler, and async update path.
Spawning a Thread
The sc_core::sc_spawn function allows you to create a new process dynamically at runtime. It accepts a callable (usually generated via sc_bind), a string name, and optional spawn configuration options.
Process Handles (sc_process_handle)
When you spawn a process, sc_spawn returns an sc_process_handle. You can use this handle to control the dynamic process:
handle.suspend(): Pauses the thread.handle.resume(): Resumes the thread.handle.disable(): Disables sensitivity.handle.kill(): Immediately terminates the thread and throws ansc_unwind_exception.
Complete Dynamic Process Example
Below is a complete, fully compilable sc_main program demonstrating how to dynamically spawn threads, pass arguments to them via sc_bind, configure them as threads or methods, and manage their lifecycle using sc_process_handle.
#include <systemc>
#include <iostream>
#include <vector>
// 1. The function we want to spawn dynamically
void my_dynamic_task(int id) {
try {
std::cout << "@" << sc_core::sc_time_stamp() << " Task " << id << " started!" << std::endl;
// Consume some simulation time
sc_core::wait(10, sc_core::SC_NS);
std::cout << "@" << sc_core::sc_time_stamp() << " Task " << id << " finished!" << std::endl;
} catch (const sc_core::sc_unwind_exception& e) {
std::cout << "@" << sc_core::sc_time_stamp() << " Task " << id << " was killed!" << std::endl;
// Optionally re-throw or handle cleanup
}
}
// 2. The module that spawns other processes
class Spawner : public sc_core::sc_module {
public:
SC_HAS_PROCESS(Spawner);
std::vector<sc_core::sc_process_handle> handles;
Spawner(sc_core::sc_module_name name) : sc_core::sc_module(name) {
SC_THREAD(run_spawner);
}
void run_spawner() {
sc_core::wait(5, sc_core::SC_NS);
std::cout << "Spawner: Creating dynamic threads..." << std::endl;
for (int i = 1; i <= 3; ++i) {
// Configure the dynamic process
sc_core::sc_spawn_options opt;
// E.g., make it a method instead of a thread: opt.spawn_method();
// opt.dont_initialize();
std::string task_name = "dynamic_task_" + std::to_string(i);
// Spawn a new thread dynamically and store the handle!
sc_core::sc_process_handle h = sc_core::sc_spawn(
sc_core::sc_bind(&my_dynamic_task, i),
task_name.c_str(),
&opt
);
handles.push_back(h);
}
// Wait a short time, then kill the second thread prematurely
sc_core::wait(5, sc_core::SC_NS);
std::cout << "Spawner: Killing Task 2 prematurely..." << std::endl;
if (handles.size() >= 2 && handles[1].valid()) {
handles[1].kill();
}
}
};
int sc_main(int argc, char* argv[]) {
Spawner spawner_mod("spawner_mod");
sc_core::sc_start(30, sc_core::SC_NS);
return 0;
}Spawn Options
You can configure the dynamically spawned process using sc_core::sc_spawn_options object passed as the third parameter:
spawn_method(): Force the spawned process to be anSC_METHODinstead of a thread. (Note: Methods cannot callwait()).set_sensitivity(): Make the dynamic process sensitive to specific events, just like static processes.dont_initialize(): Prevent the process from running immediately upon creation, meaning it waits for an event in its sensitivity list to trigger it first.
This makes sc_spawn incredibly powerful for modeling complex software stacks, dynamic thread pools in Virtual Platforms, and reactive sequence items in UVM!
Comments and Corrections