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.
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;