Warum funktioniert mein einfacher Zähler VHDL nicht? Wohin gingen meine Signale?

Ich bin ein absoluter Anfänger mit VHDL und ein fast Anfänger mit digitaler Logik und ich habe ein Problem beim Durcharbeiten eines Buches, das ich gerade lese. Insbesondere soll in einer Übung ein Zähler mit einem Enable- und einem Reset-Schalter gebaut werden. Ich habe es auf eine Weise implementiert und der Compiler (Xilinx ISE) hat sich so sehr darüber beschwert, dass ich den Weg des geringsten Widerstands genommen und es in etwas geändert habe, das ich für suboptimal hielt. Aber jetzt würde ich gerne wissen, was mit meinem ursprünglichen Code falsch ist.

Hier ist das "funktionierende" Beispiel:

-- 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;

Also im Grunde setzen Sie switch0 und der Zähler beginnt hochzuzählen. Löschen Sie switch0 und es hört auf zu zählen. Stellen Sie Schalter1 ein, während Schalter0 frei ist, und der Zähler wird auf 0 zurückgesetzt. Stellen Sie Schalter1 ein, während Schalter0 gesetzt ist und der Zähler nicht zurückgesetzt wird.

Was ich wirklich wollte, war, dass der Reset-Schalter1 den Zähler löschen kann, auch wenn Schalter0 gesetzt war. Ich wollte, dass die steigende Flanke des Reset-Schalters 1 den Zähler auf 0 zurücksetzt, während der Zähler weiter steigt, wenn der Aktivierungsschalter 0 noch gesetzt ist.

Ich dachte, ich könnte ein bisschen näher kommen, indem ich den Prozess so ändere:

-- 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;

Aber seltsamerweise überschreibt der Aktivierungsschalter0 auf dem Spartan 6-Chip, den ich verwende, immer noch den Reset-Schalter1? Dann habe ich das versucht:

-- 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;

Das sollte genau das sein, was ich will, aber Xilinx ISE ist damit noch weniger zufrieden - ich bekomme diese Warnungen vom 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 wird nie verwendet? clk auch nicht? Wohin sind sie gegangen? Und als ich es auf meinem Spartan 6 ausführte, tat es dasselbe wie das „funktionierende“ Beispiel. Das habe ich auch probiert:

-- 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;

Dies wird nicht einmal erstellt - in der Implementierung erhalte ich diese Warnungen/Fehler:

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).

Kann mir jemand sagen, was an diesen überarbeiteten Prozessen nicht koscher ist? Ich verstehe, dass es viele Dinge gibt, die in VHDL codiert werden können, aber nicht in ein Hardwaredesign übersetzt werden können, und die zweite meiner Überarbeitungen scheint in diese Kategorie zu fallen, aber kann mir jemand erklären, was hier vor sich geht? ? Was ist falsch an einem Latch mit asynchronen Resets und Sets? Was ist falsch an verschachtelten Wenn-Dann-End-Blöcken? Was mache ich hier falsch?

UPDATE: Wie in der Antwort hätten die Beispiele 3 und 4 niemals funktionieren können, da ein Prozess nicht mehrmals getaktet werden kann - er kann nur ein Signal mit einer steigenden Flanke () oder einem Ereignis darauf haben. Es stellt sich heraus, dass Beispiel 2 tatsächlich funktioniert und das tut, was ich will. Ich habe es durch den Simulator laufen lassen und es schien in Ordnung zu sein, und als ich es dann erneut für Hardware synthetisierte, funktionierte es auf meinem Spartan 6 einwandfrei. Ich muss etwas Seltsames getan haben, als ich es das erste Mal ausprobiert habe?

Antworten (1)

der rise_edge() Parameter ist eigentlich nur für Taktsignale. Eine Möglichkeit wäre folgende:

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;

Dies wird als asynchroner Reset-Prozess bezeichnet, was bedeutet, dass der Zähler immer dann zurückgesetzt wird, wenn reset (switch1) hoch ist, unabhängig vom Taktstatus. Ein synchron zurückgesetzter Prozess würde genauso aussehen wie Ihr zweites Beispiel:

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;

Sie haben jedoch angegeben, dass Ihr zweites Beispiel nicht wie erwartet funktioniert hat. Mein Verdacht ist, dass dies daran liegt, dass die Signale, die es ansteuern, eher Schalter als getaktete Logiksignale sind. Schalter schalten nicht wirklich sofort, sondern hüpfen ein paar Mal zwischen Ein und Aus, bevor sie sich niederlassen. Sie müssen eine Art Entprellprozess implementieren, um ein Signal zu erzeugen, das mit Ihrer Uhr synchron ist und Ihren Timer steuern kann.

Erwähnenswert ist auch, dass das FPGA-Design viel einfacher ist , wenn Sie die Dinge zuerst simulieren. Xilinx ISE enthält einen Simulator – erstellen Sie eine Testbench und verwenden Sie sie! So können Sie leicht sehen, was in Ihrer Implementierung passiert, bevor Sie sich jemals der Hardware nähern.

Ah - ja - ich habe gelesen, dass Sie nur eine rise_edge()- oder 'event-Instanz in einem Prozess haben dürfen, was erklären würde, warum die letzten beiden Lösungen von mir nicht funktioniert haben und nicht funktionieren würden. Ich mache mir immer noch Gedanken darüber, was ein "Prozess" in Bezug auf digitale Schaltungen ist - die Begründung, die ich gehört habe, war, dass Sie keine 2 rise_edge () haben können, weil dies so wäre, als hätten Sie zwei Taktsignale für ein Flip-Flop .
Sie haben absolut Recht mit dem Zurücksetzen und Aktivieren - das sind Schalter. Danke für den entrümpelnden Einblick. Ich markiere dies als Antwort.
@TedMiddleton Ein digitaler Prozess ist eine Reihe von Registern in Reihe. Um dies zu demonstrieren, codieren Sie einen Prozess und werfen Sie einen Blick auf das generierte RTL-Schema. Hier ist ein Beispiel. Ich habe die Prozesse markiert, und ich habe auch die asynchronen Teile des Codes markiert. i.imgur.com/HGH1sU9.png
PS: Dieser Code war nicht der tatsächliche Code, der zum Generieren des Schaltplans verwendet wurde, der tatsächlich verwendete Code war viel komplizierter, aber er zeigt die Art der resultierenden Architektur, die Sie für beide sehen.