Una vez más sobre retrasos en el código fuente del proyecto FPGA o una simple pregunta para una entrevista para un trabajo de desarrollador FPGA



Hace algún tiempo, durante una discusión en compañía de desarrolladores profesionales de FPGA, surgió una discusión sobre la aprobación de una entrevista. Qué preguntas se hacen allí y qué se podría hacer. Sugerí dos preguntas:

  1. Dé un ejemplo de un código síncrono sin usar retrasos, lo que dará diferentes resultados al modelar y al trabajar en equipos reales.
  2. Corrija este código con demoras.

Después de esta pregunta, se produjo una animada discusión, como resultado de lo cual decidí considerar este tema con más detalle.

Ya toqué este tema un poco en el artículo anterior . Ahora con más detalle. Aquí hay un texto de ejemplo:

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; 

Por simplicidad, todo el código se coloca en un componente.

Las señales clk1 y a son señales de exposición de prueba. clk1 es una frecuencia de reloj de 100 MHz, la señal a tiene dos ciclos de reloj a 0 y cuatro ciclos de reloj a 1. La señal a se genera con un retraso de 1 nc con respecto al borde ascendente de clk1 . Estas dos señales son suficientes para describir el problema.

Las diferentes opciones de código sintetizado pueden ser descomentadas y modeladas.
Considere la primera opción, este es un código sintetizado sin demora y que utiliza la reasignación de la frecuencia del reloj.

Aquí están los resultados de la simulación para la opción 1:



El diagrama muestra visualmente que las señales de reloj clk1 y clk2 coinciden, pero en realidad clk2 se retrasa en relación con clk1 por el valor del retraso delta. La señal c retrasa la señal b en un ciclo de reloj. Esto es correcto Pero la señal d debe coincidir con la señal c , pero esto no sucede. Funciona antes

Recordemos qué retraso delta es. Este es un concepto fundamental, se basa en el trabajo de simuladores de eventos, que utilizamos al modelar circuitos lógicos.

El simulador tiene el concepto de tiempo modelo. Todos los eventos en el sistema se adjuntan a este tiempo modelo. Veamos la formación de la frecuencia del reloj:

 clk1 <= not clk1 after 5 ns; 

Supongamos que ahora estamos modelando solo clk1 , no hay otras señales.
En el momento inicial de tiempo, clk1 es 0, esto se establece cuando se declara la señal. El simulador ve un requisito para invertir la señal. La palabra clave after da instrucciones para asignar un nuevo valor en 5 ns en relación con el tiempo actual del modelo. El simulador ve esto y toma nota de que en el momento 5 ns el valor de clk1 será 1. Si bien este es un modelo futuro, por cierto, aún puede cambiar. A continuación, el simulador escanea las señales restantes. El simulador verá que para un momento dado en el tiempo del modelo, todo está hecho y él puede calcular el siguiente momento. Surge la pregunta: ¿cuál es el próximo momento? En principio, son posibles diferentes opciones. Por ejemplo, Simulink tiene un modo de tono fijo. En este caso, el tiempo del modelo aumentará en cierta cantidad y los cálculos continuarán.

Los sistemas de simulación de circuitos digitales funcionan de manera diferente. Pasan al siguiente evento, que ya han colocado en el futuro en su eje de tiempo modelo. En este caso, serán 5 ns. El simulador verá que clk1 ha cambiado y calculará un nuevo valor para él, será 0, que también se colocará con un retraso de 5 ns en el eje de tiempo. Es decir serán 10 ns. Y así, el proceso continuará hasta que finalice el tiempo de simulación especificado.

Ahora agreguemos las señales a y b .

La señal a se asigna en el proceso. Para la señal b , la construcción condicional cuando se usa; La función rising_edge ( clk1 ) analiza clk1 y devuelve verdadero cuando el frente está fijo, es decir el valor anterior es 0 y el valor actual es 1.

En el tiempo de modelo 5 ns, clk1 cambiará. Será igual a 1 y por el momento de 10 ns se creará un evento para establecerlo en 0. Pero esto es más tarde. Mientras todavía estamos en el momento de 5 ns y continuamos los cálculos. El simulador va a la línea
 b<=a when rising_edge(clk1); 
Dado que hay una función que depende de clk1, el simulador calculará el valor de la función, verá que devuelve verdadero y asignará
 b<=a; 


