Wie kann ich in VHDL "egal" -Signale angeben?

11

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_addressund write_dataSignale spielen keine Rolle, wann das write_enableSignal 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_1164Paket definiert den Wert '-' -- Don't carefür std_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_addresskann 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;
Fritz
quelle
Könnten Sie näher erläutern, was Sie mit write_addressund tun möchten write_data? Welche Optimierung erwarten Sie?
fru1tbat
Ich hoffe, das Beispiel macht klarer, was ich erreichen möchte.
Fritz

Antworten:

9

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:

if instruction(15 downto 8) = "11111001" then
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
else
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
end if;

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 verwendet std_match(), was ich für Ihre Dekodierung in Betracht ziehen würde, z.

if std_match(instruction(15 downto 8), "1100----") then

An diesem Punkt ist es wahrscheinlich besser, wenn Sie nur verwenden case?.

fru1tbat
quelle
6

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 (Ref.: "XST User Guide")
  • Altera (Ref.: "Empfohlene HDL-Codierungsstile")
  • Synplify (Ref.: "Synplify Referenzhandbuch")

Xilinx und Altera werden behandeln '-'und 'X'als egal, Synplify wird diese behandeln und außerdem 'U'und 'W'(schwach) als egal.

Carl
quelle
1
Ich hatte eine andere Anwendung dieses Nachteils. Der Code funktionierte in der Simulation, aber nicht auf dem FPGA, da mein Code wie folgt aussah : if signal = '1' then a; else b; end if;. Leider signalwar das nicht 1oder 0aber -. Also in der Simulation wurde der elseZweig ausgeführt, aber in der Hardware -stellte sich heraus, dass ein 1, so dass der wahre Zweig ausgeführt wurde ...
Fritz
Ja, ich hatte ähnliche Bugs-Pass-Simulationen, aber in meinem Fall gibt es meistens die 'U's, die zu Beginn der Simulationen häufig verwendet wurden und dazu führten, dass ein elseCodeblock 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.
Carl
Nachdem ich diesen Fehler gefunden hatte, stellte ich sicher, dass ich immer so etwas schrieb 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;und if write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;. Plus das gleiche für write_data. Zusammen sollte das fast alle diese Fehler auffangen.
Fritz
Das ist ein Weg, aber das ist mir viel zu ausführlich. Ich würde mir diese Möglichkeit innerhalb der VHDL-Sprache wünschen.
Carl
1
Nun ja, VHDL ist ein bisschen ausführlich, aber das ist nur der VHDL-Weg. : D Andererseits ist es auch sehr explizit und macht hinter meinem Rücken keine "schwarze Magie", was ich sehr schön finde (vgl. Das Zen von Python "Explizit ist besser als implizit").
Fritz