Why is output not driven through interface clocking block? - interface

In the example below, I am trying to pulse the data output on my_interface. However, data always remains 0.
interface my_interface(
input clock,
output data);
clocking cb #(posedge clock);
output data;
endclocking
endinterface // my_interface
module test;
logic clock;
wire data;
my_interface my_interface(.*);
initial begin
clock = 0;
#1 $display("%0d data:%0d", $time, data);
#10;
my_interface.cb.data <= 1;
#1 $display("%0d data:%0d", $time, data);
#(my_interface.cb);
my_interface.cb.data <= 0;
#1 $display("%0d data:%0d", $time, data);
#20 $display("%0d data:%0d", $time, data);
$finish();
end
always #5 clock = ~clock;
endmodule
The sim output is:
# 1 data:z
# 12 data:z
# 16 data:0
# 36 data:0
I'd like to understand why is data never 1 in the example above?
I can fix the issue by replacing #10 with #(my_interface.cb);, but I don't know why this fix works.
Code and results on EDA Playground: http://www.edaplayground.com/s/4/198

It's very dangerous to use any other blocking events other than #(my_interface.cb) when driving or sampling clocking block signals The reason your code is behaving that way is because of this statement in section 14.16 Synchronous drives:
It is possible for a drive statement to execute at a time that is not
coincident with its clocking event. Such drive statements shall
execute without blocking, but shall perform their drive action as if
they had executed at the time of the next clocking event.
In your example, your two drive statements wind up driving in the same cycle, and last write wins.
So the rule is only to use #(my_interface.cb) to block a process that is driving or sampling clocking block signals. That includes not using wait(cb.signal) and instead using #(cb iff (cb.signal))

Related

How does always_ff works?

