0
\$\begingroup\$

I have an I2S transmitter with an AXI-Stream interface.

AXIS_I2S

entity AXIS_I2S is
    Generic (   WIDTH       : INTEGER := 16
                );
    Port (  aclk        : in STD_LOGIC;
            aresetn     : in STD_LOGIC;

            -- AXI-Stream interface
            TDATA_RXD   : in STD_LOGIC_VECTOR(31 downto 0);
            TREADY_RXD  : out STD_LOGIC;
            TVALID_RXD  : in STD_LOGIC;

            -- I2S interface
            MCLK        : in STD_LOGIC;
            LRCLK       : out STD_LOGIC;
            SCLK        : out STD_LOGIC;
            SD          : out STD_LOGIC        
            );
end AXIS_I2S;

architecture AXIS_I2S_Arch of AXIS_I2S is

    type AXIS_State_t is (State_Reset, State_WaitForTransmitterReady, WaitForValid, WaitForBusy);

    signal CurrentState : AXIS_State_t                                      := State_Reset;

    signal FIFO             : STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0)  := (others => '0');

    signal TransmitterValid : STD_LOGIC                                     := '0';
    signal TransmitterReady : STD_LOGIC;
    signal TransmitterResetN: STD_LOGIC_VECTOR(1 downto 0) := (others => '1');

    component I2S_Transmitter is
        Generic (   WIDTH   : INTEGER := 16
                    );
        Port (  MCLK    : in STD_LOGIC;
                ResetN  : in STD_LOGIC;
                Ready   : out STD_LOGIC;
                Valid   : in STD_LOGIC;
                Data    : in STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0);
                LRCLK   : out STD_LOGIC;
                SCLK    : out STD_LOGIC;
                SD      : out STD_LOGIC
                );
    end component;

begin

    Transmitter : I2S_Transmitter generic map(  WIDTH => WIDTH
                                                )
                                  port map(     MCLK => MCLK,
                                                ResetN => TransmitterResetN(1),
                                                Valid => TransmitterValid,
                                                Ready => TransmitterReady,
                                                Data => FIFO,
                                                LRCLK => LRCLK,
                                                SCLK => SCLK,
                                                SD => SD
                                                );

    process(MCLK)
    begin
        if(rising_edge(MCLK)) then
            TransmitterResetN(0) <= aresetn;
            TransmitterResetN(1) <= TransmitterResetN(0);
        end if;
   end process;

    process(aclk)
    begin
        if(rising_edge(aclk)) then
            if(aresetn = '0') then
                CurrentState <= State_Reset;
            else
                case CurrentState is
                    when State_Reset =>
                        FIFO <= (others => '0');
                        TransmitterValid <= '0';

                        CurrentState <= State_WaitForTransmitterReady;

                    when State_WaitForTransmitterReady =>
                        TransmitterValid <= '0';

                        if(TransmitterReady = '1') then
                            CurrentState <= WaitForValid;
                        else
                            CurrentState <= State_WaitForTransmitterReady;
                        end if;

                    when WaitForValid =>
                        TREADY_RXD <= '1';

                        if(TVALID_RXD = '1') then
                            FIFO <= TDATA_RXD;
                            CurrentState <= WaitForBusy;
                        else
                            CurrentState <= WaitForValid;
                        end if;

                    when WaitForBusy =>
                        TransmitterValid <= '1';
                        TREADY_RXD <= '0';

                        if(TransmitterReady = '0') then
                            CurrentState <= State_WaitForTransmitterReady;
                        else
                            CurrentState <= WaitForBusy;
                        end if;

                end case;
            end if;
        end if;
    end process;
end AXIS_I2S_Arch;

I2S_Transmitter

entity I2S_Transmitter is
    Generic (   WIDTH   : INTEGER := 16                                         -- Data width per channel
                );
    Port (  MCLK    : in STD_LOGIC;                                             -- Master clock
            ResetN  : in STD_LOGIC;                                             -- Reset (active low)

            -- Communication bus
            Ready   : out STD_LOGIC;                                            -- Slave handshake to signal that the transmitter is ready
            Valid   : in STD_LOGIC;                                             -- Master handshake to signal valid data
            Data    : in STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0);          -- Audio data for both channels (Left: Top half, Right: Bottom half)

            -- I2S interface
            LRCLK   : out STD_LOGIC;                                            -- L/R clock
            SCLK    : out STD_LOGIC;                                            -- I2S serial clock
            SD      : out STD_LOGIC                                             -- I2S serial data
            );
