Hi, can someone help on implementing checking the timing without using SVA?
The timing definition of two signals-(REQ, ACK) are as follows:
1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
2.REQ should stay high for 5~7 clk cycles before goes low
3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
In reply to mlsxdx:
There are many times when SVA is a lot lot simpler.
There are other times when SVA can be complex. Below is a non-sva solution (it needs verification). I explain an alternative way to SVA in https://verificationacademy.com/forums/systemverilog/paper-understanding-sva-engine-simple-alternate-solutions
Abstract: Understanding the engine behind SVA provides not only a better appreciation and limitations of SVA, but in some situations provide features that cannot be simply implemented with the current definition of SVA. This paper first explains, by example, how a relatively simple assertion example can be written without SVA with the use of SystemVerilog tasks; this provides the basis for understanding the concepts of multithreading and exit of threads upon a condition, such as an error in the assertion. The paper then provides examples that uses computational variables within threads; those variables can cause, in some cases, errors in SVA. The strictly emulation model with tasks solves this issue.
/* The timing definition of two signals-(REQ, ACK) are as follows:
1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
2.REQ should stay high for 5~7 clk cycles before goes low
3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle */
import uvm_pkg::*; `include "uvm_macros.svh"
module top;
timeunit 1ns; timeprecision 100ps;
bit clk, req, ack;
event e1, e2, e3; // for debug
default clocking @(posedge clk); endclocking
initial forever #10 clk=!clk;
always @(posedge clk) treqack();
task automatic treqack();
automatic bit gotack, fallreq, reqhi;
if($rose(req)) begin : rose
fork
begin: req2end
-> e1;
repeat(10) begin : rpt10
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
if(ack) begin : got
gotack=1'b1;
disable rpt10;
end : got
@(posedge clk);
end : rpt10
a_gotack: assert(gotack) else `uvm_error("MYERR", "No ack after req in 10")
if(!gotack) disable req2end;
-> e2;
// 3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
repeat(10) begin : rpt10b
if(!req) begin : negreq
fallreq=1'b1;
disable rpt10b;
end: negreq
@(posedge clk) ;
end : rpt10b
a_gotfallreq: assert(fallreq) else `uvm_error("MYERR", "No fall of req in 10")
if(!fallreq) disable req2end; // may not be needed
end : req2end
begin : Reqfor7
// 2.REQ should stay high for 5~7 clk cycles before goes low
-> e3;
repeat(7) begin : rpt7
if(!req) begin : afell_req
reqhi=1'b1;
disable afell_req;
end : afell_req
@(posedge clk);
end : rpt7
areqhi7 : assert(reqhi) else `uvm_error("MYERR", "No reqhi for 7")
if(!reqhi) disable Reqfor7; // may not be needed
end : Reqfor7
join
end : rose
endtask :treqack
initial begin
repeat(200) begin
@(posedge clk);
if (!randomize(req, ack) with
{ req dist {1'b1:=1, 1'b0:=9};
ack dist {1'b1:=1, 1'b0:=9};
}) `uvm_error("MYERR", "This is a randomize error")
end
$stop;
end
endmodule
Hi Ben, thanks for your reply. It is very helpful.
I have two doubts about the code.
repeat(10) begin : rpt10
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
//[ML]---------------------------------------------------------------
//[ML]should it be $rose(ack) instead of ack?
//[ML]if($rose(ack)) begin : got
//[ML]---------------------------------------------------------------
if(ack) begin : got
gotack=1'b1;
disable rpt10;
end : got
@(posedge clk);
end : rpt10
a_gotack: assert(gotack) else `uvm_error("MYERR", "No ack after req in 10")
if(!gotack) disable req2end;
begin : Reqfor7
// 2.REQ should stay high for 5~7 clk cycles before goes low
-> e3;
//[ML]------------------------------------------------------
//[ML] how to check lower bound >=5 clk cycles??
//[ML]------------------------------------------------------
repeat(7) begin : rpt7
if(!req) begin : afell_req
reqhi=1'b1;
disable afell_req;
end : afell_req
@(posedge clk);
end : rpt7
areqhi7 : assert(reqhi) else `uvm_error("MYERR", "No reqhi for 7")
if(!reqhi) disable Reqfor7; // may not be needed
end : Reqfor7
In reply to ben@SystemVerilog.us:
Hi Ben, thanks for your reply. It is very helpful.
I have two doubts about the code.
//[ML]should it be $rose(ack) instead of ack?
[Ben] It does have to be, but it could be. Upon a rose of ack, or of ack=1 you disable the loop However, the $rose(ack) would catch the error if ack is always a 1’b1.
begin : Reqfor7
// 2.REQ should stay high for 5~7 clk cycles before goes low
-> e3;
//[ML]------------------------------------------------------
//[ML] how to check lower bound >=5 clk cycles??
//[ML]------------------------------------------------------
Below is updated code. FOr that case, I used a counter and a check on that value.
/* The timing definition of two signals-(REQ, ACK) are as follows:
1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
2.REQ should stay high for 5~7 clk cycles before goes low
3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle */
import uvm_pkg::*; `include "uvm_macros.svh"
module top;
timeunit 1ns; timeprecision 100ps;
bit clk, req, ack;
event e1, e2, e3; // for debug
default clocking @(posedge clk); endclocking
initial forever #10 clk=!clk;
// always @(posedge clk) treqack(); // <---- Edited to:
always @(posedge clk) fork treqack(); join_none
task automatic treqack();
automatic bit gotack, fallreq, reqhi;
automatic int count;
if($rose(req)) begin : rose
fork
begin: req2end
-> e1;
repeat(10) begin : rpt10
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
if($rose(ack)) begin : got
gotack=1'b1;
disable rpt10;
end : got
@(posedge clk);
end : rpt10
a_gotack: assert(gotack) else `uvm_error("MYERR", "No ack after req in 10")
if(!gotack) disable req2end;
-> e2;
// 3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
repeat(10) begin : rpt10b
if(!req) begin : negreq
fallreq=1'b1;
disable rpt10b;
end: negreq
@(posedge clk) ;
end : rpt10b
a_gotfallreq: assert(fallreq) else `uvm_error("MYERR", "No fall of req in 10")
if(!fallreq) disable req2end; // may not be needed
end : req2end
begin : Reqfor7
// 2.REQ should stay high for 5~7 clk cycles before goes low
-> e3;
repeat(7) begin : rpt7
if(!req) begin : afell_req
reqhi=1'b1;
disable afell_req;
end : afell_req
else count=count+1'b1;
@(posedge clk);
end : rpt7
areqhi7 : assert(reqhi && count>=5) else `uvm_error("MYERR", "No reqhi for 5 to 7")
if(!reqhi) disable Reqfor7; // may not be needed
end : Reqfor7
join
end : rose
endtask :treqack
initial begin
repeat(200) begin
@(posedge clk);
if (!randomize(req, ack) with
{ req dist {1'b1:=1, 1'b0:=9};
ack dist {1'b1:=1, 1'b0:=9};
}) `uvm_error("MYERR", "This is a randomize error")
end
$stop;
end
endmodule
Hi, can someone help on implementing checking the timing without using SVA?
The timing definition of two signals-(REQ, ACK) are as follows:
1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
2.REQ should stay high for 5~7 clk cycles before goes low
3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
In reply to Rohi_417:
Multiple smaller assertions is a preferred approach here.
// Requirements:
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
ap_req2ack: assert property(
@(posedge clk) $rose(req) |-> ##[1:10] $rose(ack) );
// 2.REQ should stay high for 5~7 clk cycles before goes low
ap_reqHi: assert property(
@(posedge clk) $rose(req) |-> req[*5:7]);
//3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
ap_ack2req: assert property(
@(posedge clk) $rose(req) |-> $rose(ack) [->1]
##[1:10] $fell(req) );
There are some bugs in the task solution (it was not tested).
Will post an update, as the task approach showed errors when compared to the SVA solution.
And that proves that SVA is preferred, with few exceptions as explained in my paper.
Ben Cohen http://www.systemverilog.us/ben@systemverilog.us
For training, consulting, services: contact Home - My cvcblr
/* The timing definition of two signals-(REQ, ACK) are as follows:
1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
2.REQ should stay high for 5~7 clk cycles before goes low
3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle */
import uvm_pkg::*; `include "uvm_macros.svh"
module top;
timeunit 1ns; timeprecision 100ps;
bit clk, req, ack;
int delay=1;
// Requirements:
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
ap_req2ack: assert property(
@(posedge clk) $rose(req) |-> ##[1:10] $rose(ack) );
// 2.REQ should stay high for 5~7 clk cycles before goes low
ap_reqHi: assert property(
@(posedge clk) $rose(req) |-> req[*5:7]);
//3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
ap_ack2req: assert property(
@(posedge clk) $rose(req) |-> $rose(ack) [->1]
##[1:10] $fell(req) );
event e1, e2, e3, e4, e5, e6; // for debug
default clocking @(posedge clk); endclocking
initial forever #10 clk=!clk;
always @(posedge clk) fork treqack(); join_none
task automatic treqack();
automatic bit gotack, fallreq, reqhi;
automatic int count;
if($rose(req)) begin : rose
fork
begin: req2end
-> e1;
rpt10: repeat(10) begin
// 1.After REQ assert, ACK has to be asserted in 1~10 clk cycle
-> e6;
@(posedge clk); // <-- Update
if($rose(ack)) begin : got
gotack=1'b1;
disable rpt10;
end : got
//@(posedge clk);
end : rpt10
a_gotack: assert(gotack) else `uvm_error("MYERR", "No ack after req in 10")
if(!gotack) disable req2end;
-> e2;
// 3.After ACK assert, REQ has to be de-asserted in 1~10 clk cycle
rpt10b: repeat(10) begin
@(posedge clk); // Updated
if(!$fell(req)) begin : negreq
fallreq=1'b1;
disable rpt10b;
end: negreq
// @(posedge clk) ;
end : rpt10b
a_gotfallreq: assert(fallreq) else `uvm_error("MYERR", "No fall of req in 10")
if(!fallreq) disable req2end; // may not be needed
end : req2end
begin : Reqfor7
// 2.REQ should stay high for 5~7 clk cycles before goes low
-> e3; count=1; // Update, because of the rose(req)
rpt7: repeat(7) begin
$display("%t count=%d", $time, count); -> e5;
@(posedge clk); // Updated
if($fell(req) || count==5) begin : afell_req // update
reqhi=1'b1; -> e4; $display("%t IN count=%d", $time, count);
disable rpt7; // Update
end : afell_req
else count=count+1'b1;
//@(posedge clk);
end : rpt7
$display("%t OUT count=%d", $time, count);
areqhi7 : assert(reqhi && count>=5) else `uvm_error("MYERR", "No reqhi for 5 to 7")
// if(!reqhi) disable Reqfor7; // may not be needed
end : Reqfor7
join
end : rose
endtask :treqack
initial begin
repeat(200) begin
repeat(delay) @(posedge clk);
#1; if (!randomize(req, ack, delay) with
{ req dist {1'b1:=1, 1'b0:=9};
ack dist {1'b1:=1, 1'b0:=1};
delay dist {3:=1, 4:= 1, 5:=2, 7:= 1, 12:= 1};
}) `uvm_error("MYERR", "This is a randomize error")
end
$stop;
end
endmodule
When I came back to digest the non-SVA solution, I realized that it will throw an error on the first {gotack,fallreq}. What is wrong with my understanding?
Here is the simplified example, the assertion failed on the first got_b is 0, not 1. I am surprised that got_b is not assigned successfully.
In reply to mlsxdx:
I modified your code because it was not correct in terms of threads and the forks/
See my new paper at the July Verification Horizons
Take a look at my diagram on The SystemVerilog flow of time slots and event regions
I also modified your testbench; you should avoid the time a <= value.
Use instead repeat(n) @(posedge clk); a <= value;
Follow this code, I think you’ll get it.
//The example to try to use non-SVA method to check timing relationship between a and b
//b is asserted after a asserted within 10 clk cycle
module tb;
bit clk;
bit enable;
logic a,b;
event e1,e2;
int thread=1;
always #1 clk = !clk;
always @(posedge clk) begin
if(enable == 1) begin
fork do_chk(thread); join_none
$display("%t thread= %d a=%b, b=%b", $realtime, thread, a, b);
thread+=1'b1;
end
end
task automatic do_chk(int thread);
bit got_b;
//fork: do_chk
-> e1;
if(a == 1'b1) begin : a1
-> e2;
clk_rpt: repeat(10) begin : rpt10
@(posedge clk);
if(b == 1) begin : b1
got_b = 1;
disable clk_rpt;
end : b1
end : rpt10
$display($time,,"thread %d got_b=%0b",thread, got_b);
B_A_TM: assert(got_b) $display($time,,"Success!");
else $error($time, "thread %d Fail!", thread);
end : a1 // if (a == 1'b1)
else return; // if(!got_b) disable do_chk;
endtask
initial begin
#10 enable <= 1;
end
initial begin
{a,b} <= 0;
repeat(20) @(posedge clk);
a <= 1;
repeat(10) @(posedge clk);
b <= 1;
repeat(20) @(posedge clk);
$finish();
end
initial begin
$dumpfile("dump.fsdb");
$dumpvars(0,tb);
end
endmodule // tb
11 thread= 1 a=0, b=0
13 thread= 2 a=0, b=0
15 thread= 3 a=0, b=0
17 thread= 4 a=0, b=0
19 thread= 5 a=0, b=0
21 thread= 6 a=0, b=0
23 thread= 7 a=0, b=0
25 thread= 8 a=0, b=0
27 thread= 9 a=0, b=0
29 thread= 10 a=0, b=0
31 thread= 11 a=0, b=0
33 thread= 12 a=0, b=0
35 thread= 13 a=0, b=0
37 thread= 14 a=0, b=0
39 thread= 15 a=0, b=0
41 thread= 16 a=1, b=0
43 thread= 17 a=1, b=0
45 thread= 18 a=1, b=0
47 thread= 19 a=1, b=0
49 thread= 20 a=1, b=0
51 thread= 21 a=1, b=0
53 thread= 22 a=1, b=0
55 thread= 23 a=1, b=0
57 thread= 24 a=1, b=0
59 thread= 25 a=1, b=0
61 thread 17 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 17 Fail!
61 thread 18 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 18 Fail!
61 thread 19 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 19 Fail!
61 thread 20 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 20 Fail!
61 thread 21 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 21 Fail!
61 thread 22 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 22 Fail!
61 thread 23 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 23 Fail!
61 thread 24 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 24 Fail!
61 thread 25 got_b=0
"testbench.sv", 37: tb.do_chk.a1.B_A_TM: started at 61ns failed at 61ns
Offending 'got_b'
Error: "testbench.sv", 37: tb.do_chk.a1.B_A_TM: at time 61 ns
61thread 25 Fail!
61 thread 26 got_b=1
61 Success!
61 thread= 26 a=1, b=1
63 thread 27 got_b=1
63 Success!
63 thread= 27 a=1, b=1
65 thread 28 got_b=1
65 Success!
65 thread= 28 a=1, b=1
67 thread 29 got_b=1
67 Success!
67 thread= 29 a=1, b=1
69 thread 30 got_b=1
69 Success!
69 thread= 30 a=1, b=1
71 thread 31 got_b=1