Search code examples
vhdlhdlvivado

Vivado stops simulation on feedback circuit


I'm trying to do a circuit consisting of a 2 to 1 multiplexer (8-bit buses), an 8-bit register and an 8-bit adder. These components are all tested and work as expected.

The thing is: if I try to send the output of the Adder to one of the inputs of the multiplexer (as seen in the image by the discontinued line), the simulation will stop rather suddenly. If I don't do that and just let ain do its thing, everything will run just as it should, but I do need the output of the adder to be the one inputted to the multiplexer.

Diagram of what I'm trying to do

The simulation is the following:

Simulation

The code is:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Sumitas is
    port (m   : in  STD_LOGIC;
          clk : in  STD_LOGIC;
          ain : in  STD_LOGIC_VECTOR (7 downto 0);
          Add : out STD_LOGIC_VECTOR (7 downto 0));
end Sumitas;

architecture rtl of Sumitas is
    component Adder8bit
        port (a, b : in  STD_LOGIC_VECTOR (7 downto 0);
              Cin  : in  STD_LOGIC;
              S    : out STD_LOGIC_VECTOR (7 downto 0);
              Cout : out STD_LOGIC);
    end component;
    
    component GenericReg
        generic (DataWidth : integer := 8);
        port (en      : in  STD_LOGIC;
              dataIn  : in  STD_LOGIC_VECTOR (DataWidth - 1 downto 0);
              dataOut : out STD_LOGIC_VECTOR (DataWidth - 1 downto 0));
    end component;
    
    component GenericMux2_1
        generic (DataWidth : integer := 8);
        port (a, b : in  STD_LOGIC_VECTOR (DataWidth - 1 downto 0);
              Z    : in  STD_LOGIC;
              S    : out STD_LOGIC_VECTOR (DataWidth - 1 downto 0));
    end component;
    
    constant DW : integer := 8;
    
    signal AddOut_s, MuxOut_s : STD_LOGIC_VECTOR (7 downto 0);
    signal PCOut_s            : STD_LOGIC_VECTOR (7 downto 0);
    
begin
    
    m0 : GenericMux2_1
        generic map (DataWidth => DW)
        port map (a => "00000000",
                  b => AddOut_s,
                  Z => m,
                  S => MuxOut_s);

    PC : GenericReg
        generic map (DataWidth => DW)
        port map (en      => clk,
                  dataIn  => MuxOut_s,
                  dataOut => PCOut_s);

    Add0 : Adder8bit
        port map (a    => "00000001",
                  b    => PCOut_s,
                  Cin  => '0',
                  S    => AddOut_s,
                  Cout => open);
    Add <= AddOut_s;    
end rtl;

and the testbench:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity bm_Sumitas is
end bm_Sumitas;

architecture benchmark of bm_Sumitas is
    component Sumitas
        port (m   : in  STD_LOGIC;
              clk : in  STD_LOGIC;
              ain : in  STD_LOGIC_VECTOR (7 downto 0);
              Add : out STD_LOGIC_VECTOR (7 downto 0));
    end component;
    
    signal clk_s, m_s    : STD_LOGIC;
    signal Add_s, ain_s  : STD_LOGIC_VECTOR (7 downto 0);
    
    constant T : time := 2 ns;
    
begin

    benchmark : Sumitas
        port map (m   => m_s,
                  clk => clk_s,
                  ain => ain_s,
                  Add => Add_s);

    clk_proc: process
    begin
        clk_s <= '0';
            wait for T/2;
        clk_s <= '1';
            wait for T/2;
    end process;

    bm_proc : process
    begin
        m_s <= '0';
            wait for 10 ns;
        m_s <= '1';
            wait for 100 ns;
    end process;

    ains_proc : process
    begin
        ain_s <= "00001111";
        for I in 0 to 250 loop
            ain_s <= STD_LOGIC_VECTOR(TO_UNSIGNED(I, ain_s'length));
            wait for T;
        end loop;
    end process;
end benchmark;

How can I do the thing I want? I'm ultimately trying to simulate a computer I designed. I have every component already designed and I'm coupling them together.


Solution

  • Constructing a Minimal, Complete, and Verifiable example requires filling in the missing components:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity Adder8bit is
        port (a, b : in  STD_LOGIC_VECTOR (7 downto 0);
              Cin  : in  STD_LOGIC;
              S    : out STD_LOGIC_VECTOR (7 downto 0);
              Cout : out STD_LOGIC);
    end entity;
    
    architecture foo of adder8bit is
        signal sum: std_logic_vector (9 downto 0);
        use ieee.numeric_std.all;
    begin
        sum <= std_logic_vector ( unsigned ('0' & a & cin) +
                                  unsigned ('0' & b & cin ));
        s <= sum(8 downto 1);
        cout <= sum(9);
    end architecture;
    
    library ieee;
    use ieee.std_logic_1164.all;
    
    entity GenericReg is
        generic (DataWidth : integer := 8);
        port (en      : in  STD_LOGIC;
              dataIn  : in  STD_LOGIC_VECTOR (DataWidth - 1 downto 0);
              dataOut : out STD_LOGIC_VECTOR (DataWidth - 1 downto 0));
    end entity;
    
    architecture fum of genericreg is
    begin
        dataout <= datain when en = '1';
    end architecture;
    

    with behavioral model substitutes.

    (It's not that much work, copy the component declarations paste them, substitute entity for component and add the reserved word is, followed by simple behaviors in architectures.)

    It reproduces the symptom you displayed in your simulation waveform:

    bm_sumitas.jpg

    You can see the essential point of failure occurs when the register enable (ms_s) goes high.

    The simulator will report operation on it's STD_OUTPUT:

    %: make wave
    /usr/local/bin/ghdl -a   bm_sumitas.vhdl
    /usr/local/bin/ghdl -e   bm_sumitas
    /usr/local/bin/ghdl -r  bm_sumitas --wave=bm_sumitas.ghw --stop-time=40ns
    ./bm_sumitas:info: simulation stopped @11ns by --stop-delta=5000
    /usr/bin/open  bm_sumitas.gtkw
    %:
    

    Note the simulation stopped at 11 ns because of a process executing repeatedly in delta cycles (simulation time doesn't advance).

    This is caused by a gated relaxation oscillator formed by the enabled latch, delay (a delta cycle) and having at least one element of latch input inverting each delta cycle.

    The particular simulator used has a delta cycle limitation, which will quit simulation when 5,000 delta cycles occur without simulation time advancing.

    The genericreg kept generating events with no time delay in assignment, without an after clause in the waveform, after 0 fs (resolution limit) is assumed.

    Essentially when the enable is true the signal will have at least one element change every simulation cycle due to the increment, and assigns the signal a new value for at least one element each simulation cycle without allowing the advancement of simulation time by not going quiescent.

    You could note the simulator you used should have produced a 'console' output with a similar message if it were capable (and enabled).

    So how it this problem cured? The easiest way is to use a register (not latch) sensitive to a clock edge:

    architecture foo of genericreg is
    begin
        dataout <= datain when rising_edge(en);
    end architecture;
    

    Which gives us the full simulation:

    bm_sumitas.jpg