-1

I understand the usual textbook difference between signal and variable in VHDL:

  • variable := updates immediately inside a process

  • signal <= updates after a delta cycle

  • variables are local, signals are global

However, I am confused about how this maps to actual FPGA hardware after synthesis and implementation.

My Lead engineer told me:

“In hardware, variable and signal are the same.
Both appear one clock later because hardware uses flip-flops.
So using a variable for edge detection is wrong.”

I want to check if that is actually true.

Context: SPI master with internal SCLK and edge detection

I have an SPI master where I generate an internal SCLK (from a system clock) and detect its edges using variables inside a single clocked process.

Here is a trimmed version of the real code:

⚙ Example code

Inside a clocked process:

process (CLK, RSTB)
  -- edge detection variables
  variable prev_sclk_v   : std_logic := '0';
  variable next_sclk_v   : std_logic;
  variable sclk_rise_v   : std_logic;
  variable sclk_fall_v   : std_logic;
begin
  if RSTB = '0' then
    sclk_r      <= '0';
    sclk_div_cnt <= 0;
    prev_sclk_v := '0';
    ...
  elsif rising_edge(CLK) then

    done_r      <= '0';
    sclk_rise_v := '0';
    sclk_fall_v := '0';

    -- SCLK next value (divider logic, 40MHz -> 10MHz)
    next_sclk_v := sclk_r;

    if busy_r = '1' then
      if sclk_div_cnt = 1 then
        sclk_div_cnt <= 0;
        next_sclk_v  := not sclk_r;
      else
        sclk_div_cnt <= sclk_div_cnt + 1;
      end if;
    else
      sclk_div_cnt <= 0;
      next_sclk_v  := '0';  -- idle: SCLK low (mode 0)
    end if;

    -- edge detection using variables (same-cycle valid)
    if (prev_sclk_v = '0' and next_sclk_v = '1') then
      sclk_rise_v := '1';
    elsif (prev_sclk_v = '1' and next_sclk_v = '0') then
      sclk_fall_v := '1';
    end if;

    -- store for next cycle
    prev_sclk_v := next_sclk_v;
    sclk_r      <= next_sclk_v;

    -- FSM example: use edge flags in the same clock
    case state_r is
      when send_header =>
        if sclk_fall_v = '1' then
          -- shift MSB first, etc...
        end if;

      when receive_byte_a =>
        if sclk_rise_v = '1' then
          -- sample SDIO on SCLK rising edge
        end if;
      ...
    end case;

  end if;
end process;

My understanding of how these map to hardware

From a hardware point of view, my interpretation is:

  • sclk_r is a signal assigned in a clocked process ⇒ it is a flip-flop (registered SCLK).

  • prev_sclk_v is a variable, but its value is carried from cycle to cycle via
    prev_sclk_v := next_sclk_v;
    ⇒ this infers a flip-flop as well (state).

  • next_sclk_v is a variable used only to compute “next SCLK” in the current clock cycle and then assigned to sclk_r.
    It does not hold state by itself ⇒ synthesized as pure combinational logic feeding the D input of sclk_r.

  • sclk_rise_v / sclk_fall_v are variables used only within this one clock cycle as edge-detect flags.
    They are not stored into a signal or reused in the next cycle ⇒ they should be synthesized as pure combinational signals (no flip-flops).

What another engineer told me

Another engineer told me essentially:

“In hardware, variable and signal are the same.
Everything is driven by flip-flops, so both are effectively one-clock delayed.
A variable ‘still depends on flip-flops’ so your variable-based edge detection is conceptually wrong.”

This conflicts with my understanding that:

  • Whether something becomes a register or not is not about “variable vs signal”,
    but about whether its value needs to be preserved across clock cycles.

My actual questions

My actual questions

From a real FPGA synthesis/implementation perspective (not just simulation semantics):


1) Is this statement correct?

A variable only infers a flip-flop if its value must be preserved across clock cycles
(for example, if it feeds itself or is stored into a registered signal).

If a variable is used only as an intermediate value within a single clock cycle and
its value is not needed in the next cycle, it is synthesized as pure combinational logic,
not a register.


2) In my specific code, is the following mapping accurate?

  • prev_sclk_v → variable, but used as state across cyclesregister (FF)

  • sclk_r → signal assigned in a clocked process ⇒ register (FF)

  • next_sclk_v, sclk_rise_v, sclk_fall_v → used only within this cycle ⇒ combinational (no FF)


3) Is the following summary accurate?

“variable vs signal” is not the true hardware distinction;
the real distinction is combinational vs sequential (state).

A variable becomes a register only if it represents state across cycles;
otherwise it is purely combinational.

A signal assigned in a clocked process is typically used to hold state (flip-flops).


