Ich bin neu in der VHDL- und FPGA-Programmierung, und obwohl ich eine ganze Reihe von Problemen kenne, die zwischen Simulation und Synthese auftreten können, hat mich dieses spezielle Problem ratlos gemacht.
Mein Design ist ziemlich einfach:
Der SPI-Bus nimmt ein Datenbyte vom darüber liegenden Modell entgegen, das er dann bei steigenden Taktflanken herausschiebt.
Beim Zurücksetzen werden alle Signale auf Anfangswerte gesetzt. Ich triggere diesen Eingang, indem ich einen Draht zwischen dem Reset-Pin und Masse an meinem FPGA anschließe.
Die SPI-Schnittstelle tritt dann in eine "Warte"-Phase ein, in der sie auf ein Signal von dem obigen Modell wartet, das immer hoch ist, daher verlässt sie diese Wartephase innerhalb eines Taktzyklus.
Er tritt dann in eine "Schreib"-Stufe ein, wo er bei der ersten ansteigenden Flanke des Takteingangs das nächste Bit von seinem Dateneingang zu seinem Datenausgang schreibt und den Taktausgang niedrig schreibt. Bei der folgenden Taktflanke schreibt es seinen Taktausgang hoch, verschiebt die Daten zu dem Gerät, auf das es warten würde, das mit den Ausgangspins des FPGA verbunden ist, und erhöht den Zähler, der verfolgt, welches Eingangsbit des Eingangsbytes es setzt nächsten Eingangstaktzyklus.
Dieses Muster wiederholt sich, bis der Ausgangstakt hoch gesetzt wird, verschiebt das letzte Eingangsbit heraus und inkrementiert den Eingangsbit-Auswahlzähler auf 8. Bei der nächsten ansteigenden Flanke des Eingangstakts setzt das Gerät alle Variablen zurück und schaltet in den "Warten"-Modus um. anstatt zu versuchen, das Eingangsbit Nr. 8 des Eingangsbytes auszugeben, das nicht existiert, da das Eingangsbyte-Array ein bit_vector mit Indizes mit der Bezeichnung 0 bis 7 ist.
Da diese Version sowohl das Eingangsbyte als auch das Eingangstriggerbit konstant hält, wiederholt das Gerät seine Ausgabe endlos.
Dies alles funktioniert wie erwartet in der Simulation und fast wie erwartet in der Realität, mit Ausnahme der endgültigen Verschiebung aus den Daten. Wenn ich das Programm auf das FPGA hochlade, scheint es den Schritt des Hochschreibens des Taktausgangs auf das 8. Bit zu überspringen und springt stattdessen direkt zur Rücksetzphase und setzt den folgenden Eingangstaktzyklus in die "Wartephase" zurück. Es stellt die Datenausgabe gut ein, aber anstatt die Taktausgabe zum letzten Mal vor dem Zurücksetzen hoch zu setzen, springt es einfach zum Zurücksetzen.
Mein Glaube ist, dass dies mit der Tatsache zu tun hat, dass es den Eingangsbitauswahlzähler auf 8 erhöht, während es gleichzeitig den Ausgangstakt hoch schreibt. Anstatt bis zum nächsten Eingangstaktzyklus zu warten, um zu erkennen, dass dieser Wert jetzt 8 ist und zurückgesetzt werden sollte, sieht es aus irgendeinem Grund sofort die Änderung auf 8 und entscheidet sich für das Zurücksetzen, da der Zähler jetzt auf 8 steht.
Eine letzte Anmerkung, in meinem Code gibt es ein "Statusbit" in der SPI-Schnittstellenarchitektur, das von der obigen Architektur in diesem Design nicht verwendet wird, aber in zukünftigen Designs wird es eine notwendige Sache sein, es aufzunehmen. Ich würde diesen Debugging-Code vereinfachen, indem ich ihn entferne, aber wenn er aus allen Implementierungsebenen entfernt wird, tritt der oben aufgeführte Fehler nicht auf, und das FPGA verhält sich genauso perfekt wie die Simulation. Ich habe keine Ahnung, warum das so ist.
Hier ist mein Code:
Die unterste Ebene im Design, die SPI-Schnittstelle:
entity SPI is
port (data_out, clk_out, status_out : out bit; data_in : in bit_vector(0 to 7); CLOCK, begin_in, RESET: in bit);
end entity SPI;
architecture SPIArch of SPI is
type SPIState is (waiting, writing);
signal current_state : SPIState := waiting;
subtype byteCount is integer range 0 to 8;
signal current_bit : integer := 0;
signal data_set : bit := '0';
begin
shiftOut : process (CLOCK, RESET)
begin
if (RESET = '0') then
current_bit <= 0;
data_set <= '0';
clk_out <= '0';
data_out <= '0';
status_out <= '1';
current_state <= waiting;
elsif (CLOCK = '1' and CLOCK'event) then
case current_state is
when waiting =>
if (begin_in = '1') then
status_out <= '0';
current_state <= writing;
end if;
when writing =>
if (current_bit /= 8) then
if (data_set = '1') then
clk_out <= '1';
data_set <= '0';
current_bit <= current_bit + 1;
end if;
if (data_set = '0') then
data_out <= data_in(current_bit);
clk_out <= '0';
data_set <= '1';
end if;
else
data_out <= '0';
clk_out <= '0';
current_bit <= 0;
status_out <= '1';
data_set <= '0';
current_state <= waiting;
end if;
end case;
end if;
end process shiftOut;
end architecture SPIArch;
Als nächstes die Ebene über der SPI-Schnittstelle, dem "Treiber", um die SPI-Schnittstelle intern mit dem Testwert zu versorgen (beachten Sie, dass der große Taktteiler verwendet wurde, damit ich mithilfe von LEDs sehen konnte, was an den Ausgängen meines FPGA vor sich ging):
entity SPIDrive is
port (data_out, clk_out, status_out : out bit; CLOCK, RESET: in bit);
end entity SPIDrive;
architecture SPIDriveArch of SPIDrive is
signal SPI_clk : bit;
signal SPI_counter : integer;
signal SPI_data : bit_vector(0 to 7);
signal SPI_begin : bit;
begin
testDevice : entity work.SPI(SPIArch)
port map (data_out, clk_out, status_out, SPI_data, SPI_clk, SPI_begin, RESET);
outputTest : process (CLOCK, RESET)
begin
if (RESET = '0') then
SPI_clk <= '0';
SPI_counter <= 0;
SPI_data <= B"10110011";
SPI_begin <= '1';
elsif (CLOCK = '1' and CLOCK'event) then
SPI_counter <= SPI_counter + 1;
if (SPI_counter = 5000000) then
SPI_counter <= 0;
SPI_clk <= not SPI_clk;
end if;
end if;
end process outputTest;
end architecture SPIDriveArch;
Die letzte Ebene, die nur als Prüfstand für die Simulation verwendet wird und lediglich die Taktquelle und den Reset erzeugt, die normalerweise von einem On-Board-Oszillator und einem Eingangspin kommen:
entity testbench1 is
end entity testbench1;
architecture testbench1Arch of testbench1 is
signal data_out, clk_out, status_out, CLOCK, RESET: bit;
begin
troll : entity work.SPIDrive(SPIDriveArch)
port map (data_out, clk_out, status_out, CLOCK, RESET);
process
begin
RESET <= '0';
CLOCK <= '0';
wait for 1 ns;
RESET <= '1';
while (true) loop
wait for 1 ns;
CLOCK <= '1';
wait for 1 ns;
CLOCK <= '0';
end loop;
end process;
end architecture testbench1Arch;
Hier ist die Ausgabe meiner Simulation (HINWEIS, dass ich den Taktteiler im „SPI-Treiber“-Code auf 10 statt 5 Millionen geändert habe, um die Anzeige in der Simulation zu erleichtern, und dass ich einen Reset 1 ns in der Simulation auslöse (wie in mein Testbench-Code), obwohl es im Bild schwer zu sehen ist):
Um Verwirrung darüber zu vermeiden, was mein Fehler tatsächlich war, ist hier eine (schlecht) bearbeitete Version des Simulationsbildes, die im Wesentlichen zeigt, was ich im wirklichen Leben von meinem FPGA sehe:
Eine letzte, wahrscheinlich unnötige Ergänzung, ich bekomme eine große Anzahl scheinbar ignorierbarer Warnungen während der Synthese (zum Beispiel eine große Anzahl von Latches bei meiner Dateneingabe, was sinnvoll ist, da ich den Dateneingabewert nie ändere), aber ich bekomme auch drei mit der Angabe " keine Designbeschränkungen" in meinen Dateien. Ich nehme an, dass dies für eine Art Simulation und nicht die Ursache meiner Probleme sind, aber sie schienen möglicherweise bemerkenswert, also erwähne ich sie.
Wie im Titel erwähnt, verwende ich Lattice Diamond für die Programmierung und Simulation (obwohl die Simulation Active-HDL öffnet) und das Lattice MACHXOLF-6900C FPGA
Beantwortung meiner eigenen Frage hier, wie sich herausstellt, sollten Sie KEINE Taktteiler in VHDL verwenden. Ich hatte fälschlicherweise angenommen, dass dies in Ordnung ist, solange Sie jede Uhr als Uhr behandeln, aber wie sich herausstellt, gibt es einen einzigen hardwarespezifischen Weg, den die Uhr nimmt, und eine zweite Uhr, die von einem Teiler stammt, hat einfach nicht die gleiche kleine -Verzögerungseigenschaften der benutzerdefinierten Taktlinie.
Ich habe das alles aus diesem Forumsbeitrag ( edaboard.com/thread283723.html ), in dem sie empfehlen, Taktteiler durch Taktgeber zu ersetzen. Anstatt also Ihre gesamte Logik durch eine Taktflanke auslösen zu lassen, haben Sie eine Taktflanke, die einen Blick auf eine Bedingung wirft, die prüft, ob "Taktfreigabe" hoch ist, und wenn sie hoch ist, führt sie den Code aus. Solange Sie die Taktfreigabe nur für einen Taktzyklus hoch halten, verhält es sich gleich. Sie drehen ihn im Grunde alle 10 Millionen Zyklen hoch und beim 10000001. niedrig, wenn Sie zum Beispiel eine Uhr wollen, die durch 10 Millionen geteilt wird.
Sie wollen vermeiden, hinzugehen
if(clk = '1' and clock'event and clock_enable)
da ich mir ziemlich sicher bin, dass Sie logische Operationen mit Ihrer Uhr vermeiden sollten (ich bin neu in VHDL, also habe ich das selbst noch nie erlebt, lesen Sie es einfach in anderen Forenbeiträgen). Stattdessen gehst du
if (clk = '1' and clock'event) then
if (clock_enable = '1') then
...
rising_edge(clk)
und zu verwenden.falling_edge(clk)
Andreas
Kevin Brant
Kevin Brant