SystemC 的动态进程机制,核心是在仿真运行时(而非 elaboration 阶段)动态创建、调度和管理进程,突破静态进程(SC_THREAD/SC_METHOD)的 "预注册" 限制,大幅提升建模灵活性,尤其适用于测试台动态配置、事务跟踪、动态流量生成等场景。
一、动态进程的核心概念
-
定义 :仿真执行期间(sc_start () 之后)通过
sc_spawn创建的进程,可按需创建 / 终止,支持带参数、返回值的进程调用,弥补静态进程 "elaboration 阶段固定注册" 的局限性。 -
核心优势 :
- 适配动态场景:如测试台根据配置启动不同数量的流量生成器、跟踪拆分事务的完成状态。
- 资源优化:无需预分配最大数量的静态进程,仅在需要时创建,节省仿真资源。
-
与静态进程的关键区别 :
特性 静态进程(SC_THREAD/SC_METHOD) 动态进程(sc_spawn 创建) 创建时机 elaboration 阶段(模块构造时) 仿真运行时(sc_start () 之后) 参数传递 不支持直接传递 支持最多 8 个参数(值 / 引用传递) 返回值 不支持 支持通过引用接收返回值 灵活性 固定数量和行为 按需创建 / 终止,动态配置
二、动态进程的启用与基础语法
(一)启用动态进程
必须在包含 SystemC 头文件前定义宏SC_INCLUDE_DYNAMIC_PROCESSES,启用动态进程功能:
cpp
运行
#define SC_INCLUDE_DYNAMIC_PROCESSES // 必须在#include <systemc>之前
#include <systemc>
(二)动态进程的函数声明
被 spawn 的函数可是普通函数 或模块成员函数,支持参数和返回值(静态进程不支持):
cpp
运行
// 普通函数示例(无参数、无返回值)
void inject(void);
// 普通函数示例(带参数、带返回值)
int count_changes(sc_signal<int>& sig);
// 模块成员函数示例
class TestChan : public sc_module {
public:
bool Track(sc_signal<packet>& pkt); // 带引用参数、返回bool
};
(三)动态进程注册:sc_spawn
通过sc_spawn函数注册动态进程,返回sc_process_handle对象,用于后续进程控制(如等待终止、暂停 / 恢复)。
1. 核心语法
-
注册普通函数(无返回值): cpp
运行
sc_process_handle h = sc_spawn( sc_bind(&funcName, ARGS...), // 绑定函数与参数(sc_bind用于参数传递) "process_name", // 进程名(可选,建议唯一) spawnOptions // 进程选项(可选,如设置为SC_METHOD) ); -
注册成员函数(带返回值): cpp
运行
int ret_val; // 存储返回值的变量 sc_process_handle h = sc_spawn( sc_bind(&ClassName::methName, this, ARGS...), // this指向当前模块实例 "process_name", spawnOptions, &ret_val // 接收返回值的引用 );
2. 参数传递规则
-
默认按值传递:参数会被拷贝到进程栈中。
-
按引用 / 常量引用传递:需使用
sc_ref(var)或sc_cref(var)包装,避免拷贝大对象或修改外部变量:cpp
运行
sc_signal<int> sig; sc_spawn(sc_bind(&count_changes, sc_ref(sig))); // 引用传递sig
(四)进程选项:sc_spawn_option
通过sc_spawn_option配置动态进程的特性,核心用法:
cpp
运行
sc_spawn_option opt;
opt.spawn_method(); // 设置为SC_METHOD类型(默认是SC_THREAD)
opt.dont_initialize(); // 跳过初始化执行
opt.set_sensitivity(&evt); // 设置静态灵敏度(绑定事件)
opt.set_stack_size(1024); // 设置进程栈大小(仅专家级使用)
三、动态进程的关键操作
(一)进程句柄:sc_process_handle
sc_spawn返回的sc_process_handle是动态进程的 "控制接口",核心功能:
- 等待进程终止:
wait(h.terminated_event())(仅适用于 SC_THREAD,SC_METHOD 无终止逻辑)。 - 获取进程名:
h.name()。 - 进程控制:暂停、恢复、终止等(见下文 "进程控制方法")。
示例:简单动态进程创建与等待
cpp
运行
void spawned_thread() {
cout << "动态进程启动:" << sc_get_current_process_handle().name() << endl;
wait(10, SC_NS);
cout << "动态进程退出" << endl;
}
void main_thread() {
// 创建动态进程,获取句柄
sc_process_handle h = sc_spawn(sc_bind(&spawned_thread), "dyn_thread");
wait(h.terminated_event()); // 等待动态进程终止
cout << "主进程:动态进程已完成" << endl;
}
(二)SC_FORK/SC_JOIN:并行启动多个动态进程
用于批量启动多个动态进程,并等待所有进程完成(类似 Verilog 的 fork/join),核心语法:
cpp
运行
SC_FORK
sc_spawn(sc_bind(&func1, args1), "p1"),
sc_spawn(sc_bind(&func2, args2), "p2"),
sc_spawn(sc_bind(&func3, args3), "p3")
SC_JOIN
示例:多接口并行测试
cpp
运行
SC_MODULE(ForkTest) {
sc_fifo<double> fifo1, fifo2;
SC_CTOR(ForkTest) { SC_THREAD(main_thread); }
void main_thread() {
// 并行启动两个动态进程,分别处理两个FIFO
SC_FORK
sc_spawn(sc_bind(&ForkTest::process_fifo, this, sc_ref(fifo1)), "fifo1_proc"),
sc_spawn(sc_bind(&ForkTest::process_fifo, this, sc_ref(fifo2)), "fifo2_proc")
SC_JOIN
cout << "所有FIFO处理完成" << endl;
}
void process_fifo(sc_fifo<double>& fifo) {
double data;
while (fifo.nb_read(data)) {
cout << "处理数据:" << data << endl;
wait(5, SC_NS);
}
}
};
(三)进程控制方法(提议中的标准特性)
SystemC 2.3+ 提议在sc_process_handle中添加进程控制方法,支持暂停、恢复、终止等操作,核心接口:
cpp
运行
// 枚举:控制是否影响子进程
enum sc_descendant_inclusion_info {
SC_NO_DESCENDANTS, // 仅影响当前进程
SC_INCLUDE_DESCENDANTS // 影响当前进程及子进程
};
// 核心控制方法
h.suspend(SC_NO_DESCENDANTS); // 暂停进程
h.resume(SC_NO_DESCENDANTS); // 恢复进程
h.disable(SC_NO_DESCENDANTS); // 禁用进程(忽略灵敏度)
h.enable(SC_NO_DESCENDANTS); // 启用进程
h.kill(SC_NO_DESCENDANTS); // 终止进程
h.reset(SC_NO_DESCENDANTS); // 异步重启进程
h.throw_it(exc, SC_NO_DESCENDANTS); // 向进程抛出异常(模拟中断)
关键说明:
- 这些方法目前是标准提议,需确认所用 SystemC 版本是否支持。
suspend/resume:暂停时仍收集事件,恢复后立即响应;disable/enable:禁用时忽略所有事件。
四、核心要点与实践建议
- 启用动态进程的宏必须前置 :
SC_INCLUDE_DYNAMIC_PROCESSES需在#include <systemc>之前定义,否则编译报错。 - 参数传递注意事项 :
- 引用传递必须用
sc_ref/sc_cref包装,否则编译失败。 - 返回值的存储变量需保证在进程生命周期内有效(避免栈变量被释放)。
- 引用传递必须用
- 进程名唯一性:动态进程名会自动拼接父模块层级名,建议显式指定唯一名称,便于调试。
- SC_METHOD 类型的动态进程 :需通过
sc_spawn_option::spawn_method()设置,且不可调用wait()(与静态 SC_METHOD 规则一致)。 - 资源管理 :动态进程创建后需通过
sc_process_handle跟踪,避免 "孤儿进程" 导致仿真资源泄漏。 - 适用场景优先 :动态进程适合测试台、事务跟踪等灵活场景;核心功能建模(如固定硬件逻辑)仍建议用静态进程(仿真效率更高)。
五、练习指引
- 核心示例:simple_spawn(基础动态进程创建)、Fork(SC_FORK/SC_JOIN 并行测试)、process_control(进程控制方法演示)。
- 练习重点:尝试动态创建带参数的进程、用
sc_process_handle等待进程终止、通过 SC_FORK/SC_JOIN 实现多接口并行仿真。