4) If my understanding is correct, what would be the proper way to explain this to an engineer who claims that “variables and signals behave the same in hardware and both update next clock anyway”?

I want to explain clearly—based on actual synthesis behavior—why edge-detect flags implemented as variables are combinational in this context, while converting them to signals would introduce a one-cycle delay.

If any part of this reasoning is incorrect, I’d really appreciate a detailed correction.
I want to be sure I understand not just simulation behavior, but the actual synthesized hardware differences between variables and signals in this kind of design.

5
  • 1
    Would you mind to extend your example to a minimal reproducible example, saving us all from inventing the missing parts, please? Please note that this would include a test bench. Commented Nov 18 at 9:09
  • 2
    There is a simple rule for a clocked process: If you always assign a value to a variable before the variable is read, then you will get no flipflop (and you should not assign a value to the variable in the asynchronous reset branch in that case). If you read the variable before it is assigned a value, then you will get a flipflop. Commented Nov 18 at 9:22
  • 3
    What the engineers told you does not make sense. Memory elements (latches or DFFs) are inferred from a variable if and only if the variable is read before being assigned. If this happens in a "clocked process" DFFs are inferred. If this happens in a combinatorial process latches are inferred (and it is frequently not what you want). Answers to your questions: 1 and 3: more or less ("read before assigned" is simpler and more accurate), 2: yes, 4: how to explain things to people who don't understand is a complex question; show them examples? Commented Nov 18 at 15:52
  • Thanks everyone for the explanations. I now see that variables and signals have no inherent hardware difference. A flip-flop is inferred only when state must be retained, following the read-before-write pattern. Assigned before read → combinational. Read before assignment → previous value needed → flip-flop. In my case, prev_sclk_v holds state, so it becomes a flip-flop. sclk_rise_v, sclk_fall_v, and next_sclk_v are same-cycle only, so combinational. sclk_r is intentionally registered. This clears things up. Thanks again for the help!! Commented Nov 19 at 5:02
  • 2
    @HyungJinSong Note that there is no standard for how hardware is inferred from HDL code. Attempts were made to do with with IEEE 1076.6-2004, but this was withdrawn after lack of support from vendors. How HDL maps to hardware is vendor/tool dependent, but there are many conventions that are common for all tools. Commented Nov 19 at 13:31

1 Answer 1

-3

VARIABLE vs SIGNAL makes no distinction in the hardware.

The difference is during compilation.

The name of a SIGNAL always refers to the same node in the logic diagram / schematic -- typically the output of a D flip-flow. The assignment attaches something to the input of the flip-flop, but the name continues to refer to the flip-flop output.

The name of a VARIABLE is rebound by assignment, at the point of the assignment the name now refers to the output of the logic described by the right-hand of the assignment.

Sign up to request clarification or add additional context in comments.

7 Comments

This does not really answer the question and introduces more confusion than clarifications, I think. A signal does not necessarily refer to a hardware node. The synthesis/optimization phases can transform it in such a way that there is no corresponding hardware node at all. It is not "typically" the output of a D flip-flop, this depends on what it used for. And a variable can perfectly correspond to the output of a D flip-flop too (depending when it is assigned), not only to the output of the "logic described by the right-hand of the assignment".
A variable would generally not correspond (only) to the output of a D flip-flop, it maps to different points in the circuit during its life. A signal maps to a single point in the circuit (which of course may be optimized away). That is the difference.
process(clk) variable v: bits; begin if rising_edge(clk) then o <= v; v := i; end process; Here v is a variable that corresponds to the output of a DFF (with input i), and only that.
The difference between variables and signals is a matter of semantics (immediate vs. delayed update, local vs. global). These semantic differences may cause behavior differences... or not. As the goal of logic synthesizers is to infer hardware with the "same" behavior as the HDL model, using variables or signals may cause differences in the inferred hardware... or not. It is perfectly possible to design an example where replacing a variable by a signal does not make any difference after synthesis, or another example where it does.
You absolutely can use variables to describe the same circuit as you can build with signals... but the mapping of the names to the circuit will be different.
process(clk) variable v: bits; begin if rising_edge(clk) then o <= v; v := i; end process; produces the same as signal v: bits; ... process(clk) begin if rising_edge(clk) then v <= i; o <= v; end process;. In the two synthesized hardware, signal and variable v correspond to the output of the same DFF (with input i).
The claim "variable v corresponds to the output of the DFF" just doesn't make sense. You could say "on the line o <= v, v corresponds to the output of the DFF", and that would be true. But that binding does not apply to the entire source code, when dealing with variables. In (and after) the line v := i, v now corresponds to the input of the DFF, not the output.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.