Chapter 9

Registers

This chapter covers the VHDL required to describe registers. Throughout this chapter and the rest of the book, the term register will be used to refer to a flip-flop or a bank of flip-flops with common controls.

This chapter explains how a register is modelled, how this model is mapped onto flip-flops by a synthesiser. It then describes how to model other behaviour, such as resettable registers, gated registers. All of these models use synthesis templates to ensure the correct mapping from VHDL model to hardware.

9.1 Basic D-Type Register

The best way to explain how to describe a register in VHDL is with an example. The example is a complete VHDL design, with an entity and architecture. This is just to show the context; it is not a requirement that each register is described as a separate design unit and it is rare to do so.

The only recommended way to describe a register for logic synthesis is with a process. There are other ways of obtaining similar behaviour for simulation, but they will not necessarily be synthesisable.

library ieee;

use ieee.std_logic_1164.all;

entity Dtype is

  port (d, ck : in std_logic;

        q : out std_logic);

end;

architecture behaviour of Dtype is

begin

  process

  begin

    wait until rising_edge(ck);

    q <= d;

  end process;

end;

The register is described by the process statement. The process is recognised as a register because it matches a template built into the synthesiser. This is an example of a VHDL construct that does not have a direct mapping to hardware, so this template method must be used to identify the register. The template that is recognised is the wait statement that contains the until expression rising_edge(ck). This expression must be used if the process is to be interpreted as a register, although there are other templates with similar behaviour that may be optionally used instead of this one. The full set of register templates is described in Section 9.4.

9.2 Simulation Model

This explanation will be based on the basic register example, the process of which is repeated here:

process

begin

  wait until rising_edge(ck);

  q <= d;

end process;

In this example, the wait statement is the first statement in the process, so when simulation is started, execution of the process will suspend at this point. The wait condition is:

wait until rising_edge(ck);

The condition is normally in two parts, the on clause and the until clause. The on clause contains a list of signals known as the sensitivity list. The wait statement is activated if an event occurs on any of the signals in the on clause. In this case, there is no on clause at all, so there is an implicit on clause containing all of the signals mentioned in the until clause. In this example, the only signal mentioned in the until clause is the signal ck, so there is an implied on clause containing just this signal. This makes the full form of the wait statement as follows:

wait on ck until rising_edge(ck);

The on clause means that the wait statement will only be activated if an event happens on signal ck. It will not be activated if an event occurs on d.

Once the wait statement is activated, the until condition is tested. The until condition is a boolean expression and so must evaluate to true or false. If it evaluates to false, the wait statement is deactivated and the process remains suspended. If on the other hand, the condition evaluates to true, then process execution will resume.

In this example, the until condition is the function call rising_edge(ck). This function will return true if an event has just happened on ck and the current value of ck is '1'. The only way in which an event can happen on ck that leaves it with the current value '1' is a rising edge, so the overall effect of this wait statement is that the process only resumes when a rising edge happens on the clock signal ck.

Once the process has resumed, the statements in it are executed. The only statement in this process is:

q <= d;

This results in a transaction being generated for q. As with concurrent signal assignments (signal assignments outside a process – see Chapter 3), signal q does not get updated immediately, but a transaction is queued for the next delta cycle at the current simulation time. If the signal value is changed by the transaction, that change will be seen at the next delta cycle. When the process reaches its end, it loops back to the start where the wait statement is reached again and the process suspends. It will stay suspended until the next activating event, namely the next rising edge on signal ck. Event processing resumes only when the process is suspended at the wait statement.

In summary, after a rising edge on the clock signal ck, the process executes once, resulting in an update transaction on q being queued. This transaction results in q being updated on the next delta cycle, in other words at the same simulation time as the clock edge. This update transaction will only cause an event if the value of signal q is changed. The value of q is then preserved until the next activating event for the process, which will not happen until the next rising edge on signal ck causes the process to execute again.

It can be seen that this behaviour is the behaviour of an edge-triggered register.

9.3 Synthesis Model

