====== VPI_within_VVP ====== /* * Copyright (c) 2001 Stephen Williams (steve@icarus.com) * * $Id: vpi.txt,v 1.7 2003/02/09 23:33:26 steve Exp $ */ VPI WITHIN VVP System tasks and functions in Verilog are implemented in Icarus Verilog by C routines written with VPI. This implies that the vvp engine must provide at least a subset of the Verilog VPI interface. The minimalist concepts of vvp, however, make the method less then obvious. Within a Verilog design, there is a more or less fixed web of vpiHandles that is the design database as is available to VPI functions. The Verilog standard defines quite a lot of types, but the vvp only implements the ones it needs. The VPI web is added into the design using special pseudo-ops that create the needed objects. LOADING VPI MODULES The vvp runtime loads VPI modules at runtime before the parser reads in the source files. This gives the modules a chance to register tasks and functions before the source is compiled. This allows the compiler to resolve references to system tasks and system functions to a vpiHandle at compile time. References to missing tasks/function can thus be caught before the simulation is run. NOTE: This also, miraculously, allows for some minimal support of the compiletf call. From the perspective of VPI code, compilation of the VVP source is not unlike compilation of the original Verilog. The handle that the vvp threads have to the VPI are the vpiHandles of the system tasks and functions. The %vpi_call instruction, once compiled, carries the vpiHandle of the system task. SYSTEM TASK CALLS A system task call invokes a VPI routine, and makes available to that routine the arguments to the system task. The called routine gets access to the system task call by calling back the VPI requesting the handle. It uses the handle, in turn, to get hold of the operands for the task. All that vvp needs to know about a system task call is the handle of the system task definitions (created by the vpi_register_systf function) and the arguments of the actual call. The arguments are tricky because the list has no bound, even though each particular call in the Verilog source has a specific set of parameters. Since each call takes a fixed number of parameters, the input source can include in the statement the list of arguments. The argument list will not fit in a single generated instruction, but a vpiHandle that refers to a vpiSysTfCall does. Therefore, the compiler can take the long argument list and form a vpiSysTaskCall object. The generated instruction then only needs to be a %vpi_call with the single parameter that is the vpiHandle for the call. SYSTEM FUNCTION CALLS System function calls are similar to system tasks. The only differences are that all the arguments are input only, and there is a single magic output that is the return value. The same %vpi_call can even be used to call a function. System functions, like system tasks, can only be called from thread code. However, they can appear in expressions, even when that expression is entirely structural. The desired effect is achieved by writing a wrapper thread that calls the function when inputs change, and that writes the output into the containing expression. SYSTEM TASK/FUNCTION ARGUMENTS The arguments to each system task or call are not stored in the instruction op-code, but in the vpiSysTfCall object that the compiler creates and that the %vpi_call instruction ultimately refers to. All the arguments must be some sort of object that can be represented by a vpiHandle at compile time. Arguments are handled at compile time by the parser, which uses the argument_list rule to build a list of vpiHandle objects. Each argument in the argument_list invokes whatever function is appropriate for the token in order to make a vpiHandle object for the argument_list. When all this is done, an array of vpiHandles is passed to code to create a vpiSysTfCall object that has all that is needed to make the call. SCOPES VPI can access scopes as objects of type vpiScope. Scopes have names and can also contain other sub-scopes, all of which the VPI function can access by the vpiInternalScope reference. Therefore, the run-time needs to form a tree of scopes into which other scoped VPI objects are placed. A scope is created with a .scope directive, like so: