Understanding how exactly 'assert' works

Hi.
I have encountered an issue that puzzles me when using ‘assert’, specifically when trying to assert the value of of a flip-flop after the sampling edge (rising edge in my case). If I place an ‘always’ block that prints all the changes on a flip-flop output I can see that at the rising edge it equals the expected value. However if I try to ‘assert’ the value at the same moment in time, the assert ‘thinks’ that flip flop did not yet sample, only a a moment later (that can be arbitrarily small). ‘if’ behave like ‘assert’ (trying to compare to the expected value at that moment). Below is my sample code, and attached an image showing the contradiction between the assert and the prints. I am using Modelsim as simulator. 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 //Clock cycle
	@(posedge clk);
	assert(result == 1'b1);	_**//this fails at 70ns, meaning that result != 1**_
	
 end
 
 always @ (result)
  begin
	$display("result=%h time=%t",result, $realtime); _**//This will print that result ==1 at 70ns**_
  end

endmodule

In reply to ariy:

You are making assignment to result with a non-blocking assignment(NBA). An immediate assertion executes like any other procedural statement—there is no sampling. You will get the old value of result. But thealways @(result) gets triggered after the NBA in the same time slot, so it has the updated value.

Also, your initial block has races because you are changing resetb and selector without using NBAs and making the changes coincident with the posedge clk. You need to use NBAs in your stimulus or make the changes away form the posedge clk.

In reply to dave_59:

Thank you very much for your response!

However I changed the example_t module to use a blocking assignment (=) and the outcome is the same.
Generally, I thought that NBAs will affect a module internally, i.e if I have a few flops inside the module and I use NBAs, they will all be assigned with values at the same time (roughly) while with blocking statements it will happen in sequence. However here once “selector” has changed on the outside, I expect the flop (result) to sample after one cycle, and I did wait #20 explicitly plus added @posedge for absolute sync.

I admit I am new to digital design and perhaps I have big gaps of fully understanding how NBAs work and also about how exactly procedural statements work.
Would be grateful if you can elaborate.

Thanks!