SystemC 是一种基于 C++ 语言的用于 系统设计 的计算机语言,是用 C++ 编写的一组库和宏。为了更好的了解 SystemC 的用途,我们先从集成电路(IC:通用集成电路、专用集成电路)设计的一些基础讲起,谈谈 SystemC 在 IC 设计流程中的定位;通过对比 SystemC 和我们更为熟悉的 Verilog,进一步了解 SystemC 。
Y-chart ==> IC 层次设计
数字集成电路的设计可以分为系统级、芯片级(行为级、结构级)、RTL级、门级、电路级和版图级。
- 系统级是把各个部件联系为一个有机的整体:
- 芯片级:行为级就是实现何种功能, 结构级更接近电路的实际结构,电路的层次化描述,类似于电路框图;
- RTL级就是寄存器级,贴近实际电路结构的描述,描述的细节到寄存器内容传输级别,可以精确描述电路的工作原理、执行顺序,细化到寄存器级别的结构描述等:
- 寄存器由逻辑门构成,逻辑门由电路构成,最后电路由半导体晶体管P和N结(pmos/nmos)构成,版图完整描述了电路的细节:
Why SystemC?
通常,系统由软件部分和硬件部分组成,系统的一部分功能由软件实现,而另一部分功能则由硬件实现。早期的系统比较简单,系统工程师将准备设计的系统划分为软件部分和硬件部分,分别由软件工程师和硬件工程师进行设计、仿真、实现和改进,最后再将软件部分和硬件部分结合起来形成系统。
- 软件工程师使用 C 或 C++ 等程序设计语言,因为这些语言专长于描述串行执行的程序,用来仿真软件部分;
- 硬件工程师则使用 VHDL 或 Verilog 等硬件描述语言,因为这些语言专长于描述并行运行的硬件,用来仿真硬件部分。
但是,随着电子系统的不断发展,系统结构越来越复杂,系统元件也越来越多,这就要求系统工程师在先期划分软件和硬件时,就对整个系统性能有很好的了解和掌握,以便更好地划分软件和硬件,减小设计中不必要的失误所带来的损失和风险。SystemC 也就由此孕育而生,因为它能够满足对软件和硬件协同仿真的需求。
SystemC 是由一组 C++ 类库所组成的 建模平台,加入了一个仿真核,可以在系统级、行为级和 RTL 级支持硬件建模;SystemC 既是一个 C++ 类库,又是一种设计方法,可以用来有效地创建软件精确算法、硬件结构模型,以及 SOC 与系统级设计的接口,可以在各个抽象层次上对系统和硬件建模。
利用 SystemC 描述硬件系统的过程如下(很多概念与使用 verilog 等硬件语言进行系统设计相似):
- 定义模块结构,利用 SystemC 的模块类来描述一个模块;
- 定义引脚,利用端口类定义特定模块的引脚,例如时钟引脚,数据引脚等;
- 定义进程,模块的功能描述主要是通过进程来实现,在 SystemC 中进程具有并发性;此外进程的执行需要一定的触发条件,如可以通过引脚值的改变来触发进程;
- 通过上面的三个步骤基本上完成了对一个硬件模块的描述,接下来需要实例化模块,模块实际上是一个类,实例化的过程在 SystemC 的主函数中进行,在主函数中需要定义一些信号对象,这些信号对象用于连接系统中各个模块的引脚,接着创建各个模块的对象,把对象中对应的引脚与前面定义的信号对象相连接,信号对象和引脚都有数据类型,例如可以在程序中定义整型,布尔型等数据类型的信号或引脚对象。只有相同数据类型的引脚和信号才能进行连接。
通过上面的四个步骤完成一个系统的设计,便可以进行仿真运行和验证整个系统的正确性。
像我们经常使用的 verilog 有一套 IDE,最后通过综合工具综合出电路(常见的如 FPGA 的 bitstream),SystemC 也有专门的综合工具,完成系统硬件实现。
SystemC 环境配置
- Win:使用 VS 对
msvc80
进行构建,生成 SystemC 库(静态库/动态库)- 如何在 VS 中引用 SystemC 动态库呢,如下:
-
1
2
3
4
5
6
7
8
9
10
11C/C++→General →Additional Include Directories (?\systemc-<version>\src)
C/C++ →Language →Enable Run-Time Type Info→Yes
C/C++→Code Generation→Runtime Library→Multi-thread Debug(/MTd)
C/C++→ Command Line→Additional Options加上 /vmg /D_CRT_SECURE_NO_DEPRECATE (注意空格!!)
Linker →General→Additional Library Directories (?\systemc-<version>\msvc80\SystemC\Debug)
Linker →Input→Additional Dependencies (SystemC.lib)
- Linux
- 构建生成动态库/静态库
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25# 1. Change to the top level directory (systemc-<version>)
xxx@...:~$ cd systemc-<version>
xxx@...:~$ pwd
# 2. Create a temporary directory(automake's out-of-source build), e.g.,
xxx@...:~$ mkdir build
# 3. Change to the temporary directory, e.g.,
xxx@...:~$ cd build
# 4. Configure the package for your system, e.g.,
xxx@...:~$ ../configure CXX=g++ --disable-async-updates
# 5. Compile the package.
xxx@...:~$ make
# 6. [option] verify the compiled package by testing the example suite.
xxx@...:~$ make check
# 7. Install the package.
xxx@...:~$ make install
# 8. You can now remove the temporary directory, .e.g,
xxx@...:~$ cd ..
xxx@...:~$ rm -rf build
# Alternatively, you can keep the temporary directory to allow you to:
# a) Experiment with the examples.
# b) Later uninstall the package.
# To clean up the temporary directory, enter:
xxx@...:~$ make clean
# To uninstall the package, enter:
xxx@...:~$ make uninstallHello World
:调用 SystemC 动态链接库
-
hello.h 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using namespace std;
SC_MODULE(hello){
SC_CTOR(hello){
cout << "Hello,SystemC!" << endl;
}
};
/*
* Use the other kind
class hello : public sc_module{
public:
hello(sc_module_name name) : sc_module(name){
cout << "Hello,SystemC!" << endl;
}
};
*/
-
hellp.cpp 1
2
3
4
5
6
int sc_main(int argc, char** argv){
hello h("hello");
return 0;
} -
Makefile 1
2
3
4
5
6
7
8
9
10
11
12LIB_DIR=-L your-systemc-dir/lib-linux64
INC_DIR=-I your-systemc-dir/include
LIB=-l systemc
APP=hello
all:
g++ -o $(APP) $(APP).cpp $(LIB_DIR) $(INC_DIR) $(LIB)
clean:
rm -rf $(APP)- 构建运行会出现以下错误
-
1
2xxx@xxx$ ./hello
error while loading shared libraries: libsystemc-2.3.1.so: cannot open shared object file: No such file or directory
-
1
2
3
4
5
6xxx@xxx$ cd /etc/ld.so.conf.d
xxx@xxx$ sudo gedit usr-libs.conf
# 添加上述的 LIB_DIR 路径到新建的 usr-libs.conf 文件
your-systemc-dir/lib-linux64
# 退出后让配置生效
xxx@xxx$ sudo ldconfig- 也可以直接调用 SystemC 静态链接库
-
Makefile 1
2
3
4
5
6
7
8
9
10
11
12LIB_DIR=-L your-systemc-dir/lib-linux64
INC_DIR=-I your-systemc-dir/include
LIB=-Wl,-Bstatic -l systemc -Wl,-Bdynamic
APP=hello
all:
g++ -o $(APP) $(APP).cpp $(LIB_DIR) $(INC_DIR) $(LIB)
clean:
rm -rf $(APP)
SystemC vs Verilog(初级等效性)
SystemC 的基本类库有四种:模块(Module)、进程(Process),端口/信号(Port/Signal)和数据类型(Data Types)。其中模块(Module)是 SystemC 中最基本的结构单元,其内可以包含端口、信号、进程或子模块。
- 进程(Process)有三类:Method Process,Thread Process 和 Clocked Thread Process;进程是 SystemC 中的基本执行单元,它用来模拟目标设备或系统的行为
- Method Process(方法进程) 类似模块中的成员函数,体现模块的功能特性。当有事件(信号值发生变化)发生时,Method Process 开始执行,一直到操作完,操作期间不能被挂起或无限循环运行;
- Thread Process(线程) 和 Method Process 的主要区别在于进程执行过程中可以被挂起和再次被激活,直到运行完毕;
- Clocked Thread Process(钟控线程) 是 Thread Process 的一个子集,主要对时序电路和同步操作进行描述,它在时钟边沿触发时开始运行。
- 端口/信号(Port/Signal)类主要负责模块间的相互连接和通信,其中端口有输入、输出和双向输入/输出3种类型;
- 数据类型(Data Types)继承了C++中的所有数据类型,同时为描述硬件的物理信息扩展了时间、延时和逻辑等物理数据类型。
在 SystemC 中,硬件模块的引脚是通过端口类对象来进行描述的,而连线则用到了信号类,进程类则是用来描述硬件模块的并发性的结构。①基本结构等效性
- Verilog HDL 的基本组成结构是模块
module
,对应的 SystemC 描述是sc_module
或SC_MODULE
; - 与模块之间相联系的是端口信号的定义:Verilog 的输出信号也可以作为输入,而 SystemC 的端口定义了数据转移的方向,所以对于同样作为输入的 Verilog HDL 输出信号 output,在 SystemC 中必须定义为 inout;
- Verilog HDL 不支持浮点型的输出,而 SystemC 则支持,这在 Verilog HDL 到 SystemC 转换的时候不会产生问题,反之则不可以;
- 对于 Verilog HDL 中的连续赋值语句
assign
和always
语句,都对应于 SystemC 的进程。SytemC 的进程有3种:方法进程(SC_METHOD
)、线程(SC_THREAD
)和钟控线程(SC_CTHREAD
)- 方法进程用来进行组合逻辑模拟;
- 线程可用来模拟测试平台;
- 钟控线程用来模拟同步有限状态机;
- 后两种都是行为模型 ,至少需要行为级综合工具才能综合;
- 方法进程可以用来描述寄存器传输级电路,一般将 Verilog HDL 中的
assign
和always
语句翻译成方法进程。- 对于一个
always
语句中的所有阻塞赋值语句,应该映射到一个 SystemC 进程中 - 对于不同变量的非阻塞赋值,应该映射为不同的 SystemC 进程
- 对于一个
- Verilog HDL 的
initial
语句等效于 SystemC 进程的构造函数SC_CTOR
②时间模型的等效性
- 在Verilog HDL 中没有专门的时钟,用户需要定义一个 reg 信号来模拟时钟,SystemC 中则直接定义了结构
sc_clock
用于定义时钟。 -
Verilog HDL 1
2
3
4
5
6// define a 20 MHz clock
module(clk);
initial clk = #50;
always #25
clk = ~clk;
endmodule -
SystemC 1
2// sc_clock (const char *name_, double period_, double duty_cycle_=0.5, double start_time_=0.0, bool posedge_first_=true)
sc_clock clk("clk", 50, 0.5, 5, false);
- Verilog HDL中,用
timescale
定义缺省的时间单位,在 SystemC 中使用了sc_set_time_resolution()
和sc_set_default_time_unit()
,两者等效。 -
Verilog HDL 1
-
SystemC 1
2sc_set_time_resolution(1, SC_PS);
sc_set_default_time_unit(1, SC_NS);
③等待(wait)与事件(event)的等效性
-
Verilog HDL 1
2
3
4
5
6
7
8
module dif(clk, din, dout);
input clk, din;
output dout;
assign dout <= #
endmodule
- 在SystemC 中,
wait()
只能在线程或者钟控线程内使用,在方法进程内须用与wait()
等效的函数next_trigger()
-
SystemC 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SC_MODULE(delayer) {
sc_in<bool> din;
sc_out<bool> dout;
SC_CTOR(diff) {
SC_METHOD(do_it);
sensitive << din;
}
void do_it() {
next_trigger(DLY, SC_NS);
dout = din;
}
};
④仿真调度模型的等效性
- 为了支持寄存器传输级的并行描述,SystemC 采用了与 Verilog HDL 基本相同的调度模型——基于Δ(delta)延迟。 一个Δ周期包括求值和更新两个阶段,在一个时间点上 ,这样的Δ周期会持续出现,直到在求值前后的结果不再发生变化,而在宏观上,时间并没有前进。
Soft/Firm/Hard IP Core$^{[option]}$
- 目前集成电路设计基本上都是用IP核搭积木的形式。
- IP核分为行为(Behavior)、结构(Structure)和物理(Physical)三级不同程度的设计,对应描述功能行为的不同分为三类,即软核(Soft IP Core)、完成结构描述的固核(Firm IP Core)和基于物理描述并经过工艺验证的硬核(Hard IP Core)。
- 软核就是我们熟悉的 RTL 代码,ARM 便是以软核为主的。
- 硬件描述语言(Hardware Design Language,HDL)文本形式提交给用户,它经过 RTL 级设计优化和功能验证,但其中不含有任何具体的物理信息;
- 用户可以以此综合出正确的门电路级设计网表,并可以进行后续的结构设计,具有很大的灵活性,借助于EDA综合工具可以很容易地与其他外部逻辑电路合成一体,根据各种不同半导体工艺,设计成具有不同性能的器件;
- 主要缺点是缺乏对时序、面积和功耗的预见性。而且IP软核以源代码的形式提供的,IP知识产权不易保护。
- 固核就是指网表。
- IP固核的设计程度则是介于软核和硬核之间,除了完成软核所需的设计外,还完成了门级电路综合和时序仿真等设计环节;
- 一般以门级电路网表的形式提供给用户。
- 固核 vs 固件
固件(firmware)常指写入EROM(可擦写只读存储器)或EEPROM(电可擦可编程只读存储器)中的程序。
固件是担任着一个系统最基础最底层工作的软件。在硬件设备中,固件就是硬件设备的灵魂,一些硬件设备除了固件以外没有其它软件组成,因此该固件也就决定着硬件设备的功能及性能;
固件是指设备内部保存的设备”驱动程序”,通过固件,操作系统才能按照标准的设备驱动实现特定机器的运行动作。
driver和firmware没有什么直接的关系,但 firmware 通常由驱动去加载。在 Linux Kernel 中,driver 和 firmware 是有明确含义的。
driver 是控制被操作系统管理的外部设备(devices)的代码段;
firmware,是表示运行在非”控制处理器”(指不直接运行操作系统的处理器,例如外设中的处理器,或者被用于 bare metal 的主处理器的其中一些核)中的程序。
- 硬核就是指经过验证的设计版图。
- 基于半导体工艺的物理设计,已有固定的拓扑布局和具体工艺,并已经过工艺验证,具有可保证的性能;
- 提供给用户的形式是电路物理结构掩模版图和全套工艺文件。由于无需提供寄存器转移级(Register Transfer Level,RTL)文件,因而更易于实现IP保护;
- 缺点是灵活性和可移植性差。