770 likes | 1.68k Views
系统级设计描述语言 SystemC. 第三部分 SystemC 行为建模基础. 教材: 陈曦 徐宁仪《 SystemC 片上系统设计》,科学出版社,2003 本 ppt 大部分内容参考此书,仅作为大学教学之用, 请勿用于商业. 徐宁仪 xuny97@mails.tsinghua.edu.cn Xuny @ smth 东主楼9区324房间 62781914. 版权所有 200 4. 课程主要内容. 共讲4次 ,争取讲清楚 Why&How , 重点是 Concept. 内容包括: 为什么用 SystemC 做片上系统设计? SystemC 基本语法*
E N D
系统级设计描述语言SystemC 第三部分 SystemC行为建模基础 教材:陈曦 徐宁仪《SystemC片上系统设计》,科学出版社,2003 本ppt大部分内容参考此书,仅作为大学教学之用, 请勿用于商业 徐宁仪 xuny97@mails.tsinghua.edu.cn Xuny @smth 东主楼9区324房间 62781914 版权所有 2004
课程主要内容 • 共讲4次,争取讲清楚Why&How, 重点是Concept • 内容包括: • 为什么用SystemC做片上系统设计? • SystemC基本语法* • SystemC行为建模基础* • SystemC交易级建模与通信细化* • SystemC 方法库 • Master/Slave库 • 验证库
第三部分 SystemC行为建模基础
一些重要概念回顾 • SystemC是一种系统描述语言,而不仅仅是一种新的硬件描述语言。 • SystemC 的主要目的是实现用单一语言完成设计规范、系统体系结构分析、验证平台和行为模型。 • 用SystemC的行为模型描述的算法模块比RTL模块快10~100倍。 • SystemC的更大价值在于其提供高层次设计流程。行为级综合工具经过多年的发展渐渐成熟,SystemC和行为综合工具结合的设计方法将会大大提高设计和验证效率. SystemC特别为行为综合设计的语法将使它成为非常适合行为级综合的设计语言。
行为建模的目的 • 系统体系结构探索 • 当要建模的目标系统十分复杂时,利用行为模型迅速建模,使得设计者对目标设计有更清楚的理解则有利于软硬件划分、减少系统功耗。 • 嵌入式软件开发与调试 • 基于行为模型的软硬件协同验证更使得设计初期(尤其在硬件平台已经做好之前)就可以对软件进行初步调试,从而节省设计周期 。基于行为模型的验证方法也能够保证验证平台的一致性。 • 利用行为级综合工具得到RTL级的设计。
SystemC 行为建模 • SystemC行为级建模中一个很重要的概念被称为交易级建模 (Transaction Level Modeling或者TLM)。(稍后讲述) • SystemC的接口 (Interface)、端口 (Port)和通道 (Channel)三个概念是进行SystemC 行为建模的基础 。(本部分的重点是理解这三个概念)
系统抽象:行为,通信和时序 • 系统抽象的三个关键元素 • 行为:算法(运算,控制…) • 通信:各个算法模块之间的数据交互,控制配合 • 时序:行为和通信的在时间域上的协调 • 举例
系统抽象的三个元素-举例-存储器 • 存储器的行为 • 读:read (address) • 写: write (address, data) • 复位:reset • 存储器的通信 • RTL级: 地址总线,数据总线,读信号,写信号,复位信号 • 交易级:目的地址,数据 • 时序 • 信号配合时序 • 读写速度 • 复位时间
系统抽象的三个元素-举例-OFDM接收机 • 行为: OFDM(正交频分复用)的接收算法 • 通信: 数据通路,控制通路 • 时序:各个模块之间的时钟周期配合,算法本身配合
系统抽象的三个元素-举例-嵌入式处理器 • 行为: 完成汇编语言描述的程序 • 通信: • Flash CPU • CPURAM • DMARAM • IOCPU • 时序: • 系统总线
行为建模对语言的要求(1 of 2) • 行为和通信分离(分开描述,时序隐含) • 系统体系结构探索,需要对行为和通信的各种组合进行探索(1CPU? 3CPU?共享总线?直接通道?) • 嵌入式软件开发与调试(软件开发不关心具体硬件通信细节) • 优点 • 行为(或者计算)和通信分开,称为正交化处理。可以分别在各自的部分进行修改,而不影响另外一部分的功能和完整性。 • 处理复杂问题的方法 - 分而治之 • 时间:分步骤 • 细节:分层 • 本质:正交
端口通道绑定 行为建模对语言的要求(2 of 2) • 接口方法调用(Interface Method Call) • 解决”行为和通信分离后,通信如何进行”的问题 • 一组给定的通信方法(method)被称为接口(interface) • 接口(或者说通信方法)由通道实现。 • 模块能够使用它们的端口(port)来与实现相应接口的通道进行互连。 接口的特性是不牵扯具体实现;所以它能够联系行为与通信
端口通道绑定 SystemC的行为建模语言支持 • 模块(module)是系统行为的主要载体。 • 通道(channel)是通信的主要载体。 • 时序隐含在模块和通道的描述中。 • 特点 • 行为和通信分开 • 支持接口方法调用
接口(Interface) • 接口的实现: • C++中的抽象类。(抽象类和纯虚函数) • 接口的特性是不牵扯具体实现;所以它能够联系行为与通信
接口(Interface) • 抽象类的特点是它定义了一组抽象方法(这里”方法”是C++中的一个名词,通常理解为类的成员函数),但不定义这些方法的具体实现(这里实现指的是给出成员函数的函数体部分) • 通道(channel)实现一个或者多个接口 。
存储器接口实例 enum transfer_status { TRANSFER_OK = 0, TRANSFER_ERROR }; 存储器读接口: template <class T> class mem_read_if: public sc_interface {public: virtual transfer_status read( \ unsigned int address,T& data) = 0; };
存储器接口实例 存储器写接口: template <class T> class mem_write_if: public sc_interface {public: virtual transfer_status write( \ unsigned int address, \ T& data) = 0; };
存储器接口实例 复位接口: class reset_if: public sc_interface { public: virtual bool reset()=0; };
存储器接口实例 完整的RAM接口: template<class T> class ram_if: public mem_write_if<T>, mem_read_if<T>, reset_if{ public: virtual unsigned int start_address() const = 0; virtual unsigned int end_address() const = 0; };
结论 • 我们将mem_read_if和mem_write_if定义为模板类是因为抽象的存储器的每一个单元可以存储的内容可以是任何类,如存储一个字节char,一个半字short,一个字int,一个packet。 • 接口是可以分层的,复杂的接口可以由多个简单的接口继承而得到 。 • 类sc_interface是所有接口类的父类,所有其它类都直接或者间接的从类sc_interface继承而来。
端口(port) • 连接模块内的进程(行为)和通道(通信) • 基本的SystemC端口类型:sc_in<T>、sc_out<T>、sc_inout<T> • 为了满足行为建模的需要,SystemC允许用户自己定义端口类型。 sc_port<InterfaceType, ChannelNumber = 1>
一些接口定义的例子: //连接到一个RAM上 sc_port< ram_if > ram_port1; //可以连接到N个RAM上 sc_port< ram_if, N > ram_portN; //不限制所连接的RAM数量 sc_port<ram_if, 0 > ram_port0; 端口 • 设计中端口所实际连接的通道的数目可以通过size()方法来访问。如,可以通过ram_port0.size()得到实际连接到ram_port0的通道RAM的数量。
端口 • 端口必须与特定的通道接口相连,或者同父模块的端口相连。一个模块的端口连接到零个、一个或者多个通道,或者零个、一个或者多个父模块的端口,但必须至少连接到1个通道或者父模块的端口上。 • 类sc_port<IF,N>是所有端口的基类,它是一个模板类。IF是接口类型,N是所连接的同一类型的通道数目,也就是接口数,它的缺省值是1。
端口实例-RAM读写端口 SC_MODULE(Master){ sc_in_clk clk; sc_port<ram_if<int>> ram_port;//端口实例 void main_action( );//见下一页 int data; unsigned int address; SC_CTOR(){ SC_CTHREAD(main_action,clk.pos()); } };
端口实例-RAM读写端口 void Master::main_action(){ wait(); int i=0; address=0;//any valid address while(i++<100){ if(status=ram_port->write(address,data)){ …//Display data} else cout<<"RAM write fail"<<endl; if(status=ram_port->read(address, data)){ …//Display data } else cout<<"RAM read fail"<<endl; wait(); address++; }
通道基础 • 在SystemC中,接口本身只是定义了一组通信方法,而不具体负责这些方法如何实现。通道才是这些接口方法的实现者。 • 通道可以实现一个或者多个接口。 • 通道可以连接一个或者多个模块。 • SystemC中通道分为两种:基本通道和分层通道 • 基本通道不包含任何进程,也不对外展现出任何可见结构,它们也不能够直接的或者间接的调用其它基本通道。 • 分层通道本身是一个模块,可以包含进程、子模块,也可以包含和调用其它通道。
端口与通道的关联 Top(sc_module_name name, int size) :sc_module(name) , fifo1("Fifo1", size) , source1("source1") , sink1("sink1"), clk("Clk",PERIOD,SC_NS) {//端口与通道的关联 source1.write_port(fifo1); source1.clk(clk); sink1.read_port(fifo1); sink1.clk(clk); } }; #include "source.h" #include "sink.h“ #define PERIOD 20 class Top : public sc_module { public: sc_clock clk; sc_fifo<char> fifo1; source source1; sink sink1;
基本通道 • 基本通道不包含任何进程,也不对外展现出任何可见结构,它们也不能够直接的或者间接的调用其它基本通道。 • SystemC 2.01中定义了若干基本通道类型,它们是 : • sc_signal<T>,sc_signal_rv<N> • sc_mutex • sc_fifo<T> • sc_semaphore • sc_buffer<T>
基本通道 - sc_signal<T> • sc_signal<T>是最基本的通道,它用于连接模块的基本端口sc_in<T>、sc_out<T>和sc_inout<T>。 • 最多只有一个sc_out<T>或者sc_inout<T>可以连接到sc_signal<T>,否则就会产生典型的多驱动情况。 • 可以有多个sc_in<T>同时连接到sc_signal<T>。 • sc_signal<T>继承于基本通道类,并实现了sc_signal_inout_if<T>接口 。 sc_signal_inout _if<T>接口 的最重要成员函数read()和write() 。
基本通道 - sc_signal_rv<T> • sc_signal_rv<T>是所谓“解析的”信号通道,与sc_signal<T>的不同之处是它允许同时有多个端口连接到其上并进行写操作。
基本通道 - sc_buffer<T> • sc_buffer<T>继承于sc_signal<T>,并重载了write()和update()函数。 • sc_buffer<T>不管write()写的数据是否与原数据相同,都要求进行数据更新;而sc_signal<T>首先要检查新数据是否与原数据相同,如果不同才进行更新。
基本通道 - sc_fifo<T> • FIFO示意图
write(&T)代表写FIFO的方法。 • read()是读FIFO的方法,它返回队头单元的数据。 • num_free()用于查询FIFO还有多少空单元。 • num_available()查询FIFO还有多少个数据可以读。 基本通道 - sc_fifo<T> • sc_fifo<T>是SystemC核心语言库中已经实现了的FIFO通道。 • Size代表FIFO的总单元数, 对于sc_fifo,Size的默认值为16。
sc_fifo<T>例化的例子 sc_fifo<int> fifo1; //一个可以存储整型数据的深度为16的FIFO sc_fifo<packet> fifo2(64);//一个可以存储64个packet类型数据的FIFO
sc_fifo<T>实现的接口 • sc_fifo<T>实现的是接口sc_fifo_in_if<T>和sc_fifo_out_if<T>
sc_fifo_in_if<T> template <class T> class sc_fifo_in_if: virtual public sc_interface {public: // blocking read virtual void read( T& ) = 0; virtual T read() = 0; // non-blocking read virtual bool nb_read( T& ) = 0; // get the number of available samples virtual int num_available() const = 0; // get the data written event virtual const sc_event& data_written_event() const = 0; ……}
sc_fifo_out_if<T> template <class T> class sc_fifo_out_if : virtual public sc_interface { public: // blocking write virtual void write( const T& ) = 0; // non-blocking write virtual bool nb_write( const T& ) = 0; // get the number of free spaces virtual int num_free() const = 0; // get the data read event virtual const sc_event& data_read_event() const = 0; ……}
使用sc_fifo的例子-信源信宿通过FIFO通信 • 信源(Source)和信宿(Sink)通过FIFO通信。
信源模块 class source : public sc_module { public: sc_in_clk clk; sc_port<sc_fifo_out_if<char> > write_port; SC_HAS_PROCESS(source); source(sc_module_name name) : sc_module(name) { SC_CTHREAD(main,clk.neg()); } void main();//See in next slide };
信源模块 void source::main() { int i=0; const char str []="For any problems,feel free to contact the author via Email:chenxiee @mails.tsinghua.edu.cn!\n"; wait(); while (true){ if (rand() & 1) { if (str[i]) {write_port->write(str[i++]);} } wait(); } }
信宿模块 #include <systemc.h> class sink : public sc_module { public: sc_in_clk clk; sc_port<sc_fifo_in_if<char> > read_port; SC_HAS_PROCESS(sink); sink(sc_module_name name) : sc_module(name) { SC_CTHREAD(main,clk.neg()); } void main(); //See in next slice };
信宿模块 void sink::main() { char c; while (true) { if (rand() & 1) {read_port->read(c); cout << c; } wait(); } }
Top模块 Top(sc_module_name name, int size) :sc_module(name) , fifo1("Fifo1", size) , source1("source1") , sink1("sink1"), clk("Clk",PERIOD,SC_NS) {//端口与通道的关联 source1.write_port(fifo1); source1.clk(clk); sink1.read_port(fifo1); sink1.clk(clk); } }; #include "source.h" #include "sink.h“ #define PERIOD 20 class Top : public sc_module { public: sc_clock clk; sc_fifo<char> fifo1; source source1; sink sink1;
sc_main( ) #include <systemc.h> #include "tb.h" int sc_main (int, char **) { unsigned size= 16; Top Top1(“Top1", size); cout <<"Testbench started,the \ simulation result is:" << endl; sc_start(100000, SC_NS); cout<<"\n"<<endl; return 0; }
从本例看模块、接口、端口、通道之间的关系 • 接口是一个C++抽象类,它定义了一组抽象方法,但不定义这些方法的具体实现。 • 通道实现一个或者多个接口。也就是说,通道必须继承一个或者多个接口,这些接口中定义的抽象方法必须在通道中实现。 • 端口总是与一定的接口类型相关联的,端口只能连接到实现了该类接口的通道上。 • 通过端口,模块中的进程可以连接到通道并使用通道提供的方法
基本通道 - sc_semaphore • 通常翻译为信号量。 • 信号量代表可用资源实体的数量,所以可以认为信号量就是一个资源计数器,它限制的是同时使用某共享资源(也称为临界资源)的进程的数量。信号量计数的值代表的就是当前仍然可用的共享资源的数量。 • sc_semaphore实现的是sc_semaphore_if接口,定义见下页。
sc_semaphore_if class sc_semaphore_if: virtual public sc_interface {public: // lock (take) the semaphore, block if not available virtual int wait() = 0; // lock (take) the semaphore, return -1 if not available virtual int trywait() = 0; // unlock (give) the semaphore virtual int post() = 0; // get the value of the semphore virtual int get_value() const = 0;
sc_semaphore::wait() • 其中,wait()方法获得一个信号量,其作用效果是获得一份资源的使用权,使信号量计数减一,如下面的实现代码。 int sc_semaphore::wait(){ while( in_use() ) { sc_prim_channel::wait( m_free ); } -- m_value; return 0; }
sc_semaphore的构造函数 • sc_semaphore( int init_value_ ); • sc_semaphore( const char* name_, int init_value_ );