我们在进行RTL仿真时,有时候会遇到HDL工程和C语言工程需要进行数据通信时,使用$readmem()等系统任务会方便很多,但是有时候,实现较复杂功能时,$readmem()就会稍显不足。这时,就需要我们编写特殊的系统任务,来实现。
HDL语言提供的PLI,VPI正是为了解决这个问题而设计的,本小节,我们就熟悉一下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)
在了解了VPI的含义以及工作原理之后,我们通过下面一个简单的例子来说明VPI的具体使用方法。
针对不同的仿真工具(VCS,modelsim,NC sim),使用VPI有不同的方式。
本实验以modelsim为例。
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
};
hello.v:
module hello; initial $hello; endmodule
为了测试方便,我编写了一个简单的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
下面是在linux下的测试结果:
通过上面的例子,我们看到了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
深入浅出FPGA-18-VPI,布布扣,bubuko.com
原文:http://blog.csdn.net/rill_zhen/article/details/21986363