Reputation: 319
2 questions
always_comb
, is this possible since there is no clocking reference for an always_comb
block - as seen here -> [sequential logic in always_comb
]Thanks :D
Upvotes: 0
Views: 604
Reputation: 7573
Regarding question 1. non-blocking assignments don't necessarily imply sequential behavior. Blocking/non-blocking assignments are a simulation construct. Let's look at a small example to understand it better:
module some_gate(
input logic a,
input logic b,
output logic c,
output logic d
);
// c is 'a or b'
// d is 'a and c'
endmodule
We have two input signals and two output signals. We want to model a composite gate.
In classical Verilog style, we would write the following:
always @(a or b) begin
c = a || b;
d = a && c;
end
This means, whenever a
or b
changes, compute the values of c
and of d
. Because we used a blocking assignment for c
, the value we computed here gets "propagated" to the computation of d
. This essentially means that we've chained the logic described by d
to come after the logic for c
.
We can test this using a little testbench:
module test;
logic a, b, c, d;
some_gate dut(.*);
always @(a or b or c or d)
$display("[%0d] a = %b, b = %b, c = %b, d = %b", $time(), a, b, c, d);
initial begin
#1 a = 1;
#1 b = 1;
#1 a = 0;
#1 $finish();
end
endmodule
If we simulate this, we'll get:
[1] a = 1, b = x, c = x, d = x
[1] a = 1, b = x, c = 1, d = 1
[2] a = 1, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 0
This is because the display process triggers once for each change of the variables. a
gets changed from x
to 1
, but c
haven't been updated yet. Then c
and d
get updated in the same time step. Later on, we change b
, but this doesn't trigger any changes on c
or d
. Then, we change a
again and later in the same time slice, d
gets updated.
It's a bit redundant to have to specify the sensitivity list, since if we know that we want combinatorial logic, we should re-trigger the process whenever something on the right hand side of an assignment changes. This is what always_comb
is for.
We can re-write our code using always_comb
, by just getting rid of the sensitivity list:
always_comb begin
c = a || b;
d = a && c;
end
Running this code will lead to the same prints:
[1] a = 1, b = x, c = x, d = x
[1] a = 1, b = x, c = 1, d = 1
[2] a = 1, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 0
Now comes the interesting part. We can model the exact same circuit using always_comb
and non-blocking assignments:
always_comb begin
c <= a || b;
d <= a && c;
end
Running this code, though, will produce slightly different prints:
[1] a = 1, b = x, c = x, d = x
[1] a = 1, b = x, c = 1, d = x
[1] a = 1, b = x, c = 1, d = 1
[2] a = 1, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 1
[3] a = 0, b = 1, c = 1, d = 0
Notice that in the first time step we have 3 prints, instead of one. Let's look closer at what happens here. First, we change a
, but c
and d
haven't been updated yet. Second, c
gets updated, but d
stays the same. This is because of the non-blocking assignment on c
, leading to d
"seeing" the "old" value of c
. Third, after c
was officially updated, the tool schedules an update on d
and we see the last print for the first time step. The rest is the same as in the previous cases.
Remember from a previous paragraph that always_comb
re-triggers whenever something on the right hand side of an assignment changes. The trick here is that this always_comb
actually behaves like:
always @(a or b or c) begin
c <= a || b;
d <= a && c;
end
This is non-standard Verilog, but would still model the same logic. Notice that c
has been explicitly added to the sensitivity list. If we omit this, then we'd be describing a latch. This style is discouraged, because it's easy to get wrong (i.e. forget to add intermediate logic nodes to the sensitivity list).
The key takeaway point here is that blocking or non-blocking assignments don't either describe sequential or combinatorial logic. It's the entire context (i.e. the sensitivity lists that determine when they get executed) that controls what logic circuit gets inferred from the code.
The full code example is available on EDAPlayground.
Regarding question 2. this is tool specific and this isn't the forum for this. You should ask this on the vendor's website.
Upvotes: 4