Using 'x in sequantial logic
Yes, Verilog-wise, you could use this syntax. However, in your particular example, it does not really make sense to do this and you could consider it to be a bad coding practive. Instead of explicitly assigning 'x, you can also omit the asynchronous reset.
The following two processes will synthesise to the same flip-flop. I would personally recommend to use the latter style.
// Assigning 'x to tell synthesis tool that there is no reset value
always @(posedge clk or negedge reset_n)
if(~reset_n)
reg_1 <= 'x;
else
reg_1 <= reg_1_next;
// Omitting the asynchronous reset from the sensitivity list to tell
// synthesis tool that there is no reset
always @(posedge clk)
reg_1 <= reg_1_next;
In general: if you have different variables which must be either resetable or non-resetable, you should split their assignment into different always-blocks. This generally makes your code better readable. See the example below:
// Do NOT do this
always @(posedge clk or negedge reset_n)
if(~reset_n)
begin
vld <= 1'b0;
reg_1 <= 'x;
end
else
begin
vld <= vld_next;
reg_1 <= reg_1_next;
end
// Do this
always @(posedge clk or negedge reset_n)
if(~reset_n)
vld <= 1'b0;
else
vld <= vld_next;
always @(posedge clk)
reg_1 <= reg_1_next;
Bonus
Having said that, there are cases where it could make sense to assign 'x in the reset condition to tell the synthesis tool to not generate resetable flops for particular variables. Please take a look at this answer: https://stackoverflow.com/a/21477457/7949378
Lets create an example, based on this answer. Lets say you have a struct with 1 valid-signal (vld) and 2 data signals (data_a and data_b). The data is only valid when vld is 1'b1. In other words, we can save area by only resetting vld and not resetting data_a and data_b.
Now, we want to use the full potential of structs, and simply assign the full struct instead of the seperate members (see struct_example_q <= struct_example_next;). This means we cannot split this always-block into two seperate processes (like I recommened before). In that case, we must explicitly tell the synthesis tool to not reset the data signals.
See the code below:
typedef struct {
logic vld;
logic [31:0] data_a;
logic [31:0] data_b;
} struct_example_t;
struct_example_t struct_example_next;
struct_example_t struct_example_q;
always @(posedge clk or negedge reset_n)
if (!reset_n)
begin
/**
* Only reset the valid-bit
* We could use '{default:'x} to simplify this even further
**/
struct_example_q.data_a <= 'x;
struct_example_q.data_b <= 'x;
struct_example_q.vld <= 1'b0;
end
else
begin
struct_example_q <= struct_example_next;
end
Using 'x in combinatorial logic
Lets first look at your RTL:
always_comb begin
case(state):
STATE_1:begin
out_1 = 1;
out_2 = x;
end
STATE_2:begin
out_1 = x;
out_2 = 0;
end
end
I want to note that this is not really the best example. Assuming that the FSM is full—i.e., that STATE_1 and STATE_2 are the only two states state can take—you would achieve exactly the same with the code below, assuming you don't case anyway about out_1 and out_2 in the other states.
always_comb begin
out_1 = 1;
out_2 = 0;
end
Now, for the sake of the example, lets assume that we cannot rewrite this. In that case, you should set default values before your case-statement. This prevents the synthesis logic from infering latches in your don't-care state, but it also helps you to not run into issues with 'x once you start doing gate-level simulations (GLS). Using your example, you RTL would look like the code below. (Again note that the case here is kind of redundant.)
always_comb begin
out_1 = 1;
out_2 = 0;
case(state):
STATE_1:begin
out_1 = 1;
end
STATE_2:begin
out_2 = 0;
end
end
You will see that this strategy makes sense once you have more elaborate FSMs.
Bonus
I want to give an example where using unique or priority can make sense (instead of using 'x as default value). Take a look at the RTL below and assume that select == 3'b0 will never occur:
always_comb
begin
out_1 = 'x;
case (1'b1)
select[0]: out_1 = a & b;
select[1]: out_1 = a ^ b;
select[2]: out_1 = a | b;
endcase
end
Setting a default value for out_1 will prevent the logic from inferring a latch (since it does not know that select == 3'b0 can never occur). Furthermore, the 'x here will help the synthesis tool to optimize this logic (not necessarily w.r.t. area!). However, as we discussed before, using 'x is generally considered to be bad practice.
Instead of using a default value, you can use the priority keyword to tell the synthesis tool that all valid cases have been listed and that the tool must evaluate your cases in order. Because of that, the following case will also be considered as full:
always_comb
priority case (1'b1)
select[0]: out_1 = a & b;
select[1]: out_1 = a ^ b;
select[2]: out_1 = a | b;
endcase
If you, additionally, can be sure that select is a onehot signal ($countones(select) == 1), you can use the unique keyword. This will tell the synthesis tool that this is full-parallel case
always_comb
unique case (1'b1)
select[0]: out_1 = a & b;
select[1]: out_1 = a ^ b;
select[2]: out_1 = a | b;
endcase
Note that simulators will trry to enforce these assumptions by throwing errors at you if you violate the assumptions that are necessary to use priority or unique.