mikrocontroller.net

Forum: FPGA, VHDL & Co. VHDL Timing Constraints nicht erfüllt


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein VHDL - Design entworfen, das ein AD - Wandler Modul, einen 
FIR Tiefpass und einen DA - Wandler umfasst. Ich benutze das ZYBO Z7020 
mit Xilinx Vivado. Das gesamte Design verwendet den internen 125MHz 
Clock als Input. Dieser wird im AD- und DA Modul auf 1MHz 
heruntergetaktet. Der Filter arbeitet direkt mit den 125MHz Ich erhalte 
nun den Fehler, dass die Timinganforderungen nicht erfüllt sind. Mit 
dieser Meldung hatte ich bisher nie etwas zu tun. Deswegen kann ich auch 
überhaupt nicht herauslesen, was ich jetzt zu tun habe. Im Anhang findet 
Ihr die Design Timing Summary. Im Unterordner "Intra - Clock - Paths" 
werden mir 10 Pfade angezeigt, deren Slacks scheinbar zu klein sind. Es 
wird hier von einer zu hohen Arrival Time geredet. Das Elaborate Design 
hängt ebenfalls an.

Wie muss ich jetzt weiter vorgehen? Ich kann auch gerne meinen Quellcode 
bereitstellen, falls das weiterhilft.

