搜档网
当前位置:搜档网 › SystemC From The Ground Up学习笔记中文

SystemC From The Ground Up学习笔记中文

SystemC From The Ground Up学习笔记中文
SystemC From The Ground Up学习笔记中文

(一)systemC简介

SystemC是一种系统设计语言。严格讲,SystemC是一个能够描述系统和硬件的C++类库。和其他硬件描述语言(如Verilog、VHDL)一样,SystemC支持RTL级建模,然而SystemC最擅长的却是描述比RTL更高层、更抽象的系统级和结构级。

SystemC可以使得系统设计工作早期便达到很好的软硬件协同,这对于及早发现设计中的问题有很大帮助。

SystemC支持多种技术来实现现代设计的复杂性。

Abstraction

Design reuse

Team discipline

Project reuse

Automation

(二)TLM-BASED METHODOLOGY

2.1 Transaction-Level Modeling Overview

1、事务级建模主要用途为

·硬件体系结构的性能分析和行为分析。

·软硬件划分和协同设计。

·作为底层设备驱动和硬件仿真模型的接口。

·集成操作系统仿真器和硬件模拟器。

·作为精确到周期的模型和交易级模型的仿真平台。

·作为测试图样,或者模拟设计的系统环境。

2、特点

·交易层是将理想的结构映射到需要考虑资源分配和设计约束的结构中。

·存储器和寄存器的映射是精确的。

·允许多线程通信。

·可以通过对数据类型的约束来对总线的突发传输或者突发传输的片断进行建模。

·事件驱动的仿真机制,带有时间估计。

·能够基于传输量和延时约束进行延时性能估计。性能估计能够采取基于报告的方式。也就是说,时间驱动的通道能够根据总线宽度和总线协议计算并报告一个交易需要多少个时钟周期。或者通过插入延时来仿真时间。

·能够利用参数来精细的调整设计规范,以解决约束过紧或者过松的情形。·利用参数来对不同的总线协议和信号接口进行建模。

3、TLM

·交易可以理解为系统模型中两个组件之间的一次数据交换。这个交换与所采用的协议无关,因为交易级的模型通常不牵扯到具体的总线时序等细节。

·一个数据交易可能是在系统组件之间传输的单个的字,多个字或者整个数据结构。

例如:一次DMA,一次存储器读或者写、一次寄存器读或者写都可以看成是一个交易。

4、交易级建模提供的一个非常重要的用途就是可以在整个设计的比较早的阶段就开始进行嵌入式软件的开发。

好处是:并行开发、仿真速度快、协同设计与验证

更早进行软件开发

更早更好的硬件功能验证测试

建立一个从客户需求到详细软硬件规范的完整清晰路径

5、交易级建模与SystemC的通信机制

SystemC的通信机制有两个特点特别适合TLM建模:

·功能与通信分离。也就是实现具体算法的部分与实现数据和事件传输的部分分离。功能由SystemC的模块(sc_module)来实现,而通信由通道(channel)来实现。

·接口方法调用(Interface Method Call,IMC)。一组给定的通信方法(method)被称为接口(interface),包括数据接口和控制接口。通道(channel)是由一个或者多个接口来实现。模块能够使用它的端口(port)调用实现了相应接口并关联到该端口的通道实例方法。

(三)SystemC概述

1、主要组件

2、SystemC Compilation Flow

3、面向硬件的特征

The major hardware-oriented features implemented within SystemC: Time model (sc_time、sc_clock)

Hardware data types

Module hierarchy to manage structure and connectivity Communications management between concurrent units ofexecution(通道)Concurrency model(SC_METHOD、 SC_THREAD、 SC_CTHREAD)

4、SystemC Components

5、SystemC Simulation Kernel

(四)数据类型

数据类型的选择依赖于值的范围,要求的精度,要求的操作,数据类型的选择影响仿真的速度、合成器和综合的结果。

4.1 Numeric Representation

字符串表示:

4.2 Native Data Types

