Jak mogę określić sygnały „nie przejmuj się” w VHDL?

11

Na kursach z projektowania logicznego wszyscy dowiedzieliśmy się, że można zminimalizować funkcję logiczną, na przykład za pomocą mapy Karnaugh lub algorytmu Quine – McCluskey . Dowiedzieliśmy się również, że wartości „Don't Care” zwiększają potencjał minimalizacji.

Na przykład weź plik rejestru. write_addressI write_datasygnały naprawdę nie ma znaczenia, gdy write_enablesygnał jest '0'. Dlatego należy im przypisać wartość „Don't Care”, aby umożliwić więcej optymalizacji w logice napędzającej te sygnały (tj. Nie w samym pliku rejestru).

Jaki jest prawidłowy sposób określenia takich wartości „Don't Care” w VHDL, aby pozwolić narzędziu syntezującemu więcej miejsca na możliwe optymalizacje?


Do tej pory znalazłem następujące rzeczy, które mogą być odpowiednie. Ale nie jestem pewien, jakie są zalety i wady każdego podejścia:

  • Po prostu nie przypisuje sygnału. Wygląda na to, że może działać. Okazało się jednak, że nie działa, gdy chcesz zdefiniować pewnego rodzaju „stałą nic nie rób” record, ponieważ stałe zapisu muszą być w pełni określone (przynajmniej tak mówi mi Modelsim).
  • std_logic_1164Pakiet definiuje wartość '-' -- Don't caredla std_ulogic. Wygląda na to, że jest to semantycznie poprawny wybór wyraźnego „nie przejmuj się”, ale nigdy nie widziałem, aby był używany nigdzie (z wyjątkiem niepowiązanych case?konstrukcji VHDL-2008 ).
  • Modeleim używa tej wartości 'X'do wyświetlania niezdefiniowanych sygnałów. Nie jestem jednak pewien, czy narzędzia do syntezy rozumieją jawne 'X'przypisanie jako „nie przejmuj się”.

Oto uproszczony fragment kodu dla wyjaśnienia, w którym zainicjowałem sygnały „nie przejmuj się” '-'.

Jak widać, sygnał control.reg_write_addressmoże mieć 3 różne wartości: "----", instruction(11 downto 8);i instruction(3 downto 0);. Teraz spodziewam się, że zostanie to zsyntetyzowane do multipleksera 2-wejściowego, jeśli '-'zostanie zinterpretowane jako „nie obchodzi”. Gdybym zainicjował sygnał (others => '0')zamiast '-', narzędzie musiałoby wygenerować 3-wejściowy multiplekser.

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
źródło
Mógłbyś rozwinąć co próbujesz zrobić z write_addressi write_data? Jakiej optymalizacji oczekujesz?
fru1tbat
Mam nadzieję, że przykład wyjaśnia, co chcę osiągnąć.
Fritz

Odpowiedzi:

9

Zostawię to ekspertowi LRM, aby udzielił bardziej szczegółowej odpowiedzi, ale w skrócie, twoje podejście powinno być prawidłowe - przeprowadziłem szybki test z najnowszą wersją Quartusa i działa '-'tak, jak powinien - wygenerowana logika jest zmniejszona zgodnie z oczekiwaniami, gdy wyjście jest domyślnie ustawione na '-'( 'X'działa też, nawiasem mówiąc,). Więcej na temat wymienionych przez ciebie podejść:

  • Nie przypisanie sygnału nie jest tak naprawdę opcją dla twojego przykładu, oczywiście, jeśli nie chcesz zatrzasków. Jeśli jest to proces taktowany, nieco lepiej, ale nadal dostaniesz możliwości tam, gdzie ich nie potrzebujesz. Może brakuje mi twojej intencji tutaj.

  • '-', jak wcześniej wspomniano, jest prawdopodobnie najlepszą opcją, zarówno ze względów semantycznych, jak i praktycznych.

  • Zależy od tego, co rozumiesz przez „niezdefiniowany”. 'X'jest technicznie „nieznany”. 'U'jest dla niezainicjowanych sygnałów, które ModelSim wyświetla jak "X"dla reprezentacji szesnastkowych. 'X'wydaje się jednak działać, jak zauważyłem powyżej.

Inną alternatywą byłoby samodzielne przeprowadzenie optymalizacji i jednoznaczne usunięcie jednego przypadku z testu:

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;

Ma to jednak znaczące wady (głównie związane z klarownością kodu) i prawdopodobnie wybrałbym bardziej idealne rozwiązanie.

Nawiasem mówiąc, '-'jest również powszechnie używany z std_match(), którego rozważę użycie do dekodowania, np .:

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

Chociaż w tym momencie prawdopodobnie lepiej po prostu użyć case?.

fru1tbat
źródło
6

Krótko mówiąc: jest to legalny VHDL i zwykle jest obsługiwany przez narzędzia do syntezy.

Jednak rzadko się zdarza, że ​​się go używa. Naprawdę nie wiem dlaczego. Twój kod wydaje mi się dobrym przykładem tego, kiedy jego użycie byłoby sensowne.

Jest jednak jedna wada, o której należy pamiętać: podczas syntezy funkcje sterujące wyjściami, w których nie ma znaczenia, mogą różnić się między przebiegami syntezy. To sprawia, że ​​synteza jest mniej deterministyczna. Jeśli wyjścia, które zostały zdefiniowane jako nieistotne, są używane (przez pomyłkę), może to utrudnić znalezienie błędu.

Wsparcie narzędziowe

Przynajmniej następujące narzędzia zaakceptują „nie obchodzi mnie” i skorzystają z możliwości optymalizacji:

  • Xilinx (zob .: „Podręcznik użytkownika XST”)
  • Altera (ref .: „Zalecane style kodowania HDL”)
  • Synplify (zob .: „Podręcznik referencyjny Synplify”)

Xilinx i Altera będzie traktować '-'i 'X'jak nie obchodzi, Synplify będzie traktować tych, a ponadto 'U'i 'W'(słaby), a nie obchodzi.

Carl
źródło
1
Miałem inne zastosowanie tej wady. Kod pracował w symulacji, ale nie na FPGA, ponieważ mój kod wyglądało: if signal = '1' then a; else b; end if;. Niestety signalnie było 1lub 0ale -. Tak więc w symulacji elsegałąź została wykonana, ale w sprzęcie -okazało się, że, a 1więc wykonano prawdziwą gałąź ...
Fritz
Tak, miałem podobne symulacje przejścia błędów, ale najczęściej w moim przypadku są takie 'U', które są powszechne na początku symulacji, które zostały użyte, prowadząc do wykonania elsebloku kodu. Byłoby wspaniale, gdyby można było w jakiś sposób wprowadzić warunki warunkowe do propagowania 'U's, podobnie jak zachowanie współbieżnych wyrażeń boolowskich.
Carl
Po znalezieniu tego błędu upewniłem się, że zawsze napiszę coś takiego if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;. Dodałem następujące informacje do wszystkich rejestrów i pamięci: assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR;i if write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;. Plus to samo dla write_data. Razem powinno to złapać prawie wszystkie z tych błędów.
Fritz,
To sposób, ale dla mnie to zbyt szczegółowe. Chciałbym mieć taką możliwość w języku VHDL.
Carl
1
Cóż, tak, VHDL jest nieco gadatliwy, ale to tylko sposób VHDL. : D Z drugiej strony jest również bardzo wyraźne i nie robi „czarnej magii” za moimi plecami, co uważam za całkiem miłe (por. Zen Pythona „Jawne jest lepsze niż niejawne”).
Fritz