Salidas incorrectas en la entidad VHDL

Tengo lecciones sobre VHDL en una de mis clases de la universidad y tengo que escribir de forma simple entityque generará un reloj a partir de una fuente de 1MHz. Estoy usando CoolRunner-II CPLD Starter Kit con ISE Webpack 13.1.

Cuando ejecuto la simulación de mi código, obtengo resultados extraños. No tengo idea, dónde está el problema. Mi entidad VHDL se ve así:

entity clock is
  Port ( clk_in : in  STD_LOGIC;
    clk_1M : out  STD_LOGIC;
    clk_500k : out  STD_LOGIC;
    clk_100k : out  STD_LOGIC;
    clk_1k : out  STD_LOGIC;
    clk_1hz: out STD_LOGIC);
end clock;

La entrada es una señal de 1MHz del oscilador y quiero crear una señal de salida de 1MHz, 500kHz, 100kHz, 1kHz y 1Hz. Definí varias señales:

signal c100k: std_logic_vector(3 downto 0) := (others => '0' );
signal c1k:  std_logic_vector(9 downto 0) := (others => '0' );
signal c1hz: std_logic_vector(9 downto 0) := (others => '0' );
--
signal c500k_out: std_logic := '0';
signal c100k_out: std_logic := '0';
signal c1k_out: std_logic := '0';
signal c1hz_out: std_logic := '0';

Y finalmente, mi código es:

process (clk_in) begin
  if clk_in'event and clk_in = '1' then
    -- 500kHz
    c500k_out <= not c500k_out;
  end if;
end process;

process (clk_in) begin
  if clk_in'event and clk_in = '1' then
    -- 100kHz
    c100k <= c100k + '1';
    if c100k = X"A" then
      c100k <= (others => '0' );
      c100k_out <= '1';
    else
      c100k_out <= '0';
    end if;
  end if;
end process;

--
-- Code for 1kHz and 1Hz is same as 100kHz
--

clk_1M <= clk_in;           -- Clock source 1Mhz
clk_500k <= c500k_out;  -- Clock source 500kHz
clk_100k <= c100k_out;  -- Clock source 100kHz
clk_1k <= c1k_out;      -- Clock source 1kHz
clk_1hz <= c1hz_out;        -- Clock source 1Hz

Cuando ejecuto la simulación, obtuve estos resultados extraños:

Simulación de reloj

¿Qué está mal con mi código?

Agregué código de trabajo aquí -- pub.uart.cz/vhdl

Respuestas (4)

Lo primero que veo, que probablemente no sea la causa de su problema, es la falta de cualquier tipo de reinicio. ModelSim no declara automáticamente que todas las señales sean '0', y tratar de decir s <= not s cuando s no es 1 o 0 no le dará lo que desea. Veo que los declara todos '0' en la parte superior, pero un circuito adecuado tendría una entrada de reinicio que maneja brevemente al comienzo de su simulación.

