vhdl Coding


Example

This example is the second of a series of 3. If you didn't yet, please read the Block diagram example first.

With a block diagram that complies with the 10 rules (see the Block diagram example), the VHDL coding becomes straightforward:

  • the large surrounding rectangle becomes the VHDL entity,
  • internal arrows become VHDL signals and are declared in the architecture,
  • every square block becomes a synchronous process in the architecture body,
  • every round block becomes a combinatorial process in the architecture body.

Let us illustrate this on the block diagram of a sequential circuit:

A sequential circuit

The VHDL model of a circuit comprises two compilation units:

  • The entity that describes the circuit's name and its interface (ports names, directions and types). It is a direct translation of the large surrounding rectangle of the block diagram. Assuming the data are integers, and the clock uses the VHDL type bit (two values only: '0' and '1'), the entity of our sequential circuit could be:
entity sequential_circuit is
  port(
    Data_in:  in  integer;
    Clock:    in  bit;
    Data_out: out integer
  );
end entity sequential_circuit;
  • The architecture that describes the internals of the circuit (what it does). This is where the internal signals are declared and where all processes are instantiated. The skeleton of the architecture of our sequential circuit could be:
architecture ten_rules of sequential_circuit is
  signal Sum, Next_sum: integer;
begin
  <...processes...>
end architecture ten_rules;

We have three processes to add to the architecture body, one synchronous (square block) and two combinatorial (round blocks).

A synchronous process looks like this:

process(clock)
begin
  if rising_edge(clock) then
    o1 <= i1;
    ...
    ox <= ix;
  end if;
end process;

where i1, i2,..., ix are all arrows that enter the corresponding square block of the diagram and o1, ..., ox are all arrows that output the corresponding square block of the diagram. Absolutely nothing shall be changed, except the names of the signals, of course. Nothing. Not even a single character.

The synchronous process of our example is thus:

  process(clock)
  begin
    if rising_edge(clock) then
      Sum <= Next_sum;
    end if;
  end process;

Which can be informally translated into: if clock changes, and only then, if the change is a rising edge ('0' to '1'), assign the value of signal Next_sum to signal Sum.

A combinatorial process looks like this:

process(i1, i2,... , ix)
  variable v1: <type_of_v1>;
  ...
  variable vy: <type_of_vy>;
begin
  v1 := <default_value_for_v1>;
  ...
  vy := <default_value_for_vy>;
  o1 <= <default_value_for_o1>;
  ...
  oz <= <default_value_for_oz>;
  <statements>
end process;

where i1, i2,..., in are all arrows that enter the corresponding round block of the diagram. all and no more. We shall not forget any arrow and we shall not add anything else to the list.

v1, ..., vy are variables that we may need to simplify the code of the process. They have exactly the same role as in any other imperative programing language: hold temporary values. They must absolutely be all assigned before being read. If we fail guaranteeing this, the process will not be combinatorial any more as it will model kind of memory elements to retain the value of some variables from one process execution to the next. This is the reason for the vi := <default_value_for_vi> statements at the beginning of the process. Note that the <default_value_for_vi> must be constants. If not, if they are expressions, we could accidentally use variables in the expressions and read a variable before assigning it.

o1, ..., om are all arrows that output the corresponding round block of your diagram. all and no more. They must absolutely be all assigned at least once during the process execution. As the VHDL control structures (if, case...) can very easily prevent an output signal from being assign, we strongly advice to assign each of them, unconditionally, with a constant value <default_value_for_oi> at the beginning of the process. This way, even if an if statement masks a signal assignment, it will have received a value anyway.

Absolutely nothing shall be changed to this VHDL skeleton, except the names of the variables, if any, the names of the inputs, the names of the outputs, the values of the <default_value_for_..> constants and <statements>. Do not forget a single default value assignment, if you do the synthesis will infer unwanted memory elements (most likely latches) and the result will not be what you initially wanted.

In our example sequential circuit, the combinatorial adder process is:

  process(Sum, Data_in)
  begin
    Next_sum <= 0;
    Next_sum <= Sum + Data_in;
  end process;

Which can be informally translated into: if Sum or Data_in (or both) change assign the value 0 to signal Next_sum and then assign it again value Sum + Data_in.

As the first assignment (with the constant default value 0) is immediately followed by another assignment that overwrites it, we can simplify:

  process(Sum, Data_in)
  begin
    Next_sum <= Sum + Data_in;
  end process;

The second combinatorial process corresponds to the round block we added on an output arrow with more than one destination in order to comply with VHDL versions prior 2008. Its code is simply:

  process(Sum)
  begin
    Data_out <= 0;
    Data_out <= Sum;
  end process;

For the same reason as with the other combinatorial process, we can simplify it as:

  process(Sum)
  begin
    Data_out <= Sum;
  end process;

The complete code for the sequential circuit is:

-- File sequential_circuit.vhd
entity sequential_circuit is
  port(
    Data_in:  in  integer;
    Clock:    in  bit;
    Data_out: out integer
  );
end entity sequential_circuit;

architecture ten_rules of sequential_circuit is
  signal Sum, Next_sum: integer;
begin
  process(clock)
  begin
    if rising_edge(clock) then
      Sum <= Next_sum;
    end if;
  end process;

  process(Sum, Data_in)
  begin
    Next_sum <= Sum + Data_in;
  end process;

  process(Sum)
  begin
    Data_out <= Sum;
  end process;
end architecture ten_rules;

Note: we could write the three processes in any order, it would not change anything to the final result in simulation or in synthesis. This is because the three processes are concurrent statements and VHDL treats them as if they were really parallel.