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:
Should I propagate 'X' values (to the FSM state and mealy outputs) or should I just
report
an error?Is there a more readable code template to test complex conditions as in the example above?
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.)
'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:16numeric_std
warnings. 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:28is_x
could be applied in my example above, but usually the inputs are separate signals, and then I would start the condition check withif is_x(input1) or is_x(input0)
. Usingto_01
actually means to report an error (Question 1). \$\endgroup\$ – Martin Zabel Nov 20 '16 at 19:03