SystemC的意义,网上能查到,这里总结一下,System C是C++的library,类似UVM是systemverilog的library
下图是SystemC在整个项目中的角色
去官网下载tar包, https://www.accellera.org/downloads/standards/systemc
我下载的是SystemC 2.3.3 (Includes TLM), Core SystemC Language and Examples (tar.gz)
1.放到linux 任意路径,tar -zxvf 解包
2.在systemc-2.3.3中有一个INSTALL的文件,安装步骤就可以了
2.1 mkdir objdir; 2.2 cd objdir; 2.3 export CXX=g++ or setenv CXX g++ 2.4 ../configure --prefix=/usr/local/systemc (重新指定安装路径) 2.5 make; 2.6 make check 2.7 make install 2.9 cleean
在安装的文件下有 example, 进入make,
如果报错的话根据提示看一下就好,遇到了TARGET_ARCH, 使用的机器是linux64, default 这个变量是空的
这里写了一个简单的hello world, 并自己写了一个makefile
例子没什么好说的,System C电子系统设计一开始就把code show出来了
#ifndef _HELLO_H
#define _HELLO_H
#include "systemc.h"
SC_MODULE(hello) {
SC_CTOR(hello) {
cout<<"Hello SystemC!"<
g++ -O2 -Wall -Werror -Wno-unused-variable -Isrc -I/user/systemc/include -c main.cpp -o main.o
g++ -O2 -Wall -Werror -Wno-unused-variable -L/user/systemc/lib-linux64 -Wl,-rpath=/user/systemc/lib-linux64 -lsystemc -lm -o com main.o
这里可能会报找不到.so, 需要加入-Wl,-rpath=xxx, 把路径指过去,或者设置环境变量LD_LIBRARY_PATH
CXX := g++
GCCVERSION := $(shell expr `gcc -dumpversion`)
#FLAGS_STRICT = -pedantic -Wno-long-long
DEBUG = -g
CXXFLAGS= -O2 -Wall -Werror -Wno-unused-variable
SYSTEMC = /user/systemc
INCDIR = -Isrc -I$(SYSTEMC)/include
LIBDIR = -L$(SYSTEMC)/lib-linux64
LIBS = -lsystemc -lm
RPATH = $(SYSTEMC)/lib-linux64
SRCS := $(wildcard ./*.cpp)
OBJS = $(patsubst %cpp, %o, $(SRCS)) main.o
%.o: %.cpp
echo "Compile " $< " to " $@
$(CXX) $(CXXFLAGS) $(INCDIR) -c $< -o $@
all: $(OBJS)
$(CXX) $(CXXFLAGS) $(LIBDIR) -Wl,-rpath=$(RPATH) $(LIBS) -o $@ $^
./all
clean:
rm -rf *.o *.out ./com
这个例子来自于System C电子系统设计,这里记录遇到的问题
1.sc_start(100, SC_NS); // 2.3.3版本需要把clock 单位加上
2.vcd 文档打开,simvision/verdi, open database 打开就行
SC_MODULE(module)
实际上SC_MODULE的code如下: #define SC_MODULE(module_name) struct module_name : public sc_module, 所以也可以使用C++的方式定义module,class module : public sc_module; 两者区别是前者默认所有的成员是public的,而后者是private的
1. SC_CTOR
SC_CTOR(module_name) 中定义了SC_METHOD等3个进程所需要的一些定义
2.传统C++的方式
SC_AS_PROCESS(module_name);
module_name ( sc_module_name n): sc_module(n) {...}
析构函数: ~module_name(){...}
sc_in
sc_out
sc_inout
sc_in_clk clk;
sc_signal
read() // mem[addr.read()]wr_data.read();
write()
typedef sc_in
sc_clock 类: 参数(name, period, 时间单位, 占空比, 开始时间, 第一个逻辑是低电平还是高电平)
eg: sc_clock clk("fclk", 20, SC_NS, 0.5, 5, true);
推荐写法: 1. sc_clock clk1("clk1", 20, SC_NS); 2. sc_clock clk2("clk2", 20, SC_NS, 0.5);
, : (a, b) 将a, b 串起来, 类似位拼接
range(left, range): 位选择 a[left, range]
位减操作:and_reduce, 类似&A
sc_bigint
sc_biguint
可以通过自定义SC_MAX_NBITS提高速度, 推荐SC_MAX_NBITS定义为BITS_PER_DIGIT(30)的整数倍
typedef struct _frame {...} frame;
typedef struct _frame {
unsigned short frame_constrol;
char src_addr[6];
char dst_addr[6];
char *body;
} frame;
System C 基本进程有3种
1. SC_METHOD
2. SC_THREAD
3. SC_CTHREAD
sc_method 需要敏感列表,不能使用wait()这样的语句,避免死循环,防止时间停止。 当敏感列表中有事件发生,它会被调用,调用后立刻返回。
每个clk的上升沿, main被执行一次。
SC_CTOR(tb) {
SC_METHOD(main);
sensitive<
sc_thread 能够被挂起和重新激活。 sc_thread使用wait()挂起, 当敏感列表中有事件发生,进程被重新激活运行到新的wait语句,在一次仿真中thread一旦退出,将不再进入。
eg: SC_THREAD(main);
sensitive< Clock Thread Process, 是一种特殊的thread,继承于thread, 但是只能在时钟上升沿或者下降沿被触发。 wait() 只能用于thread/Cthread. 参数有下面种 1.wait(); 等待敏感列表中的事件 2. wait(const sv_event&) ; 等待事件发生,eg: sc_event e1; wait(e1); 3. wait(sc_event_or_list &); 等待事件之一发生; eg: sc_event e1, e2, e3; wait(e1|e2|e3); 4. wait(sc_event_and_list &); 等待事件全部发生; 5. wait(200, SC_NS); 6. wait(200, SC_NS, e1); 在200NS内等待e1, 如果期间等不到后,就不再等带e1 next_trigger(); 只用于SC_METHOD SC_METHOD and SC_THREAD必须有敏感列表, 使用sensitive设置敏感列表: sensitive<<敏感列表<<敏感列表N; sensitive< 如果进程不希望在0时刻执行,此时可以使用dont_initialize(); systemC 允许仿真结果dump位VCD(Value Change Dump)格式, dump struct data需要重载sc_trace(); 1.常用macro: SC_REPORT_INFO(msg_type, msg) SC_REPORT_WARNING(msg_type, msg) SC_REPORT_ERROR SC_REPORT_FATAL sc_assert(expr) 主要使用sc_report_handler and sc_report, SC_REPORT_INFO <-> sc_report_handler::report(SC_INFO, msg_type, msg, _FILE_, _LINE_); systemc 中定义了interface, port, channel. 接口是一个C++抽象类,通道实现一个或者多个接口 接口中定义的抽象方法在通道中实现 1.接口是C++抽象类 2. 接口的所有方法用“=0”标识是纯虚函数 3.C++ 不允许创建抽象类对象 4. sc_interface是所有接口的基类 5.接口不包括任何数据成员 ram interface example 自定义端口: 一个端口可以同时连接到一个或者多个实现了同一接口的通道上: 格式: sc_port sc_port sc_port 系统抽象的3个关键: 通道才是接口方法的实现者 通道本身也可能是module 通道分为primitive channel 和hierarchical channel. 基本通道包括:sc_fifo, sc_signal, sc_signal_rv, sc_buffer, sc_semaphore, sc_mutex. 互锁, class sc_mutex_if: virtual public sc_interface notify() cancel() eg: sc_event e; e.notify(2, SC_NS); wait(e); 打印相关信息 如果已经有DPI-C相关知识的话,这部分跟RTL-C 是一样的 extern C function, 目的是为了verilog能call 这部分function, 在verilog中调用call_systemc_function, 转到SC中sc_model__called_from_sv,在转到sc_model中的input_string_extern_c interface 中关联与SC的function 在构造函数中加入SC_THREAD 可以先参考下面的文档 https://blog.csdn.net/ocarvb/article/details/108450311 下面网址可以下载uvmc library https://verificationacademy.com/cookbook/uvm-connect sc, sv 沟通的transaction可以通过uvm_object_utils来关联,但是顺序必须一样 一种是sv中的init直接连接sc中的target,另一种可以在tb中写一个adapter,使用passthrough做切割 1. uvm agent monitor中发出command 2. 在env将init socket 连接到SC层 3.在SC中实现socket, main中connect, 在module中通过register_b_transport 指定执行哪个function 1. SC 部分 2. SV中接收 SC_CTHREAD
SC_CTOR(tb){
SC_CTHREAD(gen_input,clk.pos());
reset_signal_is(rst_i, true);
}
wait() and next_trigger()
dont_initialize() and sensitive
SystemC 波形dump
// trace file creation
sc_trace_file * tf = sc_create_vcd_trace_file("Nand2");
sc_trace(tf, N2.A, "A");
sc_trace(tf, N2.B, "B");
sc_trace(tf, N2.F, "F");
sc_start(200, SC_NS);
sc_close_vcd_trace_file(tf);
struct packet {
char src_addr;
char dst_addr;
int payload;
}
void sc_trace(sc_trace_file *tf, const packet v, const string name) {
int i;
sc_trace(tf, v.src_addr, name+".src_addr");
sc_trace(tf, v.dst_addr, name+".dst_addr");
sc_trace(tf, v.payload, name+".payload");
}
SystemC 报告机制
四 SystemC 行为建模
TLM 知识
interface
enum transfer_status { TRANSFER_OK=0, TRANSFER_ERROR};
template
port(端口)
Channel (通道)
sc_mutex_if
sc_semaphore_if
sc_event
TLM2
cout<<"tlm release:\n\t"<
TLM2基本组件
SystemC with UVM via DPI
verilog call C
C部分:
SC_MODULE( sc_model ) {
...
SC_CTOR( sc_model ) {
...
}
};
//
SC_MODULE_EXPORT(sc_model)
extern "C" {
sc_model * sc_model__getScopeByName( const char* inst ) {
sc_model* scope = NULL;
scope = dynamic_cast
verilog
interface sc_model_if ();
import uvm_pkg::*;
event my_event; //*< event triggered by call by SystemC model
chandle scope; //*< C instance scope
// called by interface code to get chandle for C instance scope
import "DPI-C" pure function chandle sc_model__getScopeByName ( input string inst );
// called by interface code to establish callback scope for model
import "DPI-C" context function void sc_model__linkSvScope( chandle scope );
// importing the functions and tasks that we need to be able to access in the SystemC model
import "DPI-C" context task sc_model__called_from_sv ( chandle scope, input string input_string );
// exporting functions that we want the c model to be able to call
export "DPI-C" function sv_called_from_sc_model;
string sc_model_hierarchy;
task call_systemc_function(input string input_string);
if( scope != null )
sc_model__called_from_sv ( scope, input_string );
else
`uvm_fatal( $sformatf("%m"), "Call to call_systemc_function before connecting to hierarchy" )
endtask
function void sv_called_from_sc_model (input string input_string);
`uvm_info($sformatf("%m"), $sformatf("function sv_called_from_sc_model called by the SystemC model with: %s", input_string), UVM_MEDIUM )
`uvm_info($sformatf("%m"), "triggering event to notify the UVM environment", UVM_MEDIUM )
-> my_event;
endfunction
task wait_for_event();
@(my_event);
// or perhaps wait(my_event.triggered()); to avoid races if its not triggered multiple times in a time step
endtask
endinterface
C call verilog
C 部分
SC_MODULE(sc_model){
SC_CTOR( sc_model ) {
SC_THREAD(call_verilog);
}
void call_verilog() {
ostringstream msg;
msg << "Entering function call_verilog";
SC_REPORT_INFO( "SYSTEMC:: ", msg.str().c_str() );
wait(5, SC_NS);
svSetScope(sv_if);
sv_called_from_sc_model("string passed from SystemC model");
}
}
verilog 部分
interface sc_model_if ();
import uvm_pkg::*;
// exporting functions that we want the c model to be able to call
export "DPI-C" function sv_called_from_sc_model;
function void sv_called_from_sc_model (input string input_string);
`uvm_info($sformatf("%m"), $sformatf("function sv_called_from_sc_model called by the SystemC model with: %s", input_string), UVM_MEDIUM )
`uvm_info($sformatf("%m"), "triggering event to notify the UVM environment", UVM_MEDIUM )
-> my_event;
endfunction
endinterface
Xcelium command:
elaborate
xrun +UVM_NO_RELNOTES -xmlibdirname tmp_xcelium.d -l gen_header.log -64bit -licqueue -sv \
-uvm -vtimescale 1ns/1ns -f filelist -elaborate \
-dpi \
-dpiheader dpi_export.h \
-dpiimpheader dpi_import.h
run
xrun -quiet -64bit -licqueue -sv +UVM_NO_RELNOTES -uvm +UVM_VERBOSITY=HIGH \
# SystemC model
-sysc -I.
../src/sc_model.cpp
-scautoshell verilog
# Verilog testbench
-vtimescale 1ns/1ns -f filelist
# DPI export
-dpi
#debug
-g
#-tcl
#-input debug.tcl
-linedebug
#-uvmlinedebug
SystemC with UVM via TLM2
tips:
SV to SC
class my_monitor extends uvm_component;
`uvm_component_utils(my_monitor)
uvm_tlm_b_initiator_socket#(uvm_tlm_generic_payload) init_skt;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
init_skt = new("init_skt", this);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
uvm_tlm_generic_payload gp;
uvm_tlm_time delay;
byte unsigned wdata[];
......
gp = uvm_tlm_generic_payload::type_id::create("gp");
delay = new("delay");
wdata = new[`CRB_DATA_WIDTH/8];
wdata = {<
adapter:
class b_socket_barrier #(type T=uvm_tlm_generic_payload) extends uvm_component;
`uvm_component_param_utils(xxx_b_socket_barrier#(T))
uvm_tlm_b_target_socket#(xxx_b_socket_barrier#(T),T) tgt_skt;
uvm_tlm_b_initiator_socket#(T) init_skt;
virtual task b_transport(T obj,uvm_tlm_time dly);
init_skt.b_transport(obj,dly);
endtask
endclass
env:
uvmc_pkg::uvmc_tlm#(uvm_tlm_generic_payload)::connect(agt.monitor.init_skt,"sv2sc_cmd");
#include "systemc.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
#include "tlm.h"
using namespace tlm;
class top_module : public sc_module{
public:
top_module (sc_module_name name); // constructure
tlm_utils::simple_target_socket
SC 2 SV
module top;
multi_passthrough_initiator_socket
adapter:
class tlm2_to_tlm1_b_socket_barrier #(type T=uvm_tlm_generic_payload) extends uvm_component;
`uvm_component_param_utils(tlm2_to_tlm1_b_socket_barrier#(T))
uvm_tlm_b_target_socket#(tlm2_to_tlm1_b_socket_barrier#(T),T) tgt_skt;
uvm_analysis_port #(T) m_ap;
task b_transport(T obj,uvm_tlm_time dly);
m_ap.write(obj);
endtask
endclass
model:
uvm_analysis_port#(item) ap;
barrier.m_ap.connect(ap);
uvmc_pkg::uvmc_tlm#(item)::connect(barrier.tgt_skt,"out_0");
env:
model.ap.connect(m_scb.output_sb.expected_trans_fifo.analysis_export);
完整example
C code:
// because of dpi, so the code is not clean
#include
SV code:
// the uvm agent is ready, here put all code on uvm_test
`include "uvm_macros.svh"
import uvm_pkg::*;
import uvmc_pkg::*;
class my_uvm_test extends uvm_test;
`uvm_component_utils(my_uvm_test)
uvm_tlm_b_initiator_socket#(uvm_tlm_generic_payload) ini_skt;
uvm_tlm_b_target_socket#(my_uvm_test, uvm_tlm_generic_payload) tgt_skt;
function void build_phase (uvm_phase phase);
ini_skt= new("ini_skt", this);
tgt_skt = new("tgt_skt",this);
endfunction // build_phase
function void connect_phase(uvm_phase phase);
uvmc_pkg::uvmc_tlm#(uvm_tlm_generic_payload)::connect(tgt_skt, "out");
uvmc_pkg::uvmc_tlm#(uvm_tlm_generic_payload)::connect(ini_skt, "in");
$display("connect done");
endfunction
// SC to SV, if have multi socket, can add a adapter class, and instance adapters
// eg: uvmc_pkg::uvmc_tlm#(payload)::connect(barrier0.tgt_skt,"out_0");
// uvmc_pkg::uvmc_tlm#(payload)::connect(barrier1.tgt_skt,"out_1");
// b_transport is implement on the barrier class
task b_transport(uvm_tlm_generic_payload obj,uvm_tlm_time dly);
$display("SV: b_transport, addr=%0d", obj.get_address());
//obj.set_response_status(tlm::TLM_OK_RESPONSE);
endtask
task run_phase(uvm_phase phase);
phase.raise_objection(this);
begin
uvm_tlm_generic_payload gp;
uvm_tlm_time delay;
byte unsigned wdata[];
gp = uvm_tlm_generic_payload::type_id::create("gp");
delay = new("delay");
wdata = new[1];
wdata = {<
xcelium command:
xrun -quiet -64bit -licqueue-sv -mess -define DENALI_SV_NC -define DENALI_UVM
-uvmhome CDNS-1.1d
-loadvpi /tools/cadence/vip/11.30.066/tools.lnx86/denali_64bit/verilog/libcdnsv.so:cdnsvVIP:export
-top tb_top
-v2001
+UVM_NO_RELNOTES
-uvm +UVM_VERBOSITY=HIGH
-sysc -sc_main -I. ../src/sc_model.cpp -scautoshell verilog -DSC_INCLUDE_DYNAMIC_PROCESSES
-vtimescale 1ns/1ns ../src/my_pkg.sv -f uvmc-pkg.f ../src/tb_top.sv
-dpi -g