systemc 心得

更新时间:2023-12-09 11:05:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

1. module是一个类,从sc_module扩展而来,包含其他modules以及processes,channels and ports。

2. simulation process是由systemc kernel调度执行,它是sc_module的成员函数,并被注册登记到simulation kernel中。由此simulation kernel是这些simulation process唯一的caller,那么这些simulation process没必要带参数和返回值了。sc_module中也可定义一些process,但这些process不被simulation kernel调用,仅仅是一些被simulation process调用的函数。

3. simulation process的registeration发生在elaboration的时候,即module的constructor执行的时候(使用SC_METHOD,SC_THREAD)。

4. SC_METHOD 是sc_module的成员函数,它没有任何参数和返回值,被simulation kernel不停的反复调用,一旦执行无延迟并执行到底。而SC_THREAD只执行一次带延迟,会挂起自己并等待。

5. static sensitivity 和dynamic sensitivity。 对于动态敏感, SC_METHOD用next_trigger(arg), SC_THREAD用wait(arg)来实现。 6. systemC的datatype由类模板以及运算符重载来实现。

supported data type = Native C++ data type + STL data type + SystemC datatype( logic, integers and fixed-point)

systemc 还提供了systemc data type 和 C++ data type 之间的转化函数。 所有的systemc data type都属于sc_dt namespace。

1. logic : sc_bv, sc_lv, bool, sc_logic, 注意sc_bit 被 C++里面的 bool取代了。

2. integer: sc_int, sc_uint (其中W<=64) , sc_bigint, sc_biguint (其中BITWIDTH>64)

systemC的integer比传统的C++的integer类型提供了bit操作和concatination以及可配置位宽。

3. fixed point: sc_fixed

1. systemC的入口是sc_main,而实质上是由用户不可见的main,调用sc_main(int argc,char* argv[])来实现的。systemC提供了sc_argc(), sc_argv()来读取argc和argv[]。 sc_main 包含三个阶段: elaboration,simulation,post-processing。 elaboration阶段完成module的连接以及process的注册。由sc_start()进入simulation阶段,由scheduler调度进程。sc_start()结束后进入post-processing阶段,此阶段对result进行处理并报告。而callback在某些点可插入执行代码。如end_of_elaboration(), start_of_simulation, end_of_simulation. 2. module 声明如下: [cpp] view plaincopyprint? #include SC_MODULE(module_name) {MODULE_BODY};更倾向于:#include class module_name : public sc_module {public:MODULE_BODY}; 具体内容有: 1)ports 2) channel instance 3) data instance 4) sub-designs 5) constructor 6) destructor 7) simulation process 8) member functions module constructor: SC_MODULE(module_name) {SC_CTOR(module_name): Initialization // C++ initialization list{Subdesign_AllocationSubdesign_Connectivity Process_Registration // SC_THREAD...Miscellaneous_Setup}}; process 由systemC kernel调用,所以不需要参数和返回值。 3. recommanded style -----------------Name.h------------------------#ifndef NAME_H#define NAME_HSubmodule forward class declarationsSC_MODULE(NAME) { Port declarations Channel/Submodule* definitions // Constructor declaration: SC_CTOR(NAME); Process declarations Helper declarations};#endif-----------------Name.cpp------------------------#include #include \SC_HAS_PROCESS(NAME); *submodules are implemented using pointers instead of direct instantiation. This method allows for dynamicdesign configuration. 以前DSP算法都是用C++的浮点直接表示,但是为了更精确以及综合的方便,systemC引入了fixed-point类型。它定义了整个的bit宽度以及整数部分的bit宽度(WL,IWL)。加_fast后缀的指定精度限制再53bit之内。ed这个过去式表明在compile的时候就固定了,无法改变。 4. 由于systemc扩展了自己的datatype,这些datatype就是一个类。它们的赋值以及初始化,有时会使用字符串。譬如 sc_lv<6> mask = \ 这些datatype变量值的打印,实际上是先把它们转化成字符串,然后打印出来。譬如 cout<>): sc_lv<6> mask = 15; cout<< mask 5. SystemC data type重载并扩展了所有C++的操作符。类似于C++,不同类型的data type进行运算必须显示的转化其中之一。 sc_int<64> g(\sc_int<64> h(\sc_int<64> i(\sc_bigint<70> bigsum = g + h + i; // Doesn’t workbigsum = sc_bigint<70>(g) + h + i;// Works 6. 常用的STL vector, map,set, List,deque 1. systemC sc_time datatype 类似于 systemc datatype, 通过运算符重载支持+=, <,... 以及流<<, >>, 同样也有到C++类型的转化函数。 2. sc_time 类型被simulation kernel用来跟踪simulation time,以及定义timeout和delay 3. sc_time name(double, sc_time_unit)…; sc_time_unit有 SC_NS, SC_PS等枚举 4. 在sc_time对象创建前,可用sc_set_time_resolution(double, sc_time_unit); 来指定resolution 5. sc_time current_time = sc_time_stamp(); 获得当前时间邮戳。 6. sc_start() 也可指定timeout时间。 7. wait(sc_time) 1. SystemC 使用 simulation process来实现concurrency。simulation process 运行了一段code segment之后我们期望它能返回,将控制权交给simulation kernel。 2. SC_THREAD的限制: 1) 只能在module中使用,因此function必须在作为module的成员 2) 只能在elaboration 阶段使用。3)只启动一次,一旦退出不能重启。 4)遇到wait,进行上下文切换,将控制权交给kernel 3. SystemC simulation 四个阶段 1) elaboration: connection and registration 2) initialization: 将没有特别声明dont initialized的process放入set of runable process,dont initialized process 放入set of waiting process 3) simulation: schedule processes to run and advance time。它又分为两个阶段:evaluate和advance time。当出现下列情况时才会advance time a)nothing in runable process,b)A process meet sc_stop() c)timeout。 4) post-processing 4. event syntax: event_name.notify(void); // Immediate event_name.notify(SC_ZERO_TIME);// Delayed event_name.notify(sc_time); // Timed (time>0) event_name.notify(double,units);// Convenience 一个sc_evnet对象只能有一个outstanding scheduled event。譬如: sc_event A_event;A_event.notify(10,SC_NS);A_event.notify( 5,SC_NS); // only this one staysA_event.notify(15,SC_NS); event在触发前可以取消,用event_name.cancel(); 等待event的语法: wait(time); // timeout is the eventwait(double,time_unit); // conveniencewait(event); // single eventwait(event1 | eventn?); // any of thesewait(event1 & eventn?); // all of thesewait(time,event); // event or timeoutwait(time,event1 | eventn?);// any event or timeoutwait(time,event1 & eventn?);// all events or timeoutwait(); // static sensitivity – discussed later Zero Time Delay event_name.notify(SC_ZERO_TIME);// when all runable process complete, notification are issued. wait (SC_ZERO_TIME);// wait all runable process complete. 5. SC_METHOD

