在SystemC中,通道是实现模块间通信的核心机制。它的设计哲学是将模块的功能实现与模块间的通信方式彻底分离,使得系统在从高层抽象到底层RTL的逐步细化过程中,只需要更换或修改通道,而无需改动模块本身的功能代码,极大地提升了模型的可重用性和灵活性。通道可以理解为一个“通信的容器”,它封装了所有通信协议和同步逻辑的具体实现。
1 分类
1.1 原生通道 (Primitive Channel)
定义:最基础的通道,不包含其他模块或进程,也不能访问其他通道。
用途:提供最基本的通信和同步机制。
常见类型:
sc_signal:用于实现RTL风格的计算-提交(compute-commit)范式。它拥有“当前值”和“下一个值”,任何写入操作都只修改下一个值,并在仿真内核的更新阶段(update phase)统一提交为当前值。这保证了在同一个delta-cycle内,所有进程对同一信号的读取结果是一致的,避免了零延迟竞争。
sc_fifo:提供具有固定深度的先进先出(FIFO)通道,用于数据缓冲。
sc_mutex、sc_semaphore:用于进程间的互斥和同步。
所有原生通道都继承自基类 sc_prim_channel,它们必须继承并实现一个或多个 SystemC 接口类。
1.2 层次化通道 (Hierarchical Channel)
定义:它本身就是一个sc_module,因此内部可以包含进程、端口、子模块以及其他通道。
用途:用于实现复杂的通信协议,例如总线模型或TLM(Transaction-Level Modeling,事务级建模)中的通用通道。一个典型的例子是AHB或AXI总线模型,它内部包含了仲裁器、译码器等逻辑,外部模块通过其提供的接口方法来发起总线事务。
2 原生通道
2.1 sc_mutex
sc_mutex 是SystemC提供的一个用于互斥访问共享资源的原生通道。它确保了同一时刻只有一个进程能够进入临界区执行代码,从而保护共享数据不被并发修改导致错误。
sc_mutex 类实现了 sc_mutex_if 接口类。这个类包含了几种访问方法,包括阻塞和非阻塞风格。阻塞方法只能在 SC_THREAD 进程中使用。
互斥量维护一个锁定状态(locked/unlocked),进程通过两个操作来控制对共享资源的访问:
| 操作 | 方法 | 行为 |
|---|---|---|
| 锁定(Lock/获取) | lock() | 如果互斥量未锁定,立即锁定并返回;如果已被锁定,进程被阻塞直到互斥量被释放 |
| 尝试锁定(Trylock) | trylock() | 如果互斥量未锁定,立即锁定并返回true;如果已被锁定,立即返回false(不阻塞) |
| 解锁(Unlock/释放) | unlock() | 释放互斥量,唤醒一个因等待该互斥量而被阻塞的进程 |
classsc_mutex:publicsc_interface{public:// 构造函数sc_mutex();// 锁定互斥量(阻塞式)intlock();// 尝试锁定互斥量(非阻塞)inttrylock();// 解锁互斥量intunlock();// 获取当前锁定状态(仅用于调试)intget_lock()const;};说明:
lock() - 阻塞式获取
如果互斥量未锁定:立即锁定,返回0(成功)
如果互斥量已锁定:进程被挂起,直到拥有者调用unlock()释放它
trylock() - 非阻塞式尝试
如果互斥量未锁定:立即锁定,返回0(成功)
如果互斥量已锁定:不阻塞,返回-1(失败)
unlock() - 释放
释放互斥量
如果有进程因lock()阻塞,唤醒最早被阻塞的一个
只有当前拥有该互斥量的进程才能调用unlock()
get_lock() - 查看锁定状态(1=锁定,0=未锁定,主要用于调试)
sc_mutex 在电子设计中的一个应用是对共享总线进行仲裁。在没有仲裁器设计的情况下,可以使用 sc_mutex 快速管理总线资源,直到仲裁器设计完成。
sc_mutex 也可以作为实现总线模型的类的一部分,如下所示:
classbus:publicsc_module{sc_mutex bus_access;…voidwrite(intaddr,intdata){bus_access.lock();// perform writebus_access.unlock();}…};sc_mutex 的一个缺点是没有事件来通知 sc_mutex 何时被释放。这个缺点意味着你需要基于其他事件或基于时间的延迟反复使用 trylock() 来访问共享资源。
2.2 sc_semaphore
sc_semaphore 是SystemC提供的一个用于进程间同步与互斥的原生通道。它本质上是一个计数信号量(Counting Semaphore),用于控制多个进程对共享资源的访问。
对于某些资源,你可能想要对它们建模多个副本或所有者。为了管理这种类型的资源,SystemC 提供了 sc_semaphore 类。
sc_semaphore 类继承并实现了 sc_semaphore_if 接口。在创建 sc_semaphore 对象时,需要指定可用的数量。从某种意义上说,互斥锁只是计数为一的信号量。对 sc_semaphore 的访问包括等待可用资源,然后在使用完资源后发布完成通知。
信号量维护一个整数值(计数值),代表当前可用的资源数量。进程可以通过两种操作来使用它:
| 操作 | 方法 | 行为 |
|---|---|---|
| 获取(Wait/取走) | wait() | 如果计数值 > 0,将计数值减1并立即返回;如果计数值 = 0,进程被阻塞,直到计数值 > 0 |
| 释放(Post/归还) | post() | 将计数值加1,并唤醒一个因等待该信号量而被阻塞的进程(如果有的话) |
与互斥量(sc_mutex)的区别
| 特性 | sc_semaphore | sc_mutex |
|---|---|---|
| 计数 | 可以有多个(计数值 > 1) | 始终为1(二值信号量) |
| 所有者 | 无所有者概念,任何进程都可以post | 有所有者,只有获取它的进程才能释放 |
| 适用场景 | 控制多个同类资源的访问 | 保护共享资源的互斥访问 |
| 典型用途 | 连接池、缓冲池、生产者-消费者 | 临界区保护 |
sc_semaphore的接口:
classsc_semaphore:publicsc_interface{public:// 构造函数:初始计数值默认为1explicitsc_semaphore(intinit_value=1);// 获取信号量(如果不可用则阻塞)intwait();// 尝试获取信号量(非阻塞),成功返回0,失败返回-1inttrywait();// 释放信号量intpost();// 获取当前计数值intget_value()const;};说明:
wait() - 阻塞式获取
如果计数值 > 0:计数值减1,立即返回0(成功)
如果计数值 = 0:进程被挂起,直到有post()操作唤醒它
trywait() - 非阻塞式尝试获取
如果计数值 > 0:计数值减1,返回0(成功)
如果计数值 = 0:不阻塞,直接返回-1(失败)
post() - 释放
计数值加1
如果有进程因wait()阻塞,唤醒其中最早被阻塞的一个
get_value() - 查看当前计数值(仅用于调试)
2.3 sc_fifo
sc_fifo 是SystemC提供的一个有界先进先出(FIFO)数据缓冲通道,用于在进程间传递数据。它内部维护一个固定大小的环形缓冲区,遵循先进先出的原则,是生产者-消费者模式的典型实现工具。
| 操作 | 方法 | 行为 |
|---|---|---|
| 写入(Write) | write() | 如果FIFO未满,将数据写入队列尾部并立即返回;如果FIFO已满,进程被阻塞直到有空间可用(被read()释放) |
| 读取(Read) | read() | 如果FIFO非空,从队列头部读取数据并立即返回;如果FIFO为空,进程被阻塞直到有数据写入 |
| 非阻塞写入 | nb_write() | 如果FIFO未满,写入数据并返回true;如果已满,立即返回false(不阻塞) |
| 非阻塞读取 | nb_read() | 如果FIFO非空,读取数据并返回true;如果为空,立即返回false(不阻塞) |
| 窥探(Peek) | peek() | 读取队首数据但不移除,如果FIFO为空则阻塞 |
| 非阻塞窥探 | nb_peek() | 读取队首数据但不移除,如果为空则立即返回false |
| 查询当前数据个数 | num_available() | 返回FIFO中有效数据的数量 |
| 查询空闲空间 | num_free() | 返回FIFO中还能写入多少个数据 |
template<classT>classsc_fifo:publicsc_interface{public:// 构造函数:指定FIFO深度(默认16)explicitsc_fifo(intsize_=16);// ====== 阻塞式操作 ======voidwrite(constT&);voidread(T&);Tread();// ====== 非阻塞式操作 ======boolnb_write(constT&);boolnb_read(T&);// ====== 窥探操作(读取但不移除) ======voidpeek(T&)const;Tpeek()const;boolnb_peek(T&)const;// ====== 状态查询 ======intnum_available()const;// 当前数据数量intnum_free()const;// 当前空闲空间boolempty()const;// 是否为空boolfull()const;// 是否为满// ====== 重置 ======voidreset();// 清空FIFO};