本文为 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,或者知乎 私信,并指明哪一篇博客,我看到一定及时回复!

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

XianMu WeChat Pay

WeChat Pay

XianMu Alipay

Alipay