嵌入式系统导论[02] 浅谈 System C

 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 环境配置

Github: ArchC/SystemC

  • Win:使用 VSmsvc80进行构建,生成 SystemC 库(静态库/动态库)
    • 如何在 VS 中引用 SystemC 动态库呢,如下:
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    C/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 uninstall
    • Hello 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
    #ifndef _HELLO_H
    #define _HELLO_H

    #include "systemc.h"
    #include <iostream>

    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;
    }
    };
    */

    #endif
  • hellp.cpp
    1
    2
    3
    4
    5
    6
    #include "hello.h"

    int sc_main(int argc, char** argv){
    hello h("hello");
    return 0;
    }
  • Makefile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    LIB_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
    2
    xxx@xxx$  ./hello
    error while loading shared libraries: libsystemc-2.3.1.so: cannot open shared object file: No such file or directory

  需要增添链接器ld加载路径

  • 1
    2
    3
    4
    5
    6
    xxx@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
    12
    LIB_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_moduleSC_MODULE
  • 与模块之间相联系的是端口信号的定义:Verilog 的输出信号也可以作为输入,而 SystemC 的端口定义了数据转移的方向,所以对于同样作为输入的 Verilog HDL 输出信号 output,在 SystemC 中必须定义为 inout;
  • Verilog HDL 不支持浮点型的输出,而 SystemC 则支持,这在 Verilog HDL 到 SystemC 转换的时候不会产生问题,反之则不可以;
  • 对于 Verilog HDL 中的连续赋值语句assignalways语句,都对应于 SystemC 的进程。SytemC 的进程有3种:方法进程(SC_METHOD)、线程(SC_THREAD)和钟控线程(SC_CTHREAD)
    • 方法进程用来进行组合逻辑模拟;
    • 线程可用来模拟测试平台;
    • 钟控线程用来模拟同步有限状态机;
    • 后两种都是行为模型 ,至少需要行为级综合工具才能综合;
    • 方法进程可以用来描述寄存器传输级电路,一般将 Verilog HDL 中的assignalways语句翻译成方法进程。
      • 对于一个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
    `timescale 1 ns/1 ps
  • SystemC
    1
    2
    sc_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
    `define DLY 2
    module dif(clk, din, dout);
    input clk, din;
    output dout;

    assign dout <= # `DLY din;

    endmodule
  • 在SystemC 中,wait()只能在线程或者钟控线程内使用,在方法进程内须用与wait()等效的函数next_trigger()
  • SystemC
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #define DLY 2
    #include "systemc.h"
    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(电可擦可编程只读存储器)中的程序。
       固件是担任着一个系统最基础最底层工作的软件。在硬件设备中,固件就是硬件设备的灵魂,一些硬件设备除了固件以外没有其它软件组成,因此该固件也就决定着硬件设备的功能及性能;
       固件是指设备内部保存的设备”驱动程序”,通过固件,操作系统才能按照标准的设备驱动实现特定机器的运行动作。
      driverfirmware没有什么直接的关系,但 firmware 通常由驱动去加载。在 Linux Kernel 中,driver 和 firmware 是有明确含义的。
       driver 是控制被操作系统管理的外部设备(devices)的代码段;
       firmware,是表示运行在非”控制处理器”(指不直接运行操作系统的处理器,例如外设中的处理器,或者被用于 bare metal 的主处理器的其中一些核)中的程序。

  • 硬核就是指经过验证的设计版图
    • 基于半导体工艺的物理设计,已有固定的拓扑布局和具体工艺,并已经过工艺验证,具有可保证的性能;
    • 提供给用户的形式是电路物理结构掩模版图和全套工艺文件。由于无需提供寄存器转移级(Register Transfer Level,RTL)文件,因而更易于实现IP保护;
    • 缺点是灵活性和可移植性差。

References

文章目录
  1. 1. Y-chart ==> IC 层次设计
  2. 2. Why SystemC?
  3. 3. SystemC 环境配置
  4. 4. SystemC vs Verilog(初级等效性)
    1. 4.1. ①基本结构等效性
    2. 4.2. ②时间模型的等效性
    3. 4.3. ③等待(wait)与事件(event)的等效性
    4. 4.4. ④仿真调度模型的等效性
  5. 5. Soft/Firm/Hard IP Core$^{[option]}$
  6. 6. References