SVA concurrent array comparision - system-verilog

I'm new to systemverilog and SVA and I'm trying to create an assertion for a prbs generator, comparing any given array (logic [6:0]) with the same array 127 clock cycles after. The problem is that the operators I found helpful to compare arrays do not allow concurrency and the ones allowing concurrency operate only with bits.
Trying to exemplify what I wanted to do, would be something like this:
logic [6:0] seq
assert property (#posedge clock) (seq === #127 seq));

Something along the lines of:
property DLY;
logic MEM [6:0];
( 1'b1, MEM = seq ) |-> ##127 (seq_OUT == MEM);
endproperty
DLY_CHK : assert property (#(posedge clock) DLY);
ought to do the trick. This uses the a local variable and ( , ) construct to assign a local variable (MEM). This is then checked 127 clock cycles later.
With the ( , ) construct, if the LHS of the comma is true (which in my example it always is), then the RHS of the comma is executed. You might want to change the 1'b1 to some kind of control signal. You can put one of three things on the RHS of the comma:
an assignment to a local variable
an increment or decrement of a local variable
a call to a task, task method, void function, void function method or system task
See sections 16.10 and 16.11 of IEEE 1800-2012.

Related

What is the best way to check an event occurred in the past in SVA?

I want to check in my design that when signal b get asserted, then signal a should have gotten asserted 3 to 5 cycles before.
I'm looking for the different ways to check that.
Currently I'm using the following logic
sequence s_test();
##1 $rose(a) ##[3:5] 1;
endsequence
property p_test();
##1 $rose(b) |-> s_test.triggered();
Is there a way to check that property without using the sequence triggered mechanism ? I guess I could also use something like $past(a, 3) || ... || $past(a, 5), but that's cumbersome.
Also what's the difference between the sequence triggered and matched mechanism ?
We can have two approaches here: cause then effect or effect because of cause.
Cause then effect approach:
You can use a forward-time-based assertion stating that when s_test is triggered, then b should go high in 1-5 clock period of time window:
s_test.triggered |-> ##[1:5] $rose(b);
Effect then cause approach:
Alternatively, if s_test is a signal, then you can use a glue logic which monitors past 5 values of s_test. Thereafter, the assertion checks that the earlier values of s_test must have atleast 1'b1 when b rises from 0 to 1.
bit[1:5] earlier;
always #(posedge clk) begin
earlier <= {s_test, earlier[1:5]}; // shift for 5 clocks
end
p1_past20: assert property(#(posedge clk)
$rose(b) |-> $countones(earlier) >= 1);
A similar discussion is available here and a reference is over here.
You can use $past something like below.
property test_past;
#(posedge clk)
$rose(b) |-> ##[3:5] $past(a);
endproperty
triggered & matched methods differ for single clock & multi clock sequences.
Both methods show end point of a sequence, but triggered method evaluates to true if the operand sequence has reached it's end point at that particular time and false otherwise.
Whereas matched method detects endpoint of sequence, referenced in multiclocked sequence. So it provides synchronization between 2 sequences and evaluates to true after match, untill arrival of 1st clock tick of destination sequence.
triggered status of a sequence is set in observed region and is persisted through the remainder of the timestep. Whereas matched status of a sequence is set in observed region and is persisted untill the observed region of the arrival of first clock tick of destination sequence after match.

What is a difference between following two logic implementation from Hardware perspective?

Implementation 1:
logic [2:0][3:0] reg0; // Packed
always_ff#(clk_a)
reg0[1:0] <= in0[1:0];
always_ff#clk_b)
reg0[3:2] <= in1[1:0];
Implementation 2:
logic [2:0] reg0 [3:0]; // unpacked
always_ff#(clk_a)
reg0[1:0] <= in0[1:0];
always_ff#clk_b)
reg0[3:2] <= in1[1:0];
Why tool gives me multi-driver error for implementation 1?
The difference is what the LRM considers a variable. You are not allowed to have multiple assignments to the same variable from different processes. A packed array is considered a variable as well as each element if an unpacked array. The reason for this restriction has more do do with efficient simulation implementation and not really with hardware implementation and the distinction about what constitutes a variable is used in other places in the LRM (i.e. pass by reference).

VHDL 'range => '0' command

Was hoping someone could answer my question. I came across this command in a VHDL code and was not sure what it does exactly. Could someone clarify the following?
if ( element1 = (element1'range => '0')) then
given that element1 is a 4 bit std_logic_vector, what is this condition saying? I could not find a direct answer for this in the few books I had or on google.
Thanks!
It's saying, create a temporary array aggregate the size of the range specified, with every element set to '0'. Whatever that range is.
Preventing accidents when the size of element1 changes.
EVERY time you see magic numbers like 3 downto 0, or for i in 0 to 3 loop ... try to replace them with this or equivalent, because for i in element1'range loop ... will never loop off the end of your array.
The defined range is necessary because the relational operator = (like <, > and the others) doesn't restrict its arguments to the same length, so the simpler form of aggregate (others => '0') doesn't work, because its size is undefined.
The condition will return true if element1 contains only '0'. It is a way of writing this that does not depend on the size of element1. In this case element1'range is 3 downto 0. If you were to change this to, for example, 5 downto 0, the if condition would still work.
(element1'range => '0') is an array aggregate with element choices for the range of element1 and associates those elements with the value '0' creating a composite value that takes it's type from context - the left hand side of the "=" operator (IEEE Std 1076-2008 9.3.3 Aggregates, 9.3.3.3 Array aggregates).
The if statement (10.8 If statement) condition element1 = (element1'range => '0') determines if element1 is all '0's in a range independent manner, the equality relational operator (9.2.3 Relational operators) returning a BOOLEAN value.
This method of evaluating the value of element1 is immune to the declaration of element1 changing (6.4.2 Object declarations, 6.4.2.3 Signal declarations, 6.4.2.4 Variable declarations, 6.5.2 Interface object declarations).
The outer pair of parentheses for the condition (an expression) are superfluous in VHDL (10.2 Wait statement, the BNF for condition, 9. Expressions, 9.1 General, the BNF for intermediary target primary allows them).

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

What should '{default:'1} do in system verilog?

I have an array that I would like to initialize to all 1. To do this, I used the following code snippet:
logic [15:0] memory [8];
always_ff #(posedge clk or posedge reset) begin
if(reset) begin
memory <= '{default:'1};
end
else begin
...
end
end
My simulator does what I think is the correct thing and sets the registers to 16'hFFFF on reset. However, my lint tool gives me a warning that bit 0 has an async set while bits 1 through 15 have async resets. This implies that the linter thinks that this code assigns 16'h0001 to the registers.
Since both tools come from the same vendor I file a bug report since they can't both be right.
The question is: Which behavior is correct according to the spec? There is no example that shows this exact situation. Section 5.7.1 mentions that:
An unsized single-bit value can be specified by preceding the single-bit value with an apostrophe ( ' ), but
without the base specifier. All bits of the unsized value shall be set to the value of the specified bit. In a
self-determined context, an unsized single-bit value shall have a width of 1 bit, and the value shall be treated
as unsigned.
'0, '1, 'X, 'x, 'Z, 'z // sets all bits to specified value
I f this is a "self-determined context" then the answer is 1 bit sign extended to 16'h0001, but if it is not, then I guess the example which says it "sets all bits to the specified value" applies. I am not sure if this is a self -determined context.
The simulator is correct: memory <= '{default:'1}; will assign all each bit in memory to 1. The linting tool does have a bug. See IEEE Std 1800-2012 ยง 10.9.1 Array assignment patterns:
The **default:***value* applies to elements or subarrays that are not matched by either index or type key. If the type of the element or subarray is a simple bit vector type, matches the self-determined type of the value, or is not an array or structure type, then the value is evaluated in the context of each assignment to an element or subarray by the default and shall be castable to the type of the element or subarray; otherwise, an error is generated. ...
The LRM goes beyond what is synthesizable when it comes to assignment patterns. And most of the tools are sill playing catchup with supporting all the SystemVerilog features. Experiment to make sure your tools (simulator,synthesizer, lint tool, logic-equivalency-checker, etc.) all have the necessary support for the features you want.