Forum: FPGA, VHDL & Co. Zeilen-Puffer in VHDL


von Constantin H. (tino94)


Lesenswert?

Hallo liebe Forengemeinde,

im Rahmen meiner Bachelorarbeit versuche ich einen 
Bildverarbeitungsalgorithmus in VHDL zu implementieren.

Um diverse Filterfunktionen und bspw. die Varianz unter einer 
vorgegebenen Maske zu berechnen habe ich einen Zeilen-Puffer mittels 
Fifo's implementiert.

Anfangs habe ich dies noch mit einer FSM getan um die Steuersignale der 
Fifo's zu setzen und rückzusetzen. Die FSM macht was sie soll, nur ist 
das Elaborationsergebnis ziemlich mächtig und endet in einem 
Miltiplexer-Wald.

Ich habe dann im Prinzip die selbe Funktionalität ohne FSM 
implementiert. Es funktioniert auch diesmal, nur das es hier in einem 
Multiplexer-Dschungel endet ! :D

Nun stelle ich mir die Frage wie es einfacher klappt. Ich bin mir auch 
ziemlich sicher das es irgendwie mit ein paar FlipFlops, diversen 
Logik-Gattern und Multiplexern klappt, ich komme aber einfach nicht 
darauf.

Hier ist mal bsp.haft der Code für eine 5x5 Maske, also 4 Fifo's.
Die gültigen Daten einer Spalte der 5x5 Maske liegen bei einem o_lVal = 
'1' und o_fVal = '1' an o_video bzw. LB_data an, um im Weiteren mit 
diesen zu rechnen.
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity LB_src is
6
--    generic (NUM_LB : integer := 4 );
7
    Port (   clk, i_lVal, i_fVal, n_rst : in std_logic;
8
             i_data : in std_logic_vector(7 downto 0);
9
             
10
             o_lVal, o_fVal : out std_logic;
11
--             o_data : out std_logic_vector(8*(NUM_LB+1)-1 downto 0) );
12
             o_data : out std_logic_vector(39 downto 0) );
13
end LB_src;
14
15
architecture Behavioral of LB_src is
16
    
17
---- Konstanten
18
constant NUM_LB : integer := 4;
19
constant ONES_VEC : std_logic_vector(NUM_LB-1 downto 0) := (others => '1');
20
 
21
---- Komponenten    
22
component LB_Fifo
23
   PORT (
24
    clk : IN STD_LOGIC;
25
    srst : IN STD_LOGIC;
26
    din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
27
    wr_en : IN STD_LOGIC;
28
    rd_en : IN STD_LOGIC;
29
    dout : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
30
    full : OUT STD_LOGIC;
31
    empty : OUT STD_LOGIC
32
  );