Autor: St. D. (st_d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

: Bearbeitet durch User
Autor: Samuel C. (neoexacun)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein FIR scheint die 125MHz nicht zu packen. Entweder musst du den 
schneller bekommen, pipelinen oder den Takt senken. Bist du auf die 
125MHz angewiesen?

: Bearbeitet durch User
Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
St. D. schrieb:
> Du kannst hier anfangen:
> 
https://www.xilinx.com/support/documentation-navigation/design-hubs/dh0006-vivado-design-analysis-and-timing-closure-hub.html
> bzw.
> 
https://www.xilinx.com/content/dam/xilinx/support/documentation/sw_manuals/xilinx2019_1/ug906-vivado-design-analysis.pdf

Ich bin leider absolut unfähig, mir aus den Dokumentationen von Xilinx 
Informationen herauszuholen bzw. tue ich ich leider schwer damit, diese 
zu verstehen. Deswegen krame ich mich eigentlich dauernd durch Foren, 
hier kam ich nun aber nicht mehr weiter.

Samuel C. schrieb:
> Dein FIR scheint die 125MHz nicht zu packen. Entweder musst du den
> schneller bekommen, pipelinen oder den Takt senken. Bist du auf die
> 125MHz angewiesen?

Hast Du das nur daran abgelesen, dass die Timing Gruppe "sys_clk_pin" 
betroffen ist?
Generell bin ich darauf nicht angewiesen. Meine Idee war nur, dass ich 
pro Sample bereits 21 Multiplikationen und Additionen durchführen muss. 
Deswegen dachte ich, versorge ich den FIR einfach mal mit dem höchsten, 
was ich zur Verfügung habe. Ich versuche mal, den Takt zu halbieren.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Im Unterordner "Intra - Clock - Paths"
> werden mir 10 Pfade angezeigt, deren Slacks scheinbar zu klein sind.

Alex K. schrieb:
> Deswegen kann ich auch
> überhaupt nicht herauslesen, was ich jetzt zu tun habe.

Die Timing-Analyse ermittelt die Signallaufzeiten von einem Register zu 
einem anderen - in VHDL übersetzt: von einem getakteten Prozess zum 
nächsten. Dabei wird davon ausgegangen, daß die Daten rechtzeitig zur 
nächsten sensitiven Taktflanke (abzüglich der Setup-Zeit des 
Zielregisters) dort ankommen müssen.

Du hast nun für die angemeckerten Pfade zwischen den Registern so viel 
Kombinatorik eingebaut, daß das zeitlich nicht mehr hinhaut, d.h. die 
Daten kommen erst nach der Taktflanke an und dein Design funktioniert 
nicht (entweder weil dein Design mit "alten" Daten hantiert oder 
schlimmer - mit Glitches).

Deine Logik mußt Du jetzt so optimieren, daß das eben wieder möglich 
ist.

Grundsätzlich hast Du diese Möglichkeiten:

- kombinatorische Logik vereinfachen
- kombinatorische Logik in zwei (oder mehr) Taktzyklen aufteilen 
und/oder pipelinen
- "mir Wurscht" sagen und Multicycles einbauen (u.U. musst Du dann dein 
Design so umbauen, daß dein Zielregister mitgeteilt bekommt, wann die 
Daten wirklich gültig sind)

Autor: Samuel C. (neoexacun)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Hast Du das nur daran abgelesen, dass die Timing Gruppe "sys_clk_pin"
> betroffen ist?
Nein. Es ist zwar nciht viel zu erkennen, aber es sieht so aus, als 
wären ausschließlich Endpunkte innerhalb deines FIRs betroffen ("from", 
"to").

> Generell bin ich darauf nicht angewiesen. Meine Idee war nur, dass ich
> pro Sample bereits 21 Multiplikationen und Additionen durchführen muss.
> Deswegen dachte ich, versorge ich den FIR einfach mal mit dem höchsten,
> was ich zur Verfügung habe. Ich versuche mal, den Takt zu halbieren.
Machst du diese Multiplikationen und Additionen bereits sequentiell oder 
parallel?
Falls sequentiell: Eventuell noch feiner Auflösen, also mehr 
Zwischenstufen berechnen.
Falls parallel: Entweder sequenzialisieren oder Takt senken.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Samuel C. schrieb:
> Machst du diese Multiplikationen und Additionen bereits sequentiell oder
> parallel?

Die mache ich bereits sequentiell, in Abhängigkeit von einem Counter.
if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation 
   filter_rdy_intern <= '1';  
   ready_for_operations <=  '0';          
else
   sop := sop + coeff(counter_filter) * signed(x(counter_filter)); 
   counter_filter := counter_filter + 1;
end if;
So sieht das Stück Code dazu aus. Parallel wäre ja mit Hilfe einer for 
loop, oder?

Markus F. schrieb:
> - kombinatorische Logik in zwei (oder mehr) Taktzyklen aufteilen
> und/oder pipelinen

Wie sähe das denn aus? Sagen wir, ich habe den oben geposteten VHDL 
Code. Würde es dann dementsprechend reichen, die Addition und 
Multiplikation alle zwei Takte auszuführen? (sofern dann die 
Timinganforderungen erfüllt werden)

Würde es dazu noch etwas bringen, Multiplikation und Addition getrennt 
zu machen? Oder ist die Addition gegenüber der Multiplikation sowieso so 
schnell, dass das praktisch nichts ausmacht? Das Aufteilen eines großen 
Prozesses in nacheinanderfolgende kleinere Prozesse ist doch die 
Bedeutung von Pipelining, oder?

: Bearbeitet durch User
Autor: FPGA zum Spass (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Problem ist der zu lange Signalpfad, die Synthese schafft es nicht 
der zur Verfügung stehenden Zeit(8ns) die Daten von den Quellregistern 
durch die Logik und die DSP Slices bis zu den Zielregistern zu bringen.

DSP Slices? Ja genau! Das ist wohl auch das eigentlich Problem hier, 
denn 125Mhz mit 6 Logicleveln sind normalerweise leicht für ein Artix 7 
das so leer ist. (nur 990 Endpoints)

Dein Endpoint liegt in eben diesen DPS Slices, warscheinlich am Ende 
einer Multiplikation.(to: ....dsp/D)

Diese DSP Slices haben Register direkt intern vor und hinter der 
Berechnung.
Die bekommst du ganz einfach genutzt, indem du eine zusätzliche 
Registerstufe vor die Multiplikation baust.
Den Rest macht die Synthese selbst.

Damit läuft der Multiplizierer locker auf 250 Mhz und die verbleibende 
Logik davor mit nur 5 Levels locker auf 200 Mhz.

Autor: FPGA zum Spass (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Würde es dazu noch etwas bringen, Multiplikation und Addition getrennt
> zu machen?

Genau das meinte ich mit dem Post gerade eben :)

Damit geht es garantiert.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation
>    filter_rdy_intern <= '1';
>    ready_for_operations <=  '0';
> else
>    sop := sop + coeff(counter_filter) * signed(x(counter_filter));
>    counter_filter := counter_filter + 1;
> end if;

zeig' mal das Drumrum.

Da ist offensichtlich eine Variable im Spiel und das sieht mir nach 
multiply-accumulate aus?

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> zeig' mal das Drumrum.
>
> Da ist offensichtlich eine Variable im Spiel und das sieht mir nach
> multiply-accumulate aus?

Ich schreibe hier mal das gesamte Modul hinein:
--
--Implementierung der Direktform eines FIR - Tiefpasses
--Kennwerte: Abtastrate: 50 kHz
--f_Durchlass = 0,8kHz
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
--passband Ripple = 0,1db

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;


entity FIR_Test is
    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
        
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end FIR_Test;

architecture FIR_Test_arch of FIR_Test is
    
    type    tap_line is array(0 to N) of std_logic_vector(12 downto 0);    --Typenerklärung: Array zum Verschieben 
                                                                                -- =(Zeitverzögern) des Inputs
                                                                                    
    type    table is array(0 to N) of signed(12 downto 0);  --Typenerklärung: Array aus Filterkoeffizienten,                                         
                                                                                                                    
    --States
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        mathOperations,
                        shiftToDA     
                    );
    
    signal x    :   tap_line  := (others=>(others=>'0'));

    constant coeff  : table:= (
                                "1" & X"fcd",
                                "1" & X"ffd",
                                "0" & X"015",
                                "0" & X"03f",
                                "0" & X"07b",
                                "0" & X"0c4",
                                "0" & X"114",
                                "0" & X"161",
                                "0" & X"1a2",
                                "0" & X"1cc",
                                "0" & X"1db",
                                "0" & X"1cc",
                                "0" & X"1a2",
                                "0" & X"161",
                                "0" & X"114",
                                "0" & X"0c4",
                                "0" & X"07b",
                                "0" & X"03f",
                                "0" & X"015",
                                "1" & X"ffd",
                                "1" & X"fcd"
                               );  --Koeffiziententabelle, von a_20 bis a_0
--                                 --Darstellung: signed 1.12 Bit Zahl
                                   
   signal current_state :   states := startup;   --Enthält den aktuellen Status, initialisiert mit "startup"
   signal filter_rdy_intern :   std_logic := '0';   --Internes Signal für die fertige Filteroperation
   signal ready_for_operations   :   std_logic := '0';   --Daten einlesen fertig
   
   --Debugging
--   signal test_counter      :   integer range 0 to (N + 1);
--   signal test_sop          :   std_logic_vector (25 downto 0) := (others => '0');
 
begin
  
    --Schreiben der Statemachine
    write_statemachine  :   process(clk, rst)
    begin
        --Reset aktiv low
        if (rst = '0') then
            current_state <= startup;
        --Signaländerung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            
            --Bei Error: Zurück zu Startup
            if (error_from_ADC = '1') then
                current_state <= startup;
            --Sample einlesen
            elsif (enable_data = '1' and ready_for_operations = '0' and filter_rdy_intern = '0') then
                current_state <= readAndShift;
            --Aktuelles Sample verrechnen
            elsif (ready_for_operations = '1' and filter_rdy_intern = '0') then
                current_state <= mathOperations;
            --Daten an DAC übermitteln
            elsif (filter_rdy_intern = '1' and data_acknowledged = '0') then
                current_state   <=  shiftToDA;
            else
                current_state <= waitForADC;
            end if;
        end if;
    end process write_statemachine;  
    
    --Durchführen der Operationen abhängig vom State
    statemachine    :   process(clk)
    variable result    :   signed(25 downto 0);    --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
    variable temp_result    :   signed(25 downto 0);       
    variable counter_filter :   integer range 0 to (N + 1) := 0;

    begin
        if (rising_edge(clk)) then
            case (current_state) is
            
                --Alles zurücksetzen
                when startup   =>
                    filter_rdy_intern <= '0';
                    ready_for_operations   <=  '0';
                    result := (others => '0');
                    counter_filter := 0;
                --Auf nächstes Sample warten
                when waitForADC =>
                    filter_rdy_intern <= '0';
                    ready_for_operations   <=  '0';
                    counter_filter := 0;        --Zurücksetzen des Zählers
                    result := (others => '0');     --Zurücksetzen des Ergebnisspeichers
                    
                --Neues sample einlesen. Array wird von vorne
                --nach hinten aufgefüllt
                when readAndShift =>
                    
                    if(ready_for_operations = '0') then --nur 1x Shiften
                        x(0) <= "0" & x_in;
                        for i in N downto 1 loop
                            x(i)    <=  x(i-1);    
                        end loop;
                    end if;
                    ready_for_operations <= '1';  
                      
                --Multiplikation und Addition des neuesten Samples
                --Und Aufaddieren zum Gesamtergebnis
                when mathOperations =>
--                    test_counter    <=  counter_filter;
                    if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation 
                        filter_rdy_intern <= '1';  
                        ready_for_operations <=  '0';          
                    else
                        result := result + coeff(counter_filter) * signed(x(counter_filter)); --Durchführung einer Multiplikation und einer Addition
--                        test_sop    <=  std_logic_vector (result);
                        counter_filter := counter_filter + 1;
                    end if;
                    
                --Daten an DA übermitteln
                when shiftToDA =>
                    y <= std_logic_vector(result(23 downto 12));   --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
            end case;
        else
            NULL;
        end if;
    end process statemachine;      

end FIR_Test_arch;

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... und wie kommt die arme Statemachine aus dem "waitForADC"-Status 
wieder raus, wenn sie dort mal angelangt ist?

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> ... und wie kommt die arme Statemachine aus dem "waitForADC"-Status
> wieder raus, wenn sie dort mal angelangt ist?

Sorry, der Gesamtzusammenhang besteht zwischen einem AD Wandler Modul, 
dem FIR und einem DA Wandler Modul. Der AD Wandler sendet bei jedem 
Sample ein Signal, sodass der "waitForADC" Status verlassen wird. Es ist 
das Signal "enable_data"

: Bearbeitet durch User
Autor: -gb- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast einen AD und DA in VHDL geschrieben?!
Und zu dem FIR:
Wenn du den parallel aufbaust, dann brauchst du genausoviele DSP 
Slices/Multiplizierer wie Koeffizienten aber nur einen Takt und das 
durchzurechnen.
Wenn du den als Pipeline baust brauchst du nur einen DSP 
Alice/Multiplizierer aber dafür brauchst du so viele Takte wie es 
Koeffizienten sind.
Auf einem Artix sollten beide Möglichkeiten mit 125 MHz laufen.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Der AD Wandler sendet bei jedem
> Sample ein Signal, sodass der "waitForADC" Status verlassen wird. Es ist
> das Signal "enable_data"


Achso, sorry, das ist ja eine if-Kaskade (nicht registriert, weil man 
das eher nicht so macht, wenn man's nicht braucht).

Das wird nicht der Grund für die Timing-Fehler sein, aber das Konstrukt 
braucht so etwa fünf mal so viele Ressourcen wie ein entsprechendes 
case-Statement.

Wenn man keine Prioritäten braucht (brauchst Du ja hier nicht, oder?) 
ist das entsprechende case-Statement die bessere Wahl.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
-gb- schrieb:
> Du hast einen AD und DA in VHDL geschrieben?!

Ne, nur die Anschlussschnittstellen für die Datenübertragung per I2C / 
SPI :D

Markus F. schrieb:
> Das wird nicht der Grund für die Timing-Fehler sein, aber das Konstrukt
> braucht so etwa fünf mal so viele Ressourcen wie ein entsprechendes
> case-Statement.

Ich baue meinen Code gerade komplett um, ich habe dann nur noch case 
statements... Multiplikation und Addition trenne ich vorerst trotzdem

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Als neuen Code habe ich jetzt diesen. Die Simulation sieht gut aus, 
allerdings habe ich es in Verbindung mit den beiden Wandlern noch nicht 
zum Laufen bekommen.
Die Fehlermeldung ist jetzt weg, allerdings sendet der AD Wandler 
plötzlich kein Signal mehr für ein neues Sample, obwohl ich an der Logik 
zwischen den Modulen gar nichts verändert habe..... Ich werde dann mal 
weiter debuggen. Die Hilfestellung war echt super. Allerdings habe ich 
noch zwei weitere Nachfragen.
1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: DSP 
FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is 
not pipelined. Pipelining DSP48 input will improve performance."
Allerdings dachte ich, ich hätte jetzt gepipelined? Oder ist damit 
gemeint, dass man bestenfalls die Multiplikation auch noch aufteilt, so 
wie in Samuel C. schrieb:
> Falls sequentiell: Eventuell noch feiner Auflösen, also mehr
> Zwischenstufen berechnen.
beschrieben?

2)Eine weiter Warnung meldet mir: "[DRC ZPS7-1] PS7 block required: The 
PS7 cell must be used in this Zynq design in order to enable correct 
default configuration."



--
--Implementierung der Direktform eines FIR - Tiefpasses
--Kennwerte: Abtastrate: 50 kHz
--f_Durchlass = 0,8kHz
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
--passband Ripple = 0,1db

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;


entity FIR_Test is
    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
        
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end FIR_Test;

architecture FIR_Test_arch of FIR_Test is
    
    type    tap_line is array(0 to N) of std_logic_vector(12 downto 0);    --Typenerklärung: Array zum Verschieben 
                                                                                -- =(Zeitverzögern) des Inputs
                                                                                    
    type    table is array(0 to N) of signed(12 downto 0);  --Typenerklärung: Array aus Filterkoeffizienten,                                         
                                                                                                                    
    --States
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        multiply,
                        add,
                        shiftToDA     
                    );
    
    signal x    :   tap_line  := (others=>(others=>'0'));

    constant coeff  : table:= (
                                "1" & X"fcd",
                                "1" & X"ffd",
                                "0" & X"015",
                                "0" & X"03f",
                                "0" & X"07b",
                                "0" & X"0c4",
                                "0" & X"114",
                                "0" & X"161",
                                "0" & X"1a2",
                                "0" & X"1cc",
                                "0" & X"1db",
                                "0" & X"1cc",
                                "0" & X"1a2",
                                "0" & X"161",
                                "0" & X"114",
                                "0" & X"0c4",
                                "0" & X"07b",
                                "0" & X"03f",
                                "0" & X"015",
                                "1" & X"ffd",
                                "1" & X"fcd"
                               );  --Koeffiziententabelle, von a_20 bis a_0
