您的位置:首页 > 其它

深入浅出FPGA-18-VPI

2014-03-24 21:07 302 查看

引言

我们在进行RTL仿真时,有时候会遇到HDL工程和C语言工程需要进行数据通信时,使用$readmem()等系统任务会方便很多,但是有时候,实现较复杂功能时,$readmem()就会稍显不足。这时,就需要我们编写特殊的系统任务,来实现。
HDL语言提供的PLI,VPI正是为了解决这个问题而设计的,本小节,我们就熟悉一下VPI。

1,VPI简介

Verilog过程接口(Verilog Procedural Interface, VPI),最初被称为编程语言接口(Program Language Interface, PLI) 2.0,是一个针对C语言的Verilog过程接口。它可以使数字电路的行为级描述代码直接调用C语言的函数,而用到的C语言函数也可以调用标准的Verilog系统任务。Verilog程序结构是IEEE 1364编程语言接口标准的一部分。它最新的版本是2005年更新的。

更多信息请参考: http://en.wikipedia.org/wiki/Verilog_Procedural_Interface
http://www.asic-world.com/verilog/pli6.html#Verilog_Procedural_Interface_(VPI)

2,简单示例

在了解了VPI的含义以及工作原理之后,我们通过下面一个简单的例子来说明VPI的具体使用方法。
针对不同的仿真工具(VCS,modelsim,NC sim),使用VPI有不同的方式。
本实验以modelsim为例。

a,编写C语言实现

hello.c

/*
* vpi simple test
* rill,2014-03-21
*/

#include "vpi_user.h"

static PLI_INT32 hello(PLI_BYTE8 * param) {
  vpi_printf("Hello Rill!\n");
 return 0;
}

// Associate C Function with a New System Task
void registerHelloSystfs(void) {
  s_vpi_systf_data task_data_s;
  vpiHandle systf_handle;
  task_data_s.type = vpiSysTask;
  task_data_s.sysfunctype = vpiSysTask;
  task_data_s.tfname = "$hello";
  task_data_s.calltf = hello;
  task_data_s.compiletf = 0;
  task_data_s.sizetf = 0;
  task_data_s.user_data = 0;

  systf_handle = vpi_register_systf(&task_data_s);
  vpi_free_object(systf_handle);
}

// Register the new system task here
void (*vlog_startup_routines[]) () = {
   registerHelloSystfs,
   0  // last entry must be 0 
};


b,编写verilog HDL文件

hello.v:

module hello;
initial $hello;
endmodule

c,测试

为了测试方便,我编写了一个简单的shell脚本,如下所示:
hello.sh:
#!/bin/bash
# Rill creat 140321

gcc -c -I/home/openrisc/modelsim/modeltech/include hello.c
ld -G -o hello.sl hello.o
vlib work
vlog hello.v
vsim -c -pli hello.sl hello
#run -all
#quit


d,测试结果

下面是在linux下的测试结果:



3,实际应用实例

通过上面的例子,我们看到了verilog和C语言的交互过程。
其实,VPI的本质就是为了方便C语言和verilog之间交换数据。下面是一个实际工程中部分关键代码,请参考:
vpi_demo.c:

// VPI includes
#include <vpi_user.h>

uint32_t vpi_pipe[2]; // [0] - read, [1] - write

void check_for_command();
void get_command_data();
void return_command_data();

//=========================================
void init_pipe(void)
{
	if(pipe(vpi_pipe) == -1)
    {
      perror("pipe error\n");
      exit(1);
    }
}

//=========================================
void check_for_command(char *userdata){

  vpiHandle systfref, args_iter, argh;

  struct t_vpi_value argval;

  int value,i;

  int n;

  unsigned char data;

  //if(DBG_JP_VPI) printf("check_for_command\n");
  
  //n = read(rsp_to_vpi_pipe[0], &data, 1);
  
  n = read(vpi_pipe[0], &data, 1);

  if ( ((n < 0) && (errno == EAGAIN)) || (n==0) )
    {
      // Nothing in the fifo this time, let's return
      return;
    }
  else if (n < 0)
    {
      // some sort of error
      perror("check_for_command");

      exit(1);
    }
  
  if (DBG_JP_VPI)
  {
    printf("jp_vpi: c = %x:",data);
    print_command_string(data);
    fflush(stdout);
  }
  
  // Return the command to the sim
  
  // Obtain a handle to the argument list
  systfref = vpi_handle(vpiSysTfCall, NULL);
  
  // Now call iterate with the vpiArgument parameter
  args_iter = vpi_iterate(vpiArgument, systfref); 

  // get a handle on the variable passed to the function
  argh = vpi_scan(args_iter);

  // now store the command value back in the sim
  argval.format = vpiIntVal;
  
  // Now set the command value
  vpi_get_value(argh, &argval);

  argval.value.integer = (uint32_t) data;

  // And vpi_put_value() it back into the sim
  vpi_put_value(argh, &argval, NULL, vpiNoDelay);
  
  // Cleanup and return
  vpi_free_object(args_iter);

  n = write(vpi_to_rsp_pipe[1],&data,1);
  if (DBG_JP_VPI) printf("jp_vpi: r");

  if (DBG_JP_VPI) printf("\n");
   
  return;
}

