本文为 SystemC 实现寄存器来同步的建模方法。
# 应用场景
仿真时可能有类似这样的需求:
将一系列的指令发送到三个模块 A、B、C,三个模块相互独立地执行这些指令。
为了进行同步控制,这些指令中含有 sync 类型的指令,并借助寄存器来实现模块之间的同步。
比如我创建 20 个寄存器,A 模块的 Sync 指令是往寄存器 0 号位写入 1,B 收到的 sync 指令是读寄存器 0 号位,读到 1 才能执行后续指令;然后 B 往寄存器 1 号位写入 10,C 在寄存器 1 号位读到 10,才能够执行后续指令。以此实现执行同步。
# 初步尝试
依据上述需求,我非常直接地创建了 sc_signal<int> reg[20]
来作为寄存器。
每个模块通过以下方式对 sync 指令进行读和写
// 然后写寄存器的时候调用 : | |
mSyncRegisters->reg[reg_id].write(num); | |
// 读寄存器的时候调用: | |
while (mSyncRegisters->reg[reg_id].read() != num) { | |
wait(mSyncRegisters->reg[reg_id].value_changed_event()); // 等待寄存器变化 | |
} |
但是发现了如下错误:
Error: (E115) sc_signal<T> cannot have more than one driver
原来 sc_signal 这个变量不能被多个 module 驱动。此时,问题就变得棘手起来。
# 解决方案
报错寻找问题,以及询问 deepseek,花费大半天时间。
终究还是没能继续使用 sc_signal
。deepseek 帮我用 sc_event
手写了整个逻辑。
这里是解决方案:
#include <systemc.h> | |
#include <vector> | |
class reg_file_if : public virtual sc_core::sc_interface { | |
public: | |
virtual void write(int reg_index, int value) = 0; | |
virtual int read(int reg_index) = 0; | |
virtual void wait_until(int reg_index, int value) = 0; | |
}; | |
class SyncRegFile : public sc_core::sc_module, public reg_file_if { | |
public: | |
SC_HAS_PROCESS(SyncRegFile); | |
SyncRegFile(sc_core::sc_module_name name, size_t num_regs) | |
: sc_core::sc_module(name), | |
reg_array(num_regs, 0), | |
reg_events(num_regs){} | |
virtual void write(int reg_index, int value) override { | |
if (reg_index < 0 || static_cast<size_t>(reg_index) >= reg_array.size()) | |
return; | |
mutex.lock(); | |
reg_array[reg_index] = value; | |
mutex.unlock(); | |
reg_events[reg_index].notify(sc_core::SC_ZERO_TIME); | |
} | |
virtual int read(int reg_index) override { | |
if (reg_index < 0 || static_cast<size_t>(reg_index) >= reg_array.size()) | |
return 0; | |
mutex.lock(); | |
int value = reg_array[reg_index]; | |
mutex.unlock(); | |
return value; | |
} | |
virtual void wait_until(int reg_index, int value) override { | |
if (reg_index < 0 || static_cast<size_t>(reg_index) >= reg_array.size()) | |
return; | |
while (true) { | |
mutex.lock(); | |
bool condition_met = (reg_array[reg_index] == value); | |
mutex.unlock(); | |
if (condition_met) break; | |
wait(reg_events[reg_index]); | |
} | |
} | |
private: | |
std::vector<int> reg_array; | |
std::vector<sc_core::sc_event> reg_events; | |
sc_core::sc_mutex mutex; | |
}; | |
class WorkerModule : public sc_core::sc_module { | |
public: | |
SC_HAS_PROCESS(WorkerModule); | |
sc_core::sc_port<reg_file_if> reg_file; | |
const std::string module_name; | |
WorkerModule(sc_core::sc_module_name name, const char* mod_name) | |
: sc_module(name), module_name(mod_name) | |
{ | |
SC_THREAD(main_process); | |
} | |
void main_process() { | |
if (module_name == "A") { | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleA: Writing reg0=1" << std::endl; | |
reg_file->write(0, 1); | |
wait(10, sc_core::SC_NS); | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleA: Finished" << std::endl; | |
} | |
else if (module_name == "B") { | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleB: Waiting for reg0==1" << std::endl; | |
reg_file->wait_until(0, 1); | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleB: Writing reg1=10" << std::endl; | |
reg_file->write(1, 10); | |
wait(5, sc_core::SC_NS); | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleB: Finished" << std::endl; | |
} | |
else if (module_name == "C") { | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleC: Waiting for reg1==10" << std::endl; | |
reg_file->wait_until(1, 10); | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleC: Starting execution" << std::endl; | |
wait(15, sc_core::SC_NS); | |
std::cout << "[" << sc_core::sc_time_stamp() << "] ModuleC: Finished" << std::endl; | |
} | |
} | |
}; | |
class Top : public sc_core::sc_module { | |
public: | |
SyncRegFile reg_file; | |
WorkerModule mod_a, mod_b, mod_c; | |
Top(sc_core::sc_module_name name) | |
: sc_module(name), | |
reg_file("RegFile", 20), // 20 个寄存器 | |
mod_a("ModuleA", "A"), | |
mod_b("ModuleB", "B"), | |
mod_c("ModuleC", "C") | |
{ | |
mod_a.reg_file(reg_file); | |
mod_b.reg_file(reg_file); | |
mod_c.reg_file(reg_file); | |
} | |
}; | |
int sc_main(int argc, char* argv[]) { | |
Top top("Top"); | |
std::cout << "Starting simulation..." << std::endl; | |
sc_core::sc_start(100, sc_core::SC_NS); // 运行 100ns | |
std::cout << "Simulation finished at " << sc_core::sc_time_stamp() << std::endl; | |
return 0; | |
} |
预期输出:
Starting simulation...
[0 s] ModuleA: Writing reg0=1
[0 s] ModuleB: Waiting for reg0==1
[0 s] ModuleC: Waiting for reg1==10
[0 s] ModuleB: Writing reg1=10
[0 s] ModuleC: Starting execution
[10 ns] ModuleA: Finished
[5 ns] ModuleB: Finished
[15 ns] ModuleC: Finished
Simulation finished at 15 ns
# 后记
本博客目前以及可预期的将来都不会支持评论功能。各位大侠如若有指教和问题,可以在我的 github 项目 或随便一个项目下提出 issue,或者知乎 私信,并指明哪一篇博客,我看到一定及时回复!