0
\$\begingroup\$

When it comes to (synchronously) resetting registers in HDL (I'll use Verilog), is it considered bad practice to code the reset combinatorially? As far as I can tell, the following two snippets are functionally equivalent.

Behavioral:

always @ (posedge clk) begin
    if (rst) current_val <= 1'b0;
    else     current_val <= din;
end

Structural:

assign next_val = (rst == 1'b0) ? din : 1'b0;
always @ (posedge clk) begin
    current_val <= next_val;
end

I learned to write the logic the behavioral way, and only recently came across the second. In terms of HDL convention/best practices, is one preferrable to the other? Also, would they be mapped to different hardware?

\$\endgroup\$
  • 1
    \$\begingroup\$ #1 looks clearer to me, so I would use that. They'll almost certainly map to the same hardware, so use whichever is clearest to you (or your team) \$\endgroup\$ – C_Elegans Dec 17 '18 at 1:54
1
\$\begingroup\$

There is no guarantee that the logic in the "Structural" example would be recognised by standard synthesis tools. It is entirely possible that you wouldn't get a register with synchronous reset. To understand why that is we need to consider exactly how the code is translated into the technology of the device being used.

Below is a diagram of half an Intel FPGA Cyclone V Adaptive Logic Module (image copyright Intel FPGA).

enter image description here

At the top of the image a series of signals called syncload, sclr and aclr can be seen. These are the control signals for the registers in the block. Other device families will have different dedicated signals. Correctly written code will infer use of these dedicated control signals. If the desired dedicated control signals are not available, or are inferred incorrectly, then inputs to the LUTs will be used instead. At best this will waste resources, at worst this could cause functionality to split across multiple ALMs and cause timing failure. We have no direct control over this, only the ability to infer through HDL. A good read on this can be found in the white paper here: https://www.xilinx.com/support/documentation/white_papers/wp275.pdf

Also, inspection of your code shows that it is not functionally equivalent to the "Behavioural" example. Next_val is being updated asynchronously to Current_val which has all sorts of implications for routing, timing and meta-stability. However, in simulation you would see none of that and both sets of code would be superficially identical.

HDL convention is to use recognised patterns for such low level constructs as this to ensure the synthesis tools will recognise them. These are provided by the synthesis tool designers. An example from Intel FPGA's Quartus Prime User Guide: Design Recommendations (https://www.intel.com/content/www/us/en/programmable/products/design-software/fpga-design/quartus-prime/user-guides.html) :

always @ (posedge clock)
begin
  if (!rst_n)
  begin
    reg1 <= 1’bo;
    reg2 <= 1’b0;
  end
  else
  begin
    reg1 <= data_a;
    reg2 <= data_b;
  end
end

Also, the general principle of readability in code would suggest that your "Structural" example is not to be favoured.

\$\endgroup\$
  • \$\begingroup\$ Can you explain how the structural code might infer something that is not the functional equivalent of a synchronous reset? What else could you get? Nevertheless, I agree that the behavioral code is the more readable and standard approach. \$\endgroup\$ – Elliot Alderson Dec 17 '18 at 18:37
4
\$\begingroup\$

It won't make any difference in the synthesized hardware. But writing in an HDL, or more precisely, writing at the register transfer level (RTL) abstraction should take you away from the structural level. It certainly will be more efficient to simulate and for others to understand your code when you write in a much simpler style.

\$\endgroup\$
1
\$\begingroup\$

Both of those are RTL, are functionally equivalent, and likely will produce the same HW if all other things remain equal between the two synthesis settings.

Typically, "Behavioral" refers to "non-synthesizable" constructs, and "structural" refers to the use of primitives (e.g. netlist, or netlist-like). "RTL" is high-level, synthesizable constructs, as you have written (assuming the rest of the file conforms to those definitions).

Your first example is more typical, at least in terms of reset. You may, however, have logic (other than reset) that fans-in to more than one register or other combinatorial term(s) and thus compels you to create that intermediate term. Other than that, it is completely up to you and your style... and, if it is just a matter of style, I would recommend you employ the KISS-method, making it easy to follow and understand, as in your first example.

If you want to (try and) "force" the synthesizer to implement something specific, you can try writing structural code. Structural code though, is not portable, less easily changed, and typically not necessary. The tools are really good at PAR, so if you feel the need to be instantiating primitives, outside of your clocks and resets (buffers, DCMs, etc.), memories (maybe) and IO (again, maybe), you probably want to re-evaluate your part selection and/or design.

It is a good question you asked though. You should look into the religious wars regarding 1, 2, and 3-process state machines. Personally, I use 3-process state machines (combinatorial next state, combinatorial outputs, synchronous updates)... it is a hold-over approach from long ago regarding the synthesis tools interpretation, which is not as much an issue these days. Here is a fairly good discussion of it https://vhdlwhiz.com/n-process-state-machine/

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.