Encore une fois sur les retards dans le code source du projet FPGA ou une simple question pour un entretien pour un job de développeur FPGA



Il y a quelque temps, lors d'une discussion en compagnie de développeurs professionnels de FPGA, une discussion a surgi sur la réussite d'une interview. Quelles questions y sont posées et ce qui pourrait être posé. J'ai proposé deux questions:

  1. Donnez un exemple de code synchrone sans utiliser de délais, ce qui donnera des résultats différents lors de la modélisation et lorsque vous travaillez dans un équipement réel
  2. Corrigez ce code avec des retards.

Après cette question, une discussion animée a suivi, à la suite de laquelle j'ai décidé d'examiner cette question plus en détail.

J'ai déjà abordé ce sujet un peu dans l' article précédent . Maintenant plus en détail. Voici un exemple de texte:

library IEEE; use IEEE.STD_LOGIC_1164.all; entity delta_delay is end delta_delay; architecture delta_delay of delta_delay is signal clk1 : std_logic:='0'; signal clk2 : std_logic; alias clk3 : std_logic is clk1; --    clk1 signal a : std_logic; signal b : std_logic; signal c : std_logic; signal d : std_logic; begin ---    --- clk1 <= not clk1 after 5 ns; pr_a: process begin a <= '0' after 1 ns; wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); a <= '1' after 1 ns; wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); end process; ---   -    --- clk2 <= clk1; --    ,        ---  1 -     --- b <= a when rising_edge( clk1 ); c <= b when rising_edge( clk1 ); d <= b when rising_edge( clk2 ); ---  2 -     --- -- --clk2 <= clk1; --b <= a after 1 ns when rising_edge( clk1 ); --c <= b after 1 ns when rising_edge( clk1 ); --d <= b after 1 ns when rising_edge( clk2 ); ---  3 -          alias --- --b <= a when rising_edge( clk1 ); --c <= b when rising_edge( clk1 ); --d <= b when rising_edge( clk3 ); end delta_delay; 

Pour plus de simplicité, tout le code est placé dans un seul composant.

Les signaux clk1 et a sont des signaux d'exposition de test. clk1 est une fréquence d'horloge de 100 MHz, le signal a contient deux cycles d'horloge à 0 et quatre cycles d'horloge à 1. Le signal a est généré avec un retard de 1 nc par rapport au front montant de clk1 . Ces deux signaux suffisent à décrire le problème.

Différentes options de code synthétisées peuvent être non commentées et modélisées.
Considérez la première option, il s'agit d'un code synthétisé sans délai et utilisant la réaffectation de la fréquence d'horloge.

Voici les résultats de la simulation pour l'option 1:



Le diagramme montre visuellement que les signaux d'horloge clk1 et clk2 coïncident, mais en fait clk2 est retardé par rapport à clk1 de la valeur du retard delta. Le signal c retarde le signal b d'un cycle d'horloge. C'est correct. Mais le signal d doit coïncider avec le signal c , mais cela ne se produit pas. Cela fonctionne plus tôt.

Souvenons-nous du retard delta. Il s'agit d'un concept fondamental, il est basé sur le travail de simulateurs d'événements, que nous utilisons lors de la modélisation de circuits logiques.

Le simulateur a le concept de temps de modèle. Tous les événements du système sont associés à cette heure de modèle. Regardons la formation de la fréquence d'horloge:

 clk1 <= not clk1 after 5 ns; 

Supposons maintenant que nous modélisons uniquement clk1 , il n'y a pas d'autres signaux.
Au moment initial du temps, clk1 est 0, il est défini lorsque le signal est déclaré. Le simulateur voit la nécessité d'inverser le signal. Le mot clé after donne des instructions pour affecter une nouvelle valeur en 5 ns par rapport à l'heure actuelle du modèle. Le simulateur le voit et note qu'à l'instant 5 ns, la valeur de clk1 sera 1. Bien qu'il s'agisse d'un modèle futur, il pourrait d'ailleurs encore changer. Ensuite, le simulateur balaye les signaux restants. Le simulateur verra que pour un instant donné dans le temps du modèle, tout est fait et il peut calculer l'instant suivant. La question se pose - quel est le moment suivant? En principe, différentes options sont possibles. Par exemple, Simulink a un mode à pas fixe. Dans ce cas, le temps du modèle augmentera d'une certaine quantité et les calculs se poursuivront.