--                                 --Darstellung: signed 1.12 Bit Zahl
                                   
   signal current_state :   states := startup;   --Enthält den aktuellen Status, initialisiert mit "startup"
   
   
   --Debugging
   signal test_counter :   integer range 0 to (N + 1) := 0;
   signal test_result   :   signed(25 downto 0) := (others => '0');
   signal test_temp_result   :   signed(25 downto 0) := (others => '0');
 
begin
  
    --Durchführen der Operationen abhängig vom State
    statemachine    :   process(clk, rst)
    variable result    :   signed(25 downto 0);    --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
    variable temp_result    :   signed(25 downto 0);       
    variable counter_filter :   integer range 0 to (N + 1) := 0;

    begin
        
        if (rst = '0') then
            current_state <= startup;
        --Signaländerung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is
            
                --Alles zurücksetzen
                when startup   =>
                    result := (others => '0');
                    temp_result := (others => '0');
                    counter_filter := 0;
                    
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;
                    
                --Auf nächstes Sample warten
                when waitForADC =>
                    counter_filter := 0;        --Zurücksetzen des Zählers
                    result := (others => '0');     --Zurücksetzen des Ergebnisspeichers
                    temp_result := (others => '0'); --Zurücksetzen des Zwischenspeichers
                    
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                    
                --Neues sample einlesen. Array wird von vorne
                --nach hinten aufgefüllt
                when readAndShift =>
                    x(0) <= "0" & x_in;
                    for i in N downto 1 loop
                        x(i)    <=  x(i-1);    
                    end loop;
                    
                    current_state <= multiply;
                      
                --Multiplikation mit Koeffizienten
                when multiply =>
