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 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 objects 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 of a given object type in a design. acc_next_ Value Change Link (VCL) routines : They allow the user system task to add and delete objects from the list of objects that are monitored . acc_vcl_ Fetch routines : They can extract a variety of information about objects (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)); output_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 0 none Stop simulation. Display simulation time and message. 1 none Finish simulation. Display simulation time and message. 0 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()); }
Recommend
More recommend