vhdluninitialized-constant

Uninitialized signal value for unknown reason


I am trying to use 3 components of the same type under a main module and the displayed output value is uninitialized. It seems that somehow the code I wrote is forcing certain values ​​that contradict the logic I intended.

The problem is in the CLR signal. It's supposed to reset the counters values when all of them have the same digit as the TOP_COUNT input (value of 2 in the testbench in the screensot below).

The component:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity counter is
    Port ( EN : in STD_LOGIC;
           CLR : INOUT STD_LOGIC :='0'; -- WAS BUFFER
           clk_5 : in STD_LOGIC;
           RST : in STD_LOGIC;
           TOP_COUNT : in STD_LOGIC_VECTOR (3 downto 0);
           TOP_CNT_REACH : out STD_LOGIC;
           REACH_9 : out STD_LOGIC;
           Q : inout integer range 0 to 9); -- was buffer
end counter;

architecture Behavioral of counter is

signal top_cnt: integer range 0 to 9;
signal temp_CLR: std_logic:='0';

begin
top_cnt <= to_integer(unsigned(TOP_COUNT));

process (clk_5)
begin
    if rising_edge(clk_5) then
        if RST = '1' or CLR = '1' OR temp_CLR = '1' then  -- CLR should rise for 1 clk_5 cycle
            Q <= 0;
        elsif EN = '1' then
           Q <= Q + 1;
        end if;
    end if;
end process;         


OUTPUT_UPDATES:            
process(Q, top_cnt)
begin
    if Q = top_cnt then
        TOP_CNT_REACH <= '1';
    else
        TOP_CNT_REACH <= '0';        
    end if;
    
    if Q = 9 then
        REACH_9 <= '1';
        temp_CLR <= '1'; 
    else 
        REACH_9 <= '0';
        temp_CLR <= '0';
    end if;         
end process;

The logic part that uses the component:

LSD: counter port map (en=>en(0), clr =>clr(0), clk_5=>clk5, rst=>reset, top_count=>top_count, 
                       top_cnt_reach=>top_cnt_reach(0), reach_9=>reach_9(0), Q=>Q0);
ISD: counter port map (en=>en(1), clr =>clr(1), clk_5=>clk5, rst=>reset, top_count=>top_count, 
                       top_cnt_reach=>top_cnt_reach(1), reach_9=>reach_9(1), Q=>Q1);  
MSD: counter port map (en=>en(2), clr =>clr(2), clk_5=>clk5, rst=>reset, top_count=>top_count, 
                       top_cnt_reach=>top_cnt_reach(2), reach_9=>reach_9(2), Q=>Q2); 

LSD_LOGIC:
EN(0) <= '1';
process(clk5) -- REACH_9(0)
begin
    if clk5 = '1' then
        if REACH_9(0) = '1' then
            CLR(0) <= '1';
            EN(1) <= '1';    
        else 
            CLR(0) <= '0'; 
            EN(1) <= '0';   
        end if;  
    end if;     
end process;

ISD_LOGIC:
process(clk5)  -- REACH_9(1)
begin
    if clk5='1' then
        if REACH_9(1) = '1' then
            CLR(1) <= '1';
            EN(2) <= '1';    
        else 
            CLR(1) <= '0';
            EN(2) <= '0';    
        end if;  
     end if;      
end process;

MSD_LOGIC:
process(clk5) -- TOP_CNT_REACH(0)
begin
    if clk5 = '1' then
        if (top_cnt_reach = "111") then
            clr <= "111"; -- clr(2) <= '1';
        else
            clr(2) <= '0';    
        end if;
    end if;        
end process;

The simulation results:

enter image description here

I also tested the component with a testbench for one unit of the component and the results were good. But when I combined the 3 components under the main module problems appeared.


Solution

  • TLDR: Your signal CLR(2 downto 0) has multiple drivers on it.

    Think of each independent assignment you do as the output of a hardware gate - in VHDL we also call this a driver. For a process each signal that you do an assignment to in that process has a single driver on it - even if there are multiple assignments to that signal in the process.

    Your components each have CLR as an inout with the value initialized to '0'. Given that there are assignments to CLR inside that design they will drive a '0'. To fix this, make it an IN.

    entity counter is
        Port ( EN : in STD_LOGIC;
               CLR : in STD_LOGIC :='0'; -- WAS INOUT
    

    In the following process, you drive all elements of CLR in the first part of the if branch. This process will initially drive CLR(1 downto 0) to a U and then once top_cnt_reach = "111" it will drive them all to a '1'. You can fix this by only driving clr(2) in this process as shown below:

    MSD_LOGIC:
    process(clk5) -- TOP_CNT_REACH(0)
    begin
        if clk5 = '1' then
            if (top_cnt_reach = "111") then
                clr(2) <= '1';
            else
                clr(2) <= '0';    
            end if;
        end if;        
    end process;
    

    Are you trying to create registers (aka flip-flops) or latches? If you are intending to create registers, then code the rising edge of clock as one of the following:

    -- preferred choice for readability
    if rising_edge(Clk5) then 
    
    -- also correct
    if Clk5 = '1' and Clk5'event then 
    

    If you are intending to create latches, you are missing a couple of signals from the sensitivity lists. Latches are rare for this sort of application so I will assume not.

    You could put all three processes together as follows. This allows you to do an assignment to all 3 bits - but be careful as I did it first so it has lower priority to the assignments of clr(1 downto 0)

    LSD_LOGIC:
    EN(0) <= '1';
    process(clk5) -- REACH_9(0)
    begin
        if rising_edge(clk5) then
            if (top_cnt_reach = "111") then
                clr <= "111"; -- clr(2) <= '1';
            else
                clr(2) <= '0';    
            end if;
    
            if REACH_9(0) = '1' then
                CLR(0) <= '1';
                EN(1) <= '1';    
            else 
                CLR(0) <= '0'; 
                EN(1) <= '0';   
            end if;  
    
            if REACH_9(1) = '1' then
                CLR(1) <= '1';
                EN(2) <= '1';    
            else 
                CLR(1) <= '0';
                EN(2) <= '0';    
            end if;  
        end if;        
    end process;