Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verilog/VHDL - How to avoid resetting data registers within a single always block?

I like to avoid resetting data registers that don't need to be reset. For example, when streaming data through pipeline stages, if each stage has a valid bit, there is no need to reset the data registers. (I believe this eases routing and timing on the data registers.)

This can easily be accomplished by using separate always blocks (or processes in vhdl), but I find this verbose, and doing the following is problematic because the data register is essentially being enabled by the reset.

always @(posedge clk)
if (rst)
  out_valid <= 0;
  // NOTE: out_data is not reset
else begin
  out_valid <= in_valid;
  if (in_valid)
    out_data <= in_data + 1;
end

Instead, I've been putting the reset clause at the end of the always block and taking advantage of the "last assignment wins" rule.

always @(posedge clk)
begin
  out_valid <= in_valid;
  if (in_valid)
    out_data <= in_data + 1;

  if (rst)
    out_valid <= 0
end

I haven't seen a lot of people using this style. Are there any drawbacks or issues that I'm missing? Is there a better way of doing this?

Bonus Question: What if the reset is asynchronous? As in:

always @(posedge clk, posedge rst)
begin
  out_valid <= in_valid;
  if (in_valid)
    out_data <= in_data + 1;

  if (rst)
    out_valid <= 0
end

In this case I think the synthesizer will connect the reset signal to the data register, which defeats the purpose. Is there an elegant way of decoupling the data register from the reset signal without resorting to separate always block?

like image 672
mksuth Avatar asked Jan 31 '14 00:01

mksuth


2 Answers

As you have shown in the question for synchronous resets the basic pattern is:

always @(posedge clk) begin
  if (rst) begin
    out_valid <= 'b0;
  end
  else begin
    out_valid <= in_valid;
  end
end

With asynchronous reset:

always @(posedge clk or posedge reset) begin
  if (rst) begin
    out_valid <= 'b0;
  end
  else begin
    out_valid <= in_valid;
  end
end

I would not recommend mixing implying asyn reset flip-flops with non reset flip-flops as it easily looks like a typo that you forgot to add the resets. separating these two block seems like a good design separation. You are clearly separating control and data.

For the synchronous reset you should be ok to put the reset last as it is just a mux flop.

I would not recommend it though. The following apply a little stronger to the async case but the softer reasons apply to both.

Synthesis tools look for this pattern, when using your suggested method you may find that synthesis tools struggle or give incorrect results. Simulators and synthesis tools are often very different tools, using different code parsing engines under the hood. Just because it simulates ok does not mean it will synthesize ok.

Large designs may have many people working on them for months before it hits synthesis. If you have a dedicated Synthesis team they may be restricted to the version of tools that they can use based on internal quality control. Therefore even if you have proven to yourself that this works with the latest, or a particular version of the tools it may still end up causing a lot of extra work and extra design and synthesis cycles.

You may find that if company wide enforced linting tools and rules are put in place that this style would no longer pass a code quality review.

Softer (not strict will it work) reasons: An engineers role is to build something weighing up all the decision effecting cost, timescale and suitability for the required task. Using known working technology and methodologies where appropriate helps reduce risk and adds reliability to time scales.

Having industry standard coding practises, allows code to be handed over to anyone within the team or to external contractors. Using familiar coding patterns speeds up reading and understanding of code, which lead to a lower overall cost of design.

like image 32
Morgan Avatar answered Oct 14 '22 03:10

Morgan


Synchronous Resets

I've been using the "last assignment wins" for resets for >5 years. Personally I find it easier to read and it saves having to needlessly indent the entire process one level. I've never seen any problems using this coding style with the FPGA synthesis tools I use (ISE, Quartus, Synplify).

Asynchronous Resets

To answer your bonus question regarding asynchronous resets - this is a little trickier. Consider the following:

typedef struct packed {
    logic               a;
    logic               b;
} state_t;

state_t                 state;

always_ff @(posedge clk or negedge areset_n) begin
    if (~areset_n) begin
        state.a         <= 1'b0;
    end else begin
        state.a         <= data_a;
        state.b         <= data_b;
    end
end

We have a bit of a problem. We don't want state.b to have a reset, but it's part of the same structure as state.a which is reset. Being good engineers we use always_ff processes but this actually means we can't split the code into separate processes. Besides, it would become very messy and error prone keeping track of which members are assigned in which process for a large state structure.

The above code actually synthesises to the following:

example of gated input

We can see that the areset_n signal acts as an enable to the state.b register. Typically this isn't what we want, and since most FPGAs don't support this architecture natively the synthesis tool is forced to insert additional logic to emulate the enable:

enter image description here

This extra logic reduces timing margin and uses up precious resource. Thankfully, there is a workaround:

always_ff @(posedge clk or negedge areset_n) begin
    if (~areset_n) begin
        state.a         <= 1'b0;
        state.b         <= 1'bx;
    end else begin
        state.a         <= data_a;
        state.b         <= data_b;
    end
end

Note that we have assigned x to state.b during the reset. As we can see, Quartus now synthesises our intended circuit:

enter image description here

Bonus Rant

As an aside, I'm not a big fan of being constrained to "standard practice" because the possibility that tools might get is wrong. "Conventional wisdom" is often incorrect or outdated.

Be adventurous and push the tools to their limits. If you hit a bug in the tool or a limitation then raise a bug with the vendor and adjust your style to compensate. Sure sometimes you'll spend time looking into a strange bug that turns out to be the fault of the toolchain but you should be looking at the output of your tools anyway. The time spent will be more than saved by the improved levels of abstraction and re-use made available with some of the newer language features.

If more of us pushed to get new features supported or participated in improving the languages then vendors wouldn't be able to get away with fobbing us all off with tools that are already 5-years out of date! The level of conservatism in RTL development hampers innovation - we love complaining about the tools but we don't participate enough in making them better.

like image 78
Chiggs Avatar answered Oct 14 '22 03:10

Chiggs