VHDL Cómo diseñar un búfer de pantalla (marco)

Estoy tratando de usar un búfer de pantalla para almacenar, cambiar y enviar los bits de datos de video a la interfaz de transmisión DVI.

  • Estoy usando el kit de desarrollo Altera Cyclone III.
  • Usaré 1440x900 a 60 Hz como resolución, por lo que mi velocidad de reloj de píxeles es de 106,7 MHz. La interfaz DVI está escrita y probada dentro de otro proyecto que no usaba un búfer de pantalla, pero creo que no es una fuente de ningún problema.

No puedo decidir qué tipo de RAM usar como búfer de pantalla. Después de leer un poco, llegué a la conclusión de que la RAM inferida de doble puerto sería la mejor opción. Sin embargo, no estoy seguro de cómo usar sus puertos de lectura y escritura. Ya logré inferir un bloque ram y lo instalé usando una función que genera un archivo .mif.

Mi progreso actual es el siguiente:

  • Las palabras tendrán una longitud de 16 bits. Esto significa que la RAM tendrá 81K direcciones.
  • El puerto de LECTURA siempre tendrá su señal de activación de escritura BAJA (0).
  • El puerto WRITE siempre tendrá su señal de activación de escritura ALTA (1).

Así que estoy tratando de escribir y leer SIMULTÁNEAMENTE. El reloj para ambos es el mismo.

No incrusté ningún código por simplicidad, y traté de ser lo más claro posible. Si es necesario o deseado, publicaré fragmentos de código.

Mi pregunta es la siguiente: ¿Hay una mejor manera de abordar este tema, porque mis intentos hasta ahora no han tenido éxito? Parece que no puedo escribir en la RAM y no puedo encontrar el motivo. Algo me dice que tengo problemas con el tiempo, si alguien puede relacionarse con eso, por favor, ayúdame.

¡Gracias!

Editar:

Sí, estoy almacenando 1 bit por píxel con fines de almacenamiento y estoy decidiendo el color en el bloque de transmisión DVI que viene después. La decisión se basa en las coordenadas de hcounter y vcounter del DVI. Ejemplifico la RAM con '1' en cada celda. Verifico esto leyendo y enviándolo a la pantalla con un color constante:

 if vcounter < 900 then
   if hcounter < 1440 then
        if pixel_in_sgnl = '0' then
         dviRed  <= "11111111";
         dviGreen<= "00000000";
         dviBlue <= "00000000";
         else
         dviRed   <= "00000000";
         dviBlue  <= "11111111";
         dviGreen <= "00000000";
        end if;

Sin embargo, cuando trato de modificar el contenido de la RAM y luego lo leo y lo envío a la pantalla, todavía lo leo como 1, porque el color no cambia. Por ejemplo, en el siguiente código, quiero escribir "0000000000000000" en la ubicación 10000 y, como resultado, debería observar una línea de color diferente en un fondo constante. Espero haber sido lo suficientemente claro.

FB: FrameBuffer port map ( data_a => "0000000000000000",
                                data_b => "ZZZZZZZZZZZZZZZZ",   
                                addr_a => 10000, --address_write_sgnl,
                                addr_b => address_read_sgnl,
                                we_a  => '1',
                                we_b => '0',
                                clk_106 => DVI_clock,
                                q_a => open,
                                q_b => pixel_data_sgnl);

Esta es la RAM inferida que encontré en línea y modifiqué ligeramente:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity FrameBuffer is
    port 
    (   
        data_a  : in std_logic_vector(15 downto 0);
        data_b  : in std_logic_vector(15 downto 0);
        addr_a  : in natural range 0 to 80999;
        addr_b  : in natural range 0 to 80999;
        we_a        : in std_logic ;
        we_b        : in std_logic ;
        clk_106     : in std_logic;
        q_a     : out std_logic_vector(15 downto 0);
        q_b     : out std_logic_vector(15 downto 0)
    );

end FrameBuffer;

architecture rtl of FrameBuffer is

    -- Build a 2-D array type for the RAM
    subtype word_t is std_logic_vector(15 downto 0);
    type memory_t is array(0 to 80999) of word_t;

    FUNCTION initialize_ram
        return memory_t is
        variable result : memory_t;
        BEGIN
            FOR i IN 0 to 80999 LOOP
                result(i) := "1111111111111111";
            END LOOP;
        RETURN result;
    END initialize_ram;


    -- Declare the RAM
    shared variable ram : memory_t :=initialize_ram ;

