Systemverilog assertion to check bad signal transition - system-verilog

I am trying to write an assertion that will fire only if a signal transitions on the rising edge of 'clk'. I wrote below code to test out my ideas
module test();
bit clk, clkb;
int d;
assign clkb = ~clk;
initial begin
clk = 0;
forever #100 clk = ~clk;
end
initial begin
d = 10;
#150 d = 20;
end
sva_d_chgd: assert property (#(posedge clk) $stable(d,#(clkb)))
else $error($psprintf("err: time = %0d, clk = %b, d = %0d", $time, clk, d));
always # (d or clk) begin
$display("time = %0d, clk = %b, d = %0d", $time, clk, d);
if ($time > 200) $finish;
end
endmodule
Above code returns the following output in VCS:
time = 0, clk = 0, d = 10
time = 100, clk = 1, d = 10
"test.vs", 18: test.sva_d_chgd: started at 100s failed at 100s
Offending '$stable(d, #(clkb))'
Error: "test.vs", 18: test.sva_d_chgd: at time 100
err: time = 100, clk = 1, d = 10
time = 150, clk = 1, d = 20
time = 200, clk = 0, d = 20
time = 300, clk = 1, d = 20
$finish called from file "test.vs", line 23.
$finish at simulation time 300
Why did the assertion fire at time 100 when 'd' remained stable until time 150?

In your code, stable checks at every posedge of clk to see if the value of "d" has changed or not between the previous two edges of clkb. Since at the very first posedge of clk there was no previous clkb edge value of "d", stable returns "unknown" instead of "true" or "false" which causes your assertion to fail.
I've added a reset signal to your code and disabled the assertion until after the first posedge of clk. I also moved when "d" changes.
module test();
bit clk, clkb, rst;
int d;
assign clkb = ~clk;
initial begin
clk = 0;
forever #100 clk = ~clk;
end
initial begin
rst = 1;
#150 rst = 0;
end
initial begin
d = 10;
#250 d = 20;
end
sva_d_chgd: assert property (#(posedge clk)
disable iff (rst)
$stable(d,#(clkb)))
else $error($psprintf("err: time = %0d, clk = %b, d = %0d", $time, clk, d));
always # (d or clk) begin
$display("time = %0d, clk = %b, d = %0d", $time, clk, d);
if ($time > 400) $finish;
end
endmodule
Here's the output:
# time = 0, clk = 0, d = 10
# time = 100, clk = 1, d = 10
# time = 200, clk = 0, d = 10
# time = 250, clk = 0, d = 20
# time = 300, clk = 1, d = 20
# ** Error: err: time = 300, clk = 1, d = 20
# Time: 300 ns Started: 300 ns Scope: test.sva_d_chgd File: assert_test.sv Line: 26
# time = 400, clk = 0, d = 20
# time = 500, clk = 1, d = 20
# ** Note: $finish : assert_test.sv(30)
# Time: 500 ns Iteration: 1 Instance: /test
This gets around the first unwanted failure of your assertion but I think the way you coded your assertion it is still not actually trapping the condition you're looking for.

Related

SystemVerilog X propagation issue

I'm having an issue with my SV code. I'm attempting to simulate a carry look ahead adder. However, when I look at my timing results
they show result has having an x propagated, as well as SUM.
Here is my SystemVerilog code
module fulladder (input logic i_bit1, i_bit2, i_carry,
output logic o_sum, o_carry);
assign o_sum = i_bit1 ^ i_bit2 ^ i_carry;
assign o_carry = (i_bit1 & i_bit2) | (i_carry & (i_bit1 ^ i_bit2));
endmodule
module carry_lookahead_adder
#(parameter WIDTH)
(input logic [WIDTH-1:0] i_add1,
input logic [WIDTH-1:0] i_add2,
output logic [WIDTH:0] o_result
);
logic [WIDTH:0] w_C;
logic [WIDTH-1:0] w_G, w_P, w_SUM;
//Generate full adders
genvar i;
generate for (i= 1; i<WIDTH; i++)
begin : f_loop
fulladder fi (
.i_bit1(i_add1[i]),
.i_bit2(i_add2[i]),
.i_carry(w_C[i]),
.o_sum(w_SUM[i]),
.o_carry()
);
end
endgenerate
genvar jj;
generate
for (jj=0; jj<WIDTH; jj++)
begin
assign w_G[jj] = i_add1[jj] & i_add2[jj];
assign w_P[jj] = i_add1[jj] | i_add2[jj];
assign w_C[jj+1] = w_G[jj] | (w_P[jj] & w_C[jj]);
end
endgenerate
assign w_C[0] = 1'b0; //No carry input
assign o_result = {w_C[WIDTH], w_SUM};
endmodule
and the testbench
module carry_lookahead_adder_tb (w_RESULT);
parameter WIDTH = 32;
logic [WIDTH-1:0] r_ADD_1 = 0;
logic [WIDTH-1:0] r_ADD_2 = 0;
output logic [WIDTH:0] w_RESULT;
carry_lookahead_adder #(.WIDTH(WIDTH)) carry_lookahead_inst
(
.i_add1(r_ADD_1),
.i_add2(r_ADD_2),
.o_result(w_RESULT)
);
initial
begin
$dumpfile("dump.vcd");
$dumpvars;
#10;
r_ADD_1 = 32'b00000000000000000000000000000000;
r_ADD_2 = 32'b00000000000000000000000000000001;
#10;
r_ADD_1 = 32'b00000000000000000000000000000010;
r_ADD_2 = 32'b00000000000000000000000000000010;
#10;
r_ADD_1 = 32'b00000000000000000000000000000101;
r_ADD_2 = 32'b00000000000000000000000000000110;
#10;
r_ADD_1 = 32'b00000000100000000000000000000101;
r_ADD_2 = 32'b00000000100000000000000000000110;
#10;
r_ADD_1 = 32'b11111111111111111111111111111111;
r_ADD_2 = 32'b11111111111111111111111111111111;
#10;
r_ADD_1 = 32'b00000000000000000000000000000000;
r_ADD_2 = 32'b00000000000000000000000000000001;
#10;
end
endmodule // carry_lookahead_adder_tb
Can anyone clue me into what may be causing this x? Sorry to post my full code; I'm just lost as to where the problem may be coming from.
Bit [0] of w_SUM is unknown because you are not driving it. Change the generate for loop so that the count starts from 0, not 1. Change:
generate for (i= 1; i<WIDTH; i++)
to:
generate for (i= 0; i<WIDTH; i++)
After this change, the x goes away.
The problem was that the for loop was not generating the right number of fulladder instances: you need 32, but you only got 31. There was no fulladder instance for you to connect w_SUM[0], i_add1[0], etc., to.

I don't understand this SV randomization randc behaviour

This is code for transaction class,
class transaction;
//declaring the transaction items
randc bit [3:0] a;
randc bit [3:0] b;
bit [6:0] c;
function void display(string name);
$display("-------------------------");
$display("- %s ",name);
$display("-------------------------");
$display("- a = %0d, b = %0d",a,b);
$display("- c = %0d",c);
$display("-------------------------");
endfunction
endclass
And this is code for generator class,
class generator;
rand transaction trans;
int repeat_count;
mailbox gen2driv;
event ended;
function new(mailbox gen2driv);
this.gen2driv = gen2driv;
endfunction
task main();
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
trans.display("[ Generator ]");
gen2driv.put(trans);
end
-> ended;
endtask
endclass
The value of repeat_count that I passed is 10, and here's the output:
- a = 2, b = 0
- c = 0
- a = 1, b = 9
- c = 0
- a = 9, b = 9
- c = 0
- a = 7, b = 15
- c = 0
- a = 10, b = 15
- c = 0
- a = 3, b = 1
- c = 0
- a = 13, b = 12
- c = 0
- a = 1, b = 9
- c = 0
- a = 7, b = 5
- c = 0
- a = 3, b = 15
- c = 0
But, values during randomization are not showing cyclic repetition. It is repeating itself before all possible value can occur for variables a and b.
Move the transaction constructor out of the repeat loop. Change:
repeat(repeat_count) begin
trans = new();
to:
trans = new();
repeat(repeat_count) begin
It seems new resets the initial random permutation of the range values each time it is called.
Here is a self-contained, runnable example which demonstrates the issue (and the fix):
class transaction;
randc bit [3:0] a; // 16 values: 0-15
function void display;
$display("a=%0d", a);
endfunction
endclass
class generator;
rand transaction trans;
task main;
//trans = new(); // Un-comment this line for fix
repeat (16) begin
trans = new(); // Comment out this line for fix
if (!trans.randomize()) $fatal(1, "trans randomization failed");
trans.display();
end
endtask
endclass
module tb;
generator gen = new();
initial gen.main();
endmodule
Yeah exactly to what #toolic said...initializing with new operator inside the repeat loop would create a new space every time the loop runs and thus the newly created object (transaction in your case) has no trace of the values that has been previously exercised. Thus giving out a random number making randc of no use. It though works normal with ' rand' keyword.

how to delay the beginning of a clock?

Using system verilog and new to it and to verilog,
I want to delay the start of clock by 46.666ns in a testbench.
for this I declared another signal, toggled it to 1 after 46.666 and gated my clock with it. however it is not working, and I don't understand why. any help would be very appreciated.
the code I am using:
// generate CLKXI and inject to vt
logic clk = 1;
logic clkstart = 0;
initial begin
#46.666ns clkstart = 1;
end
always
begin
if (clkstart && ~clk) #21.25ns clk = ~clk;
else if (clkstart && clk) #20.416ns clk = ~clk;
end
assign test_wrapper.dut_top.CLKXI = clk;
The problem is when clkstart is 0, your always block gets into a zero-delay infinite loop, and time cannot advance. I think what you want is
initial begin
#46.666ns
forever begin
#21.25ns clk = 0;
#20.416ns clk = 1;
end

Verilog sync counter

module syncounter(qa,qabar,qb,qbbar,overflow,ja,ka,jb,kb,clk,rst);
output qa,qabar,qb,qbbar,overflow;
input ja,ka,jb,kb,clk,rst;
reg qa,qb,qabar,qbbar,overflow;
//var1 = qa;
jkflip jk1(qa,qabar,ja,ka,clk,rst);
assign var1 = qa;
jkflip jk2(qb,qbbar,var1,var1,clk,rst);
always # (ja,ka,jb,kb)
begin
if(qa & qb)
begin
overflow = 1;
end
end
endmodule
The code for flip flop is
module jkflip(q,qbar,j,k,clk,rst);
input j,k,clk,rst;
output q,qbar;
wire j,k,clk,rst;
reg q,qbar;
always #(posedge clk)
begin
if(rst) q<=~q;
else
begin
case ({j,k})
2'b00 : q<=q;
2'b01 : q<=1'b0;
2'b10 : q<=1'b1;
2'b11 : q<=~q;
endcase
end
end
endmodule
Testbench for counter
module syncountw();
reg j0,k0,j1,k1,clk,rst;
wire q0,q0bar,q1,q1bar,overflow;
syncounter syn1(q0,q0bar,q1,q1bar,overflow,j0,k0,j1,k1,clk,rst);
always #1 clk = !clk;
//always #1 j0 =1 ;
//always #1 k0 = 1;
initial begin
clk = 1;
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
#2
j0 = 1;
k0 = 1;
end
endmodule
I am getting a error called:
Error (suppressible): (vsim-3053) C:/Modeltech_pe_edu_10.4/examples/syncounter.v(7): Illegal output or inout port connection for port 'q'.
# Time: 0 ns Iteration: 0 Instance: /syncountw/syn1/jk1 File: C:/Modeltech_pe_edu_10.4/examples/jkflip.v
# ** Error (suppressible): (vsim-3053) C:/Modeltech_pe_edu_10.4/examples/syncounter.v(7): Illegal output or inout port connection for port 'qbar'.
# Time: 0 ns Iteration: 0 Instance: /syncountw/syn1/jk1 File: C:/Modeltech_pe_edu_10.4/examples/jkflip.v
# ** Error (suppressible): (vsim-3053) C:/Modeltech_pe_edu_10.4/examples/syncounter.v(9): Illegal output or inout port connection for port 'q'.
# Time: 0 ns Iteration: 0 Instance: /syncountw/syn1/jk2 File: C:/Modeltech_pe_edu_10.4/examples/jkflip.v
# ** Error (suppressible): (vsim-3053) C:/Modeltech_pe_edu_10.4/examples/syncounter.v(9): Illegal output or inout port connection for port 'qbar'.
# Time: 0 ns Iteration: 0 Instance: /syncountw/syn1/jk2 File: C:/Modeltech_pe_edu_10.4/examples/jkflip.v
qa, qb, qabar, & qbbar are declared as reg, they should be wire. The reg type is for signals assigned by procedural blocks (i.e.: initial & always) in the scope of the current module.
var1 is assigned, but never declared.
Change:
reg qa,qb,qabar,qbbar,overflow;
//var1 = qa;
To:
reg overflow;
wire var1;
FYI: you forgot to assign qbar a value

Simulation in verilog using $monitor

I've been trying to implement full adder in Verilog. I have implemented it and it is also showing results on Isim. Only problem is that when I try to see the simulation using $monitor command, it is showing me only 1 result, not all simulation results. Here is testbench code:
module Full_adder_s2_testbench;
// Inputs
reg a;
reg b;
reg cin;
// Outputs
wire sum;
wire cout;
// Instantiate the Unit Under Test (UUT)
Full_adder_s2 uut (
.a(a),
.b(b),
.cin(cin),
.sum(sum),
.cout(cout)
);
integer i;
initial begin
// Initialize Inputs
a = 0;
b = 0;
cin = 0;
// Wait 100 ns for global reset to finish
#100;
end
always # ( a, b, cin )
begin
// generate truth table
for ( i = 0; i < 8; i = i + 1 )
// every 10 ns set a, b, and cin to the binary rep. of i
#10 {a, b, cin} = i;
$monitor( "%d ns: a + b + cin = %b + %b + %b = cout sum = %b %b",
$time, a, b, cin, cout, sum );
// stop 10ns after last change of inputs
#10 $stop;
end
endmodule
And here is result in ISIM:
# run 1000 ns
Simulator is doing circuit initialization process.
Finished circuit initialization process.
400 ns: a + b + cin = 1 + 1 + 1 = cout sum = 1 1
Stopped at time : 410 ns : in File "E:/Namal/FYP/My work/XILINX/Full_adder_s2/Full_adder_s2_testbench.v" Line 66
$monitor is only meant to be setup once and will trigger every time a signal changes, try using $display since you already have the statement inside of your always #*.
While learning Verilog I would encourage you to use begin end liberally. The issue is that only 1 line was in the for loop, the $display/$monitor was outside and so only executed once at the start.
always #* begin
// generate truth table
for ( i = 0; i < 8; i = i + 1 ) begin //<-- Added begin
// every 10 ns set a, b, and cin to the binary rep. of i
#10 {a, b, cin} = i;
$display( "%d ns: a + b + cin = %b + %b + %b = cout sum = %b %b", $time, a, b, cin, cout, sum );
end //<--Added end
// stop 10ns after last input
#10 $stop;
end
Full example on EDA Playground.
NB: it is best not to use manual sensitivity lists any more replace always # ( a, b, cin ) with always #*. This will result in quicker refactoring and lowering the chance of RTL to gates simulation mismatch.