SC_METHOD一旦执行必须执行到底永远不会挂起(也不会被其他任何进程中断),所以中间不允许任何时间延迟或者block。 SC_METHOD中的变量会消失,每次调用SC_METHOD时local的变量都必须重新初始化,而SC_THREAD中的变量只要THREAD不消失就一直存在(挂起时会保存现场)。所以一般SC_METHOD会依赖全局变量(即module中声明的变量)。而SC_THREAD多用自己的局部变量。 为了区别method和thread,强烈推荐XX_method, XX_thread这种coding style catching event For Method: next_trigger(time);next_trigger(timeout,time_unit); //conveniencenext_trigger(event);next_trigger(event1 | eventi?); //any of thesenext_trigger(event1 & eventi?); //all of these//requirednext_trigger(timeout,event); //event with timeoutnext_trigger(timeout,event1 | eventi?);//any +timeoutnext_trigger(timeout,event1 & eventi?);//all + timeoutnext_trigger(void); //re-establish static sensitivity Process resume的方法有: SC_THREAD通过wait, SC_METHOD通过next_trigger. SC_METHOD还可以通过static sensitivity list来进行resume。如下 // IMPORTANT: Must follow process registrationsensitive << event [<< event]?; // streaming style 而且可以返回sc_event的对象都可以加入到sensitive list中。 此外SC_THREAD结合其中的wait()也可实现static sensitivity. 6. 用sc_event_queue 来支持多个outstanding event schedule。 7. dont_initialize() 1. 开启dynamic process enabler: #define SC_INCLUDE_DYNAMIC_PROCESSES #include 2. register dynamic process with void return: sc_process_handle hname = // ordinary functionsc_spawn( sc_bind(&funcName, ARGS?)//no return value ,processName ,spawnOptions);sc_process_handle hname = // member functionsc_spawn( sc_bind(&methName, object, ARGS?)//no return ,processName ,spawnOptions); 3. register dynamic process with return sc_process_handle hname = // ordinary functionsc_spawn( &returnVar ,sc_bind(&funcName, ARGS?) ,processName ,spawnOptions);sc_process_handle hname = // member functionsc_spawn( &returnVar ,sc_bind(&methodName, object, ARGS ?) ,processName ,spawnOptions); 以上object指向calling module,通常用this替代。如果所传的参数是ref而不是value,则用 sc_ref(var) // reference sc_cref(var) // constant reference sc_bind 将参数传给类spawned function。sc_process_handle 这个类提供了一种安全的机制用来操作unspawned以及spawned process,而不是依赖于指针。 sc_spawn_option objname;objname.spawn_method();// register as SC_METHODobjname.dont_initialize();objname.set_sensitivity(event_ptr);objname.set_sensitivity(port_ptr);objname.set_sensitivity(interface_ptr);objname.set_sensitivity(event_finder_ptr);objname.set_stack_size(value); // experts only! 5. spawned process example #define SC_INCLUDE_DYNAMIC_PROCESSES#include ?void spawned_thread() {// This will be spawned cout << \ << sc_get_current_process_handle().name() << \ wait(10,SC_NS); cout << \}void simple_spawn::main_thread() { wait(15,SC_NS); // Unused handle discarded sc_spawn(sc_bind(&spawned_thread)); cout << \ << \ wait(15,SC_NS); cout << \ << \} sc_process_handle h =sc_spawn(sc_bind(&spawned_thread));// Do some work?// Wait for spawned thread to returnwait(h.terminated_event()); 6. SC_FORK ... SC_JOIN SC_FORK COMMA_SEPARATED_LIST_OF_SPAWNSSC_JOIN [cpp] view plaincopyprint? 1. DataStream d1, d2; 2. SC_FORK