Aquí comienza la parte más interesante: cuando es necesario cambiar el valor de b . Parece necesario cambiarlo ahora, en este momento. Pero tenemos procesos paralelos. Quizás todavía necesitemos el valor de b para calcular otras señales. Y aquí viene el concepto de retraso delta. Este es el valor mínimo por el cual cambia el tiempo del modelo. Este valor ni siquiera tiene la dimensión del tiempo. Esto es solo un delta. Pero puede haber muchos de ellos. Y tanto que el simulador simplemente se detiene por error o se congela.
Entonces, se establecerá un nuevo valor de b por el momento 5 ns + 1 (1 es el primer retraso delta). El simulador verá que ya no hay nada que calcular por el momento 5 ns e irá al siguiente momento, y esto será 5 ns + 1; En este momento, rising_edge (ckl1) no funciona. Y el valor de b se establecerá en 1. Después de eso, el simulador irá al momento 10 nc.

Ahora agreguemos las señales c , d y veamos por qué son diferentes.
Es mejor considerar el momento del tiempo del modelo 25 ns teniendo en cuenta los retrasos delta

deltaclk1clk2re (clk1)re (clk2)bcd
0 010 0ciertofalso0 00 00 0
111falsocierto10 00 0
210 0falsofalso10 01

Nota: re - rising_edge

La tabla muestra que en el momento en que se activa la función rising_edge ( clk2 ), el valor de b ya es 1. Y, por lo tanto, se asignará a la señal d .

Basado en el sentido común, este no es el comportamiento que esperábamos del código. Después de todo, simplemente reasignamos la señal clk1 a clk2 y esperábamos que las señales cyd fueran las mismas. Pero siguiendo la lógica del simulador, esto no es así. Esta es una característica PRINCIPAL . Esta característica, por supuesto, debe ser conocida por los desarrolladores de proyectos FPGA y, por lo tanto, esta es una buena y necesaria pregunta para una entrevista.

¿Qué pasará durante la síntesis? Pero el sintetizador seguirá el sentido común, hará que las señales clk2 y clk1 sean una señal y, por lo tanto, c y d también serán las mismas. Y con ciertas configuraciones de sintetizador, también se combinarán en una señal.

Este es solo el caso cuando modelar y trabajar en equipos reales dará resultados diferentes. Quiero señalar que la razón de los diferentes resultados es la lógica diferente del simulador y el sintetizador. Esta es una diferencia BÁSICA. Esto no tiene nada que ver con las limitaciones de tiempo. Y si su proyecto en el modelo y en la plancha muestra resultados diferentes, entonces verifique, tal vez un diseño como ese se deslizó allí

 clk2 <= clk1 

Ahora la segunda pregunta es arreglar este código con demoras.
Esta es la opción 2. Puede ser descomentado y modelado.
Aquí está el resultado.



El resultado es correcto. Que paso Hagamos una tabla nuevamente para un intervalo de 25 - 36 ns
tiempodeltaclk1clk2re (clk1)re (clk2)bcd
250 010 0ciertofalso0 00 00 0
25111falsocierto0 00 00 0
260 011falsofalso10 00 0
350 010 0ciertofalso10 00 0
35111falsocierto10 00 0
360 011falsofalso111

Se puede ver que el valor de b no cambia en los momentos de los frentes clk1 , clk2 . Un retraso de 1 ns toma el momento en que la señal cambia más allá de la zona de respuesta de borde. Este código se está acercando a la realidad. En un circuito real, hay tiempo para que se active el activador y para que se propague la señal. Este tiempo debe ser menor que el período de la frecuencia del reloj, de hecho, esto es lo que hace el trazador, y esto es lo que verifica el análisis de tiempo.

La causa del error es la reasignación de la señal del reloj mediante la asignación habitual en la que aparece un retraso delta. Sin embargo, el lenguaje VHDL tiene una construcción de alias. Esto le permite obtener un nombre diferente para la señal. Aquí está el anuncio:

 alias clk3 : std_logic is clk1; 

En el texto de ejemplo, puede descomentar la opción 3: funcionará correctamente.

Este ejemplo está escrito en VHDL. Tal vez este es el problema solo de este idioma? Pero aquí están las mismas opciones en Verilog.

Texto oculto
 `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 



  • Opción 1: sin demora. No funciona correctamente
  • Opción 2 - con demoras. Funciona correctamente
  • Opción 3: reasignación por cable. Funciona correctamente

Verilog tiene el concepto de reg y wire. En este caso, la reasignación de la señal del reloj a través del cable parece más natural. Esto es análogo a una asignación de alias en VHDL. Esto alivia un poco la tensión del problema, pero aún necesita saberlo.
Verilog también tiene el concepto de asignación bloqueante y no bloqueante. La asignación de señal byc se puede escribir de otra manera:

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

Y puedes hacer esto:

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

Dependiendo del orden de las líneas, el resultado será diferente.

Volviendo al tema de la entrevista, quiero enfatizar una vez más que estas preguntas son para comprender la esencia del problema. Y a partir de la comprensión del problema, uno puede sacar varias conclusiones, por ejemplo, qué estilo de código usar. Personalmente, siempre uso la asignación de retraso.

Los archivos de muestra están disponibles aquí.

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


All Articles