SystemC 基础 | Noxim

SystemC 是一个系统级软/硬件协同设计语言,用 SystemC 可以很方便地实现一个软件算法的硬件实现,以及完成一个系统级的设计。SystemC 是 C++ 的一个第三方库,有着 C++ 的很多语法特性,学习成本较低。本文主要介绍 SystemC 的一些基本语法。

模块 (module)

模块是 SystemC 设计中的基本设计单元。模块可以使得设计者将一个复杂的系统分割为一些更小但易于管理的部分。SystemC 模块的功能和作用与 HDL 语言中的模块是相类似的。

模块在 SystemC 中的关键字为 SC_MODULE。紧跟着关键字后的是模块的名称如 SC_MODULE(fifo),这就定义了一个叫 fifo 的模块。定义的模块也可以像 HDL 语言一样包含端口、信号、其它模块、处理过程和结构体,这些单元实现用以实现模块的功能。

通过端口可以将几个模块连接起来。模块被保存为 .h 文件。如果在一个模块中调用其它模块,只需像 C++ 中引入库一样将要调用的模块作为一个库引入即可。

过程 (process)

模块中的 process 类似于 C 语言中的子程序,与C语言中的子程序不同的的是它具有 HDL 语言中的触发功能。process 的具体工作部分被保存为 .cpp 文件。

process 的调用类似于在 C 语言中子程序的调用。根据不同的要求,SystemC 中有三种处理过程:

  • Methods: SC_METHOD()
  • Threads: SC_THREAD()
  • Clock Threads: SC_CTHREAD()

    SC_METHOD()

    SC_METHOD() 是用来描述组合逻辑,它由输入信号的变化触发,但不能在两次调用中保存控制状态。并且在 SC_METHOD() 中不能包含无限循环。由于组合逻辑有可能导致毛刺的产生,从而影响系统性能,所以 SC_METHOD() 不宜太复杂。下面是个简单的 SC_METHOD() 例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SC_MODULE (example) {
sc_in<bool> din;
sc_out<bool> dout; // 端口
void inverter(); // 处理过程声明
SC_CTOR(example) {
SC_METHOD(inverter);
sensitive(din); // 处理过程由输入变化触发
}
};

void example::inverter {
bool internal;
internal = din; // 输入数据取反后由输岀端口输出
dout = ~internal;
}

SC_METHOD() 是一种阻塞式的功能进程,当这个进程被敏感列表触发之后,获取仿真控制权开始运行,直到运行完成,将控制权返回 SystemC 仿真内核。使用 SC_METHOD() 注册的功能函数不能含有无限循环,这会导致仿真卡死在这个任务中,控制权无法返回仿真内核。

SC_THREAD()

SC_THREAD() 是最常用的处理过程,基本上可以用在任何地方。它是由输入信号的变化触发,但与 SC_METHOD() 不同的是它可以在两次调用中保存控制状态。它的功能类似于寄存器的功能。SC_THREAD() 中可以包含有 wait() 函数,这使得处理过程可以被挂起。

这一宏与 SC_METHOD() 的区别是这一种功能进程在仿真开始时运行,碰到 wait() 挂起,直到敏感列表中的信号再次触发这一进程,从上次跳出的 wait() 处继续运行,因此这种进程可以使用循环体包括 wait() 的无限循环。

SC_METHOD() 运行很多次,SC_THREAD() 运行一次。

SC_CTHREAD()

SC_CTHREAD()SC_THREAD() 的一种特殊情况。SC_CTHREAD() 能产生更好的综合效果。SC_CTHREAD() 中可以使用 wait() 函数。在不同的状态间加入 wait() 函数,设计人员可以用 SC_CTHREAD() 来实现状态机。这种设计风格是简便的而且容易理解。SC_CTHREAD() 只能由时钟信号沿触发,而 SC_THREAD() 可以由其它非时钟信号触发。如果在时钟上跳边触发,可以使用 pos() 函数,反之用 neg()。为进一步说明 SC_CTHREAD(),下面给出了一个 SC_CTHREAD() 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SC_MODULE (example) {
sc_in_clk clock; // 输入时钟
sc_in<bool> trigger, din;
sc_out<bool> dout;
void invert();
SC_CTOR (example) {
SC_CTHREAD(toggler, clock.pos()); // 时钟上升沿触发
}
}

void example::invert {
bool last = false;
for (;;) {
wait_until(trigger.delayed() == true); // 等到下个时钟上升沿且 trigger=1 再执行
last = din; dout = last; wait();
last = ~din; dout = last; wait(); // 下个上升沿才更改数据
}
}

端口与信号

与 HDL 语言相似,使用 SystemC 库就可以在 C 程序中加入端口和信号。这些原先 C 语言中没有的功能,使设计更复合硬件设计的要求。端口与端口通过信号加以连接。对于这一点习惯用 HDL 进行设计的设计人员是很容易理解和接受的,因为这和 HDL 中端口的功能是一样的。信号只是在一个模块的内部使用,这也是和 HDL 中信号的用法是一致的。下面是端口和信号声明的例子:

1
2
3
4
sc_in<type> din; // 输入端口
sc_out<type> dout; // 输出端口
sc_inout<type> q; // 输入输出端口
sc_signal<sc_logic> i[16]; // 创建一个具有 16 比特的逻辑型信号

信号连接使用如下所示的方式完成:

1
point.port_name(signal_name)

数据类型

考虑到硬件设计的要求,SystemC 中也加入了一些硬件设计中常用的数据类型。具体如下:

类型 说明
sc_int<n> 有符号整数类型,最大有 64 个比特位
sc_uint<n> 无符号整数类型,最大有 64 个比特位
sc_bigint<n> 有符号整数类型,任意比特位,其最大比特位定义在 sc_constants.h
sc_biguint<n> 符号整数类型,任意比特位,其最大比特位定义在 scconstants.h
sc_bit 二值数据,单比特位
sc_bv<n> 二值数据,任意比特位
sc_logic 四值数据,单比特位
sc_lv<n> 四值数据,任意比特位
sc_fixed 参数固定的有符号定点数
sc_ufixed 参数固定的无符号定点数
sc_fix 参数不固定的有符号定点数
sc_ufix 参数不固定的无符号定点数

编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// hello.h
#ifndef _HELLO_H
#define _HELLO_H
#include "systemc.h"
SC_MODULE(hello){
SC_CTOR(hello){
cout << "Hello, SystemC!" << endl;
}
};
#endif

// hello.cpp
#include "hello.h"
int sc_main(int i, char* a[]) {
hello h("hello");
return 0;
}
1
g++ hello.cpp -I /home/zhongtian/NOC/noxim/bin/libs/systemc-2.3.1/include -L //home/zhongtian/NOC/noxim/bin/libs/systemc-2.3.1/lib-linux64  -lsystemc -o hello

参考

最好的入门级SystemC基础知识简明教程

作者

zhongtian

发布于

2020-01-03

更新于

2023-12-16

许可协议

评论