VHDL - how to use inout as inout and as normal out?

I wanted to ask if it is possible to use an inout pin as inout and normal out? The two behaviours should be switched through a MUX. The reason for this weird looking implementation is that I have two boards and I want to use the same bitstream. On one board, the same pin is connected to a LED through GPIO and on the other it goes to my I2C bus connection. The software tries to detect the I2C and if successful it sets a register. If not, it clears it.

LED_or_SDA      : inout std_logic; -- port definition

process (register)
begin
if ( register = '1') then -- software sets this register
LED_or_SDA <= I2C_SDA; -- here I want to use it as inout
else
LED_or_SDA <= gpio_reg; -- here I want to use it as normal out
end if;
end process;


This implementation throws the error "bidirect pad net is driving non-buffer primitives" during translate. Is there a solution for this?

• I don't remember VHDL, but since the syntax highlighting is not working I suspect there is some error in the code. – clabacchio Mar 25 '14 at 12:32
• The code I posted is just to give an idea. The real code is similar to this and goes past through synthesis. But that is beside the point. My question is about how such a thing could be done and if yes what is wrong with the way I code above. – user39236 Mar 25 '14 at 12:42
• You need to have it driven from a tristate buffer, I think. – pjc50 Mar 25 '14 at 16:35

There is no fundamental reason why an inout pin cannot be used as a simple output...just ignore the input signal. I suspect your problem is in the actual VHDL code (rather than the version you posted) or in the details of how you are implementing the design on an FPGA.

Most FPGAs do not have internal tri-state buffers except at the IOB (I use Xilinx terms). Therefore it is recommended to put all inout signals at the top-level (with the associated 'Z' driving logic), and use plain old in and out ports throughout your design.

In fact, given an inout port "DataBus", I create signals "DataBus_in" and "DataBus_out". Everywhere I read from the data bus, I use "<= DataBus_in", and any time I want to write something to the data bus, I use "DataBus_out <=". Other than this snippet, the actual inout port DataBus is used nowhere else, and the _in and _out versions are used exclusively.

    DataBus_in <= DataBus;    -- always read the value to xyz_in

tsb_bus_driver: process(OE, DataBus_out) is
begin
if (OE= '1') then DataBus <= DataBus_out;
else DataBus <= (others => 'Z');
end if;
end process tsb_bus_driver;


Elsewhere...

DataBus_out <= foo;  -- example of writing to xyz_out
bar <= DataBus_in;   -- example of reading from xyz_in


You're unlikely to be able to synthesise connecting your tristate I2C line via a process like that. I would do this (I'm assuming you already have SDA_IN and SDA_OUT signals which you are currently using to create your SDA)...

LED_or_SDA <= gpio_reg when register = '0'
else '0' when register = '1' and I2C_SDA_OUT = '0'
else 'Z';
I2C_SDA_IN <= LED_or_SDA when register = '0'
else '1'; -- idle high

• +1, for this specific application, SDA_OUT is open-drain. My example assumed totem pole outputs. – ajs410 Mar 27 '14 at 21:10