1、模块的声明:SC_MOUDLE(Dcu){}和class dcu::public sc_moudle{}作用一样。new sc_module实例时可以传入一个字符串参数,作为sc_module的名称。如果不传入,sc_module会为实例自动生成一个名称。
2、构造函数:SC_CTOR(Dcu)可以在类的声明中使用,代替构造函数。sc_module中提供默认的析构函数,也可以通过~Dcu()自定义析构函数。构造函数可以直接传入多个参数。
3、sc_main两个参数第一个参数表示输入参数个数,第二个参数是一个数组指针。
4、sc_start(10,SC_NS)表示启动仿真,并持续10ns。sc_stop()是停止仿真,没有参数。sc_start如果不带参数会运行到所有进程和线程结束。
在sc_main和基础函数中可以通过sc_time delay(1, SC_NS); sc_core::sc_start(delay);进行时延,但是这个时延会和sc_start(200, SC_NS);时间累加。和wait的时间是重合的。
5、 SC_METHOD是唯一的可以综合的寄存器传输级进程。通常用于独立功能、复杂状态、系统通信和协调。一个进程只能对应一张敏感表。敏感表上有事件发生,进程函数就会被调用。敏感表中可以包含:sc_signal、sc_port、sc_clock和sc_event。进程函数中不能有wait语句。可以通过wait_until()和next_trigger(clk.pos())实现时延,next_trigger不会阻塞进程当次执行,而且会使得进程多次循环执行。例子:
void(返回值必须是void) SC_METHOD(func); //SC_METHOD是一个宏,不带入口参数,加在func之前,注册func()为进程的一部分。
sensitive<<a<<b; //敏感表,sc_signal<a>或者b发生变化,均会触发func()线程函数的执行,组合敏感
sensitive_pos(a);//上升沿触发,a从非1变成1的时候触发,沿敏感
sensitive_neg(a); //下降沿触发,a从非0变成0的时候触发
sc_in<>.pos()表示端口上升沿,.pos()方法只能用于端口,不能用于信号。
SC_HAS_PROCESS(Dcu); // 声明模块支持进程和线程,在class A:public sc_module中使用
6、SC_THREAD是时间驱动的,不需要敏感表,但是会通过wait(条件)阻塞模拟器。通常用于模块内并行处理共享资源管理和事件驱动局部行为。
SC_THREAD(func)线程函数中 wait() 函数可以用来等待任意类型的事件,包括其他信号的变化、特定的时间延迟等。 如:wait(10,SC_NS)表示线程等待10ns后继续执行。
wait(e1)//等待e1事件触发
wait(200, SC_NS, e1)//等待e1事件触发,或200ns
wait(sc_time(200, SC_NS), e1)//等待e1事件触发,或200ns
SC_CTHREAD(func,clk)是钟控线程,是严格按照clk的上升沿(或下降沿)触发的。
常见时间单位:SC_SEC(秒), SC_MS(毫秒,10^-3秒), SC_US(微秒,10^-6秒), SC_NS(纳秒,10^-9秒), SC_PS(皮秒,10^-12秒), SC_FS(飞秒,10^-15秒)
sc_set_time_resolution(1, SC_FS); //设置时间分辨率为1飞秒(fs),意味着在模拟中能够精确到飞秒级别的时间单位。它决定了系统能够处理的最小时间间隔或最小时间粒度,影响计算的细节级别。
sc_set_default_time_unit(1, SC_SEC); //将默认的时间单位设置为秒(s),意味着在没有其他指定的情况下,时间将以秒为单位来处理和表示。默认时间单位设置为秒意味着用户输入和输出的时间值会以秒为单位显示,影响用户与数据交互时的单位。
sc_time_stamp()
返回当前的仿真时间,即仿真时间的时间戳。
7、sc_mutex在线程执行过程中通过sc_mutex.lock()和sc_mutex.unlock()实现资源的加锁和释放。
8、dont_initialize()函数作用是避免构造函数默认初始化操作,在构造函数中存在进程或者线程时,需要触发条件才会执行进程。每个进程都有各自对应的dont_initialize()
9、sc_event用法:
创建事件:使用 sc_event 创建事件对象。
触发事件:使用 notify() 方法来触发事件。可以指定延迟时间,一般在线程或者方法进程中使用。
等待事件:使用 wait() 方法在进程中等待事件的触发。
sc_event_queue
:将事件排入队列,并在特定时间点触发这些事件。
sc_event_queue eq1;
eq1.notify(1, SC_SEC); //1s后触发事件
10、sc_semaphore:信号量,是操作系统提供的管理公有资源的有效手段。代表可用资源实体的数量,主要用于限制同时使用某个共享资源的进程的数量。用法:
#include <boost/interprocess/sync/semaphore.hpp>
boost::interprocess::seaphore sem(2(初始计数为2,代表最多只能有两个线程中包含sem))
sem.wait();//等待信号量
共享资源代码
sem.post();//释放信号量
11、sc_in<类型>、sc_out<类型>和sc_inout<类型>都是端口,是sc_port的子类。可以调用sc_in自己的read()函数读取sc_in端口中的数据。sc_out自己的write(数据非信号)实现数据在sc_out端口写数据。read和write隐含隐式类型转化(如果不使用read或者write方法,无法对端口直接进行数据操作比如range,需要通过read或者write方法从端口中读出到数据中)。一个 sc_out
端口和一个 sc_in
端口通常需要进行绑定,以便它们可以有效地进行数据交换。
12、sc_signal,信号是数据的载体,端口需要通过信号传递数据,传输方向取决于连接部分的端口状态。
sc_signal<int> sign; //信号声明,必须在模块内部
sign.write(1); //信号初始化
sc_in<int> in; //声明端口,必须在模块内部
in(sign); //通过底层重载函数bind实现端口和信号的绑定,将数据2通过signal传入in端口。
sc_signal<sc_uint<4> > 在两个>>中间需要添加一个空格,为了适配模板函数。
不能直接获取端口和信号的特定位,需要先把端口和信号赋值到变量中再获取变量的特定位。
端口或者信号赋值是在进程结束的时候实现的,所以要端口读取操作需要在sc_start之后,而变量的赋值是在语句执行的时候实现。
13、sc_clock类生成周期性时钟信号。常见构造函数参数:
•sc_clock( const char* name_, //名称
• double period_v_, //时长
• sc_time_unit period_tu_, //单位
• double duty_cycle_, //高电平占空比,如:0.7,70%高电平
• double start_time_v_, //初始相位,如: 1.0时钟周期起始点为1.0
• sc_time_unit start_time_tu_//初始相位单位,如: SC_NS
• bool posedge_first_ = true ); //时钟信号初始值,true:从上升沿开始;false:从下降沿开始;
sc_in_clk是时钟输入端口(等价于sc_in<bool>),用于连接sc_clock生成的时钟信号。
sc_clock无法在模块中直接初始化,一般是在sc_main中初始化,然后连接模块中的sc_in_clk端口。
14、SystemC运行将仿真结果保存为以下三种标准波形格式:VCD/WIF/ISDB。VCD波形创建过程如下:
sc_trace_file *wf = sc_create_vcd_trace_file("waveform");//创建vcd波形文件句柄
sc_trace(句柄。信号,"名称");//输出波形,需要放在sc_start之前
sc_close_vcd_trace_file(wf); //关闭波形
通过modelsim终端输入vcd2wlf a.vcd b.wlf将vcd文件转化为wlf文件,通过file->open打开wlf文件,将波形拉到view->wave中。
15、可以通过SC_REPORT_WARNING/INFO/ERROR("module_name","warning !!!");实现信息打印。
16、需要绑定main和main中new模块的时钟和reset信号。
17、锁存器:当敏感表中信号有效时,将输入信号赋值给存储信号,无效时,存储信号保持不变。