Verilog/VHDL - How to avoid resetting data registers within a single always block?
asynchronous, reset, synchronous, verilog, vhdl
Solution
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:
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:
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:
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.
Problem
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?