Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
14 years ago

Mux INOUT port in VHDL

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.

20 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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?

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- 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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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;
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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,
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

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