This was an interview question asked by a top 10 company of US.
Code 1:
always @(a or b or sel) begin
if (sel == 1)
c = a;
else if (sel == 0)
c
Yes, this will still synthesize to a multiplexer1. A synthesis tool will interpret this RTL as if the sensitivity list were complete.
However, a large issue here arises because you will see different behaviour in your RTL simulations compared to the gate-level simulations/the actual silicon. In your RTL simulation, c
will only change if a
or b
change. If only the select-signal sel
of the multiplexer changes, your output c
will not change in RTL simulations.
When intending to create combinatorial logic, it is generally advisable to use
always @(*)
or
always_comb
if you can use SystemVerilog. This has the advantage that you never run into sensitivity-list related simulation/synthesis mismatches. The latter keyword furthermore has the advantage that you explicitly tell tools that you want to create combinatorial logic (rather than unintended latches, for example).
Finally: Mills & Cummings wrote a great paper on RTL Coding Styles That Yield Simulation and Synthesis Mismatches. This issue, among many others, is described here very well. I can greatly recommend to take a look at this paper!
As you indicated in your comment, you were wondering what would happen is sel === 1'bx
. To better understand this matter, I highly recommend reading I'm Still In Love With My X! by Stuart Sutherland. I will give a very brief summary on your particular example here.
Conditional blocks in SystemVerilog are prone to X-optimism, which is defined in the aforementioned paper as:
X-optimism has been defined [...] as any time simulation converts an X value on the input to an operation or logic gate into a 0 or 1 on the output. [...] SystemVerilog can be overly optimistic, meaning an X propagates as a 0 or 1 in simulation when actual silicon is still ambiguous.
When looking at your code, we will see that an sel === 1'bx
will not propagate to c
. Rather, the simulator will hold the previous value of c
and mimic a latch. This is overly X-optimistic and is a simulation mismatch with actual silicon, since the select line will not be X here: a signal is either 1 or 0. In other words, this will not be a latch in silicon!
One solution could be to make the multiplexer X-pessimistic in simulation, so that we would detect this undetermined state. To do so, we assign X to c
when sel
is neither 0, nor 1:
always_comb
if (sel)
c = a;
else if (!sel)
c = b;
`ifndef SYNTHESIS // see footnote 2
else
c = 'x;
`endif
This has the problem, however, that it is overly pessimistic. When both a
and b
have the same value, we would unambiguously know what value c
would have in actual silicon (regardless of the value of sel
).
The aformentioned paper gives the conditional operator (? :
) as a possible solution:
condition ? expression1 : expression2;
If the condition evaluates to unknown, the operator does a bit-by-bit comparison of the values of expression1 and expression2. For each bit position, if that bit is 0 in both expressions, then a 0 is returned for that bit. If both bits are 1, a 1 is returned. If the corresponding bits in each expression are different, or Z, or X, then an X is returned for that bit
So, by using the code below, we would have a compromise between the two aforementioned solutions:
always_comb
c = sel ? a : b;
The downside here is that the conditional operator is not suited for more complex expressions.
The three approaches summarized:
╭───────────╥─────────────────────────────────────────────────╮
│ input ║ sel(t) │
├───╥───╥───╫────────────┬─────────────┬────────────┬─────────┤
│sel║ a ║ b ║ optimistic │ pessimistic │ compromise │ silicon │
╞═══╬═══╬═══╬════════════╪═════════════╡════════════╪═════════╡
│ X ║ 0 ║ 0 ║ sel(t-1) │ X │ 0 │ 0 │
│ X ║ 0 ║ 1 ║ sel(t-1) │ X │ X │ 0/1 │
│ X ║ 1 ║ 0 ║ sel(t-1) │ X │ X │ 0/1 │
│ X ║ 1 ║ 1 ║ sel(t-1) │ X │ 1 │ 1 │
└───╨───╨───╨────────────┴─────────────┘────────────┴─────────┘
In his paper, Sutherland gives another solution with which I agree: It is best to use assertions to detect design issues which cause X, instead of letting X propagate through the design and spending a lot of time finding the root cause. For your particular code, this could look like this:
always_comb
begin
assert (!$isknown(sel))
else $error("sel = X!");
if (sel)
c = a;
else if (!sel)
c = b;
end
1: I assume here that sel
is a 1-bit signal. Otherwise, you would end up with a latch
2: See my answer here for some more information on the SYNTHESIS
macro identifier.