SystemC:Channel

在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_semaphoresc_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};