Su proceso de contador básico se ve mal; básicamente, cuando el conteo llega a diez, está emitiendo un '1' y durante los otros 9/10 del tiempo está emitiendo un '0' (para el ejemplo de 100k, el ejemplo de 1Hz sería un pulso alto de 1us y un 999us poco tiempo Creo que lo que quieres es algo como esto:

gen_clk100: process(clk, rst)
begin
    if rising_edge(clk) then
        if count = 10 then
            clk100 <= not clk100;
            count <= 0;
        else
            count <= count + 1;
        end if;
    end if;

    if rst = '1' then
        clk100 <= '0';
        count <= 0;
    end if;
end process;

Tenga en cuenta varias cosas:

  • No estoy incrementando todo el tiempo. Incremento si el conteo no está al máximo, y lo incremento de lo contrario.

  • Estoy alternando el reloj de salida cuando se alcanza el conteo máximo

  • Incluí un reinicio asíncrono (con borrado síncrono); esto garantiza que sus señales se inicialicen correctamente y no tengan problemas de metaestabilidad cuando se libera el reinicio.

También mencionas que las otras secciones son iguales. Creo que tienes un problema con varios controladores como el que mencionó Yann. Verifique una y otra vez su código para asegurarse de que no está asignando la salida en dos procesos diferentes, porque eso es exactamente lo que la simulación dice que está haciendo. Es mejor hacer un módulo contador genérico o...

¿Por qué tantos contadores? Todos sus divisores de frecuencia se pueden manejar con un solo conteo y luego "quitar" las condiciones para hacer las divisiones que le interesan. También puede tener un solo contador y usar el operador (módulo) para manejar cada caso de divisor mod.

Finalmente, también usaría tipos reales o enteros para el conteo en lugar de std_logic_vectors, pero solo soy yo.

La falta de reinicio no va a causar problemas en ModelSim ni en ningún simulador (creo que está usando ISIM). Todas las señales reciben un valor inicial cuando se declara la señal, por lo que es suficiente para el simulador. Su código de contador de ejemplo daría como resultado una mala lógica debido a múltiples controladores (los bloques "si aumenta" y "si se reinicia" se consideran procesos separados). La forma correcta sería hacer "si se reinicia... de lo contrario si se eleva_el borde...". Aconsejo no usar tipos reales o enteros para señales que se sintetizarán en lógica real (no solo simulada). Pero así soy yo.
Estoy de acuerdo, y es por eso que dije específicamente que los reinicios no estaban causando el problema. El ejemplo de proceso que di es un ejemplo de libro de texto que se usa en docenas de diseños; no hay ningún problema con él, al menos no con Quartus o ISE. Los tipos Real/Integer también se sintetizan correctamente en mi experiencia. ¿Te has encontrado con problemas?
Real no sintetiza. Puede usar un real, pero debe resolverse en un tipo de vector de bits o entero en el momento de la síntesis. No puede, por ejemplo, tener un contador de tipo "real" que cuente por 0,184739 cada ciclo de reloj. Si bien el número entero sintetiza, el uso de SLV en su lugar le brinda mucho más control. Muchas veces puedo optimizar la lógica para que sea mucho más pequeña con un rendimiento mucho mayor usando SLV que entero (en Xilinx FPGA). Con números enteros no tiene control ni acceso a la representación lógica interna en la FPGA, mientras que SLV le permite hacer trucos como utilizar la cadena de transporte de manera eficiente.
Tienes razón; la única vez que he usado real es como un número entero, por lo que el huevo está en mi cara por sugerirlo sin un descargo de responsabilidad adecuado. :-/ Sin embargo, he usado tipos de enteros a rango para mis contadores casi exclusivamente (señal/variable foo: rango de enteros de 0 a 255;) -- No creo que esté menos controlado que slv en ese sentido, y hace que su código mucho más limpio para arrancar (ninguna de estas tonterías sin firmar (some_slv) o x"0400"). Sé que la metodología de ISE vs Quartus es bastante diferente. Es una de las razones por las que prefiero Altera a Xilinx, pero esa es otra guerra santa por completo. :-)
Gracias. Tu respuesta me ayudó mucho. Ahora, todas las fuentes de reloj funcionan bien y la simulación da los resultados correctos. En este momento, estoy tratando de reescribir mi código en un proceso con un contador.
Dado que es una división de reloj de una década (10 = 2 * 5, y 5 no es una potencia de 2), tocar un solo contador sería muy costoso. Creo que los contadores de módulo 10 en cascada están bien, equivalentes a usar un contador BCD con conexión lógica a las primeras señales de acarreo.
Por cierto, el estilo de un pulso por período es exactamente correcto si desea usarlos como habilitaciones de reloj, en lugar de relojes, evitando así más dominios de reloj. También satisfarán los requisitos de longitud de pulso dentro de un FPGA, por lo que es posible el uso posedge... pero no los confunda con sincrónicos con clk_in (hay al menos un retraso de propagación de registro).
Realmente depende de cuáles son los requisitos reales; Tomé lo que estaba preguntando como una pregunta de ayuda con la tarea y respondí en consecuencia. Sí, desea utilizar las habilitaciones de reloj ( if rising_edge(clk) then if ce_100k = '1' then ... end if; end if;) en lugar de crear nuevos relojes ( if rising_edge(clk100k) then ... end if;) y hay diferentes formas de optimizar el sistema como señaló @DavidKessner, pero creo que lo que sugerí fue el mejor "propósito general" y el consejo "más seguro". y consejos que tienen muchas posibilidades de hacer lo que necesita.
@DavidKessner: ¿tiene un ejemplo de números enteros que no usan la cadena de transporte de manera efectiva? Según tengo entendido, fue hace mucho tiempo cuando eso fue cierto, ¡si es que alguna vez lo fue!
@MartinThompson Este es un gran tema que no se puede abordar en un comentario. Si hace una pregunta, estaré más que feliz de abordarla con algún detalle. Pero aquí está la respuesta rápida: no es que los números enteros en sí mismos no usen la cadena de transporte de manera efectiva, es que no tiene fácil acceso a la representación lógica interna del número entero y, por lo tanto, no puede hacer algunos trucos matemáticos que Haz que tu lógica sea más pequeña y más rápida. La mayoría de estos trucos matemáticos aprovechan la cadena de acarreo, por eso digo que con los números enteros no se puede hacer un uso efectivo de la cadena de acarreo.
@DavidKessner: Ahh, está bien, ahora veo lo que quieres decir, sí, eso es cierto. Y para permitirle dar más detalles, haré la pregunta como sugiere :) electronics.stackexchange.com/q/27921/763