The model just described has a simulation behaviour that is equivalent to an edge-triggered register (or flip-flop if you prefer). A synthesiser recognises it as such by recognising the specific syntax of the wait statement.

There is no hardware mapping of arbitrary processes, and synthesisers can only map those processes that meet the specific rules or templates. For registers, the specific rules require a wait statement of the kind in the example. No other conditions may be added to the until clause and no other signals may be added to the on clause. The exception to this rule is the asynchronous reset, dealt with in Section 9.9.

This kind of synthesis-specific rule is referred to as a template. It is important to know when a rule results from the definition of the VHDL language and when it results from a synthesis template. This is because errors arising from VHDL rules will be caught during the simulation phase of the design cycle, whereas errors arising from synthesis templates will not be caught until the synthesis phase of the design cycle. Furthermore, templates can vary between synthesis systems, whereas VHDL rules are constant between simulators. The register model described here is a synthesis template, so it is good practice to check the specific rules for the synthesiser you will be using and to test your understanding of them with some trial syntheses before committing to a large design project. In this case the template is standardised and should be recognised by all synthesis tools.

The register model is implemented by the synthesiser as a block of combinational logic representing the sequential code in the process, followed by a register on every output of the combinational logic block and every feedback within it. The outputs of the block are the set of all signals that are assigned to in the registered process. Thus, all signal assignments in a registered process will result in registers on those target signals.

Put more simply, registered and combinational processes are interpreted in similar ways, with the same combinational logic being generated from the contents. However, registered processes then have registers added to all the process's outputs and feedback paths. There is no latch inference, since feedback is registered.

To illustrate this, the following example shows a trivial combinational process:

process

begin

  wait on a, b;

  z <= a and b;

end process;

This is interpreted as the hardware shown in Figure 9.1.

Figure 9.1 Simple combinational circuit.

img

The same process contents, but now contained in a process conforming to a register template, gives the same circuit but with a register on the output:

process

begin

  wait until rising_edge(ck);

  z <= a and b;

end process;

This is interpreted as the hardware in Figure 9.2.

Figure 9.2 Registered circuit.

img

The clock signal must be a one-bit type, which is nearly always of type std_logic.

This example illustrates the way in which a synthesiser interprets a registered process: the contents of the process, excluding the register template part, is interpreted as a combinational process and then a register is added to every output. Where latches would be inferred in a combinational process, gated registers are inferred in the registered process.

9.4 Register Templates

So far, all the discussion has concerned one template for a registered process. There are in fact several different templates. The four most common templates will be described here, although there are others possible – this is not a comprehensive list but a safe list that should be supported by all synthesis tools. These example templates have two slightly different modes of operation in simulation, but are exactly equivalent in synthesis.

9.4.1 Basic Template

The template that has already been used is what will be referred to as the basic template:

process

begin

  wait until rising_edge(ck);

  q <= d;

end process;

This is the most compact form of the registered process. It only works for types that have an associated rising_edge function. The most common logic type, std_logic, has this function defined in the std_logic_1164 package.

This template can optionally have an explicit on clause:

process

begin

  wait on ck until rising_edge(ck);

  q <= d;

end process;

This template is sometimes used in a modified form that avoids the use of a function call by using a simplified expression to recognise the clock edge. This results in what will be referred to as the basic template with an event expression:

process

begin

  wait until ck'event and ck = '1';

  q <= d;

end process;

This used to be the preferred form, but has been supplanted by the template using the edge function that was described earlier.

Note that there is some redundancy in this wait statement. The wait statement is activated only by events on signal ck anyway, therefore the test for ck'event is unnecessary. However, a synthesis system usually requires this test for an event in order to recognise that the process is a register template and not a combinational process, so it should be included even though it is redundant.

9.4.2 If Statement Template

This template has a slightly different mode of operation in simulation. To illustrate, here's what will be referred to as the if statement template:

process

begin

  wait on ck;

  if rising_edge(ck) then

    q <= d;

  end if;

end process;

