9
\$\begingroup\$

The Verilog always statement, namely

always @(/* condition */)
    /* block of code */

executes the block of code whenever condition is satisfied. How is such an always block implemented in hardware?

\$\endgroup\$
3
  • \$\begingroup\$ I think it heavily depends on what the block of code is.. \$\endgroup\$
    – m.Alin
    Commented Apr 9, 2012 at 13:36
  • 1
    \$\begingroup\$ And whether the condition is posedge x or just x \$\endgroup\$
    – Justin
    Commented Apr 9, 2012 at 14:26
  • \$\begingroup\$ @Justin: Let's assume there is no posedge. \$\endgroup\$
    – Randomblue
    Commented Apr 9, 2012 at 15:14

3 Answers 3

17
\$\begingroup\$

First, note that not all Verilog designs are synthesizable. Usually, only a very specific subset of constructs can be used in a design that is to be realized in hardware.

One important restriction that pops up is that every reg variable can only be assigned to in at most one always statement. In other words, regs have affinity to always blocks.

The following types of always blocks can generally be used.

always @(*) begin
    // combinational
end

always @(posedge clk) begin
    // sequential
end

In the former case, the * indicates that the block should be executed whenever any signal used in the block changes or, equivalently, that the block should be executed continuously. Therefore, regs that have affinity to combinational always blocks are implemented as signals computed from other signals using combinational logic, i.e. gates.

Registers that have affinity to always blocks of the latter type, on the other hand, are outputs of D flip-flops that are clocked on the rising edge of clk (falling edge if negedge is used). Inputs to the flip-flops are, again, computed with combinational logic from other signals.

Consider the following, somewhat contrived example.

reg out, out_n;
always @(*) begin
    out_n = !out;
end
always @(posedge clk) begin
    out <= !out;
end

Here, out_n is associated with the first always block, out with the second. out_n will be implemented with a single NOT gate that will drive out_n and be driven from out (note that it is a pure combinational logic). On the other hand, out will be driven by a flip-flop clocked from clk. The input to the flip-flop will again be computed by a NOT gate from out (which is driven by the aforementioned flip-flop). Optimizing synthesizers will combine the two NOT gates and use one NOT gate and one flip-flop.

Depending on the hardware you have available, other types of constructs can be used. For example, if the flip-flops have asynchronous resets, the following construct is also synthesizable.

always @(posedge clk or posedge rst) begin
    if (rst)
        // reset
    else
        // sequential
end
\$\endgroup\$
3
  • \$\begingroup\$ Thanks. Regarding the *, I thought it indicated that the block should be executed whenever any signal in the block changes (as opposed to the design). \$\endgroup\$
    – Randomblue
    Commented Apr 10, 2012 at 8:17
  • \$\begingroup\$ @Randomblue, you're right, I'll fix the answer. Note, however, that the two are equivalent in behavior. \$\endgroup\$
    – avakar
    Commented Apr 10, 2012 at 8:21
  • \$\begingroup\$ True; fair enough! \$\endgroup\$
    – Randomblue
    Commented Apr 10, 2012 at 8:26
3
\$\begingroup\$

An always block is commonly used to describe a flip-flop, a latch, or a multiplexer. The code would be implemented with a flip-flop, a latch, or a multiplexer.

In an FPGA a flip-flop and a latch are generally just two different configurations of a more general-purpose register device. A multiplexer would be constructed from one or more general-purpose logic elements (LUTs).

In general, there's two ways to go about design with Verilog:

  1. Visualize the logic you want in terms of gates and registers, then figure out how to describe it in Verilog. The synthesis guide books from the FPGA vendors or synthesis tool vendors give boiler-plate for the most common structures you might want to work with.

  2. Just write Verilog and don't worry about what the underlying hardware looks like. However, even if you do this, you still have to know what is and what isn't synthesizable. So again, you will look to the boilerplate provided by your tool vendor and adapt it to your application.

EDIT

Avakar's answer is a much better one for your question, but this spurred some interesting discussion about the differences between Xilinx and Altera so I won't delete it.

\$\endgroup\$
5
  • \$\begingroup\$ "flip-flop and a latch are generally just two different configurations" Are they? I'd expect latches to be implemented with LUTs (with care if the LUTs are not glitch-free). \$\endgroup\$
    – avakar
    Commented Apr 9, 2012 at 16:20
  • \$\begingroup\$ @avakar, I know that in all Xilinx FPGAs (or at least all remotely recent ones) the latches use the same hardware as a flip-flop, differing by only a single bit in the configuration bitstream. I'm not sure about other brands. \$\endgroup\$ Commented Apr 9, 2012 at 16:40
  • \$\begingroup\$ Hmm. Some older Altera designs had feedback paths that would allow for LUT to be used to implement latches. It almost looks like the main routing might be needed to implement latches at all in the newer designs. This is not surprising though, as in modern RTL design actual latches (rather than flip-flops) are rarely desired. \$\endgroup\$ Commented Apr 9, 2012 at 17:14
  • \$\begingroup\$ @avakar, I'm more familiar with Xilinx, where the register device can be configured as a flip-flop or a latch. If that's not possible in Altera or some other vendor, it would make the general advice "don't design with latches" even stronger. \$\endgroup\$
    – The Photon
    Commented Apr 9, 2012 at 17:59
  • \$\begingroup\$ @KevinCathcart and Photon: I see, I'm not familiar with Xilinx, only with Altera Cyclone series, which have no dedicated latch circuitry. \$\endgroup\$
    – avakar
    Commented Apr 9, 2012 at 18:38
1
\$\begingroup\$

As has been said not all always blocks are synthesizable. There are also some blocks which the synthesis tools will accept but which will produce results that differ from what a simulator will produce.

First off the sensitivity list. The usual rule is it must either contain only edge detection constructs (and there is usually a limited selection of possible combinations) or it must contain (possibly through the use of * or systemverilog's always_comb) every signal used as an input to the block. We call the former a sequential block and the latter a combinatorial block. Typically if you only include a subset of inputs in a combinatorial block synthesis tools will just ignore you and act as if the full list had been specified (creating simulation/synthesis mismatches)

Second blocking vs noblocking assignments. In a combinatorial block the difference doesn't matter much, but in a sequential block it matters a great deal.

In a sequential block nonblocking assignments model a register fairly directly while blocking assignments model variables (which may or may not imply registers depending on the order of setting and reading). As a rule a "reg" set using blocking assigments in a sequential block should only be read in the same block and blocking and nonblocking assingments should not be mixed on the same "reg".

Mixing blocking and nonblocking assignments to the same item is likely to cause synthesis failures. Making a blocking assingment in one block and reading it in another is likely to cause simulation/synthesis mismatches (and possiblly even mismatches between different simulation runs).

Now we have the basic rules out of the way we can consider how the compiler turns code into logic.

The first step is to unroll all loops. This means that loops must have a maximum iteration count that can be determined at synthesis time or you will get a synthesis failure.

Then the tool can analyse the control flow of the block and turn it into a data flow. Each variable becomes one or more signals. Each if statement or similar construct becomes one or more multiplexers selecting which set of results will actually be used. If a variable keeps it's value from one run of a combinatorial always block to the next then a register will be generated to keep the value.

The tool will then likely try and apply some optmisations.

In quartus you can see the results of this process after building your project by going into "tools->netlist viewers->rtl viewer".

After generating this structural representation in terms of abstract logic elements the tool will then move on to mapping those abstract elements onto the resources the chip actually has.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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