A bi-directional bus is typically implemented using a tristate buffer. When the output of the tribate buffer is "Z", you can read from the inout
port, when the buffer controls the line, it acts as the output. In VHDL, this can be done by directly initializing the primitive (for example, IOBUF
for the Xilinx device) or by allowing your synthesis tool to output a tristate buffer, describing the logic as described above.
There are 3 signals:
T
, which is your trest control. This signal will be obtained from your synchronization logic, knowing the ULPI protocol. That is, since the bus is shared, there must be some way to know when you should receive data or send data.I
is your input data that you want to send via the bus after registering with the corresponding clock.O
is your output that you receive on the bus, before any registration / synchronization.
Key: the trestate buffer is not synchronous. This is what you do before / after the tristate buffer, which will correctly synchronize your signal. In this case, you must synchronize your inputs with the tristate buffer (which must be transferred) on the rising edge of the clock and the logged data received from the tristate / IOBUF buffer on the falling edge of the clock.
Design example.
library ieee; use ieee.std_logic_1164.all; library unisim; -- for xilinx IOBUF use unisim.vcomponents.all; entity iobuffer_example is port ( I_CLK : in std_logic; -- synchronized with bidir bus IO_DATA : inout std_logic; -- data to/from external pin on bidir bus I_DIR_CTRL : in std_logic; -- from other VHDL logic, controlling bidir bus direction O_DATA_FROM_EXTERNAL : out std_logic; -- data received over bidir bus I_DATA_TO_EXTERNAL : in std_logic); -- data to send over bidir bus end entity iobuffer_example; architecture io_buffer_arch of iobuffer_example is signal data_in : std_logic; signal data_out : std_logic; begin IOBUF_Inst : IOBUF port map ( O => data_in, -- data from bidir bus IO => IO_DATA, -- data on bidir bus I => data_out, -- data to bidir bus T => I_DIR_CTRL); -- 3-state enable input, high=input, low=output Register_Input : process (I_CLK) is begin if (falling_edge(I_CLK)) then O_DATA_FROM_EXTERNAL <= data_in; end if; end process Register_Input; Register_Output : process (I_CLK) is begin if (rising_edge(I_CLK)) then data_out <= I_DATA_TO_EXTERNAL; end if; end process Register_Output; end architecture io_buffer_arch;
Notes.
Be aware of the crossroads of a domain. There are many possible transitions for data coming out of the bus, especially if your internal logic is controlled on different clocks than the bus clock. I cannot make an offer without any details.
If you want the behavioral representation of the tribate buffer to be output using synthesis tools, can you do something similar instead of using the unisim library and IOBUF
:
PROCESS (I_DIR_CTRL, IO_DATA) BEGIN IF( I_DIR_CTRL = '1') THEN IO_DATA <= 'Z'; ELSE IO_DATA <= data_out; END IF; data_in <= IO_DATA; END PROCESS;
source share