Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17241 Discussions

Mux INOUT port in VHDL

Altera_Forum
Honored Contributor II
9,233 Views

Hi all! 

Currently I'm trying to connect my I2C master module to one of four I2C slaves using a mux without success: I can't get the bidirectional SDA signal to correctly mux. The code is as follows: 

 

mux_sda: process (sel, i_sda_o, sda) begin sda <= (others => 'Z'); if i_sda_o = '0' then sda(sel) <= '0'; end if; end process; i_sda_i <= to_x01(sda(sel));where 

 

  • sda is a vector of SDA pins on the FPGA declared as INOUT, 

  • i_sda_o the internal SDA output from the I2C Master to driver SDA, 

  • i_sda_i the internal SDA input to the I2C Master to read SDA and 

  • sel an integer used to select the pin to connect the master to. 

 

If I try to simulate the module everything works as expected. Trying to synthesise it, Quartus complains "Illegal directional connection" for every pin in sda at "i_sda_i <= ..." 

Can anyone please give me a hint why I'm not allowed to read a particular element of sda and how to solve that? Thank You! 

 

Best Regards 

Pauliman 

 

PS: I'm on Quartus 11.0 and ModelSim 6.6d.
0 Kudos
20 Replies
Altera_Forum
Honored Contributor II
5,787 Views

The code is working for me.

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

Hi FvM! 

Many thanks for checking. What can I do to allow you to verify my problem? If I try to synthesise my project, Quartus complains 

Error: Illegal directional connection from the pin "O3_DDCSDA" to the node "EDIDOutput:EDIDOutput|Mux0"  

Error: Illegal directional connection from the pin "O2_DDCSDA" to the node "EDIDOutput:EDIDOutput|Mux0"  

Error: Illegal directional connection from the pin "O1_DDCSDA" to the node "EDIDOutput:EDIDOutput|Mux0"  

Error: Illegal directional connection from the pin "O4_DDCSDA" to the node "EDIDOutput:EDIDOutput|Mux0" 

where O?_DDCSDA are the members of the sda vector and EDIDOutput is the module my mux code is in. If I double click on any of the messages Quartus highlights the line "i_sda_i <= to_x01(sda(sel));". 

Best regards 

Pauliman
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

what does the function to_x01 do? 

in any case, if you have a high speed system clock, or if read latency is not a big problem, I would recommend registering the sda bus before you read \ MUX it. it might solve your problem, and as benefit will minimize the risk for metastability.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

Hi the brick! 

to_x01() is supposed to convert all possible states of std_logic_vector to either 0, 1 or x. I'm using this to read a H as 1. The signals are I2C where the clock speed is in my case 100kHz. Registering would be an option. Thanks for the hint! 

Pauliman
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

I think you misunderstand the difference between synthesis and simulation. in real life (synthesis) there are only 2 states for std_logic, '0' or '1'. when you drive 'Z" the bus will be pulled up to '1' by the pull up resistor on the board (part of I2C standard), unless the slave forces it down to '0'. 

 

Remove all references to 'H' 'X' and 'L' from your "operational" code, leave it for test benches.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

No, 'Z' is a real state that pins can drive. It means high impedance, and IS important in VHDL code. Without it you get no tri-state drivers on your pins (and errors associated with conflicting 0 and 1 - which is what 'X' is for in VHDL). A point to note though is that this is only permissable on FPGA pins. Internal tri-states will be converted to muxes during synthesis. 

 

There is nothing wrong with 'H' and 'L', as long as its in the testbench. Inside an FPGA, they will be converted to '0' and '1'.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

My to-be-synthesised code does not contain anything else then '0', '1' and 'Z'. In the testbench I simulated the pull-up resistors mandatory for I2C by assigning SDA <= 'H';. With that the levels are correct, but I can't read SDA correctly until I convert 'H' to '1' which is why I'm using to_x01().

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

