Ok, I think I understand now.
There are two problems here:
1. How to write code to minimize the synthesized logic, maybe using '-' assignments
2. How to write code so "don't care" ('-') or "unknown" ('X') signals propagate to 'X' on the outputs in simulation
For point 1, I understand that you have paths in the state machine code for which it doesn't matter what value is assigned to an output signal.
You are correct that having no assignment creates logic to keep the current value. By assigning the value '-' (don't care), you tell the synthesis tool that any value is OK.
The tool can use a fixed '0' or '1', but also a value that depends on other signals.
In theory, this should give the synthesis tool more options to minimize the required logic.
The problem is that output signals that depend on such a signal will normally show up as 'X' in the simulation (which you actually want in this case).
I don't assign '-' to minimize logic, since if 'X' outputs are allowed, the risk is much higher that you miss a signal that should not be 'X'. Normally I consider an 'X' output as an error.
I often have a default assignment at the beginning of a process, with the most probable value. I then override that with a new assignment when necessary.
Maybe not the absolute minimum logic, but a very predictable result and no 'X' outputs in simulation.
Now to point 2, where you really want to output 'X' for signals that depend on a "don't care" signal.
In your example, the 'X' disappears in the to_integer function, but an optional "metavalue" warning will be generated by the simulator.
If the mux have a fixed size, it is easy to do your own with a "case" and an "others" that assigns 'X' to the outputs:
Code:
mux: process(all)
case address is
when "000" =>
output <= registers(0);
when "001" =>
output <= registers(1);
when "010" =>
output <= registers(2);
when "011" =>
output <= registers(3);
when "100" =>
output <= registers(4);
when "101" =>
output <= registers(5);
when "110" =>
output <= registers(6);
when "111" =>
output <= registers(7);
when others =>
output <= (others => 'X');
end case;
end process;
I know that the code above will work. The synthesis tool will ignore the "others" clause if all combinations of '0' and '1' are covered by the earlier "when" clauses.
It is possible that you can write a more generic loop that does the same thing, but I have not tried that with any synthesis tool.
One more thing about the synthesizeable code:
Only use '-' assignments to make optimizations possible for the synthesis tool. This means that you should probably never use '-' as stimuli in a test bench. Assign 'X' in the test bench if you want to see what depends on a signal.
To indicate an unknown output in simulation, assign 'X'. Only do that when all possible combinations of inputs with only '0' and '1' have been covered with other assignments, then the 'X' assignment will be ignored by the synthesis tool.
'-' is "don't care", used by the synthesis tool and the simulator. IMHO, don't use it in a test bench, only in the synthesizeable code.
'X' is "unknown", used only by the simulator.