SystemC支持所有的C++内建数据类型,包括int、long int、short int、unsigned int、unsigned long int、unsigned short int、double、float、char、bool,以及C++的string类型。

在任何情况下,C++内建数据类型在仿真速度和内存使用上是最有效的。

4.3 Arithmetic Data Types算术数据类型

提供两类算术数据类型,一类是位宽1到64,另一类位宽大于64位

1.sc_int and sc_uint

对应于C++内建的int和unsigned int,不同点是可以指定数据的位宽(1到64位),使用格式为:

sc_int NAME...;

sc_uint NAME...;

由于仿真SystemC的数据类型会比仿真C++的数据类型慢

2.sc_bigint and sc_biguint

可支持大于64位的数据。

sc_bigint NAME...;

sc_biguint NAME...;

4.4 Boolean and Multi-Value Data Types布尔及多值数据类型

1、sc_bit和sc_bv:

sc_bit NAME...; (表示0、1)

sc_bv NAME...;(位向量,长度大于1的0、1序列)

在SystemC中,SC_LOGIC_1和SC_LOGIC_0是分别表示1和0的数据常量。

sc_bit和sc_bv支持与(&)、或(|)、异或(^)操作,以及位选择([])和范围选择操作(range())。

2、sc_logic和sc_lv:

sc_logic NAME[,NAME]...;(表示1、0、X、Z四种逻辑)

sc_lv NAME[,NAME ]...;(logic vector)

SC_LOGIC_X和SC_LOGIC_Z分别表示不确定状态和高阻态

4.5 Fixed-Point Data Types定点数据类型

SystemC提供sc_fixed、sc_ufixed、sc_fix、sc_ufix以及它们的_fast后缀变种,来表示定点数据类型。

要使用这些数据类型,必须在头文件包含语句#incluede 前面加上#define SC_INCLUDE_FX。

WL表示字长度、IWL表示整数字长度,例如:(i:整数位;f:分数位;s:符号位)

sc_fixed<5, 5>表示 iiiii.

sc_fixed<5, 3>表示 iii.ff

sc_fixed<5, 0>表示 .fffff

sc_fixed<5, 7>表示 iiiii.00

sc_fixed<5, -2>表示 .ssfffff

fix和fixed的区别是fixed的定点数据类型在编译后便不能再改变。_fast后缀的类型在仿真时更快,因为它们的精度被限制为53位。

4.6 SystemC数据类型的操作符

(五)模块SC_MODULE

5.1 程序起始点: sc_main

所有程序都有一个起始点。在C/C++中,这个起始点叫做main,例如:

int main(int argc, char* argv[]) {

BODY_OF_PROGRAM

return 0;

}

在SystemC中,这个起始点被叫做sc_main。例如:

int sc_main(int argc, char* argv[]) {

ElABORATION

sc_start(); // <-- Simulatioin begins & ends in this function! [POST-PROCESSING]

return EXIT-CODE;// Zero indicates success

}

在sc_main中,代码执行分三个阶段:Elaboration、Simulation和

Post-processing。

Elaboration阶段描述系统的结构和相互连接关系,包括时钟、设计模块和通道的例化等;

Simulation阶段完成整个仿真行为;从第一次遇到sc_start( )开始到预先设定的仿真时间结束或者遇到sc_stop()。

Post-processing阶段是可选的,取决于设计者是否需要在完成仿真后还要进行其他处理。

5.2 设计的基本单元:SC_MODULE

复杂系统都是由许多独立的的功能模块组成的,这些模块代表硬件、软件或者物理实体,它们可大可小。在SystemC中用SC_MODULE来表示这些模块。

的语法如下:

#include

SC_MODULE(module_name) {

MODULE_BODY

};

SC_MODULE实际上一个C++宏:

#define SC_MODULE(module_name) \

struct module_name: public sc_module

MODULE_BODY可由以下元素构成:

Ports端口

Member channel instances成员通道的例化

Member data instances成员数据的例化

Member module instances (sub-designs)成员模块的例化

Constructor构造函数(必须的)

Destructor析构函数

Process member functions (processes)成员过程

Helper functions

5.3 SC_MODULE的构造函数:SC_CTOR

SC_MODULE的构造函数SC_CTOR主要完成以下任务:

Initializing/allocating sub-designs 初始化和分配子设计

Connecting sub-designs 连接子设计

Registering processes with the SystemC kernel 注册过程

Providing static sensitivity 提供静态敏感表

Miscellaneous user-defined setup 其他用户定义的设置。

SC_CTOR的语法如下:

5.4 执行的基本单元: SystemC Process

过程是SystemC的基本执行单元。

其语法如下:

void PROCESS_NAME(void);

SystemC过程没有参数,也没有返回值。

最简单的过程是SC_THREAD,它和软件概念上的thread很相似。SC_THREAD只被调用一次便结束。

5.5 注册最简单的过程:SC_THREAD

一旦定义了过程,就必须要让仿真内核识别和注册它。过程的注册是在构造函数SC_CTOR中进行的。

注册SC_THREAD过程的语法如下:

SC_THREAD(process_name); //Must be INSIDE constructor

下面是一个简单的例子:

SC_MODULE(simple_process_ex) {

SC_CTOR(simple_process_ex) {

SC_THREAD(my_thread_process);

}

void my_thread_process(void);

};

5.6 完成一个简单的设计:main.cpp

在my_instance中用了"my_instance"做参数,这么做的目的是为了将实例的名字存储在其内部,以便在debug时调用。sc_module的成员函数name()可用来获取当前实例的名称。

5.7 另一种构造函数:SC_HAS_PROCESS

可以在两种情况下使用该宏:

当需要将实例名称以外的参数传给构造函数;

希望将构造函数的实现放到另一个cpp文件中。

例如,一个存储设计可以允许通过参数进行不同大小的存储选择:

My_memory instance("instance", 1024);

(六)时间概念

6.1 sc_time

SystemC提供sc_time数据类型来衡量时间。时间由两部分构成:长度(magnitude)和单位(unit)。

时间数据类型语法如下:

时间单位有以下几种:

SystemC允许对sc_time对象进行加、减以及比例缩放等操作。

一个特殊的常量SC_ZERO_TIME,即代表sc_time(0, SC_SEC)。

6.2 sc_start()

sc_start()用于启动仿真阶段。sc_start()提供了一个sc_time类型的可选参数,用来指定最大仿真时间。如:

sc_start(60.0, SC_SEC);

6.3 sc_time_stamp () and Time Display

在SystemC中可以通过sc_time_stamp()来获得当前仿真时间。如:

cout << sc_time_stamp() << endl;

6.4 wait(sc_time)

wait()被用在SC_THREAD过程中完成延迟功能。当遇到wait(),SC_THREAD过程被阻塞,并且在指定的时间返回继续执行。如:

wait(delay_sc_time); // wait specified amount of time

6.5 sc_simulation_time(), 时间分辨率和单位

有时想要用C++内建数据类型操作时间,sc_simulation_time()将以当前缺省单位返回double类型的时间。

修改缺省时间单位:sc_set_default_time_unit().缺省时间单位必须是10的幂,必须大于等于时间分辨率,只能设置一次,只能在仿真开始之前设置。

指定时间分辨率:sc_set_time_resolution().时间分辨率必须是10的幂,只能在仿真开始之前设置,且只设置1次,必须在任何的非零的sc_time声明之前设置。

(七)并发Processes & Events

实时系统中的很多活动都是同时发生的,SystemC用processes模拟并发。SystemC提供了两个主要的process类型:SC_THREAD,SC_METHOD,还有第三种类型,SC_CTHREAD,是SC_THREAD的变型。

7.1 sc_event

事件就是在某个特定时间点发生的事情。一个时间没有取值,也没有持续时间。事件本身没有并不做任何可以被观察到的事情,它只是激发对它敏感的过程,从而表现出作用。

