Problema al agregar dos contadores en serie en un FPGA

Estoy usando Verilog en Lattice Diamond IDE con una placa de conexión MachXO2 7000HE de celosía.

Construí un contador básico con una entrada de límite que genera una salida de reloj de período variable. Funciona bien por sí solo, sin embargo, cuando agrego dos instancias de este módulo y las conecto en serie (para escalar la frecuencia dos veces), obtengo un resultado extraño en el FPGA. Sin embargo, la simulación parece mostrar lo que esperaba del código.

Aquí está el módulo superior:

 module clock_generator (fpga_clock, cnt1_clock, cnt2_clock, cnt2_counter);
    output  wire            fpga_clock;
    output  wire            cnt1_clock;
    output  wire            cnt2_clock;
    output  wire    [7:0]   cnt2_counter;

    reg     [7:0]   default_period_1    = 8'b00000011;
    reg     [7:0]   default_period_2    = 8'b00000011;

    defparam OSCH_inst.NOM_FREQ = "2.08";
    OSCH OSCH_inst(.STDBY(1'b0), .OSC(fpga_clock));

    counter Counter_1_inst(.clk_in(fpga_clock), .limit_in(default_period_1), .clk_out(cnt1_clock), .cnt_out());
    counter Counter_2_inst(.clk_in(cnt1_clock), .limit_in(default_period_1), .clk_out(cnt2_clock), .cnt_out(cnt2_counter)); 
endmodule

module counter (clk_in, limit_in, clk_out, cnt_out, rst);
    input   wire            clk_in;
    input   wire    [7:0]   limit_in;
    output  reg             clk_out     = 1'b1;
    output  reg     [7:0]   cnt_out     = 8'b00000000;
    input   wire            rst;

    always @(posedge clk_in or posedge rst) begin
        if (rst) begin
            clk_out <=0;
            cnt_out <=0;
        end else if (cnt_out == limit_in) begin
            clk_out <= !clk_out;
            cnt_out <= 0;
        end else begin
            cnt_out <= cnt_out + 1'b1;
        end
    end
endmodule

Y aquí está el banco de pruebas para mi simulación:

`timescale 1 ns / 1 ns

module testbench;
    wire            fpga_clock;
    wire            cnt1_clock;
    wire            cnt2_clock;
    wire    [7:0]   cnt2_counter;

    clock_generator dut(.fpga_clock(fpga_clock), .cnt1_clock(cnt1_clock), .cnt2_clock(cnt2_clock), .cnt2_counter(cnt2_counter));

    initial begin
        #1400000000
        $finish;
    end
endmodule

Aquí está el resultado de la simulación:

salida de simulación

Y la salida del alcance:

ch1 - fpga_reloj

ch2 - cnt1_clock

ch3 - cnt2_clock

ch4 - contador_cnt2[1]

salida de alcanceY la configuración de la sonda:Configuración1

El canal 3 ( cnt2_clock ) debe tener el doble del período de cnt2_counter[1] , como está en la salida de la simulación. En cambio, como puede ver, es una explosión de bordes de frecuencia más alta donde debería estar el borde único. He estado en esto por un tiempo ahora. ¿Qué me estoy perdiendo?

Permítanme agregar también el shematic:ingrese la descripción de la imagen aquí

¡Gracias!

########## Editar con imágenes adicionales

Salida de alcance con cnt2_counter[0]

ch1 - fpga_reloj

ch2 - cnt1_clock

ch3 - contador_cnt2[0]

ch4 - contador_cnt2[1]

[ cnt2_contador0[4]Y la configuración de la sonda:Configuración2

Lista de pinesLista de pines

Introduzca la señal de reinicio y realice la configuración de los registros en la señal de reinicio (en lugar de asignar valores al comienzo del módulo; sospecho que debería obtener una lista de advertencias). De acuerdo con sus lecturas, el registro cambia cuando su reloj de entrada es bajo. Otra entrada para realizar el cambio de estado de registro es su entrada de configuración/reinicio, y no se enruta explícitamente a ninguna parte aquí.
@Anónimo, no, la única advertencia es esta: .../top.v(12,2-12,49) (VERI-1927) el puerto SEDSTDBY permanece desconectado para esta instancia . Agregué un reinicio como sugirió, pero no tuvo ningún efecto en la salida del alcance.
¿Está absolutamente seguro de que Ch3 en el diagrama de alcance es cnt2_clock? Pregunto, ya que está cambiando sincrónicamente a Ch1, que ha dicho que es fpga_clock. El código que ha publicado establece explícitamente que cnt2_clock se actualiza cada flanco ascendente de cnt1_clock.
Para el comentario de @Vance... verifique su informe PAR para las asignaciones de IO.
@Vance, sí, estoy seguro. He asignado cnt2_clock a un par de pines diferentes y el resultado es siempre el mismo.
@CapnJJ, lo siento, no estoy seguro de lo que quiere decir con "informe PAR".
Debería haber un archivo de informe después de ejecutar Place-and-Route (PAR) que le dirá, entre otras cosas, dónde se asignaron los pines. Se genera durante la última fase de su síntesis cuando crea el .bit (programación) archivo
Creo que vamos a necesitar más información sobre su configuración de prueba. Los resultados del alcance son muy extraños. Su código es bastante simple (aunque estoy más familiarizado con VHDL que con Verilog), y la simulación confirma la funcionalidad, por lo que no creo que el problema esté ahí. También ha proporcionado una vista RTL que se ve bien. Por lo tanto, hay algo en el hardware real que está causando este efecto. Entonces, esto se ha convertido en un problema de depuración más general. ¿Podría describir o publicar una imagen de su configuración de prueba? Además, ¿podemos mirar el rastro cnt2_counter[0].
@Vance, bueno, ahora es un desastre, pero agregué fotos de la configuración. Estoy bastante seguro de que ese no es el problema. También agregué cnt2_counter[0] .
@CapnJJ El único informe que encontré con respecto a la asignación de pines se llama "Señal/Pad" en "Informes de proceso". Muestra los mismos pines que le he asignado.
Gracias por la actualización amfast. ¿Qué tan seguro está de que está midiendo cnt2_clk en la primera imagen del alcance? Verifique el pin out que se ha asignado al dispositivo (ya sea automáticamente o por su archivo ucf/sdc) y verifique la hoja de datos para el pin out del devkit. El hecho es que cnt2_counter funciona como se esperaba, por lo que la salida debería funcionar.

Respuestas (2)

Estás usando un reloj generado. Por lo general, esto no es recomendable en un FPGA, ya que debe hacerse con mucho cuidado para evitar fallas y garantizar que el cierre de tiempo sea posible. Parece que podría estar experimentando algunas fallas en cnt1_clock que están arruinando la segunda instancia. Intente usar las habilitaciones de reloj y vea qué sucede.

Además, fpga_clock debe ser una entrada y no una salida en su módulo superior.

Cuando dices "reloj generado", ¿te refieres a cnt1_clock y cnt1_clock ? ¿Está diciendo que debería usar la activación del reloj en lugar del divisor del reloj como se describe aquí ? El hecho de que esto sea un problema es nuevo para mí, pero leerlo da la impresión de que mi código está mal escrito desde el principio.
En general, es recomendable evitar hacer @(posege something_that_isn't_a_proper_clock) ya que esto significa que su lógica podría ser "cronometrada" por cualquier tipo de falla que podría generar cualquier lógica que esté generando su señal de reloj, y la simulación no detectará esto. También hace que el análisis de tiempo sea más complejo y es más difícil cerrar el tiempo de esta manera. Por lo tanto, se prefieren las habilitaciones de reloj. Ahora, parece que su reloj generado está siendo impulsado directamente por un flip flop, por lo que, en teoría, no debería haber fallas, por lo que no estoy seguro exactamente de por qué está viendo el comportamiento que está viendo.

¿Cómo debería funcionar esto?

always @(posedge clk_in) begin
    cnt_out <= cnt_out + 1'b1;
    if (cnt_out == limit_in) begin
        clk_out <= !clk_out;
        cnt_out <= 0;
    end
end 

Tal vez te refieres a:

always @(posedge clk_in) begin
    cnt_out <= (cnt_out == limit_in) ? 0 : cnt_out + 1;
    clk_out <= (cnt_out == limit_in) ? ~clk_out : clk_out;
end 
Se supone que funciona igual que en la simulación. Y también en el FPGA en lo que respecta a Counter_1_inst . Ahora he cambiado el código anterior para incluir también una función de reinicio.
Ese código debe sintetizarse exactamente de la misma manera, aunque sea más difícil de leer.