The wait statement in this template has no until clause. This also means that it must have an explicit on clause. The on clause means that the process will be activated every time there's an event on clock signal ck, regardless of the event. The if statement then filters out just one kind of event – the rising edge in this example. If signal ck has just changed to '0', then the if statement skips over the assignment; so signal q is not assigned to and retains its value. This means that the falling edge has no effect on the value of q. However, if signal ck has just changed to '1', then the assignment is executed and so the output gets a new value, so this process still models a rising edge-triggered register.

This template can also use the event expression to test for the clock edge:

process

begin

  wait on ck;

  if ck'event and ck = '1' then

    q <= d;

  end if;

end process;

9.4.3 Sensitivity-List Template

The final template will be referred to as the sensitivity-list template. A process can have a sensitivity list instead of a wait statement to specify the set of signals that cause activation of the process. The form of the sensitivity-list template is:

process(ck)

begin

  if rising_edge(ck) then

    q <= d;

  end if;

end process;

In this example, the process remains suspended until an event happens on a signal in the sensitivity list which in this case is (ck). This activates the process that executes once and then suspends again. In this case, the process will be activated on any event on ck, just like the if template. Inside the sensitivity-list process is an if statement that filters out all events but the rising edge of signal ck.

This process is functionally equivalent to the if template, with one subtle difference. In simulation, a design is initialised by running each process until the first wait statement is reached. However, in the sensitivity-list form of process, which cannot have a wait statement (this is a rule of VHDL), the whole process is executed once.

Therefore, the exact functional equivalence is:

process

begin

  if rising_edge(ck) then

    q <= d;

  end if;

  wait on ck;

end process;

In fact, the if template can also be written like this.

Like the if template, the condition can be expressed using the event expression:]

process(ck)

begin

  if ck'event and ck = '1' then

    q <= d;

  end if;

end process;

The sensitivity-list template is clumsy in comparison with the basic template, but comes into its own when dealing with asynchronous resets, which use a variant of it – asynchronous resets will be covered later in Section 9.9. Many designers therefore choose to use the sensitivity-list template because it is easy to convert it into a resettable form or back again. The result is that this form is often used for all registers, resettable or not.

9.4.4 Positioning the Wait Statement

As the previous example shows, the sensitivity-list template is equivalent to the if statement template, but with the wait statement moved to the end of the process. This means the two templates are equivalent in synthesis but subtly different in simulation. This difference can be exploited such that simulations will initialise correctly without any impact on the synthesis results. The general rule is that the wait statement can be placed either at the beginning or at the end of the process for it to be recognised as a register template.

Processes with the wait statement at the end will be executed in their entirety at the start of simulation whereas processes with the wait statement at the beginning will not be executed at the start of simulation.

Note also the section on register initial values in Section 9.12, since this also has an impact on the results of simulation but not synthesis.

9.4.5 Specifying the Active Edge

So far, all the examples have been sensitive to the rising-edge of the clock signal. It is just as easy to specify a register sensitive to the falling-edge of the clock.

The edge function template for a falling-edge sensitive register is:

process (ck)

begin

  if falling_edge(ck) then

    q <= d;

  end if;

end process;

All the other templates can be used to specify falling-edge sensitive registers too.

One common concern is that the event expression way of testing for an edge is naive when using std_logic, since it relies on only detecting the value at the end of the edge and doesn't check the value at the start of the edge. Thus, a transition from 'X' to '1' would be regarded as a rising edge. Some designers prefer to filter out these doubtful edges and only consider '0' to '1' transitions as rising edges. This leads to a more complicated form of the event expression being used:

process (ck)

begin

  if ck'event and ck = '1' and ck'last_value = '0' then

    q <= d;

  end if;

end process;

This template also tests the last value of ck, namely the value it had before the event. This is a rather paranoid approach, but some designers insist on it. In fact, the edge functions (rising_edge and falling_edge) implement this expression, so using the edge function template gives you this extra checking already.

It is in any case good practice to ensure that the clock signal only ever takes real values during simulation. This should be simple, since the clock distribution circuit should be a simple circuit. This would seem to be good design practice anyway, since synchronous design requires the clock to be reliable and glitch-free, which should show itself as a reliable series of real-value transitions during simulations.