end I2S_Transmitter;

architecture I2S_Transmitter_Arch of I2S_Transmitter is

    type State_t is (Reset, Idle, Transmit);

    signal CurrentState     : State_t   := Reset;

    signal Data_Int         : STD_LOGIC_VECTOR(((2 * WIDTH) - 1) downto 0) := (others => '0');

    signal SCLK_Int         : STD_LOGIC := '0';
    signal Enable           : STD_LOGIC := '0';

begin

    SCLK_Gen : process(MCLK)
        variable Counter : INTEGER := 0;
    begin
        if(falling_edge(MCLK)) then
            if(ResetN = '0') then
            else
                if(Counter < ((WIDTH / 4) - 1)) then
                    Counter := Counter + 1;
                else
                    SCLK_Int <= not SCLK_Int;
                    Counter := 0;
                end if;
            end if;
        end if;
    end process;

    I2S : process(MCLK)
        variable BitCounter : INTEGER := 0;
    begin
        if(falling_edge(MCLK)) then
            if(ResetN = '0') then
                CurrentState <= Reset;
            else
                case CurrentState is
                    when Reset =>
                        BitCounter := 0;

                        Enable <= '0';
                        Ready <= '0';
                        SD <= '0';
                        Data_Int <= (others => '0');

                        CurrentState <= Idle;

                    when Idle =>
                        BitCounter := 0;

                        Enable <= '1';
                        LRCLK <= '0';

                        if(Valid = '1') then
                            Ready <= '0';
                            Data_Int <= Data;

                            CurrentState <= Transmit;
                        else
                            Ready <= '1';

                            CurrentState <= Idle;
                        end if;

                    when Transmit =>
                        BitCounter := BitCounter + 1;

                        Data_Int <= Data_Int(((2 * WIDTH) - 2) downto 0) & "0";
                        SD <= Data_Int((2 * WIDTH) - 1);

                        if(BitCounter < WIDTH) then
                            LRCLK <= '0';
                        else
                            LRCLK <= '1';
                        end if;

                        if(BitCounter < ((2 * WIDTH) - 1)) then
                            CurrentState <= Transmit;
                        else
                            Ready <= '1';
                            CurrentState <= Idle;
                        end if;

                    end case;
                end if;
            end if;
    end process;

    SCLK <= SCLK_Int and Enable;

end I2S_Transmitter_Arch;

I have tested I2S_Transmitter alone and it works very well in hardware. So I add the additional AXI-Stream interface and test it with a simulation.

enter image description here

The simulation also looks very good. But I got some timing issues from Vivado during the implementation and the design doesn´t run in hardware.

enter image description here

It seems that there are some timing issues with the reset path

enter image description here


Update

I add synchronization for the aresetn signal for the I2S transmitter. So the TNS is reduced to -434.284 ns and the WNS to -6.622 ns.

enter image description here

Does thhis look like the same type of error?

So how can I fix these issues?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Looks like setup/hold violations between two clocks.

Looking at your AXI interface it seems that aclk and reset are in one clock domain, MCLK is another, and it is unclear how (or even if) the two clocks are related.

One approach is to use a classic 2-FF synchroniser to synch RESET into the MCLK domain, and use that synchronised version in the I2S interface.

\$\endgroup\$
2
  • \$\begingroup\$ I update my question and add the sync. \$\endgroup\$
    – Kampi
    Mar 15, 2020 at 15:08
  • \$\begingroup\$ Repeat for whichever signals are the problem now. Analyse which they are; analyze what the problem is; fix it. Looks like a signal called CE this time. \$\endgroup\$
    – user16324
    Mar 15, 2020 at 15:11
1
\$\begingroup\$

You need to read up on how to do clock domain crossing properly. You have logic driven by two different clocks that's directly interconnected. Not good! It's unlikely to work correctly as written. You have to implement proper synchronization techniques when interfacing between two different clocks, and then properly constrain the crossings. Or use a pre-made asynchronous FIFO module.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.