Monthly Archives: May 2011

Variables and signals in VHDL – and when variables are better

VHDL has two types of what-would-normally-be-called-variables:

  • signals, which must be used for inter-process communication, and can be used for process-local storage
  • variables which are local to a process.

(there’s also variables of a protected type which we’ll ignore for now as they’re another thing altogether)

Now one of the big differences between signals and variables is the way they are updated. When a signal is used within a process and is assigned to to change its value, the value does not update until the end of the process. For example (assume foo is “ to start with):

  wait until rising_edge(clk);
  foo <= 1; -- an update is scheduled
  sig <= foo + 1; -- but hasn't happened yet, so sig is assigned 1 (again, only scheduled)
end process; -- at this point, foo is updated with the value 1 and sig also gets 1

Compare with the situation where foo is a variable:

  variable foo : integer;
  wait until rising_edge(clk);
  foo := 1; -- foo gets 1 immediately
  sig <= foo + 1; -- so sig has an update to 2 scheduled
end process; -- at this point, foo is already 1 and sig gets its scheduled update to 2

This can cause some interesting effects on coding style – take for example this question on StackOverflow.

A code example is provided there which shows what has to happen due to the update semantics of signals – deeply nested ifs.

Here’s an alternative. Let’s define a procedure first of all to increment a variable and wrap around if it is greater than some maximum value. Also return a flag to inform us if the variable wrapped:

procedure inc_wrap (i : inout integer; maximum : positive; wrapped : inout boolean) is
  if i = maximum then
    wrapped := true;
    i := 0;
    wrapped := false;
    i := i + 1;
  end if;
end procedure;

Then our update process looks like this:

if second_enable = '1' then
  inc_wrap(s_ls_int, 9, wrapped);
  if wrapped then
    inc_wrap(s_ms_int, 5, wrapped);
  end if;
  if wrapped then
    inc_wrap(m_ls_int, 9, wrapped);
  end if;
  if wrapped then
    inc_wrap(m_ms_int, 5, wrapped);
  end if;
  if wrapped then
    if h_ms_int < 2 then -- wrap at 9 for 0-19 hour
      inc_wrap(h_ls_int, 9, wrapped);
    else -- wrap at 3 if past 20 hours
      inc_wrap(h_ls_int, 3, wrapped);
    end if;
  end if;
  if wrapped then
    inc_wrap(h_ms_int, 2, wrapped);
  end if;
end if;

All the code from this posting is at Github

Tool switches

@boldport asked:

What are your #FPGA design space exploration techniques?

which he expands upon:

“Design space exploration” is the process of trying out different settings and design methods for achieving better performance. Sometimes the goals are met without any of it — the default settings of the tools are sufficient. When they’re not, what are your techniques to meet your performance goals?

Yet again, the 140 character constraint leaves me with things unspoken….

Working where I do in the automotive market means that it’s not good enough to miss timing by a few picoseconds and say “it’ll be fine, ship it”. If you miss timing, you /have/ to make it pass.

My experience with tool tweakery is that it gains you a 2-5% timing improvement – which can be enough to meet timing when you just missed.

The downside is that usually, when you go and change the design (due to the requirements changing yet again), you find yourself with a slightly different 10ps timing violation which maybe this time the tools can’t get around. Or maybe with a change one of the seed parameters, it will, after some trial runs.

So, I’ve given up on that approach as being too variable. It’s much harder to give estimates of when something will be ready when timing closure is a “tweak the knobs a number of times and see”.

What I do now is rework things until it meets timing easily. That way, it’s likely to stay that way.

Techniques include:

  • Pipelining – adding registers
  • Constraining unconstrained integers – occasionally, the synthesiser doesn’t figure out the range an integer variable or signal can take on, so needs telling. This is happening less and less as synthesis tools get cleverer.
  • Simplifying algorithms

This give me a much more predictable build process. It’s seen me fine, even for a nearly full Spartan3 device with some logic running at 160+MHz DDR.

Of course, if you are right up against the limits of the device speed and you’ve pipelined and constrained and everything else, then tweaking tool parameters is all you have left – anyone in that position has my sympathies!