void get_command_data(char *userdata){

  vpiHandle systfref, args_iter, argh;

  struct t_vpi_value argval;

  int value,i;

  int n = 0;

  uint32_t data;
  
  char* recv_buf;

  recv_buf = (char *) &data; // cast data as our receive char buffer

 read_command_data_again:  
  n = read(vpi_pipe[0],recv_buf,4);
  
  if ((n < 4) && errno==EAGAIN)
    goto read_command_data_again;
  else if (n < 4)
    {
      printf("jp_vpi: get_command_data errno: %d\n",errno);
      perror("jp_vpi: get_command_data read failed");
    }
  if (DBG_JP_VPI) printf("jp_vpi: get_command_data = 0x%.8x\n",data);

  // Obtain a handle to the argument list
  systfref = vpi_handle(vpiSysTfCall, NULL);
  
  // Now call iterate with the vpiArgument parameter
  args_iter = vpi_iterate(vpiArgument, systfref); 

  // get a handle on the variable passed to the function
  argh = vpi_scan(args_iter);

  // now store the command value back in the sim
  argval.format = vpiIntVal;
  
  // Now set the data value
  vpi_get_value(argh, &argval);
  argval.value.integer = (uint32_t) data;
  
  // And vpi_put_value() it back into the sim
  vpi_put_value(argh, &argval, NULL, vpiNoDelay);
  
  // Cleanup and return
  vpi_free_object(args_iter);
   
   return;

}

void return_command_data(char *userdata){

  vpiHandle systfref, args_iter, argh;

  struct t_vpi_value argval;

  int value,i;

  int n, length;

  uint32_t data;
  
  char* send_buf;

  // Obtain a handle to the argument list
  systfref = vpi_handle(vpiSysTfCall, NULL);
  
  // Now call iterate with the vpiArgument parameter
  args_iter = vpi_iterate(vpiArgument, systfref); 

  // get a handle on the length variable
  argh = vpi_scan(args_iter);
  
  argval.format = vpiIntVal;
  
  // get the value for the length object
  vpi_get_value(argh, &argval);

  // now set length
  length = argval.value.integer;

  // get a handle on the object passed to the function
  argh = vpi_scan(args_iter);

  // now store the command value back in the sim
  argval.format = vpiIntVal;
  
  // Now set the data value
  vpi_get_value(argh, &argval);

  data = (uint32_t) argval.value.integer;
  
  // Cleanup and return
  vpi_free_object(args_iter);

  if (DBG_JP_VPI) printf("jp_vpi: return_command_data %d bytes, 0x%.8x\n",length,data);
  
  send_buf = (char *) &data; //cast our long as a char buf
  
  // write the data back
  n = write(vpi_pipe[1],send_buf,length);

  return;

}

//=========================================

void register_check_for_command() {
  s_vpi_systf_data data = {vpiSysTask, 
			   0, 
			   "$check_for_command", 
			   (void *)check_for_command, 
			   0, 
			   0, 
			   0};

  vpi_register_systf(&data);

  return;
}

void register_get_command_data() {
  s_vpi_systf_data data = {vpiSysTask, 
			   0, 
			   "$get_command_data", 
			   (void *)get_command_data, 
			   0, 
			   0, 
			   0};

  vpi_register_systf(&data);

  return;
}

void register_return_command_data() {
  s_vpi_systf_data data = {vpiSysTask, 
			   0, 
			   "$return_command_data", 
			   (void *)return_command_data, 
			   0, 
			   0, 
			   0};

  vpi_register_systf(&data);

  return;
}

//=========================================
void (*vlog_startup_routines[]) () = {
  register_check_for_command,
  register_get_command_data,
  register_return_command_data,
  0  // last entry must be 0 
};


vpi_demo.v:

integer cmd;

reg [31:0] cmd_data;
reg [31:0] exec_data;

task exec_cmd;
input [31:0]  cmd_data;
begin
	exec_data <= cmd_data + 1;//verilog process code
end
	  

task main;
	begin
		while (1)
		begin
			cmd = -1;
	
			while (cmd == -1)
			begin
				#1000 $check_for_command(cmd);
				
				case (cmd)
				`TEST_CMD0:
				begin
					$get_command_data(cmd_data);
					exec_cmd(cmd_data);
					$return_command_data(4,exec_data);
				end
			end
		end
	end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: