预备打工人之SystemC学习
例子
SystemC头文件
任何SystemC设计都必须包含合适的SystemC库定义的头文件,一般使用
#include “systemc.h”
但是在大型设计,常常希望只包括需要的名字空间以加快仿真,减少名字冲突。
模块
模块是设计的一个最基本的单位,是完成一个特定功能的基本单元。整个设计就是顶层模块。模块化的管理允许设计者把复杂的电子系统划分为更小的模块,每个模块独立进行设计和验证。
一个模块可以包含一些其他的基本元素,比如一个sram模块。
SC_MODULE(sram8x256){
......//具体内容
};
其中,SC_MODULE就是模块的意思,name就是模块的名字。SC_MODULE是SystemC库中定义的一个宏,使用它就是定义了一个新的C++类:
#define SC_MODULE(name)\
struct module_name: public sc_module
模块的构造和析构函数
构造函数
上面也说了,SC_MODULE 就是一个C++的类,所以也具有构造函数和析构函数。这两个函数的定义就不介绍了。而SC_MODULE 的构造函数除了初始化等功能外,还需要指定敏感表。例如:
SC_CTOR(sram8x256){
men=new sc_uint<8>[256];
for (int i=0;i<256;i++) mem[i]=0;
SC_METHOD(sram_prc);
sensitive<<clk.pos();
};
SystemC的构造函数用SC_CTOR标识与构造,其中构造函数的名字必须与模块的名字相同。sensitice表示敏感表,这说明这个模块对clk的上升沿敏感。而SC_METHOD表示这个模块是个进程。
SystemC包括三个基本的进程:SC_METHOD,SC_CTHREAD和SC_THREAD等。
析构函数
SystemC也有类似的类的析构函数,一般也有虚析构函数。
~sram8x256(){
if(mem){
delete mem;
mem=0;
}
}
与C++中一样,析构函数一般会清除函数申请的内存。
模块的内部函数
一个模块可以声明内部变量来保存模块内存的数据。
端口和信号
端口和信号的定义
端口
芯片通过关键与电路板上其他芯片或者原件通讯,管脚有输入、输出和双向管脚。在SystemC中,模块通过端口(Port)与与其他模块通讯,分为Input,Output和Inout。而两个模块间的端口用信号(Signal)连接,父/子模块的端口可以直接相连。
端口的数据类型可以是:
· C++数据类型,如int,char等
· SystemC专有数据类型sc_int、sc_bigint等
· 用户自定义结构
· 抽象端口
端口的定义方法:
sc_out <端口类型> 端口名
端口的使用
SystemC的端口定义了于特定端口类型相关的方法,如read()和write()。下面的语句是等效的。
mem[addr.read()]=wr_data.read();
mem[addr.read()]=wr_data;
而下面的语句是非法的:
mem[addr]=wr_data.read();
因为addr是sc_uint<8>类型,而数据的索引是unsigned int,c++不支持该类型转换。
延时
延时是模块的端口与模块内数据的重要区别之一。为了真实仿真硬件,所以需要手动添加延时。
多驱动处理器
在实际电路中多驱动也是很常见的。在SystemC中引入了解析逻辑向量信号来解决多驱动的问题。
sc_in_rv<n> x;//x是n比特的向量型输入端口
SystemC时钟和时间模型
时钟
时钟是电路中不可缺少的基本要素。在SystemC代码中可以找到
typedef sc_in<bool> sc_in_clk;
因此,时钟也可以自己定义。
此外,时钟被作为一个特殊的处理对象,是sc_clock类。
可以用如下的语句定义
sc_clock clk1("clk1",20,SC_NS,0.5,5,true);
表示20个时间单位,时间单位是ns,占空比是0.5,在5ns时第一次变化,开始是高电平
时间
时间常用来初始化或者改变其他变量。
sc_time time1(20,SC_NS);
第一个是时间长度,第二个是时间单位
另一个是等待时间
wait (10,SC_NS);
但是,SystemC时间分辨率是仿真系统处理器的最小实际单位。
基本数据类型
作为C++的一个硬件库,SystemC当然支持所有的C++数据类型,同时SystemC还定义了专有的数据类型。具体如下:
sc_bit和sc_logic数据类型
数字系统中最常用的四个逻辑为:
‘0’——逻辑低电平
‘1’——逻辑高电平
‘Z’——高阻态
‘X’——不定值
sc_bit只有‘1’和‘0’两种,而sc_logic还多有‘Z’和‘X’两种。sc_bit可以和bool型混用。
固定精度整型数据类型
sc_int和sc_uint类型这两种是固定宽度的整型数据。这两是1~64比特中任意宽度的整型数据类型。
sc_bigint和sc_biguint是任意位的整数.
任意长度比特和逻辑向量
sc_bv表示的比特向量类型
用户自定义类型
这里和C++一样,一般使用struct来创建。
定点数据类型
SystemC的四种基本定点数据类型
- sc_fixed ,参数是静态的,设定后无法更改
- sc_ufixed,是无符号数,参数是静态的。
- sc_fix,是非静态的,其字长和整数部分长度可以是变量。
- sc_ufix,wufuhao 是非静态的,其字长和整数部分长度可以是变量。
例如,
sc_fixed <16,8,SC_RND_ZERO,SC_SAT> sx_val;
说明一下,16表示总字长,8表示整数部分字长,SC_RND_ZERO是量化模式,SC_SAT是溢出模式
总结是,
sc_fixed <wl,iwl,q_mode,o_mode,n_bits> sx_val;
进程
在操作系统中,进程是程序在并发环境中给的执行过程,具有动态性、并发性、独立性、异步性和结构性五大特征。在SystemC中,进程是一个级别的执行单位。模块就可以是进程。
而SystemC中,进程是一个基本执行单位,共有三种
- SC_METHOD,这个是最基础的模块,算是方法进程。每次敏感列表上有事件时,就会被调用,调用后就立刻返回。注意,这类进程中不能使用wait()这样的语句。
- SC_THREAD,线程进程,能够被挂起和重新激活。线程进程使用wait进行挂机。当敏感列表上有事件时,线程进程被重新激活知道遇到新的wait再重新挂架。在一次仿真中,线程进行一旦退出,就不能再次进入。线程进程的一个方便的用途是藐视testbench的输入激励和输出获取。
- SC_CTHREAD,钟线程进程是特殊的线程进程,他集成与线程进程,但是只能在上升沿或者下降沿被处罚或者激活。这可以更好的行为综合。
wait()和next_trigger()
wait()
wait()只能用于线程进程和钟控线程进程
- wait(),等待敏感表中有事件发生。
- wait(const sc_event&),等待事件发生,如下面的例子
sc_event e1,e2,e3;
wait (e1);
wait (e1|e2|e3);
wait (e1&e2&e3);
- wait(double v,sc_time_unit tu),等待一段时间
wait(100,SC_NS)
- wait(double v,sc_time_unit tu,const sc_enent &e),如果时间内事件发生或者超过时间,进程将被激发
- wait(0,SC_NS)等待一个延时时间
next_trigger()
只能用于SC_METHOD进程。与wait参数相同,调用后立即返回,可以多次使用。
dont_initialize()和sensitive
dont_initialize()是希望进程在仿真的0时不被执行,sensitive是敏感表
仿真与波形
顶层函数sc_main()
SystemC本质上是C++,因此,使用大多数C++编译器都可以编译和连接。若将SystemC目标设计为可执行文件,必须设计SystemC顶层函数sc_main()。
int sc_main(int argc,char *argb[])
仿真控制
sc_start()在sc_main中调用,是指仿真开始。
-
void sc_start(); 没有参数就表示仿真一直进行到sc_stop()函数为止。
-
void sc_start(const sc_time & duartion); 是时间数据类型,说明仿真duartion个时间单位。
-
void sc_start(double t,sc_time_unit tu); sc_time_unit 表示时间单位
SystemC波形
SystemC运行将仿真结果保存为VCD格式,可以用HDL仿真软件打开。具有以下特点
- 只有在整个仿真期间都存在的信号和变量才能被耿总
- 任何类型的信号和变量都能被跟踪
- 不同格式的波形文件可以在同义词仿真过程中同时产生。