Indexed ports are used to access composite HDL objects in SystemVerilog (SV). Their most frequent use is to access SV multi-dimensional arrays by defining a simple indexed port and accessing the array elements with the port indexes.
Ports in general, and Indexed ports specifically, are static objects that need to be known in the environment build up. Indexed ports were implemented in such a way that each port needs SV stub code, meaning, you have to load your e code and write its stub file using ‘write stub <agent>' command. Otherwise, you would get a checksum error during simulation.
There are several disadvantages of using a stub file:
- In general, generating a new stub file requires HDL recompilation and re-elaboration. This might take a very long time since it actually rebuilds the entire DUT.
- Some additional port attributes are required to describe the indexed port characteristics. Those additional port attributes are for stub purposes only, and cthey reate a need for extra coding
- Non IES (Incisive Enterprise Simulator) users can't use Indexed ports to access Verilog memories; they can only access SV arrays. The reason for this limitation is that the code we generate in the stub file uses constructs that are supported only in SV (DPI-C functions).
After saying this, wouldn't it be nice if we could make indexed ports to work without a stub file?
This is where the new port attribute pli_access() comes to our help. pli_access() allows you to access the HDL object, in this case the SV or Verilog array, with the PLI -- which is the C interface of the simulator -- rather than through stub access.
Let's look at an example which demonstrates how pli_access() can add flexibility to your code:
//top.sv
module top();
reg [9:0] arr [10:0][10:0];
endmodule // top
top module has a 2 dimensional array named arr.
Typically, you would define an indexed port to access this array like that:
<' //top.e
method_type foo(a:int,b:int);
unit u {
p : simple_port (foo) of uint(bits:10) is instance;
keep p.hdl_BLOCKED EXPRESSION == "<p>[<a>][<b>]";
keep p.hdl_path() == "arr";
};
‘>
But, what if instead of 10, my memory size is determined by parameter? Alternately, the size can be bigger than 32 bits but I don't know it up front. With the regular usage, I have to define the type of the port element prior to run time, so it will go into the stub.
However, using the pli_access() attribute will not create an entry in the stubs, and therefore will not need to know the size before the run. We then can use the port type as a list of bit, which will be dynamically allocated according to what we will assign:
<' //top.e
method_type foo(a:int,b:int);
unit u {
p : simple_port (foo) of list of bit is instance;
keep p.pli_access() == TRUE;
//keep p.hdl_BLOCKED EXPRESSION == "<p>[<a>][<b>]"; //not needed
keep p.hdl_path() == "arr";
};
‘>
As you can see above, we applied the following changes:
- We set pli_access() port attribute to TRUE, indicating this port should not affect the stub.
- We don't need to set hdl_BLOCKED EXPRESSION port attribute anymore; this used to define the indexed port access in the stub.
- We changed the type of the port element to be list of bit instead of a fixed type (this would not have worked without pli_access() in place - Specman would have told us it needs a static type with a known size). In general, If you use pli_access(), you also don't need to use declared_size() indexed port attribute
Please note that pli_access() can be used to access only static arrays which are not defined inside classes.
pli_access() also eliminates the need to create a stub file when using the part select on a signal's hdl_path() when using the VCS simulator.
Please refer to documentation for more information about pli_access().
Nir Hadaya and Avi Farjoun