Interface d’une caméra TRDB-D5M avec un FPGA DE0-Nano

Ceci est mon premier projet en VHDL, effectué en novembre dernier (désolé, je suis en train de rattraper la documentation des derniers mois 🙂

J’ai d’abord commencé par réussir à amener la clock de 50MHz sur la sortie XCLKIN de la caméra. C’est un simple signal en vhdl :

———————————
— Clock output to the D5M camera
———————————
— library declaration
library IEEE;
use IEEE.std_logic_1164.all;
— entity
entity D5M_clock is
    port ( CLOCK_50 : in std_logic;
        D5M_XCLKIN : out std_logic);
end D5M_clock;
— architecture
architecture myD5M_clock of D5M_clock is
begin
    D5M_XCLKIN <= CLOCK_50; -- put 50Mhz clock on the clock input of the camera
end myD5M_clock;

Ensuite, j’ai fait l’interfaçage avec la sortie du pixel sur le port parallèle :

———————
— Pixel Interfacing
———————
— library declaration
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
— entity
entity pixel_interface is
    port ( D5M_PIXCLK : in std_logic;
        D5M_LVAL : in std_logic;
        D5M_FVAL : in std_logic;
        –D5M_D0 : in std_logic;
        –D5M_D1 : in std_logic;
        –D5M_D2 : in std_logic;
        –D5M_D3 : in std_logic;
        –D5M_D4 : in std_logic;
        –D5M_D5 : in std_logic;
        D5M_D6 : in std_logic;
        D5M_D7 : in std_logic;
        D5M_D8 : in std_logic;
        D5M_D9 : in std_logic;
        D5M_D10 : in std_logic;
        D5M_D11 : in std_logic;
———————————————
        –pixel : out std_logic_vector(11 downto 0));
        pixel_out : out std_logic_vector(5 downto 0);
        Xpos_out : out unsigned(11 downto 0);
        Ypos_out : out unsigned(10 downto 0));
end pixel_interface;
— architecture
architecture my_pixel_interface of pixel_interface is
    signal Xpos : unsigned(11 downto 0);
    signal Ypos : unsigned(10 downto 0);
    signal newline : std_logic;
begin
    pixel_latch: process(D5M_PIXCLK) — latch pixel data
    begin
        if (falling_edge(D5M_PIXCLK) and (D5M_LVAL = ‘1’) and (D5M_FVAL = ‘1’)) then — read pixel data when it’s valid
            pixel_out <= D5M_D11 & D5M_D10 & D5M_D9 & D5M_D8 & D5M_D7 & D5M_D6;
        end if;
    end process pixel_latch;
    position_counter : process(D5M_FVAL,D5M_LVAL,D5M_PIXCLK)
    begin
        if (D5M_FVAL = ‘0’) then
            Ypos <= to_unsigned(-1, Ypos'length); -- -1 to start the first count at 0
        elsif (D5M_LVAL = ‘0’) then
            Xpos <= to_unsigned(-1, Xpos'length); -- Reset Xpos counter (-1 to start the first count at 0)
            newline <= '1';
        elsif (falling_edge(D5M_PIXCLK)) then — When a new valid pixel is read
            if (newline = ‘1’) then — Increment Ypos count only once a new line
                Ypos <= Ypos + 1;
                newline <= '0';
            end if;
            Xpos <= Xpos + 1; -- Increment Xpos count
        end if;
    end process position_counter;
    Xpos_out <= Xpos; -- Final output assignement
    Ypos_out <= Ypos;
end my_pixel_interface;

Dans l’architecture, il y a deux process : pixel_latch et position_counter. Pixel_latch est une latch qui permet de lire le port parallèle au bon moment, lorsque la PIXCLOCK est en falling edge (la D5M écrit le pixel sur le port en rising edge) et le met dans un vecteur pixel_out. Position_counter permet de se synchroniser sur les autres sorties de la caméra, soit FVAL (frame valid) et LVAL (line valid) afin de compter les pixels et de savoir la position du pixel actuel en x et en y sur l’image, afin de bien pouvoir reconstruire celle-ci ultérieurement. Le -1 est reconverti en unsigned comme étant la valeur maximale du compteur (2^11-1 pour Xpos et 2^10-1 pour Ypos, ici présent), permettant que la première valeur de position soit 0,0 et non pas 1. Le compteur est incrémenté tant et aussi longtemps que FVAL et LVAL sont à l’état haut. Un signal newline est produit pour compter les lignes (Ypos). La programmation de cette architecture m’a permis de mieux comprendre la différence entre les conditions en programmation classique et celles dans un process de vhdl : il ne peut y avoir qu’une clock que l’on traite, tous les signaux doivent avoir des états définis peu importe ce qu’il se passe, même si on ne s’en sert pas. Bref, la base de la pensée sous-jacente à la conception matérielle des circuits logiques.

Finalement, un dernier composant permet de choisir un seul pixel de l’image et l’afficher sur les DELs de la carte, pour voir si tout fonctionne bien :

—————————-
— Pixel selection to output
—————————-
— library declaration
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
— entity
entity pixel_selection is
    port ( pixel_in : in std_logic_vector(5 downto 0);
        Xpos_in : in unsigned(11 downto 0);
        Ypos_in : in unsigned(10 downto 0);
———————————————
        POUT0 : out std_logic;
        POUT1 : out std_logic;
        POUT2 : out std_logic;
        POUT3 : out std_logic;
        POUT4 : out std_logic;
        POUT5 : out std_logic);
end pixel_selection;
— architecture
architecture my_pixel_selection of pixel_selection is
begin
    pixsel : process(Xpos_in,Ypos_in)
    begin
        if ((Xpos_in = 1296) and (Ypos_in = 972)) then
            POUT0 <= pixel_in(0);
            POUT1 <= pixel_in(1);
            POUT2 <= pixel_in(2);
            POUT3 <= pixel_in(3);
            POUT4 <= pixel_in(4);
            POUT5 <= pixel_in(5);
        end if;
    end process pixsel;
end my_pixel_selection;

C’était avant que je ne comprenne qu’on pouvait directement mapper les vecteurs dans le pin map, sans avoir besoin d’expliciter chaque bit comme dans le code ci-haut. Mais pour la compréhension de ce qu’il se passe, c’est bien de commencer comme ça : le LSB est relié à la DEL0, etc jusqu’à la DEL5. J’aurais pu utiliser les 8 DELs, mais de toute manière, mon but était de l’interfacer avec un écran en RGB16bit, ce qui a été fait (prochain article! 😉