Dynamic generation of signal spies in testbench - system-verilog

I have a .txt file that contains certain signals that I want to monitor in my testbench during the application of some stimulus.
I am creating an initial block in which I am reading the file and then I try to generate a init_signal_spy() for every one of the lines that I have read.
The code that I have written up until this point has the following format:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
//static logic net_to_be_probed;
automatic sig_and_spy entry = new();
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array
probes = new [probes.size() + 1] (probes);
// Link the extracted new line with the probe list
// - this raises an error "An invalid empty string was passed in as the Destination object."
// - expected since the last element is empty...
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
In the code above, just before I issue the generation for the spy, I get why the error
An invalid empty string was passed in as the Destination object.
is generated by the compiler. Although the array has been resized, it does not hold any element i.e., its empty. Thus, I tried creating locally a logic variable that then I assign to the signal spy within the loop in the following manner:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
logic new_probe;
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array and copy old values.
probes = new [probes.size() + 1] (probes);
// Add the new probe to the Testbenchs' probes array
probes[probes.size()-1] = new_probe;
// Again, An invalid empty string was passed in as the Destination object.
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
But then again, I see the same error at runtime during the simulation. So...Is there a way of achieving such a "dynamic" signal monitoring in the testbench somehow? As far as I understood the error concerns that the destination object is NOT a signal of the testbench. Thus the logic new_probe has no effect. Which is to be expected I mean, but is there a way of achieving the desired behavior in the Testbench via sysverilog?

You have at least two problems.
Both the source and destination arguments to init_signal_spy() need to be strings. Your destination argument is an integral variable with a 0 value, and that gets interpreted as a null string. init_signal_spy() was designed for mixed language simulation, and using strings was the only way to achieve that.
Your destination variable should be queue, not a dynamic array. Every time you re-size a dynamic array, the previous elements get relocated and that breaks the previous connection made by signal spy.
This example shows the proper syntax for string this up
module top;
int A[$];
int s1,s2;
initial begin
A.push_back(0);
$init_signal_spy("s1","A[0]");
A.push_back(0);
$init_signal_spy("s2","A[1]");
#1 s1 = 1;
#1 s2 = 2;
#1 $display("%p",A);
end
endmodule
A far better solution for performance is converting your .txt file into actual SystemVerilog code that can be compiled into your testbench.

Related

Retain interactivity in Simulink sheet called from an object method

Let's say I have a simulation set up in Simulink, which consists of three blocks: Input (From Workspace block), model calculation (S-function block) and results (Outport).
In my class, I do a lot of preprocessing on the input data, which is not shown here. Then, I point the Input block to use the inputArray, which is a property of my class.
classdef SimulationClass < handle
%SimulationClass Simulate process using model
properties
inputArray double = ones(101, 11)
modelFilename char
simOpts struct
simulationTime double
simulationResultsArray double
end
methods
function obj = SimulationClass(simulationTime)
obj.simulationTime = simulationTime;
end
function prepareSimulation(obj, modelFilename)
obj.modelFilename = modelFilename;
open_system(obj.modelFilename);
set_param([obj.modelFilename '/fromWorkspaceBlock'],...
'VariableName', 'obj.inputArray');
obj.simOpts = simset('SrcWorkspace', 'current');
end
function runSimulation(obj)
[~, ~, obj.simulationResultsArray] = sim(obj.modelFilename,...
obj.simulationTime, obj.simOpts);
end
end
end
I can easily start the simulation run from the command prompt.
mySim = SimulationClass(1:10);
mySim.prepareSimulation('mySimulinkSheet');
mySim.runSimulation;
But, if I play around with some settings (e.g., different solvers) and press Run in Simulink, it doesn't work, since the data is not in the base workspace. I.e., I lose the interactivity with Simulink.
Any ideas on how to resolve this issue? The only workaround I have figured out so far would be to write an additional method (like writePropertiesToBaseWorkspace) which would convert the classes properties into variables, using assignin().

My mex function ignores my if statement

