In Logic Design courses we all learned that it is possible to minimize a logic function, for example by using a Karnaugh map or the Quine–McCluskey algorithm. We also learned that "Don't Care" values increase the minimization potential.
For example take a register file. The
write_data signals don't really matter when the
write_enable signal is
'0'. Thus, they should be assigned a "Don't Care" value to allow more optimizations in the logic that is driving these signals (i.e. not in the register file itself).
What is the correct way to specify such "Don't Care" values in VHDL in order to allow the synthesis tool more room for possible optimizations?
So far I've found the following things which might be suitable. But I'm not really sure what the pros and cons of each approach are:
- Simply not assigning the signal. This seems like it could work. However I found that it doesn't work when you want to define a "do nothing constant" of some
recordtype, since record constants need to be fully specified (at least Modelsim tells me so).
std_logic_1164package defines the value
'-' -- Don't carefor
std_ulogic. This looks like it is the semantically correct choice for an explicit "don't care", but I've never seen it used anywhere (except in the unrelated VHDL-2008
- Modelsim uses the value
'X'to display undefined signals. However I'm not sure if synthesis tools understand an explicit
'X'-assignment as "don't care".
Here's an oversimplified code snippet for clarification, where I've initialized the don't care signals with
As you can see, the signal
control.reg_write_address can have 3 different values:
instruction(11 downto 8); and
instruction(3 downto 0);. Now I'd expect this to be synthesized to a 2-input multiplexer if
'-' is interpreted as "don't care". Had I initialized the signal with
(others => '0') instead of
'-', the tool would have to generate a 3-input multiplexer instead.
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; package mytypes is type control_signals_t is record write_enable : std_logic; write_address : std_ulogic_vector(3 downto 0); read_address : std_ulogic_vector(3 downto 0); end record; -- All members of this constant must be fully specified. -- So it's not possible to simply not assign a value. constant CONTROL_NOP : control_signals_t := ( write_enable => '0', write_address => (others => '-'), read_address => (others => '-') ); end package; library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library cfx; use cfx.mytypes.all; entity control_unit is port( instruction : in std_ulogic_vector(15 downto 0); write_data : out std_ulogic_vector(15 downto 0); ctrl : out control_signals_t ); end entity; architecture rtl of control_unit is begin decode_instruction : process(instruction) is begin -- Set sensible default values that do nothing. -- Especially all "write_enable" signals should be '0'. -- Everything else is mostly irrelevant (don't care). ctrl <= CONTROL_NOP; write_data <= (others => '-'); if instruction(15 downto 12) = "1100" then -- Load 8 bit of data into the register file ctrl.write_enable <= '1'; write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16)); ctrl.write_address <= instruction(11 downto 8); elsif instruction(15 downto 8) = "11111001" then -- Load 4 bit of data into the register file write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16)); ctrl.write_address <= instruction(3 downto 0); elsif instruction(15 downto 8) = "10110101" then -- Read from the register file. Don't use the write signals at all. ctrl.read_address <= instruction(3 downto 0); end if; end process; end architecture;