--                    test_counter    <=  counter_filter;
--                    if (counter_filter = N) then   --Abbruchbedingung für Rechenoperationen  
--                        current_state <= shiftToDA;           
--                    else
                    temp_result :=  coeff(counter_filter) * signed(x(counter_filter));
--                    test_temp_result    <=  temp_result;
                    current_state <= add;
--                    end if;
                    
                --Aufaddieren der Zwischenergebnisse    
                when add =>
                    result := result + temp_result;
--                    test_result  <=  result;
                    
                    if (counter_filter = N) then
                        current_state <= shiftToDA;
                    else
                        current_state <= multiply;
                        counter_filter := counter_filter + 1;
                    end if;
                --Daten an DA übermitteln
                when shiftToDA =>
                    y <= std_logic_vector(result(23 downto 12));   --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      

end FIR_Test_arch;

: Bearbeitet durch User
Autor: Samuel C. (neoexacun)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du 
benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der 
gerade stattfindet. Sie wird nirgends gespeichert.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da solltest Du nochmal drüber nachdenken.

Autor: Tobias B. (Firma: www.elpra.de) (ttobsen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Samuel C. schrieb:
> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
> gerade stattfindet. Sie wird nirgends gespeichert.

In dem geposteten Code machen die Variablen keine Probleme. Die koennten 
problemlos auch als Signale deklariert werden und sollten entsprechend 
vom Synthesetool gleich behandelt werden.

Von daher ist das schon in Ordnung (sowohl im ersten als auch im zweiten 
Code).

Ich schaetze mal das hier
result := result + coeff(counter_filter) * signed(x(counter_filter));

war der kritische Teil und der wurde in der zweiten Code Variante in 2 
Schritte aufgeteilt.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tobias B. schrieb:
> Ich schaetze mal das hier
> result := result + coeff(counter_filter) * signed(x(counter_filter));
>
> war der kritische Teil und der wurde in der zweiten Code Variante in 2
> Schritte aufgeteilt.

Bezüglich des Timings scheint es das gewesen zu sein. Aber wie gesagt 
scheint noch immer irgendwas nicht zu stimmen, da die Module irgendwie 
untereinander nicht mehr funktionieren oder irgendwie falsch 
implementiert werden. Mich stört diese Warnung mit den fehlenden 
Constraints. Die habe ich definitiv drinn und hab mich auch zu Lösungen 
in anderen Foren umgesehen. Viele meinten hier allerdings, dass sie die 
Warnung einfach haben stehen lassen und es funktionierte trotzdem.

Markus F. schrieb:
> Da solltest Du nochmal drüber nachdenken.

Worüber genau?

Tobias B. schrieb:
> Samuel C. schrieb:
>> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
>> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
>> gerade stattfindet. Sie wird nirgends gespeichert.
>
> In dem geposteten Code machen die Variablen keine Probleme. Die koennten
> problemlos auch als Signale deklariert werden und sollten entsprechend
> vom Synthesetool gleich behandelt werden.

Kann ich ja dennoch zu Signalen umschreiben.

Aber das alles erst morgen, auch wenn die Zeit langsam drängt ; )