33
end component;
34
35
---- Signale   
36
signal LB_data, LB_data_del, LB_data_del_del : std_logic_vector(i_data'length*(NUM_LB+1) -1 downto 0) := (others => '0');
37
signal dout_f1, dout_f2, dout_f3, dout_f4 : std_logic_vector(i_data'range) := (others => '0');
38
signal i_data_sig, i_data_sig_del : std_logic_vector(i_data'range) := (others => '0');
39
40
signal f_full_REG, f_empty_REG, f_empty_REG_del, f_wr_REG, f_rd_REG, mask_wr, mask_rd : std_logic_vector(NUM_LB-1 downto 0) := (others => '0');
41
signal lVal_REG, fVal_REG : std_logic_vector(NUM_LB downto 0) := (others => '0');
42
signal last_line_cnt : unsigned(NUM_LB downto 0) := (others => '0');
43
44
signal lVal_sig, fVal_sig, r_edge_lVal, f_edge_lVal, r_edge_fVal, f_edge_fVal : std_logic := '0';
45
46
signal ONES_WITH_ZERO_VEC : std_logic_vector(NUM_LB-1 downto 0) := (others => '1');
47
48
begin
49
50
-- Fifo Instanziierung
51
Fifo0 : LB_Fifo
52
    port map(clk => clk, srst => (not n_rst), din => i_data_sig, wr_en => f_wr_REG(0),
53
             rd_en => f_rd_REG(0), dout => dout_f1, full => f_full_REG(0), empty => f_empty_REG(0));
54
55
Fifo1 : LB_Fifo
56
    port map(clk => clk, srst => (not n_rst), din => dout_f1, wr_en => f_wr_REG(1),
57
             rd_en => f_rd_REG(1), dout => dout_f2, full => f_full_REG(1), empty => f_empty_REG(1));
58
59
Fifo2 : LB_Fifo
60
    port map(clk => clk, srst => (not n_rst), din => dout_f2, wr_en => f_wr_REG(2),
61
             rd_en => f_rd_REG(2), dout => dout_f3, full => f_full_REG(2), empty => f_empty_REG(2));
62
63
Fifo3 : LB_Fifo
64
    port map(clk => clk, srst => (not n_rst), din => dout_f3, wr_en => f_wr_REG(3),
65
             rd_en => f_rd_REG(3), dout => dout_f4, full => f_full_REG(3), empty => f_empty_REG(3));
66
67
ONES_WITH_ZERO_VEC(0) <= '0';
68
69
o_data <= LB_data;
70
LB_data <= dout_f4 & dout_f3 & dout_f2 & dout_f1 & i_data_sig_del;
71
 
72
r_edge_lVal <= i_lVal AND (NOT lVal_sig);
73
f_edge_lVal <= (NOT i_lVal) AND lVal_sig;
74
75
f_edge_fVal <= (NOT i_fVal) AND fVal_sig;
76
77
CONTROLL_FIFOS: process(clk) begin
78
    if rising_edge(clk) then
79
        if n_rst = '0' then
80
            lVal_REG <= (others => '0');        
81
            fVal_REG <= (others => '0');  
82
            
83
            o_lVal <= '0';
84
            o_fVal <= '0';
85
            
86
            mask_wr(mask_wr'left downto 1) <= (others => '0');  
87
            mask_rd(mask_rd'left downto 1) <= (others => '0');  
88
            mask_wr(0) <= '1';  
89
            
90
            last_line_cnt <= (others => '0'); 
91
        
92
        else
93
            i_data_sig <= i_data;
94
            i_data_sig_del <= i_data_sig;  
95
            
96
            f_empty_REG_del <= f_empty_REG;              
97
            lVal_sig <= i_lVal;
98
            fVal_sig <= i_fVal;     
99
              
100
                   
101
            if r_edge_lVal = '1' and i_fVal = '1' then        
102
                f_wr_REG(0) <= '1';    
103
                f_rd_REG <= mask_rd(mask_rd'left downto 0);
104
                
105
                fVal_REG <= fVal_REG(fVal_REG'left-1 downto 0) & '1';
106
                lVal_REG <= lVal_REG(lVal_REG'left-1 downto 0) & '1';
107
            
108
            -- i_lVal ist mindestens für einen Takt auf '1'-Pegel
109
            elsif i_lVal = '1' and i_fVal = '1' then
110
                o_lVal <= lVal_REG(lVal_REG'left);
111
                o_fVal <= fVal_REG(fVal_REG'left); 
112
                f_wr_REG <= mask_wr;
113
            end if;
114
            
115
            -- fallende Flanke i_lVal
116
            if f_edge_lVal = '1' and i_fVal = '1' then
117
                f_rd_REG <= (others => '0');                  
118
                f_wr_REG(0) <= '0';   
119
                
120
                mask_wr <= mask_wr(mask_wr'left-1 downto 0) & '1' ;
121
                mask_rd <= mask_rd(mask_rd'left-1 downto 0) & '1' ;
122
            
123
            -- i_lVal ist mindestens einen Takt auf '0'-Pegel
124
            elsif i_lVal = '0' and i_fVal = '1' then
125
                o_lVal <= '0'; 
126
              
127
                f_rd_REG <= (others => '0');                  
128
                f_wr_REG <= (others => '0');       
129
            end if;    
130
            
131
            -- fallende Flanke i_fVal -> Frame ist vorbei
132
            if f_edge_fVal = '1' then
133
                last_line_cnt <= last_line_cnt + 1;              
134
                f_rd_REG <= (others => '0');
135
                f_wr_REG(0) <= '0';             
136
                mask_wr <= mask_wr(mask_wr'left-1 downto 0) & '0';    
137
     
138
            -- Letzte Zeilen -> d.H. Fifos nacheinander leer lesen
139
            elsif f_edge_fVal = '0' and i_fVal = '0' and unsigned(f_empty_REG) = 0 then
140
                f_rd_REG <= mask_rd(mask_rd'left downto 0);
141
                f_wr_REG <= (others => '0');
142
                o_lVal <= '0';
143
            end if;
144
               
145
            -- last_line_cnt = 1 -> Aufhören in Fifo zu schreiben, o_lVal ein Takt ausschalten und last_line_cnt hochzählen    
146
            if last_line_cnt = 1 then 
147
                f_wr_REG <= (others => '0');
148
                  
149
                o_lVal <= '0';
150
                last_line_cnt <= last_line_cnt + 1;
151
                
152
            -- last_line_cnt = 2 -> o_lVal wieder einschalten, Letzte Zeilen aus Fifos raus-lesen bzw. rein-schreiben     
153
            elsif last_line_cnt = 2 then
154
                o_lVal <= lVal_REG(lVal_REG'left);
155
                f_wr_REG <= mask_wr;
156
                f_rd_REG <= mask_rd(mask_rd'left downto 0);
157
            end if;    
158
              
159
            -- Das Erste / Ein weiteres  Fifo ist leer.     
160
            if f_empty_REG /= f_empty_REG_del and last_line_cnt = 2 then
161
162
                mask_rd <= mask_rd(mask_rd'left-1 downto 0) & '0';   
163
                mask_wr <= mask_wr(mask_wr'left-1 downto 0) & '0';   
164
             
165
                f_rd_REG <= (others => '0');                  
166
                f_wr_REG <= (others => '0'); 
167
                
168
                o_lVal <= '0';
169
                
170
                -- Alles Fifos sind leer
171
                if f_empty_REG = ONES_VEC then
172
                    o_fVal <= '0'; 
173
                    
174
                    mask_wr(0) <= '1';  
175
                    mask_rd <= (others => '0'); 
176
                   
177
                    lVal_REG <= (others => '0');
178
                    fVal_REG <= (others => '0');
179
                    last_line_cnt <= (others => '0');
180
                end if;                     
181
            end if;            
182
        end if;         
183
    end if;
184
end process CONTROLL_FIFOS;
185
186
end Behavioral;

Ich nutze Vivado 2021.2 als IDE.

Über Hilfestellungen, Anregungen und Tipps würde ich mich sehr freuen.

Ich danke allen Vorab und wünsche,
einen schönen Sonntag.

von J. S. (engineer) Benutzerseite


Lesenswert?

Für so etwas postet man kein VHDL sondern ein Blockschaltbild und ein 
Diagramm aus dem das Zeitverhalten = Funktion des Systems hervor geht.

Was ich schon wieder sehe, ist das pathologisch benutzte und in 
Logikdesigns völlig deplazierte NOT_RESET.

Constantin H. schrieb:
> Hier ist mal bsp.haft der Code für eine 5x5 Maske, also 4 Fifo's.
Meine 5x5 laufen mit 6 FIFOs - wenn es 100% on the fly und asynchron 
beschrieben werden muss.

: Bearbeitet durch User
von FPGA Notfallseelsorge (Gast)


Lesenswert?

Jürgen S. schrieb:
> Meine 5x5 laufen mit 6 FIFOs

Verschwendung von kostbarem BRAM.

von J. S. (engineer) Benutzerseite


Lesenswert?

FPGA Notfallseelsorge schrieb im Beitrag #7116082:
> Verschwendung von kostbarem BRAM.

Nee, intelligente Bandbreitenoptimierung!

Man schreibt die neue aktive Zeile mit dem Tempo des Videotakts in das 
jeweils freie RAM und multiplext diese 6er-Gruppe auf beiden Seiten. Am 
Ausgang werden jeweils die 5 "alten" Zeilen, die komplett geschrieben 
wurden, dargestellt. Diese kann man mit jedem beliebigen Takt lesen. 
Zusammen mit dem langsamen HDMI und der Austastlücke bei HYSNCH bekommt 
man damit genug Zeit, die RAMs zweimal auszulesen und so zwei 
unterschiedliche Filter "zur gleichen Zeit" anzuwenden. Zusammen mit 
einer geschickten 3x2-2x3-Taktung kann man damit auch die 3 Farben von 2 
Pixeln mit 6/2 = 3 parallelen Pfaden auf dem HDMI-Takt bearbeiten und 
packt das im Spartan 6, bzw. UHD im Artix 7 unter Einsparung von jeweils 
33% - 50% der wertvollen Multiplier. Von denen werden nämlich bei 
größeren Matchern eine ganz Menge benötigt. Wenn man es ganz cool macht, 
kann man dann die unangetasteten RAMs noch dazu nutzen, andere Dinge zu 
tun, wie Defektpixelkorrektur, Interpolation etc ...

von FPGA Notfallseelsorge (Gast)


Lesenswert?

Bringt dem TO aber nichts. Der schreibt was von FIFOs, und hat nicht die 
Anforderung von mehreren Filtern.
Für das was er will kommt man mit 4 FIFOs aus.

von Vancouver (Gast)


Lesenswert?

Hm, das klingt mächtig kompliziert. Ich habe das so gelöst:

- Wenn du das Read-Enable einer Fifo mit dem Full-Flag verbindest, 
machst du ohne zusätzliche Logik aus einer FIFO ein Schieberegister 
(SR).

- Angenommen, die Zeilenlänge ist N, dann erzeugst Du auf diese Weise 4 
SR der Länge N-5. Vor den Eingang baust du jeweils eine Kette aus 5 
einzelnen Registern und hast damit wieder SR der Länge N, wobei du auf 
die ersten 5 Elemente parallel zugreifen kannst.

- Du verkettest 4 dieser SRs und hängst ans Ende nochmal 5 einzelne 
Register,
also so:

   RRRRR-------------------------...
...RRRRR-------------------------...
...RRRRR-------------------------...
...RRRRR-------------------------...
...RRRRR

R sind die Register, und "-" die Fifo-SRs. An den Eingang legst du dann 
in jedem Takt einen neuen Pixel. Wenn die ganze Speicherkette 
vollgelaufen ist, hast du an den 25 einzelen Registern die Pixel eines 
5x5-Kernels anliegen, auf dem du nach Lust und Laune herumrechnen 
kannst. Bezogen auf das Bild wandert der Kernel in jedem Takt eine 
Position weiter. Die Gesamtlatenz der Pipeline ist 4N+5, danach bekommst 
du in jedem Takt einen neuen Kernel. Du wirst den Filter als auch 
pipelinen müssen.

von J. S. (engineer) Benutzerseite


Lesenswert?

Constantin H. schrieb:
> für eine 5x5 Maske, also 4 Fifo's.
Kommt drauf an, wie man es timen möchte. Man braucht in jedem Fall einen 
Speicher für alle Punkte von 5 Zeilen und mit nur 4 FIFOs muss 
gleichzeitig auf einen gelesen und geschrieben werden und dies auch mit 
demselben Takt. Je nachdem wie der Filterkernel aussieht, muss nicht 
parallel auf alle 25 Punkte gleichzeitig zugegriffen werden. Umgekehrt 
kann es auch sein, dass man wegen oversampling, 
Zwischenzeileninterpretation einige Punkte / Zeilen mehrfach braucht. 
Über die Art der zu implementierenden Filter wurde ja nichts gesagt.

FPGA Notfallseelsorge schrieb im Beitrag #7116290:
> Der schreibt was von FIFOs, und hat nicht die
> Anforderung von mehreren Filtern.
Ich lese hier das:

Constantin H. schrieb:
> Um diverse Filterfunktionen und bspw. die Varianz unter einer
> vorgegebenen Maske zu berechnen
"diverse Filterfunktionen" kann ja Allesmögliche sein, inklusive 
asynchron angedocktem AXI-Core. Im Allgemeinen Fall, muss man das 
Einschreiben und Auslesen so weit wie möglich von einander ablösen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.