3
\$\begingroup\$

I'm a complete beginner with VHDL and an almost beginner with digital logic and I'm having a problem working through a book I'm reading. In particular, an exercise asks to build a counter with an enable and a reset switch. I implemented it one way and the compiler (Xilinx ISE) complained about it enough that I took the path of least resistance and changed it to something that I thought was sub-optimal. But now I'd like to know what's wrong with my original code.

Here's the 'working' example:

-- EXAMPLE 1
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity clocks is
    Port ( 
           switch0 : in  STD_LOGIC;
           switch1 : in  STD_LOGIC;
           LEDs : out  STD_LOGIC_VECTOR (7 downto 0);
           clk  : in STD_LOGIC
           );
end clocks;

architecture Behavioral of clocks is
 signal counter : STD_LOGIC_VECTOR( 29 downto 0 ) := ( others => '0' );
begin

LEDs <= counter( 29 downto 22 );

clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( clk ) then
            if switch0 = '1' then
                counter <= counter + 1;
            elsif switch1 = '1' then
                counter <= ( others => '0' );
            end if;
        end if;
    end process;

end Behavioral;

So basically you set switch0 and the counter begins counting up. Clear switch0 and it stops counting up. Set switch1 while switch0 is clear and the counter will reset to 0. Set switch1 while switch0 is set and the counter doesn't reset.

What I really wanted was for the reset switch1 to be able to clear the counter even if switch0 was set. I wanted the reset switch1's rising edge to reset the counter back to 0 while the counter keeps climbing if the enable switch0 is still set.

I thought I could get a bit closer by changing the process to this:

-- EXAMPLE 2
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( clk ) then
            if switch1 = '1' then
                counter <= ( others => '0' );
            elsif switch0 = '1' then
                counter <= counter + 1;
            end if;
        end if;
    end process;

But strangely, on the Spartan 6 chip I'm using, the enable switch0 still overrides the reset switch1? Then I tried this:

-- EXAMPLE 3
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( switch1 ) then
            counter <= ( others => '0' );
        else
            if rising_edge( clk ) then
                if switch0 = '1' then
                    counter <= counter + 1;
                end if;
            end if;
        end if;
    end process;

This should be exactly what I want, but Xilinx ISE is even less happy with this - I get these warnings from the compiler:

WARNING:Xst:647 - Input <switch0> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved. 
WARNING:Xst:647 - Input <clk> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:2404 -  FFs/Latches <counter<1:30>> (without init value) have a constant value of 0 in block <clocks>. 
WARNING:Par:288 - The signal clk_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:288 - The signal switch0_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:288 - The signal switch1_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:283 - There are 3 loadless signals in this design. This design will cause Bitgen to issue DRC warnings.

switch0 is never used? Neither is clk? Where did they go? And when I ran it on my spartan 6, it did the same thing as the 'working' example. I tried this too:

-- EXAMPLE 4
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( switch1 ) then
            counter <= ( others => '0' );
        end if;
        if rising_edge( clk ) then
            if switch0 = '1' then
                counter <= counter + 1;
            end if;
        end if;
    end process;

This won't even build - in implementation I get these warnings/errors:

WARNING:Xst:647 - Input <clk> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:3002 - This design contains one or more registers/latches that are directly
   incompatible with the Spartan6 architecture. The two primary causes of this is
   either a register or latch described with both an asynchronous set and
   asynchronous reset, or a register or latch described with an asynchronous
   set or reset which however has an initialization value of the opposite 
   polarity (i.e. asynchronous reset with an initialization value of 1).

Can anyone clue me in as to what is it about these re-worked processes that isn't kosher? I understand that there are a lot of things that can be coded in VHDL but that can't be translated to a hardware design, and the second of my revisions seems to fall into that category, but can anyone explain to me what's going on here? What's wrong with a latch with async resets and sets? What's wrong with nested if-then-end blocks? What am I doing wrong here?

UPDATE: As in the answer, examples 3 and 4 could never have worked because a process can't be clocked multiple times - it can only have one signal with a rising_edge() or 'event on it. Example 2, it turns out, does actually work and do what I want it to do. I ran it through the simulator and it seemed ok, and then when I synthesized it again for hardware, it worked just fine on my spartan 6. I must have done something strange the first time I tried it?

\$\endgroup\$
6
\$\begingroup\$

the rising_edge() parameter is only really for clock signals. One option would be the following:

clk_proc: process( clk, switch1, switch0 )
begin
    if switch1 = '1' then
        counter <= ( others => '0' );
    elseif rising_edge( clk ) then
        if switch0 = '1' then
            counter <= counter + 1;
        end if;
    end if;
end process;

This is known as an asynchronously reset process, which means that it'll reset the counter whenever reset (switch1) is high, regardless of clock state. A synchronously reset process would look the same as your second example:

clk_proc: process( clk, switch1, switch0 )
begin
    if rising_edge( clk ) then
        if switch1 = '1' then
            counter <= ( others => '0' );
        elsif switch0 = '1' then
            counter <= counter + 1;
        end if;
    end if;
end process;

You did state though that your second example didn't work as expected. My suspicion is that this is because the signals driving it are switches rather than clocked logic signals. Switches don't actually switch instantaneously, instead bouncing between on and off a few times before settling. You need to implement some sort of debounce process to produce a signal which is synchronous with your clock and can control your timer.

It's also worth mentioning that FPGA design is a lot easier if you simulate stuff first. Xilinx ISE includes a simulator - create a testbench and use it! That lets you easily see what's happening in your implementation before you ever get near hardware.

\$\endgroup\$
  • \$\begingroup\$ Ah - yes - I've read that you're only allowed one rising_edge() or 'event instance in a process, which would explain why the last two solutions of mine didn't and wouldn't work. I'm still wrapping my head around what a 'process' is in digital circuit terms - the reasoning I heard was that you can't have 2 rising_edge()'s because it would be like having two clock signals for a flip-flop. \$\endgroup\$ – Ted Middleton Mar 20 '14 at 15:58
  • \$\begingroup\$ You're absolutely right about the reset and enable - they are switches. Thanks for the debouncing insight. I'm marking this as the answer. \$\endgroup\$ – Ted Middleton Mar 20 '14 at 15:59
  • 1
    \$\begingroup\$ @TedMiddleton A process in digital terms ends up being a number of registers in series. In order to demonstrate this, code a process and take a look at the generated RTL schematic. Here's an example. I've marked the processes, and I've also marked the asynchronous parts of the code. i.imgur.com/HGH1sU9.png \$\endgroup\$ – stanri Mar 21 '14 at 6:56
  • 1
    \$\begingroup\$ PS: That code was not the actual code used to generate the schematic, the actual code used was a lot more complicated, but it does demonstrate the kind of resulting architecture you see for both. \$\endgroup\$ – stanri Mar 21 '14 at 7:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.