Saturday 14 January 2017

Using a clock signal

Up to now our project has been pure combinatorial logic-- the output signals of the circuit are just a function of its inputs, and it has no internal state information (i.e. no storage elements are used). As you may have discovered, the order of the statements made no difference to the design-- all assignments were active all the time. Now that we can perform addition we should be able to make a project that implements a counter. However, one thing is missing-- the ability to track the passage of time.

This is the big difference between designing in VHDL and programming. In a program there is the "thread of execution" and its associated state information-- the program counter, the stack pointer, the contents of registers and so on. In VHDL there isn’t.

What is needed is somewhere to store the values of signals and some way of synchronizing when these stored values should change. To progress our designs further we need flip-flops and a clock signal.

Flip-flops

A flip-flop stores one bit of information, and this stored bit is updated when its "Clock Enable" signal is asserted, and the desired transition occurs on the clock signal-- either from 1 to 0 (a falling edge) or from 0 to 1 (a rising edge).

Clock signals

A clock signal is an input that has regular transitions between the low and high state, and is therefore very useful for keeping all the parts of a design in sync. The Papilio One has a clock signal running at 32,000,000 cycles per second (32MHz), whereas the Basys2 board has a clock signal that can run at either 25,000,000, 50,000,000 or 100,000,000 cycles per second (25MHz, 50MHz or 100MHz). This is not as big a difference between boards as it sounds, because later on we will see how the FPGA can be used to generate other frequencies from this reference clock.

This chapter’s projects will be based around a binary counter running at 32MHz (so when a Basys2 is used it will run about 50% quicker). As 32,000,000 is quite a big number (a million times faster than what human eyes can see) we need some way to slow it down. The easiest way is to create a 28-bit counter, and only show the top eight bits on the LEDs.

But first we need to know about VHDL processes and the "IF" statement.

VHDL 

From a designer’s point of view, a VHDL process is a block of statements that operate sequentially and is triggered when any of the signals that it is ’sensitive’ to change value.

Here is a process called my_process that is sensitive to two signals (input1 and input2):

my_process: process (input1, input2)
begin
output1 <= input1 and input2;
end process;

Note

• Any event (change of value) on the signals listed in the sensitivity list is what triggers the process.
• For purely combinatorial logic it should be all inputs, and none of the signals assigned within the process should be in its sensitivity list (or it will be evaluated multiple times during simulation)
• For a clocked process the sensitivity list should be the clock signal and all asynchronous signals-- usually the clock signal and maybe an async reset
• If you don’t follow these rules you will get lots of odd behaviors in simulations as your process will be triggered when you don’t expect, or fail to trigger at all. When you try to implement the design in hardware it will fail to work anything like it did in simulation.

The usefulness of processes is that they allow you to use sequential statements, the most useful of which is the IF statement.

IF statements

VHDL has an "if" statement, much like any other language. This syntax is:

if [condition] then
[statements]
end if;
and
if [condition] then
[statements]
else
[statements]
end if;
Remember that "if" statements can only be used inside a process block - if you attempt to use them outside of a process you will get compilation errors. Also remember that there is a ; following the "end if" statement!

VHDL supports all the normal comparisons you would expect-- just be aware that "not equals" is "/=" - very strange

Coding style tip

If you are used to C, you might be tempted to use something like the following to implement a counter:

if(counter < counts-1)
counter++;
else
counter=0;

This is bad form as it is a comparison of value, and not a test for equality. The tools might implement this using a "math" function, rather than a logic function. Due to the time that ’carries’ take (about 0.05ns per bit) this may lower your design’s performance and increases resource usage.
If you can ensure that the value of "counter" stays between "0" and "counts-1" then it is far better to use the VHDL equivalent of the following:

if(counter == counts-1)
counter=0;
else
counter++;

This is because test for equality are much quicker as the carry chain is not used.

Detecting the rising edge of a clock

Any of the normal tests (equality, inequality. . . ) used in programming can be used to test values against each other. If we use these tests on our signals we usually end up generating combinatorial logic. For example:

select_switch: process(switch(0), switch(1), switch(2))
begin
if switch(0) = ’1’ then
result <= switch(1);
else
result <= switch(2);
end if;
end process;

This could equally be implemented with the following concurrent (always active) statement:

result <= (switch(1) and switch(0)) or (switch(2) and not(switch(0)));

We can also look for a signal’s transition as the condition that triggers something to happen. The easiest way to do this is to use the "rising_edge" function:

if rising_edge(clock_signal) then
result <= switch(1);
end if;

Another common way that you might see is to test the "event attribute" of the clock signal, which evaluates to true if this signal is the one that triggered the process to be evaluated, and then also check that the clock signal is 1. Together these tests can detect the rising edge of the clock:

if clock_signal’event and clock_signal = ’1’ then
[statements]
end if;

Although common in older textbooks, the use of "clock_signal’event and clock_signal = ’1’" is now discouraged. It assumes that the clock signal was a 0 before the event was triggered, and can cause problems during simulation.

Declaring storage elements

Storage elements are declared just like a local signal. It is how you use them that implicitly makes them act as storage. If a signal only gets assigned during a clock transition it will be implemented using flip-flops:
...
architecture behavioural of counter
signal counter : STD_LOGIC_VECTOR(7 downto 0);
begin
count: process(clock)
begin
if rising_edge(clock) then
counter <= counter+1
end if;
end process;
end architecture;
...
The other situation that triggers a signal to be implemented as a flip-flop is when not all paths through a process assign it a value:

count: process(clock)
begin
if rising_edge(clock) then
if switch1 = ’1’ then
if switch2 = ’1’ then
output_signal <= ’1’;
else
output_signal <= ’0’;
end if;
end if;
end if;
end process;

A flip-flop will be assigned to hold output_signal to keep its value when switch1 changes from 1 to 0.

As with programming languages, it is always good practice to assign an initial value to your storage elements:

signal counter : STD_LOGIC_VECTOR(7 downto 0) := "00000000";

or perhaps more conveniently when working with larger signals:

signal counter : STD_LOGIC_VECTOR(7 downto 0) := (others => ’0’);

It is usually safe to assume that uninitialized signals will be zero, however simulations will show the signal as being ’undefined’,
as will the result of any operations performed on that signal.

So here is the finished 8-bit counter:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Switches_LEDs is
Port ( switches : in STD_LOGIC_VECTOR(7 downto 0);
LEDs : out STD_LOGIC_VECTOR(7 downto 0);
clk : in STD_LOGIC
);
end Switches_LEDs;
architecture Behavioral of Switches_LEDs is
signal counter : STD_LOGIC_VECTOR(7 downto 0) := (others => ’0’);
begin
clk_proc: process(clk)
begin
if rising_edge(clk) then
counter <= counter+1;
end if;
end process;
end Behavioral;

Warning

Currently the project doesn’t use the LEDs output-- if you build using this code as-is the counter won’t have any usefuleffect and will be optimized out, leaving you with an empty design!

No comments:

Post a Comment