Tricky: does that mean that I'm not allowed to connect INOUT ports to internal modules and use them there are tri-state? I've grouped my tri-state pins (O?_DDCSDA) into a std_logic_vector (sda) and connected that vector to my EDIDOutput module in which the mux is located. Do I need to move the mux to the main module to get it working?

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

Tricky, I don't see any contradiction between our posts. I specifically wrote you can drive 'Z', but the signal value will be determent by the outside world.  

I also explained that 'H' and 'L' are for test benches. 

 

Pauliman, you don't need the x01 function. if the signal is driven 'Z' (from the master) and 'H' (from test bench) the simulator will translate it as '1' there is a table that defines which value "wins" in every conflict. in your case 'H' simulates the pull up resistor. 

in any case you wrote x01 as part of your code in the first post, and 'x' is also for simulation and test benches only.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

 

--- Quote Start ---  

 

Pauliman, you don't need the x01 function. if the signal is driven 'Z' (from the master) and 'H' (from test bench) the simulator will translate it as '1' there is a table that defines which value "wins" in every conflict. in your case 'H' simulates the pull up resistor. 

in any case you wrote x01 as part of your code in the first post, and 'x' is also for simulation and test benches only. 

--- Quote End ---  

 

 

You still need the to_x01 function, given what Pauliman said he is doing in his testbench. 

'Z' driven with 'H' gives 'H' 

 

if input = '1' then - would fail in simulation. 

 

if to_X01(input) = '1' then -would pass. 

 

Its only when you drive '1' and 'H' that you get '1'. 

You also have to be careful, because 'H' driven against '0' gives '0'.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

the brick: thanks for this clarification. I used to_x01 to solve problems when comparing states in the module reading SDA. If I just read 'H' on the line, a compare with '1' fails. According to my techbench I'm also reading 'H's into my internal sift register. I need to compare that later with a preassigned address (a std_logic_vector containing '0' and '1') which also always fails. Whats your suggested method to solve that?

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

Pauliman. Yes you can read inout ports. Just read it when you're driving 'Z' onto it. I assume you have a CS or WE port coming in, so you know when data is avaibable on the bus. If you dont - you wont know when to read it.

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

 

--- Quote Start ---  

Tricky: does that mean that I'm not allowed to connect INOUT ports to internal modules and use them there are tri-state? I've grouped my tri-state pins (O?_DDCSDA) into a std_logic_vector (sda) and connected that vector to my EDIDOutput module in which the mux is located. Do I need to move the mux to the main module to get it working? 

--- Quote End ---  

 

 

Theres no problem with reading inout ports. The usual thing is the have them as inout only at the FPGA pin, and then convert it to separate in and out. With the incoming CS and WE inputs you should know when to read the port.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

As I wrote in my initial post, I'm feeding the INOUT pins into a vector and then down into the EDIDOutput module where I'm trying to mux one to my I2C Master. The master has dedicated input and output signals which I'm trying to combine into the selected INOUT signal by the mux code I posted. Due to the nature of I2C all members can only pull-down the bus. I accomplished this by writing either '0' or 'Z' to SDA. But the problem is, that I don't understand why Quartus does not let me read SDA. The reading of SDA generates the error messages I posted.

0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

I also don't understand why you can't compile the code. I guess, the problem isn't in the shown part. For clarfication my test application that did compile. 

 

The to_x01() point has been already discussed. It's meaningless for synthesis, but needed if you simulate I2C bus operation with a weak pull-up and open drain drivers at the slave. 

library ieee; use ieee.std_logic_1164.all; entity test0 is port ( sel : in integer range 0 to 3; sda : inout std_logic_vector(0 to 3); i_sda_o : in std_logic; i_sda_i : out std_logic ); end entity; architecture rtl of test0 is begin mux_sda: process (sel, i_sda_o, sda) begin sda <= (others => 'Z'); if i_sda_o = '0' then sda(sel) <= '0'; end if; end process; i_sda_i <= to_x01(sda(sel)); end rtl;
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

