对于稍大一点的project,我们在经过算法验证(C语言/C++语言)之后,直接进行RTL设计,往往比较困难,这时,我们就需要一种介于算法验证和RTL设计之间的形式来实现,而SystemC就是其中比较好的。
本小节,我们就熟悉一下SystemC。
如果你有C++,C以及verilog HDL的编程经验,你会发现SystemC非常容易使用。
SystemC是在C++的基础上扩展了的一个硬件类和仿真核而形成的C++的超集,所以,SystemC本质上就是C++,所以systemC程序就可以用g++来编译。
如果我们要向编译运行SystemC程序,只需要安装一下SystemC的库即可,步骤如下:
直接从官网下载的包,一般由于和系统的g++的版本不兼容,出现两个小问题。
修改systemc-2.2.0_rill_modified/src/sysc/datatypes/bit/sc_bit_proxies.h中的mutable去掉。
716行:
//mutable X& m_obj;//rill modify X& m_obj;
//mutable X& m_left;//rill modify //mutable Y& m_right; //mutable int m_delete; //mutable int& m_refs; X& m_left; Y& m_right; int m_delete; int& m_refs;
修改systemc-2.2.0_rill_modified/src/sysc/utils/sc_utils_ids.cpp中,增加两个头文件:
#include "cstring" //Rill add #include "cstdlib" //Rill add
http://download.csdn.net/detail/rill_zhen/7203851
首先在~/目录下mkdir systemc 作为我们的安装目录。
export CXX=g++ mkdir objdir cd objdir ../configure -prefix=/home/openrisc/systemc make make install
一门语言,光看语法,是学不会的,多练多写,很快就能掌握,为了便于学习systemc,并验证刚才安装的库是否可用,我写了一个简单的例子。
无论是哪种编程语言,主要包括数据类型和流程控制两大部分,如果掌握了这两个部分,我们基本就可以掌握这门语言了。systemc也不例外。
为了全面的说明systemc的使用方法,在bench.cpp和bench.h中加了一些注释。
adder.h:
#ifndef ADDER_H_ #define ADDER_H_ #include "systemc.h" struct adder : sc_module { sc_in<bool> clk; sc_in<bool> rst; sc_in<bool> enable; sc_in<unsigned int> a1; sc_in<unsigned int> a2; sc_out<unsigned int> sum; sc_out<bool> done; void calc(); SC_CTOR(adder) { SC_METHOD(calc); sensitive << clk.pos(); }; }; #endif //ADDER_H_
adder.cpp:
/* * file name :adder.cpp * func :systemc simple test * author :Rill * date :2014-04-16 */ #include "systemc.h" #include "adder.h" #define CALC_IDLE 0 #define CALC_GET_VALUE 1 #define CALC_OUTPUT 2 void adder :: calc() { static unsigned int a1_tmp; static unsigned int a2_tmp; static unsigned int sum_tmp; static unsigned int flag; if(rst) { printf(" adder rst...\n"); sum = 0; done = 0; flag = CALC_IDLE; } else { if(flag == CALC_IDLE) { done = 0; if(enable) { flag = CALC_GET_VALUE; a1_tmp = a1; a2_tmp = a2; } else { flag = CALC_IDLE; } } else if(flag == CALC_GET_VALUE) { sum_tmp = a1_tmp + a2_tmp; flag = CALC_OUTPUT; } else if(flag == CALC_OUTPUT) { sum = sum_tmp; done = 1; flag = CALC_IDLE; } else { flag = CALC_IDLE; } } } /******************* EOF ********************/
bench.h:
#ifndef BENCH_H_ #define BENCH_H_ #include "systemc.h" struct bench : sc_module { sc_in<bool> clk;//sc_in<sc_bit> clk; sc_in<bool> rst; sc_out<bool> enable; sc_out<unsigned int> a1;//sc_out<sc_uint> a1;sc_out<32> a1; sc_out<unsigned int> a2;//scout<64> a3 = (a1,a2);a3 = {a1,a2} verilog sc_in<unsigned int> sum; sc_in<bool> done; sc_out<bool> finish; //====================== unsigned int cnt; void enale_adder(); void testbench(); SC_CTOR(bench) { SC_METHOD(testbench);//SC_THREAD,SC_CTHREAD sensitive << clk.pos();//sensitive list:clk.neg() | sensitive << rst; sensitive_pos(clk),sensitive(rst) }; }; #endif //BENCH_H
bench.cpp:
/* * file name :bench.cpp * func :systemc simple test * author :Rill * date :2014-04-16 */ #include "systemc.h" #include "bench.h" #define BENCH_INPUT 0 #define BENCH_WAIT 1 #define SIM_CNT 10 void bench :: enale_adder() { a1 = a1 + 3; a2 = a2 + 4; enable = 1; cnt = cnt + 1; } void bench :: testbench() { static unsigned int flag;//note the 'static' if(rst) { printf("bench rst...\n"); a1 = 0; a2 = 0; enable = 0; finish = 0; cnt = 0; flag = BENCH_INPUT; } else { if(cnt > SIM_CNT) { finish = 1; printf("test end\n"); } else { if(flag == BENCH_INPUT) { enale_adder(); flag = BENCH_WAIT; } else if(flag == BENCH_WAIT) { enable = 0; if(done == true) { printf("cnt=%d,%d + %d = %d\n",(int)cnt,(int)a1,(int)a2,(int)sum);//here we must explicit cast to int from unsigned int. flag = BENCH_INPUT; } } } } } /******************* EOF ********************/
main.cpp:
/* * file name :main.cpp * func :systemc simple test * author :Rill * date :2014-04-16 */ #include "systemc.h" #include "bench.h" #include "adder.h" #define CLK_PERIOD 10 int sc_main(int,char*[]) { sc_trace_file * tf = NULL; unsigned loop = 0; sc_signal<bool> clk; sc_signal<bool> rst; sc_signal<bool> enable; sc_signal<unsigned int> a1; sc_signal<unsigned int> a2; sc_signal<unsigned int> sum; sc_signal<bool> done; sc_signal<bool> finish; bench bench0("BENCH"); bench0.clk(clk);//bench0<<clk<<rst<<enable<<a1<<a2<<sum<<done; bench0.rst(rst); bench0.enable(enable); bench0.a1(a1); bench0.a2(a2); bench0.sum(sum); bench0.done(done); bench0.finish(finish); adder adder0("BENCH"); adder0.clk(clk); adder0.rst(rst); adder0.enable(enable); adder0.a1(a1); adder0.a2(a2); adder0.sum(sum); adder0.done(done); tf = sc_create_vcd_trace_file("rill_sc_tf");//tf = sc_creat_wif_trace_file("rill_sc_tf"); sc_trace(tf,clk,"clk"); sc_trace(tf,rst,"rst"); sc_trace(tf,enable,"enable"); sc_trace(tf,a1,"a1"); sc_trace(tf,a2,"a2"); sc_trace(tf,sum,"sum"); sc_trace(tf,done,"done"); sc_trace(tf,finish,"finish"); sc_start(0,SC_NS); for(loop=0;loop<3;loop++)//rst 3 cycles { clk.write(1); rst.write(1); sc_start(CLK_PERIOD/2,SC_NS); clk.write(0); sc_start(CLK_PERIOD/2,SC_NS); } rst.write(0); while(1 != finish.read())//sim calc { clk.write(1); sc_start(CLK_PERIOD/2,SC_NS); clk.write(0); sc_start(CLK_PERIOD/2,SC_NS); } return 0; } /******************* EOF ********************/
Makefile:
TARGET_ARCH = linux
CC = g++
DEBUG = -g
OTHER = -Wno-deprecated
#CFLAGS = $(OTHER)
CFLAGS = -Wall -m32 -O3
MODULE = app
SRCS = main.cpp bench.cpp adder.cpp
OBJS = $(SRCS:.cpp=.o)
# Variabile che indica dove si trova la libreria SystemC
SYSTEMC = /home/openrisc/systemc
INCDIR = -I$(SYSTEMC)/include
LIBDIR = -L$(SYSTEMC)/lib-$(TARGET_ARCH)
LIBS = -lsystemc
EXE = $(MODULE).x
# Comunica al make su quali tipi di estensioni deve eseguire le regole di suffisso.
.SUFFIXES: .cpp .o .x
$(EXE): $(OBJS) $(SYSTEMC)/lib-$(TARGET_ARCH)/libsystemc.a
$(CC) $(CFLAGS) $(LIBDIR) -o $@ $(OBJS) $(LIBS)
# Comunica al make di eseguire una compilazione
# C++ per tutti i file aventi estensione .c
# e per i quali i relativi file oggetto non
# sono stati ancora aggiornati
.cpp.o:
$(CC) $(CFLAGS) $(INCDIR) -c $<
clean:: rm -f $(OBJS) *~ $(EXE) core *.vcd *.wif
编码完成之后,我们就可以编译运行了:
make ./app.x gtkwave rill_sc_tf
需要说明的是,如果你在运行时,提示找不到动态库文件(libsystemc.so),这可能是你的系统的环境变量(LD_LIBRARY_PATH)没有设置,设置一下即可:
修改.bashrc文件,增加如下语句之后,重新打开一个终端即可。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/openrisc/systemc/lib-linux
除了从打印信息来debug之外,我们还可以通过分析vcd波形文件来进行调试:
上面的例子中,我们可以看到模块间的信号的波形,但是如何才能查看子模块内部的信号呢?有两种方式可以实现。
方式1:
sc_trace(tf,adder0.test,"test");
sc_trace(tf, adder0, "adder0");
adder.h:
#ifndef ADDER_H_ #define ADDER_H_ #include "systemc.h" struct adder : sc_module { sc_in<bool> clk; sc_in<bool> rst; sc_in<bool> enable; sc_in<unsigned int> a1; sc_in<unsigned int> a2; sc_out<unsigned int> sum; sc_out<bool> done; unsigned int test; void calc(); SC_CTOR(adder) { SC_METHOD(calc); sensitive << clk.pos(); }; }; extern void sc_trace(sc_trace_file *tf, const adder& v, const char * NAME); #endif //ADDER_H_
adder.cpp:
/* * file name :adder.cpp * func :systemc simple test * author :Rill * date :2014-04-16 */ #include "systemc.h" #include "adder.h" #define CALC_IDLE 0 #define CALC_GET_VALUE 1 #define CALC_OUTPUT 2 void adder :: calc() { static unsigned int a1_tmp; static unsigned int a2_tmp; static unsigned int sum_tmp; static unsigned int flag; if(rst) { printf(" adder rst...\n"); sum = 0; done = 0; flag = CALC_IDLE; test = 0;//sc_trace test } else { test = test + 1;//sc_trace test if(flag == CALC_IDLE) { done = 0; if(enable) { flag = CALC_GET_VALUE; a1_tmp = a1; a2_tmp = a2; } else { flag = CALC_IDLE; } } else if(flag == CALC_GET_VALUE) { sum_tmp = a1_tmp + a2_tmp; flag = CALC_OUTPUT; } else if(flag == CALC_OUTPUT) { sum = sum_tmp; done = 1; flag = CALC_IDLE; } else { flag = CALC_IDLE; } } } void sc_trace(sc_trace_file *tf, const adder& v, const char * NAME) { sc_trace(tf,v.test, "addr0.test"); } /******************* EOF ********************/
main.cpp:
/* * file name :main.cpp * func :systemc simple test * author :Rill * date :2014-04-16 */ #include "systemc.h" #include "bench.h" #include "adder.h" #define CLK_PERIOD 10 int sc_main(int,char*[]) { unsigned loop = 0; sc_trace_file * tf = NULL; sc_signal<bool> clk; sc_signal<bool> rst; sc_signal<bool> enable; sc_signal<unsigned int> a1; sc_signal<unsigned int> a2; sc_signal<unsigned int> sum; sc_signal<bool> done; sc_signal<bool> finish; tf = sc_create_vcd_trace_file("rill_sc_tf");//tf = sc_creat_wif_trace_file("rill_sc_tf"); bench bench0("BENCH"); bench0.clk(clk);//bench0<<clk<<rst<<enable<<a1<<a2<<sum<<done; bench0.rst(rst); bench0.enable(enable); bench0.a1(a1); bench0.a2(a2); bench0.sum(sum); bench0.done(done); bench0.finish(finish); adder adder0("BENCH"); adder0.clk(clk); adder0.rst(rst); adder0.enable(enable); adder0.a1(a1); adder0.a2(a2); adder0.sum(sum); adder0.done(done); sc_trace(tf,clk,"clk"); sc_trace(tf,rst,"rst"); sc_trace(tf,enable,"enable"); sc_trace(tf,a1,"a1"); sc_trace(tf,a2,"a2"); sc_trace(tf,sum,"sum"); sc_trace(tf,done,"done"); sc_trace(tf,finish,"finish"); //sc_trace(tf,adder0.test,"test"); sc_trace(tf, adder0, "adder0"); sc_start(0,SC_NS); for(loop=0;loop<3;loop++)//rst 3 cycles { clk.write(1); rst.write(1); sc_start(CLK_PERIOD/2,SC_NS); clk.write(0); sc_start(CLK_PERIOD/2,SC_NS); } rst.write(0); while(1 != finish.read())//sim calc { clk.write(1); sc_start(CLK_PERIOD/2,SC_NS); clk.write(0); sc_start(CLK_PERIOD/2,SC_NS); } return 0; } /******************* EOF ********************/
下面是波形:
对于一门语言,流程控制,除了模块内部之外,另外一个重要的部分就是模块间的层次组织,上面我们了解了模块内部的流程控制,下面我们对上面的代码稍作修改,即可展示systemc的module hierarchy。
a,将bench.cpp文件名改为benc_sub.cpp并进行简单修改
benc_sub.cpp修改后如下:
/* * file name :bench_sub.cpp * func :systemc simple test * author :Rill * date :2014-04-19 */ #include "systemc.h" #include "bench_sub.h" #define BENCH_INPUT 0 #define BENCH_WAIT 1 #define SIM_CNT 10 void bench_sub :: enale_adder() { a1 = a1 + 3; a2 = a2 + 4; enable = 1; cnt = cnt + 1; } void bench_sub :: testbench() { static unsigned int flag;//note the 'static' if(rst) { printf("bench rst...\n"); a1 = 0; a2 = 0; enable = 0; finish = 0; cnt = 0; flag = BENCH_INPUT; } else { if(cnt > SIM_CNT) { finish = 1; printf("test end\n"); } else { if(flag == BENCH_INPUT) { enale_adder(); flag = BENCH_WAIT; } else if(flag == BENCH_WAIT) { enable = 0; if(done == true) { printf("cnt=%d,%d + %d = %d\n",(int)cnt,(int)a1,(int)a2,(int)sum);//here we must explicit cast to int from unsigned int. flag = BENCH_INPUT; } } } } } /******************* EOF ********************/
b,复制bench.h为bench_sub.h,并稍作修改
bench_sub.h修改后如下:
#ifndef BENCH_SUB_H_ #define BENCH_SUB_H_ #include "systemc.h" struct bench_sub : sc_module { sc_in<bool> clk;//sc_in<sc_bit> clk; sc_in<bool> rst; sc_out<bool> enable; sc_out<unsigned int> a1;//sc_out<sc_uint> a1;sc_out<32> a1; sc_out<unsigned int> a2;//scout<64> a3 = (a1,a2);a3 = {a1,a2} verilog sc_in<unsigned int> sum; sc_in<bool> done; sc_out<bool> finish; //====================== unsigned int cnt; void enale_adder(); void testbench(); SC_CTOR(bench_sub) { SC_METHOD(testbench);//SC_THREAD,SC_CTHREAD sensitive << clk.pos();//sensitive list:clk.neg() | sensitive << rst; sensitive_pos(clk),sensitive(rst) }; }; #endif //BENCH_SUB_H_
c,修改bench.h,在构造函数里例化bench_sub模块
bench.h修改后如下:
#ifndef BENCH_H_ #define BENCH_H_ #include "systemc.h" #include "bench_sub.h" struct bench : sc_module { sc_in<bool> clk;//sc_in<sc_bit> clk; sc_in<bool> rst; sc_out<bool> enable; sc_out<unsigned int> a1;//sc_out<sc_uint> a1;sc_out<32> a1; sc_out<unsigned int> a2;//scout<64> a3 = (a1,a2);a3 = {a1,a2} verilog sc_in<unsigned int> sum; sc_in<bool> done; sc_out<bool> finish; //====================== bench_sub * bench_sun_inst; SC_CTOR(bench) { bench_sun_inst = new bench_sub("bench_sub"); bench_sun_inst->clk(clk); bench_sun_inst->rst(rst); bench_sun_inst->enable(enable); bench_sun_inst->a1(a1); bench_sun_inst->a2(a2); bench_sun_inst->sum(sum); bench_sun_inst->done(done); bench_sun_inst->finish(finish); }; }; #endif //BENCH_H
d,修改Makefile中的SRCS中的bench.cpp为bench_sub.cpp
经过上面的修改后,我们重新编译运行,查看波形:
本实验中的代码,我已上传:
http://download.csdn.net/detail/rill_zhen/7217629
本小节,我们简单熟悉了一下systemc,更深入的学习还要通过实际项目来历练。
enjoy!