Forum: FPGA, VHDL & Co. Wie unterschiedliche Clock-Domains verwenden?


von Daniel K. (daniel_k80)


Lesenswert?

Hallo zusammen,

ich habe einen I2S-Transmitter, den ich mit einem AXI-Stream Interface 
ausstatten möchte. Der Transmitter alleine lässt sich ohne Probleme 
synthetisieren und funktioniert im FPGA wunderbar. Mit dem AXI-Stream 
Interface kombiniert ergeben sich aber Timing Violations beim Worst 
Negative Slack und beim Total Negativ Slack. Jetzt habe ich gelesen, 
dass es u. U. daran liegen könnte, dass ich die Signale nicht ordentlich 
einsynchronisiert habe. Daher habe ich mir mal die Erklärung vom Lothar 
Miller angeschaut

http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren

und versucht die Synchronisation für das Reset-Signal (für den Anfang) 
meines Transmitters umzusetzen:
1
entity AXIS_I2S is
2
    Generic (   WIDTH       : INTEGER := 16
3
                );
4
    Port (  aclk        : in STD_LOGIC;
5
            aresetn     : in STD_LOGIC;
6
7
            -- AXI-Stream interface
8
            TDATA_RXD   : in STD_LOGIC_VECTOR(31 downto 0);
9
            TREADY_RXD  : out STD_LOGIC;
10
            TVALID_RXD  : in STD_LOGIC;
11
12
            -- I2S interface
13
            MCLK        : in STD_LOGIC;
14
            LRCLK       : out STD_LOGIC;
15
            SCLK        : out STD_LOGIC;
16
            SD          : out STD_LOGIC        
17
            );
18
end AXIS_I2S;
19
20
architecture AXIS_I2S_Arch of AXIS_I2S is
21
22
    type AXIS_State_t is (State_Reset, State_WaitForTransmitterReady, WaitForValid, WaitForBusy);
23
24
    signal CurrentState : AXIS_State_t                                      := State_Reset;
25
26
    signal FIFO             : STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0)  := (others => '0');
27
28
    signal TransmitterValid : STD_LOGIC;
29
    signal TransmitterReady : STD_LOGIC;
30
    signal TransmitterResetN: STD_LOGIC_VECTOR(1 downto 0);
31
32
    component I2S_Transmitter is
33
        Generic (   WIDTH   : INTEGER := 16
34
                    );
35
        Port (  MCLK    : in STD_LOGIC;
36
                ResetN  : in STD_LOGIC;
37
                Ready   : out STD_LOGIC;
38
                Valid   : in STD_LOGIC;
39
                Data    : in STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0);
40
                LRCLK   : out STD_LOGIC;
41
                SCLK    : out STD_LOGIC;
42
                SD      : out STD_LOGIC
43
                );
44
    end component;
45
46
begin
47
48
    Transmitter : I2S_Transmitter generic map(  WIDTH => WIDTH
49
                                                )
50
                                  port map(     MCLK => MCLK,
51
                                                ResetN => TransmitterResetN(1),
52
                                                Valid => TransmitterValid,
53
                                                Ready => TransmitterReady,
54
                                                Data => x"FF00FF00",
55
                                                LRCLK => LRCLK,
56
                                                SCLK => SCLK,
57
                                                SD => SD
58
                                                );
59
60
    -- Synchronize reset
61
    process(MCLK)
62
    begin
63
        if(rising_edge(MCLK)) then
64
            TransmitterResetN(0) <= aresetn;
65
            TransmitterResetN(1) <= TransmitterResetN(0);
66
        end if;
67
    end process;
68
69
    process(aclk)
70
    begin
71
        if(rising_edge(aclk)) then
72
            if(aresetn = '0') then
73
                CurrentState <= State_Reset;
74
            else
75
                case CurrentState is
76
                    when State_Reset =>
77
                        FIFO <= (others => '0');
78
                        --TransmitterValid <= '0';
79
80
                        CurrentState <= State_WaitForTransmitterReady;
81
82
                    when State_WaitForTransmitterReady =>
83
                        --TransmitterValid <= '0';
84
85
                        --if(TransmitterReady = '1') then
86
                            CurrentState <= WaitForValid;
87
                        --else
88
                        --    CurrentState <= State_WaitForTransmitterReady;
89
                        --end if;
90
91
                    when WaitForValid =>
92
                        TREADY_RXD <= '1';
93
                        
94
                        if(TVALID_RXD = '1') then
95
                            FIFO <= TDATA_RXD;
96
                            CurrentState <= WaitForBusy;
97
                        else
98
                            CurrentState <= WaitForValid;
99
                        end if;
100
101
                    when WaitForBusy =>
102
                        --TransmitterValid <= '1';
103
                        TREADY_RXD <= '0';
104
105
                        --if(TransmitterReady = '0') then
106
                            CurrentState <= State_WaitForTransmitterReady;
107
                        --else
108
                        --    CurrentState <= WaitForBusy;
109
                        --end if;
110
111
                end case;
112
            end if;
113
        end if;
114
    end process;
115
end AXIS_I2S_Arch;

Das aktuelle Design ergibt einen WNS and TNS von -5,7 ns, sprich es wird 
auch nicht funktionieren. Wo habe ich da den Denkfehler drin, bzw. muss 
ich es ganz anders machen?