FvM: thanks for sharing your code. 

For further evaluation I modified the module where the MUX is in such, that it has separate IN and OUT ports for sda. In the top level module I combine this signals to the actual INOUT port pin. Interestingly that works. Can anyone explain me why? 

Previously I had 

signal sda_vec : std_logic_vector(1 to 2); sda_vec(0) <= SDA1; sda_vec(1) <= SDA2; so that sda_vec consisted of INOUT elements. sda_vec was connected to the module, where the MUX is in and the MUX had to handle the tri-state logic. 

Now I've changed it to 

signal sda_vec_in, sda_vec_out : std_logic_vector(1 to 2); sda_vec_in(1) <= SDA1; sda_vec_in(2) <= SDA2; SDA1 <= sda_vec_out(1); SDA2 <= sda_vec_out(2); and connected separate IN and OUT signals to the MUX, which does not have to care about tri-state anymore. 

I also noticed, that Quartus is completely happy, if sda_vec_out contains 'Z'. The result seems not to change if I replace "SDA1 <= sda_vec_out(1);" by "SDA1 <= '0' when sda_vec_out(1) = '0' else 'Z';". What are the pro's and con's?
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

I'm don't exactly understand what your original code was. Also in the assignment  

SDA1 <= sda_vec_out(1); 

which logic states are driven by sda_vec_out(1)? 

 

It's clear, that the physical SDA line should be driven in an open drain manner, either '0' or 'Z'. Also, there's only one physical open drain driver in the FPGA I/O cell. A tri-state respectively inout port can be nevertheless connected through the design hierarchies, if you do it right.
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

SDA1 and SDA2 are the INOUT pins on the FPGA. The I2C master module is driving SDA either '0' or 'Z' and constantly read it. 

As I only need to communication with one of the two SDA pins at a time, I created the MUX of the initial post which is located in a lower module. This lower module has its sda input/outputs as a INOUT std_logic_vector. In the top level module, I combined the SDA1 and SDA2 pin into a signal of type std_logic_vector using the code I posted last. So the FPGA pins are combined into a vector and then connected to a INOUT port of the module where the MUX is in. The MUX is then either driving sda '0' or 'Z' and constantly reading it. And that seems to be the problem: driving and reading at the same time in the lower module does not synthesise for me. 

The following is my initial code between FPGA-Pins and MUX, which does not synthesise. 

entity FPGA is port ( signal SDA1 : inout std_logic; signal SDA2 : inout std_logic ); end entity FPGA; architecture rtl of FPGA is signal sda_vec : std_logic_vector(1 to 2); begin sda_vec <= SDA1 & SDA2; MUX: entity work.MUX PORT MAP ( sda => sda_vec, ); end rtl; ENTITY MUX IS PORT ( sda : inout std_logic_vector(1 to 2) := (others => 'Z'); ); END MUX; ARCHITECTURE rtl OF MUX is signal i_sda_i, i_sda_o : std_logic; -- input/output sda to/from I2C master begin mux_sda: process (sel, sda) begin sda <= (others => 'Z'); if i_sda_o = '0' then sda(sel) <= '0'; end if; end process; i_sda_i <= to_x01(sda(sel)); end rtl; Today I change the interface of MUX to separate IN and OUT ports and combined them to the physical pins in the top level module. That synthesises. But why?
0 Kudos
Altera_Forum
Honored Contributor II
5,787 Views

There's no bidrectional assignment between SDA1 & SDA2 and port sda of entity MUX, just an unidirectional read: 

sda_vec <= SDA1 & SDA2; MUX: entity work.MUX PORT MAP ( sda => sda_vec,
0 Kudos
Altera_Forum
Honored Contributor II
5,560 Views

So how can I connect SDA1 and SDA2 bidirectional to MUX? Separate ports for IN and OUT is what I now have.

0 Kudos
Reply