3. sc_spawn( sc_bind(&dut::AXI_xmt,this,sc_ref(d1)), \4. ,sc_spawn(sc_bind(&dut::PCIX_rcv,this,sc_ref(d1)),\5. ,sc_spawn(sc_bind(&dut::USB2,this,sc_ref(d1)), \6. ,sc_spawn(sc_bind(&dut::HT1_xtm,this,sc_ref(d2)), \7. ,sc_spawn(sc_bind(&dut::HT2_rcv,this,sc_ref(d2)), \8. SC_JOIN

为了帮助有效地进行communication,引入了channel的概念。sc_prim_channel扩展出了三个primitive channel: sc_mutex, sc_semaphore, sc_fifo。 这些primitive channel实现了对应的interface: sc_mutex_if; sc_semaphore_if; sc_fifo_in_if;sc_fifo_out_if;

1. mutex Syntax sc_mutex NAME;NAME.lock(); // Lock the mutex,// wait until unlocked if in useint NAME.trylock() // Non-blocking, returns successNAME.unlock(); // Free a previously locked mutex 2. sc_semaphore: syntax sc_semaphore NAME(COUNT);NAME.wait(); // Lock one semaphore// Wait until available if in useint NAME.trywait() // Non-blocking, return successint NAME.get_value() // Returns available semaphoresNAME.post(); // Free one previously locked// semaphore mutex is a semaphore with a count of one 3. sc_fifo sc_fifo inherit from sc_fifo_in_if and sc_fifo_out_if; \reading out from fifo. \sc_fifo NAME(SIZE);NAME.write(VALUE);NAME.read(REFERENCE);? = NAME.read() /* function style */if (NAME.nb_read(REFERENCE)) { // Non-blocking// true if success?}if (NAME.num_available() == 0) wait(NAME.data_written_event());if (NAME.num_free() == 0) next_trigger(NAME.data_read_event()); 4. software 之间的通讯经常使用unbounded fifo,也就是STL里面的list. 此外还有mailbox。 5. 利用指针传递object,效率高,但是需要一种安全的机制,因此出现了smart pointer in boost library,即shared_ptr。 typedef boost::shared_ptr smart_ptr然后我们在定义 fifo 的时候 用sc_fifo_in in;然后在实现的时候定义smart_ptr ptr;...in->nb_read(ptr);if (ptr->data()== STOP) ... smart_ptr 类似于一个指向 mydata的指针。所不同的是这个指针牛逼得能够自己管理自己,自动删除自己,从来不需要delete这个指针。 Evaluate - update Channel: sc_signal. 1. notify(SC_ZERO_TIME), the notification 发生在 evaluation结束后的update phase. 2. sc_signal 也是systemC的 primitive channel, 他利用request-update作为同步的机制。 sc_signal signame[, signamei]?;//define?signame.write(newvalue);varname = signame.read();wait(signame.value_changed_event()|...);wait(signame.default_event()|...);if (signame.event() == true) {// occurred in previous delta-cycle 3. sc_signal重载了=,但不建议使用,这样比较危险。 signame = newvalue;// implicit .write() dangerousvarname = signame;// implicit .read() mild danger 4. sc_buffer 类似于 sc_signal 是 evaluate-update channel. 而且在一个delta cycle内只允许single process 写,不允许多个process写。 5. systemc引入了sc_signal_resolved, sc_signal_rv来解决multiple writer的问题(即bus has contention or high Z)。 有点类似于 sc_signal 唯一的区别是但是允许multiple writer。

6) 对于sc_signal 还有 signame.posedge_event, signame.negedge_event. 7) request update 讲解

当一个process调用write method时,write method先将值写入New value存储区域,然后调用sc_primitive_channel::request_update() method 来通知simulation kernel。当 update phase 到来时,kernel 自动调用曾在evaluation phase 时请求更新(request update) 的channel 中的 update()method。这个update() method不光拷贝new value 到current value,而且会解决竞争,以及notify一些事件从而唤醒某些进程。 1. Design Hierarchy

2. How to creat hierarchy

1) Direct/indirect Top-Level Implementation //=================direct===================//FILE: main.cpp#include #include \int sc_main(int argc, char* argv[]) { Car car_i(\ sc_start(); return 0;}//=================indirect===================//FILE: main.cpp#include #include \int sc_main(int argc, char* argv[]) { Car* car_iptr; // pointer to Car 2) direct submodule //FILE:Car.h#include \#include \SC_MODULE(Car) { Body body_i; Engine eng_i; Car(sc_module_name nm);};//FILE:Car.cpp#include #include \// ConstructorSC_HAS_PROCESS(Car);Car::Car(sc_module_name nm): sc_module(nm), body_i(\ 3) indirect submodule -- For IP deliver //FILE:Engine.hclass FuelMix;class Exhaust;class Cylinder;SC_MODULE(Engine) { FuelMix* fuelmix_iptr; Exhaust* exhaust_iptr; Cylinder* cyl1_iptr; Cylinder* cyl2_iptr; Engine(sc_module_name nm); // Constructor};//FILE: Engine.cpp#include #include \#include \#include \ 注意上例中 Engine.h 没有包括 #include \而是移到了Engine.cpp中。这就是最大的好处,你只需要提供Engine.h 和 Engine.o 或者 Engine.a就行了,而且 .o, .a都可以方便的更新。 1. 概念的引入: port是一个指针,它指向模块外部的channel。 sc_export是一个指针,它指向其他模块内部的channel。interface是一个抽象类(定义了一些纯虚函数),它也可被看成API,可派生出各个类。 2. 定义: A SystemC interface is an abstract class that inherits from sc_interface and provides only pure virtual declarations of methods referenced by SystemC channels and ports. No implementations or data are provided in a SystemC interface. SystemC interface 是个抽象类,它由sc_interface派生而来,仅仅声明了纯虚函数(这些纯虚函数会被channel以及port引用),而不含有任何数据成员。 A SystemC channel is a class that inherits from either sc_channel or from sc_prim_channel, and the channel should1 inherit and implement one or more SystemC interface classes. A channel implements all the pure virtual methods of the inherited interface classes. SystemC channel 由sc_channel或sc_prim_channel派生而来,channel必须至少由一个interface派生而来,并实现interface class 中定义的所有纯虚函数。 A SystemC port is a class templated with and inheriting from a SystemC interface. Ports allow access of channels across module boundaries. SystemC port 绑定了interface,并重载了->运算符。语法如下: sc_port portname; SC_MODULE(stereo_amp) {sc_port > soundin_p;sc_port > soundout_p;?};//注意 当嵌套templated class时,需要一个空格在“>”之前 当进行端口连接时(sc_port例化执行时),sc_port重载了()运算符,保存了channel object的指针。因此sc_port可看成一个interface pointer指向了channel。sc_port还重载了->,让sc_port表现得更像一个指针。 3. process间的通讯 1. 和同层process通讯是通过event以及interface. 如下 process<-->interface<-->channel<-->interface<-->process。 2. 通过port和module外部通讯 3.和同层子module通讯是通过interface。 如下 process<-->interface<--->channel<--->submodule port 或者 process<-->interface<---> sc_export Standard Interface 1. FIFO interface sc_fifo channel 由 sc_fifo_in_if 以及 sc_fifo_out_if扩展而来。所谓interface的out于in是相对于module的port而言的。sc_fifo_in_if提供了所有的从FIFO输入到module的纯虚函数。而sc_fifo_out_if提供了所有的从module输出到FIFO的纯虚函数。其中read和write函数是blocking的(FIFO空或满时等待) // Definition of sc_fifo input interfacetemplateclass sc_fifo_in_if: virtual public sc_interface {public: virtual void read( T& ) = 0; virtual T read() = 0; virtual bool nb_read( T& ) = 0; virtual int num_available() const = 0; virtual const sc_event& data_written_event() const = 0;};// Definition of sc_fifo output interfacetemplate class sc_fifo_out_if: virtual public sc_interface {public: virtual void write(const T& ) = 0; virtual bool nb_write(const T& ) = 0; 2. Signal Interface 类似的,sc_signal channel由两个interface扩展而来。sc_signal_in_if,sc_signal_inout_if. // Definition of sc_signal input interfacetemplateclass sc_signal_in_if: virtual public sc_interface {public: virtual const sc_event& value_changed_event() const = 0; virtual const T& read() const = 0; virtual bool event() const = 0;};// Definition of sc_signal input/output interfacetemplateclass sc_signal_inout_if: public sc_signal_in_if{public: virtual void write( const T& ) = 0;}; 3. Sensitivity and Event finder sensitive list 中可以是event,我们很期待SC_METHOD process 能够被channel中定义事件触发。但是channel中的event不能直接放在sensitive list中,因为我们知道port指向了这些channel,这就意味着sensitive list中有port->xxx_event. 而port在此时 处于未定义。

systemc 的event finder解决了这个问题。sc_event_finder 将actual event的判定拖后到elaboration结束之后。 [cpp] view plaincopyprint? class eslx_port:public sc_port,1>{public:// Use a typedef to shorten syntax below typedef sc_signal_in_ifif_type; sc_event_finder& ef_posedge_event() const { return *newsc_event_finder_t(*this,&if_type::posedge_event); // first parameter:port being found(*this) // second parameter: member function returns reference to event of interes }//end ef_posedge_event};//============================================SC_MODULE(my_module) { eslx_port my_p; ? SC_CTOR(?) { channel中常有的event是 data_write_event, posedge_event, value_changed_event, default_event. 如果嫌sc_event_finder使用起来麻烦,那么systemc已经定义好了一些template specialized ports。这些ports由sc_port扩展而来,并且包括了event finder。使用时只要用这些specialized port代替general的port就行了。 // Equalizer.hSC_MODULE(Equalizer) { sc_fifo_in raw_fifo_ip; sc_fifo_out equalized_fifo_op; //sc_fifo_in以及sc_fifo_out 就是specialized port void equalizer_thread(); SC_CTOR(Equalizer) { SC_THREAD(equalizer_thread); sensitive<< raw_fifo_ip.data_written(); // 仅仅在elaboration phase时使用\ }};//===========================================// Equalizer.cppvoid Equalizer::equalizer_thread() { for(;;) { double sample; result; GUIDELINE: Use dot (.) in the elaboration section of the code, but use arrow (->) in processes. GUIDELINE: To avoid confusion, never use the assignment operator with sc_signal or sc_port >. Instead, usethe write() method.有哪些specialized port? sc_in, sc_out, sc_inout, sc_inout_resolved, sc_inout_rv, sc_fifo_in, sc_fifo_out. 一般都是这种形式xxx_in, xxx_out. 4. Port Array and Policy port array 允许产生一堆相同的port。 port Policy规定了是否至少有一个port,或全部port必须连接起来。或者都可以不连。 sc_port portname;// N=0..MAX Default N=1// POL is of type sc_port_policy// POL defaults to SC_ONE_OR_MORE_BOUND// POL other enumeration are SC_ZERO_OR_MORE_BOUND, SC_ALL_BOUND //FILE: Switch.hSC_MODULE(Switch) { sc_port ,5 ,SC_ONE_OR_MORE_BOUND > T1_ip; sc_port ,0 > request_op; ...}; 5. sc_export是为了把channel放入到设计的module(即开发的IP)的内部,这样就隐藏了一些连接的细节。使用的时候这个sc_export就像一个channel。有点类似于一个pin脚,它拉到了模块的管脚上,但是default是不可见的。只有debug的时候是可见的。

本文来源:https://www.bwwdw.com/article/2gy5.html

Top