- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The code is working for me.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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'.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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().
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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'.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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 hadsignal 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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
So how can I connect SDA1 and SDA2 bidirectional to MUX? Separate ports for IN and OUT is what I now have.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page