Contexte
Ceci est un projet personnel; en ce qui concerne la connexion d'un FPGA à un N64, les valeurs d'octets que le FPGA reçoit sont ensuite envoyées via UART à mon ordinateur. Il fonctionne plutôt bien! À des moments aléatoires, malheureusement, l'appareil échouera, puis récupérera. Grâce au débogage, j'ai réussi à trouver le problème, mais je suis perplexe sur la façon de le résoudre car je suis assez incompétent avec VHDL.
Je joue avec le VHDL depuis quelques jours et je suis peut-être incapable de résoudre ce problème.
Le problème
J'ai un oscilloscope mesurant le signal N64 dans le FPGA, et l'autre canal se connecte à la sortie du FPGA. J'ai également des broches numériques enregistrant la valeur du compteur.
Essentiellement, le N64 envoie 9 bits de données, dont un bit STOP. Le compteur compte les bits de données reçus et lorsque j'atteins 9 bits, le FPGA commence à transmettre via UART.
Voici le comportement correct:
Le FPGA est la forme d'onde bleue et la forme d'onde orange est l'entrée du N64. Pendant la durée de réception, mon FPGA "fait écho" le signal de l'entrée à des fins de débogage. Une fois que le FPGA compte jusqu'à 9, il commence à transmettre les données via UART. Notez que les broches numériques comptent jusqu'à 9 et que la sortie FPGA devient FAIBLE immédiatement après la fin du N64.
Voici un exemple d'échec:
Notez que le compteur ignore les bits 2 et 7! Le FPGA atteint la fin, attendant le prochain bit de démarrage du N64 mais rien. Le FPGA expire donc et récupère.
Il s'agit du VHDL pour le module de réception N64. Il contient le compteur: s_bitCount.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity N64RX is
port(
N64RXD : in STD_LOGIC; --Data input
clk25 : in STD_LOGIC;
clr : in STD_LOGIC;
tdre : in STD_LOGIC; --detects when UART is ready
transmit : out STD_LOGIC; --Signal to UART to transmit
sel : out STD_LOGIC;
echoSig : out STD_LOGIC;
bitcount : out STD_LOGIC_VECTOR(3 downto 0);
data : out STD_LOGIC_VECTOR(3 downto 0) --The significant nibble
);
end N64RX;
--}} End of automatically maintained section
architecture N64RX of N64RX is
type state_type is (start, delay2us, sigSample, waitForStop, waitForStart, timeout, count9bits, sendToUART);
signal state: state_type;
signal s_sel, s_echoSig, s_timeoutDetect : STD_LOGIC;
signal s_baudCount : STD_LOGIC_VECTOR(6 downto 0); --Counting variable for baud rate in delay
signal s_bitCount : STD_LOGIC_VECTOR(3 downto 0); --Counting variable for number of bits recieved
signal s_data : STD_LOGIC_VECTOR(8 downto 0); --Signal for data
constant delay : STD_LOGIC_VECTOR(6 downto 0) := "0110010"; --Provided 25MHz, 50 cycles is 2us
constant delayLong : STD_LOGIC_VECTOR(6 downto 0) := "1100100";
begin
n64RX: process(clk25, N64RXD, clr, tdre)
begin
if clr = '1' then
s_timeoutDetect <= '0';
s_echoSig <= '1';
s_sel <= '0';
state <= start;
s_data <= "000000000";
transmit <= '0';
s_bitCount <= "0000";
s_baudCount <= "0000000";
elsif (clk25'event and clk25 = '1') then --on rising edge of clock input
case state is
when start =>
--s_timeoutDetect <= '0';
s_sel <= '0';
transmit <= '0'; --Don't request UART to transfer
s_data <= "000000000";
s_bitCount <= X"0";
if N64RXD = '1' then
state <= start;
elsif N64RXD = '0' then --if Start bit detected
state <= delay2us;
end if;
when delay2us => --wait two microseconds to sample
--s_timeoutDetect <= '0';
s_sel <= '1';
s_echoSig <= '0';
if s_baudCount >= delay then
state <= sigSample;
else
s_baudCount <= s_baudCount + 1;
state <= delay2us;
end if;
when sigSample =>
--s_timeoutDetect <= '1';
s_echoSig <= N64RXD;
s_bitCount <= s_bitCount + 1;
s_baudcount <= "0000000";
s_data <= s_data(7 downto 0) & N64RXD;
state <= waitForStop;
when waitForStop =>
s_echoSig <= N64RXD;
if N64RXD = '0' then
state <= waitForStop;
elsif N64RXD = '1' then
state <= waitForStart;
end if;
when waitForStart =>
s_echoSig <= '1';
s_baudCount <= s_baudCount + 1;
if N64RXD = '0' then
s_baudCount <= "0000000";
state <= delay2us;
elsif N64RXD = '1' then
if s_baudCount >= delayLong then
state <= timeout;
elsif s_bitCount >= X"9" then
state <= count9bits;
else
state <= waitForStart;
end if;
end if;
when count9bits =>
s_sel <= '0';
if tdre = '0' then
state <= count9bits;
elsif tdre = '1' then
state <= sendToUART;
end if;
when sendToUART =>
transmit <= '1';
if tdre = '0' then
state <= start;
else
state <= sendToUART;
end if;
when timeout =>
--s_timeoutDetect <= '1';
state <= start;
end case;
end if;
end process n64RX;
--timeoutDetect <= s_timeoutDetect;
bitcount <= s_bitCount;
echoSig <= s_echoSig;
sel <= s_sel;
data <= s_data(4 downto 1);
end N64RX;
Alors, des idées? Conseils de débogage? Conseils sur le codage des machines à états finis?
En attendant, je continuerai à jouer avec (je l'aurai éventuellement)! Aidez-moi Stack Exchange, vous êtes mon seul espoir!
Éditer
Une autre découverte dans mon débogage, les états passeront de waitForStart à waitForStop. J'ai donné à chaque état une valeur avec waitForStart égal à '5' et waitForStop égal à '4'. Voir l'image ci-dessous:
Réponses:
Je ne vois pas de synchroniseur sur la ligne de données rx.
Toutes les entrées asynchrones doivent être synchronisées avec l'horloge d'échantillonnage. Il y a plusieurs raisons à cela: métastabilité et routage. Ce sont des problèmes différents mais interdépendants.
Il faut du temps pour que les signaux se propagent à travers la structure FPGA. Le réseau d'horloge à l'intérieur du FPGA est conçu pour compenser ces retards de «déplacement» afin que toutes les bascules du FPGA voient l'horloge au même moment exact. Le réseau de routage normal ne dispose pas de cela, et s'appuie à la place sur la règle selon laquelle tous les signaux doivent être stables pendant un peu de temps avant que l'horloge ne change et rester stables pendant un peu de temps après le changement d'horloge. Ces petits morceaux de temps sont connus comme les temps de configuration et de maintien pour une bascule donnée. Le composant lieu et itinéraire de la chaîne d'outils a une très bonne compréhension des retards de routage pour le périphérique spécifique et fait l'hypothèse de base qu'un signal ne viole pas les temps de configuration et de maintien des bascules dans le FPGA.
Lorsque vous avez des signaux qui ne sont pas synchronisés avec l'horloge d'échantillonnage, vous pouvez vous retrouver dans la situation où une bascule voit la "vieille" valeur d'un signal puisque la nouvelle valeur n'a pas eu le temps de se propager. Vous êtes maintenant dans la situation indésirable où la logique regardant le même signal voit deux valeurs différentes. Cela peut provoquer un mauvais fonctionnement, des machines d'état en panne et toutes sortes de ravages difficiles à diagnostiquer.
L'autre raison pour laquelle vous devez synchroniser tous vos signaux d'entrée est ce que l'on appelle la métastabilité. Il existe des volumes écrits sur ce sujet, mais en résumé, les circuits logiques numériques sont à leur niveau le plus élémentaire un circuit analogique. Lorsque votre ligne d'horloge augmente, l'état de la ligne d'entrée est capturé et si cette entrée n'est pas un niveau haut ou bas stable à ce moment, une valeur "intermédiaire" inconnue peut être capturée par la bascule d'échantillonnage.
Comme vous le savez, les FPGA sont des bêtes numériques et ne réagissent pas bien à un signal qui n'est ni haut ni bas. Pire, si cette valeur indéterminée fait son chemin au-delà de la bascule d'échantillonnage et dans le FPGA, elle peut provoquer toutes sortes de bizarreries car de plus grandes parties de la logique voient maintenant une valeur indéterminée et tentent de la comprendre.
La solution est de synchroniser le signal. À son niveau le plus élémentaire, cela signifie que vous utilisez une chaîne de tongs pour capturer l'entrée. Tout niveau métastable qui aurait pu être capturé par la première bascule et réussi à le distinguer obtient une autre chance d'être résolu avant qu'il n'atteigne votre logique complexe. Deux bascules sont généralement plus que suffisantes pour synchroniser les entrées.
Un synchroniseur de base ressemble à ceci:
Connectez la broche physique de la ligne de données rx du contrôleur N64 à l'entrée async_in du synchroniseur et connectez le signal sync_out à l'entrée rxd de votre UART.
Les signaux non synchronisés peuvent provoquer des problèmes étranges . Assurez-vous que toute entrée connectée à un élément FPGA qui n'est pas synchronisé avec l'horloge du processus de lecture du signal est synchronisée. Cela inclut les boutons poussoirs, les signaux UART 'rx' et 'cts' ... tout ce qui n'est pas synchronisé avec l'horloge que le FPGA utilise pour échantillonner le signal.
(Un aparté: j'ai écrit la page à www.mixdown.ca/n64dev il y a de nombreuses années. Je viens de réaliser que j'ai rompu le lien lors de la dernière mise à jour du site et que je le corrigerai le matin lorsque je serai de retour à un ordinateur. Je ne savais pas que tant de gens utilisaient cette page!)
la source
Votre problème est que vous utilisez des signaux non synchronisés pour prendre des décisions dans votre machine d'état. Vous devez alimenter tous ces signaux externes via des synchroniseurs à double FF avant de les utiliser dans la machine d'état.
C'est un problème subtil avec les machines à états qui peut survenir dans toute transition d'état qui implique un changement de deux bits ou plus dans la variable d'état. Si vous utilisez une entrée non synchronisée, l'un des bits peut changer tandis que l'autre ne change pas. Cela vous amène à un état différent de celui prévu, et il peut ou non être un état légal.
Cette dernière instruction est la raison pour laquelle vous devez également toujours avoir un cas par défaut (en VHDL,
when others => ...
) dans votre déclaration de cas de machine d'état qui vous fait passer de n'importe quel état illégal à un état légal.la source
when others =>
aidait, mais il s'avère que cela ne vous donne pas ce que vous prétendez (sous n'importe quel synthétiseur que j'ai utilisé) à moins que vous ajoutiez des attributs pour vous assurer que le synthétiseur comprend que vous voulez une machine d'état "sûre". Le comportement normal consiste à optimiser pour une représentation à chaud et à ne pas fournir de logique de récupération. Voir par exemple xilinx.com/support/answers/40093.html et synopsys.com/Company/Publications/SynopsysInsight/Pages/… .