I have a mex function that takes in a field of a struct in the third input (i.e. prhs[2]), which is a boolean. If true, it will parse information from the fourth input (i.e. prhs[3]). In a nutshell, this is the code excerpt:
mxValue = mxGetField(prhs[3], 0, "change"); mxLogical *change;
change = mxGetLogicals(mxValue);
mexPrintf("true/false: %i \n", *change);
mexEvalString("drawnow;");
if ( change ) {
mexPrintf("...Parsing info... \n");
mexEvalString("drawnow;");
mxValue = mxGetField(prhs[3], 0, "info");
nRows = mxGetM(mxValue); nCols = mxGetN(mxValue);
Eigen::Map<Eigen::VectorXd> info((double *)mxGetPr(mxValue),nRows);
}
As you can see, I do a printout to see whether the input prhs[2] is true or false. Even if the function prints out false, the if statement gets executed regardless, because I can see the printout ...Parsing info....
Why is my MATLAB mex function ignoring my if statement?
C is not MATLAB! C is C!
You are checking if pointer change has a value. It does indeed have a value, a memory direction e.g. #72BA21, to the location where the value of the boolean is stored.
You can either check the contents of whats inside that specific direction if(*change) as #buzjwa suggest, or grab the information on the array, instead of a pointer to it, using mxGetData.
As a side note: learn to debug, or at least, print statements. a simple mexPrintf() call would have shown you what change contains

how to generate array of interfaces having different connections?

I have declared following interface:
interface data_x #(parameter g_DataWidth = 8)
(input ckrs_t ClkRs_ix);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
always_comb begin
ClkRs = ClkRs_ix;
end
endinterface
The interface has data bus and the data enable, and it is as well associated with the clock and reset signal, which is a typedef ckrs_t.
I have a module, which accepts as an argument array of those interfaces:
module fourmclinks
(...
data_x packet_ox[NUMBER_OF_GBT_LINKS-1:0],
data_x packet_ix[NUMBER_OF_GBT_LINKS-1:0],
...
);
The problem I have is, that I need to declare in top-level entity an array of those data_x interfaces, but each time use different ClkRs_ix input clock. (It is used in the gbts, where each receiver has its own clock and reset signal).
I tried many things, including this one:
ckrs_t txclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
data_x #(.g_DataWidth(g_FrameSize)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++) begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs_ix.clk =
GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs_ix.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
Hence making empty/virtual/unassigned/... interface array declaration, and then in generate loop assign correct signals to it. This simulates, but quartus does not compile it claiming
value cannot be assigned to input "ClkRs_ix".
How to correctly generate array of interfaces, each having different input connection? Please help
I'm bit smarter now, so here is the solution to the problem. But first issues:
it is not possible just to remove 'input' direction from the port declaration in the data_x interface declaration above. If this is done, one has to then manually assign clock and reset lines for every instance of the data_x object. This is indeed possible, but one loses all the beauty of having the clock and reset automatically assigned during the instantiation of the interface
it is not possible either in this particular case to make a virtual interface, and connect the signals in the for loop. Root cause of this is the presence of always_comb, which takes in the input reset/clock and assigns it to the internal signals. So this assignment, together with manual assignment of reset and clock in the top-level entity results in driving those signals from two sources, which Quartus will not digest
So the only possible way, which I found is following:
Declare the data_x interface to generate the always_comb on demand:
interface data_x #(
parameter g_hasClock = 1,
parameter g_DataWidth = 8)
(
input ckrs_t ClkRs_ix
);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
generate
if(g_hasClock) begin
always_comb begin
ClkRs = ClkRs_ix;
end
end
endgenerate
endinterface // data_x
Instantiate the interface with unbound ClkRs_ix. Note the usage of g_hasClock, which instantiates the data_x interface without always_comb block, hence Quartus stops complaining about multiple drivers:
data_x #(.g_DataWidth(g_FrameSize),
.g_hasClock(0)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
And then generate interface with different clocks:
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++)
begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs.clk = GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
This works. It is not so nice because we have to do it manually. Just for sake of completeness: if the clocks for all interfaces is the same, all that code above boils down to this snippet:
ckrs_t txclock_x, rxclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
I'm sure this is not the best solution ever, but it is compilable and gives the result needed.