PS: Ja, ich weiß das man verschiedene Clock-Domainen meiden sollte, bzw. 
es nicht ganz trivial ist (siehe mein Beispiel). Das Problem hier ist 
allerdings, dass der AXI Bus des Systems leider auf einer komplett 
anderen Frequenz läuft und das sich das mit dem I2S beißt (100 MHz AXI, 
12,288 MHz I2S). Daher bleibt mir leider nichts anderes übrig.

Danke für ein paar Denkanstöße :)

von Markus F. (mfro)


Lesenswert?

AXI reset ist doch normalerweise asynchron?

Beitrag "Re: Altera Reset Synchronizer richtig machen"

P.S.: und wenn etwas asynchron ist, braucht der Timing Analyzer ein 
set_false_path statement

: Bearbeitet durch User
Beitrag #6180951 wurde vom Autor gelöscht.
von Christoph Z. (christophz)


Lesenswert?

Ich arbeite nicht mit Altera, darum nehme ich mal an, dass es für den 
Reset so ist, wie Markus F. schreibt.

Für die I2S Komponente musst du das "Valid" Signal einsynchronisieren. 
Dabei musst du aufpassen, dass es in der AXI Domäne genug lange aktiv 
bleibt, bis es deine I2S Domäne sicher gelesen hat (die ist ja ca. 8 mal 
langsamer). Also entweder einen Puls-stretcher einfügen oder auf 
Vier-Phasen-Handshake umbauen.

Das Synchronisierte "Valid" und das "Data" Signal brauchen dann beide 
noch eine False-path Constraint.

von Daniel K. (daniel_k80)


Lesenswert?

Christoph Z. schrieb:
> Ich arbeite nicht mit Altera, darum nehme ich mal an, dass es für den
> Reset so ist, wie Markus F. schreibt.
>
> Für die I2S Komponente musst du das "Valid" Signal einsynchronisieren.
> Dabei musst du aufpassen, dass es in der AXI Domäne genug lange aktiv
> bleibt, bis es deine I2S Domäne sicher gelesen hat (die ist ja ca. 8 mal
> langsamer). Also entweder einen Puls-stretcher einfügen oder auf
> Vier-Phasen-Handshake umbauen.
>
> Das Synchronisierte "Valid" und das "Data" Signal brauchen dann beide
> noch eine False-path Constraint.

Das Valid-Signal habe ich erst einmal ausgeklammert. Mit den I2S 
Signalen hatte ich einen TNS von -1000 ns und daher wollte ich erst 
einmal schauen welche Signale da mit rein spielen. Aktuell habe ich nur 
den Reset in beiden Clock-Domainen aktiv und da bekomme ich es schon 
nicht hin den TNS auf 0 zu reduzieren...
Hab es auch schon mit dem ASYNC_REG Attribut probiert
1
    attribute ASYNC_REG : string;
2
    attribute ASYNC_REG of TransmitterResetN        : signal is "TRUE";

Aber auch das hat leider nicht geholfen (mit Vivado von Xilinx).

Edit: Hab nun was (anscheinend) passendes gefunden:
1
set_false_path -from [get_clocks clk_fpga_0] -to [get_clocks -of_objects [get_pins System_i/ClockingWizard/inst/mmcm_adv_inst/CLKOUT0]]

Weiß noch nicht ob es das richtige ist, aber der Fehler ist weg...

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Daniel K. schrieb:
> Weiß noch nicht ob es das richtige ist, aber der Fehler ist weg...

Genau. Jetzt prüft es der Timing Analyzer einfach nicht mehr. Du musst 
dann allerdings selbst dafür sorgen, dass das Signal entweder an einen 
asynchronen Eingang geht oder passend eingetaktet wird.

von Christoph Z. (christophz)


Lesenswert?

Daniel K. schrieb:
> Weiß noch nicht ob es das richtige ist, aber der Fehler ist weg...

Nochmals etwas klarer Übersetzt: Die Fehlermeldung ist jetzt weg, dein 
Design behandelt den Übergang zwischen den Taktdomänen aber noch nicht 
richtig, wird also in Realität hin und wieder komische Sachen machen 
(Verhext, haunted design...).

von Daniel K. (daniel_k80)


Lesenswert?

Ich glaube ich habe jetzt eine Antwort gefunden. Das Problem mit dem 
Reset habe ich durch einen zweiten Reset gelöst, der für das Interface 
da ist und mittels "Processor System Reset" auf den entsprechenden Takt 
synchronisiert wird. Für die Signale nehme ich dann einen entsprechenden 
xpm_cdc Block:

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2016_2/ug953-vivado-7series-libraries.pdf

Damit scheint es (zumindest) laut der Implementierung zu klappen. Ich 
bin gespannt...

von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Kurzes Update:
Die Synchronisation und die Implementierung scheint nun zu funktionieren 
und auch das AXI-Stream Interface funktioniert zu 95% so wie es soll. 
Aktuell habe ich noch ein paar µs keine vernünftigen Daten, aber das 
scheint noch ein Problem in der Software oder in der Hardware zu sein 
(siehe Screenshot). Das ist aber definitiv ein anderes Problem...

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.