1
\$\begingroup\$

I recently added 'X' propagation to my RT-Level VHDL designs, to detect early whenever operations take place on unknown ('X') or uninitialized ('U') values. The latter may come from registers, which I forgot to reset, or from memory which cannot be reset at all. Unknown values may come from logic operations on uninitialized values. I do not care about unknown or uninitialized values as long as they do not lead to undefined behavior, e.g., undefined state changes in a finite state machine (FSM). I do not distinguish between 'X', 'U' or any other meta-value, all are treated as 'X'.

I'm familiar with applying 'X' propagation to data paths, but I struggle to apply it to FSMs because the code is getting much more complex and readability is decreasing. Here is a stripped-down example, how my FSMs with 'X' propagation look like:

library ieee;
use ieee.std_logic_1164.all;

entity fsm is
    port (
        clock  : in std_logic;
        reset  : in std_logic;
        input  : in  std_logic_vector(1 downto 0);
        output : out std_logic); -- mealy output
end entity fsm;

architecture rtl of fsm is
    type states is (RUNNING, WAITING, UNKNOWN);
    signal state : states
        -- synthesis translate_off
        := UNKNOWN
        -- synthesis translate_on
        ;
    signal next_state : states;
begin  -- architecture rtl

    process(state, input)
    begin
        next_state <= state;
        output     <= '0';

        case state is
            when RUNNING =>
                case input(1) and not input(0) is -- some complex condition
                    when '1' => next_state <= WAITING;
                    when '0' => output     <= '1';
                    when others =>
                        next_state <= UNKNOWN;
                        output     <= 'X';
                end case;

            when WAITING =>
                next_state <= RUNNING;

            when UNKNOWN =>
                output <= 'X';
        end case;
    end process;

    process(clock)
    begin
        if rising_edge(clock) then
            case to_x01(reset) is
                when '1' =>    state <= RUNNING;
                when '0' =>    state <= next_state;
                when others => state <= UNKNOWN;
            end case;
        end if;
    end process;
end architecture rtl;

I use case statements to check inputs for some condition, so that, I don't have to repeat the condition as with if statements:

if (input(1) and not input(0)) = '1' then
    next_state <= WAITING;
elsif (input(1) and not input(0)) = '0' then -- repeated condition
    output <= '1';
else
    next_state <= UNKNOWN; output <= 'X';
end if;

I do not find the case statement very readable because without 'X' propagation, the condition would be much more clearer:

if input(1) = '1' and input(0) = '0' then
    next_state <= WAITING;
else
    output <= '1';
end if;

Another problem is to keep track when mealy outputs will be unknown. In the example above output will be unknown when the condition is undetermined.

So my questions are:

  1. Should I propagate 'X' values (to the FSM state and mealy outputs) or should I just report an error?

  2. Is there a more readable code template to test complex conditions as in the example above?

  3. Is there a better method to check wether a correct reset has been applied? (Note: the state should still be unknown until the first rising clock-edge!)

All after all: the code must be synthesizable. (Due to the synthesis guard, the state UNKNOWN will never be reached in real HW and is thus optimized away.)

\$\endgroup\$
  • 2
    \$\begingroup\$ Assert or report an error. Propagating 'X' doesn't work in hardware anyway. So catch errors in simulation and fix them, and keep the code as simple as possible (but no simpler). \$\endgroup\$ – Brian Drummond Nov 20 '16 at 18:16
  • \$\begingroup\$ I found 'X' propagation very useful for data paths to get rid of the numeric_stdwarnings. The concurrent data path logic often does not known if the outcome of the operation is of interest. So, I thought about to extend it to FSMs. Yes, there is no representation for 'X' in hardware, but a signal state can be undefined. \$\endgroup\$ – Martin Zabel Nov 20 '16 at 18:28
  • \$\begingroup\$ You're in the realm of opinion. Case statements, selected and conditional signal/variable assignments are useful with lower overhead clutter. There's package std_logic_1164 functions IS_X for checking. TO_01 in package numeric_std for signed and unsigned which issue warnings (signed and unsigned are the realm of binary representations of numbers, you're interested in something that doesn't represent a binary value). \$\endgroup\$ – user8352 Nov 20 '16 at 18:48
  • \$\begingroup\$ @user8352 Ok, is_x could be applied in my example above, but usually the inputs are separate signals, and then I would start the condition check with if is_x(input1) or is_x(input0). Using to_01 actually means to report an error (Question 1). \$\endgroup\$ – Martin Zabel Nov 20 '16 at 19:03
  • \$\begingroup\$ I apologise, I meant X propagation for state machines. It does indeed have its uses in datapath logic, sorry I was unclear. \$\endgroup\$ – Brian Drummond Nov 20 '16 at 19:30
1
\$\begingroup\$

Don't. The synthesized design can't detect these values anyway, so the best you can do is implement resets properly and handle extraneous states. Then maybe add some asserts or similar to detect issues in simulation.

\$\endgroup\$
  • \$\begingroup\$ I find your answer a bit vague. How would you implement my finite state machine or a similar one? And how should one assure that all resets are implemented appropriately? \$\endgroup\$ – Martin Zabel Jul 17 '17 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.