The main source of non-real values is elaboration. All signals are initialised with the leftmost value of the type unless specified otherwise. This means that std_logic clocks will initialise to the metalogical value 'U' by default. To avoid undesirable behaviour that causes false triggering of registers during simulation, simply give the clock a real initial value when it is declared:

signal ck : std_logic := '0';

or

signal ck : std_logic := '1';

The use of the edge functions is probably the simplest solution to the specification of the active clock edge and is far more readable than the other forms. It is also standardised and widely regarded as best practice.

9.5 Register Types

The register model is not limited to one-bit signal types. Indeed it is very rare to only register a single bit. The signal being registered can be of any synthesis-supported type including integer types that are used in counters, enumeration types that are used in state machines, array types as used to describe buses and record types for buses split into fields.

The following example shows a register for an 8-bit signed signal.

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

entity Dtype is

  port (d : in signed(7 downto 0);

        ck : in std_logic;

         q : out signed(7 downto 0));

end;

architecture behaviour of Dtype is

begin

  process (ck)

  begin

    if rising_edge(ck) then

      q <= d;

    end if;

  end process;

end;

The register model is not limited to registering one signal. Any number of signals can be registered in the same registered process. All signals that are the targets of assignments in the process will be registered.

process (ck)

begin if rising_edge(ck) then

    q0 <= d0;

    q1 <= d1;

    q2 <= d2;

  end if;

end process;

In practice, register processes will be mixed with other logic, so an architecture will contain a number of concurrent assignments and processes representing combinational logic, intermixed with processes representing registers. Separating registers into separate design units simply makes the model unnecessarily complicated and obscures the design. It has only been done in these examples to show the processes in context.

9.6 Clock Types

So far, all the examples have used a clock that is of type std_logic. This is not a requirement of the template. It is possible to use any single-bit logical type for the clock signal. This includes the standard types bit, boolean and std_logic. It also includes any user-defined logical types.

However, it is good practice to use std_logic for clock signals, so all the examples in this book follow that practice.

9.7 Clock Gating

So far, all the register examples have been ungated. That is, they have taken a new value on every clock cycle. In practical circuit design it is very rare for a register to be ungated. Generally, some form of gating will be used.

From a circuit design viewpoint, there are two methods of register gating: clock gating and data gating. This section describes clock gating, the next section describes data gating.

In clock gating, the clock signal itself is switched on or off by some other control signal. However, it is rarely used and even considered bad practice to use clock gating in a synthesisable design. There are two very good reasons why clock gating is considered bad design practice for logic synthesis.

The first reason for not using clock gating is the advent of test synthesis tools. Test synthesis combines automatic overlay of scan paths with built-in test-pattern generation to give a complete test solution for a design, provided that it is a synchronous design. In particular, the scan overlay techniques require that the scan control circuitry can directly control the clocks in the design.

The second reason for not using clock gating is that the algorithms used in logic synthesis for logic minimisation cannot guarantee glitch-free logic. Indeed, this is not the intention, since the main thrust of logic minimisation is area saving and speed optimisation. It is therefore possible for a synthesis tool to create logic that is prone to generate glitches. Glitches in the control signal used to gate the clock would be disastrous to the correct functioning of the circuit.

As with many rules, there are exceptions. There are legitimate uses for clock gating, for example in the design of low-power circuits where clock gating is used to effectively disable a register, register bank or even a complete subsystem in a way that consumes no (or at least negligible) power. Indeed, simply charging and discharging the clock line itself can require significant power, so disabling the clock line to a region of a circuit that is not changing can give a significant power saving.

A good example of the application of clock gating would be a large register bank. In read mode, there is no need to clock the register bank – the registers need only be clocked in write mode. The clock could therefore be gated by the write enable control signal.

For this reason, clock gating is explained here with the warning that, if used, it is up to you to check that the synthesised clock circuits are safe. The simple rule for minimising the risk of glitches on gated clocks is to restrict the gating circuitry to be as simple as possible, with the gating signal driven through a minimum of control logic from a stable source – either from a register or a primary input that is itself guaranteed to be glitch-free.