Les systèmes de simulation de circuits numériques font différemment. Ils passent à l'événement suivant, qu'ils ont déjà placé dans le futur sur leur axe de temps modèle. Dans ce cas, ce sera 5 ns. Le simulateur verra que clk1 a changé et calculera une nouvelle valeur pour lui, ce sera 0 qui sera également placé avec un retard de 5 ns sur l'axe du temps. C'est-à-dire ce sera 10 ns. Ainsi, le processus se poursuivra jusqu'à la fin du temps de simulation spécifié.

Ajoutons maintenant les signaux a et b .

Le signal a est affecté dans le processus. Pour le signal b , la construction conditionnelle quand est utilisée; La fonction rise_edge ( clk1 ) analyse clk1 et retourne vrai lorsque le front est fixe, c'est-à-dire la valeur précédente est 0 et la valeur actuelle est 1.

Au moment du modèle 5 ns, clk1 changera. Il deviendra égal à 1 et pour le moment de 10 ns un événement sera créé pour le mettre à 0. Mais c'est plus tard. Alors que nous sommes encore dans le moment de 5 ns et nous continuons les calculs. Le simulateur passe à la ligne
 b<=a when rising_edge(clk1); 
Puisqu'il existe une fonction qui dépend de clk1, le simulateur calculera la valeur de la fonction, veillera à ce qu'elle renvoie vrai et affectera
 b<=a; 


Ici commence la partie la plus intéressante - quand il est nécessaire de changer la valeur de b . Il semblerait nécessaire de le changer maintenant, à ce stade. Mais nous avons des processus parallèles. Peut-être avons-nous encore besoin de la valeur de b pour calculer d'autres signaux. Et voici le concept de retard delta. Il s'agit de la valeur minimale de décalage de l'heure du modèle. Cette valeur n'a même pas la dimension du temps. Ce n'est qu'un delta. Mais il peut y en avoir beaucoup. Et tellement que le simulateur s'arrête simplement par erreur ou se fige.
Ainsi, une nouvelle valeur de b sera fixée pour le moment 5 ns + 1 (1 est le premier retard delta). Le simulateur verra qu'il n'y a déjà rien à calculer pour l'instant 5 ns et ira à l'instant suivant, et ce sera 5 ns + 1; À ce moment, la hausse_edge (ckl1) ne fonctionne pas. Et la valeur de b sera fixée à 1. Après cela, le simulateur passera à l'instant 10 nc.

Ajoutons maintenant les signaux c , d et voyons pourquoi ils sont différents.
Il est préférable de considérer le moment du temps du modèle 25 ns en tenant compte des retards delta

deltaclk1clk2re (clk1)re (clk2)bcd
010vraifaux000
111fauxvrai100
210fauxfaux101

Remarque: re - rise_edge

Le tableau montre qu'au moment où la fonction montée_crête ( clk2 ) est déclenchée, la valeur de b est déjà 1. Et donc, elle sera affectée au signal d .

Basé sur le bon sens, ce n'est pas le comportement que nous attendions du code. Après tout, nous avons simplement réaffecté le signal clk1 à clk2 et nous nous attendions à ce que les signaux c et d soient les mêmes. Mais suivant la logique du simulateur, ce n'est pas le cas. Il s'agit d'une caractéristique PRINCIPALE . Cette fonctionnalité, bien sûr, doit être connue des développeurs de projets FPGA et c'est donc une bonne et nécessaire question pour un entretien.

Que se passera-t-il pendant la synthèse? Mais le synthétiseur suivra le bon sens, il fera les signaux clk2 et clk1 un signal et donc c et d seront également les mêmes. Et avec certains réglages de synthétiseur, ils seront également combinés en un seul signal.