SystemC中的process通过动态或者静态敏感表来等待一个事件的发生。声明一个事件的语法如下:

由于sc_event没有取值,只能对它采取两种动作:等待或导致它发生。

7.2 Simplified Simulation Engine

需要理解事件驱动模拟是怎样工作的。见5页SystemC Simulation Kernel.

7.3 SC_THREAD

过程SC_THREAD被且只被仿真器启动一次。SC_THREAD一旦被执行,就完全控制仿真过程,直到其自己将控制返回给仿真器。

SC_THREAD有两种返回控制给仿真器的方法,一种是简单退出(如return),这意味着永远结束该过程。因此,常常在SC_THREAD中使用含有至少一条wait语句的无限循环。

另一种返回控制的方法是调用wait方法,wait挂起sc_thread进程。有时候wait 并不是被直接使用的,比如sc_fifo的阻塞读或写在FIFO分别为空或满的时候将隐式的激活wait。

7.4 SC_THREAD::wait()的动态敏感表

SC_THREAD依靠wait来挂起自身。当wait被执行时,当前SC_THREAD进程的状态将被保存,同时仿真内核得到控制权,启动另一个准备好的进程。当被挂起的过程重新被启动时,它将从wait后的语句开始执行。wait的语法如下:

当使用timeout时,在wait后立即调用布尔函数timed_out()来检测是否是time out 导致进程返回。例如:

7.6 启动事件: .notify()

事件通过使用notify()来显式的发生。

面向对象的语法风格:

调用.notify() 会使任何等待该事件的进程立即由等待转为就绪。

.notify(SC_ZERO_TIME)会在当前等待被执行的所有过程完成后,才将等待该事件的过程转为等待被执行(所谓的下一个delta-cycle)。

对于.notify(time)来说,如果有多个notification,那么只有时间最近的那个是有效的。我们可以用.cancel()来取消所有notification。

7.7 SC_METHOD

SC_METHOD从不在其内部被挂起(不能直接或间接的使用wait(),否则会报runtime error的错误),SC_METHOD一旦运行便会运行到底,然后返回。仿真引擎根据动态或静态敏感表不断的调用SC_METHOD。

语法如下:

SC_METHOD(process_name); //Located inside constructor

在SC_THREAD中变量分配是永久的,即变量是永远存在的。

在SC_METHOD中,每次被调用时变量都要被声明和初始化,如果希望保存

SC_METHOD中的值,那么就需要使用SC_MODULE中定义的局部变量。

7.8 SC_METHOD: next_trigger()的动态敏感表

SC_METHOD processes 使用 next_trigger() 动态的指定它们的敏感性。

语法如下:

next_trigger不会阻碍当前SC_METHOD的执行,它会改变下一次调用该SC_METHOD的敏感表。如果没有静态敏感表,那么SC_METHOD的所有执行分支都应当有next_trigger,否则可能导致该SC_METHOD不再被调用。

7.9 进程的静态敏感表

动态敏感表示在仿真阶段的建立的,并且可以在仿真中被改变。

静态敏感表是在elaboration阶段(仿真前)建立的,静态敏感表一旦建立就不能再改变。静态敏感表只应用于最近的进程注册。

建议选择类似于C++流的第一种语法风格。

7.10 dont_initialize

SystemC的仿真引擎将在开始时初始化执行所有的进程,然后有时候某些进程不需要在一开始就执行。SystemC提供了dont_initialize()来阻止初始化

// IMPORTANT: Must follow process registration

dont_initialize();

使用 dont_initialize 需要一个静态敏感表,否则将没法启动进程。

7.11 sc_event_queue

sc_event只能被安排一次(如果多个notification只有时间最近的那个会被执行),sc_event_queue可以使一个事件被安排多次甚至是在同一时间。

(八)基本通道

并发过程之间的通讯可以使用事件和模块成员数据。由于没法保证下一个从就绪态执行的是哪个进程,在共享数据是必须十分小心使用这两种方法。