begin


    -- Port A
    process(clk_106)
    begin
        if(rising_edge(clk_106)) then -- Port A
            if(we_a = '1') then
                ram(addr_a) := data_a;
            -- Read-during-write on the same port returns NEW data
                q_a <= data_a;
            else
            -- Read-during-write on the mixed port returns OLD data
                q_a <= ram(addr_a);
            end if;
        end if;
    end process;

    -- Port B
    process(clk_106)
    begin
        if(rising_edge(clk_106)) then -- Port B
            if(we_b = '1') then
                ram(addr_b) := data_b;
            -- Read-during-write on the same port returns NEW data
                q_b <= data_b;
            else
            -- Read-during-write on the mixed port returns OLD data
                q_b <= ram(addr_b);
            end if;
        end if;
    end process;
end rtl;

Edit2:

Creo que logré resolver el problema de escribir en RAM, sin embargo, todavía puedo discutirlo para mejorar mi diseño. Ahora estoy atascado con la alimentación de mi salida de búfer a DVI. Como mencioné anteriormente, tengo 1 bit por píxel en la RAM, pero el ancho de salida de la RAM es de 16 bits. Así que necesito almacenar estos 16 bits y enviarlos a DVI uno por uno en cada borde del reloj DVI. Lo hago de la siguiente manera:

process(clk106M, locked_sgnl)
begin
  if locked_sgnl = '0' then
                address_counter <= (others => '0');
                counter <= "10000";
                pixel_in_register <= (others => '0');
  elsif rising_edge(clk106M) and (locked_sgnl = '1') then
     if (address_counter < "10011110001101000") then
                        if counter >= 16 then
                                pixel_in_register <= pixel_in;
                                address_counter <= address_counter + '1';
                                counter <= "00000";
                        else
                                pixel_in_sgnl <= pixel_in_register(0);
                                pixel_in_register <= '0' & pixel_in_register(15 downto 1);
                                counter <= counter + '1';
                        end if;
          else
                        address_counter <= (others => '0');
          end if;

 end if;
end process;

 buffer_address_dvi <= address_counter;

Uso un registro y mantengo la salida del búfer dentro. Luego, en cada ciclo de reloj durante 16 ciclos, saco el LSB del contenido del registro y cambio los datos a la derecha. Lo que se me queda en la cabeza es si necesito generar la dirección de lectura antes o no.

Actualmente, si hay un 0 en el búfer, ese píxel corresponde a verde. Si es 1, corresponde a azul. Lo que hago es instanciar la memoria RAM llena de '1'. Luego escribo '0' en una parte e intento observar una línea horizontal gruesa en la pantalla. Afortunadamente, veo esa línea horizontal, pero no está estacionaria. Es barrer la pantalla de arriba hacia abajo o de abajo hacia arriba, no puedo diferenciar. ¿Cuál podría ser la causa de este problema en particular?

Supongo que solo está almacenando 1 bit por píxel, 16 píxeles por ubicación de almacenamiento. Si está utilizando RAM "inferida", entonces sí, necesitamos ver el código que está utilizando. También ayudaría si describe (o mejor aún, proporciona el código) lo que sea que esté escribiendo y por qué cree que el problema es escribir y no leer.
@DaveTweed Edité mi pregunta original. ¡Espero haber agregado suficiente información! Gracias
Parece que te equivocas un poco en tu tiempo vertical, muy probablemente por 1.
@mng No pude resolver el problema y ahora estoy realmente molesto. ¿Algún consejo sobre qué modificar? ¡Gracias!
Debe verificar todo lo que está contando a lo largo de la matriz, porque algo se está desalineando. Intente cambiar los números y vea cómo afecta el resultado. Si no puede encontrar nada malo, es posible que también deba mirar la interfaz DVI.

Respuestas (1)

Lo encontré hace un par de días:

Estaba aumentando la dirección del búfer de cuadros no solo durante la parte activa del DVI sino también en los períodos de borrado. Esto resultó en un cambio en la pantalla. Gracias a todos por sus respuestas o comentarios.