The following example shows the use of a gated clock in a modified version of the basic D-type introduced at the start of the chapter.

library ieee;

use ieee.std_logic_1164.all;

entity GDtype is

  port (d, ck, en : in std_logic;

        q : out std_logic);

end;

architecture behaviour of GDtype is

  signal cken : std_logic;

begin

  cken <= ck when en = '1' else '1';

   process (ck)

  begin

    if rising_edge(cken)

      q <= d;

     end if;

   end process;

end;

The resulting circuit is illustrated in Figure 9.3

Figure 9.3 Clock gating circuit.

img

Note that the clock enable disables the clock by holding it high. This is based on the assumption that the enable is driven by another register output triggered by the same clock edge as this register, namely on the rising edge of the clock, so any change in the enable signal will happen immediately after a rising edge when the clock is high. Any glitches are likely to happen in this early part of the clock cycle, so choosing to disable by holding the clock high effectively makes the circuit immune to these glitches. Of course, once the clock goes low the circuit will become sensitive to glitches on the enable signal again, but provided the propagation time of the enable signal is less than the high period of the clock, this will be a safe design.

9.8 Data Gating

Data gating is the preferred, glitch-resistant, way of providing an enable control on a synthesisable register. Data gating is so-called because it controls the data input of the register instead of the clock input. Therefore, the register is continuously clocked. Data gating works by feeding back the register output to its input whilst the enable is in its inactive state. The basic circuit is shown in Figure 9.4

Figure 9.4 Data gating circuit.

img

A common concern is that the multiplexer is a large overhead. In practice most technologies have data-gating registers that have been optimised to eliminate most of this overhead and therefore have an area similar to a clock-gated register, but with the benefit that there are no special timing problems or testing problems with this circuit.

The VHDL for a data-gated register based on the previous example used for clock gating is:

library ieee;

use ieee.std_logic_1164.all;

entity GDtype is

  port (d, ck, en : in std_logic;

        q : out std_logic);

end;

architecture behaviour of GDtype is

begin

  process (ck)

  begin

  if rising_edge(ck) then

    if en = '1' then

       q <= d;

      end if;

  end if;

  end process;

end;

The combinational part of this register model looks exactly like a latch circuit. However, within a register model it becomes a data-gated register.

The reason this models a gated register is that, in simulation, the value of q is preserved until a new value is assigned to it. In this case, the assignment is bypassed as long as the enable signal is in its inactive state so the value of q is preserved even though the register is being clocked. In hardware terms, this is equivalent to the register output being connected back to the input. In fact, this is exactly what the synthesiser does. During the synthesis of the registered process, the structure of the if statement is analysed to see if there are any circumstances that would lead to the registered signal not getting a new value. If any such conditions are detected, they are implemented as feedback of the previous value. The conditions under which those signals require feedback become the control signals for the multiplexer.

A common pitfall when using the if template for registers is to try to combine the clock if statement with the enable if statement:

process (ck)

begin

  if rising_edge(ck) and en = '1' then

    q <= d;

  end if;

end process;

This will work perfectly in simulation, but may not be recognised as a register template during synthesis, because it does not correspond to any of the common register templates.

Note: this style of process has now been added to the VHDL synthesis standard and so some tools do allow it. However, adoption of the synthesis standard has been slow and patchy, so check the documentation to see if your synthesis tool does. If you are interested in cross-tool portability, avoid it.

The safe rule, which is probably still worth keeping to until all synthesisers conform to the new standard, is that the register template must be kept separate from its contents – in other words, the if statement that detects the clock edge must be kept separate from the if statement that implements the register enable. The safe and portable form of this process is:

process (ck)

begin

  if rising_edge(ck) then

    if en = '1' then

      q <= d;

    end if;

  end if;

end process;

9.9 Asynchronous Reset

It is sometimes useful to be able to reset a register to a predefined value. This is particularly true of wide, multi-bit registers.

There are two forms of register reset; asynchronous and synchronous. It is important to distinguish between the two forms and to use the correct one for the circumstances. This section describes asynchronous resets and the next section describes synchronous resets.

