In Logic Design-Kursen haben wir alle gelernt, dass es möglich ist, eine Logikfunktion zu minimieren, beispielsweise mithilfe einer Karnaugh-Karte oder des Quine-McCluskey-Algorithmus . Wir haben auch erfahren, dass "Don't Care" -Werte das Minimierungspotential erhöhen.
Nehmen Sie zum Beispiel eine Registerdatei. Die write_address
und write_data
Signale spielen keine Rolle, wann das write_enable
Signal ist '0'
. Daher sollte ihnen ein "Don't Care" -Wert zugewiesen werden, um weitere Optimierungen in der Logik zu ermöglichen, die diese Signale steuert (dh nicht in der Registerdatei selbst).
Wie können solche "Don't Care" -Werte in VHDL korrekt angegeben werden, damit das Synthesewerkzeug mehr Raum für mögliche Optimierungen bietet?
Bisher habe ich die folgenden Dinge gefunden, die geeignet sein könnten. Ich bin mir jedoch nicht sicher, welche Vor- und Nachteile die einzelnen Ansätze haben:
- Das Signal einfach nicht zuweisen. Dies scheint zu funktionieren. Ich habe jedoch festgestellt, dass es nicht funktioniert, wenn Sie eine Art "Nichts tun-Konstante" definieren möchten
record
, da Datensatzkonstanten vollständig angegeben werden müssen (zumindest Modelsim sagt es mir). - Das
std_logic_1164
Paket definiert den Wert'-' -- Don't care
fürstd_ulogic
. Dies scheint die semantisch korrekte Wahl für ein explizites "egal" zu sein, aber ich habe noch nie gesehen, dass es irgendwo verwendet wird (außer in den nicht verwandten VHDL-2008-case?
Konstrukten). - Modelsim verwendet den Wert
'X'
, um undefinierte Signale anzuzeigen. Ich bin mir jedoch nicht sicher, ob Synthesewerkzeuge eine explizite'X'
Zuordnung als "egal" verstehen .
Hier ist ein stark vereinfachtes Code-Snippet zur Verdeutlichung, mit dem ich die nicht interessierenden Signale initialisiert habe '-'
.
Wie Sie sehen können, das Signal control.reg_write_address
kann drei verschiedene Werte annehmen: "----"
, instruction(11 downto 8);
und instruction(3 downto 0);
. Jetzt würde ich erwarten, dass dies zu einem Multiplexer mit 2 Eingängen synthetisiert wird, wenn dies '-'
als "egal" interpretiert wird. Hätte ich das Signal (others => '0')
stattdessen mit initialisiert '-'
, müsste das Tool stattdessen einen Multiplexer mit 3 Eingängen erzeugen.
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;
quelle
write_address
und tun möchtenwrite_data
? Welche Optimierung erwarten Sie?Antworten:
Ich werde es einem LRM-Experten überlassen, eine detailliertere Antwort zu geben, aber kurz gesagt, Ihr Ansatz sollte gültig sein - ich habe einen Schnelltest mit einer neueren Version von Quartus durchgeführt und es funktioniert so,
'-'
wie es soll - die generierte Logik wird reduziert wie erwartet, wenn die Ausgabe standardmäßig auf eingestellt ist'-'
('X'
funktioniert übrigens auch). Weitere Informationen zu den von Ihnen aufgeführten Ansätzen:Das Signal nicht zuzuweisen ist für Ihr Beispiel natürlich keine Option, wenn Sie keine Latches möchten. Wenn es sich um einen getakteten Prozess handelt, sind Sie etwas besser dran, aber Sie erhalten immer noch Aktivierungen, bei denen Sie sie möglicherweise nicht benötigen. Vielleicht vermisse ich deine Absicht hier.
'-'
Wie bereits erwähnt, ist dies wahrscheinlich sowohl aus semantischen als auch aus praktischen Gründen die beste Option.Kommt darauf an, was du mit "undefiniert" meinst.
'X'
ist technisch "unbekannt".'U'
ist für nicht initialisierte Signale, die ModelSim wie"X"
für Hex-Darstellungen anzeigt .'X'
scheint jedoch zu funktionieren, wie ich oben erwähnt habe.Eine andere Alternative wäre, die Optimierung selbst durchzuführen und einen Fall aus dem expliziten Test zu entfernen:
Dies hat jedoch erhebliche Nachteile (hauptsächlich im Zusammenhang mit der Klarheit des Codes), und ich würde mich wahrscheinlich für eine idealere Lösung entscheiden.
Übrigens
'-'
wird auch häufig mit verwendetstd_match()
, was ich für Ihre Dekodierung in Betracht ziehen würde, z.An diesem Punkt ist es wahrscheinlich besser, wenn Sie nur verwenden
case?
.quelle
Kurz gesagt: Es ist legales VHDL und wird normalerweise von Synthesewerkzeugen unterstützt.
Es ist jedoch eher ungewöhnlich, dass es verwendet wird. Ich weiß nicht wirklich warum. Ihr Code scheint mir ein gutes Beispiel dafür zu sein, wann es sinnvoll wäre, ihn zu verwenden.
Es gibt jedoch einen Nachteil , den man beachten sollte: Bei der Synthese können die Funktionen, die Ausgaben steuern, bei denen es nicht darum geht, zwischen den Syntheseläufen unterschiedlich sein . Dies macht die Synthese weniger deterministisch. Wenn (aus Versehen) Ausgaben verwendet werden, die als egal definiert wurden, kann dies das Auffinden des Fehlers erschweren.
Werkzeugunterstützung
Zumindest die folgenden Tools akzeptieren keine Bedenken und nutzen die Optimierungsmöglichkeiten:
Xilinx und Altera werden behandeln
'-'
und'X'
als egal, Synplify wird diese behandeln und außerdem'U'
und'W'
(schwach) als egal.quelle
if signal = '1' then a; else b; end if;
. Leidersignal
war das nicht1
oder0
aber-
. Also in der Simulation wurde derelse
Zweig ausgeführt, aber in der Hardware-
stellte sich heraus, dass ein1
, so dass der wahre Zweig ausgeführt wurde ...'U'
s, die zu Beginn der Simulationen häufig verwendet wurden und dazu führten, dass einelse
Codeblock ausgeführt wurde. Es wäre wunderbar, wenn Bedingungen irgendwie dazu gebracht werden könnten,'U'
s zu verbreiten , ähnlich wie bei gleichzeitigen booleschen Ausdrücken.if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;
. Und ich habe allen Registern und Erinnerungen Folgendes hinzugefügt:assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR;
undif write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;
. Plus das gleiche fürwrite_data
. Zusammen sollte das fast alle diese Fehler auffangen.