3

My question is regarding FPGA design - if I have some registers in my design that I dont care what will be their reset value, can I set the reset value to x? will that improve area efficiency (will the synthesizer be able to utilize this and generate the design in a more efficient way?) For example,

always @(posedge clk or negedge reset_n) begin
    if(~reset_n) begin
        reg_1 <= 'x
    end
...
end

Edit: And another question in a similar topic - assuming I have a state machine for example that I dont care what will some outputs be in some states - does setting them to 'x improve area efficiency? for example, if I have a fsm with two states, STATE_1, STATE_2, and two outputs will the synthesis tool be able to utilize the following code:

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

better than this:

always_comb begin
   case(state):
   STATE_1:begin
      out_1 = 1;
      out_2 = 0;
   end
   STATE_2:begin
      out_1 = 0;
      out_2 = 0;
   end
end

(under the assumption that I dont care what is out_2 in STATE_1, and what is out_1 in STATE_2). Thanks

fptech20
  • 103
  • 1
  • 6

1 Answers1

2

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.

Silicon1602
  • 1,151
  • 1
  • 7
  • 18
  • Hi @fptech20, you could do this in combinational logic and the synthesis tool will optimize this. However, I would generally recommend to _not_ do this, since it could cause mismatches once you start running gate-level simulations. Instead of assigning `'x`, I would recommend to look into the (System)Verilog keywords `unique`, `unique0`, and `priority`. – Silicon1602 Jul 02 '20 at 10:02
  • Could you please see my edit with the additional question? I dont see how unique/priority can be helpful here... – fptech20 Jul 02 '20 at 10:14
  • 1
    @fptech20, see my edit. – Silicon1602 Jul 02 '20 at 11:40
  • Great! that answered my question fully. Just another small thing - assuming what I want to optimize is area and that I dont have any tight timing constraint - do you have any good advice on stuff that can reduce area? any design rules or synthesis optimizations (I'm using synplify pro) that can be useful? – fptech20 Jul 02 '20 at 14:49