Asynchronous resets override the clock and so act immediately to change the value of the register. They are generally used to implement global system-level resets enforced from off-chip and therefore should only ever be controlled by primary inputs to a circuit. They are very sensitive to glitches on the reset signal because asynchronous reset controls are in effect always active. There is no guarantee that synthesised circuits are glitch free and there probably never will be such a guarantee. If they are used in a circuit, very careful design is needed to ensure safe operation.

In general, an asynchronous reset signal should be driven by a primary input as part of a system-wide asynchronous reset scheme. Internal resets should usually be synchronous.

Asynchronous resets require an alternative register template which is recognised as such by a synthesis tool. As with the basic D-type register, there are many different ways to write models that act like asynchronous resets during simulation, but only those that conform to the synthesis templates will be synthesisable.

A basic resettable register based on the original D-type from the beginning of the chapter is shown here.

process(ck, rst)

begin

  if rst = '1' then

    q <= '0';

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

The circuit represented by this example is shown in Figure 9.5. Note how the asynchronous clear input of a register has been used to implement the asynchronous reset.

Figure 9.5 Asynchronous reset.

img

The difference between this template and the basic register template is the addition of the reset signal to the sensitivity list of the process and the addition of the reset branch of the if statement before the clocked branch. This means that, during simulation, the process will execute due to any event on either the clock or the reset signal. The two branches of the if statement have conditions on them that filter out the relevant behaviour for both asynchronous reset and clock activation.

The reset condition must come as the first branch of the if statement because asynchronous resets override the clock behaviour. As long as the reset signal is in its active state, the reset value is asserted. When the reset is deactivated, the second branch of the if statement detects rising clock edges.

The reset value can be any constant value. This is particularly useful with multi-bit types, such as arrays, records, integers and enumeration types. Consider the following example where both q and d are 4-bit unsigned numbers:

process(ck, rst)