I do have a problem understanding how always_ff works in a way of creating a mesh of logic gates.
What do I mean ? When I use always_comb like here :
module gray_koder_dekoder(i_data, i_oper, o_code);
parameter LEN = 4;
input logic [LEN-1:0] i_data;
input logic i_oper;
output logic [LEN-1:0] o_code;
int i;
always_comb
begin
o_code = '0;
i = LEN-1;
if (i_oper == 1'b1) // 1'b1 - operacja
begin // kodowania
o_code = i_data ^ (i_data >> 1);
end
else // dla kazdej innej wartosci
begin // realizuj dokodowanie
o_code = i_data;
for (i=LEN-1; i>0; i=i-1)
begin
o_code[i-1] = o_code[i]
^ i_data[i-1];
end
end
end
endmodule
So how do I see it.
At the beggining the program sees that output is 0000,
now if the i_oper is equal 1 so the input is 1 then it checks changes the o_code to i_data ^ (i_data >> 1) so now the program want's to do combination of logic gates for this operation but if the i_oper is equal 0 then the program makes another set of logic gates to get different o_code.
So the always_comb gives the final result for every bite in the i_data that results in o_code.
So my teacher said that always_comb is "blocking" but always_ff is not "blocking" I don't get it ...
So the always_ff doesn't give the final result of logic gates for the input to get a specific output ?
Another example of always_comb :
module gray_dekoder (i_gray, o_data);
parameter LEN = 4;
input logic [LEN-1:0] i_gray;
output logic [LEN-1:0] o_data;
always_comb
begin
o_data = i_gray;
for (int i=LEN-1; i>0; i=i-1)
o_data[i-1] = o_data[i] ^ i_gray[i-1];
end
endmodule
So at the beggining the program sees that the output is 0000 so it will make a set of logic combination to have 0 at the end. Then he sees loop for that modifies the output so the program checks every bit of the input like bit nr 3 then nr 2 then nr 1 etc. and creates for every input specific output so now the output is not 0000 anymore but set of instructions that modifies the output made from loop "for"
so the always comb gives a final result from the analizing the whole code from top to bottom of "always_comb" and creates a set of instructions/set of logic gates that helps it. Because always_comb overwrites the previous instructions like 0000 it was a basic instruction but then was overwrited by the loop "for"
But maybe I think wrongly because if instruction doesn't overwrite the 0000 instruction like here :
module replace(i_a, i_b, o_replaced, o_error);
parameter BITS = 4;
input logic signed [BITS-1:0] i_a, i_b;
output logic signed [BITS-1:0] o_replaced;
output logic o_error;
int i;
always_comb
begin
o_replaced = '0;
if(i_b < 0 || i_b > BITS)
begin
o_error = 1;
o_replaced = 'x;
end
else
begin
i = i_b;
o_replaced = i_a;
o_replaced[i-1] = 1;
o_error = 0;
end
end
endmodule
I have here 0000 output that isn't overwrited for "else" So I don't know what happens then.
I think of always_comb as an "final result" that gives a set of instructions how to create logic gates. But the final result is at the end, so if something changes then the beggining result doesn't matter "overwrited" but with if loop it doesn't work with my mind set.
So always_ff I heard that it doesn't give a final result that it can stop at any point not like in always_comb that the program analysis from top to bottom.
Verilog is designed to represent behavior of hardware and it is not a regular programming language. It operates different semantics.
At a very top glance, hardware consists of combinational logic and flops (and latches). From the other point of view hardware is a set of parallel functions which are synchronized across design by clocks. This means that at a clock edge a lot of hardware devices start working in parallel and they should produce results by the next clock edge. Those results could be used by other functions at the next clock cycle.
Roughly, combinational logic defines a function, flops provide synchronization.
In verilog all those devices are described with always blocks. TUse of edges, e.g., #(posedge clk) provides synchronization points and usually defines flops in the code. A simple function and a flop look like the following.
// combinational logic
always #* // you can use always_comb instead
val = in1 & in2; // a combinational function
// flop
always #(posedge clk) // you can use always_ff instead
out <= val; // synchronization
So, in the example val is calculated by the combinational function and its synchronous out is made available to other functions to be used by a flop. You can see progression of clocks and results in a waveform.
So, this is what always_ff is doing, just providing synchronization and expressing flops for synthesis.
In general, always, always_comb, always_ff and always_latch are identical. The last three are system verilog blocks and just provide additional hints to the compiler which can run additional checks on them. I intentionally used just always blocks in my example to show that. There are some other conditions which need to be programmed to cleanly express the intention. So, your assertion about different working of always_ff has no base. It works the same as other always blocks.
What I think confuses you, is use of blocking (=) and non-blocking (<=) assignments. It does not matter for synthesis which one you use, but it matters for simulation. The difference is described and numerous documents and examples. To understand it properly you need to look into verilog simulation scheduling semantics.
But the rule of thumb is that you should use non-blocking assignments (<=) in flops and use blocking (=) in combinational logic. In flops '<=' allows simulating real behavior of flops. Remember that hardware is a massively-parallel evaluation engine. Consider the following example:
always_ff #(posedge clk) begin
out1 <= in1;
out2 <= out1;
end
The above example defines at least two flops working at posedge clk. out1 and out2 must be synchronized at this clock. It means, the flops have to catch values which existed before the edge and present them after the edge. So, for out1 the value existed before the edge is in1, evaluated by a combinational logic. What would be the value of out2? Which value existed before the edge? Apparently, the value of the out1 before it gets changed to the new value of in1.
clk ___|---|___|---|___
in1 0
out1 x 0 << new value of in1
out2 x << old value of ou1
.
in1 1 .
out1 . 1 << new value of in1
out2 . 0 << old value of ou1
So, after evaluation of the block, the at the first edge the value of out2 will be 'x' (previous value of out1), at the second clock edge it will finally get value of 'in1' as it existed at the previous clock cycle.
I hope it would make your understanding a bit better.

Understanding how exactly 'assert' works

I am facing a puzzling behavior of Systemverilog 'assert' when trying to verify a flip-flop had sampled the input value on a rising edge. It seems that the assert will pass if I add an arbitrarily small delay AFTER the rising edge, but not right on it. However if I add an 'always' block that prints the value of the output at the rising edge time, it will show the correct value. BTW and 'if' comparison behaves like 'assert'. I am using Modelsim as my simulator. Below is my code with inline comments to illustrate the problem. Please also look at the image in the link for further illustration.
Thanks.
`timescale 1ns/1ns
module example_t(input logic clk, input logic resetb, input logic selector, output logic result);
always_ff # (posedge clk or ~resetb)
if(~resetb)
result <= 0;
else
result <= selector ? 1'b1 : 1'b0;
endmodule
module tst();
logic clk;
logic resetb;
logic selector;
logic result;
example_t example(.clk(clk), .resetb(resetb), .selector(selector), .result(result));
//Simulate a clock
always
#10 clk = ~clk;
initial
begin
clk = 0;
resetb = 0; //Reset
selector = 0;
#20
#(posedge clk);
resetb = 1;
#20
#(posedge clk);
selector = 1;
#20 //**At this time we are at 70ns**
#(posedge clk); //**We are already at the posedge, but adding just in case**
assert(result == 1'b1); //**This will fail, meaning result[enter image description here][1]==0 at 70ns**
end
always # (result)
begin
$display("result=%h time=%t",result, $realtime); //**This will print the value at 70ns as 1**
end
endmodule
You have a couple of problems in the code.
The problem is a race on the selector (and on resetb). You modify the signals with blocking assignments at the same simulation cycle as you use them. Depending on simulator or simulation conditions the update can happen before or after its use. As a result, synopsys and cadence have result ready at time 50, making assertion happy. On the other hand, mentor an aldera make result ready at 70 only and cause assertion failure at this time. You can avoid this issue and use nbas and make every compiler to have results ready at 70.
There is a slightly different source of race which involves the immeditate assertion in the initial block. The conditional expression in it will be executed as a part of the procedural block flow in active region, before non-blocking assignments. It still sees the old value of the result and is evaluated to false.
The way around it is to set 'selector' and resetb way before the posedge. The following will make everyone happy. The result will be ready at 50 and assertion at 70 will be happy as well.
initial
begin
clk = 0;
resetb = 0; //Reset
selector = 0;
#15
resetb = 1; // set it before the next posedge
#(posedge clk); // wait for posedge
#10
selector = 1; // set it before the next edge
#20 //**At this time we are at 70ns**
#(posedge clk); //**We are already at the posedge, but adding just in case**
assert(result == 1'b1) $info("Assertion is ok at %0t", $realtime); //**This will fail, meaning result[enter image description here][1]==0 at 70ns**
#20 $finish;
end

Unsuccessful assignment in SystemVerilog

I write ldpc_if.sv and ldpc_transaction.sv as follows.
"ldpc_if.sv"
interface ldpc_if#(parameter COLS=9216, parameter ROWS=1024) (input clk, input reset);
logic [COLS-ROWS-1:0] en_enq_data;
logic en_enq_valid;
logic en_enq_ready;
logic [ROWS-1:0] en_deq_data;
logic en_deq_valid;
logic en_deq_ready;
logic [COLS-1:0] de_enq_data;
logic de_enq_valid;
logic de_enq_ready;
logic [COLS-1:0] de_deq_data;
logic de_deq_valid;
logic de_deq_ready;
endinterface
"ldpc_transaction.sv"
class ldpc_transaction#(parameter WIDTH=8192) extends uvm_sequence_item;
rand bit [WIDTH-1:0] data;
bit [8191:0] encode_data_in;
bit [1023:0] encode_data_out;
bit [9215:0] decode_data;
`uvm_object_utils(ldpc_transaction)
function new(string name = "ldpc_transaction");
super.new();
endfunction
endclass
And I write ldpc_monitor.sv to monitor interface.
task ldpc_monitor::collect_one_pkt(ldpc_transaction tr);
while(1) begin
#(posedge vif.clk);
if(vif.en_enq_valid && vif.en_enq_ready) break;
end
tr.encode_data_in <= vif.en_enq_data;
while(1) begin
#(posedge vif.clk)
if(vif.en_deq_valid && vif.en_deq_ready) break;
end
tr.encode_data_out <= vif.en_deq_data;
while(1)begin
#(posedge vif.clk)
if(vif.de_deq_valid && vif.de_deq_ready)begin
break;
end
end
tr.decode_data <= vif.de_deq_data;
$display("tr.decode_data = %0h", tr.decode_data);
$display("vif.de_deq_data = %0h", vif.de_deq_data);
endtask
vcs compiles all files successfully. However, tr.decode_data is always displayed as zero. But, vif.de_deq_data is correct. Why is vif.de_deq_data not assigned to tr.decode_data.
It's because the $display you've used to display your transaction is blocking. Conversely, you've used an non-blocking assignment to set tr.decode_data.
Thus, your $display statement actually gets executed before your assignment. Getting a 0 is just an artefact of your simulator - could be any random stuff in the memory assigned to that variable (though most simulators just reset to 0).
Quick search revealed this useful example which illustrates exactly your problem.
https://verificationguide.com/systemverilog/systemverilog-nonblocking-assignment/

SystemVerilog 2-bit register decoding problem

I have two 2-bit inputs and an output of 1-bit. So what I'm trying to do is code a next state value with taking AND of the two inputs, then use non-blocking <= to assign that value into register xy_r, which is now two D-flipflops. So I am seeking to get always the XOR-value of the xy_r previous clock edge as my output xor_out. I suppose that the way below is not the right one?
It worked in the simulation but then again in RTL Synthesis I didn't end up with an XOR gate but a third flip-flop so it seems like xor_out is treated as a register.
I suppose I can't use assign outside of the if-else-statements because in that case the output wouldn't follow the xy_r previous state but the present.
Can you please assist me how to solve this issue, if there is a somewhat simple way.
module 2ffsxor
(input logic clk,
input logic rst_n,
input logic [1:0] x_in,
input logic [1:0] y_in,
output logic xor_out
);
logic [1:0] xy;
logic [1:0] xy_r;
always # (posedge clk or negedge rst_n)
begin
if (rst_n == '0)
xy_r <= '0;
else
begin
xy = x_in & y_in;
xy_r <= xy;
xor_out = xy_r[0] ^ xy_r[1];
end
end
endmodule
And here's a schematic what it should be:
Schematic 2ffsxor
Synthesis treats this code as if you had wrote
begin
xy_r <= x_in & y_in;
xor_out <= xy_r[0] ^ xy_r[1];
end
That's two stages of registers. But since you assigned xor_out with a blocking assignment, you have a simulation race condition for any process that tries to read xor_out. The race would be from the result from the previous values of xy_r and the result before that!
If you use an continuous assign statement outside this always block, any process reading xor_out on the clock edge will see the result from the previous state of xy_r.
But why not write this as on register:
always # (posedge clk or negedge rst_n)
begin
if (rst_n == '0)
xor_out <= '0;
else
begin
xy = x_in & y_in;
xor_out <= xy[0] ^ xy[1];
end
end

Verilog counter counts wrong

I'm trying to implement a counter that counts up an internal value every clock-pulse with an input value.
module Counter(in, clk, out);
input clk;
input [7:0] in;
wire clk;
wire [7:0] in;
output [7:0] out;
reg [7:0] out;
always # (posedge clk) begin
out <= out + in;
end
endmodule
The output I get is correct most of the time but sometimes the counter does not increment as expected. Here is a link to a waveform of the output. As can be seen the counter jumps from 5 to 10 even though in is 3. Can someone help me?
Your output appears to be in octal in the waveform, or base 8 (I'm guessing this is the 'O 000' in the second column).
In this case 'd5 + 'd3 = 'd8 ('o010). So everything seems to be working correctly.