Search code examples
vhdlspivivado

Multiple Driver Nets on output of IOBUF


I wrote the following module which shall act as a switch between a standard 4-wire SPI and the 3-wire SPI of an AD9637 ADC. Therefore I instantiated an IOBUF, count clock edges and switch the IOBUF from output to input after the 16th clock cycle, where the ADC starts to put out data on the shared line. However, I get e Multiple Driver Nets error, trying to implement the design in Vivado 2019.1 and I cannot wrap my head around why this happens, as in my description, the affected signal io_SDIO is only driven by the IO pin of the instantiated IOBUF. The full error is

[DRC MDRV-1] Multiple Driver Nets: Net zsys_i/SPI_3wire_adapter_AD_0/U0/o_MISO has multiple drivers: zsys_i/SPI_3wire_adapter_AD_0/U0/IOBUF_inst/IBUF/O, and zsys_i/SPI_3wire_adapter_AD_0/U0/o_MISO_reg/Q.

The full design is

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

entity SPI_3wire_adapter_AD9637 is
    port (
        o_count : out std_logic_vector(4 downto 0);
        o_enable : out std_logic;
    
        i_SCLK :    in STD_LOGIC;           --PS side clock on which the module operates syncronously
        i_MOSI :    in STD_LOGIC;           --MOSI PS side (input)
        o_MISO :    out STD_LOGIC;          --MISO PS side (output)
        i_CS :      in STD_LOGIC;           --CS PS side (input)
    
        o_SCLK :    out STD_LOGIC;          --SPI clock (output)
        o_CS :      out STD_LOGIC;          --SPI CS (output)
        io_SDIO :   inout STD_LOGIC         --SPI SDIO - shared MOSI/MISO for 3-wire SPI (output/input)
    );
end SPI_3wire_adapter_AD9637;

architecture behavioral of SPI_3wire_adapter_AD9637 is

    signal s_CLK_count: integer range 0 to 23 := 0;
    signal s_N_enable_output : STD_LOGIC := '0';

begin

IOBUF_inst : IOBUF
generic map (
   DRIVE => 12,
   IOSTANDARD => "DEFAULT",
   SLEW => "SLOW")
port map (
   O => o_MISO,             -- Buffer output
   IO => io_SDIO,           -- Buffer inout port (connect directly to top-level port)
   I => i_MOSI,             -- Buffer input
   T => s_N_enable_output   -- 3-state enable input, high=input, low=output
);

o_CS <= i_CS;
o_SCLK <= i_SCLK;

o_count <= std_logic_vector(to_unsigned(s_CLK_count, 5));
o_enable <= s_N_enable_output;

    process(i_SCLK, i_CS)
    begin
        if falling_edge(i_CS) then
            -- Reset CLK_count and turn IOBUF into output mode
            s_CLK_count <= 0;
            s_N_enable_output <= '0'; -- set IOBUF to output mode
        end if;
        
        if falling_edge(i_SCLK) then
            if s_CLK_count = 16 then
                -- on the 16th clk edge switch to input mode
                s_N_enable_output <= '1'; -- set IOBUF to input mode
            end if;
            
            -- on all falling clk edges increment the CLK_count if active
            if i_CS = '0' then
                s_CLK_count <= s_CLK_count + 1;
            end if;
            
            if i_CS = '1' then
                -- use the falling clk edges to reset the system if inactive, i.e., CS == 1
                s_N_enable_output <= '1'; -- set IOBUF to input modde
            end if;
        end if;
        
    end process;

end behavioral;

My approach to the design was to create a signal s_N_enable_output which represents the desired input/output mode of the IOBUF and drive the T pin of the IOBUF with this signal only. By onyl mapping the shared data line to the inout port and not driving it directly in the process I was hoping to avoid such Multiple Driver Nets errors.

EDIT: I have overlooked that there is another Multiple Driver Nets error on the o_MISO pin:

[DRC MDRV-1] Multiple Driver Nets: Net zsys_i/SPI_3wire_adapter_AD_0/U0/o_MISO has multiple drivers: zsys_i/SPI_3wire_adapter_AD_0/U0/IOBUF_inst/IBUF/O, and zsys_i/SPI_3wire_adapter_AD_0/U0/o_MISO_reg/Q.

Solution

  • I made the design suitable for synthesis and implementation by introducing an intermidiate signal s_CLK_count_greater_15 and handling all the "state changes" in one process:

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    Library UNISIM;
    use UNISIM.vcomponents.all;
    
    entity SPI_3wire_adapter_AD9637 is
        port (
            o_count : out std_logic_vector(4 downto 0);
            o_enable : out std_logic;
        
            i_SCLK :    in STD_LOGIC;           --PS side clock on which the module operates syncronously
            i_MOSI :    in STD_LOGIC;           --MOSI PS side (input)
            o_MISO :    out STD_LOGIC;          --MISO PS side (output)
            i_CS :      in STD_LOGIC;           --CS PS side (input)
        
            o_SCLK :    out STD_LOGIC;          --SPI clock (output)
            o_CS :      out STD_LOGIC;          --SPI CS (output)
            io_SDIO :   inout STD_LOGIC         --SPI SDIO - shared MOSI/MISO for 3-wire SPI (output/input)
        );
    end SPI_3wire_adapter_AD9637;
    
    architecture behavioral of SPI_3wire_adapter_AD9637 is
    
        signal s_CLK_count: integer range 0 to 23 := 0;
        signal s_IN_notOUT : STD_LOGIC;
        signal s_CLK_count_greater_15 : STD_LOGIC;
    
    begin
    
    IOBUF_inst : IOBUF
    generic map (
       DRIVE => 12,
       IOSTANDARD => "DEFAULT",
       SLEW => "SLOW")
    port map (
       O => o_MISO,             -- Buffer output
       IO => io_SDIO,           -- Buffer inout port (connect directly to top-level port)
       I => i_MOSI,             -- Buffer input
       T => s_IN_notOUT         -- 3-state enable input, high=input, low=output
    );
    
    o_CS <= i_CS;
    o_SCLK <= i_SCLK;
    
    o_count <= std_logic_vector(to_unsigned(s_CLK_count, 5));
    s_IN_notOUT <= not i_CS and s_CLK_count_greater_15;
    o_enable <= s_IN_notOUT;
    
    process(i_SCLK)
    begin
        if falling_edge(i_SCLK) then
            
            if i_CS = '1' then
                s_CLK_count <= 0;          
            else
                s_CLK_count <= s_CLK_count + 1; -- on all falling clk edges increment the CLK_count if CS is low
            end if;
            
            if s_CLK_count > 15 then
                s_CLK_count_greater_15 <= '1';
            else
                s_CLK_count_greater_15 <= '0';
            end if;
            
        end if;
    end process;
    
    end behavioral;