Autor: Tobias B. (Firma: www.elpra.de) (ttobsen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Mich stört diese Warnung mit den fehlenden
> Constraints. Die habe ich definitiv drinn und hab mich auch zu Lösungen
> in anderen Foren umgesehen. Viele meinten hier allerdings, dass sie die
> Warnung einfach haben stehen lassen und es funktionierte trotzdem.

Kannst du den Timing Report als TXT Datei einfach mal hochladen? Mit dem 
Report kommen wir deutlich weiter als mit den Screenshots.

Autor: Samuel C. (neoexacun)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Samuel C. schrieb:
> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
> gerade stattfindet. Sie wird nirgends gespeichert.

Markus F. schrieb:
> Da solltest Du nochmal drüber nachdenken.

Nvm. Selbst nen Knoten im Hirn. Sollte auch so gehen. Würde ich 
persönlich aber nicht so schreiben.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Markus F. schrieb:
>> Da solltest Du nochmal drüber nachdenken.

Sorry, damit warst nicht Du gemeint, sondern Samuel C.

Alex K. schrieb:
> Mich stört diese Warnung mit den fehlenden
> Constraints.

Welche? Meinst Du das hier:

Alex K. schrieb:
> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: DSP
> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
> not pipelined. Pipelining DSP48 input will improve performance."

?

Das heisst m.E. lediglich, daß das DSP slice nicht mit der optimalen 
Performance betrieben wird. Das kann offensichtlich während der Addition 
bereits die nächste Multiplikation parallel berechnen. Das wirst Du 
m.M.n. nur hinbekommen, wenn Du den DSP48E1 explizit instanziierst (also 
die "Formel" nicht direkt hinschreibst).

Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Tobias B. schrieb:
> Kannst du den Timing Report als TXT Datei einfach mal hochladen? Mit dem
> Report kommen wir deutlich weiter als mit den Screenshots.

Der Report des aktuellen Programms hängt hier an.

Markus F. schrieb:
> Welche? Meinst Du das hier:

Bei mir taucht diese Meldung auf: "[Constraints 18-5210] No constraints 
selected for write.
Resolution: This message can indicate that there are no constraints for 
the design, or it can indicate that the used_in flags are set such that 
the constraints are ignored. This later case is used when running 
synth_design to not write synthesis constraints to the resulting 
checkpoint. Instead, project constraints are read when the synthesized 
design is opened.
"

Autor: Sigi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Wie muss ich jetzt weiter vorgehen? Ich kann auch gerne meinen Quellcode
> bereitstellen, falls das weiterhilft.

Zuerst mal eine einfache Lösung: Du fügst ein Clock-Enable
zum Design hinzu. Wegen 1MHz-AD/DA und den 125MHz für's
Design hast du ja ca 125 Schritte je Sampling-Periode.
Mach daraus 62 (müüste vom Timing hers chon locker ausreichen)
oder 31 (dann gibt's garantiert keine TIming-Probleme mehr)
per Clock-Enable und füge ein MultiCycle-Constraint zu deinen
Constraints hinzu. (Voraussetzung ist natürlich, dass deine
FIR-Stufe mehr oder weniger gepipelined ist)

Zweite, aufwendigere Lösung: Nimm dein Design komplett
auseinander und und trenne es in die einzelnen Aufgaben,
so lässt sich das Ganze auch einfacher verstehen und
verifizieren. Z.B. würde ich den Datenpuffer als
zyklisches Shift-Register implementieren (ein neues
Datum muss eingefügt werden können und bei jedem
FIR-Schritt wird einmal zyklisch geshiftet). Ebenso
würde ich den Koeffizientenvektor zyklisch shiftend
anlegen. Beide Shift-Register liefern dann für den
DSP den (registrierten!) Input. So kann das DSP
ebenfalls getrennt implementiert werden (Pipelining
sollte locker umsetzbar sein).
Die Steuerung übernimmt dann ein Zustandsautomat,
der als Input vom AD-Wandler das Startsignal und vom
Shift-Register das Finish-Signal bekommt und die
Steuersignale an die Shift-Register und den DA
liefert. Dabei sollten die Output-Signale des
Automaten registriert sein, was wichtig für ein
schnelles Timing ist.
Jede dieser Teilkomponente läuft auf einem Z20xx
locker mit zig-100MHz, auch ohne Clock-Enable.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sigi schrieb:
> Zweite, aufwendigere Lösung: Nimm dein Design komplett
> auseinander und und trenne es in die einzelnen Aufgaben,
> so lässt sich das Ganze auch einfacher verstehen und
> verifizieren. Z.B. würde ich den Datenpuffer als
> zyklisches Shift-Register implementieren (ein neues
> Datum muss eingefügt werden können und bei jedem
> FIR-Schritt wird einmal zyklisch geshiftet)

Das habe ich in meinem aktuellen Entwurf ja jetzt gemacht.

Sigi schrieb:
> Ebenso
> würde ich den Koeffizientenvektor zyklisch shiftend
> anlegen

Wieso soll der geshiftet werden? Die Koeffizienten bleiben doch quasi 
starr in ihrer Position.

Sigi schrieb:
> Beide Shift-Register liefern dann für den
> DSP den (registrierten!) Input. So kann das DSP
> ebenfalls getrennt implementiert werden (Pipelining
> sollte locker umsetzbar sein).

Ich denke, das habe ich nicht verstanden. Du beziehst dich hier auf das 
Teilproblem, das in der Warnung genannt wird?

">> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: 
DSP
>> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
>> not pipelined. Pipelining DSP48 input will improve performance.""

Sigi schrieb:
> Die Steuerung übernimmt dann ein Zustandsautomat,
> der als Input vom AD-Wandler das Startsignal und vom
> Shift-Register das Finish-Signal bekommt und die
> Steuersignale an die Shift-Register und den DA
> liefert. Dabei sollten die Output-Signale des
> Automaten registriert sein, was wichtig für ein
> schnelles Timing ist.

Den Zustandsautomaten habe ich ja jetzt durch die when..case Logik 
implementiert. Das Startsignal stammt auch vom AD - Wandler, und dass 
die Daten am DA - Wandler angekommen sind, wird auch von dem DA Wandler 
Modul bestätigt.

In meinem Gesamtdesign habe ich jetzt allerdings das Problem, dass 
scheinbar plötzlich gar nichts mehr funktioniert. Die Starbedingung ganz 
am Anfang ist dadurch gegen, dass der DA Wandler in den Status geht, 
dass er auf ein Ergebnis zur Wandlung wartet. In diesem Status aktiviert 
der DA Wandler den AD Wandler, außer dieser gibt einen Error aus. Das 
Errorsignal ist allerdings 0, trotzdem gibt der DA Wandler das Signal 
zum aktivieren nicht aus.

Woran kann es liegen, dass wenn ich doch nur etwas an dem FIR Modul 
geändert habe, dass bei Implementierung nichts mehr läuft? Habe ich 
immernoch Timing Probleme, obowhl mir diese nicht angezeigt werden?

Und bezüglich dieser Warnung:
""[DRC ZPS7-1] PS7 block required: The
PS7 cell must be used in this Zynq design in order to enable correct
default configuration.""
Der PS7 Block scheint sich auf den auf dem Board befindlichen ARM 
Prozessor zu beziehen, wieso sollte ich den jetzt plötzlich für mein 
Design brauchen?

: Bearbeitet durch User
Autor: Sigi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Das habe ich in meinem aktuellen Entwurf ja jetzt gemacht.
Nur zum Teil, es wird bei dir nur für den AD-Teil geshiftet.

> Wieso soll der geshiftet werden? Die Koeffizienten bleiben doch quasi
> starr in ihrer Position.
Das und meine letzte Bemerkung haben mit deiner DSP-Formel
> result := result + coeff(counter_filter) * signed(x(counter_filter));
zu tun: Die Indizierung impliziert eine Riesen-MUX, die
durch Shiftregister vermieden werden kann, und das sogar
zweimal. Ausserdem wird durch ein Shift-Register z.B. auf
das erste Werte-Register zugegriffen (registrierter Zugriff),
bei deiner MUX-Lösung wird ein Logikbaum generiert, und
der ist bei dir nunmal nicht registriert (könnte man jetzt
einen Schritt im Voraus machen, ist aber ein anderes Thema).

Alex K. schrieb:
> Ich denke, das habe ich nicht verstanden. Du beziehst dich hier auf das
> Teilproblem, das in der Warnung genannt wird?
>
> ">> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining:
> DSP
>>> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
>>> not pipelined. Pipelining DSP48 input will improve performance.""

Nur zu einem kleinen Teil, eben die Input-Stage deines DSP48.
Wenn dort Register verwendet werden (statt einer MUX), dann ist
schonmal ein kleiner Happen deines Timing-Problems weg.
Für die Output-Stage können auch Register aktiviert werden
(=> max Performance), das kann allerdings nur durch
Instanziierung einer konkreten DSP48-Komponente mit entsprechenden
Setting gemacht werden.

Alex K. schrieb:
> Den Zustandsautomaten habe ich ja jetzt durch die when..case Logik
> implementiert. Das Startsignal stammt auch vom AD - Wandler, und dass
> die Daten am DA - Wandler angekommen sind, wird auch von dem DA Wandler
> Modul bestätigt.

Schon, aber du hast dort alles zusammen, und das führt dann
z.B. zu folgendeh Problem

Alex K. schrieb:
> Woran kann es liegen, dass wenn ich doch nur etwas an dem FIR Modul
> geändert habe, dass bei Implementierung nichts mehr läuft? Habe ich
> immernoch Timing Probleme, obowhl mir diese nicht angezeigt werden?

Wenn du alles wie von mir in Lösung 2 beschrieben in
Komponenten packst und jeweils per Testbench sicherstellst,
dass die jeweilingen Timings/Protokolle eingehalten werden,
dann kannst du ohne Probleme eine Änderung vornehmen, sollange
du natürlich die Timings beibehälst d.h. der Testbench sein
OK gibt.

Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Okay, mein letzter Fehler war selten dämlich, habe einen Zustand beim DA 
Wandler nicht auskommentiert, den ich auskommentieren wollte.....
Der Filter tut jetzt tatsächlich, was er soll! Vielen Danke für die 
gesamte Hilfestellung. Den von mir zuletzt geposteten Code habe ich 
nicht mehr geändert, obwohl sicher noch viel damit optimiert werden 
kann. Ich hänge mal eine Grafik an, die die Funktionsweise bestätigt. 
Der Input ist der analoge Input vor dem AD Wandler, der Output ist das 
analoge Ausgangssignal vom DA Wandler.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.