Wie kann ich in VHDL „egal“-Signale angeben?

In Logikdesign-Kursen haben wir alle gelernt, dass es möglich ist, eine Logikfunktion zu minimieren, zum Beispiel durch Verwendung einer Karnaugh-Abbildung oder des Quine-McCluskey-Algorithmus . Wir haben auch gelernt, dass "Don't Care" -Werte das Minimierungspotential erhöhen.

Nehmen Sie zum Beispiel eine Registerdatei. Die Signale write_addressund write_dataspielen keine Rolle, wenn das write_enableSignal ist '0'. Daher sollte ihnen ein "Don't Care"-Wert zugewiesen werden, um mehr Optimierungen in der Logik zu ermöglichen, die diese Signale antreibt (dh nicht in der Registerdatei selbst).

Was ist der richtige Weg, um solche "Don't Care"-Werte in VHDL anzugeben, um dem Synthesetool mehr Raum für mögliche Optimierungen zu geben?


Bisher habe ich folgende Dinge gefunden, die geeignet sein könnten. Aber ich bin mir nicht sicher, was die Vor- und Nachteile der einzelnen Ansätze sind:

  • Einfach das Signal nicht zuordnen. Das scheint so zu funktionieren. Ich habe jedoch festgestellt, dass es nicht funktioniert, wenn Sie eine "Do Nothing-Konstante" eines recordTyps definieren möchten, da Datensatzkonstanten vollständig angegeben werden müssen (zumindest sagt mir Modelsim).
  • Das std_logic_1164Paket definiert den Wert '-' -- Don't carefür std_ulogic. Dies sieht so aus, als wäre es die semantisch korrekte Wahl für ein explizites "egal", aber ich habe es noch nie irgendwo verwendet gesehen (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'-Zuweisung als "egal" verstehen.

Hier ist ein stark vereinfachtes Code-Snippet zur Verdeutlichung, in dem ich die don't care-Signale mit initialisiert habe '-'.

Wie Sie sehen können, kann das Signal control.reg_write_address3 verschiedene Werte haben: "----", 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 mit (others => '0')statt mit initialisiert '-', müsste das Tool stattdessen einen 3-Eingangs-Multiplexer generieren.

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_addressKönnten Sie näher erläutern, was Sie mit und versuchen write_data? Welche Optimierung erwarten Sie?
Ich hoffe, das Beispiel macht deutlicher, was ich erreichen möchte.

Antworten (2)

Ich überlasse es einem LRM-Experten, eine detailliertere Antwort zu geben, aber kurz gesagt, Ihr Ansatz sollte gültig sein - ich habe einen schnellen Test mit einer neueren Version von Quartus durchgeführt, und es funktioniert so, '-'wie es soll - die generierte Logik ist reduziert wie erwartet, wenn die Ausgabe standardmäßig eingestellt ist '-'( 'X'funktioniert übrigens auch). Mehr 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 wünschen. Wenn es sich um einen getakteten Prozess handelt, sind Sie etwas besser dran, aber Sie erhalten immer noch Aktivierungen, wo Sie sie möglicherweise nicht benötigen. Vielleicht übersehe ich hier deine Absicht.

  • '-', wie bereits erwähnt, ist wahrscheinlich sowohl aus semantischen als auch aus praktischen Gründen die beste Option.

  • Kommt drauf 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 aber zu funktionieren, wie ich oben schon geschrieben habe.

Eine andere Alternative wäre, die Optimierung selbst durchzuführen und einen Fall explizit aus dem Test herauszunehmen:

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 sind Sie jedoch wahrscheinlich besser dran, wenn Sie einfach case?.

Kurz gesagt: Es ist legales VHDL und wird typischerweise von Synthese-Tools unterstützt.

Es ist jedoch eher ungewöhnlich, es verwendet zu sehen. 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 , dessen man sich bewusst sein sollte: Bei der Synthese könnten die Funktionen, die Ausgänge ansteuern, bei denen Don't Care's beteiligt sind, zwischen Synthesedurchläufen unterschiedlich sein. Dies macht die Synthese weniger deterministisch. Werden (irrtümlich) als don't care definierte Outputs verwendet, kann dies das Auffinden des Fehlers erschweren.

Werkzeugunterstützung

Zumindest die folgenden Tools akzeptieren don't cares und nutzen die Optimierungsmöglichkeiten:

  • Xilinx (ref.: „XST-Benutzerhandbuch“)
  • Altera (Ref.: "Empfohlene HDL-Codierungsstile")
  • Synplify (ref.: "Synplify-Referenzhandbuch")

Xilinx und Altera behandeln '-'und 'X'als don't care, Synplify behandeln diese und außerdem 'U'und 'W'(schwach) als don't care.

Ich hatte eine andere Anwendung dieses Nachteils. Der Code funktionierte in der Simulation, aber nicht auf dem FPGA, weil mein Code so aussah: if signal = '1' then a; else b; end if;. Leider signalwar das nicht 1oder 0aber -. In der Simulation wurde also die elseVerzweigung ausgeführt, aber in der Hardware -stellte sich heraus, dass es sich um eine handelte 1, also wurde die wahre Verzweigung ausgeführt ...
Ja, ich hatte ähnliche Bugs-Pass-Simulationen, aber meistens gibt es in meinem Fall die 'U's, die am Anfang von Simulationen üblich sind und dazu führen, dass ein elseCodeblock ausgeführt wird. Es wäre wunderbar, wenn Bedingungen irgendwie dazu gebracht werden könnten, 'U's weiterzugeben, ähnlich dem Verhalten von gleichzeitigen booleschen Ausdrücken.
Nachdem ich diesen Fehler gefunden hatte, stellte ich sicher, dass ich immer so etwas wie 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 abfangen.
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.
In Ihrem Fall würde ich es vorziehen, zu 'X'("unknown") zu fahren, anstatt , oder, noch besser, bei der Eingabe auf die Ausgabe und alles andere zu '-'propagieren , was nicht oder zu ist - das ist etwas logischer. Es ist eine gute Idee, nach usw. auf 's zu suchen, und ich mache es oft. (Die Überprüfung von Adresse/Daten wann ist natürlich ein weiterer Schritt.) '-''-'01'X''X'write enablewrite enable='1'
Nun ja, VHDL ist ein bisschen ausführlich, aber das ist halt der VHDL-Weg. :D Andererseits ist es auch sehr explizit und macht keine "schwarze Magie" hinter meinem Rücken, was ich ganz nett finde (vgl. The Zen of Python "Explicit is better than implicit").