wait($time >1000); cannot work in system-verilog?

I use this code to wait for a specific simulation time
initial begin
$display("A");
wait($time>1000);
$display("B");
end
the simulation result is:
A
I didnot see B printed.
If I use following code, it works.
while($time <1000) #1;
Is it because vcs needs to judge the wait condition once any viriable in the condition statement changes, $time is changing too frequently so vcs doesnot allow this usage?
#Tudor 's answer enlighten me. I tried #Tudor 's code with some modification. It turns out when wait(func(arglist)); vcs only retry to evaluate the function when arglist changes. Because $time has no args, vcs will only evaluate $time the 1st time, won't retry.
module top;
int the_time = 0;
int in_arg = 0;
function int the_time_f(int in);
return the_time;
endfunction // the_time_f
initial begin
$display("A");
// This works because 'the_time' is a variable
//wait(the_time > 10);
// This doesn't work because 'the_time_f' is a function
wait(the_time_f(in_arg) >10);
$display("B at %t", $time);
end
initial begin
#10ns;
the_time = 11;
#10ns;
in_arg = 1;
#20ns;
$finish();
end
endmodule // top
got following result
A
B at 20ns
This seems to be a gray area in the standard. In section 9.4 Procedural timing controls of the IEEE Std 1800-2012 Standard, it's mentioned that event control can be either implicit (changed of nets or variables) or explicit (fields of type event). $time is a system function, not a variable. I've also tried using a function for the wait and it also doesn't work:
module top;
int the_time = 0;
function int the_time_f();
return the_time;
endfunction // the_time_f
initial begin
$display("A");
// This works because 'the_time' is a variable
//wait(the_time > 10);
// This doesn't work because 'the_time_f' is a function
wait(the_time_f() > 10);
$display("B");
end
initial begin
#10ns;
the_time = 11;
#20ns;
$finish();
end
endmodule // top
Waiting on a change of a variable works fine, but waiting for a change on a function's return value doesn't work. IMHO, the compiler should have flagged this as a compile error (same for using $time) since it seems to just ignore the statement.
In an event control #(expression) or wait(expression) that suspends a process, SystemVerilog scheduling semantics requires an event to evaluate the expression (called an evaluation event. See section 4.3 Event Simulation of the 1800-2012 LRM) If an expression includes a function, only the arguments to that function are visible to cause an event evaluation (There is an exception for class methods in at a write to any member of the object in the method call will cause an event) See section 9.4.2 Event control
In an event driven simulation, the value of time is just an attribute of the current time slot, it is never an event. The simulator processes all events for the current time slot in a queue, and when that queue is empty, it advances time to the next time slot queue. So it might simulate time slots 0,5,7,10, skipping over the unmentioned times. Using your while loop, that would create a time sot for every consecutive time unit between 0 and 1000 - extremely inefficient.
So just use
#(1000); // wait for 1000 relative time units

Preventing Function Overriding in Lua Table

In my program when two functions with the same name are defined for the same table, I want my program to give an error. What's happening is that it's simply just calling the last function and executing it.
Here's a sample code
Class{'Cat'}
function Cat:meow( )
print("Meow!")
end
function Cat:meow()
print("Mmm")
end
kitty = Cat:create()
kitty:meow()
The result of the execution is only: "Mmm"
Instead I want something like an error message to be given.
Unfortunately, __newindex does not intercept assignments to fields which already exist. So the only way to do this is to keep Cat empty and store all its contents in a proxy table.
I don't know the nature of your OOP library, so you'll have to incorporate this example on your own:
local Cat_mt = {}
-- Hide the proxy table in closures.
do
local proxy = {}
function Cat_mt:__index(key)
return proxy[key]
end
function Cat_mt:__newindex(key, value)
if proxy[key] ~= nil then
error("Don't change that!")
end
proxy[key] = value
end
end
Cat = setmetatable({}, Cat_mt)