Bidirectional databus design

16,530

A bidirectional bus is typically implemented by using a tristate buffer. When the tristate buffer output is "Z" you can read from the inout port, when the buffer is driving the line, it acts as an output. In VHDL, this can be implemented by directly instanciating a primitive (e.g. IOBUF for Xilinx device), or by letting your synthesis tool infer tristate buffer by describing logic as described above.

There are 3 signals you are dealing with here:

  • T which is your tristate control. This signal will be derived from your synchronization logic, knowing the protocol of ULPI. That is since the bus is shared, there must be some way of knowing when you should be receiving data vs. sending data.
  • I this is your input data that you wish to send across the bus, after being registered by the appropriate clock.
  • O this is your output data that you are receiving over the bus, prior to any registering/synchronization.

Key: A tristate buffer is not synchronous. It is what you do before/after the tristate buffer that will properly synchronize your signal. In this case, you must synchronize your inputs to the tristate buffer (to be transmitted) on the rising clock edge, and registered data received from tristate buffer/IOBUF on the falling clock edge.

Sample Design.

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 mindful of cross clock domain crossings. There are many possible crossings here for data going out and coming from the bus, especially if your internal logic is driven on a different clock than the bus clock. I can't make a suggestion without more detail.

If you would like a behavioral representation of the tristate buffer to be inferred by the synthesis tools, can you could do something like this, instead of using 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;
Share:
16,530
JakobJ
Author by

JakobJ

Updated on June 07, 2022

Comments

  • JakobJ
    JakobJ almost 2 years

    I need to communicate with a chip via a bidirectional databus (ULPI).

    As far as I can ascertain, data is shifted out on the ULPI bus on rising clock edges, and read on falling clock edges. My problem is that when reading a register, I first need to be sensitive to rising edges (to write the command to the chip on the databus), and then to falling edges when reading register output onto the bus from the chip.

    I'm unclear as to how to design this the best way.

    I tried with one process which had a case statement, but for this to work, my process needs to be sensitive to both rising and falling edges, which I think is not good. Or is it actually OK?