SystemC提供一种“握手(handshake)”变量来实现过程间的通讯,称作通道(channel)。通道有primitive和hierarchical两种。

8.1 Primitive Channels 基本通道

基本通道不包含任何进程,也不对外展现出任何可见结构,它们也不能够直接的或者间接的调用其它基本通道。

所有的primitive channel都是从sc_prim_channel基类继承而来。

下面介绍最简单的三种primitive channel:sc_mutex、sc_semaphore、sc_fifo。

8.2 sc_mutex

在elaboration阶段,mutex被创建(有唯一的名字),之后任何需要该资源的进程必须lock该mutex以阻止其他进程使用该共享资源,当进程不再使用互斥资源时需要unlock该mutex,如果另一个进程试图使用一个锁定的资源,该进程将被阻止直到资源解锁。

SystemC 通过sc_mutex 通道实现mutex。sc_mutex包含许多访问函数,包括阻塞和非阻塞两种风格。阻塞风格的函数只能在SC_THREAD过程中使用。

语法如下:

在电子设计中,可以用sc_mutex作为共享总线的仲裁。当仲裁器设计好后再替换该sc_mutex。

8.3 sc_semaphore

有些资源可以有多个copy或owner,SystemC提供sc_semaphore来管理这种资源。当建立信号量对象时,指定有多少可用是必须的。其实mutex是只有一个计数值的信号量。

信号量代表可用资源实体的数量,所以可以认为信号量就是一个资源计数器,它限制的是同时使用某共享资源(也称为临界资源)的进程的数量。信号量计数的值代表的就是当前仍然可用的共享资源的数量。

sc_semaphore的语法如下:

sc_semaphore实现的是sc_semaphore_if接口。

其中,wait()方法获得一个信号量,其作用效果是获得一份资源的使用权,使信号量计数减一,如下面的实现代码。

int sc_semaphore::wait(){

while( in_use() ) { sc_prim_channel::wait( m_free ); }

-- m_value;

return 0;

}

8.4 sc_fifo

FIFO 是用来管理数据流的最常用的数据结构。SystemC 提供sc_fifo 来实现FIFO 。sc_fifo<>在默认情况下深度是16,另外还需要指定元素的数据类型。 sc_fifo 的语法如下:

sc_fifo是SystemC 核心语言库中已经实现了的FIFO 通道 。

write(&T)代表写FIFO 的方法。

read()是读FIFO 的方法,它返回队头单元的数据。

num_free()用于查询FIFO 还有多少空单元。

num_available()查询FIFO 还有多少个数据可以读。 Size 代表FIFO 的总单元数, 对于sc_fifo ,Size 的默认值为16。

对于复杂数据类型的元素,将指针传给sc_fifo 将会更加高效。

(九)EV ALUATE-UPDATE CHANNELS 通常,电子信号有单一的来源,但是多个接收器。使所有的sinks 能在同一时间检测到信号更新是很重要的。

num_free()num_avaiable()

.........write(&T)read()

Size

9.1 Completed Simulation Engine

信号通道使用这个更新状态作为数据同步点。为了达到同步,每个通道有两个存储区域:当前值和更新值。有两组数据可见:新的和当前的。当一个进程写入信号通道时,该进程存入更新域而不是当前域,同时进程调用request_update()以引起仿真内核在更新状态调用该通道的update()方法。

当evaluate phase 完成后(没有就绪进程),内核为每一个请求更新的通道实例调用update()。update()方法有时不只是简单的将更新值复制到当前当前值,也可能指出改变或唤醒一个等待态的进程。

9.2 sc_signal, sc_buffer

基本通道sc_signal<>和跟它比较相似的sc_buffer<>都是使用的evaluate-update paradigm。关于声明、读及写的定义:

这两种通道的好处之一是他们有一个限制:在一个特定的delta-cycle只有一个进程可以写入给定的信号.

相关主题