Convert unsigned int to Time in System-verilog - system-verilog

I have in large part of my System-Verilog code used parameters to define different waiting times such as:
int unsigned HALF_SPI_CLOCK = ((SYSTEM_CLK_PERIOD/2)*DIVISION_FACTOR); //DEFINES THE TIME
Now since I define a timescale in my files I can directly use these params to introduce waiting cycles:
`timescale 1ns/1ns
initial begin
#HALF_SPI_CLOCK;
end
Now I want to have time-specified delays everywhere. Means that the simulation will still respect all the timings even if I change the timescale. I would like to keep the parameters but wherever I have a wait statement, I need to specify the time. Something like
#(HALF_SPI_CLOCK) ns;
But this is not accepted by Modelsim. Is there a way to cast a parameter or an Unsigned int to a variable of type time in System-Verilog? Is there a way to specify the time unit? I have looked around but could not find any workaround.
The reason why I want to have control over the time and make it independent from the timescale is because I intend to change thetimescale later to try to make my simulation faster.
Other recommendations or thoughts are very welcome*

It is possible to pass time as a parameter in SystemVerilog, like:
module my_module #(time MY_TIME = 100ns);
initial begin
#MY_TIME;
$display("[%t] End waiting", $time);
end
endmodule
Or use multiplication to get the right time units, like:
module my_module2 #(longint MY_TIME = 100);
initial begin
# (MY_TIME * 1us);
$display("[%t] End waiting 2", $time);
end
endmodule
See runnable example on EDA Playground: http://www.edaplayground.com/x/m2

This will simulate and do what you want. While not the most elegant, it works.
task wait_ns(int num);
repeat (num) #1ns;
endtask
...
wait_ns(HALF_SPI_CLOCK);
This could have a negative impact simulation speed depending on how the timescale, clock events, and the unit of delay relate to each other.

Related

How to guarantee checker runs after monitor each timestep

I have several agents each with their own monitor and analysis ports connected to a checker. The checker is organized like below where it calls each check() function every cycle in a specific order. This is done this way to handle the case where we get an input and output txn in the same cycle (design has "bypass" logic to immediately output the txn it sees on its input in the same cycle).
If we go with design #2 (below), there is no guarantee that we will process the input_txn first, so if we happen to process the output_txn first, the assertion could fire because it doesn't know that there was an input_txn in the same cycle. I have had success using Design #1 to handle the case where we get an input and output txn in the same cycle; however I now realize this is still not guaranteed to work correctly because it's possible that the simulator could execute the checker's run_phase() after the output_agent's run_phase() but before the input_agent's run_phase(), and I could get the same issue.
What I really want is almost a "check_phase" for each timestep, so I can guarantee all agents monitors' have finished executing in the current timestep before the checker starts executing. Is there any way to guarantee the checker executes after all other processes in the current timestep?
P.S. I'm not looking for advice on how to improve my checker, this is just a very dumbed down version of my actual testbench I made to easily convey the problem I have.
## Design 1 ##
class my_checker extends uvm_component;
//boiler plate uvm...
task run_phase();
forever begin
check_inputs();
check_outputs();
#(posedge vinft.clk);
end
endtask
function check_inputs();
input_txn_c txn;
if (input_analysis_fifo.try_get(txn)) begin // non-blocking try_get()
//do check
pending_txn_cnt++;
end
endfunction
function check_outputs();
output_txn_c txn;
if (output_analysis_fifo.try_get(txn)) begin //non-blocking try_get()
assert(pending_txn_cnt > 0);
pending_txn_cnt--;
end
endfunction
endclass
## Design 2 ##
class my_checker extends uvm_component;
//boiler plate uvm...
task run_phase();
fork
check_inputs();
check_outputs();
join_none
endtask
task check_inputs();
input_txn_c txn;
forever begin
input_analysis_fifo.get(txn); //blocking get()
//do check
pending_txn_cnt++;
end
endtask
task check_outputs();
output_txn_c txn;
forever begin
output_analysis_fifo.get(txn); //blocking get
assert(pending_txn_cnt > 0);
pending_txn_cnt--;
end
endtask
endclass
Since you use a FIFO for both the input and output, you should be able to use this simple design:
class my_checker extends uvm_component;
//boiler plate uvm...
input_txn_c txni;
output_txn_c txno;
task run_phase();
forever begin
// Wait until there is an input transaction: txni
input_analysis_fifo.get(txni);
// Wait until there is an output transaction: txno
output_analysis_fifo.get(txno);
// Now there is a pair of transactions to compare: txni vs. txno
// call compare function...
end
endtask
// compare function...
endclass
Since the get calls are blocking, you just need to wait until you have an input transaction, then wait until you have an output transaction. It does not matter if they arrive in the same timestep. Once you have an in/out pair, you can call your compare function.
I don't think you need to check the transaction count for every pair. If you want, you could check if the FIFO's still have anything in them at the end of the test.

recomend the way to write a monitor in UVM with defferent event polarity

I am trying to implement a monitor for VDU(Video display unit) and the way the VDU can be programmed says that sync signals have controllable polarity. This means than according to VDU settings monitor should react on #posedge or #negedge event. Is there any way to pass the type (means posesge or negedge) via configuration data base or do something like this. Instead of write if(truth) #posedge else #negedge. And assertion also needs to be controlled this way but assertion at list designed to take event type as an argument but I am no sure config data base calls are allowed inside interface.
On option is to conditionally trigger an event. For example, you can have the bellow in you interface:
event mon_clk_ev;
bit mon_polarity;
always #(posedge clk) if ( mon_polarity) ->mon_clk_ev;
always #(negedge clk) if (!mon_polarity) ->mon_clk_ev;
Then you can use mon_clk_ev are the clock event in your monitor, interface, clocking block, or assertion.
mon_polarity could be assigned by your monitor, uvm_config_db, or other logic.
Example using uvm_config_db (Note using uvm_bitstream_t so it can be assigned with the uvm_set_config_int plusarg):
initial begin
start_of_simulation_ph.wait_for_state( UVM_PHASE_STARTED, UVM_GTE );
if (!uvm_config_db#(uvm_bitstream_t)::exists(null,"","mon_polarity")) begin
// default if not in database
uvm_config_db#(uvm_bitstream_t)::set(null,"*","mon_polarity",1'b1);
end
forever begin
void'(uvm_config_db#(uvm_bitstream_t)::get(null,"","mon_polarity",mon_polarity));
uvm_config_db#(uvm_bitstream_t)::wait_modified(null,"","mon_polarity");
end
end
You should write your code assuming positive polarity, but feed them through an xor operator.
logic signal; // your signal from DUT
logic signal_corrected; // signal with positive polarity
bit signal_polarity; // 0 = positive ; 1 = negative
assign signal_corrected = signal ^ signal_polarity;
Now you can use signal_corrected in your assertions. You can certainly call uvm_config_db#(bit)::get() from the interface if it has been set in your testbench. You might need to use uvm_config_db#(bit)::wait_modified() to wait for it to be set before you get it.

Is there a systemverilog method to check the time granularity?

I am having hard time working with systemverilog data type time.
Lets say there are two packages coming from third party.
package p1;
import p2:*;
class d;
time c;
function print();
$display("t1 is %t, t2 is %t\n", c.t1, c.t2);
c = t2 - t1;
$display("c is %t\n", c);
endfunction
endpackage : p1
package p2;
class c;
time t1;
time t2;
endclass : c
endpackage : p2
If t2 is 100ns, t1 is 10ns, c prints out 900000. Can someone tell me what might have happened with the code above? Is there a way to check the time granularity in both the packages?
First of all, there is nothing special about the time data type—it's just a 64-bit unsigned integer. What matters is the timescale/unit where you make assignments to t1 and t2 using $time
Unfortunately there's no way to know what the timescale is from another design unit is unless you establish a mutually agreed upon time unit. You can do that by dividing all times by that mutual unit.
realtime t1;
t1 = $realtime/1ns;
It's best to use $realtime to get any fractional time steps.
From this example. You can use the $printtimescale system function to display the default timescale or the timescale of a given module instance.
You could log this and do checks in your external batch processing, e.g. via a Makefile.
Or see my other answer, here, which captures the timescale into a variable then uses it to measure frequency and time.
`timescale 1ns/1ps
module tb;
des m_des();
alu m_alu();
initial begin
$printtimescale(tb);
$printtimescale(tb.m_alu);
$printtimescale(tb.m_des);
end
endmodule
module alu;
endmodule
`timescale 1ns/10ps
module des;
endmodule

Possible workaround for async negedge reset?

I'd like to have a register with async reset signal, like following:
always #(posedge clk or negedge rst_n)
begin
if(!rst_n)
out <= 1'b0
else
out <= in
end
I have tried class AsyncReset() and withReset(). However, the generated code uses a posedge reset and the variable of AsyncReset() does not accept !.
Is there any workaround for this?
While you cannot invert the AsyncReset type directly (generally applying logic to an AsyncReset is bad because it can glitch), you can cast to a Bool and back:
val reset_n = (!reset.asBool).asAsyncReset
val reg = withReset(reset_n)(RegInit(0.U(8.W)))
Runnable example: https://scastie.scala-lang.org/ERy0qHt2Q3OvWIsp9qiiNg
I thought Jack's quick comment about avoiding glitches deserved a longer explanation.
Using an asynchronous reset creates a second timing arc in the design, from the reset to the end flop. The reset signal can be asserted at any time but needs to be de-asserted synchronous to the clock otherwise the flop can become metastable.
A common technique to do this is to use a reset synchronizer.
https://scastie.scala-lang.org/hutch31/EPozcu39QBOmaB5So6fyeA/13
The synchronizer shown in the above code is coded directly in Verilog as I do not know a way to keep the FIRRTL optimizer from pruning through constant optimization. The logic downstream of the reset sync can be either sync or async reset.

What is the benefit of automatic variables?

I'm looking for benefits of "automatic" in Systemverilog.
I have been seeing the "automatic" factorial example. But I can't get though them. Does anyone know why we use "automatic"?
Traditionally, Verilog has been used for modelling hardware at RTL and at Gate level abstractions. Since both RTL and Gate level abstraction are static/fixed (non-dynamic), Verilog supported only static variables. So for example, any reg or wire in Verilog would be instantiated/mapped at the beginning of simulation and would remain mapped in the simulation memory till the end of simulation. As a result, you can take dump of any wire/reg as a waveform, and the reg/wire would have a value from the beginning till the end, since it is always mapped. In a programmers perspective, such variables are termed static. In C/C++ world, to declare such a variable, you will have to use storage class specifier static. In Verilog every variable is implicitly static.
Note that until the advent of SystemVerilog, Verilog supported only static variables. Even though Verilog also supported some constructs for modelling at behavioural abstraction, the support was limited by absence of automatic storage class.
automatic (called auto in software world) storage class variables are mapped on the stack. When a function is called, all the local (non-static) variables declared in the function are mapped to individual locations in the stack. Since such variables exist only on the stack, they cease to exist as soon as the execution of the function is complete and the stack correspondingly shrinks.
Amongst other advantages, one possibility that this storage class enables is recursive functions. In Verilog world, a function can not be re-entrant. Recursive (or re-entrant) functions do not serve any useful purpose in a world where automatic storage class is not available. To understand this, you can imagine a re-entrant function as a function which dynamically makes multiple recursive instantiations of itself. Each instance gets its automatic variables mapped on the stack. As we progress into the recursion, the stack grows and each function gets to make its computations using its own set of variables. When the function calls return the computed values are collated and a final result made available. With only static variables, each function call will store the variable values at the same common locations thus erasing any benefit of having multiple calls (instantiations).
Coming to the factorial algorithm, it is relatively easy to conceptualize factorial as a recursive algorithm. In maths we write factorial(n) = n(factial(n-1))*. So you need to calculate factorial(n-1) in order to know factorial(n). Note that recursion can not be completed without a terminating case, which in case of factorial is n=1.
function automatic int factorial;
input int n;
if (n > 1)
factorial = factorial (n - 1) * n;
else
factorial = 1;
endfunction
Without automatic storage class, since all variables in a function would be mapped to a fixed location, when we call factorial(n-1) from inside factorial(n), the recursive call would overwrite any variable inside the caller context. In the factorial function as defined in the above code snippet, if we do not specify the storage class as automatic, both n and the result factorial would be overwritten by the recursive call to factorial(n-1). As a result the variable n would consecutively be overwritten as n-1, n-2, n-3 and so on till we reach the terminating condition of n = 1. The terminating recursive call to factorial would have a value of 1 assigned to n and when the recursion unwinds, factorial(n-1) * n would evaluate to 1 in each stage.
With automatic storage class, each recursive function call would have its own place in the memory (actually on the stack) to store variable n. As a result consecutive calls to factorial will not overwrite variable n of the caller. As a result when the recursion unwinds, we shall have the right value for factorial(n) as n*(n-1)(n-2) .. *1.
Note that it is possible to define factorial using iteration as well. And that can be done without use of automatic storage class. But in many cases, recursion makes it possible for the user to code algorithms in a more intuitive fashion.
I propose 1 example as below(using fork...join_none):
Ex.1 (non-using automatic) : value output will be "3 3 3 3". because i take the latest value after exit for loop, i is stored in static memory location. This may be a bug in your code.
initial begin
for( int i =0; i<=3 ; i++)
fork
$write ("%d ", i);
join_none
end
Ex.2 (using automatic) : value output will be "0 1 2 3". Because in each loop, value of i is copied to k, and fork..join_none spawn a thread with each value of k ( each loop will locate 1 memory space for k : k0, k1, k2, k3):
initial begin
for( int i =0; i<=3 ; i++)
fork
automatic int k = i;
$write ("%d ", k);
join_none
end
Another example is the use of fork join inside for loop -
Without the use of automatic, the fork join inside the for loop will not work correctly.
for (int i=0; i<`SOME_VALUE ; i++) begin
automatic int id=i;
fork
task/function using the id above ;
...
join_none
end