我们在进行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