Estás asignando señales clk_1M, clk_500k, clk_100k, clk_1k y clk_1hz en diferentes procesos en el banco de pruebas. Al mismo tiempo, ha creado una instancia de su DUT que (como sugiere Yann Vernier) genera las mismas señales. Descomente los procesos del banco de pruebas (¡excepto clk_in!) y estará bien.

Además, le aconsejaría que agregue una señal de reinicio asíncrono a la entidad del reloj para que sea sintetizable.

-1 No necesita un reinicio asíncrono para hacerlo sintetizable; en muchos casos, no desea un reinicio asíncrono (aunque no duele mucho en un CPLD Coolrunner2).
@Per E, parece que te han votado a la baja por tu consejo final, no por la esencia del consejo. Parece que nadie robó su respuesta, parece que, en cambio, se tomaron el tiempo para explicar más explícitamente el origen del problema. Perdón por cualquier confusión que esto pueda haber generado.
El banco de pruebas fue generado automáticamente por ISE, así que supuse que el código no tiene errores. Eliminé todos los procesos clk_*k y ahora, la simulación se ejecuta sin problemas.
Perdón por la confusion. Déjame elaborar. Recientemente, mi empleador tuvo un retraso en el proyecto de más de 6 meses debido a una mala codificación de VHDL. Como respuesta, a principios de esta semana dirigí la capacitación de VHDL para un grupo de ingenieros. La primera diapositiva de mi presentación fue la diapositiva del título. La diapositiva n.° 2 hablaba sobre todos los códigos/consejos VHDL incorrectos que encuentra en Internet. Lamentablemente, su comentario sobre los reinicios asíncronos fue como sal en una herida reciente. Sentí que otros no dieron una respuesta lo suficientemente detallada, por eso la respondí yo mismo. Incluso le di a Yann Vernier un +1 porque fue el primero en decir "varios conductores".
@DavidKessner: Punto tomado. Supongo que se reduce a las preferencias de estilo de mi profesor de diseño digital entonces. Aunque no puedo pensar en una razón para no inicializar los registros al reiniciar, supongo que después de leer un poco lo haré.

Tienes varios conductores. No se muestran en los fragmentos de código publicados, pero son evidentes en los archivos no fragmentados .

Mirando el archivo test_clock.vhd, crea una instancia de su unidad bajo prueba. En el mapa de puertos, asigna una salida de clock() a clk_1M. Más tarde, tienes este trozo de código:

clk_1M_process :process
   begin
        clk_1M <= '0';
        wait for clk_1M_period/2;
        clk_1M <= '1';
        wait for clk_1M_period/2;
   end process;

Este código también asigna un valor a clk_1M; por lo tanto, tiene varios controladores en sus señales.

Otras señales tienen problemas similares, así que no los revisaré aquí.

No lo veo en los fragmentos de código, pero la simulación se parece mucho a que tiene varios controladores para algunas señales. Cuando están de acuerdo, obtienes un nivel lógico, pero cada vez que no están de acuerdo, obtienes X: un conflicto en la simulación, que probablemente no sea sintetizable (si lo fuera, significaría una fritura corta). Una suposición es que es posible que haya cometido un error en alguna parte al copiar ese proceso de divisor de reloj, y le vendría bien un componente que podría instanciar en su lugar.

Gracias, pero no veo ninguna parte donde tenga "múltiples controladores". ISE está informando este tipo de error, así que supongo que no hay nadie. He puesto el código completo y la prueba aquí: pub.uart.cz/vhdl