0
votes

I'm attempting to write a I2C bus master in VHDL - and thoroughly test it to ensure it works etc. In doing so, I've written the module and a subsequent test-bench to test it under different stimuli - namely changing addresses and data input after each sending cycle (which is when the busy goes low).

To do this, I first attempted to increment the value in the data bus and decrement the values in the address bus, using the techniques described in this question here. However, when I did this, rather than the address bus taking the new value, it takes an uninitialised value and the assignment statement doesnt seem to execute.

I then attempted to use an intermediate integer signal, however this produced the same results, however this means that the address & data buses take the correct value for the first cycle - but then don't increment and instead take a unknown state!

Ultimately my question is why do these errors occur - and how do I fix/avoid them?

My code for the I2C master module can be found here and the code for the I2CBus (testbench) can be found here. Any other information I can provide to help please just say!

Thanks very much,

David

-----Code using intermediate signals------

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY I2CBus IS
END I2CBus;

ARCHITECTURE behavior OF I2CBus IS
    COMPONENT IIC_Master
        PORT(
            CLOCK   : IN    std_logic;
            RESET_N : IN    std_logic;
            ENA     : IN    std_logic;
            ADR     : IN    std_logic_vector(6 downto 0);
            RW      : IN    std_logic;
            DAT_WR  : IN    std_logic_vector(7 downto 0);
            DAT_RD  : OUT   std_logic_vector(7 downto 0);
            BUSY    : OUT   std_logic;
            SCL     : INOUT std_logic;
            SDA     : INOUT std_logic;
            ACK_ERR : BUFFER std_logic
        );
    END COMPONENT;

    --Inputs
    signal CLOCK   : std_logic := '0';
    signal RESET_N : std_logic;         --active high
    signal ENA     : std_logic;         --active high
    signal ADR     : std_logic_vector(6 downto 0);
    signal RW      : std_logic;         --read high write low
    signal DAT_WR  : std_logic_vector(7 downto 0);

    --BiDirs
    signal SCL : std_logic;
    signal SDA : std_logic;

    --Outputs
    signal DAT_RD  : std_logic_vector(7 downto 0);
    signal BUSY    : std_logic;
    signal ACK_ERR : std_logic;

    -- Clock period definitions
    constant CLOCK_period : time := 5 ns;

    --Signals to vary
    signal address    : integer := 127;
    signal input_data : integer := 0;

BEGIN
    -- Instantiate the Unit Under Test (UUT)
    uut : IIC_Master PORT MAP(
            CLOCK   => CLOCK,
            RESET_N => RESET_N,
            ENA     => ENA,
            ADR     => ADR,
            RW      => RW,
            DAT_WR  => DAT_WR,
            DAT_RD  => DAT_RD,
            BUSY    => BUSY,
            SCL     => SCL,
            SDA     => SDA,
            ACK_ERR => ACK_ERR
        );

    -- Clock process definitions
    CLOCK_process : process
    begin
        CLOCK <= '0';
        wait for CLOCK_period / 2;
        CLOCK <= '1';
        wait for CLOCK_period / 2;
    end process;

    -- Reset process
    reset : process
    begin
        reset_n <= '0';
        ADR     <= "1111111";           --This doesn't seem to happen the first time?
        DAT_WR  <= "00000000";          --Nor does this?
        RW      <= '0';
        wait for 50 ns;
        reset_n <= '1';
        ENA     <= '1';

        wait;
    end process;

    stim_proc : process
    begin
        DAT_WR <= std_logic_vector(to_unsigned(input_data, 8));
        ADR    <= std_logic_vector(to_unsigned(address, 7));
        if input_data < 127 then
            address    <= address - 1;
            input_data <= input_data + 1;
            wait until BUSY = '0' and RESET_N = '1';
        elsif unsigned(DAT_WR) > 126 then
            wait;
        end if;
    end process;
end behavior;

------1st Stimulus Process w/out intermediate signals

stim_proc : process
begin
    if input_data < 127 then
        wait until BUSY = '0' and RESET_N = '1';
        ADR    <= std_logic_vector(unsigned(ADR) + 1);
        DAT_WR <= std_logic_vector(unsigned(DAT_WR) + 1);
    elsif unsigned(DAT_WR) > 126 then
        wait;
    end if;
end process;

Results from second simulation using intermediate signals enter image description here

2

2 Answers

4
votes

The usual cause of this is the resolution of multiple assignments to signals like ADR in several different processes. In hardware terms. this is equivalent to short circuiting together the output of several different ICs. (It doesn't work without special care, and can destroy your ICs).

So check that you are assigning ADR and similar signals in only one process, and if necessary, rewrite to combine those processes into one process.

If you need to connect several outputs together, there are 2 ways that work successfully, though I don't think it's the right approach here.

  1. Wired-OR outputs (or Wired-AND). In Wired-OR scheme the signal is permanently pulled weakly low by an assignment like ADR <= (others -> 'L'); and this can be overridden by each output driving either 'Z' for low or '1' for high. This works because multiple outputs can safely drive the signal at once.
  2. Tri-state outputs, with arbitration to make sure only one process drives the output at any time. The others all drive 'Z' or (others => 'Z') onto the same signal, to signify they are inactive.
2
votes

The answer from Brian Drummond is correct. I just wand to explain in at your example. At time 0, both the reset as well as the stim_proc process assign the same value:

reset : process
begin
    ...
    ADR     <= "1111111";
    ...
 end process;

and

stim_proc : process
begin
    ADR    <= std_logic_vector(to_unsigned(address, 7)); -- with address = 127
    ...
end process;

So, the result for ADR is "1111111". But after decrementing address (in original code with intermediate version), the stim_proc process assigns a different value (than the reset process) after it starts over. You see this in the waveform. When address gets 126, which is "1111110", only the lowest bit of ADR gets X because only this bit differs from "1111111" assigned in the reset process.

Solution 1

If you just want to initialize a signal, assign the initialization value at the signal declaration. (I think, this is what you want according to your VHDL comment.) That is:

signal ADR     : std_logic_vector(6 downto 0)  := (others => '1');

Solution 2

If you (really) want to assign "1111111" only for the first 50 ns seconds from the reset process, then you have to assign (others => 'Z') (tri-state), (others => 'Z') (weak pull-down) or (others => 'H') (weak pull-up) afterwards in this process to allow an "overriding" by the stim_proc process, e.g.:

reset : process
begin
    ADR     <= "1111111";
    wait for 50 ns;
    ADR     <= (others => 'Z');
    wait;
 end process;