summary:本文主要写c case怎么在sv的环境里面跑起来,实现c case里面调用sv的function 和task。
以C case 里面需要对寄存器进行读写为例:
1. main.cpp文件里面
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <chrono>
#include <vector>
extern "C" {
void sv_apb_write(const int addr,const int data);
int sv_apb_read(const int addr, uint32_t* data);
}
void write_reg(uint32_t word_addr, uint32_t data) {
sv_apb_write(word_addr,data);
}
int check_reg(uint32_t word_addr, uint32_t exp, bool verbose=true) {
uint32_t data;
sv_apb_read(word_addr,&data);
//std::cout << "read: " << std::hex << addr << " data: " << data << std::endl;
if(data != exp && verbose) {
std::cout << "READ APB@" << std::hex << word_addr << " Failed, exp:" << exp << ", got:" << data << std::endl;
return 1;
}
return 0;
}
extern "C" {
void vx_main_basic() {
check_reg(0x1,0x80000000); //read 0x1 addr and compare the read data with 0x80000000
write_reg(0x1,0x80000000); //write 0x1 addr and compare the read data with 0x80000000
check_reg(0x1,0x80000000); //read 0x1 addr and compare the read data with 0x80000000
std::cout << "Test PASSED" << std::endl;
}
}
在这里可以看到,C函数里面,调用了check_reg 和write_reg 函数。这些函数在同一个文件里面声明,并且调用了 sv_apb_read和sv_apb_write,这两个函数是在sv侧export的。
2. 在sv 侧写一个dpi_function.sv 文件
export "DPI" task sv_apb_read;
export "DPI" task sv_apb_write;
task sv_apb_read(input int addr, output int data);
skybox_base_sequence::my_base_seq.read_reg32(addr<<2,data);
endtask
task sv_apb_write(input int addr,input int data);
base_sequence::my_base_seq.write_reg32(addr<<2,data);
endtask
在这个dpi_function.sv文件里面,export了 sv_apb_read和sv_apb_write 这两个函数到C侧,使得C case能够拿到sv这边执行的函数。
3. 在base_sequence 里面,做如下声明以及写相应的函数
`ifndef _BASE_SEQUENCE_SV
`define _BASE_SEQUENCE_SV
class base_sequence extends uvm_seq;
`uvm_object_param_utils(base_sequence)
`uvm_declare_p_sequencer(vsequencer)
static base_sequence my_base_seq;
function new(string name = "base_sequence");
super.new(name);
my_base_seq = this;
endfunction: new
extern virtual task write_reg32(bit[63:0] addr, bit[31:0] data);
extern virtual task read_reg32(bit[63:0] addr, output bit[31:0] data);
endclass
task base_sequence::write_reg32(bit[63:0] addr, bit[31:0] data);
apb_master_cfg_sequence cfg_seq;
cfg_seq = apb_master_cfg_sequence::type_id::create("cfg_seq");
cfg_seq.isWrite = 1;
cfg_seq.addrRW = addr;
cfg_seq.wrData = data;
cfg_seq.start(p_sequencer.amba_apb_seqr,this);
`uvm_info("WRITE REG",$sformatf("the addr=%0h,the data=%0h",addr,data),UVM_LOW)
endtask: write_reg32
task base_sequence::read_reg32(bit[63:0] addr, output bit[31:0] data);
apb_master_cfg_sequence cfg_seq;
cfg_seq = apb_master_cfg_sequence::type_id::create("cfg_seq");
cfg_seq.isWrite = 0;
cfg_seq.addrRW = addr;
cfg_seq.start(p_sequencer.amba_apb_seqr,this);
data = cfg_seq.rdData;
`uvm_info("READ REG",$sformatf("the addr=%0h,the data=%0h",addr,data),UVM_LOW)
endtask: read_reg32
在base_sequence 里面写好write_reg32 和read_reg32, 这里需要注意声明一个static 的my_base_seq,这样才能被dpi_function.sv文件引用。
4. 在sv侧调用vx_main_basic函数,将整个C case执行起来。
`ifndef __C_CASE_SEQ_SVH__
`define __C_CASE_SEQ_SVH__
import "DPI" context task vx_main_basic();
`include "dpi_func.sv"
class C_case_seq extends base_sequence;
`uvm_object_utils_begin(C_case_seq)
`uvm_object_utils_end
extern virtual task body();
endclass
task C_case_seq::body();
if($test$plusargs("BASIC")) begin
vx_main_basic();
endtask
`endif
5. 通过在编译选项里面加入 -CFLAGS 让它识别 .h 的头文件,需要编译的.cpp 文件直接告诉编译器文件的地址,.cpp文件就能被成功编译
compile_option =-CFLAGS '-I/header -std=c++17'
compile_option = $dir/main.cpp
6. case 跑完的波形:正好完成了read >> write >> read的操作。