C'est juste le cas lorsque la modélisation et le travail dans des équipements réels conduiront à des résultats différents. Je tiens à noter que la raison des différents résultats est la logique différente du simulateur et du synthétiseur. Il s'agit d'une différence de BASE. Cela n'a rien à voir avec les contraintes de temps. Et si votre projet dans le modèle et dans le fer montre des résultats différents, alors vérifiez, peut-être qu'un design comme celui-ci s'est glissé là-dedans

 clk2 <= clk1 

Maintenant, la deuxième question est de corriger ce code avec des retards.
Il s'agit de l'option 2. Elle peut être non commentée et modélisée.
Voici le résultat.



Le résultat est correct. Que s'est-il passé? Faisons un tableau à nouveau pour un intervalle de 25 à 36 ns
le tempsdeltaclk1clk2re (clk1)re (clk2)bcd
25010vraifaux000
25111fauxvrai000
26011fauxfaux100
35010vraifaux100
35111fauxvrai100
36011fauxfaux111

On voit que la valeur de b ne change pas aux moments des fronts clk1 , clk2 . Un retard de 1 ns prend le moment où le signal change au-delà de la zone de réponse de front. Ce code se rapproche de la réalité. Dans un circuit réel, il y a un certain temps pour que le déclencheur se déclenche et que le signal se propage. Ce temps doit être inférieur à la période de la fréquence d'horloge, en fait, c'est ce que fait le traceur, et c'est ce que l'analyse de temps vérifie.

La cause de l'erreur est la réaffectation du signal d'horloge par l'affectation habituelle à laquelle apparaît un retard delta. Cependant, le langage VHDL a une construction d'alias. Cela vous permet d'obtenir un nom différent pour le signal. Voici l'annonce:

 alias clk3 : std_logic is clk1; 

Dans l'exemple de texte, vous pouvez décommenter l'option 3 - cela fonctionnera correctement.

Cet exemple est écrit en VHDL. Peut-être que c'est le problème uniquement de cette langue? Mais voici les mêmes options dans Verilog.

Texte masqué
 `timescale 1 ns / 1 ps module delta_delay_2 (); reg clk1 = 1'b0; reg clk2; wire clk3; reg a = 1'b0; reg b; reg c; reg d; initial begin forever clk1 = #5 ~clk1; end initial begin repeat(10) begin #20 a = 1'b1; #60 a = 1'b0; end end //   -    --- always @(clk1) clk2 <= clk1; //  1 -     always @(posedge clk2) d <= b; always @(posedge clk1) begin c <= b; b <= a; end //  2 -     //always @(posedge clk1) b = #1 a; // //always @(posedge clk1) c = #1 b; // //always @(posedge clk2) d = #1 b; //  3 -     //      assign //assign clk3 = clk1; // //always @(posedge clk3) d <= b; // //always @(posedge clk1) //begin // c <= b; // b <= a; //end endmodule 



  • Option 1 - pas de retard. Cela ne fonctionne pas correctement.
  • Option 2 - avec retards. Cela fonctionne correctement.
  • Option 3 - réaffectation par fil. Cela fonctionne correctement.

Verilog a le concept de reg et wire. Dans ce cas, la réaffectation du signal d'horloge par le fil semble plus naturelle. Ceci est analogue à une affectation d'alias dans VHDL. Cela soulage quelque peu la tension du problème, mais vous devez toujours le savoir.
Verilog a également le concept d'affectation bloquante et non bloquante. L'affectation des signaux b et c peut être écrite d'une autre manière:

 always @(posedge clk1) begin c = b; b = a; end 

Et vous pouvez le faire:

 always @(posedge clk1) begin b = a; c = b; end 

Selon l'ordre des lignes, le résultat sera différent.

Revenant au sujet de l'entretien, je tiens à souligner une fois de plus que ces questions visent à comprendre l'essence du problème. Et à partir de la compréhension du problème, on peut tirer diverses conclusions, par exemple, quel style de code utiliser. Personnellement, j'utilise toujours l'attribution de retard.

Des exemples de fichiers sont disponibles ici.

Source: https://habr.com/ru/post/fr419875/


All Articles