Ich erstelle ein n-Bit-Schieberegister. Wenn das Freigabesignal hoch ist, möchte ich, dass das Schieberegister n-mal verschoben wird, unabhängig davon, ob die Freigabe weiterhin hoch oder niedrig ist. Ich habe eine for-Schleife eingefügt, um n-mal innerhalb eines Prozesses zu verschieben. Mein Code ist unten angegeben.
Ich glaube nicht, dass die for-Schleife funktioniert, da die Verschiebung nicht auf n-mal beschränkt ist. Wo gehe ich falsch?
library ieee;
use ieee.std_logic_1164.all;
entity SReg is
generic (
n : integer := 4
);
port(
clk: in std_logic;
reset: in std_logic;
enable: in std_logic; --enables shifting
parallel_in: in std_logic_vector(n-1 downto 0);
s_in: in std_logic; --serial input
s_out: out std_logic --serial output
);
end SReg;
architecture behavioral of SReg is
signal temp_reg: std_logic_vector(n-1 downto 0) := (Others => '0');
begin
process (clk,reset)
begin
if (reset = '1') then
temp_reg <= parallel_in;
elsif (clk'event and clk='1') then
if (enable ='1') then
--shifting n number of bits
for i in 0 to n-1 loop
s_out <= temp_reg(0);
temp_reg <= s_in & temp_reg(n-1 downto 1);
end loop;
end if;
end if;
end process;
end behavioral;
In VHDL wird eine for-Schleife in Nullzeit ausgeführt . Das heißt, anstatt zwischen jeder Iteration einen Taktzyklus zu warten, wird die gesamte Schleife innerhalb eines Taktzyklus durchlaufen, wobei am Ende nur das Endergebnis der Schleife angezeigt wird. Dies ist, was in Ihrem Code passiert. Die gesamte Schleife wird in einem einzigen Taktzyklus ausgeführt.
Was Sie wirklich wollen, ist eine Schleife, bei der jede Iteration an einer neuen Taktflanke auftritt. Dadurch kann s_in in jedem Taktzyklus aus s_out verschoben werden.
Das Durchführen einer Schleife, bei der jede Iteration an einer Taktflanke auftritt, erfordert keinen for-Schleifenbefehl, sondern nutzt stattdessen die Sensitivitätsliste des Prozesses. Hier ist wie:
Jedes Mal, wenn sich eines der Signale auf der Empfindlichkeitsliste (in diesem Fall „clk, reset“) ändert, wird ein Prozess ausgelöst. Das bedeutet, dass der Prozess bereits bei jedem Taktzyklus eine Schleife durchläuft (wenn sich eine Uhr in der Sensitivitätsliste befindet). Sie können dies zu Ihrem Vorteil nutzen, um eine Operation vom Typ for-Schleife auszuführen, bei der jede Iteration der Schleife in einem Taktzyklus erfolgt.
Zuerst brauchen Sie einen Zähler:
process(clk,reset)
variable shift_counter: integer := 0;
begin
shift_counter
verfolgt, wie viele Iterationen (oder Verschiebungen) bisher stattgefunden haben. Sie werden mit vergleichen shift_counter
, um n-1
zu sehen, ob Sie schon fertig sind.
Als nächstes könnte es eine gute Idee sein, an die Zustände zu denken, in denen sich Ihr Prozess befinden wird. Vielleicht ein Wartezustand, wenn der Prozess nicht wechselt, und ein Wechselstatus, wenn er es ist.
Die Zustandssignaldefinition:
TYPE POSSIBLE_STATES IS (waiting, shifting);
signal state : POSSIBLE_STATES;
Im eigentlichen Ablauf:
case state is
when waiting =>
Okay, was passiert also, wenn wir auf eine Freigabe warten? Es wäre eine gute Idee, alle (gesteuerten) Variablen auf einen bekannten Wert zu setzen. Das bedeutet, dass vielleicht so etwas eine gute Idee ist:
shift_counter := 0;
temp_reg <= parallel_in;
s_out <= '0';
Dies ist nützlich, da Sie dann genau wissen, was Ihre Signalwerte sind, wenn die Freigabe hoch geht. Außerdem können Sie am Ende der Schicht den Zustand wieder auf "Wartend" ändern, um sich wieder für die Freigabe bereit zu machen.
Was wird also einen Zustandswechsel vom Warten zum Verschieben auslösen? Das ist einfach:
if(enable = '1') then
state <= shifting;
else
state <= waiting;
end if;
Okay, also nächster Zustand. Verschiebung.
Zuerst wollen wir den Verschiebungszähler inkrementieren und die eigentliche Verschiebung durchführen:
when shifting =>
shift_counter := shift_counter + 1;
s_out <= temp_reg(0);
temp_reg <= s_in & temp_reg(n-1 downto 1);
Und dann auch erkennen, wann das Schalten beendet ist, um den Schaltzustand zu verlassen und zum Warten zurückzukehren:
if (shift_counter >= n-1) then
state <= waiting;
else
state <= shifting;
end if;
Und das ist es!
Beachten Sie im folgenden Codeabschnitt, dass der „Zurücksetzen“-Zustand und der „Warten“-Zustand unterschiedlich sind. Dies ist nützlich, da das asynchrone Zurücksetzen im Allgemeinen nur beim Start auftritt und während dieser Zeit keine Datenverarbeitung erwartet wird. Indem wir den temp_reg <= parallel_in
in den Wartezustand (außerhalb des asynchronen Resets) versetzen, ermöglichen wir, dass die Modulsteuerung parallel_in
korrekt startet, ohne dass während des Resets Daten gesendet werden müssen. Außerdem kann jetzt bei Bedarf in den Wartezustand eingetreten werden, ohne dass ein asynchroner Reset durchgeführt werden muss.
Beachten Sie auch, dass ich in meinem Prozess nur 3 Signale (4, die die Variable zählen) und nur diese Signale ansteuere. Wenn ein Signal in einem Prozess getrieben wird, sollte es nirgendwo anders als in diesem Prozess getrieben werden. Nicht außerhalb des Prozesses, nicht in einem anderen Prozess. Ein Signal wird innerhalb eines Prozesses und nur eines Prozesses getrieben. Sie können das Signal an anderen Stellen mit anderen Signalen vergleichen (if-Anweisungen und dergleichen), aber geben Sie dem Signal nirgendwo einen Wert, außer in einem Prozess. Und im Allgemeinen wird es im Rücksetzabschnitt definiert und dann, wo immer es notwendig ist, im eigentlichen Prozess. Aber nur 1 Prozess. Wenn mir das gesagt worden wäre, hätte es mir beim Lernen viel Zeit gespart.
Hier ist der gesamte Code in einem Stück:
library ieee;
use ieee.std_logic_1164.all;
entity SReg is
generic ( n : integer := 4);
port( clk: in std_logic;
reset: in std_logic;
enable: in std_logic; --enables shifting
parallel_in: in std_logic_vector(n-1 downto 0);
s_in: in std_logic; --serial input
s_out: out std_logic --serial output
);
end SReg;
architecture behavioral of SReg is
signal temp_reg: std_logic_vector(n-1 downto 0) := (Others => '0');
TYPE POSSIBLE_STATES IS (waiting, shifting);
signal state : POSSIBLE_STATES;
begin
process(clk,reset)
variable shift_counter: integer := 0;
begin
if(reset = '1') then
temp_reg <= (others => '0');
state <= waiting;
shift_counter := 0;
elsif(clk'event and clk='1') then
case state is
when waiting =>
shift_counter := 0;
temp_reg <= parallel_in;
s_out <= '0';
if(enable = '1') then
state <= shifting;
else
state <= waiting;
end if;
when shifting =>
shift_counter := shift_counter + 1;
s_out <= temp_reg(0);
temp_reg <= s_in & temp_reg(n-1 downto 1);
if (shift_counter >= n-1) then
state <= waiting;
else
state <= shifting;
end if;
end case;
end if;
end process;
end behavioral;
Die Antwort von @stanri ist beeindruckend gründlich und ziemlich genau ... wenn ich die erste Aussage zusammenfassen / verdeutlichen darf, drückt die "for" -Anweisung in einer HDL einfach "syntaktische Replikation" aus, nicht "sequentielle Ausführung".
Das heißt, es erzeugt einfach mehr Hardwareelemente (Gatter) und informiert den Prozessfluss nicht. Ich würde sagen, die Schleife wird zur Ausarbeitungszeit (Kompilierung) erweitert, nicht dass sie "in Nullzeit ausgeführt" wird, schließlich wird es zur Laufzeit immer noch eine Ausbreitungsverzögerung durch die vom "for"-Konstrukt generierten Elemente geben.
Beginnen Sie nicht mit dem Schreiben von VHDL-Code, beginnen Sie mit dem Zeichnen von Logikschemata (zumindest auf einer gewissen Abstraktionsebene). Letztendlich ist HDL nur eine textbasierte Möglichkeit, den Inhalt von Logikschemata auszudrücken.
Orange
sherrelbc
If a signal is driven in one process, it shouldn't be driven anywhere else but that process.
Das macht sehr viel Sinn. Allzu oft werde ich mit dem Fehler "mehrere Treiber" konfrontiert! Was genau wirds_in
hier gemacht? Dieses Schieberegister scheint die parallelen Daten zwischenzuspeichern und dann seriell herauszuschieben.Stanri
Stanri
Shashank-VM