Programming Language Interface Sayed Amirhossein Mirhosseini Uses - - PowerPoint PPT Presentation
Programming Language Interface Sayed Amirhossein Mirhosseini Uses - - PowerPoint PPT Presentation
Programming Language Interface Sayed Amirhossein Mirhosseini Uses of PLI PLI can be used to define additional system tasks and functions e.g. monitoring tasks , stimulus tasks , PLI can be used to extract design information such
Uses of PLI
PLI can be used to define additional system
tasks and functions
e.g. monitoring tasks , stimulus tasks , …
PLI can be used to extract design information
such as hierarchy, connectivity, fanout, and number of logic elements of a certain type.
PLI can be used to write special-purpose or
customized output display routines.
General Verilog-based application software can
be written with PLI routines.
This software will work with all Verilog simulators
because of the uniform access provided by the PLI interface.
A simple PLI Task
#include "veriuser.h" /*include the file provided in release dir */ int hello_verilog() { io_printf("Hello Verilog World\n"); }
The io_printf is a PLI library routine that
works exactly like printf.
Linking PLI Tasks
Whenever the task $hello_verilog is invoked in
the Verilog code, the C routine hello_verilog must be executed.
The simulator needs to be aware that a new
system task called $hello_verilog exists and is linked to the C routine hello_verilog
This process is called linking the PLI routines into the
Verilog simulator.
Different simulators provide different mechanisms to
link PLI routines.
Invoking PLI Tasks
Once the user-defined task has been linked
into the Verilog simulator, it can be invoked like any Verilog system task by the keyword $hello_verilog: module hello_top; initial $hello_verilog; //Invoke the user-defined task hello_verilog endmodule
General Flow
Access Routines
Access routines can read information about
- bjects in the design.
Objects can be one of the following types:
Module instances, module ports, module pin-to-pin paths,
and intermodule paths
Top-level modules Primitive instances, primitive terminals Nets, registers, parameters, specparams Integer, time, and real variables Timing checks Named events
Mechanics of access routines
Access routines always start with the prefix
acc_.
A user-defined C routine that uses access
routines must first initialize the environment by calling the routine acc_initialize().
When exiting, the user-defined C routine must
call acc_close().
#include "acc_user.h" Access routines use the concept of a handle to
access an object.
Handles are predefined data types that point to
specific objects in the design.
handle top_handle;
Types of access routines
Handle routines:
They return handles to objects in the design.
acc_handle_.
Next routines :
They return the handle to the next object in the set
- f a given object type in a design.
acc_next_
Value Change Link (VCL) routines :
They allow the user system task to add and delete
- bjects from the list of objects that are monitored .
acc_vcl_
Fetch routines :
They can extract a variety of information about
- bjects (e.g. hierarchical path name, relative name)
acc_fetch_.
Example 1: Get Module Port List
#include "acc_user.h" int get_ports() { handle mod, port; int input_ctr = 0; int output_ctr = 0; int inout_ctr = 0; acc_initialize(); mod = acc_handle_tfarg(1); /* get a handle to the module instance first argument in the system task argument list */ port = acc_handle_port(mod, 0); /* get the first port of the module */ while( port != null ) /* loop for all ports */ { if (acc_fetch_direction(port) == accInput) /* Input port */ { io_printf("Input Port %s \n", acc_fetch_fullname(port)); /* full hierarchical name */ input_ctr++; }
Example 1 (cont.)
else if (acc_fetch_direction(port) == accOutput) /* Output port */ { io_printf("Output Port %s \n", acc_fetch_fullname(port));
- utput_ctr++;
} else if (acc_fetch_direction(port) == accInout) /* Inout port */ { io_printf("Inout Port %s \n", acc_fetch_fullname(port)); inout_ctr++; } port = acc_next_port(mod, port); /* go to the next port */ } io_printf("Input Ports = %d Output Ports = %d, Inout ports = %d\n\n", input_ctr, output_ctr, inout_ctr); acc_close(); }
Example 1 (cont.)
module top; wire OUT; reg I0, I1, S; mux2_to_1 my_mux(OUT, I0, I1, S); /*Instantiatethe 2-to-1 mux*/ initial begin $get_ports("top.my_mux"); /*invoke task $get_ports to get port list*/ end endmodule
Output :
Output Port top.my_mux.out Input Port top.my_mux.i0 Input Port top.my_mux.i1 Input Port top.my_mux.s Input Ports = 3 Output Ports = 1, Inout ports = 0
Example 2: Monitor Nets
#include "acc_user.h" int my_monitor() { handle net; char *netname ; /*pointer to store names of nets*/ char *malloc(); acc_initialize(); /*initialize environment*/ net = acc_handle_tfarg(1); /*get a handle to the net to be monitored*/ netname = malloc(strlen(acc_fetch_fullname(net))); strcpy(netname, acc_fetch_fullname(net)); /* Call the VCL routine to add a signal to the monitoring list*/ /* Pass four arguments to acc_vcl_add task*/ /* 1st : handle to the monitored object (net) 2nd : Consumer C routine to call when the object value changes (display_net) 3rd : String to be passed to consumer C routine (netname) 4th : Predefined VCL flags: vcl_verilog_logic or vcl_verilog_strength */ acc_vcl_add(net, display_net, netname, vcl_verilog_logic); acc_close(); }
Example 2 (cont.)
Whenever the value of the net changes, the
acc_vcl_add calls the consumer routine display_net and passes a pointer to a data structure of the type p_vc_record
typedef struct t_vc_record{ int vc_reason; /*reason for value change*/ int vc_hightime; /*Higher 32 bits of 64-bit simulation time*/ int vc_lowtime; /*Lower 32 bits of 64-bit simulation time*/ char *user_data; /*String passed as the argument of acc_vcl_add*/ union { /*New value of the monitored signal*/ unsigned char logic_value; double real_value; handle vector_handle; s_strengths strengths_s; } out_value; } *p_vc_record;
Example 2 (cont.)
display_net(p_vc_record vc_record) { io_printf("%d New value of net %s is %c \n", vc_record->vc_lowtime, vc_record->user_data, convert_to_char(vc_record->out_value.logic_value)); }
char convert_to_char(char logic_val) { char temp; switch(logic_val) { case vcl0: temp='0'; break; case vcl1: temp='1'; break; case vclX: temp='X'; break; case vclZ: temp='Z'; break; } return(temp); }
Example 2 (cont.)
module top; wire OUT; reg I0, I1, S; mux2_to_1 my_mux(OUT, I0, I1, S); //Instantiate the module mux2_to_1 initial //Add nets to the monitoring list begin $my_monitor("top.my_mux.sbar"); $my_monitor("top.my_mux.y1"); end initial //Apply Stimulus begin I0=1'b0; I1=1'b1; S = 1'b0; #5 I0=1'b1; I1=1'b1; S = 1'b1; #5 I0=1'b0; I1=1'b1; S = 1'bx; #5 I0=1'b1; I1=1'b1; S = 1'b1; end endmodule
Example 2 (cont.)
Output :
0 New value of net top.my_mux.y1 is 0 0 New value of net top.my_mux.sbar is 1 5 New value of net top.my_mux.y1 is 1 5 New value of net top.my_mux.sbar is 0 5 New value of net top.my_mux.y1 is 0 10 New value of net top.my_mux.sbar is X 15 New value of net top.my_mux.y1 is X 15 New value of net top.my_mux.sbar is 0 15 New value of net top.my_mux.y1 is 0
Utility Routines
Utility routines are miscellaneous PLI routines
that pass data in both directions across the Verilog/user C routine boundary.
Do long arithmetic Display messages Halt, terminate, save, and restore simulation …
Utility routines are also popularly called "tf"
routines.
Always start with the prefix tf_. #include "veriuser.h"
Example 3: my_stop_finish
Verilog provides the system tasks $stop and
$finish that suspend and terminate the simulation.
my_stop_finish does both based on its
arguments :
1st Argument 2nd Argument Action none Stop simulation. Display simulation time and message. 1 none Finish simulation. Display simulation time and message. any value Stop simulation. Display simulation time, module instance from which stop was called, and message. 1 any value Finish simulation. Display simulation time, module instance from which stop was called, and message.
Example 3 (cont.)
#include "veriuser.h" int my_stop_finish() { if(tf_nump() == 1) /* if 1 argument is passed , display only simulation time */ { if(tf_getp(1) == 0) /* if the argument is 0, then stop the simulation*/ { io_printf("Mymessage: Simulation stopped at time %d\n", tf_gettime()); tf_dostop(); /*stop the simulation*/ } else if(tf_getp(1) == 1) /* if the argument is 0 , terminate the simulation*/ { io_printf("Mymessage: Simulation finished at time %d\n“, tf_gettime()); tf_dofinish(); /*terminate the simulation*/ } else /* Pass warning message */ tf_warning("Bad arguments to \$my_stop_finish at time%d\n“,tf_gettime()); }
Example 3 (cont.)
else if(tf_nump() == 2) /* if 1 argument is passed, print module instance and time*/ { if(tf_getp(1) == 0) /* if the argument is 0 then stop the simulation*/ { io_printf ("Mymessage: Simulation stopped at time %d in instance %s \n", tf_gettime(), tf_mipname()); tf_dostop(); /*stop the simulation*/ } else if(tf_getp(1) == 1) /* if the argument is 0 then terminate the simulation*/ { io_printf ("Mymessage: Simulation finished at time %d in instance %s \n", tf_gettime(), tf_mipname()); tf_dofinish(); /*terminate the simulation*/ } else /* Pass warning message */ tf_warning("Bad arguments to \$my_stop_finish at time %d\n",tf_gettime()); } }
Example 3 (cont.)
module top; wire OUT; reg I0, I1, S; mux2_to_1 my_mux(OUT, I0, I1, S); //Instantiate the module mux2_to_1 initial //Apply Stimulus begin I0=1'b0; I1=1'b1; S = 1'b0; $my_stop_finish(0); //Stop simulation. Don't print module instance name #5 I0=1'b1; I1=1'b1; S = 1'b1; $my_stop_finish(0,1); //Stop simulation. Print module instance name #5 I0=1'b0; I1=1'b1; S = 1'bx; $my_stop_finish(2,1); //Pass bad argument 2 to the task #5 I0=1'b1; I1=1'b1; S = 1'b1; $my_stop_finish(1,1); //Terminate simulation. Print module instance //nameend endmodule
Example 3 (cont.)
Output :