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_address
and 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
record
type, since record constants need to be fully specified (at least Modelsim tells me so). - The
std_logic_1164
package defines the value'-' -- Don't care
forstd_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-2008case?
constructs). - 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;
write_address
andwrite_data
? What optimization do you expect to take place? \$\endgroup\$