c case 与sv 之间的交互

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的操作。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在SV语言中,case语句用于根据不同的条件执行不同的代码块。它类似于其他编程语言中的switch语句。case语句可以使用wildcard来匹配多个条件。通常使用通配符"*"来表示任意值。当case语句的条件与通配符匹配时,相应的代码块将被执行。这在编写测试用例时非常有用,可以覆盖多种情况。使用wildcard可以简化代码并提高可读性。例如,下面是一个使用wildcard的case语句的示例: ```systemverilog case (signal) 4'b10?? // 当signal的值为10开头的任意两位数时执行此代码块 4'b01?? // 当signal的值为01开头的任意两位数时执行此代码块 default // 当signal的值与上述条件都不匹配时执行此代码块 endcase ``` 在上述示例中,当signal的值为10开头的任意两位数时,第一个代码块将被执行;当signal的值为01开头的任意两位数时,第二个代码块将被执行;当signal的值与上述条件都不匹配时,default代码块将被执行。使用wildcard可以方便地处理多种情况,提高代码的灵活性和可维护性。\[2\] #### 引用[.reference_title] - *1* *2* *3* [【数字设计验证】System Verilog(sv)稍微进阶的笔记(一)](https://blog.csdn.net/qq_43658753/article/details/128505261)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值