# Beginning and end of a time step

#### stanford

##### Full Member level 2
Non-blocking assignment evaluates the RHS expression at the beginning of a time step and schedules the LHS update to take
place at the end of the time step.

Let's say we have
Code:
always_ff @(posedge clk)
c <= a | b;
If we have a non-blocking assignment inside a always_ff block, does the beginning of a timestep mean the current clock edge and the end of the time step mean the next clk edge? So that (a | b) is evaluated in the current clock edge but the assignment happens in the next clk edge?

Thanks!

#### dave_59

This is not the correct way of thinking about it. The RHS of non-blocking assignment gets evaluated as soon as the previous statement completes. The LHS c gets scheduled to update after in another region after everything in the current active region completes. This guarantees that any other always_ff reading c does not read the updated value until the next clock cycle.

#### ThisIsNotSam

Non-blocking assignment evaluates the RHS expression at the beginning of a time step and schedules the LHS update to take
place at the end of the time step.

Let's say we have
Code:
always_ff @(posedge clk)
c <= a | b;
If we have a non-blocking assignment inside a always_ff block, does the beginning of a timestep mean the current clock edge and the end of the time step mean the next clk edge? So that (a | b) is evaluated in the current clock edge but the assignment happens in the next clk edge?

Thanks!
In my experience, people tend to misunderstand the concepts of delta cycles and how a simulator works internally. I see students that struggle with the concepts of sequential vs combinational, blocking vs non-blocking. On top of that, they try to understand the simulator internals and it's just a big mess.

So, is there a reason why you are asking this question?

#### stanford

##### Full Member level 2
This is not the correct way of thinking about it. The RHS of non-blocking assignment gets evaluated as soon as the previous statement completes. The LHS c gets scheduled to update after in another region after everything in the current active region completes. This guarantees that any other always_ff reading c does not read the updated value until the next clock cycle.
I'm still confused. Let's say in the waveform,
Code:
clk edge 0: a = 0, b = 0
clk edge 1: a = 1, b = 1
Now, at clk edge 1, the always_ff will be triggered and the RHS will be evaluated (a || b) = (1 || 1) = 1, or will it evaluate with the previous value (a || b) = (0 || 0) = 0? If the latter is true, how does the simulator ensure it uses the values before a or b changes?

#### dave_59

If you use blocking assignments to a and b, you have a race condition. That is the whole point of using non-blocking assignments when one always process writes, and another always process reads the same variable, and both blocks are synchronized to the same clock edge.

#### stanford

##### Full Member level 2
If you use blocking assignments to a and b, you have a race condition. That is the whole point of using non-blocking assignments when one always process writes, and another always process reads the same variable, and both blocks are synchronized to the same clock edge.
Let me give a more specific example to show what I'm confused about.

Code:
input in;

always_ff @(posedge clk)
c <= a | b;

always_comb begin
a = in;
b = a;
end

1     2     3
clk     __|--|__|--|__|--|__

in       _______|-------------

c         ____________|------
In the wave, we see the input 'in' change from 0 -> 1 at clk edge 2. At edge 2, isn't there a race condition as to which always block gets executed first? If always_ff is executed first, it will see a = b = 0 and c = (0 | 0) = 0 at edge 2. If always_comb block execute first, a = b = 1 and c = (1 | 1) at edge 2. I'm trying to understand the sequence of event that takes place at edge 2. If you could use this example to explain, that might help clear up my confusion better. thanks!

Last edited:

#### dave_59

You have to look at how every signal changes value. Assuming every signal that changes on a clock edge uses a non-blocking assignment, there will be no race conditions. And you may have to look through several layers of combinational logic to find the synchronous change. This means flattening the module hierarchy and finding where "in" gets its new value from.

#### stanford

##### Full Member level 2
Code:
input in;

always_ff @(posedge clk)
c <= a | b;

always_ff @(posedge clk)
in <= foo;

always_comb begin
a = in;
b = a;
end

1     2     3
clk     __|--|__|--|__|--|__

foo    ___|---------------------

in       _______|-------------

a        _______|-------------

b        _______|-------------

c         ____________|------
Is the order of events below correct at clk edge 2? could you please point out clearly which step is wrong and how it should change?

1. positive clk edge 2 triggers always_ff block to execute first
2. a) RHS of always_ff is evaluated to (a | b = 0 | 0 = 0), assignment of c is delayed until end of time step
b) RHS of always_ff is evaluated to (foo = 1), assignment of in is delayed until end of time step
3. Since input 'in' does not change until the end of time step, always_comb does not get triggered?
4. LHS of non-blocking statements is assigned to the new value (c = 0, in = 1) at clk edge 2
5. always_comb gets triggered because 'in' changed and a = 1 and b = 1 gets the new value at edge 2

Last edited:

##### Super Moderator
Staff member
It seems like you think the always_comb is going to be triggered at the same time as the always_ff resulting in some sort of race condition.

The sensitivity for the always_ff is the low to high transition of clk. The always_comb is sensitive to changes on in and a (RHS).

So as soon as in changes at the end of the always_ff @(posedge clk) timestep the always_comb gets triggered.

Maybe you should read this paper by Cummings on the event region. It's somewhat easier to understand than Section 4 of the 1800-2017 IEEE standard.

#### stanford

##### Full Member level 2
It seems like you think the always_comb is going to be triggered at the same time as the always_ff resulting in some sort of race condition.

The sensitivity for the always_ff is the low to high transition of clk. The always_comb is sensitive to changes on in and a (RHS).

So as soon as in changes at the end of the always_ff @(posedge clk) timestep the always_comb gets triggered.

Maybe you should read this paper by Cummings on the event region. It's somewhat easier to understand than Section 4 of the 1800-2017 IEEE standard.
Can you take a look at my steps above? I think you are saying the same thing.

#### stanford

##### Full Member level 2
Code:
input in;

always_ff @(posedge clk)
c <= a | b;

always_ff @(posedge clk)
in <= foo;

always_comb begin
a = in;
b = a;
end

1     2     3
clk     __|--|__|--|__|--|__

foo    ___|---------------------

in       _______|-------------

a        _______|-------------

b        _______|-------------

c         ____________|------
Is the order of events below correct at clk edge 2? could you please point out clearly which step is wrong and how it should change?

1. positive clk edge 2 triggers always_ff block to execute first
2. a) RHS of always_ff is evaluated to (a | b = 0 | 0 = 0), assignment of c is delayed until end of time step
b) RHS of always_ff is evaluated to (foo = 1), assignment of in is delayed until end of time step
3. Since input 'in' does not change until the end of time step, always_comb does not get triggered?
4. LHS of non-blocking statements is assigned to the new value (c = 0, in = 1) at clk edge 2
5. always_comb gets triggered because 'in' changed and a = 1 and b = 1 gets the new value at edge 2
I understand the theory, but I just wanted to make sure my understanding of the real life example makes sense. If someone has a good understanding of this topic, could you please confirm or correct it? thanks