Problemas con el controlador VGA en CPLD

Lo que intento hacer es crear un controlador VGA a partir de un CPLD Lattice MachXO en Verilog.

El problema

Estoy intentando mostrar el color rojo con una resolución de 640x480 @ 60Hz usando un reloj interno de 25.175 MHz para el CPLD; sin embargo, cuando conecto el CPLD a un monitor, aparece el mensaje "Fuera de rango"; ningún monitor que pruebo puede entender qué resolución me gustaría.

lo que he probado

He simulado el código en ModelSim (imágenes incluidas), y todo parece verse bien excepto por un problema. Cuando cuento la cantidad de pasos de tiempo que han ocurrido durante la zona de visualización de V-Sync (cuando H-Sync está dibujando) y lo divido por la frecuencia de H-Sync, obtengo 479 pulsos, uno menos que los 480 líneas que debería estar dibujando. No entiendo de dónde podría provenir esto, ya que he verificado mis tiempos muchas veces y sospecho que esto puede ser un síntoma del problema, pero no estoy seguro.

Los números que estoy usando para generar mis números para los tiempos son de Tiny VGA: tinyvga.com/vga-timing/640x480@60Hz En lugar de contar líneas para V_SYNC, estoy contando pulsos de reloj, sin embargo, los números deben ser equivalentes (es decir, 2 800 líneas de conteo = 1600 pulsos). Puedo cambiar este comportamiento al conteo de líneas más tarde.

A continuación se muestra mi código y las imágenes de los tiempos de ModelSim.

module Top(RESET, H_SYNC, V_SYNC, RED);
    input  wire RESET;
    output wire H_SYNC;
    output wire V_SYNC;
    output wire RED;

    wire rgb_en;

    /*** Test Bench Code ***/
     //reg osc_clk, reset;
     //initial begin
         //#0 reset     = 0;
         //#0 osc_clk = 0;
         //#2 reset     = 1;
     //end

     //always #1 osc_clk = ~osc_clk;

    OSCC        OSCC_1 (.OSC(osc_clk)); /*< IP clock module for Lattice CPLD    >*/
    Controller  CNTRL(.NRST(RESET), .CLK(osc_clk), .H_SYNC(H_SYNC), .V_SYNC(V_SYNC), .RGB_EN(rgb_en));

    assign RED = (rgb_en ? 1:1'bz); 

endmodule

module Controller(CLK, NRST, H_SYNC, V_SYNC, RGB_EN);
    input  wire CLK;        /*< CLK input from Top module   >*/
    input  wire NRST;       /*< Reset input from Top module >*/
    output reg  H_SYNC;     /*< Goes to VGA Horizontal Sync >*/
    output reg  V_SYNC;     /*< Goes to VGA Verical Sync    >*/
    output reg  RGB_EN  ;   /*< Enables RGB values durning display time on H_SYNC >*/

    reg [9:0] h_counter;   /*< Tracks amount of pulses from CLK                 >*/
    reg [18:0] v_counter;  /*< Tracks amount of pulses from H_SYNC              >*/

    `define H_SYNC_PULSE        10'd96      /*< Length of Sync Pulse            >*/
    `define H_BACK_PORCH_END    10'd144     /*< Pulse len + Porch Len           >*/
    `define H_FRONT_PORCH_STRT  10'd784     /*< Front Porch Len - Max           >*/
    `define H_COUNT_MAX         10'd799     /*< Max line pulses for resolution  >*/

    `define V_SYNC_PULSE        19'd1600    /*< 2 H_SYNC lines       >*/
    `define V_BACK_PORCH_END    19'd28000   /*< 33+2 H_SYNC lines    >*/
    `define V_FRONT_PORCH_STRT  19'd412000  /*< 525-10 H_SYNC lines  >*/
    `define V_COUNT_MAX         19'd419999  /*< 525 H_SYNC lines     >*/

    /*** Logic for H_SYNC ***/
    always @(*) begin
        if (h_counter < `H_SYNC_PULSE) begin
            H_SYNC = 0;
            RGB_EN = 0;
        end
        /* If H_Sync is in the display zone, enable RGB */
        else if (h_counter > `H_BACK_PORCH_END && h_counter < `H_FRONT_PORCH_STRT) begin
            H_SYNC = 1;
            RGB_EN = 1;
        end
        /* During the Front Porch period, disable RGB */
        else begin
            H_SYNC = 1;
            RGB_EN = 0;
        end
    end

    /*** Logic for V_SYNC ***/
    always @(*) begin
        if (v_counter < `V_SYNC_PULSE) begin
            V_SYNC = 0;
        end
        else begin
            V_SYNC = 1;
        end
    end

    /*** Counter logic ***/
    always @(posedge CLK) begin
        if (h_counter >= `H_COUNT_MAX || !NRST) begin
            h_counter <= 11'b00;
        end
        else begin
            h_counter <= h_counter + 1;
        end
    end

    always @(posedge CLK) begin
        if (v_counter >= `V_COUNT_MAX || !NRST) begin
            v_counter <= 11'b00;
        end
        else begin
            v_counter <= v_counter + 1;
        end
    end

endmodule

H/V_SYNC y sus respectivos contadores se escriben entre sí

Realmente debería incluir las formas de onda reales medidas con un osciloscopio en su pregunta. He encontrado pocas razones para confiar en las simulaciones de lógica programable.
Un comentario general: en su código, dice que está creando "máquinas de estado" para V_SYNC y H_SYNC, pero me parece que esos bloques siempre solo harán lógica combinacional. Lo único que tiene actualmente como una máquina de estado es el contador.
Estoy de acuerdo. El comentario ha sido editado.

Respuestas (2)

Debe mantener la salida H_SYNC incluso cuando V_SYNC está activo.

Gracias Dave, encontré material que también confirmó tu respuesta. Desde entonces, ajusté el código para que la salida H_SYNC siempre se esté ejecutando y actualizaré las imágenes tan pronto como pueda más tarde hoy. Desafortunadamente, el problema aún persiste; Sigo recibiendo errores "Fuera de rango".

Finalmente me he concentrado en mi problema. En la documentación del CPLD y según las sugerencias de mis profesores, pensé que el reloj interno del CPLD era configurable. Resulta que lo que estaba leyendo era el margen de error del reloj, no la programabilidad. Mi problema es que el oscilador interno no ha estado funcionando a 25,175 MHz como pensaba.

En caso de que algún otro alma busque esto en Google, que se sepa que el Lattice MachXO 2280 no se puede usar para la salida VGA debido al valor aleatorio y la falta de programación del reloj sin usar un oscilador externo.