begin

  if rst = '1' then

    q <= to_unsigned(10, q'length);

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

The resulting reset circuitry is shown in Figure 9.6. Only the reset wiring has been shown to illustrate the way the reset signal is connected to either the preset or clear inputs of the individual registers to make up the bit-pattern for the reset value (equivalent to the unsigned integer value 10).

Figure 9.6 Asynchronous reset to a value.

img

Because the asynchronous reset is implemented by using the registers' preset and clear inputs, the reset value must be a constant. It is not possible to asynchronously reset to the value of a signal.

Another consequence of the synthesis rules for asynchronous resets is that it is not generally possible to have both asynchronous preset and clear controls on a synthesisable register. This is far from obvious since it is easy to model such a register. Here is a VHDL model for a register with preset and clear controls:

process(ck, preset, clear)

begin

  if preset = '1' then

    q <= '1';

  elsif clear = '1' then

    q <= '0';

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

However, this model is not generally synthesisable, although some synthesisers support it. The reason is that synthesis does not work only on single-bit registers (in other words, it does not work only on single flip-flops). Rather, most registers are multi-bit registers.

With multi-bit registers the concept of preset and clear is not very useful. What is far more useful is the concept of reset-to-value. In other words, the reset scheme for register synthesis is arranged so that the reset can set the register to any value, not just the limited values of all-zeros or all-ones.

As a consequence, synthesis vendors generally do not allow multiple reset controls, since that raises the possibility of enormous complexity in decoding the reset logic into preset and clear lines for each flip-flop in the register, with the possibility of introducing glitches on those control lines. Glitches in asynchronous reset circuits are absolutely fatal to a design because the reset lines act immediately and override the clock. Therefore, any such decoding is bad design practice. It is reasonable to disallow it.

It would be possible to recognise the special cases where no decoding logic was necessary (which happens when the values in each reset branch are mutually exclusive), but this is not generally done.

To give another full example, the following is an asynchronously resettable counter using type unsigned to count from 0 to 15 and round to 0 again. It exploits the characteristic of the arithmetic operators for the numeric types that wrap round on overflow. The asynchronous reset sets the counter to zero.

signal ck, rst : std_logic;

signal count : unsigned(3 downto 0);

...

process(ck, rst)

begin

  if rst = '1' then

    count <= to_unsigned(0, count'length);

  elsif rising_edge(ck) then

    count <= count + 1;

  end if;

end process;

9.9.1 Simulation Model of Asynchronous Reset

It is worth pausing at this point to look again at the simulation behaviour of the registered process when there is an asynchronous reset present. This is because the behaviour is rather more complex than the basic registered process described earlier.

Here is the simplest asynchronously resettable register model again:

process(ck, rst)

begin

  if rst = '1' then

    q <= '0';

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

Note that this is similar in form to the sensitivity-list template for the basic register. The difference is that there are now two signals in the sensitivity list, the reset signal and the clock signal. This means that the process will be activated when an event occurs on either signal.

Consider what happens when the reset signal rst goes high, activating the asynchronous reset. The event on rst causes the process to execute. Since the reset signal has gone high, the first branch of the if statement will be executed and the signal q will have a transaction posted on the transaction queue that sets it to '0'. The process then finishes execution and suspends. During process suspension, the signal q will be updated with will go to '0' (or stay at '0' if it is already at that value).

If clock events happen whilst the reset is activated, those events will trigger the process again, but this will simply cause the first branch of the if statement to be re-executed. This first branch has no test for an event on rst, it is simply selected by the condition rst = '1', so the fact that events are happening on the clock signal does not cause the second branch of the if statement to be executed. This means that, as expected, the reset signal overrides the clock and simply causes the reset condition to be reasserted.

There is a little inefficiency in this model during simulation, in that it does cause the reset condition to be reasserted due to events on the clock. These cause unnecessary extra transactions on q. However, none of these transactions cause events to be generated because q is being held at a constant value.

When the rst signal goes low again, this will cause the process to be triggered yet again. However, since the value of rst is now '0' the first branch of the if statement will not be selected. Since there has been no event on ck, the second branch will also not be selected. As a consequence, the falling edge of rst has no effect on the value of q.

On the next clock edge, the process will be executed again. The first branch of the if statement will not be selected because the value of rst is '0'. The second branch of the if statement will be selected if the clock edge is a rising edge. This causes the signal q to be assigned the value of input d.

It can be seen that the process now acts like a conventional registered process and will continue to do so as long as the reset signal remains inactivated.

In summary, the reset signal overrides the clock signal and acts as a level-sensitive control signal. The reset is activated immediately and is not synchronised to the clock. The clocked part of the model acts like a conventional register when the reset is inactivated.

There are many other ways in which this behaviour could be modelled in VHDL. However, this particular model has been chosen for its relative simplicity. Remember that synthesisers recognise this as an asynchronously resettable register by matching it to a template. If a different model is used, it may simulate correctly but it will not match the template during synthesis.

9.9.2 Asynchronous Reset Templates

When discussing simple registers, four standard templates were discussed. Not all of these templates can be converted into asynchronously resettable registers. In fact, there are only two templates that have resettable versions: these are the sensitivity-list template and the if-statement template.

The sensitivity-list is the template that has just been used in the previous sections. Its basic form is:

process(ck, rst)

begin if rst = '1' then

    q <= '0';

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

The if-statement template is similar to the above template, but uses a wait statement instead of the sensitivity list:

process

begin

  wait on ck, rst;

  if rst = '1' then

    q <= '0';

  elsif rising_edge(ck) then

    q <= d;

  end if;

end process;

Both templates use the same structure of if statement, it is just the way in which the processes are sensitised to their control signals that differs between the two templates.

9.10 Synchronous Reset

A synchronous reset control uses a very similar structure to the data-gating circuit described in Section 9.8. The only difference is that, instead of feeding back the register output to its input, a reset value is fed to the register input when the reset signal is in its active state.

In contrast to asynchronous resets, synchronous resets take effect on the active edge of the clock and so fit the synchronous design philosophy. As such, they require no special handling at all and are simply a special case of combinational logic with a registered output. Synchronous resets can be controlled by any data signal in a circuit and are insensitive to glitches. All resets driven by logic within the circuit should generally be synchronous resets.

The VHDL for a synchronously resettable process is shown below.

signal d, ck, rst : std_logic;

signal q : std_logic;

...

process (ck)

begin

  if rising_edge(ck) then

    if rst = '1' then

      q <= '0';

    else

      q <= d;

    end if;

  end if;

end process;

The circuit created for this example is shown in Figure 9.7.

Figure 9.7 Synchronous reset.

img

The example shows the register being reset to '0'. There is no restriction on the value to which the register is reset, a feature that becomes particularly useful with other types, such as arrays, records, integers and enumeration types.

Consider the following example where both q and d are 4-bit signals of type unsigned:

signal d : unsigned (3 downto 0);

signal ck, rst : in std_logic;

signal q : out unsigned (3 downto 0);

...

process (ck)

begin

  if rising_edge(ck) then

    if rst = '1' then

      q <= to_unsigned(10, q'length);

    else

      q <= d;

    end if;

  end if;

end process;

The resulting reset circuitry is shown in Figure 9.8. Only the reset wiring has been shown to illustrate the way the reset signal controls the individual registers to make up the bit-pattern for the reset value (equivalent to the unsigned integer value 10 in this example).

Figure 9.8 Synchronous reset to a value.

img

In fact, the synchronous reset is a special case of a multiplexed register and the ‘reset' value needn't even be a constant value. It could be another signal.

To give another example, the following is a description of a resettable counter using types unsigned to count from 0 to 15 and then wrap round back to 0. It is the synchronously resettable version of the example in the previous subsection.

signal ck, rst : std_logic;

signal count : unsigned(3 downto 0);

...

process (ck)

begin

  if rising_edge(ck) then

    if rst = '1' then

      count <= to_unsigned(0, count'length);

    else

      count <= count + 1;

    end if;

  end if;

end process;

Note the way in which the reset if statement is nested inside the register template if statement. The argument here is exactly the same as for data gating – the register template part must be kept separate from the combination part of the model.

9.11 Registered Variables

It is also possible to create registers using variables. Remember that a registered process is interpreted by synthesis as a combinational circuit and then placing a register on every signal assigned to in the registered process and every feedback path. This usually means that variables do not get registered. However, if there is feedback of a previous variable value, then this feedback must be via a register to make the process synchronous.

Here is an alternative way of writing a counter, using the type unsigned that implements integers with wrap-around:

process (ck)

  variable count : unsigned (7 downto 0);

begin

  if rising_edge(ck) then

    if rst = '1' then

      count := to_unsigned(0, count'length);

    else

      count := count + 1;

    end if;

    result <= count;

  end if;

end process;

In this case, in the else part of the inner if statement, the previous value of count is being read to calculate the next value. This gives us the feedback since the read occurs before the assignment.

Note that this example actually creates two registers. According to the feedback rules, variable count will be registered. However, signal result will also have a register on it, because all signals assigned to in a registered process get registered. The extra register will always contain the same value as the register for count. This redundant register will be optimised away by the synthesiser, so can be ignored.

9.12 Initial Values

There has already been a discussion of initial values in Section 8.3. However, it is worth having a reminder of the pitfalls associated with initial values at this stage since the most common problems with initial values are found in registered logic.

Every signal in a VHDL model is given an initial value during the pre-simulation period known as elaboration. The initial value can be user defined in the signal declaration or it defaults to the leftmost value of the signal type or subtype. This means that the simulation is guaranteed to start in a known state for every simulation on any simulator. Specifically, it means that counters and state machines will always start at a known state during simulation.

There is, however, no hardware equivalent of elaboration and so initial values are ignored by synthesis. This means that registers, and specifically counters and state machines, will start in an unknown state. It is the designer's responsibility to allow for this by ensuring that a circuit can be put into a known state either by designing in an initialisation scheme or by providing an external reset. Either scheme can be tested in simulation by giving registered signals a `bad' initial value and then ensuring that the circuit initialises correctly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.117.109