1、下载 uvmc (uvm connect)
https://download.csdn.net/download/yp18792574062/88529417?spm=1001.2014.3001.5501
2、配置相关环境变量
export UVM_HOME=${VCS_HOME}/etc/uvm
export UVMC_HOME=/home/yangpan/yangpan/uvmc/uvmc-2.3.1
然后执行 source ~/.zshrc 更新
3、编译 uvmc 自带的例子
编译之前需要更新 gcc 的版本,这里将 gcc 版本降低到 5.2.0,因为 vcs2018 只支持少数几个 gcc 版本,降低 gcc 的版本可以参考之前的文章
cd examples/converters
ln -sf Makefile.vcs Makefile
make comp EXAMPLE=sv2sc
./simv
make comp EXAMPLE=sc2sv
./simv
4、自己动手写一个 sc 到 sv 再到 sc 的例子
因为对 system verilog 不熟悉,所以这里 sv 在接收到数据后就立马发送给 sc,实现一个数据的回传效果
Makefile
SYSCAN = syscan -cpp g++ -cc gcc -tlm2 \
-cflags -g \
-cflags -DVCS \
-cflags -std=c++11 \
-cflags -I${VCS_HOME}/etc/systemc/tlm/include/tlm/tlm_utils \
-cflags -I${UVMC_HOME}/src/connect/sc \
-cflags -I${UVMC_HOME}/src \
-cflags -Icpp \
${UVMC_HOME}/src/connect/sc/uvmc.cpp
VLOGAN = vlogan -q -sverilog \
+incdir+${UVM_HOME}/src ${UVM_HOME}/src/uvm_pkg.sv \
+incdir+${UVMC_HOME}/src/connect/sv ${UVMC_HOME}/src/connect/sv/uvmc_pkg.sv \
-timescale=1ns/1ps
VCS_ELAB = vcs -q -sysc=deltasync -lca \
-sysc -cpp g++ -cc gcc \
-timescale=1ns/1ps \
-CFLAGS -DVCS ${UVM_HOME}/src/dpi/uvm_dpi.cc
CURRENT_DIR = $(shell pwd)
CPP_DIR = $(shell find $(CURRENT_DIR)/cpp -maxdepth 20 -type d)
SRCS_CPP += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.cpp))
SRCS_CC += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.cc))
SRCS_C += $(foreach dir, $(CPP_DIR), $(wildcard $(dir)/*.c))
SV_DIR = $(shell find $(CURRENT_DIR)/sv -maxdepth 20 -type d)
SRCS_SV += $(foreach dir, $(SV_DIR), $(wildcard $(dir)/*.sv))
comp:
$(SYSCAN) -full64 $(SRCS_CPP) $(SRCS_CC) $(SRCS_C)
$(VLOGAN) -full64 $(SRCS_SV) +define+UVM_OBJECT_MUST_HAVE_CONSTRUCTOR
$(VCS_ELAB) -full64 sv_main sc_main # 这里要写 verilog、sv、sc 对外的模块名字
clean:
rm -rf simv* work csrc ucli.key vc_hdrs.h vcs.log AN* *.log *.log.cmp *.vpd DVE* .vlogan*
run:
./simv
Systemc 发送模块
// sender.h
#pragma once
#include "systemc.h"
#include <string>
#include <iomanip>
#include "uvmc.h"
using namespace uvmc;
#include <tlm.h>
using namespace sc_core;
using namespace tlm;
#include "simple_initiator_socket.h"
using tlm_utils::simple_initiator_socket;
class producer : public sc_module {
public:
SC_HAS_PROCESS(producer);
producer(sc_module_name ins_name);
virtual ~producer();
void run();
public:
simple_initiator_socket<producer> out;
tlm_analysis_port<tlm_generic_payload> ap;
sc_event done;
};
class Sender : public producer {
public:
SC_HAS_PROCESS(Sender);
Sender(sc_module_name instname);
~Sender();
void SendData();
};
// sender.cpp
#include "sender.h"
#include <string>
producer::producer(sc_module_name ins_name) : sc_module(ins_name), out("out"), ap("ap") {
SC_THREAD(run);
}
producer::~producer() = default;
void producer::run() {
for (int i = 0; i < 10; ++i) {
std::string data = "xxxxx: " + std::to_string(i);
tlm_generic_payload gp;
gp.set_data_ptr((uint8_t*)data.c_str());
gp.set_command(TLM_WRITE_COMMAND);
gp.set_address(rand());
sc_time delay = sc_time(10, SC_NS);
gp.set_data_length(data.length());
out->b_transport(gp, delay);
ap.write(gp);
wait(20, SC_NS);
}
done.notify();
}
Sender::Sender(sc_module_name instname) : producer(instname) {
SC_THREAD(SendData);
}
Sender::~Sender() = default;
void Sender::SendData() {
uvmc_raise_objection("run");
wait(done);
uvmc_drop_objection("run");
}
SystemC 接收模块
// receiver.h
#pragma once
#include <string>
#include <iomanip>
#include <systemc.h>
#include <tlm.h>
using namespace tlm;
#include "simple_target_socket.h"
using tlm_utils::simple_target_socket;
class Receiver : public sc_module {
public:
SC_HAS_PROCESS(Receiver);
Receiver(sc_module_name ins_name);
~Receiver();
virtual void b_transport(tlm_generic_payload &gp, sc_time &t);
public:
simple_target_socket<Receiver> in;
tlm_analysis_port<tlm_generic_payload> ap;
};
// receiver.cpp
#include "receiver.h"
Receiver::Receiver(sc_module_name ins_name) : sc_module(ins_name), in("in"), ap("ap") {
in.register_b_transport(this, &Receiver::b_transport);
}
Receiver::~Receiver() = default;
void Receiver::b_transport(tlm_generic_payload &gp, sc_time &t) {
unsigned char* data = gp.get_data_ptr();
int len = gp.get_data_length();
std::cout << "receive data: " << (char*)data << std::endl;
ap.write(gp);
}
sc_main
// main.cpp
#include <systemc.h>
#include "receiver.h"
#include "sender.h"
int sc_main(int argc, char* argv[]) {
Sender sender("sender");
uvmc_connect(sender.out, "42");
Receiver receiver("receiver");
uvmc_connect(receiver.in, "foo");
sc_start(1000, SC_NS);
return 0;
}
System Verilog 数据回传模块
// sv_loop.sv
import uvm_pkg::*;
import uvmc_pkg::*;
`include "uvm_macros.svh"
class producer extends uvm_component;
uvm_tlm_b_initiator_socket #() out;
uvm_analysis_port #(uvm_tlm_gp) ap;
uvm_phase run_ph;
`uvm_component_utils(producer)
function new(string name, uvm_component parent=null);
super.new(name,parent);
out = new("out", this);
ap = new("ap", this);
run_ph = uvm_run_phase::get();
endfunction
task send(uvm_tlm_gp t, uvm_tlm_time delay);
run_ph.raise_objection(this);
out.b_transport(t, delay);
ap.write(t);
run_ph.drop_objection(this);
/*
uvm_tlm_gp gp = new;
uvm_tlm_time delay = new("del",1e-12);
run_ph.raise_objection(this);
delay.set_abstime(10,1e-9);
assert(gp.randomize() with { gp.m_byte_enable_length == 0;
gp.m_length inside {[1:8]};
gp.m_data.size() == m_length; } );
`uvm_info("PRODUCER/PKT/SEND",{"\n",gp.sprint()},UVM_MEDIUM)
out.b_transport(gp,delay);
ap.write(gp);
#100;
`uvm_info("PRODUCER/END_TEST", "Dropping objection to ending the test",UVM_LOW)
run_ph.drop_objection(this);
*/
endtask
endclass
class consumer extends uvm_component;
uvm_tlm_b_target_socket #(consumer) in;
uvm_analysis_port #(uvm_tlm_generic_payload) ap;
producer prod;
`uvm_component_utils(consumer)
function new(string name, uvm_component parent=null);
super.new(name,parent);
in = new("in", this);
ap = new("ap", this);
prod = new("prod");
uvmc_tlm #()::connect(prod.out, "foo");
endfunction
virtual task b_transport(uvm_tlm_gp t, uvm_tlm_time delay);
// `uvm_info("CONSUMER/PKT/RECV",{"\n",t.sprint()},UVM_MEDIUM)
// #(delay.get_realtime(1ns,1e-9));
// delay.reset();
// ap.write(t);
// uvm_phase phase;
prod.send(t, delay);
endtask
endclass
module sv_main;
consumer cons = new("cons");
// producer prod = new("prod");
initial begin
uvmc_tlm #()::connect(cons.in, "42");
// uvmc_tlm #()::connect(prod.out, "foo");
run_test();
end
endmodule
5、编译运行
make comp
./simv
注意如果编译在 uvmc_connect.cpp 263 行报错,提示错误如下
那么需要将对应行代码修改为
cerr << "UVMC Error: Cannot open connections file '" << filename << "'" << endl;
打印输出
6、关键点
Systemc 和 System Verilog 的通信接口有点类似于 c++ 里面的 socket 通信一样,如果要通信,那么需要绑定到相同的一个端口上,这里的端口是一个字符串
// systemc
Sender sender("sender");
uvmc_connect(sender.out, "42");
Receiver receiver("receiver");
uvmc_connect(receiver.in, "foo");
// system verilog
consumer cons = new("cons");
uvmc_tlm #()::connect(cons.in, "42");
prod = new("prod");
uvmc_tlm #()::connect(prod.out, "foo");
对于 sv 接收,需要重写 b_transport 方法,并且需要定义输入 socket 接口
uvm_tlm_b_target_socket #(consumer) in;
uvm_analysis_port #(uvm_tlm_generic_payload) ap;
virtual task b_transport(uvm_tlm_gp t, uvm_tlm_time delay);
endtask
对于 sv 发送,需要定义输出 socket 接口,并且调用 b_transport 进行发送
uvm_tlm_b_initiator_socket #() out;
uvm_analysis_port #(uvm_tlm_gp) ap;
run_ph.raise_objection(this);
out.b_transport(t, delay);
ap.write(t);
run_ph.drop_objection(this);
对于 sc 的接收端,需要定义输入 socket 接口,然后重写了 b_transport 方法接收数据
simple_target_socket<Receiver> in;
tlm_analysis_port<tlm_generic_payload> ap;
对于 sc 的发送端,需要定义输出 socket 接口,然后调用 b_transport 发送
simple_initiator_socket<producer> out;
tlm_analysis_port<tlm_generic_payload> ap;